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"
63 #include <QCoreApplication>
68 #ifdef HAVE_SYS_STAT_H
69 # include <sys/stat.h>
74 using namespace lyx::support;
75 using os::external_path;
79 /////////////////////////////////////////////////////////////////////
83 /////////////////////////////////////////////////////////////////////
87 class ReadReadyEvent : public QEvent {
90 ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
93 DWORD inpipe() const { return inpipe_; }
101 string errormsg(DWORD const error)
105 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
106 FORMAT_MESSAGE_FROM_SYSTEM |
107 FORMAT_MESSAGE_IGNORE_INSERTS,
109 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
110 (LPTSTR) &msgbuf, 0, NULL)) {
111 message = static_cast<char *>(msgbuf);
114 message = "Unknown error";
122 DWORD WINAPI pipeServerWrapper(void * arg)
124 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
125 if (!lyxcomm->pipeServer()) {
126 // Error exit; perform cleanup.
127 lyxcomm->ready_ = false;
128 lyxcomm->closeHandles();
129 CloseHandle(lyxcomm->server_thread_);
130 CloseHandle(lyxcomm->stopserver_);
131 CloseHandle(lyxcomm->outbuf_mutex_);
132 lyxerr << "LyXComm: Closing connection" << endl;
138 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
139 : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
141 for (int i = 0; i < MAX_PIPES; ++i) {
143 pipe_[i].handle = INVALID_HANDLE_VALUE;
146 deferred_loading_ = false;
151 bool LyXComm::pipeServer()
156 for (i = 0; i < MAX_PIPES; ++i) {
157 bool const is_outpipe = i >= MAX_CLIENTS;
158 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
159 : PIPE_ACCESS_INBOUND;
160 string const pipename = external_path(pipeName(i));
162 // Manual-reset event, initial state = signaled
163 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
165 error = GetLastError();
166 lyxerr << "LyXComm: Could not create event for pipe "
167 << pipename << "\nLyXComm: "
168 << errormsg(error) << endl;
172 pipe_[i].overlap.hEvent = event_[i];
173 pipe_[i].iobuf.erase();
174 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
175 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
176 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
179 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
180 error = GetLastError();
181 lyxerr << "LyXComm: Could not create pipe "
182 << pipename << "\nLyXComm: "
183 << errormsg(error) << endl;
189 pipe_[i].state = pipe_[i].pending_io ?
190 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
194 // Add the stopserver_ event
195 event_[MAX_PIPES] = stopserver_;
198 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
204 while (!checkStopServer()) {
205 // Indefinitely wait for the completion of an overlapped
206 // read, write, or connect operation.
207 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
210 // Determine which pipe instance completed the operation.
211 i = wait - WAIT_OBJECT_0;
212 LASSERT(i >= 0 && i <= MAX_PIPES, /**/);
214 // Check whether we were waked up for stopping the pipe server.
218 bool const is_outpipe = i >= MAX_CLIENTS;
220 // Get the result if the operation was pending.
221 if (pipe_[i].pending_io) {
222 success = GetOverlappedResult(pipe_[i].handle,
223 &pipe_[i].overlap, &status, FALSE);
225 switch (pipe_[i].state) {
226 case CONNECTING_STATE:
227 // Pending connect operation
229 error = GetLastError();
230 lyxerr << "LyXComm: "
231 << errormsg(error) << endl;
232 if (!resetPipe(i, true))
236 pipe_[i].state = is_outpipe ? WRITING_STATE
241 // Pending read operation
242 LASSERT(!is_outpipe, /**/);
243 if (!success || status == 0) {
244 if (!resetPipe(i, !success))
248 pipe_[i].nbytes = status;
249 pipe_[i].state = WRITING_STATE;
253 // Pending write operation
254 LASSERT(is_outpipe, /**/);
255 // Let's see whether we have a reply
256 if (!outbuf_.empty()) {
257 // Yep. Deliver it to all pipe
258 // instances if we get ownership
259 // of the mutex, otherwise we'll
260 // try again the next round.
261 DWORD result = WaitForSingleObject(
263 if (result == WAIT_OBJECT_0) {
264 DWORD j = MAX_CLIENTS;
265 while (j < MAX_PIPES) {
266 pipe_[j].iobuf = outbuf_;
271 ReleaseMutex(outbuf_mutex_);
273 if (pipe_[i].iobuf.empty())
274 pipe_[i].pending_io = false;
279 // Operate according to the pipe state
280 switch (pipe_[i].state) {
282 // The pipe instance is connected to a client
283 // and is ready to read a request.
284 LASSERT(!is_outpipe, /**/);
285 success = ReadFile(pipe_[i].handle,
286 pipe_[i].readbuf, PIPE_BUFSIZE - 1,
287 &pipe_[i].nbytes, &pipe_[i].overlap);
289 if (success && pipe_[i].nbytes != 0) {
290 // The read operation completed successfully.
291 pipe_[i].pending_io = false;
292 pipe_[i].state = WRITING_STATE;
296 error = GetLastError();
298 if (!success && error == ERROR_IO_PENDING) {
299 // The read operation is still pending.
300 pipe_[i].pending_io = true;
304 success = error == ERROR_BROKEN_PIPE;
306 // Client closed connection (ERROR_BROKEN_PIPE) or
307 // an error occurred; in either case, reset the pipe.
309 lyxerr << "LyXComm: " << errormsg(error) << endl;
310 if (!pipe_[i].iobuf.empty()) {
311 lyxerr << "LyXComm: truncated command: "
312 << pipe_[i].iobuf << endl;
313 pipe_[i].iobuf.erase();
316 if (!resetPipe(i, !success))
322 // The request was successfully read
323 // from the client; commit it.
324 ReadReadyEvent * event = new ReadReadyEvent(i);
325 QCoreApplication::postEvent(this,
326 static_cast<QEvent *>(event));
327 // Wait for completion
328 while (pipe_[i].nbytes && !checkStopServer(100))
330 pipe_[i].pending_io = false;
331 pipe_[i].state = READING_STATE;
335 // This is an output pipe instance. Initiate the
336 // overlapped write operation or monitor its progress.
338 if (pipe_[i].pending_io) {
339 success = WriteFile(pipe_[i].handle,
340 pipe_[i].iobuf.c_str(),
341 pipe_[i].iobuf.length(),
346 if (success && !pipe_[i].iobuf.empty()
347 && status == pipe_[i].iobuf.length()) {
348 // The write operation completed successfully.
349 pipe_[i].iobuf.erase();
350 pipe_[i].pending_io = false;
356 error = GetLastError();
358 if (success && error == ERROR_IO_PENDING) {
359 // The write operation is still pending.
360 // We get here when a reader is started
361 // well before a reply is ready, so delay
362 // a bit in order to not burden the cpu.
363 checkStopServer(100);
364 pipe_[i].pending_io = true;
368 success = error == ERROR_NO_DATA;
370 // Client closed connection (ERROR_NO_DATA) or
371 // an error occurred; in either case, reset the pipe.
373 lyxerr << "LyXComm: Error sending message: "
374 << pipe_[i].iobuf << "\nLyXComm: "
375 << errormsg(error) << endl;
377 if (!resetPipe(i, !success))
389 void LyXComm::closeHandles()
391 for (int i = 0; i < MAX_PIPES; ++i) {
393 ResetEvent(event_[i]);
394 CloseHandle(event_[i]);
397 if (pipe_[i].handle != INVALID_HANDLE_VALUE) {
398 CloseHandle(pipe_[i].handle);
399 pipe_[i].handle = INVALID_HANDLE_VALUE;
405 bool LyXComm::event(QEvent * e)
407 if (e->type() == QEvent::User) {
408 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
415 bool LyXComm::checkStopServer(DWORD timeout)
417 return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0;
421 bool LyXComm::startPipe(DWORD index)
423 pipe_[index].pending_io = false;
424 pipe_[index].overlap.Offset = 0;
425 pipe_[index].overlap.OffsetHigh = 0;
427 // Overlapped ConnectNamedPipe should return zero.
428 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
429 DWORD const error = GetLastError();
430 lyxerr << "LyXComm: Could not connect pipe "
431 << external_path(pipeName(index))
432 << "\nLyXComm: " << errormsg(error) << endl;
436 switch (GetLastError()) {
437 case ERROR_IO_PENDING:
438 // The overlapped connection is in progress.
439 pipe_[index].pending_io = true;
442 case ERROR_PIPE_CONNECTED:
443 // Client is already connected, so signal an event.
444 if (SetEvent(pipe_[index].overlap.hEvent))
448 // Anything else is an error.
449 DWORD const error = GetLastError();
450 lyxerr << "LyXComm: An error occurred while connecting pipe "
451 << external_path(pipeName(index))
452 << "\nLyXComm: " << errormsg(error) << endl;
460 bool LyXComm::resetPipe(DWORD index, bool close_handle)
462 // This method is called when an error occurs or when a client
463 // closes the connection. We first disconnect the pipe instance,
464 // then reconnect it, ready to wait for another client.
466 if (!DisconnectNamedPipe(pipe_[index].handle)) {
467 DWORD const error = GetLastError();
468 lyxerr << "LyXComm: Could not disconnect pipe "
469 << external_path(pipeName(index))
470 << "\nLyXComm: " << errormsg(error) << endl;
471 // What to do now? Let's try whether re-creating the pipe helps.
475 bool const is_outpipe = index >= MAX_CLIENTS;
478 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
479 : PIPE_ACCESS_INBOUND;
480 string const name = external_path(pipeName(index));
482 CloseHandle(pipe_[index].handle);
484 pipe_[index].iobuf.erase();
485 pipe_[index].handle = CreateNamedPipe(name.c_str(),
486 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
487 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
490 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
491 DWORD const error = GetLastError();
492 lyxerr << "LyXComm: Could not reset pipe " << name
493 << "\nLyXComm: " << errormsg(error) << endl;
498 if (!startPipe(index))
500 pipe_[index].state = pipe_[index].pending_io ?
501 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
507 void LyXComm::openConnection()
509 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
511 // If we are up, that's an error
513 LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
517 if (pipename_.empty()) {
518 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
522 // Check whether the pipe name is being used by some other instance.
523 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
524 // Tell the running instance to load the files
525 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
526 deferred_loading_ = true;
530 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
531 << " already exists.\nMaybe another instance of LyX"
532 " is using it." << endl;
537 // Mutex with no initial owner for synchronized access to outbuf_
538 outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
539 if (!outbuf_mutex_) {
540 DWORD const error = GetLastError();
541 lyxerr << "LyXComm: Could not create output buffer mutex"
542 << "\nLyXComm: " << errormsg(error) << endl;
547 // Manual-reset event, initial state = not signaled
548 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
550 DWORD const error = GetLastError();
551 lyxerr << "LyXComm: Could not create stop server event"
552 << "\nLyXComm: " << errormsg(error) << endl;
554 CloseHandle(outbuf_mutex_);
558 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
559 static_cast<void *>(this), 0, NULL);
560 if (!server_thread_) {
561 DWORD const error = GetLastError();
562 lyxerr << "LyXComm: Could not create pipe server thread"
563 << "\nLyXComm: " << errormsg(error) << endl;
565 CloseHandle(stopserver_);
566 CloseHandle(outbuf_mutex_);
573 void LyXComm::closeConnection()
575 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
577 if (pipename_.empty()) {
578 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
583 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
587 SetEvent(stopserver_);
588 // Wait for the pipe server to finish
589 WaitForSingleObject(server_thread_, INFINITE);
590 CloseHandle(server_thread_);
591 ResetEvent(stopserver_);
592 CloseHandle(stopserver_);
593 CloseHandle(outbuf_mutex_);
597 void LyXComm::emergencyCleanup()
600 SetEvent(stopserver_);
601 // Forcibly terminate the pipe server thread if it does
602 // not finish quickly.
603 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
604 TerminateThread(server_thread_, 0);
608 CloseHandle(server_thread_);
609 ResetEvent(stopserver_);
610 CloseHandle(stopserver_);
611 CloseHandle(outbuf_mutex_);
616 void LyXComm::read_ready(DWORD inpipe)
618 // Turn the pipe buffer into a C string
619 DWORD const nbytes = pipe_[inpipe].nbytes;
620 pipe_[inpipe].readbuf[nbytes] = '\0';
622 pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
624 // Commit any commands read
625 while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
626 // split() grabs the entire string if
627 // the delim /wasn't/ found. ?:-P
629 pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
630 cmd = rtrim(cmd, "\r");
631 LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
632 << ", iobuf:" << pipe_[inpipe].iobuf
635 clientcb_(client_, cmd);
638 // Signal that we are done.
639 pipe_[inpipe].nbytes = 0;
643 void LyXComm::send(string const & msg)
646 lyxerr << "LyXComm: Request to send empty string. Ignoring."
651 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
653 if (pipename_.empty())
657 lyxerr << "LyXComm: Pipes are closed. Could not send "
662 // Request ownership of the outbuf_mutex_
663 DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
665 if (result == WAIT_OBJECT_0) {
666 // If a client doesn't care to read a reply (JabRef is one
667 // such client), the output buffer could grow without limit.
668 // So, we empty it when its size is larger than PIPE_BUFSIZE.
669 if (outbuf_.size() > PIPE_BUFSIZE)
672 ReleaseMutex(outbuf_mutex_);
674 // Something is fishy, better resetting the connection.
675 DWORD const error = GetLastError();
676 lyxerr << "LyXComm: Error sending message: " << msg
677 << "\nLyXComm: " << errormsg(error)
678 << "\nLyXComm: Resetting connection" << endl;
679 ReleaseMutex(outbuf_mutex_);
686 string const LyXComm::pipeName(DWORD index) const
688 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
692 #elif !defined (HAVE_MKFIFO)
693 // We provide a stub class that disables the lyxserver.
695 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
698 void LyXComm::openConnection()
702 void LyXComm::closeConnection()
706 int LyXComm::startPipe(string const & filename, bool write)
712 void LyXComm::endPipe(int & fd, string const & filename, bool write)
716 void LyXComm::emergencyCleanup()
719 void LyXComm::read_ready()
723 void LyXComm::send(string const & msg)
727 #else // defined (HAVE_MKFIFO)
730 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
731 : pipename_(pip), client_(cli), clientcb_(ccb)
734 deferred_loading_ = false;
739 void LyXComm::openConnection()
741 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
743 // If we are up, that's an error
745 lyxerr << "LyXComm: Already connected" << endl;
748 // We assume that we don't make it
751 if (pipename_.empty()) {
752 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
756 infd_ = startPipe(inPipeName(), false);
760 outfd_ = startPipe(outPipeName(), true);
762 endPipe(infd_, inPipeName(), false);
766 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
767 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
768 << '\n' << strerror(errno) << endl;
774 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
779 void LyXComm::closeConnection()
781 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
783 if (pipename_.empty()) {
784 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
789 LYXERR0("LyXComm: Already disconnected");
793 endPipe(infd_, inPipeName(), false);
794 endPipe(outfd_, outPipeName(), true);
800 int LyXComm::startPipe(string const & file, bool write)
802 static bool stalepipe = false;
803 FileName const filename(file);
804 if (filename.exists()) {
806 // Let's see whether we have a stale pipe.
807 int fd = ::open(filename.toFilesystemEncoding().c_str(),
808 O_WRONLY | O_NONBLOCK);
810 // Another LyX instance is using it.
812 // Tell the running instance to load the files
813 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
814 deferred_loading_ = true;
818 } else if (errno == ENXIO) {
819 // No process is reading from the other end.
821 LYXERR(Debug::LYXSERVER,
822 "LyXComm: trying to remove "
824 filename.removeFile();
826 } else if (stalepipe) {
827 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
829 filename.removeFile();
832 if (filename.exists()) {
833 lyxerr << "LyXComm: Pipe " << filename
834 << " already exists.\nIf no other LyX program"
835 " is active, please delete the pipe by hand"
843 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
844 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
845 << strerror(errno) << endl;
848 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
849 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
852 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
853 << strerror(errno) << endl;
854 filename.removeFile();
859 theApp()->registerSocketCallback(fd,
860 bind(&LyXComm::read_ready, this));
867 void LyXComm::endPipe(int & fd, string const & filename, bool write)
873 theApp()->unregisterSocketCallback(fd);
875 if (::close(fd) < 0) {
876 lyxerr << "LyXComm: Could not close pipe " << filename
877 << '\n' << strerror(errno) << endl;
880 if (FileName(filename).removeFile() < 0) {
881 lyxerr << "LyXComm: Could not remove pipe " << filename
882 << '\n' << strerror(errno) << endl;
889 void LyXComm::emergencyCleanup()
891 if (!pipename_.empty()) {
892 endPipe(infd_, inPipeName(), false);
893 endPipe(outfd_, outPipeName(), true);
898 // Receives messages and sends then to client
899 void LyXComm::read_ready()
901 // FIXME: make read_buffer_ a class-member for multiple sessions
902 static string read_buffer_;
903 read_buffer_.erase();
905 int const charbuf_size = 100;
906 char charbuf[charbuf_size];
908 // As O_NONBLOCK is set, until no data is available for reading,
909 // read() doesn't block but returns -1 and set errno to EAGAIN.
910 // After a client that opened the pipe for writing, closes it
911 // (and no other client is using the pipe), read() would always
912 // return 0 and thus the connection has to be reset.
916 // the single = is intended here.
917 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
920 charbuf[status] = '\0'; // turn it into a c string
921 read_buffer_ += rtrim(charbuf, "\r");
922 // commit any commands read
923 while (read_buffer_.find('\n') != string::npos) {
924 // split() grabs the entire string if
925 // the delim /wasn't/ found. ?:-P
927 read_buffer_= split(read_buffer_, cmd,'\n');
928 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
929 << ", read_buffer_:" << read_buffer_
932 clientcb_(client_, cmd);
936 if (errno == EAGAIN) {
937 // Nothing to read, continue
941 // An error occurred, better bailing out
942 LYXERR0("LyXComm: " << strerror(errno));
943 if (!read_buffer_.empty()) {
944 LYXERR0("LyXComm: truncated command: " << read_buffer_);
945 read_buffer_.erase();
947 break; // reset connection
951 // The connection gets reset when read() returns 0 (meaning that the
952 // last client closed the pipe) or an error occurred, in which case
953 // read() returns -1 and errno != EAGAIN.
960 void LyXComm::send(string const & msg)
963 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
967 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
969 if (pipename_.empty()) return;
972 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
973 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
974 lyxerr << "LyXComm: Error sending message: " << msg
975 << '\n' << strerror(errno)
976 << "\nLyXComm: Resetting connection" << endl;
982 #endif // defined (HAVE_MKFIFO)
986 struct Sleep : QThread
988 static void millisec(unsigned long ms)
990 QThread::usleep(ms * 1000);
997 bool LyXComm::loadFilesInOtherInstance()
1000 int loaded_files = 0;
1001 FileName const pipe(inPipeName());
1002 vector<string>::iterator it = theFilesToLoad().begin();
1003 while (it != theFilesToLoad().end()) {
1004 FileName fname = fileSearch(string(), os::internal_path(*it),
1005 "lyx", may_not_exist);
1006 if (fname.empty()) {
1010 // Wait a while to allow time for the other
1011 // instance to reset the connection
1012 Sleep::millisec(200);
1013 pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
1016 string const cmd = "LYXCMD:pipe:file-open:" +
1017 fname.absFileName() + '\n';
1018 ::write(pipefd, cmd.c_str(), cmd.length());
1021 it = theFilesToLoad().erase(it);
1023 return loaded_files > 0;
1027 string const LyXComm::inPipeName() const
1029 return pipename_ + ".in";
1033 string const LyXComm::outPipeName() const
1035 return pipename_ + ".out";
1039 /////////////////////////////////////////////////////////////////////
1043 /////////////////////////////////////////////////////////////////////
1045 void ServerCallback(Server * server, string const & msg)
1047 server->callback(msg);
1050 Server::Server(string const & pipes)
1051 : numclients_(0), pipes_(pipes, this, &ServerCallback)
1057 // say goodbye to clients so they stop sending messages
1058 // send as many bye messages as there are clients,
1059 // each with client's name.
1061 for (int i = 0; i != numclients_; ++i) {
1062 message = "LYXSRV:" + clients_[i] + ":bye\n";
1063 pipes_.send(message);
1068 int compare(char const * a, char const * b, unsigned int len)
1070 using namespace std;
1071 return strncmp(a, b, len);
1075 // Handle data gotten from communication, called by LyXComm
1076 void Server::callback(string const & msg)
1078 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1080 char const * p = msg.c_str();
1082 // --- parse the string --------------------------------------------
1084 // Format: LYXCMD:<client>:<func>:<argstring>\n
1086 bool server_only = false;
1088 // --- 1. check 'header' ---
1090 if (compare(p, "LYXSRV:", 7) == 0) {
1092 } else if (0 != compare(p, "LYXCMD:", 7)) {
1093 lyxerr << "Server: Unknown request \""
1094 << p << '"' << endl;
1099 // --- 2. for the moment ignore the client name ---
1101 while (*p && *p != ':')
1102 client += char(*p++);
1108 // --- 3. get function name ---
1110 while (*p && *p != ':')
1113 // --- 4. parse the argument ---
1115 if (!server_only && *p == ':' && *(++p)) {
1116 while (*p && *p != '\n')
1121 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1122 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1124 // --- lookup and exec the command ------------------
1128 // return the greeting to inform the client that
1129 // we are listening.
1130 if (cmd == "hello") {
1132 if (numclients_ == MAX_CLIENTS) { //paranoid check
1133 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1137 while (!clients_[i].empty() && i < numclients_)
1139 clients_[i] = client;
1141 buf = "LYXSRV:" + client + ":hello\n";
1142 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1144 } else if (cmd == "bye") {
1145 // If clients_ == 0 maybe we should reset the pipes
1146 // to prevent fake callbacks
1147 int i = 0; //look if client is registered
1148 for (; i < numclients_; ++i) {
1149 if (clients_[i] == client)
1152 if (i < numclients_) {
1154 clients_[i].erase();
1155 LYXERR(Debug::LYXSERVER, "Server: Client "
1156 << client << " said goodbye");
1158 LYXERR(Debug::LYXSERVER,
1159 "Server: ignoring bye messge from unregistered client" << client);
1162 LYXERR0("Server: Undefined server command " << cmd << '.');
1168 // which lyxfunc should we let it connect to?
1169 // The correct solution would be to have a
1170 // specialized (non-gui) BufferView. But how do
1171 // we do it now? Probably we should just let it
1172 // connect to the lyxfunc in the single GuiView we
1173 // support currently. (Lgb)
1175 FuncRequest const fr(lyxaction.lookupFunc(cmd), arg);
1177 theApp()->dispatch(fr, dr);
1178 string const rval = to_utf8(dr.message());
1180 // all commands produce an INFO or ERROR message
1181 // in the output pipe, even if they do not return
1182 // anything. See chapter 4 of Customization doc.
1188 buf += client + ':' + cmd + ':' + rval + '\n';
1191 // !!! we don't do any error checking -
1192 // if the client won't listen, the
1193 // message is lost and others too
1194 // maybe; so the client should empty
1195 // the outpipe before issuing a request.
1203 // Send a notify message to a client, called by WorkAreaKeyPress
1204 void Server::notifyClient(string const & s)
1206 pipes_.send("NOTIFY:" + s + "\n");
1213 #include "moc_Server.cpp"