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)
699 void LyXComm::openConnection()
703 void LyXComm::closeConnection()
707 int LyXComm::startPipe(string const & filename, bool write)
713 void LyXComm::endPipe(int & fd, string const & filename, bool write)
717 void LyXComm::emergencyCleanup()
721 void LyXComm::read_ready()
725 void LyXComm::send(string const & msg)
729 #else // defined (HAVE_MKFIFO)
731 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
732 : pipename_(pip), client_(cli), clientcb_(ccb)
735 deferred_loading_ = false;
740 void LyXComm::openConnection()
742 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
744 // If we are up, that's an error
746 lyxerr << "LyXComm: Already connected" << endl;
749 // We assume that we don't make it
752 if (pipename_.empty()) {
753 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
757 infd_ = startPipe(inPipeName(), false);
761 outfd_ = startPipe(outPipeName(), true);
763 endPipe(infd_, inPipeName(), false);
767 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
768 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
769 << '\n' << strerror(errno) << endl;
775 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
780 void LyXComm::closeConnection()
782 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
784 if (pipename_.empty()) {
785 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
790 LYXERR0("LyXComm: Already disconnected");
794 endPipe(infd_, inPipeName(), false);
795 endPipe(outfd_, outPipeName(), true);
801 int LyXComm::startPipe(string const & file, bool write)
803 static bool stalepipe = false;
804 FileName const filename(file);
805 if (filename.exists()) {
807 // Let's see whether we have a stale pipe.
808 int fd = ::open(filename.toFilesystemEncoding().c_str(),
809 O_WRONLY | O_NONBLOCK);
811 // Another LyX instance is using it.
813 // Tell the running instance to load the files
814 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
815 deferred_loading_ = true;
819 } else if (errno == ENXIO) {
820 // No process is reading from the other end.
822 LYXERR(Debug::LYXSERVER,
823 "LyXComm: trying to remove "
825 filename.removeFile();
827 } else if (stalepipe) {
828 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
830 filename.removeFile();
833 if (filename.exists()) {
834 lyxerr << "LyXComm: Pipe " << filename
835 << " already exists.\nIf no other LyX program"
836 " is active, please delete the pipe by hand"
844 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
845 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
846 << strerror(errno) << endl;
849 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
850 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
853 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
854 << strerror(errno) << endl;
855 filename.removeFile();
860 theApp()->registerSocketCallback(fd,
861 bind(&LyXComm::read_ready, this));
868 void LyXComm::endPipe(int & fd, string const & filename, bool write)
874 theApp()->unregisterSocketCallback(fd);
876 if (::close(fd) < 0) {
877 lyxerr << "LyXComm: Could not close pipe " << filename
878 << '\n' << strerror(errno) << endl;
881 if (FileName(filename).removeFile() < 0) {
882 lyxerr << "LyXComm: Could not remove pipe " << filename
883 << '\n' << strerror(errno) << endl;
890 void LyXComm::emergencyCleanup()
892 if (!pipename_.empty()) {
893 endPipe(infd_, inPipeName(), false);
894 endPipe(outfd_, outPipeName(), true);
899 // Receives messages and sends then to client
900 void LyXComm::read_ready()
902 // FIXME: make read_buffer_ a class-member for multiple sessions
903 static string read_buffer_;
904 read_buffer_.erase();
906 int const charbuf_size = 100;
907 char charbuf[charbuf_size];
909 // As O_NONBLOCK is set, until no data is available for reading,
910 // read() doesn't block but returns -1 and set errno to EAGAIN.
911 // After a client that opened the pipe for writing, closes it
912 // (and no other client is using the pipe), read() would always
913 // return 0 and thus the connection has to be reset.
917 // the single = is intended here.
918 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())
973 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
974 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
975 lyxerr << "LyXComm: Error sending message: " << msg
976 << '\n' << strerror(errno)
977 << "\nLyXComm: Resetting connection" << endl;
983 #endif // defined (HAVE_MKFIFO)
987 struct Sleep : QThread
989 static void millisec(unsigned long ms)
991 QThread::usleep(ms * 1000);
998 bool LyXComm::loadFilesInOtherInstance()
1001 int loaded_files = 0;
1002 FileName const pipe(inPipeName());
1003 vector<string>::iterator it = theFilesToLoad().begin();
1004 while (it != theFilesToLoad().end()) {
1005 FileName fname = fileSearch(string(), os::internal_path(*it),
1006 "lyx", may_not_exist);
1007 if (fname.empty()) {
1011 // Wait a while to allow time for the other
1012 // instance to reset the connection
1013 Sleep::millisec(200);
1014 pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
1017 string const cmd = "LYXCMD:pipe:file-open:" +
1018 fname.absFileName() + '\n';
1019 if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
1020 LYXERR0("Cannot write to pipe!");
1023 it = theFilesToLoad().erase(it);
1025 return loaded_files > 0;
1029 string const LyXComm::inPipeName() const
1031 return pipename_ + ".in";
1035 string const LyXComm::outPipeName() const
1037 return pipename_ + ".out";
1041 /////////////////////////////////////////////////////////////////////
1045 /////////////////////////////////////////////////////////////////////
1047 void ServerCallback(Server * server, string const & msg)
1049 server->callback(msg);
1052 Server::Server(string const & pipes)
1053 : numclients_(0), pipes_(pipes, this, &ServerCallback)
1059 // say goodbye to clients so they stop sending messages
1060 // send as many bye messages as there are clients,
1061 // each with client's name.
1063 for (int i = 0; i != numclients_; ++i) {
1064 message = "LYXSRV:" + clients_[i] + ":bye\n";
1065 pipes_.send(message);
1070 int compare(char const * a, char const * b, unsigned int len)
1072 return strncmp(a, b, len);
1076 // Handle data gotten from communication, called by LyXComm
1077 void Server::callback(string const & msg)
1079 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1081 char const * p = msg.c_str();
1083 // --- parse the string --------------------------------------------
1085 // Format: LYXCMD:<client>:<func>:<argstring>\n
1087 bool server_only = false;
1089 // --- 1. check 'header' ---
1091 if (compare(p, "LYXSRV:", 7) == 0) {
1093 } else if (0 != compare(p, "LYXCMD:", 7)) {
1094 lyxerr << "Server: Unknown request \""
1095 << p << '"' << endl;
1100 // --- 2. for the moment ignore the client name ---
1102 while (*p && *p != ':')
1103 client += char(*p++);
1109 // --- 3. get function name ---
1111 while (*p && *p != ':')
1114 // --- 4. parse the argument ---
1116 if (!server_only && *p == ':' && *(++p)) {
1117 while (*p && *p != '\n')
1122 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1123 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1125 // --- lookup and exec the command ------------------
1129 // return the greeting to inform the client that
1130 // we are listening.
1131 if (cmd == "hello") {
1133 if (numclients_ == MAX_CLIENTS) { //paranoid check
1134 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1138 while (!clients_[i].empty() && i < numclients_)
1140 clients_[i] = client;
1142 buf = "LYXSRV:" + client + ":hello\n";
1143 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1145 } else if (cmd == "bye") {
1146 // If clients_ == 0 maybe we should reset the pipes
1147 // to prevent fake callbacks
1148 int i = 0; //look if client is registered
1149 for (; i < numclients_; ++i) {
1150 if (clients_[i] == client)
1153 if (i < numclients_) {
1155 clients_[i].erase();
1156 LYXERR(Debug::LYXSERVER, "Server: Client "
1157 << client << " said goodbye");
1159 LYXERR(Debug::LYXSERVER,
1160 "Server: ignoring bye messge from unregistered client" << client);
1163 LYXERR0("Server: Undefined server command " << cmd << '.');
1169 // which lyxfunc should we let it connect to?
1170 // The correct solution would be to have a
1171 // specialized (non-gui) BufferView. But how do
1172 // we do it now? Probably we should just let it
1173 // connect to the lyxfunc in the single GuiView we
1174 // support currently. (Lgb)
1176 FuncRequest fr(lyxaction.lookupFunc(cmd), arg);
1177 fr.setOrigin(FuncRequest::LYXSERVER);
1179 theApp()->dispatch(fr, dr);
1180 string const rval = to_utf8(dr.message());
1182 // all commands produce an INFO or ERROR message
1183 // in the output pipe, even if they do not return
1184 // anything. See chapter 4 of Customization doc.
1190 buf += client + ':' + cmd + ':' + rval + '\n';
1193 // !!! we don't do any error checking -
1194 // if the client won't listen, the
1195 // message is lost and others too
1196 // maybe; so the client should empty
1197 // the outpipe before issuing a request.
1205 // Send a notify message to a client, called by WorkAreaKeyPress
1206 void Server::notifyClient(string const & s)
1208 pipes_.send("NOTIFY:" + s + "\n");
1215 #include "moc_Server.cpp"