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>
73 #ifdef HAVE_SYS_STAT_H
74 # include <sys/stat.h>
79 using namespace lyx::support;
80 using os::external_path;
84 /////////////////////////////////////////////////////////////////////
88 /////////////////////////////////////////////////////////////////////
92 class ReadReadyEvent : public QEvent {
95 ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
98 DWORD inpipe() const { return inpipe_; }
106 string errormsg(DWORD const error)
110 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
111 FORMAT_MESSAGE_FROM_SYSTEM |
112 FORMAT_MESSAGE_IGNORE_INSERTS,
114 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
115 (LPTSTR) &msgbuf, 0, NULL)) {
116 message = static_cast<char *>(msgbuf);
119 message = "Unknown error";
127 DWORD WINAPI pipeServerWrapper(void * arg)
129 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
130 if (!lyxcomm->pipeServer()) {
131 // Error exit; perform cleanup.
132 lyxcomm->ready_ = false;
133 lyxcomm->closeHandles();
134 CloseHandle(lyxcomm->server_thread_);
135 CloseHandle(lyxcomm->stopserver_);
136 CloseHandle(lyxcomm->outbuf_mutex_);
137 lyxerr << "LyXComm: Closing connection" << endl;
143 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
145 ready_(false), pipename_(pip), client_(cli), clientcb_(ccb),
146 deferred_loading_(false)
148 for (int i = 0; i < MAX_PIPES; ++i) {
150 pipe_[i].handle = INVALID_HANDLE_VALUE;
156 bool LyXComm::pipeServer()
161 for (i = 0; i < MAX_PIPES; ++i) {
162 bool const is_outpipe = i >= MAX_CLIENTS;
163 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
164 : PIPE_ACCESS_INBOUND;
165 string const pipename = external_path(pipeName(i));
167 // Manual-reset event, initial state = signaled
168 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
170 error = GetLastError();
171 lyxerr << "LyXComm: Could not create event for pipe "
172 << pipename << "\nLyXComm: "
173 << errormsg(error) << endl;
177 pipe_[i].overlap.hEvent = event_[i];
178 pipe_[i].iobuf.erase();
179 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
180 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
181 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
184 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
185 error = GetLastError();
186 lyxerr << "LyXComm: Could not create pipe "
187 << pipename << "\nLyXComm: "
188 << errormsg(error) << endl;
194 pipe_[i].state = pipe_[i].pending_io ?
195 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
199 // Add the stopserver_ event
200 event_[MAX_PIPES] = stopserver_;
203 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
207 bool success = false;
209 while (!checkStopServer()) {
210 // Indefinitely wait for the completion of an overlapped
211 // read, write, or connect operation.
212 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
215 // Determine which pipe instance completed the operation.
216 i = wait - WAIT_OBJECT_0;
217 LASSERT(i <= MAX_PIPES, /**/);
219 // Check whether we were waked up for stopping the pipe server.
223 bool const is_outpipe = i >= MAX_CLIENTS;
225 // Get the result if the operation was pending.
226 if (pipe_[i].pending_io) {
227 success = GetOverlappedResult(pipe_[i].handle,
228 &pipe_[i].overlap, &status, FALSE);
230 switch (pipe_[i].state) {
231 case CONNECTING_STATE:
232 // Pending connect operation
234 error = GetLastError();
235 lyxerr << "LyXComm: "
236 << errormsg(error) << endl;
237 if (!resetPipe(i, true))
241 pipe_[i].state = is_outpipe ? WRITING_STATE
246 // Pending read operation
247 LASSERT(!is_outpipe, /**/);
248 if (!success || status == 0) {
249 if (!resetPipe(i, !success))
253 pipe_[i].nbytes = status;
254 pipe_[i].state = WRITING_STATE;
258 // Pending write operation
259 LASSERT(is_outpipe, /**/);
260 // Let's see whether we have a reply
261 if (!outbuf_.empty()) {
262 // Yep. Deliver it to all pipe
263 // instances if we get ownership
264 // of the mutex, otherwise we'll
265 // try again the next round.
266 DWORD result = WaitForSingleObject(
268 if (result == WAIT_OBJECT_0) {
269 DWORD j = MAX_CLIENTS;
270 while (j < MAX_PIPES) {
271 pipe_[j].iobuf = outbuf_;
276 ReleaseMutex(outbuf_mutex_);
278 if (pipe_[i].iobuf.empty())
279 pipe_[i].pending_io = false;
284 // Operate according to the pipe state
285 switch (pipe_[i].state) {
287 // The pipe instance is connected to a client
288 // and is ready to read a request.
289 LASSERT(!is_outpipe, /**/);
290 success = ReadFile(pipe_[i].handle,
291 pipe_[i].readbuf, PIPE_BUFSIZE - 1,
292 &pipe_[i].nbytes, &pipe_[i].overlap);
294 if (success && pipe_[i].nbytes != 0) {
295 // The read operation completed successfully.
296 pipe_[i].pending_io = false;
297 pipe_[i].state = WRITING_STATE;
301 error = GetLastError();
303 if (!success && error == ERROR_IO_PENDING) {
304 // The read operation is still pending.
305 pipe_[i].pending_io = true;
309 success = error == ERROR_BROKEN_PIPE;
311 // Client closed connection (ERROR_BROKEN_PIPE) or
312 // an error occurred; in either case, reset the pipe.
314 lyxerr << "LyXComm: " << errormsg(error) << endl;
315 if (!pipe_[i].iobuf.empty()) {
316 lyxerr << "LyXComm: truncated command: "
317 << pipe_[i].iobuf << endl;
318 pipe_[i].iobuf.erase();
321 if (!resetPipe(i, !success))
327 // The request was successfully read
328 // from the client; commit it.
329 ReadReadyEvent * event = new ReadReadyEvent(i);
330 QCoreApplication::postEvent(this,
331 static_cast<QEvent *>(event));
332 // Wait for completion
333 while (pipe_[i].nbytes && !checkStopServer(100))
335 pipe_[i].pending_io = false;
336 pipe_[i].state = READING_STATE;
340 // This is an output pipe instance. Initiate the
341 // overlapped write operation or monitor its progress.
343 if (pipe_[i].pending_io) {
344 success = WriteFile(pipe_[i].handle,
345 pipe_[i].iobuf.c_str(),
346 pipe_[i].iobuf.length(),
351 if (success && !pipe_[i].iobuf.empty()
352 && status == pipe_[i].iobuf.length()) {
353 // The write operation completed successfully.
354 pipe_[i].iobuf.erase();
355 pipe_[i].pending_io = false;
361 error = GetLastError();
363 if (success && (error == ERROR_IO_PENDING || error == NO_ERROR)) {
364 // The write operation is still pending.
365 // We get here when a reader is started
366 // well before a reply is ready, so delay
367 // a bit in order to not burden the cpu.
368 checkStopServer(100);
369 pipe_[i].pending_io = true;
373 success = error == ERROR_NO_DATA;
375 // Client closed connection (ERROR_NO_DATA) or
376 // an error occurred; in either case, reset the pipe.
378 lyxerr << "LyXComm: Error sending message: "
379 << pipe_[i].iobuf << "\nLyXComm: "
380 << errormsg(error) << endl;
382 if (!resetPipe(i, !success))
385 case CONNECTING_STATE:
386 LYXERR0("Wrong pipe state");
397 void LyXComm::closeHandles()
399 for (int i = 0; i < MAX_PIPES; ++i) {
401 ResetEvent(event_[i]);
402 CloseHandle(event_[i]);
405 if (pipe_[i].handle != INVALID_HANDLE_VALUE) {
406 CloseHandle(pipe_[i].handle);
407 pipe_[i].handle = INVALID_HANDLE_VALUE;
413 bool LyXComm::event(QEvent * e)
415 if (e->type() == QEvent::User) {
416 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
423 bool LyXComm::checkStopServer(DWORD timeout)
425 return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0;
429 bool LyXComm::startPipe(DWORD index)
431 pipe_[index].pending_io = false;
432 pipe_[index].overlap.Offset = 0;
433 pipe_[index].overlap.OffsetHigh = 0;
435 // Overlapped ConnectNamedPipe should return zero.
436 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
437 DWORD const error = GetLastError();
438 lyxerr << "LyXComm: Could not connect pipe "
439 << external_path(pipeName(index))
440 << "\nLyXComm: " << errormsg(error) << endl;
444 switch (GetLastError()) {
445 case ERROR_IO_PENDING:
446 // The overlapped connection is in progress.
447 pipe_[index].pending_io = true;
450 case ERROR_PIPE_CONNECTED:
451 // Client is already connected, so signal an event.
452 if (SetEvent(pipe_[index].overlap.hEvent))
456 // Anything else is an error.
457 DWORD const error = GetLastError();
458 lyxerr << "LyXComm: An error occurred while connecting pipe "
459 << external_path(pipeName(index))
460 << "\nLyXComm: " << errormsg(error) << endl;
468 bool LyXComm::resetPipe(DWORD index, bool close_handle)
470 // This method is called when an error occurs or when a client
471 // closes the connection. We first disconnect the pipe instance,
472 // then reconnect it, ready to wait for another client.
474 if (!DisconnectNamedPipe(pipe_[index].handle)) {
475 DWORD const error = GetLastError();
476 lyxerr << "LyXComm: Could not disconnect pipe "
477 << external_path(pipeName(index))
478 << "\nLyXComm: " << errormsg(error) << endl;
479 // What to do now? Let's try whether re-creating the pipe helps.
483 bool const is_outpipe = index >= MAX_CLIENTS;
486 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
487 : PIPE_ACCESS_INBOUND;
488 string const name = external_path(pipeName(index));
490 CloseHandle(pipe_[index].handle);
492 pipe_[index].iobuf.erase();
493 pipe_[index].handle = CreateNamedPipe(name.c_str(),
494 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
495 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
498 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
499 DWORD const error = GetLastError();
500 lyxerr << "LyXComm: Could not reset pipe " << name
501 << "\nLyXComm: " << errormsg(error) << endl;
506 if (!startPipe(index))
508 pipe_[index].state = pipe_[index].pending_io ?
509 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
515 void LyXComm::openConnection()
517 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
519 // If we are up, that's an error
521 LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
525 if (pipename_.empty()) {
526 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
530 // Check whether the pipe name is being used by some other instance.
531 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
532 // Tell the running instance to load the files
533 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
534 deferred_loading_ = true;
538 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
539 << " already exists.\nMaybe another instance of LyX"
540 " is using it." << endl;
545 // Mutex with no initial owner for synchronized access to outbuf_
546 outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
547 if (!outbuf_mutex_) {
548 DWORD const error = GetLastError();
549 lyxerr << "LyXComm: Could not create output buffer mutex"
550 << "\nLyXComm: " << errormsg(error) << endl;
555 // Manual-reset event, initial state = not signaled
556 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
558 DWORD const error = GetLastError();
559 lyxerr << "LyXComm: Could not create stop server event"
560 << "\nLyXComm: " << errormsg(error) << endl;
562 CloseHandle(outbuf_mutex_);
566 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
567 static_cast<void *>(this), 0, NULL);
568 if (!server_thread_) {
569 DWORD const error = GetLastError();
570 lyxerr << "LyXComm: Could not create pipe server thread"
571 << "\nLyXComm: " << errormsg(error) << endl;
573 CloseHandle(stopserver_);
574 CloseHandle(outbuf_mutex_);
581 void LyXComm::closeConnection()
583 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
585 if (pipename_.empty()) {
586 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
591 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
595 SetEvent(stopserver_);
596 // Wait for the pipe server to finish
597 WaitForSingleObject(server_thread_, INFINITE);
598 CloseHandle(server_thread_);
599 ResetEvent(stopserver_);
600 CloseHandle(stopserver_);
601 CloseHandle(outbuf_mutex_);
605 void LyXComm::emergencyCleanup()
608 SetEvent(stopserver_);
609 // Forcibly terminate the pipe server thread if it does
610 // not finish quickly.
611 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
612 TerminateThread(server_thread_, 0);
616 CloseHandle(server_thread_);
617 ResetEvent(stopserver_);
618 CloseHandle(stopserver_);
619 CloseHandle(outbuf_mutex_);
624 void LyXComm::read_ready(DWORD inpipe)
626 // Turn the pipe buffer into a C string
627 DWORD const nbytes = pipe_[inpipe].nbytes;
628 pipe_[inpipe].readbuf[nbytes] = '\0';
630 pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
632 // Commit any commands read
633 while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
634 // split() grabs the entire string if
635 // the delim /wasn't/ found. ?:-P
637 pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
638 cmd = rtrim(cmd, "\r");
639 LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
640 << ", iobuf:" << pipe_[inpipe].iobuf
643 clientcb_(client_, cmd);
646 // Signal that we are done.
647 pipe_[inpipe].nbytes = 0;
651 void LyXComm::send(string const & msg)
654 lyxerr << "LyXComm: Request to send empty string. Ignoring."
659 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
661 if (pipename_.empty())
665 lyxerr << "LyXComm: Pipes are closed. Could not send "
670 // Request ownership of the outbuf_mutex_
671 DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
673 if (result == WAIT_OBJECT_0) {
674 // If a client doesn't care to read a reply (JabRef is one
675 // such client), the output buffer could grow without limit.
676 // So, we empty it when its size is larger than PIPE_BUFSIZE.
677 if (outbuf_.size() > PIPE_BUFSIZE)
680 ReleaseMutex(outbuf_mutex_);
682 // Something is fishy, better resetting the connection.
683 DWORD const error = GetLastError();
684 lyxerr << "LyXComm: Error sending message: " << msg
685 << "\nLyXComm: " << errormsg(error)
686 << "\nLyXComm: Resetting connection" << endl;
687 ReleaseMutex(outbuf_mutex_);
694 string const LyXComm::pipeName(DWORD index) const
696 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
700 #elif !defined (HAVE_MKFIFO)
701 // We provide a stub class that disables the lyxserver.
703 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
707 void LyXComm::openConnection()
711 void LyXComm::closeConnection()
715 int LyXComm::startPipe(string const & filename, bool write)
721 void LyXComm::endPipe(int & fd, string const & filename, bool write)
725 void LyXComm::emergencyCleanup()
729 void LyXComm::read_ready()
733 void LyXComm::send(string const & msg)
737 #else // defined (HAVE_MKFIFO)
739 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
740 : infd_(-1), outfd_(-1),
741 ready_(false), pipename_(pip), client_(cli), clientcb_(ccb),
742 deferred_loading_(false)
748 void LyXComm::openConnection()
750 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
752 // If we are up, that's an error
754 lyxerr << "LyXComm: Already connected" << endl;
757 // We assume that we don't make it
760 if (pipename_.empty()) {
761 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
765 infd_ = startPipe(inPipeName(), false);
769 outfd_ = startPipe(outPipeName(), true);
771 endPipe(infd_, inPipeName(), false);
775 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
776 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
777 << '\n' << strerror(errno) << endl;
783 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
788 void LyXComm::closeConnection()
790 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
792 if (pipename_.empty()) {
793 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
798 LYXERR0("LyXComm: Already disconnected");
802 endPipe(infd_, inPipeName(), false);
803 endPipe(outfd_, outPipeName(), true);
809 int LyXComm::startPipe(string const & file, bool write)
811 static bool stalepipe = false;
812 FileName const filename(file);
813 if (filename.exists()) {
815 // Let's see whether we have a stale pipe.
816 int fd = ::open(filename.toFilesystemEncoding().c_str(),
817 O_WRONLY | O_NONBLOCK);
819 // Another LyX instance is using it.
821 // Tell the running instance to load the files
822 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
823 deferred_loading_ = true;
827 } else if (errno == ENXIO) {
828 // No process is reading from the other end.
830 LYXERR(Debug::LYXSERVER,
831 "LyXComm: trying to remove "
833 filename.removeFile();
835 } else if (stalepipe) {
836 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
838 filename.removeFile();
841 if (filename.exists()) {
842 lyxerr << "LyXComm: Pipe " << filename
843 << " already exists.\nIf no other LyX program"
844 " is active, please delete the pipe by hand"
852 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
853 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
854 << strerror(errno) << endl;
857 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
858 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
861 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
862 << strerror(errno) << endl;
863 filename.removeFile();
868 // Make sure not to call read_ready after destruction.
869 weak_ptr<void> tracker = tracker_.p();
870 theApp()->registerSocketCallback(fd, [=](){
871 if (!tracker.expired())
880 void LyXComm::endPipe(int & fd, string const & filename, bool write)
886 theApp()->unregisterSocketCallback(fd);
888 if (::close(fd) < 0) {
889 lyxerr << "LyXComm: Could not close pipe " << filename
890 << '\n' << strerror(errno) << endl;
893 if (!FileName(filename).removeFile()) {
894 lyxerr << "LyXComm: Could not remove pipe " << filename
895 << '\n' << strerror(errno) << endl;
902 void LyXComm::emergencyCleanup()
904 if (!pipename_.empty()) {
905 endPipe(infd_, inPipeName(), false);
906 endPipe(outfd_, outPipeName(), true);
911 // Receives messages and sends then to client
912 void LyXComm::read_ready()
914 // FIXME: make read_buffer_ a class-member for multiple sessions
915 static string read_buffer_;
916 read_buffer_.erase();
918 int const charbuf_size = 100;
919 char charbuf[charbuf_size];
921 // As O_NONBLOCK is set, until no data is available for reading,
922 // read() doesn't block but returns -1 and set errno to EAGAIN.
923 // After a client that opened the pipe for writing, closes it
924 // (and no other client is using the pipe), read() would always
925 // return 0 and thus the connection has to be reset.
929 // the single = is intended here.
930 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
932 charbuf[status] = '\0'; // turn it into a c string
933 read_buffer_ += rtrim(charbuf, "\r");
934 // commit any commands read
935 while (read_buffer_.find('\n') != string::npos) {
936 // split() grabs the entire string if
937 // the delim /wasn't/ found. ?:-P
939 read_buffer_= split(read_buffer_, cmd,'\n');
940 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
941 << ", read_buffer_:" << read_buffer_
944 clientcb_(client_, cmd);
948 if (errno == EAGAIN) {
949 // Nothing to read, continue
953 // An error occurred, better bailing out
954 LYXERR0("LyXComm: " << strerror(errno));
955 if (!read_buffer_.empty()) {
956 LYXERR0("LyXComm: truncated command: " << read_buffer_);
957 read_buffer_.erase();
959 break; // reset connection
963 // The connection gets reset when read() returns 0 (meaning that the
964 // last client closed the pipe) or an error occurred, in which case
965 // read() returns -1 and errno != EAGAIN.
972 void LyXComm::send(string const & msg)
975 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
979 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
981 if (pipename_.empty())
985 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
986 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
987 lyxerr << "LyXComm: Error sending message: " << msg
988 << '\n' << strerror(errno)
989 << "\nLyXComm: Resetting connection" << endl;
995 #endif // defined (HAVE_MKFIFO)
999 struct Sleep : QThread
1001 static void millisec(unsigned long ms)
1003 QThread::usleep(ms * 1000);
1010 bool LyXComm::loadFilesInOtherInstance() const
1013 FileName const pipe(inPipeName());
1015 if (theFilesToLoad().empty()) {
1016 LYXERR0("LyX is already running in another instance\n"
1017 "and 'use single instance' is active.");
1018 // Wait a while for the other instance to reset the connection
1019 Sleep::millisec(200);
1020 pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
1022 string const cmd = "LYXCMD:pipe:window-raise\n";
1023 if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
1024 LYXERR0("Cannot communicate with running instance!");
1030 int loaded_files = 0;
1031 vector<string>::iterator it = theFilesToLoad().begin();
1032 while (it != theFilesToLoad().end()) {
1033 FileName fname = fileSearch(string(), os::internal_path(*it),
1034 "lyx", may_not_exist);
1035 if (fname.empty()) {
1039 // Wait a while to allow time for the other
1040 // instance to reset the connection
1041 Sleep::millisec(200);
1042 pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
1045 string const cmd = "LYXCMD:pipe:file-open:" +
1046 fname.absFileName() + '\n';
1047 if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
1048 LYXERR0("Cannot write to pipe!");
1051 it = theFilesToLoad().erase(it);
1053 return loaded_files > 0;
1057 string const LyXComm::inPipeName() const
1059 return pipename_ + ".in";
1063 string const LyXComm::outPipeName() const
1065 return pipename_ + ".out";
1069 /////////////////////////////////////////////////////////////////////
1073 /////////////////////////////////////////////////////////////////////
1075 void ServerCallback(Server * server, string const & msg)
1077 server->callback(msg);
1080 Server::Server(string const & pipes)
1081 : numclients_(0), pipes_(pipes, this, &ServerCallback)
1087 // say goodbye to clients so they stop sending messages
1088 // send as many bye messages as there are clients,
1089 // each with client's name.
1091 for (int i = 0; i != numclients_; ++i) {
1092 message = "LYXSRV:" + clients_[i] + ":bye\n";
1093 pipes_.send(message);
1098 int compare(char const * a, char const * b, unsigned int len)
1100 return strncmp(a, b, len);
1104 // Handle data gotten from communication, called by LyXComm
1105 void Server::callback(string const & msg)
1107 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1109 char const * p = msg.c_str();
1111 // --- parse the string --------------------------------------------
1113 // Format: LYXCMD:<client>:<func>:<argstring>\n
1115 bool server_only = false;
1117 // --- 1. check 'header' ---
1119 if (compare(p, "LYXSRV:", 7) == 0) {
1121 } else if (0 != compare(p, "LYXCMD:", 7)) {
1122 lyxerr << "Server: Unknown request \""
1123 << p << '"' << endl;
1128 // --- 2. for the moment ignore the client name ---
1130 while (*p && *p != ':')
1131 client += char(*p++);
1137 // --- 3. get function name ---
1139 while (*p && *p != ':')
1142 // --- 4. parse the argument ---
1144 if (!server_only && *p == ':' && *(++p)) {
1145 while (*p && *p != '\n')
1150 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1151 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1153 // --- lookup and exec the command ------------------
1157 // return the greeting to inform the client that
1158 // we are listening.
1159 if (cmd == "hello") {
1161 if (numclients_ == MAX_CLIENTS) { //paranoid check
1162 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1166 while (!clients_[i].empty() && i < numclients_)
1168 clients_[i] = client;
1170 buf = "LYXSRV:" + client + ":hello\n";
1171 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1173 } else if (cmd == "bye") {
1174 // If clients_ == 0 maybe we should reset the pipes
1175 // to prevent fake callbacks
1176 int i = 0; //look if client is registered
1177 for (; i < numclients_; ++i) {
1178 if (clients_[i] == client)
1181 if (i < numclients_) {
1183 clients_[i].erase();
1184 LYXERR(Debug::LYXSERVER, "Server: Client "
1185 << client << " said goodbye");
1187 LYXERR(Debug::LYXSERVER,
1188 "Server: ignoring bye message from unregistered client" << client);
1191 LYXERR0("Server: Undefined server command " << cmd << '.');
1197 // which lyxfunc should we let it connect to?
1198 // The correct solution would be to have a
1199 // specialized (non-gui) BufferView. But how do
1200 // we do it now? Probably we should just let it
1201 // connect to the lyxfunc in the single GuiView we
1202 // support currently. (Lgb)
1204 FuncRequest fr(lyxaction.lookupFunc(cmd), from_utf8(arg));
1205 fr.setOrigin(FuncRequest::LYXSERVER);
1207 theApp()->dispatch(fr, dr);
1208 string const rval = to_utf8(dr.message());
1210 // all commands produce an INFO or ERROR message
1211 // in the output pipe, even if they do not return
1212 // anything. See chapter 4 of Customization doc.
1218 buf += client + ':' + cmd + ':' + rval + '\n';
1221 // !!! we don't do any error checking -
1222 // if the client won't listen, the
1223 // message is lost and others too
1224 // maybe; so the client should empty
1225 // the outpipe before issuing a request.
1233 // Send a notify message to a client, called by WorkAreaKeyPress
1234 void Server::notifyClient(string const & s)
1236 pipes_.send("NOTIFY:" + s + "\n");
1243 #include "moc_Server.cpp"