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/lassert.h"
55 #include "support/lstrings.h"
56 #include "support/os.h"
58 #include "support/bind.h"
61 #include <QCoreApplication>
65 #ifdef HAVE_SYS_STAT_H
66 # include <sys/stat.h>
71 using namespace lyx::support;
72 using os::external_path;
76 /////////////////////////////////////////////////////////////////////
80 /////////////////////////////////////////////////////////////////////
84 class ReadReadyEvent : public QEvent {
87 ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
90 DWORD inpipe() const { return inpipe_; }
98 string errormsg(DWORD const error)
102 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
103 FORMAT_MESSAGE_FROM_SYSTEM |
104 FORMAT_MESSAGE_IGNORE_INSERTS,
106 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
107 (LPTSTR) &msgbuf, 0, NULL)) {
108 message = static_cast<char *>(msgbuf);
111 message = "Unknown error";
119 DWORD WINAPI pipeServerWrapper(void * arg)
121 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
122 if (!lyxcomm->pipeServer()) {
123 // Error exit; perform cleanup.
124 lyxcomm->ready_ = false;
125 lyxcomm->closeHandles();
126 CloseHandle(lyxcomm->server_thread_);
127 CloseHandle(lyxcomm->stopserver_);
128 CloseHandle(lyxcomm->outbuf_mutex_);
129 lyxerr << "LyXComm: Closing connection" << endl;
135 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
136 : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
138 for (int i = 0; i < MAX_PIPES; ++i) {
140 pipe_[i].handle = INVALID_HANDLE_VALUE;
147 bool LyXComm::pipeServer()
152 for (i = 0; i < MAX_PIPES; ++i) {
153 bool const is_outpipe = i >= MAX_CLIENTS;
154 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
155 : PIPE_ACCESS_INBOUND;
156 string const pipename = external_path(pipeName(i));
158 // Manual-reset event, initial state = signaled
159 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
161 error = GetLastError();
162 lyxerr << "LyXComm: Could not create event for pipe "
163 << pipename << "\nLyXComm: "
164 << errormsg(error) << endl;
168 pipe_[i].overlap.hEvent = event_[i];
169 pipe_[i].iobuf.erase();
170 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
171 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
172 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
175 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
176 error = GetLastError();
177 lyxerr << "LyXComm: Could not create pipe "
178 << pipename << "\nLyXComm: "
179 << errormsg(error) << endl;
185 pipe_[i].state = pipe_[i].pending_io ?
186 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
190 // Add the stopserver_ event
191 event_[MAX_PIPES] = stopserver_;
194 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
200 while (!checkStopServer()) {
201 // Indefinitely wait for the completion of an overlapped
202 // read, write, or connect operation.
203 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
206 // Determine which pipe instance completed the operation.
207 i = wait - WAIT_OBJECT_0;
208 LASSERT(i >= 0 && i <= MAX_PIPES, /**/);
210 // Check whether we were waked up for stopping the pipe server.
214 bool const is_outpipe = i >= MAX_CLIENTS;
216 // Get the result if the operation was pending.
217 if (pipe_[i].pending_io) {
218 success = GetOverlappedResult(pipe_[i].handle,
219 &pipe_[i].overlap, &status, FALSE);
221 switch (pipe_[i].state) {
222 case CONNECTING_STATE:
223 // Pending connect operation
225 error = GetLastError();
226 lyxerr << "LyXComm: "
227 << errormsg(error) << endl;
228 if (!resetPipe(i, true))
232 pipe_[i].state = is_outpipe ? WRITING_STATE
237 // Pending read operation
238 LASSERT(!is_outpipe, /**/);
239 if (!success || status == 0) {
240 if (!resetPipe(i, !success))
244 pipe_[i].nbytes = status;
245 pipe_[i].state = WRITING_STATE;
249 // Pending write operation
250 LASSERT(is_outpipe, /**/);
251 // Let's see whether we have a reply
252 if (!outbuf_.empty()) {
253 // Yep. Deliver it to all pipe
254 // instances if we get ownership
255 // of the mutex, otherwise we'll
256 // try again the next round.
257 DWORD result = WaitForSingleObject(
259 if (result == WAIT_OBJECT_0) {
260 DWORD j = MAX_CLIENTS;
261 while (j < MAX_PIPES) {
262 pipe_[j].iobuf = outbuf_;
267 ReleaseMutex(outbuf_mutex_);
269 if (pipe_[i].iobuf.empty())
270 pipe_[i].pending_io = false;
275 // Operate according to the pipe state
276 switch (pipe_[i].state) {
278 // The pipe instance is connected to a client
279 // and is ready to read a request.
280 LASSERT(!is_outpipe, /**/);
281 success = ReadFile(pipe_[i].handle,
282 pipe_[i].readbuf, PIPE_BUFSIZE - 1,
283 &pipe_[i].nbytes, &pipe_[i].overlap);
285 if (success && pipe_[i].nbytes != 0) {
286 // The read operation completed successfully.
287 pipe_[i].pending_io = false;
288 pipe_[i].state = WRITING_STATE;
292 error = GetLastError();
294 if (!success && error == ERROR_IO_PENDING) {
295 // The read operation is still pending.
296 pipe_[i].pending_io = true;
300 success = error == ERROR_BROKEN_PIPE;
302 // Client closed connection (ERROR_BROKEN_PIPE) or
303 // an error occurred; in either case, reset the pipe.
305 lyxerr << "LyXComm: " << errormsg(error) << endl;
306 if (!pipe_[i].iobuf.empty()) {
307 lyxerr << "LyXComm: truncated command: "
308 << pipe_[i].iobuf << endl;
309 pipe_[i].iobuf.erase();
312 if (!resetPipe(i, !success))
318 // The request was successfully read
319 // from the client; commit it.
320 ReadReadyEvent * event = new ReadReadyEvent(i);
321 QCoreApplication::postEvent(this,
322 static_cast<QEvent *>(event));
323 // Wait for completion
324 while (pipe_[i].nbytes && !checkStopServer(100))
326 pipe_[i].pending_io = false;
327 pipe_[i].state = READING_STATE;
331 // This is an output pipe instance. Initiate the
332 // overlapped write operation or monitor its progress.
334 if (pipe_[i].pending_io) {
335 success = WriteFile(pipe_[i].handle,
336 pipe_[i].iobuf.c_str(),
337 pipe_[i].iobuf.length(),
342 if (success && !pipe_[i].iobuf.empty()
343 && status == pipe_[i].iobuf.length()) {
344 // The write operation completed successfully.
345 pipe_[i].iobuf.erase();
346 pipe_[i].pending_io = false;
352 error = GetLastError();
354 if (success && error == ERROR_IO_PENDING) {
355 // The write operation is still pending.
356 // We get here when a reader is started
357 // well before a reply is ready, so delay
358 // a bit in order to not burden the cpu.
359 checkStopServer(100);
360 pipe_[i].pending_io = true;
364 success = error == ERROR_NO_DATA;
366 // Client closed connection (ERROR_NO_DATA) or
367 // an error occurred; in either case, reset the pipe.
369 lyxerr << "LyXComm: Error sending message: "
370 << pipe_[i].iobuf << "\nLyXComm: "
371 << errormsg(error) << endl;
373 if (!resetPipe(i, !success))
385 void LyXComm::closeHandles()
387 for (int i = 0; i < MAX_PIPES; ++i) {
389 ResetEvent(event_[i]);
390 CloseHandle(event_[i]);
393 if (pipe_[i].handle != INVALID_HANDLE_VALUE) {
394 CloseHandle(pipe_[i].handle);
395 pipe_[i].handle = INVALID_HANDLE_VALUE;
401 bool LyXComm::event(QEvent * e)
403 if (e->type() == QEvent::User) {
404 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
411 bool LyXComm::checkStopServer(DWORD timeout)
413 return WaitForSingleObject(stopserver_, timeout) == WAIT_OBJECT_0;
417 bool LyXComm::startPipe(DWORD index)
419 pipe_[index].pending_io = false;
420 pipe_[index].overlap.Offset = 0;
421 pipe_[index].overlap.OffsetHigh = 0;
423 // Overlapped ConnectNamedPipe should return zero.
424 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
425 DWORD const error = GetLastError();
426 lyxerr << "LyXComm: Could not connect pipe "
427 << external_path(pipeName(index))
428 << "\nLyXComm: " << errormsg(error) << endl;
432 switch (GetLastError()) {
433 case ERROR_IO_PENDING:
434 // The overlapped connection is in progress.
435 pipe_[index].pending_io = true;
438 case ERROR_PIPE_CONNECTED:
439 // Client is already connected, so signal an event.
440 if (SetEvent(pipe_[index].overlap.hEvent))
444 // Anything else is an error.
445 DWORD const error = GetLastError();
446 lyxerr << "LyXComm: An error occurred while connecting pipe "
447 << external_path(pipeName(index))
448 << "\nLyXComm: " << errormsg(error) << endl;
456 bool LyXComm::resetPipe(DWORD index, bool close_handle)
458 // This method is called when an error occurs or when a client
459 // closes the connection. We first disconnect the pipe instance,
460 // then reconnect it, ready to wait for another client.
462 if (!DisconnectNamedPipe(pipe_[index].handle)) {
463 DWORD const error = GetLastError();
464 lyxerr << "LyXComm: Could not disconnect pipe "
465 << external_path(pipeName(index))
466 << "\nLyXComm: " << errormsg(error) << endl;
467 // What to do now? Let's try whether re-creating the pipe helps.
471 bool const is_outpipe = index >= MAX_CLIENTS;
474 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
475 : PIPE_ACCESS_INBOUND;
476 string const name = external_path(pipeName(index));
478 CloseHandle(pipe_[index].handle);
480 pipe_[index].iobuf.erase();
481 pipe_[index].handle = CreateNamedPipe(name.c_str(),
482 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
483 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
486 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
487 DWORD const error = GetLastError();
488 lyxerr << "LyXComm: Could not reset pipe " << name
489 << "\nLyXComm: " << errormsg(error) << endl;
494 if (!startPipe(index))
496 pipe_[index].state = pipe_[index].pending_io ?
497 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
503 void LyXComm::openConnection()
505 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
507 // If we are up, that's an error
509 LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
513 if (pipename_.empty()) {
514 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
518 // Check whether the pipe name is being used by some other program.
519 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
520 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
521 << " already exists.\nMaybe another instance of LyX"
522 " is using it." << endl;
527 // Mutex with no initial owner for synchronized access to outbuf_
528 outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
529 if (!outbuf_mutex_) {
530 DWORD const error = GetLastError();
531 lyxerr << "LyXComm: Could not create output buffer mutex"
532 << "\nLyXComm: " << errormsg(error) << endl;
537 // Manual-reset event, initial state = not signaled
538 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
540 DWORD const error = GetLastError();
541 lyxerr << "LyXComm: Could not create stop server event"
542 << "\nLyXComm: " << errormsg(error) << endl;
544 CloseHandle(outbuf_mutex_);
548 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
549 static_cast<void *>(this), 0, NULL);
550 if (!server_thread_) {
551 DWORD const error = GetLastError();
552 lyxerr << "LyXComm: Could not create pipe server thread"
553 << "\nLyXComm: " << errormsg(error) << endl;
555 CloseHandle(stopserver_);
556 CloseHandle(outbuf_mutex_);
563 void LyXComm::closeConnection()
565 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
567 if (pipename_.empty()) {
568 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
573 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
577 SetEvent(stopserver_);
578 // Wait for the pipe server to finish
579 WaitForSingleObject(server_thread_, INFINITE);
580 CloseHandle(server_thread_);
581 ResetEvent(stopserver_);
582 CloseHandle(stopserver_);
583 CloseHandle(outbuf_mutex_);
587 void LyXComm::emergencyCleanup()
590 SetEvent(stopserver_);
591 // Forcibly terminate the pipe server thread if it does
592 // not finish quickly.
593 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
594 TerminateThread(server_thread_, 0);
598 CloseHandle(server_thread_);
599 ResetEvent(stopserver_);
600 CloseHandle(stopserver_);
601 CloseHandle(outbuf_mutex_);
606 void LyXComm::read_ready(DWORD inpipe)
608 // Turn the pipe buffer into a C string
609 DWORD const nbytes = pipe_[inpipe].nbytes;
610 pipe_[inpipe].readbuf[nbytes] = '\0';
612 pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
614 // Commit any commands read
615 while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
616 // split() grabs the entire string if
617 // the delim /wasn't/ found. ?:-P
619 pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
620 cmd = rtrim(cmd, "\r");
621 LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
622 << ", iobuf:" << pipe_[inpipe].iobuf
625 clientcb_(client_, cmd);
628 // Signal that we are done.
629 pipe_[inpipe].nbytes = 0;
633 void LyXComm::send(string const & msg)
636 lyxerr << "LyXComm: Request to send empty string. Ignoring."
641 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
643 if (pipename_.empty())
647 lyxerr << "LyXComm: Pipes are closed. Could not send "
652 // Request ownership of the outbuf_mutex_
653 DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
655 if (result == WAIT_OBJECT_0) {
656 // If a client doesn't care to read a reply (JabRef is one
657 // such client), the output buffer could grow without limit.
658 // So, we empty it when its size is larger than PIPE_BUFSIZE.
659 if (outbuf_.size() > PIPE_BUFSIZE)
662 ReleaseMutex(outbuf_mutex_);
664 // Something is fishy, better resetting the connection.
665 DWORD const error = GetLastError();
666 lyxerr << "LyXComm: Error sending message: " << msg
667 << "\nLyXComm: " << errormsg(error)
668 << "\nLyXComm: Resetting connection" << endl;
669 ReleaseMutex(outbuf_mutex_);
676 string const LyXComm::pipeName(DWORD index) const
678 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
682 #elif !defined (HAVE_MKFIFO)
683 // We provide a stub class that disables the lyxserver.
685 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
688 void LyXComm::openConnection()
692 void LyXComm::closeConnection()
696 int LyXComm::startPipe(string const & filename, bool write)
702 void LyXComm::endPipe(int & fd, string const & filename, bool write)
706 void LyXComm::emergencyCleanup()
709 void LyXComm::read_ready()
713 void LyXComm::send(string const & msg)
717 #else // defined (HAVE_MKFIFO)
720 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
721 : pipename_(pip), client_(cli), clientcb_(ccb)
728 void LyXComm::openConnection()
730 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
732 // If we are up, that's an error
734 lyxerr << "LyXComm: Already connected" << endl;
737 // We assume that we don't make it
740 if (pipename_.empty()) {
741 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
745 infd_ = startPipe(inPipeName(), false);
749 outfd_ = startPipe(outPipeName(), true);
751 endPipe(infd_, inPipeName(), false);
755 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
756 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
757 << '\n' << strerror(errno) << endl;
763 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
768 void LyXComm::closeConnection()
770 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
772 if (pipename_.empty()) {
773 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
778 LYXERR0("LyXComm: Already disconnected");
782 endPipe(infd_, inPipeName(), false);
783 endPipe(outfd_, outPipeName(), true);
789 int LyXComm::startPipe(string const & file, bool write)
791 static bool stalepipe = false;
792 FileName const filename(file);
793 if (filename.exists()) {
795 // Let's see whether we have a stale pipe.
796 int fd = ::open(filename.toFilesystemEncoding().c_str(),
797 O_WRONLY | O_NONBLOCK);
799 // Another LyX instance is using it.
801 } else if (errno == ENXIO) {
802 // No process is reading from the other end.
804 LYXERR(Debug::LYXSERVER,
805 "LyXComm: trying to remove "
807 filename.removeFile();
809 } else if (stalepipe) {
810 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
812 filename.removeFile();
815 if (filename.exists()) {
816 lyxerr << "LyXComm: Pipe " << filename
817 << " already exists.\nIf no other LyX program"
818 " is active, please delete the pipe by hand"
826 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
827 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
828 << strerror(errno) << endl;
831 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
832 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
835 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
836 << strerror(errno) << endl;
837 filename.removeFile();
842 theApp()->registerSocketCallback(fd,
843 bind(&LyXComm::read_ready, this));
850 void LyXComm::endPipe(int & fd, string const & filename, bool write)
856 theApp()->unregisterSocketCallback(fd);
858 if (::close(fd) < 0) {
859 lyxerr << "LyXComm: Could not close pipe " << filename
860 << '\n' << strerror(errno) << endl;
863 if (FileName(filename).removeFile() < 0) {
864 lyxerr << "LyXComm: Could not remove pipe " << filename
865 << '\n' << strerror(errno) << endl;
872 void LyXComm::emergencyCleanup()
874 if (!pipename_.empty()) {
875 endPipe(infd_, inPipeName(), false);
876 endPipe(outfd_, outPipeName(), true);
881 // Receives messages and sends then to client
882 void LyXComm::read_ready()
884 // FIXME: make read_buffer_ a class-member for multiple sessions
885 static string read_buffer_;
886 read_buffer_.erase();
888 int const charbuf_size = 100;
889 char charbuf[charbuf_size];
891 // As O_NONBLOCK is set, until no data is available for reading,
892 // read() doesn't block but returns -1 and set errno to EAGAIN.
893 // After a client that opened the pipe for writing, closes it
894 // (and no other client is using the pipe), read() would always
895 // return 0 and thus the connection has to be reset.
899 // the single = is intended here.
900 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
903 charbuf[status] = '\0'; // turn it into a c string
904 read_buffer_ += rtrim(charbuf, "\r");
905 // commit any commands read
906 while (read_buffer_.find('\n') != string::npos) {
907 // split() grabs the entire string if
908 // the delim /wasn't/ found. ?:-P
910 read_buffer_= split(read_buffer_, cmd,'\n');
911 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
912 << ", read_buffer_:" << read_buffer_
915 clientcb_(client_, cmd);
919 if (errno == EAGAIN) {
920 // Nothing to read, continue
924 // An error occurred, better bailing out
925 LYXERR0("LyXComm: " << strerror(errno));
926 if (!read_buffer_.empty()) {
927 LYXERR0("LyXComm: truncated command: " << read_buffer_);
928 read_buffer_.erase();
930 break; // reset connection
934 // The connection gets reset when read() returns 0 (meaning that the
935 // last client closed the pipe) or an error occurred, in which case
936 // read() returns -1 and errno != EAGAIN.
943 void LyXComm::send(string const & msg)
946 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
950 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
952 if (pipename_.empty()) return;
955 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
956 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
957 lyxerr << "LyXComm: Error sending message: " << msg
958 << '\n' << strerror(errno)
959 << "\nLyXComm: Resetting connection" << endl;
965 #endif // defined (HAVE_MKFIFO)
968 string const LyXComm::inPipeName() const
970 return pipename_ + ".in";
974 string const LyXComm::outPipeName() const
976 return pipename_ + ".out";
980 /////////////////////////////////////////////////////////////////////
984 /////////////////////////////////////////////////////////////////////
986 void ServerCallback(Server * server, string const & msg)
988 server->callback(msg);
991 Server::Server(string const & pipes)
992 : numclients_(0), pipes_(pipes, this, &ServerCallback)
998 // say goodbye to clients so they stop sending messages
999 // send as many bye messages as there are clients,
1000 // each with client's name.
1002 for (int i = 0; i != numclients_; ++i) {
1003 message = "LYXSRV:" + clients_[i] + ":bye\n";
1004 pipes_.send(message);
1009 int compare(char const * a, char const * b, unsigned int len)
1011 using namespace std;
1012 return strncmp(a, b, len);
1016 // Handle data gotten from communication, called by LyXComm
1017 void Server::callback(string const & msg)
1019 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1021 char const * p = msg.c_str();
1023 // --- parse the string --------------------------------------------
1025 // Format: LYXCMD:<client>:<func>:<argstring>\n
1027 bool server_only = false;
1029 // --- 1. check 'header' ---
1031 if (compare(p, "LYXSRV:", 7) == 0) {
1033 } else if (0 != compare(p, "LYXCMD:", 7)) {
1034 lyxerr << "Server: Unknown request \""
1035 << p << '"' << endl;
1040 // --- 2. for the moment ignore the client name ---
1042 while (*p && *p != ':')
1043 client += char(*p++);
1049 // --- 3. get function name ---
1051 while (*p && *p != ':')
1054 // --- 4. parse the argument ---
1056 if (!server_only && *p == ':' && *(++p)) {
1057 while (*p && *p != '\n')
1062 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1063 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1065 // --- lookup and exec the command ------------------
1069 // return the greeting to inform the client that
1070 // we are listening.
1071 if (cmd == "hello") {
1073 if (numclients_ == MAX_CLIENTS) { //paranoid check
1074 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1078 while (!clients_[i].empty() && i < numclients_)
1080 clients_[i] = client;
1082 buf = "LYXSRV:" + client + ":hello\n";
1083 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1085 } else if (cmd == "bye") {
1086 // If clients_ == 0 maybe we should reset the pipes
1087 // to prevent fake callbacks
1088 int i = 0; //look if client is registered
1089 for (; i < numclients_; ++i) {
1090 if (clients_[i] == client)
1093 if (i < numclients_) {
1095 clients_[i].erase();
1096 LYXERR(Debug::LYXSERVER, "Server: Client "
1097 << client << " said goodbye");
1099 LYXERR(Debug::LYXSERVER,
1100 "Server: ignoring bye messge from unregistered client" << client);
1103 LYXERR0("Server: Undefined server command " << cmd << '.');
1109 // which lyxfunc should we let it connect to?
1110 // The correct solution would be to have a
1111 // specialized (non-gui) BufferView. But how do
1112 // we do it now? Probably we should just let it
1113 // connect to the lyxfunc in the single GuiView we
1114 // support currently. (Lgb)
1116 FuncRequest const fr(lyxaction.lookupFunc(cmd), arg);
1118 theApp()->dispatch(fr, dr);
1119 string const rval = to_utf8(dr.message());
1121 // all commands produce an INFO or ERROR message
1122 // in the output pipe, even if they do not return
1123 // anything. See chapter 4 of Customization doc.
1129 buf += client + ':' + cmd + ':' + rval + '\n';
1132 // !!! we don't do any error checking -
1133 // if the client won't listen, the
1134 // message is lost and others too
1135 // maybe; so the client should empty
1136 // the outpipe before issuing a request.
1144 // Send a notify message to a client, called by WorkAreaKeyPress
1145 void Server::notifyClient(string const & s)
1147 pipes_.send("NOTIFY:" + s + "\n");
1154 #include "moc_Server.cpp"