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"
58 #include "support/signals.h"
64 #include <QCoreApplication>
69 #ifdef HAVE_SYS_STAT_H
70 # include <sys/stat.h>
75 using namespace lyx::support;
76 using os::external_path;
80 /////////////////////////////////////////////////////////////////////
84 /////////////////////////////////////////////////////////////////////
88 class ReadReadyEvent : public QEvent {
91 ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
94 DWORD inpipe() const { return inpipe_; }
102 string errormsg(DWORD const error)
106 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
107 FORMAT_MESSAGE_FROM_SYSTEM |
108 FORMAT_MESSAGE_IGNORE_INSERTS,
110 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
111 (LPTSTR) &msgbuf, 0, NULL)) {
112 message = static_cast<char *>(msgbuf);
115 message = "Unknown error";
123 DWORD WINAPI pipeServerWrapper(void * arg)
125 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
126 if (!lyxcomm->pipeServer()) {
127 // Error exit; perform cleanup.
128 lyxcomm->ready_ = false;
129 lyxcomm->closeHandles();
130 CloseHandle(lyxcomm->server_thread_);
131 CloseHandle(lyxcomm->stopserver_);
132 CloseHandle(lyxcomm->outbuf_mutex_);
133 lyxerr << "LyXComm: Closing connection" << endl;
139 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
141 ready_(false), pipename_(pip), client_(cli), clientcb_(ccb),
142 deferred_loading_(false)
144 for (int i = 0; i < MAX_PIPES; ++i) {
146 pipe_[i].handle = INVALID_HANDLE_VALUE;
152 bool LyXComm::pipeServer()
157 for (i = 0; i < MAX_PIPES; ++i) {
158 bool const is_outpipe = i >= MAX_CLIENTS;
159 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
160 : PIPE_ACCESS_INBOUND;
161 string const pipename = external_path(pipeName(i));
163 // Manual-reset event, initial state = signaled
164 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
166 error = GetLastError();
167 lyxerr << "LyXComm: Could not create event for pipe "
168 << pipename << "\nLyXComm: "
169 << errormsg(error) << endl;
173 pipe_[i].overlap.hEvent = event_[i];
174 pipe_[i].iobuf.erase();
175 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
176 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
177 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
180 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
181 error = GetLastError();
182 lyxerr << "LyXComm: Could not create pipe "
183 << pipename << "\nLyXComm: "
184 << errormsg(error) << endl;
190 pipe_[i].state = pipe_[i].pending_io ?
191 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
195 // Add the stopserver_ event
196 event_[MAX_PIPES] = stopserver_;
199 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
203 bool success = false;
205 while (!checkStopServer()) {
206 // Indefinitely wait for the completion of an overlapped
207 // read, write, or connect operation.
208 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
211 // Determine which pipe instance completed the operation.
212 i = wait - WAIT_OBJECT_0;
213 LASSERT(i <= MAX_PIPES, /**/);
215 // Check whether we were waked up for stopping the pipe server.
219 bool const is_outpipe = i >= MAX_CLIENTS;
221 // Get the result if the operation was pending.
222 if (pipe_[i].pending_io) {
223 success = GetOverlappedResult(pipe_[i].handle,
224 &pipe_[i].overlap, &status, FALSE);
226 switch (pipe_[i].state) {
227 case CONNECTING_STATE:
228 // Pending connect operation
230 error = GetLastError();
231 lyxerr << "LyXComm: "
232 << errormsg(error) << endl;
233 if (!resetPipe(i, true))
237 pipe_[i].state = is_outpipe ? WRITING_STATE
242 // Pending read operation
243 LASSERT(!is_outpipe, /**/);
244 if (!success || status == 0) {
245 if (!resetPipe(i, !success))
249 pipe_[i].nbytes = status;
250 pipe_[i].state = WRITING_STATE;
254 // Pending write operation
255 LASSERT(is_outpipe, /**/);
256 // Let's see whether we have a reply
257 if (!outbuf_.empty()) {
258 // Yep. Deliver it to all pipe
259 // instances if we get ownership
260 // of the mutex, otherwise we'll
261 // try again the next round.
262 DWORD result = WaitForSingleObject(
264 if (result == WAIT_OBJECT_0) {
265 DWORD j = MAX_CLIENTS;
266 while (j < MAX_PIPES) {
267 pipe_[j].iobuf = outbuf_;
272 ReleaseMutex(outbuf_mutex_);
274 if (pipe_[i].iobuf.empty())
275 pipe_[i].pending_io = false;
280 // Operate according to the pipe state
281 switch (pipe_[i].state) {
283 // The pipe instance is connected to a client
284 // and is ready to read a request.
285 LASSERT(!is_outpipe, /**/);
286 success = ReadFile(pipe_[i].handle,
287 pipe_[i].readbuf, PIPE_BUFSIZE - 1,
288 &pipe_[i].nbytes, &pipe_[i].overlap);
290 if (success && pipe_[i].nbytes != 0) {
291 // The read operation completed successfully.
292 pipe_[i].pending_io = false;
293 pipe_[i].state = WRITING_STATE;
297 error = GetLastError();
299 if (!success && error == ERROR_IO_PENDING) {
300 // The read operation is still pending.
301 pipe_[i].pending_io = true;
305 success = error == ERROR_BROKEN_PIPE;
307 // Client closed connection (ERROR_BROKEN_PIPE) or
308 // an error occurred; in either case, reset the pipe.
310 lyxerr << "LyXComm: " << errormsg(error) << endl;
311 if (!pipe_[i].iobuf.empty()) {
312 lyxerr << "LyXComm: truncated command: "
313 << pipe_[i].iobuf << endl;
314 pipe_[i].iobuf.erase();
317 if (!resetPipe(i, !success))
323 // The request was successfully read
324 // from the client; commit it.
325 ReadReadyEvent * event = new ReadReadyEvent(i);
326 QCoreApplication::postEvent(this,
327 static_cast<QEvent *>(event));
328 // Wait for completion
329 while (pipe_[i].nbytes && !checkStopServer(100))
331 pipe_[i].pending_io = false;
332 pipe_[i].state = READING_STATE;
336 // This is an output pipe instance. Initiate the
337 // overlapped write operation or monitor its progress.
339 if (pipe_[i].pending_io) {
340 success = WriteFile(pipe_[i].handle,
341 pipe_[i].iobuf.c_str(),
342 pipe_[i].iobuf.length(),
347 if (success && !pipe_[i].iobuf.empty()
348 && status == pipe_[i].iobuf.length()) {
349 // The write operation completed successfully.
350 pipe_[i].iobuf.erase();
351 pipe_[i].pending_io = false;
357 error = GetLastError();
359 if (success && (error == ERROR_IO_PENDING || error == NO_ERROR)) {
360 // The write operation is still pending.
361 // We get here when a reader is started
362 // well before a reply is ready, so delay
363 // a bit in order to not burden the cpu.
364 checkStopServer(100);
365 pipe_[i].pending_io = true;
369 success = error == ERROR_NO_DATA;
371 // Client closed connection (ERROR_NO_DATA) or
372 // an error occurred; in either case, reset the pipe.
374 lyxerr << "LyXComm: Error sending message: "
375 << pipe_[i].iobuf << "\nLyXComm: "
376 << errormsg(error) << endl;
378 if (!resetPipe(i, !success))
390 void LyXComm::closeHandles()
392 for (int i = 0; i < MAX_PIPES; ++i) {
394 ResetEvent(event_[i]);
395 CloseHandle(event_[i]);
398 if (pipe_[i].handle != INVALID_HANDLE_VALUE) {
399 CloseHandle(pipe_[i].handle);
400 pipe_[i].handle = INVALID_HANDLE_VALUE;
406 bool LyXComm::event(QEvent * e)
408 if (e->type() == QEvent::User) {
409 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
416 bool LyXComm::checkStopServer(DWORD timeout)
418 return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0;
422 bool LyXComm::startPipe(DWORD index)
424 pipe_[index].pending_io = false;
425 pipe_[index].overlap.Offset = 0;
426 pipe_[index].overlap.OffsetHigh = 0;
428 // Overlapped ConnectNamedPipe should return zero.
429 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
430 DWORD const error = GetLastError();
431 lyxerr << "LyXComm: Could not connect pipe "
432 << external_path(pipeName(index))
433 << "\nLyXComm: " << errormsg(error) << endl;
437 switch (GetLastError()) {
438 case ERROR_IO_PENDING:
439 // The overlapped connection is in progress.
440 pipe_[index].pending_io = true;
443 case ERROR_PIPE_CONNECTED:
444 // Client is already connected, so signal an event.
445 if (SetEvent(pipe_[index].overlap.hEvent))
449 // Anything else is an error.
450 DWORD const error = GetLastError();
451 lyxerr << "LyXComm: An error occurred while connecting pipe "
452 << external_path(pipeName(index))
453 << "\nLyXComm: " << errormsg(error) << endl;
461 bool LyXComm::resetPipe(DWORD index, bool close_handle)
463 // This method is called when an error occurs or when a client
464 // closes the connection. We first disconnect the pipe instance,
465 // then reconnect it, ready to wait for another client.
467 if (!DisconnectNamedPipe(pipe_[index].handle)) {
468 DWORD const error = GetLastError();
469 lyxerr << "LyXComm: Could not disconnect pipe "
470 << external_path(pipeName(index))
471 << "\nLyXComm: " << errormsg(error) << endl;
472 // What to do now? Let's try whether re-creating the pipe helps.
476 bool const is_outpipe = index >= MAX_CLIENTS;
479 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
480 : PIPE_ACCESS_INBOUND;
481 string const name = external_path(pipeName(index));
483 CloseHandle(pipe_[index].handle);
485 pipe_[index].iobuf.erase();
486 pipe_[index].handle = CreateNamedPipe(name.c_str(),
487 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
488 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
491 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
492 DWORD const error = GetLastError();
493 lyxerr << "LyXComm: Could not reset pipe " << name
494 << "\nLyXComm: " << errormsg(error) << endl;
499 if (!startPipe(index))
501 pipe_[index].state = pipe_[index].pending_io ?
502 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
508 void LyXComm::openConnection()
510 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
512 // If we are up, that's an error
514 LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
518 if (pipename_.empty()) {
519 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
523 // Check whether the pipe name is being used by some other instance.
524 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
525 // Tell the running instance to load the files
526 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
527 deferred_loading_ = true;
531 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
532 << " already exists.\nMaybe another instance of LyX"
533 " is using it." << endl;
538 // Mutex with no initial owner for synchronized access to outbuf_
539 outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
540 if (!outbuf_mutex_) {
541 DWORD const error = GetLastError();
542 lyxerr << "LyXComm: Could not create output buffer mutex"
543 << "\nLyXComm: " << errormsg(error) << endl;
548 // Manual-reset event, initial state = not signaled
549 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
551 DWORD const error = GetLastError();
552 lyxerr << "LyXComm: Could not create stop server event"
553 << "\nLyXComm: " << errormsg(error) << endl;
555 CloseHandle(outbuf_mutex_);
559 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
560 static_cast<void *>(this), 0, NULL);
561 if (!server_thread_) {
562 DWORD const error = GetLastError();
563 lyxerr << "LyXComm: Could not create pipe server thread"
564 << "\nLyXComm: " << errormsg(error) << endl;
566 CloseHandle(stopserver_);
567 CloseHandle(outbuf_mutex_);
574 void LyXComm::closeConnection()
576 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
578 if (pipename_.empty()) {
579 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
584 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
588 SetEvent(stopserver_);
589 // Wait for the pipe server to finish
590 WaitForSingleObject(server_thread_, INFINITE);
591 CloseHandle(server_thread_);
592 ResetEvent(stopserver_);
593 CloseHandle(stopserver_);
594 CloseHandle(outbuf_mutex_);
598 void LyXComm::emergencyCleanup()
601 SetEvent(stopserver_);
602 // Forcibly terminate the pipe server thread if it does
603 // not finish quickly.
604 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
605 TerminateThread(server_thread_, 0);
609 CloseHandle(server_thread_);
610 ResetEvent(stopserver_);
611 CloseHandle(stopserver_);
612 CloseHandle(outbuf_mutex_);
617 void LyXComm::read_ready(DWORD inpipe)
619 // Turn the pipe buffer into a C string
620 DWORD const nbytes = pipe_[inpipe].nbytes;
621 pipe_[inpipe].readbuf[nbytes] = '\0';
623 pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
625 // Commit any commands read
626 while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
627 // split() grabs the entire string if
628 // the delim /wasn't/ found. ?:-P
630 pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
631 cmd = rtrim(cmd, "\r");
632 LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
633 << ", iobuf:" << pipe_[inpipe].iobuf
636 clientcb_(client_, cmd);
639 // Signal that we are done.
640 pipe_[inpipe].nbytes = 0;
644 void LyXComm::send(string const & msg)
647 lyxerr << "LyXComm: Request to send empty string. Ignoring."
652 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
654 if (pipename_.empty())
658 lyxerr << "LyXComm: Pipes are closed. Could not send "
663 // Request ownership of the outbuf_mutex_
664 DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
666 if (result == WAIT_OBJECT_0) {
667 // If a client doesn't care to read a reply (JabRef is one
668 // such client), the output buffer could grow without limit.
669 // So, we empty it when its size is larger than PIPE_BUFSIZE.
670 if (outbuf_.size() > PIPE_BUFSIZE)
673 ReleaseMutex(outbuf_mutex_);
675 // Something is fishy, better resetting the connection.
676 DWORD const error = GetLastError();
677 lyxerr << "LyXComm: Error sending message: " << msg
678 << "\nLyXComm: " << errormsg(error)
679 << "\nLyXComm: Resetting connection" << endl;
680 ReleaseMutex(outbuf_mutex_);
687 string const LyXComm::pipeName(DWORD index) const
689 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
693 #elif !defined (HAVE_MKFIFO)
694 // We provide a stub class that disables the lyxserver.
696 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
700 void LyXComm::openConnection()
704 void LyXComm::closeConnection()
708 int LyXComm::startPipe(string const & filename, bool write)
714 void LyXComm::endPipe(int & fd, string const & filename, bool write)
718 void LyXComm::emergencyCleanup()
722 void LyXComm::read_ready()
726 void LyXComm::send(string const & msg)
730 #else // defined (HAVE_MKFIFO)
732 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
733 : infd_(-1), outfd_(-1),
734 ready_(false), pipename_(pip), client_(cli), clientcb_(ccb),
735 deferred_loading_(false)
741 void LyXComm::openConnection()
743 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
745 // If we are up, that's an error
747 lyxerr << "LyXComm: Already connected" << endl;
750 // We assume that we don't make it
753 if (pipename_.empty()) {
754 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
758 infd_ = startPipe(inPipeName(), false);
762 outfd_ = startPipe(outPipeName(), true);
764 endPipe(infd_, inPipeName(), false);
768 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
769 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
770 << '\n' << strerror(errno) << endl;
776 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
781 void LyXComm::closeConnection()
783 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
785 if (pipename_.empty()) {
786 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
791 LYXERR0("LyXComm: Already disconnected");
795 endPipe(infd_, inPipeName(), false);
796 endPipe(outfd_, outPipeName(), true);
802 int LyXComm::startPipe(string const & file, bool write)
804 static bool stalepipe = false;
805 FileName const filename(file);
806 if (filename.exists()) {
808 // Let's see whether we have a stale pipe.
809 int fd = ::open(filename.toFilesystemEncoding().c_str(),
810 O_WRONLY | O_NONBLOCK);
812 // Another LyX instance is using it.
814 // Tell the running instance to load the files
815 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
816 deferred_loading_ = true;
820 } else if (errno == ENXIO) {
821 // No process is reading from the other end.
823 LYXERR(Debug::LYXSERVER,
824 "LyXComm: trying to remove "
826 filename.removeFile();
828 } else if (stalepipe) {
829 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
831 filename.removeFile();
834 if (filename.exists()) {
835 lyxerr << "LyXComm: Pipe " << filename
836 << " already exists.\nIf no other LyX program"
837 " is active, please delete the pipe by hand"
845 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
846 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
847 << strerror(errno) << endl;
850 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
851 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
854 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
855 << strerror(errno) << endl;
856 filename.removeFile();
861 // Make sure not to call read_ready after destruction.
862 weak_ptr<void> tracker = tracker_.p();
863 theApp()->registerSocketCallback(fd, [=](){
864 if (!tracker.expired())
873 void LyXComm::endPipe(int & fd, string const & filename, bool write)
879 theApp()->unregisterSocketCallback(fd);
881 if (::close(fd) < 0) {
882 lyxerr << "LyXComm: Could not close pipe " << filename
883 << '\n' << strerror(errno) << endl;
886 if (!FileName(filename).removeFile()) {
887 lyxerr << "LyXComm: Could not remove pipe " << filename
888 << '\n' << strerror(errno) << endl;
895 void LyXComm::emergencyCleanup()
897 if (!pipename_.empty()) {
898 endPipe(infd_, inPipeName(), false);
899 endPipe(outfd_, outPipeName(), true);
904 // Receives messages and sends then to client
905 void LyXComm::read_ready()
907 // FIXME: make read_buffer_ a class-member for multiple sessions
908 static string read_buffer_;
909 read_buffer_.erase();
911 int const charbuf_size = 100;
912 char charbuf[charbuf_size];
914 // As O_NONBLOCK is set, until no data is available for reading,
915 // read() doesn't block but returns -1 and set errno to EAGAIN.
916 // After a client that opened the pipe for writing, closes it
917 // (and no other client is using the pipe), read() would always
918 // return 0 and thus the connection has to be reset.
922 // the single = is intended here.
923 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
925 charbuf[status] = '\0'; // turn it into a c string
926 read_buffer_ += rtrim(charbuf, "\r");
927 // commit any commands read
928 while (read_buffer_.find('\n') != string::npos) {
929 // split() grabs the entire string if
930 // the delim /wasn't/ found. ?:-P
932 read_buffer_= split(read_buffer_, cmd,'\n');
933 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
934 << ", read_buffer_:" << read_buffer_
937 clientcb_(client_, cmd);
941 if (errno == EAGAIN) {
942 // Nothing to read, continue
946 // An error occurred, better bailing out
947 LYXERR0("LyXComm: " << strerror(errno));
948 if (!read_buffer_.empty()) {
949 LYXERR0("LyXComm: truncated command: " << read_buffer_);
950 read_buffer_.erase();
952 break; // reset connection
956 // The connection gets reset when read() returns 0 (meaning that the
957 // last client closed the pipe) or an error occurred, in which case
958 // read() returns -1 and errno != EAGAIN.
965 void LyXComm::send(string const & msg)
968 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
972 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
974 if (pipename_.empty())
978 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
979 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
980 lyxerr << "LyXComm: Error sending message: " << msg
981 << '\n' << strerror(errno)
982 << "\nLyXComm: Resetting connection" << endl;
988 #endif // defined (HAVE_MKFIFO)
992 struct Sleep : QThread
994 static void millisec(unsigned long ms)
996 QThread::usleep(ms * 1000);
1003 bool LyXComm::loadFilesInOtherInstance()
1006 int loaded_files = 0;
1007 FileName const pipe(inPipeName());
1008 vector<string>::iterator it = theFilesToLoad().begin();
1009 while (it != theFilesToLoad().end()) {
1010 FileName fname = fileSearch(string(), os::internal_path(*it),
1011 "lyx", may_not_exist);
1012 if (fname.empty()) {
1016 // Wait a while to allow time for the other
1017 // instance to reset the connection
1018 Sleep::millisec(200);
1019 pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
1022 string const cmd = "LYXCMD:pipe:file-open:" +
1023 fname.absFileName() + '\n';
1024 if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
1025 LYXERR0("Cannot write to pipe!");
1028 it = theFilesToLoad().erase(it);
1030 return loaded_files > 0;
1034 string const LyXComm::inPipeName() const
1036 return pipename_ + ".in";
1040 string const LyXComm::outPipeName() const
1042 return pipename_ + ".out";
1046 /////////////////////////////////////////////////////////////////////
1050 /////////////////////////////////////////////////////////////////////
1052 void ServerCallback(Server * server, string const & msg)
1054 server->callback(msg);
1057 Server::Server(string const & pipes)
1058 : numclients_(0), pipes_(pipes, this, &ServerCallback)
1064 // say goodbye to clients so they stop sending messages
1065 // send as many bye messages as there are clients,
1066 // each with client's name.
1068 for (int i = 0; i != numclients_; ++i) {
1069 message = "LYXSRV:" + clients_[i] + ":bye\n";
1070 pipes_.send(message);
1075 int compare(char const * a, char const * b, unsigned int len)
1077 return strncmp(a, b, len);
1081 // Handle data gotten from communication, called by LyXComm
1082 void Server::callback(string const & msg)
1084 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1086 char const * p = msg.c_str();
1088 // --- parse the string --------------------------------------------
1090 // Format: LYXCMD:<client>:<func>:<argstring>\n
1092 bool server_only = false;
1094 // --- 1. check 'header' ---
1096 if (compare(p, "LYXSRV:", 7) == 0) {
1098 } else if (0 != compare(p, "LYXCMD:", 7)) {
1099 lyxerr << "Server: Unknown request \""
1100 << p << '"' << endl;
1105 // --- 2. for the moment ignore the client name ---
1107 while (*p && *p != ':')
1108 client += char(*p++);
1114 // --- 3. get function name ---
1116 while (*p && *p != ':')
1119 // --- 4. parse the argument ---
1121 if (!server_only && *p == ':' && *(++p)) {
1122 while (*p && *p != '\n')
1127 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1128 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1130 // --- lookup and exec the command ------------------
1134 // return the greeting to inform the client that
1135 // we are listening.
1136 if (cmd == "hello") {
1138 if (numclients_ == MAX_CLIENTS) { //paranoid check
1139 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1143 while (!clients_[i].empty() && i < numclients_)
1145 clients_[i] = client;
1147 buf = "LYXSRV:" + client + ":hello\n";
1148 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1150 } else if (cmd == "bye") {
1151 // If clients_ == 0 maybe we should reset the pipes
1152 // to prevent fake callbacks
1153 int i = 0; //look if client is registered
1154 for (; i < numclients_; ++i) {
1155 if (clients_[i] == client)
1158 if (i < numclients_) {
1160 clients_[i].erase();
1161 LYXERR(Debug::LYXSERVER, "Server: Client "
1162 << client << " said goodbye");
1164 LYXERR(Debug::LYXSERVER,
1165 "Server: ignoring bye message from unregistered client" << client);
1168 LYXERR0("Server: Undefined server command " << cmd << '.');
1174 // which lyxfunc should we let it connect to?
1175 // The correct solution would be to have a
1176 // specialized (non-gui) BufferView. But how do
1177 // we do it now? Probably we should just let it
1178 // connect to the lyxfunc in the single GuiView we
1179 // support currently. (Lgb)
1181 FuncRequest fr(lyxaction.lookupFunc(cmd), from_utf8(arg));
1182 fr.setOrigin(FuncRequest::LYXSERVER);
1184 theApp()->dispatch(fr, dr);
1185 string const rval = to_utf8(dr.message());
1187 // all commands produce an INFO or ERROR message
1188 // in the output pipe, even if they do not return
1189 // anything. See chapter 4 of Customization doc.
1195 buf += client + ':' + cmd + ':' + rval + '\n';
1198 // !!! we don't do any error checking -
1199 // if the client won't listen, the
1200 // message is lost and others too
1201 // maybe; so the client should empty
1202 // the outpipe before issuing a request.
1210 // Send a notify message to a client, called by WorkAreaKeyPress
1211 void Server::notifyClient(string const & s)
1213 pipes_.send("NOTIFY:" + s + "\n");
1220 #include "moc_Server.cpp"