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"
65 #include <QCoreApplication>
70 #ifdef HAVE_SYS_STAT_H
71 # include <sys/stat.h>
76 using namespace lyx::support;
77 using os::external_path;
81 /////////////////////////////////////////////////////////////////////
85 /////////////////////////////////////////////////////////////////////
89 class ReadReadyEvent : public QEvent {
92 ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
95 DWORD inpipe() const { return inpipe_; }
103 string errormsg(DWORD const error)
107 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
108 FORMAT_MESSAGE_FROM_SYSTEM |
109 FORMAT_MESSAGE_IGNORE_INSERTS,
111 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
112 (LPTSTR) &msgbuf, 0, NULL)) {
113 message = static_cast<char *>(msgbuf);
116 message = "Unknown error";
124 DWORD WINAPI pipeServerWrapper(void * arg)
126 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
127 if (!lyxcomm->pipeServer()) {
128 // Error exit; perform cleanup.
129 lyxcomm->ready_ = false;
130 lyxcomm->closeHandles();
131 CloseHandle(lyxcomm->server_thread_);
132 CloseHandle(lyxcomm->stopserver_);
133 CloseHandle(lyxcomm->outbuf_mutex_);
134 lyxerr << "LyXComm: Closing connection" << endl;
140 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
141 : stopserver_(0), pipename_(pip), client_(cli), clientcb_(ccb)
143 for (int i = 0; i < MAX_PIPES; ++i) {
145 pipe_[i].handle = INVALID_HANDLE_VALUE;
148 deferred_loading_ = false;
153 bool LyXComm::pipeServer()
158 for (i = 0; i < MAX_PIPES; ++i) {
159 bool const is_outpipe = i >= MAX_CLIENTS;
160 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
161 : PIPE_ACCESS_INBOUND;
162 string const pipename = external_path(pipeName(i));
164 // Manual-reset event, initial state = signaled
165 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
167 error = GetLastError();
168 lyxerr << "LyXComm: Could not create event for pipe "
169 << pipename << "\nLyXComm: "
170 << errormsg(error) << endl;
174 pipe_[i].overlap.hEvent = event_[i];
175 pipe_[i].iobuf.erase();
176 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
177 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
178 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
181 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
182 error = GetLastError();
183 lyxerr << "LyXComm: Could not create pipe "
184 << pipename << "\nLyXComm: "
185 << errormsg(error) << endl;
191 pipe_[i].state = pipe_[i].pending_io ?
192 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
196 // Add the stopserver_ event
197 event_[MAX_PIPES] = stopserver_;
200 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
206 while (!checkStopServer()) {
207 // Indefinitely wait for the completion of an overlapped
208 // read, write, or connect operation.
209 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
212 // Determine which pipe instance completed the operation.
213 i = wait - WAIT_OBJECT_0;
214 LASSERT(i <= MAX_PIPES, /**/);
216 // Check whether we were waked up for stopping the pipe server.
220 bool const is_outpipe = i >= MAX_CLIENTS;
222 // Get the result if the operation was pending.
223 if (pipe_[i].pending_io) {
224 success = GetOverlappedResult(pipe_[i].handle,
225 &pipe_[i].overlap, &status, FALSE);
227 switch (pipe_[i].state) {
228 case CONNECTING_STATE:
229 // Pending connect operation
231 error = GetLastError();
232 lyxerr << "LyXComm: "
233 << errormsg(error) << endl;
234 if (!resetPipe(i, true))
238 pipe_[i].state = is_outpipe ? WRITING_STATE
243 // Pending read operation
244 LASSERT(!is_outpipe, /**/);
245 if (!success || status == 0) {
246 if (!resetPipe(i, !success))
250 pipe_[i].nbytes = status;
251 pipe_[i].state = WRITING_STATE;
255 // Pending write operation
256 LASSERT(is_outpipe, /**/);
257 // Let's see whether we have a reply
258 if (!outbuf_.empty()) {
259 // Yep. Deliver it to all pipe
260 // instances if we get ownership
261 // of the mutex, otherwise we'll
262 // try again the next round.
263 DWORD result = WaitForSingleObject(
265 if (result == WAIT_OBJECT_0) {
266 DWORD j = MAX_CLIENTS;
267 while (j < MAX_PIPES) {
268 pipe_[j].iobuf = outbuf_;
273 ReleaseMutex(outbuf_mutex_);
275 if (pipe_[i].iobuf.empty())
276 pipe_[i].pending_io = false;
281 // Operate according to the pipe state
282 switch (pipe_[i].state) {
284 // The pipe instance is connected to a client
285 // and is ready to read a request.
286 LASSERT(!is_outpipe, /**/);
287 success = ReadFile(pipe_[i].handle,
288 pipe_[i].readbuf, PIPE_BUFSIZE - 1,
289 &pipe_[i].nbytes, &pipe_[i].overlap);
291 if (success && pipe_[i].nbytes != 0) {
292 // The read operation completed successfully.
293 pipe_[i].pending_io = false;
294 pipe_[i].state = WRITING_STATE;
298 error = GetLastError();
300 if (!success && error == ERROR_IO_PENDING) {
301 // The read operation is still pending.
302 pipe_[i].pending_io = true;
306 success = error == ERROR_BROKEN_PIPE;
308 // Client closed connection (ERROR_BROKEN_PIPE) or
309 // an error occurred; in either case, reset the pipe.
311 lyxerr << "LyXComm: " << errormsg(error) << endl;
312 if (!pipe_[i].iobuf.empty()) {
313 lyxerr << "LyXComm: truncated command: "
314 << pipe_[i].iobuf << endl;
315 pipe_[i].iobuf.erase();
318 if (!resetPipe(i, !success))
324 // The request was successfully read
325 // from the client; commit it.
326 ReadReadyEvent * event = new ReadReadyEvent(i);
327 QCoreApplication::postEvent(this,
328 static_cast<QEvent *>(event));
329 // Wait for completion
330 while (pipe_[i].nbytes && !checkStopServer(100))
332 pipe_[i].pending_io = false;
333 pipe_[i].state = READING_STATE;
337 // This is an output pipe instance. Initiate the
338 // overlapped write operation or monitor its progress.
340 if (pipe_[i].pending_io) {
341 success = WriteFile(pipe_[i].handle,
342 pipe_[i].iobuf.c_str(),
343 pipe_[i].iobuf.length(),
348 if (success && !pipe_[i].iobuf.empty()
349 && status == pipe_[i].iobuf.length()) {
350 // The write operation completed successfully.
351 pipe_[i].iobuf.erase();
352 pipe_[i].pending_io = false;
358 error = GetLastError();
360 if (success && error == ERROR_IO_PENDING) {
361 // The write operation is still pending.
362 // We get here when a reader is started
363 // well before a reply is ready, so delay
364 // a bit in order to not burden the cpu.
365 checkStopServer(100);
366 pipe_[i].pending_io = true;
370 success = error == ERROR_NO_DATA;
372 // Client closed connection (ERROR_NO_DATA) or
373 // an error occurred; in either case, reset the pipe.
375 lyxerr << "LyXComm: Error sending message: "
376 << pipe_[i].iobuf << "\nLyXComm: "
377 << errormsg(error) << endl;
379 if (!resetPipe(i, !success))
391 void LyXComm::closeHandles()
393 for (int i = 0; i < MAX_PIPES; ++i) {
395 ResetEvent(event_[i]);
396 CloseHandle(event_[i]);
399 if (pipe_[i].handle != INVALID_HANDLE_VALUE) {
400 CloseHandle(pipe_[i].handle);
401 pipe_[i].handle = INVALID_HANDLE_VALUE;
407 bool LyXComm::event(QEvent * e)
409 if (e->type() == QEvent::User) {
410 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
417 bool LyXComm::checkStopServer(DWORD timeout)
419 return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0;
423 bool LyXComm::startPipe(DWORD index)
425 pipe_[index].pending_io = false;
426 pipe_[index].overlap.Offset = 0;
427 pipe_[index].overlap.OffsetHigh = 0;
429 // Overlapped ConnectNamedPipe should return zero.
430 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
431 DWORD const error = GetLastError();
432 lyxerr << "LyXComm: Could not connect pipe "
433 << external_path(pipeName(index))
434 << "\nLyXComm: " << errormsg(error) << endl;
438 switch (GetLastError()) {
439 case ERROR_IO_PENDING:
440 // The overlapped connection is in progress.
441 pipe_[index].pending_io = true;
444 case ERROR_PIPE_CONNECTED:
445 // Client is already connected, so signal an event.
446 if (SetEvent(pipe_[index].overlap.hEvent))
450 // Anything else is an error.
451 DWORD const error = GetLastError();
452 lyxerr << "LyXComm: An error occurred while connecting pipe "
453 << external_path(pipeName(index))
454 << "\nLyXComm: " << errormsg(error) << endl;
462 bool LyXComm::resetPipe(DWORD index, bool close_handle)
464 // This method is called when an error occurs or when a client
465 // closes the connection. We first disconnect the pipe instance,
466 // then reconnect it, ready to wait for another client.
468 if (!DisconnectNamedPipe(pipe_[index].handle)) {
469 DWORD const error = GetLastError();
470 lyxerr << "LyXComm: Could not disconnect pipe "
471 << external_path(pipeName(index))
472 << "\nLyXComm: " << errormsg(error) << endl;
473 // What to do now? Let's try whether re-creating the pipe helps.
477 bool const is_outpipe = index >= MAX_CLIENTS;
480 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
481 : PIPE_ACCESS_INBOUND;
482 string const name = external_path(pipeName(index));
484 CloseHandle(pipe_[index].handle);
486 pipe_[index].iobuf.erase();
487 pipe_[index].handle = CreateNamedPipe(name.c_str(),
488 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
489 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
492 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
493 DWORD const error = GetLastError();
494 lyxerr << "LyXComm: Could not reset pipe " << name
495 << "\nLyXComm: " << errormsg(error) << endl;
500 if (!startPipe(index))
502 pipe_[index].state = pipe_[index].pending_io ?
503 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
509 void LyXComm::openConnection()
511 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
513 // If we are up, that's an error
515 LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
519 if (pipename_.empty()) {
520 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
524 // Check whether the pipe name is being used by some other instance.
525 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
526 // Tell the running instance to load the files
527 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
528 deferred_loading_ = true;
532 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
533 << " already exists.\nMaybe another instance of LyX"
534 " is using it." << endl;
539 // Mutex with no initial owner for synchronized access to outbuf_
540 outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
541 if (!outbuf_mutex_) {
542 DWORD const error = GetLastError();
543 lyxerr << "LyXComm: Could not create output buffer mutex"
544 << "\nLyXComm: " << errormsg(error) << endl;
549 // Manual-reset event, initial state = not signaled
550 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
552 DWORD const error = GetLastError();
553 lyxerr << "LyXComm: Could not create stop server event"
554 << "\nLyXComm: " << errormsg(error) << endl;
556 CloseHandle(outbuf_mutex_);
560 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
561 static_cast<void *>(this), 0, NULL);
562 if (!server_thread_) {
563 DWORD const error = GetLastError();
564 lyxerr << "LyXComm: Could not create pipe server thread"
565 << "\nLyXComm: " << errormsg(error) << endl;
567 CloseHandle(stopserver_);
568 CloseHandle(outbuf_mutex_);
575 void LyXComm::closeConnection()
577 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
579 if (pipename_.empty()) {
580 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
585 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
589 SetEvent(stopserver_);
590 // Wait for the pipe server to finish
591 WaitForSingleObject(server_thread_, INFINITE);
592 CloseHandle(server_thread_);
593 ResetEvent(stopserver_);
594 CloseHandle(stopserver_);
595 CloseHandle(outbuf_mutex_);
599 void LyXComm::emergencyCleanup()
602 SetEvent(stopserver_);
603 // Forcibly terminate the pipe server thread if it does
604 // not finish quickly.
605 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
606 TerminateThread(server_thread_, 0);
610 CloseHandle(server_thread_);
611 ResetEvent(stopserver_);
612 CloseHandle(stopserver_);
613 CloseHandle(outbuf_mutex_);
618 void LyXComm::read_ready(DWORD inpipe)
620 // Turn the pipe buffer into a C string
621 DWORD const nbytes = pipe_[inpipe].nbytes;
622 pipe_[inpipe].readbuf[nbytes] = '\0';
624 pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
626 // Commit any commands read
627 while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
628 // split() grabs the entire string if
629 // the delim /wasn't/ found. ?:-P
631 pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
632 cmd = rtrim(cmd, "\r");
633 LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
634 << ", iobuf:" << pipe_[inpipe].iobuf
637 clientcb_(client_, cmd);
640 // Signal that we are done.
641 pipe_[inpipe].nbytes = 0;
645 void LyXComm::send(string const & msg)
648 lyxerr << "LyXComm: Request to send empty string. Ignoring."
653 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
655 if (pipename_.empty())
659 lyxerr << "LyXComm: Pipes are closed. Could not send "
664 // Request ownership of the outbuf_mutex_
665 DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
667 if (result == WAIT_OBJECT_0) {
668 // If a client doesn't care to read a reply (JabRef is one
669 // such client), the output buffer could grow without limit.
670 // So, we empty it when its size is larger than PIPE_BUFSIZE.
671 if (outbuf_.size() > PIPE_BUFSIZE)
674 ReleaseMutex(outbuf_mutex_);
676 // Something is fishy, better resetting the connection.
677 DWORD const error = GetLastError();
678 lyxerr << "LyXComm: Error sending message: " << msg
679 << "\nLyXComm: " << errormsg(error)
680 << "\nLyXComm: Resetting connection" << endl;
681 ReleaseMutex(outbuf_mutex_);
688 string const LyXComm::pipeName(DWORD index) const
690 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
694 #elif !defined (HAVE_MKFIFO)
695 // We provide a stub class that disables the lyxserver.
697 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
701 void LyXComm::openConnection()
705 void LyXComm::closeConnection()
709 int LyXComm::startPipe(string const & filename, bool write)
715 void LyXComm::endPipe(int & fd, string const & filename, bool write)
719 void LyXComm::emergencyCleanup()
723 void LyXComm::read_ready()
727 void LyXComm::send(string const & msg)
731 #else // defined (HAVE_MKFIFO)
733 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
734 : pipename_(pip), client_(cli), clientcb_(ccb)
737 deferred_loading_ = false;
742 void LyXComm::openConnection()
744 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
746 // If we are up, that's an error
748 lyxerr << "LyXComm: Already connected" << endl;
751 // We assume that we don't make it
754 if (pipename_.empty()) {
755 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
759 infd_ = startPipe(inPipeName(), false);
763 outfd_ = startPipe(outPipeName(), true);
765 endPipe(infd_, inPipeName(), false);
769 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
770 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
771 << '\n' << strerror(errno) << endl;
777 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
782 void LyXComm::closeConnection()
784 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
786 if (pipename_.empty()) {
787 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
792 LYXERR0("LyXComm: Already disconnected");
796 endPipe(infd_, inPipeName(), false);
797 endPipe(outfd_, outPipeName(), true);
803 int LyXComm::startPipe(string const & file, bool write)
805 static bool stalepipe = false;
806 FileName const filename(file);
807 if (filename.exists()) {
809 // Let's see whether we have a stale pipe.
810 int fd = ::open(filename.toFilesystemEncoding().c_str(),
811 O_WRONLY | O_NONBLOCK);
813 // Another LyX instance is using it.
815 // Tell the running instance to load the files
816 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
817 deferred_loading_ = true;
821 } else if (errno == ENXIO) {
822 // No process is reading from the other end.
824 LYXERR(Debug::LYXSERVER,
825 "LyXComm: trying to remove "
827 filename.removeFile();
829 } else if (stalepipe) {
830 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
832 filename.removeFile();
835 if (filename.exists()) {
836 lyxerr << "LyXComm: Pipe " << filename
837 << " already exists.\nIf no other LyX program"
838 " is active, please delete the pipe by hand"
846 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
847 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
848 << strerror(errno) << endl;
851 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
852 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
855 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
856 << strerror(errno) << endl;
857 filename.removeFile();
862 theApp()->registerSocketCallback(fd,
863 bind(&LyXComm::read_ready, this));
870 void LyXComm::endPipe(int & fd, string const & filename, bool write)
876 theApp()->unregisterSocketCallback(fd);
878 if (::close(fd) < 0) {
879 lyxerr << "LyXComm: Could not close pipe " << filename
880 << '\n' << strerror(errno) << endl;
883 if (!FileName(filename).removeFile()) {
884 lyxerr << "LyXComm: Could not remove pipe " << filename
885 << '\n' << strerror(errno) << endl;
892 void LyXComm::emergencyCleanup()
894 if (!pipename_.empty()) {
895 endPipe(infd_, inPipeName(), false);
896 endPipe(outfd_, outPipeName(), true);
901 // Receives messages and sends then to client
902 void LyXComm::read_ready()
904 // FIXME: make read_buffer_ a class-member for multiple sessions
905 static string read_buffer_;
906 read_buffer_.erase();
908 int const charbuf_size = 100;
909 char charbuf[charbuf_size];
911 // As O_NONBLOCK is set, until no data is available for reading,
912 // read() doesn't block but returns -1 and set errno to EAGAIN.
913 // After a client that opened the pipe for writing, closes it
914 // (and no other client is using the pipe), read() would always
915 // return 0 and thus the connection has to be reset.
919 // the single = is intended here.
920 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
922 charbuf[status] = '\0'; // turn it into a c string
923 read_buffer_ += rtrim(charbuf, "\r");
924 // commit any commands read
925 while (read_buffer_.find('\n') != string::npos) {
926 // split() grabs the entire string if
927 // the delim /wasn't/ found. ?:-P
929 read_buffer_= split(read_buffer_, cmd,'\n');
930 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
931 << ", read_buffer_:" << read_buffer_
934 clientcb_(client_, cmd);
938 if (errno == EAGAIN) {
939 // Nothing to read, continue
943 // An error occurred, better bailing out
944 LYXERR0("LyXComm: " << strerror(errno));
945 if (!read_buffer_.empty()) {
946 LYXERR0("LyXComm: truncated command: " << read_buffer_);
947 read_buffer_.erase();
949 break; // reset connection
953 // The connection gets reset when read() returns 0 (meaning that the
954 // last client closed the pipe) or an error occurred, in which case
955 // read() returns -1 and errno != EAGAIN.
962 void LyXComm::send(string const & msg)
965 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
969 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
971 if (pipename_.empty())
975 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
976 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
977 lyxerr << "LyXComm: Error sending message: " << msg
978 << '\n' << strerror(errno)
979 << "\nLyXComm: Resetting connection" << endl;
985 #endif // defined (HAVE_MKFIFO)
989 struct Sleep : QThread
991 static void millisec(unsigned long ms)
993 QThread::usleep(ms * 1000);
1000 bool LyXComm::loadFilesInOtherInstance()
1003 int loaded_files = 0;
1004 FileName const pipe(inPipeName());
1005 vector<string>::iterator it = theFilesToLoad().begin();
1006 while (it != theFilesToLoad().end()) {
1007 FileName fname = fileSearch(string(), os::internal_path(*it),
1008 "lyx", may_not_exist);
1009 if (fname.empty()) {
1013 // Wait a while to allow time for the other
1014 // instance to reset the connection
1015 Sleep::millisec(200);
1016 pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
1019 string const cmd = "LYXCMD:pipe:file-open:" +
1020 fname.absFileName() + '\n';
1021 if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
1022 LYXERR0("Cannot write to pipe!");
1025 it = theFilesToLoad().erase(it);
1027 return loaded_files > 0;
1031 string const LyXComm::inPipeName() const
1033 return pipename_ + ".in";
1037 string const LyXComm::outPipeName() const
1039 return pipename_ + ".out";
1043 /////////////////////////////////////////////////////////////////////
1047 /////////////////////////////////////////////////////////////////////
1049 void ServerCallback(Server * server, string const & msg)
1051 server->callback(msg);
1054 Server::Server(string const & pipes)
1055 : numclients_(0), pipes_(pipes, this, &ServerCallback)
1061 // say goodbye to clients so they stop sending messages
1062 // send as many bye messages as there are clients,
1063 // each with client's name.
1065 for (int i = 0; i != numclients_; ++i) {
1066 message = "LYXSRV:" + clients_[i] + ":bye\n";
1067 pipes_.send(message);
1072 int compare(char const * a, char const * b, unsigned int len)
1074 return strncmp(a, b, len);
1078 // Handle data gotten from communication, called by LyXComm
1079 void Server::callback(string const & msg)
1081 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1083 char const * p = msg.c_str();
1085 // --- parse the string --------------------------------------------
1087 // Format: LYXCMD:<client>:<func>:<argstring>\n
1089 bool server_only = false;
1091 // --- 1. check 'header' ---
1093 if (compare(p, "LYXSRV:", 7) == 0) {
1095 } else if (0 != compare(p, "LYXCMD:", 7)) {
1096 lyxerr << "Server: Unknown request \""
1097 << p << '"' << endl;
1102 // --- 2. for the moment ignore the client name ---
1104 while (*p && *p != ':')
1105 client += char(*p++);
1111 // --- 3. get function name ---
1113 while (*p && *p != ':')
1116 // --- 4. parse the argument ---
1118 if (!server_only && *p == ':' && *(++p)) {
1119 while (*p && *p != '\n')
1124 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1125 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1127 // --- lookup and exec the command ------------------
1131 // return the greeting to inform the client that
1132 // we are listening.
1133 if (cmd == "hello") {
1135 if (numclients_ == MAX_CLIENTS) { //paranoid check
1136 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1140 while (!clients_[i].empty() && i < numclients_)
1142 clients_[i] = client;
1144 buf = "LYXSRV:" + client + ":hello\n";
1145 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1147 } else if (cmd == "bye") {
1148 // If clients_ == 0 maybe we should reset the pipes
1149 // to prevent fake callbacks
1150 int i = 0; //look if client is registered
1151 for (; i < numclients_; ++i) {
1152 if (clients_[i] == client)
1155 if (i < numclients_) {
1157 clients_[i].erase();
1158 LYXERR(Debug::LYXSERVER, "Server: Client "
1159 << client << " said goodbye");
1161 LYXERR(Debug::LYXSERVER,
1162 "Server: ignoring bye messge from unregistered client" << client);
1165 LYXERR0("Server: Undefined server command " << cmd << '.');
1171 // which lyxfunc should we let it connect to?
1172 // The correct solution would be to have a
1173 // specialized (non-gui) BufferView. But how do
1174 // we do it now? Probably we should just let it
1175 // connect to the lyxfunc in the single GuiView we
1176 // support currently. (Lgb)
1178 FuncRequest fr(lyxaction.lookupFunc(cmd), arg);
1179 fr.setOrigin(FuncRequest::LYXSERVER);
1181 theApp()->dispatch(fr, dr);
1182 string const rval = to_utf8(dr.message());
1184 // all commands produce an INFO or ERROR message
1185 // in the output pipe, even if they do not return
1186 // anything. See chapter 4 of Customization doc.
1192 buf += client + ':' + cmd + ':' + rval + '\n';
1195 // !!! we don't do any error checking -
1196 // if the client won't listen, the
1197 // message is lost and others too
1198 // maybe; so the client should empty
1199 // the outpipe before issuing a request.
1207 // Send a notify message to a client, called by WorkAreaKeyPress
1208 void Server::notifyClient(string const & s)
1210 pipes_.send("NOTIFY:" + s + "\n");
1217 #include "moc_Server.cpp"