3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
8 * \author Angus Leeming
10 * \author Enrico Forestieri
12 * Full author contact details are available in file CREDITS.
16 Docu : To use the lyxserver define the name of the pipe in your
18 \serverpipe "/home/myhome/.lyxpipe"
19 Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
20 Each message consists of a single line in ASCII. Input lines
21 (client -> LyX) have the following format:
22 "LYXCMD:<clientname>:<functionname>:<argument>"
23 Answers from LyX look like this:
24 "INFO:<clientname>:<functionname>:<data>"
25 [asierra970531] Or like this in case of error:
26 "ERROR:<clientname>:<functionname>:<error message>"
27 where <clientname> and <functionname> are just echoed.
28 If LyX notifies about a user defined extension key-sequence,
29 the line looks like this:
30 "NOTIFY:<key-sequence>"
31 [asierra970531] New server-only messages to implement a simple protocol
32 "LYXSRV:<clientname>:<protocol message>"
33 where <protocol message> can be "hello" or "bye". If hello is
34 received LyX will inform the client that it's listening its
35 messages, and 'bye' will inform that lyx is closing.
37 See development/lyxserver/server_monitor.cpp for an example client.
38 Purpose: implement a client/server lib for LyX
45 #include "DispatchResult.h"
46 #include "FuncRequest.h"
48 #include "LyXAction.h"
50 #include "frontends/Application.h"
52 #include "support/debug.h"
53 #include "support/FileName.h"
54 #include "support/filetools.h"
55 #include "support/lassert.h"
56 #include "support/lstrings.h"
57 #include "support/os.h"
59 #include "support/bind.h"
62 #include <QCoreApplication>
67 #ifdef HAVE_SYS_STAT_H
68 # include <sys/stat.h>
73 using namespace lyx::support;
74 using os::external_path;
78 /////////////////////////////////////////////////////////////////////
82 /////////////////////////////////////////////////////////////////////
86 class ReadReadyEvent : public QEvent {
89 ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
92 DWORD inpipe() const { return inpipe_; }
100 string errormsg(DWORD const error)
104 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
105 FORMAT_MESSAGE_FROM_SYSTEM |
106 FORMAT_MESSAGE_IGNORE_INSERTS,
108 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
109 (LPTSTR) &msgbuf, 0, NULL)) {
110 message = static_cast<char *>(msgbuf);
113 message = "Unknown error";
121 DWORD WINAPI pipeServerWrapper(void * arg)
123 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
124 if (!lyxcomm->pipeServer()) {
125 // Error exit; perform cleanup.
126 lyxcomm->ready_ = false;
127 lyxcomm->closeHandles();
128 CloseHandle(lyxcomm->server_thread_);
129 CloseHandle(lyxcomm->stopserver_);
130 CloseHandle(lyxcomm->outbuf_mutex_);
131 lyxerr << "LyXComm: Closing connection" << endl;
137 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
138 : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
140 for (int i = 0; i < MAX_PIPES; ++i) {
142 pipe_[i].handle = INVALID_HANDLE_VALUE;
145 deferred_loading_ = false;
150 bool LyXComm::pipeServer()
155 for (i = 0; i < MAX_PIPES; ++i) {
156 bool const is_outpipe = i >= MAX_CLIENTS;
157 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
158 : PIPE_ACCESS_INBOUND;
159 string const pipename = external_path(pipeName(i));
161 // Manual-reset event, initial state = signaled
162 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
164 error = GetLastError();
165 lyxerr << "LyXComm: Could not create event for pipe "
166 << pipename << "\nLyXComm: "
167 << errormsg(error) << endl;
171 pipe_[i].overlap.hEvent = event_[i];
172 pipe_[i].iobuf.erase();
173 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
174 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
175 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
178 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
179 error = GetLastError();
180 lyxerr << "LyXComm: Could not create pipe "
181 << pipename << "\nLyXComm: "
182 << errormsg(error) << endl;
188 pipe_[i].state = pipe_[i].pending_io ?
189 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
193 // Add the stopserver_ event
194 event_[MAX_PIPES] = stopserver_;
197 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
203 while (!checkStopServer()) {
204 // Indefinitely wait for the completion of an overlapped
205 // read, write, or connect operation.
206 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
209 // Determine which pipe instance completed the operation.
210 i = wait - WAIT_OBJECT_0;
211 LASSERT(i >= 0 && i <= MAX_PIPES, /**/);
213 // Check whether we were waked up for stopping the pipe server.
217 bool const is_outpipe = i >= MAX_CLIENTS;
219 // Get the result if the operation was pending.
220 if (pipe_[i].pending_io) {
221 success = GetOverlappedResult(pipe_[i].handle,
222 &pipe_[i].overlap, &status, FALSE);
224 switch (pipe_[i].state) {
225 case CONNECTING_STATE:
226 // Pending connect operation
228 error = GetLastError();
229 lyxerr << "LyXComm: "
230 << errormsg(error) << endl;
231 if (!resetPipe(i, true))
235 pipe_[i].state = is_outpipe ? WRITING_STATE
240 // Pending read operation
241 LASSERT(!is_outpipe, /**/);
242 if (!success || status == 0) {
243 if (!resetPipe(i, !success))
247 pipe_[i].nbytes = status;
248 pipe_[i].state = WRITING_STATE;
252 // Pending write operation
253 LASSERT(is_outpipe, /**/);
254 // Let's see whether we have a reply
255 if (!outbuf_.empty()) {
256 // Yep. Deliver it to all pipe
257 // instances if we get ownership
258 // of the mutex, otherwise we'll
259 // try again the next round.
260 DWORD result = WaitForSingleObject(
262 if (result == WAIT_OBJECT_0) {
263 DWORD j = MAX_CLIENTS;
264 while (j < MAX_PIPES) {
265 pipe_[j].iobuf = outbuf_;
270 ReleaseMutex(outbuf_mutex_);
272 if (pipe_[i].iobuf.empty())
273 pipe_[i].pending_io = false;
278 // Operate according to the pipe state
279 switch (pipe_[i].state) {
281 // The pipe instance is connected to a client
282 // and is ready to read a request.
283 LASSERT(!is_outpipe, /**/);
284 success = ReadFile(pipe_[i].handle,
285 pipe_[i].readbuf, PIPE_BUFSIZE - 1,
286 &pipe_[i].nbytes, &pipe_[i].overlap);
288 if (success && pipe_[i].nbytes != 0) {
289 // The read operation completed successfully.
290 pipe_[i].pending_io = false;
291 pipe_[i].state = WRITING_STATE;
295 error = GetLastError();
297 if (!success && error == ERROR_IO_PENDING) {
298 // The read operation is still pending.
299 pipe_[i].pending_io = true;
303 success = error == ERROR_BROKEN_PIPE;
305 // Client closed connection (ERROR_BROKEN_PIPE) or
306 // an error occurred; in either case, reset the pipe.
308 lyxerr << "LyXComm: " << errormsg(error) << endl;
309 if (!pipe_[i].iobuf.empty()) {
310 lyxerr << "LyXComm: truncated command: "
311 << pipe_[i].iobuf << endl;
312 pipe_[i].iobuf.erase();
315 if (!resetPipe(i, !success))
321 // The request was successfully read
322 // from the client; commit it.
323 ReadReadyEvent * event = new ReadReadyEvent(i);
324 QCoreApplication::postEvent(this,
325 static_cast<QEvent *>(event));
326 // Wait for completion
327 while (pipe_[i].nbytes && !checkStopServer(100))
329 pipe_[i].pending_io = false;
330 pipe_[i].state = READING_STATE;
334 // This is an output pipe instance. Initiate the
335 // overlapped write operation or monitor its progress.
337 if (pipe_[i].pending_io) {
338 success = WriteFile(pipe_[i].handle,
339 pipe_[i].iobuf.c_str(),
340 pipe_[i].iobuf.length(),
345 if (success && !pipe_[i].iobuf.empty()
346 && status == pipe_[i].iobuf.length()) {
347 // The write operation completed successfully.
348 pipe_[i].iobuf.erase();
349 pipe_[i].pending_io = false;
355 error = GetLastError();
357 if (success && error == ERROR_IO_PENDING) {
358 // The write operation is still pending.
359 // We get here when a reader is started
360 // well before a reply is ready, so delay
361 // a bit in order to not burden the cpu.
362 checkStopServer(100);
363 pipe_[i].pending_io = true;
367 success = error == ERROR_NO_DATA;
369 // Client closed connection (ERROR_NO_DATA) or
370 // an error occurred; in either case, reset the pipe.
372 lyxerr << "LyXComm: Error sending message: "
373 << pipe_[i].iobuf << "\nLyXComm: "
374 << errormsg(error) << endl;
376 if (!resetPipe(i, !success))
388 void LyXComm::closeHandles()
390 for (int i = 0; i < MAX_PIPES; ++i) {
392 ResetEvent(event_[i]);
393 CloseHandle(event_[i]);
396 if (pipe_[i].handle != INVALID_HANDLE_VALUE) {
397 CloseHandle(pipe_[i].handle);
398 pipe_[i].handle = INVALID_HANDLE_VALUE;
404 bool LyXComm::event(QEvent * e)
406 if (e->type() == QEvent::User) {
407 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
414 bool LyXComm::checkStopServer(DWORD timeout)
416 return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0;
420 bool LyXComm::startPipe(DWORD index)
422 pipe_[index].pending_io = false;
423 pipe_[index].overlap.Offset = 0;
424 pipe_[index].overlap.OffsetHigh = 0;
426 // Overlapped ConnectNamedPipe should return zero.
427 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
428 DWORD const error = GetLastError();
429 lyxerr << "LyXComm: Could not connect pipe "
430 << external_path(pipeName(index))
431 << "\nLyXComm: " << errormsg(error) << endl;
435 switch (GetLastError()) {
436 case ERROR_IO_PENDING:
437 // The overlapped connection is in progress.
438 pipe_[index].pending_io = true;
441 case ERROR_PIPE_CONNECTED:
442 // Client is already connected, so signal an event.
443 if (SetEvent(pipe_[index].overlap.hEvent))
447 // Anything else is an error.
448 DWORD const error = GetLastError();
449 lyxerr << "LyXComm: An error occurred while connecting pipe "
450 << external_path(pipeName(index))
451 << "\nLyXComm: " << errormsg(error) << endl;
459 bool LyXComm::resetPipe(DWORD index, bool close_handle)
461 // This method is called when an error occurs or when a client
462 // closes the connection. We first disconnect the pipe instance,
463 // then reconnect it, ready to wait for another client.
465 if (!DisconnectNamedPipe(pipe_[index].handle)) {
466 DWORD const error = GetLastError();
467 lyxerr << "LyXComm: Could not disconnect pipe "
468 << external_path(pipeName(index))
469 << "\nLyXComm: " << errormsg(error) << endl;
470 // What to do now? Let's try whether re-creating the pipe helps.
474 bool const is_outpipe = index >= MAX_CLIENTS;
477 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
478 : PIPE_ACCESS_INBOUND;
479 string const name = external_path(pipeName(index));
481 CloseHandle(pipe_[index].handle);
483 pipe_[index].iobuf.erase();
484 pipe_[index].handle = CreateNamedPipe(name.c_str(),
485 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
486 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
489 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
490 DWORD const error = GetLastError();
491 lyxerr << "LyXComm: Could not reset pipe " << name
492 << "\nLyXComm: " << errormsg(error) << endl;
497 if (!startPipe(index))
499 pipe_[index].state = pipe_[index].pending_io ?
500 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
506 void LyXComm::openConnection()
508 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
510 // If we are up, that's an error
512 LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
516 if (pipename_.empty()) {
517 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
521 // Check whether the pipe name is being used by some other instance.
522 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
523 // Tell the running instance to load the files
524 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
525 deferred_loading_ = true;
529 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
530 << " already exists.\nMaybe another instance of LyX"
531 " is using it." << endl;
536 // Mutex with no initial owner for synchronized access to outbuf_
537 outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
538 if (!outbuf_mutex_) {
539 DWORD const error = GetLastError();
540 lyxerr << "LyXComm: Could not create output buffer mutex"
541 << "\nLyXComm: " << errormsg(error) << endl;
546 // Manual-reset event, initial state = not signaled
547 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
549 DWORD const error = GetLastError();
550 lyxerr << "LyXComm: Could not create stop server event"
551 << "\nLyXComm: " << errormsg(error) << endl;
553 CloseHandle(outbuf_mutex_);
557 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
558 static_cast<void *>(this), 0, NULL);
559 if (!server_thread_) {
560 DWORD const error = GetLastError();
561 lyxerr << "LyXComm: Could not create pipe server thread"
562 << "\nLyXComm: " << errormsg(error) << endl;
564 CloseHandle(stopserver_);
565 CloseHandle(outbuf_mutex_);
572 void LyXComm::closeConnection()
574 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
576 if (pipename_.empty()) {
577 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
582 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
586 SetEvent(stopserver_);
587 // Wait for the pipe server to finish
588 WaitForSingleObject(server_thread_, INFINITE);
589 CloseHandle(server_thread_);
590 ResetEvent(stopserver_);
591 CloseHandle(stopserver_);
592 CloseHandle(outbuf_mutex_);
596 void LyXComm::emergencyCleanup()
599 SetEvent(stopserver_);
600 // Forcibly terminate the pipe server thread if it does
601 // not finish quickly.
602 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
603 TerminateThread(server_thread_, 0);
607 CloseHandle(server_thread_);
608 ResetEvent(stopserver_);
609 CloseHandle(stopserver_);
610 CloseHandle(outbuf_mutex_);
615 void LyXComm::read_ready(DWORD inpipe)
617 // Turn the pipe buffer into a C string
618 DWORD const nbytes = pipe_[inpipe].nbytes;
619 pipe_[inpipe].readbuf[nbytes] = '\0';
621 pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
623 // Commit any commands read
624 while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
625 // split() grabs the entire string if
626 // the delim /wasn't/ found. ?:-P
628 pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
629 cmd = rtrim(cmd, "\r");
630 LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
631 << ", iobuf:" << pipe_[inpipe].iobuf
634 clientcb_(client_, cmd);
637 // Signal that we are done.
638 pipe_[inpipe].nbytes = 0;
642 void LyXComm::send(string const & msg)
645 lyxerr << "LyXComm: Request to send empty string. Ignoring."
650 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
652 if (pipename_.empty())
656 lyxerr << "LyXComm: Pipes are closed. Could not send "
661 // Request ownership of the outbuf_mutex_
662 DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
664 if (result == WAIT_OBJECT_0) {
665 // If a client doesn't care to read a reply (JabRef is one
666 // such client), the output buffer could grow without limit.
667 // So, we empty it when its size is larger than PIPE_BUFSIZE.
668 if (outbuf_.size() > PIPE_BUFSIZE)
671 ReleaseMutex(outbuf_mutex_);
673 // Something is fishy, better resetting the connection.
674 DWORD const error = GetLastError();
675 lyxerr << "LyXComm: Error sending message: " << msg
676 << "\nLyXComm: " << errormsg(error)
677 << "\nLyXComm: Resetting connection" << endl;
678 ReleaseMutex(outbuf_mutex_);
685 string const LyXComm::pipeName(DWORD index) const
687 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
691 #elif !defined (HAVE_MKFIFO)
692 // We provide a stub class that disables the lyxserver.
694 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
697 void LyXComm::openConnection()
701 void LyXComm::closeConnection()
705 int LyXComm::startPipe(string const & filename, bool write)
711 void LyXComm::endPipe(int & fd, string const & filename, bool write)
715 void LyXComm::emergencyCleanup()
718 void LyXComm::read_ready()
722 void LyXComm::send(string const & msg)
726 #else // defined (HAVE_MKFIFO)
729 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
730 : pipename_(pip), client_(cli), clientcb_(ccb)
733 deferred_loading_ = false;
738 void LyXComm::openConnection()
740 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
742 // If we are up, that's an error
744 lyxerr << "LyXComm: Already connected" << endl;
747 // We assume that we don't make it
750 if (pipename_.empty()) {
751 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
755 infd_ = startPipe(inPipeName(), false);
759 outfd_ = startPipe(outPipeName(), true);
761 endPipe(infd_, inPipeName(), false);
765 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
766 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
767 << '\n' << strerror(errno) << endl;
773 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
778 void LyXComm::closeConnection()
780 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
782 if (pipename_.empty()) {
783 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
788 LYXERR0("LyXComm: Already disconnected");
792 endPipe(infd_, inPipeName(), false);
793 endPipe(outfd_, outPipeName(), true);
799 int LyXComm::startPipe(string const & file, bool write)
801 static bool stalepipe = false;
802 FileName const filename(file);
803 if (filename.exists()) {
805 // Let's see whether we have a stale pipe.
806 int fd = ::open(filename.toFilesystemEncoding().c_str(),
807 O_WRONLY | O_NONBLOCK);
809 // Another LyX instance is using it.
811 // Tell the running instance to load the files
812 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
813 deferred_loading_ = true;
817 } else if (errno == ENXIO) {
818 // No process is reading from the other end.
820 LYXERR(Debug::LYXSERVER,
821 "LyXComm: trying to remove "
823 filename.removeFile();
825 } else if (stalepipe) {
826 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
828 filename.removeFile();
831 if (filename.exists()) {
832 lyxerr << "LyXComm: Pipe " << filename
833 << " already exists.\nIf no other LyX program"
834 " is active, please delete the pipe by hand"
842 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
843 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
844 << strerror(errno) << endl;
847 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
848 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
851 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
852 << strerror(errno) << endl;
853 filename.removeFile();
858 theApp()->registerSocketCallback(fd,
859 bind(&LyXComm::read_ready, this));
866 void LyXComm::endPipe(int & fd, string const & filename, bool write)
872 theApp()->unregisterSocketCallback(fd);
874 if (::close(fd) < 0) {
875 lyxerr << "LyXComm: Could not close pipe " << filename
876 << '\n' << strerror(errno) << endl;
879 if (FileName(filename).removeFile() < 0) {
880 lyxerr << "LyXComm: Could not remove pipe " << filename
881 << '\n' << strerror(errno) << endl;
888 void LyXComm::emergencyCleanup()
890 if (!pipename_.empty()) {
891 endPipe(infd_, inPipeName(), false);
892 endPipe(outfd_, outPipeName(), true);
897 // Receives messages and sends then to client
898 void LyXComm::read_ready()
900 // FIXME: make read_buffer_ a class-member for multiple sessions
901 static string read_buffer_;
902 read_buffer_.erase();
904 int const charbuf_size = 100;
905 char charbuf[charbuf_size];
907 // As O_NONBLOCK is set, until no data is available for reading,
908 // read() doesn't block but returns -1 and set errno to EAGAIN.
909 // After a client that opened the pipe for writing, closes it
910 // (and no other client is using the pipe), read() would always
911 // return 0 and thus the connection has to be reset.
915 // the single = is intended here.
916 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
919 charbuf[status] = '\0'; // turn it into a c string
920 read_buffer_ += rtrim(charbuf, "\r");
921 // commit any commands read
922 while (read_buffer_.find('\n') != string::npos) {
923 // split() grabs the entire string if
924 // the delim /wasn't/ found. ?:-P
926 read_buffer_= split(read_buffer_, cmd,'\n');
927 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
928 << ", read_buffer_:" << read_buffer_
931 clientcb_(client_, cmd);
935 if (errno == EAGAIN) {
936 // Nothing to read, continue
940 // An error occurred, better bailing out
941 LYXERR0("LyXComm: " << strerror(errno));
942 if (!read_buffer_.empty()) {
943 LYXERR0("LyXComm: truncated command: " << read_buffer_);
944 read_buffer_.erase();
946 break; // reset connection
950 // The connection gets reset when read() returns 0 (meaning that the
951 // last client closed the pipe) or an error occurred, in which case
952 // read() returns -1 and errno != EAGAIN.
959 void LyXComm::send(string const & msg)
962 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
966 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
968 if (pipename_.empty()) return;
971 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
972 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
973 lyxerr << "LyXComm: Error sending message: " << msg
974 << '\n' << strerror(errno)
975 << "\nLyXComm: Resetting connection" << endl;
981 #endif // defined (HAVE_MKFIFO)
985 struct Sleep : QThread
987 static void millisec(unsigned long ms)
989 QThread::usleep(ms * 1000);
996 bool LyXComm::loadFilesInOtherInstance()
999 int loaded_files = 0;
1000 FileName const pipe(inPipeName());
1001 vector<string>::iterator it = theFilesToLoad().begin();
1002 while (it != theFilesToLoad().end()) {
1003 FileName fname = fileSearch(string(), os::internal_path(*it),
1004 "lyx", may_not_exist);
1005 if (fname.empty()) {
1009 // Wait a while to allow time for the other
1010 // instance to reset the connection
1011 Sleep::millisec(200);
1012 pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
1015 string const cmd = "LYXCMD:pipe:file-open:" +
1016 fname.absFileName() + '\n';
1017 ::write(pipefd, cmd.c_str(), cmd.length());
1020 it = theFilesToLoad().erase(it);
1022 return loaded_files > 0;
1026 string const LyXComm::inPipeName() const
1028 return pipename_ + ".in";
1032 string const LyXComm::outPipeName() const
1034 return pipename_ + ".out";
1038 /////////////////////////////////////////////////////////////////////
1042 /////////////////////////////////////////////////////////////////////
1044 void ServerCallback(Server * server, string const & msg)
1046 server->callback(msg);
1049 Server::Server(string const & pipes)
1050 : numclients_(0), pipes_(pipes, this, &ServerCallback)
1056 // say goodbye to clients so they stop sending messages
1057 // send as many bye messages as there are clients,
1058 // each with client's name.
1060 for (int i = 0; i != numclients_; ++i) {
1061 message = "LYXSRV:" + clients_[i] + ":bye\n";
1062 pipes_.send(message);
1067 int compare(char const * a, char const * b, unsigned int len)
1069 using namespace std;
1070 return strncmp(a, b, len);
1074 // Handle data gotten from communication, called by LyXComm
1075 void Server::callback(string const & msg)
1077 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1079 char const * p = msg.c_str();
1081 // --- parse the string --------------------------------------------
1083 // Format: LYXCMD:<client>:<func>:<argstring>\n
1085 bool server_only = false;
1087 // --- 1. check 'header' ---
1089 if (compare(p, "LYXSRV:", 7) == 0) {
1091 } else if (0 != compare(p, "LYXCMD:", 7)) {
1092 lyxerr << "Server: Unknown request \""
1093 << p << '"' << endl;
1098 // --- 2. for the moment ignore the client name ---
1100 while (*p && *p != ':')
1101 client += char(*p++);
1107 // --- 3. get function name ---
1109 while (*p && *p != ':')
1112 // --- 4. parse the argument ---
1114 if (!server_only && *p == ':' && *(++p)) {
1115 while (*p && *p != '\n')
1120 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1121 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1123 // --- lookup and exec the command ------------------
1127 // return the greeting to inform the client that
1128 // we are listening.
1129 if (cmd == "hello") {
1131 if (numclients_ == MAX_CLIENTS) { //paranoid check
1132 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1136 while (!clients_[i].empty() && i < numclients_)
1138 clients_[i] = client;
1140 buf = "LYXSRV:" + client + ":hello\n";
1141 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1143 } else if (cmd == "bye") {
1144 // If clients_ == 0 maybe we should reset the pipes
1145 // to prevent fake callbacks
1146 int i = 0; //look if client is registered
1147 for (; i < numclients_; ++i) {
1148 if (clients_[i] == client)
1151 if (i < numclients_) {
1153 clients_[i].erase();
1154 LYXERR(Debug::LYXSERVER, "Server: Client "
1155 << client << " said goodbye");
1157 LYXERR(Debug::LYXSERVER,
1158 "Server: ignoring bye messge from unregistered client" << client);
1161 LYXERR0("Server: Undefined server command " << cmd << '.');
1167 // which lyxfunc should we let it connect to?
1168 // The correct solution would be to have a
1169 // specialized (non-gui) BufferView. But how do
1170 // we do it now? Probably we should just let it
1171 // connect to the lyxfunc in the single GuiView we
1172 // support currently. (Lgb)
1174 FuncRequest const fr(lyxaction.lookupFunc(cmd), arg);
1176 theApp()->dispatch(fr, dr);
1177 string const rval = to_utf8(dr.message());
1179 // all commands produce an INFO or ERROR message
1180 // in the output pipe, even if they do not return
1181 // anything. See chapter 4 of Customization doc.
1187 buf += client + ':' + cmd + ':' + rval + '\n';
1190 // !!! we don't do any error checking -
1191 // if the client won't listen, the
1192 // message is lost and others too
1193 // maybe; so the client should empty
1194 // the outpipe before issuing a request.
1202 // Send a notify message to a client, called by WorkAreaKeyPress
1203 void Server::notifyClient(string const & s)
1205 pipes_.send("NOTIFY:" + s + "\n");
1212 #include "moc_Server.cpp"