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))
381 case CONNECTING_STATE:
382 LYXERR0("Wrong pipe state");
393 void LyXComm::closeHandles()
395 for (int i = 0; i < MAX_PIPES; ++i) {
397 ResetEvent(event_[i]);
398 CloseHandle(event_[i]);
401 if (pipe_[i].handle != INVALID_HANDLE_VALUE) {
402 CloseHandle(pipe_[i].handle);
403 pipe_[i].handle = INVALID_HANDLE_VALUE;
409 bool LyXComm::event(QEvent * e)
411 if (e->type() == QEvent::User) {
412 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
419 bool LyXComm::checkStopServer(DWORD timeout)
421 return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0;
425 bool LyXComm::startPipe(DWORD index)
427 pipe_[index].pending_io = false;
428 pipe_[index].overlap.Offset = 0;
429 pipe_[index].overlap.OffsetHigh = 0;
431 // Overlapped ConnectNamedPipe should return zero.
432 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
433 DWORD const error = GetLastError();
434 lyxerr << "LyXComm: Could not connect pipe "
435 << external_path(pipeName(index))
436 << "\nLyXComm: " << errormsg(error) << endl;
440 switch (GetLastError()) {
441 case ERROR_IO_PENDING:
442 // The overlapped connection is in progress.
443 pipe_[index].pending_io = true;
446 case ERROR_PIPE_CONNECTED:
447 // Client is already connected, so signal an event.
448 if (SetEvent(pipe_[index].overlap.hEvent))
452 // Anything else is an error.
453 DWORD const error = GetLastError();
454 lyxerr << "LyXComm: An error occurred while connecting pipe "
455 << external_path(pipeName(index))
456 << "\nLyXComm: " << errormsg(error) << endl;
464 bool LyXComm::resetPipe(DWORD index, bool close_handle)
466 // This method is called when an error occurs or when a client
467 // closes the connection. We first disconnect the pipe instance,
468 // then reconnect it, ready to wait for another client.
470 if (!DisconnectNamedPipe(pipe_[index].handle)) {
471 DWORD const error = GetLastError();
472 lyxerr << "LyXComm: Could not disconnect pipe "
473 << external_path(pipeName(index))
474 << "\nLyXComm: " << errormsg(error) << endl;
475 // What to do now? Let's try whether re-creating the pipe helps.
479 bool const is_outpipe = index >= MAX_CLIENTS;
482 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
483 : PIPE_ACCESS_INBOUND;
484 string const name = external_path(pipeName(index));
486 CloseHandle(pipe_[index].handle);
488 pipe_[index].iobuf.erase();
489 pipe_[index].handle = CreateNamedPipe(name.c_str(),
490 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
491 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
494 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
495 DWORD const error = GetLastError();
496 lyxerr << "LyXComm: Could not reset pipe " << name
497 << "\nLyXComm: " << errormsg(error) << endl;
502 if (!startPipe(index))
504 pipe_[index].state = pipe_[index].pending_io ?
505 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
511 void LyXComm::openConnection()
513 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
515 // If we are up, that's an error
517 LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
521 if (pipename_.empty()) {
522 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
526 // Check whether the pipe name is being used by some other instance.
527 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
528 // Tell the running instance to load the files
529 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
530 deferred_loading_ = true;
534 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
535 << " already exists.\nMaybe another instance of LyX"
536 " is using it." << endl;
541 // Mutex with no initial owner for synchronized access to outbuf_
542 outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
543 if (!outbuf_mutex_) {
544 DWORD const error = GetLastError();
545 lyxerr << "LyXComm: Could not create output buffer mutex"
546 << "\nLyXComm: " << errormsg(error) << endl;
551 // Manual-reset event, initial state = not signaled
552 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
554 DWORD const error = GetLastError();
555 lyxerr << "LyXComm: Could not create stop server event"
556 << "\nLyXComm: " << errormsg(error) << endl;
558 CloseHandle(outbuf_mutex_);
562 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
563 static_cast<void *>(this), 0, NULL);
564 if (!server_thread_) {
565 DWORD const error = GetLastError();
566 lyxerr << "LyXComm: Could not create pipe server thread"
567 << "\nLyXComm: " << errormsg(error) << endl;
569 CloseHandle(stopserver_);
570 CloseHandle(outbuf_mutex_);
577 void LyXComm::closeConnection()
579 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
581 if (pipename_.empty()) {
582 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
587 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
591 SetEvent(stopserver_);
592 // Wait for the pipe server to finish
593 WaitForSingleObject(server_thread_, INFINITE);
594 CloseHandle(server_thread_);
595 ResetEvent(stopserver_);
596 CloseHandle(stopserver_);
597 CloseHandle(outbuf_mutex_);
601 void LyXComm::emergencyCleanup()
604 SetEvent(stopserver_);
605 // Forcibly terminate the pipe server thread if it does
606 // not finish quickly.
607 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
608 TerminateThread(server_thread_, 0);
612 CloseHandle(server_thread_);
613 ResetEvent(stopserver_);
614 CloseHandle(stopserver_);
615 CloseHandle(outbuf_mutex_);
620 void LyXComm::read_ready(DWORD inpipe)
622 // Turn the pipe buffer into a C string
623 DWORD const nbytes = pipe_[inpipe].nbytes;
624 pipe_[inpipe].readbuf[nbytes] = '\0';
626 pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
628 // Commit any commands read
629 while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
630 // split() grabs the entire string if
631 // the delim /wasn't/ found. ?:-P
633 pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
634 cmd = rtrim(cmd, "\r");
635 LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
636 << ", iobuf:" << pipe_[inpipe].iobuf
639 clientcb_(client_, cmd);
642 // Signal that we are done.
643 pipe_[inpipe].nbytes = 0;
647 void LyXComm::send(string const & msg)
650 lyxerr << "LyXComm: Request to send empty string. Ignoring."
655 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
657 if (pipename_.empty())
661 lyxerr << "LyXComm: Pipes are closed. Could not send "
666 // Request ownership of the outbuf_mutex_
667 DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
669 if (result == WAIT_OBJECT_0) {
670 // If a client doesn't care to read a reply (JabRef is one
671 // such client), the output buffer could grow without limit.
672 // So, we empty it when its size is larger than PIPE_BUFSIZE.
673 if (outbuf_.size() > PIPE_BUFSIZE)
676 ReleaseMutex(outbuf_mutex_);
678 // Something is fishy, better resetting the connection.
679 DWORD const error = GetLastError();
680 lyxerr << "LyXComm: Error sending message: " << msg
681 << "\nLyXComm: " << errormsg(error)
682 << "\nLyXComm: Resetting connection" << endl;
683 ReleaseMutex(outbuf_mutex_);
690 string const LyXComm::pipeName(DWORD index) const
692 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
696 #elif !defined (HAVE_MKFIFO)
697 // We provide a stub class that disables the lyxserver.
699 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
703 void LyXComm::openConnection()
707 void LyXComm::closeConnection()
711 int LyXComm::startPipe(string const & filename, bool write)
717 void LyXComm::endPipe(int & fd, string const & filename, bool write)
721 void LyXComm::emergencyCleanup()
725 void LyXComm::read_ready()
729 void LyXComm::send(string const & msg)
733 #else // defined (HAVE_MKFIFO)
735 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
736 : infd_(-1), outfd_(-1),
737 ready_(false), pipename_(pip), client_(cli), clientcb_(ccb),
738 deferred_loading_(false)
744 void LyXComm::openConnection()
746 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
748 // If we are up, that's an error
750 lyxerr << "LyXComm: Already connected" << endl;
753 // We assume that we don't make it
756 if (pipename_.empty()) {
757 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
761 infd_ = startPipe(inPipeName(), false);
765 outfd_ = startPipe(outPipeName(), true);
767 endPipe(infd_, inPipeName(), false);
771 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
772 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
773 << '\n' << strerror(errno) << endl;
779 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
784 void LyXComm::closeConnection()
786 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
788 if (pipename_.empty()) {
789 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
794 LYXERR0("LyXComm: Already disconnected");
798 endPipe(infd_, inPipeName(), false);
799 endPipe(outfd_, outPipeName(), true);
805 int LyXComm::startPipe(string const & file, bool write)
807 static bool stalepipe = false;
808 FileName const filename(file);
809 if (filename.exists()) {
811 // Let's see whether we have a stale pipe.
812 int fd = ::open(filename.toFilesystemEncoding().c_str(),
813 O_WRONLY | O_NONBLOCK);
815 // Another LyX instance is using it.
817 // Tell the running instance to load the files
818 if (run_mode == USE_REMOTE && loadFilesInOtherInstance()) {
819 deferred_loading_ = true;
823 } else if (errno == ENXIO) {
824 // No process is reading from the other end.
826 LYXERR(Debug::LYXSERVER,
827 "LyXComm: trying to remove "
829 filename.removeFile();
831 } else if (stalepipe) {
832 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
834 filename.removeFile();
837 if (filename.exists()) {
838 lyxerr << "LyXComm: Pipe " << filename
839 << " already exists.\nIf no other LyX program"
840 " is active, please delete the pipe by hand"
848 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
849 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
850 << strerror(errno) << endl;
853 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
854 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
857 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
858 << strerror(errno) << endl;
859 filename.removeFile();
864 // Make sure not to call read_ready after destruction.
865 weak_ptr<void> tracker = tracker_.p();
866 theApp()->registerSocketCallback(fd, [=](){
867 if (!tracker.expired())
876 void LyXComm::endPipe(int & fd, string const & filename, bool write)
882 theApp()->unregisterSocketCallback(fd);
884 if (::close(fd) < 0) {
885 lyxerr << "LyXComm: Could not close pipe " << filename
886 << '\n' << strerror(errno) << endl;
889 if (!FileName(filename).removeFile()) {
890 lyxerr << "LyXComm: Could not remove pipe " << filename
891 << '\n' << strerror(errno) << endl;
898 void LyXComm::emergencyCleanup()
900 if (!pipename_.empty()) {
901 endPipe(infd_, inPipeName(), false);
902 endPipe(outfd_, outPipeName(), true);
907 // Receives messages and sends then to client
908 void LyXComm::read_ready()
910 // FIXME: make read_buffer_ a class-member for multiple sessions
911 static string read_buffer_;
912 read_buffer_.erase();
914 int const charbuf_size = 100;
915 char charbuf[charbuf_size];
917 // As O_NONBLOCK is set, until no data is available for reading,
918 // read() doesn't block but returns -1 and set errno to EAGAIN.
919 // After a client that opened the pipe for writing, closes it
920 // (and no other client is using the pipe), read() would always
921 // return 0 and thus the connection has to be reset.
925 // the single = is intended here.
926 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
928 charbuf[status] = '\0'; // turn it into a c string
929 read_buffer_ += rtrim(charbuf, "\r");
930 // commit any commands read
931 while (read_buffer_.find('\n') != string::npos) {
932 // split() grabs the entire string if
933 // the delim /wasn't/ found. ?:-P
935 read_buffer_= split(read_buffer_, cmd,'\n');
936 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
937 << ", read_buffer_:" << read_buffer_
940 clientcb_(client_, cmd);
944 if (errno == EAGAIN) {
945 // Nothing to read, continue
949 // An error occurred, better bailing out
950 LYXERR0("LyXComm: " << strerror(errno));
951 if (!read_buffer_.empty()) {
952 LYXERR0("LyXComm: truncated command: " << read_buffer_);
953 read_buffer_.erase();
955 break; // reset connection
959 // The connection gets reset when read() returns 0 (meaning that the
960 // last client closed the pipe) or an error occurred, in which case
961 // read() returns -1 and errno != EAGAIN.
968 void LyXComm::send(string const & msg)
971 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
975 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
977 if (pipename_.empty())
981 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
982 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
983 lyxerr << "LyXComm: Error sending message: " << msg
984 << '\n' << strerror(errno)
985 << "\nLyXComm: Resetting connection" << endl;
991 #endif // defined (HAVE_MKFIFO)
995 struct Sleep : QThread
997 static void millisec(unsigned long ms)
999 QThread::usleep(ms * 1000);
1006 bool LyXComm::loadFilesInOtherInstance()
1009 FileName const pipe(inPipeName());
1011 if (theFilesToLoad().empty()) {
1012 LYXERR0("LyX is already running in another instance\n"
1013 "and 'use single instance' is active.");
1014 // Wait a while for the other instance to reset the connection
1015 Sleep::millisec(200);
1016 pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
1018 string const cmd = "LYXCMD:pipe:window-raise\n";
1019 if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
1020 LYXERR0("Cannot communicate with running instance!");
1026 int loaded_files = 0;
1027 vector<string>::iterator it = theFilesToLoad().begin();
1028 while (it != theFilesToLoad().end()) {
1029 FileName fname = fileSearch(string(), os::internal_path(*it),
1030 "lyx", may_not_exist);
1031 if (fname.empty()) {
1035 // Wait a while to allow time for the other
1036 // instance to reset the connection
1037 Sleep::millisec(200);
1038 pipefd = ::open(pipe.toFilesystemEncoding().c_str(), O_WRONLY);
1041 string const cmd = "LYXCMD:pipe:file-open:" +
1042 fname.absFileName() + '\n';
1043 if (::write(pipefd, cmd.c_str(), cmd.length()) < 0)
1044 LYXERR0("Cannot write to pipe!");
1047 it = theFilesToLoad().erase(it);
1049 return loaded_files > 0;
1053 string const LyXComm::inPipeName() const
1055 return pipename_ + ".in";
1059 string const LyXComm::outPipeName() const
1061 return pipename_ + ".out";
1065 /////////////////////////////////////////////////////////////////////
1069 /////////////////////////////////////////////////////////////////////
1071 void ServerCallback(Server * server, string const & msg)
1073 server->callback(msg);
1076 Server::Server(string const & pipes)
1077 : numclients_(0), pipes_(pipes, this, &ServerCallback)
1083 // say goodbye to clients so they stop sending messages
1084 // send as many bye messages as there are clients,
1085 // each with client's name.
1087 for (int i = 0; i != numclients_; ++i) {
1088 message = "LYXSRV:" + clients_[i] + ":bye\n";
1089 pipes_.send(message);
1094 int compare(char const * a, char const * b, unsigned int len)
1096 return strncmp(a, b, len);
1100 // Handle data gotten from communication, called by LyXComm
1101 void Server::callback(string const & msg)
1103 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1105 char const * p = msg.c_str();
1107 // --- parse the string --------------------------------------------
1109 // Format: LYXCMD:<client>:<func>:<argstring>\n
1111 bool server_only = false;
1113 // --- 1. check 'header' ---
1115 if (compare(p, "LYXSRV:", 7) == 0) {
1117 } else if (0 != compare(p, "LYXCMD:", 7)) {
1118 lyxerr << "Server: Unknown request \""
1119 << p << '"' << endl;
1124 // --- 2. for the moment ignore the client name ---
1126 while (*p && *p != ':')
1127 client += char(*p++);
1133 // --- 3. get function name ---
1135 while (*p && *p != ':')
1138 // --- 4. parse the argument ---
1140 if (!server_only && *p == ':' && *(++p)) {
1141 while (*p && *p != '\n')
1146 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1147 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1149 // --- lookup and exec the command ------------------
1153 // return the greeting to inform the client that
1154 // we are listening.
1155 if (cmd == "hello") {
1157 if (numclients_ == MAX_CLIENTS) { //paranoid check
1158 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1162 while (!clients_[i].empty() && i < numclients_)
1164 clients_[i] = client;
1166 buf = "LYXSRV:" + client + ":hello\n";
1167 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1169 } else if (cmd == "bye") {
1170 // If clients_ == 0 maybe we should reset the pipes
1171 // to prevent fake callbacks
1172 int i = 0; //look if client is registered
1173 for (; i < numclients_; ++i) {
1174 if (clients_[i] == client)
1177 if (i < numclients_) {
1179 clients_[i].erase();
1180 LYXERR(Debug::LYXSERVER, "Server: Client "
1181 << client << " said goodbye");
1183 LYXERR(Debug::LYXSERVER,
1184 "Server: ignoring bye message from unregistered client" << client);
1187 LYXERR0("Server: Undefined server command " << cmd << '.');
1193 // which lyxfunc should we let it connect to?
1194 // The correct solution would be to have a
1195 // specialized (non-gui) BufferView. But how do
1196 // we do it now? Probably we should just let it
1197 // connect to the lyxfunc in the single GuiView we
1198 // support currently. (Lgb)
1200 FuncRequest fr(lyxaction.lookupFunc(cmd), from_utf8(arg));
1201 fr.setOrigin(FuncRequest::LYXSERVER);
1203 theApp()->dispatch(fr, dr);
1204 string const rval = to_utf8(dr.message());
1206 // all commands produce an INFO or ERROR message
1207 // in the output pipe, even if they do not return
1208 // anything. See chapter 4 of Customization doc.
1214 buf += client + ':' + cmd + ':' + rval + '\n';
1217 // !!! we don't do any error checking -
1218 // if the client won't listen, the
1219 // message is lost and others too
1220 // maybe; so the client should empty
1221 // the outpipe before issuing a request.
1229 // Send a notify message to a client, called by WorkAreaKeyPress
1230 void Server::notifyClient(string const & s)
1232 pipes_.send("NOTIFY:" + s + "\n");
1239 #include "moc_Server.cpp"