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/server_monitor.c for an example client.
38 Purpose: implement a client/server lib for LyX
44 #include "FuncRequest.h"
45 #include "LyXAction.h"
48 #include "frontends/Application.h"
50 #include "support/debug.h"
51 #include "support/FileName.h"
52 #include "support/lassert.h"
53 #include "support/lstrings.h"
54 #include "support/os.h"
56 #include <boost/bind.hpp>
59 #include <QCoreApplication>
63 #ifdef HAVE_SYS_STAT_H
64 # include <sys/stat.h>
69 using namespace lyx::support;
70 using os::external_path;
74 /////////////////////////////////////////////////////////////////////
78 /////////////////////////////////////////////////////////////////////
82 class ReadReadyEvent : public QEvent {
85 ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
88 DWORD inpipe() const { return inpipe_; }
96 string errormsg(DWORD const error)
100 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
101 FORMAT_MESSAGE_FROM_SYSTEM |
102 FORMAT_MESSAGE_IGNORE_INSERTS,
104 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
105 (LPTSTR) &msgbuf, 0, NULL)) {
106 message = static_cast<char *>(msgbuf);
109 message = "Unknown error";
117 DWORD WINAPI pipeServerWrapper(void * arg)
119 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
120 if (!lyxcomm->pipeServer()) {
121 // Error exit; perform cleanup.
122 lyxcomm->ready_ = false;
123 lyxcomm->closeHandles();
124 CloseHandle(lyxcomm->server_thread_);
125 CloseHandle(lyxcomm->stopserver_);
126 CloseHandle(lyxcomm->outbuf_mutex_);
127 lyxerr << "LyXComm: Closing connection" << endl;
133 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
134 : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
136 for (int i = 0; i < MAX_PIPES; ++i) {
138 pipe_[i].handle = INVALID_HANDLE_VALUE;
145 bool LyXComm::pipeServer()
150 for (i = 0; i < MAX_PIPES; ++i) {
151 bool const is_outpipe = i >= MAX_CLIENTS;
152 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
153 : PIPE_ACCESS_INBOUND;
154 string const pipename = external_path(pipeName(i));
156 // Manual-reset event, initial state = signaled
157 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
159 error = GetLastError();
160 lyxerr << "LyXComm: Could not create event for pipe "
161 << pipename << "\nLyXComm: "
162 << errormsg(error) << endl;
166 pipe_[i].overlap.hEvent = event_[i];
167 pipe_[i].iobuf.erase();
168 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
169 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
170 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
173 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
174 error = GetLastError();
175 lyxerr << "LyXComm: Could not create pipe "
176 << pipename << "\nLyXComm: "
177 << errormsg(error) << endl;
183 pipe_[i].state = pipe_[i].pending_io ?
184 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
188 // Add the stopserver_ event
189 event_[MAX_PIPES] = stopserver_;
192 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
198 while (!checkStopServer()) {
199 // Indefinitely wait for the completion of an overlapped
200 // read, write, or connect operation.
201 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
204 // Determine which pipe instance completed the operation.
205 i = wait - WAIT_OBJECT_0;
206 LASSERT(i >= 0 && i <= MAX_PIPES, /**/);
208 // Check whether we were waked up for stopping the pipe server.
212 bool const is_outpipe = i >= MAX_CLIENTS;
214 // Get the result if the operation was pending.
215 if (pipe_[i].pending_io) {
216 success = GetOverlappedResult(pipe_[i].handle,
217 &pipe_[i].overlap, &status, FALSE);
219 switch (pipe_[i].state) {
220 case CONNECTING_STATE:
221 // Pending connect operation
223 error = GetLastError();
224 lyxerr << "LyXComm: "
225 << errormsg(error) << endl;
226 if (!resetPipe(i, true))
230 pipe_[i].state = is_outpipe ? WRITING_STATE
235 // Pending read operation
236 LASSERT(!is_outpipe, /**/);
237 if (!success || status == 0) {
238 if (!resetPipe(i, !success))
242 pipe_[i].nbytes = status;
243 pipe_[i].state = WRITING_STATE;
247 // Pending write operation
248 LASSERT(is_outpipe, /**/);
249 // Let's see whether we have a reply
250 if (!outbuf_.empty()) {
251 // Yep. Deliver it to all pipe
252 // instances if we get ownership
253 // of the mutex, otherwise we'll
254 // try again the next round.
255 DWORD result = WaitForSingleObject(
257 if (result == WAIT_OBJECT_0) {
258 DWORD j = MAX_CLIENTS;
259 while (j < MAX_PIPES) {
260 pipe_[j].iobuf = outbuf_;
265 ReleaseMutex(outbuf_mutex_);
267 if (pipe_[i].iobuf.empty())
268 pipe_[i].pending_io = false;
273 // Operate according to the pipe state
274 switch (pipe_[i].state) {
276 // The pipe instance is connected to a client
277 // and is ready to read a request.
278 LASSERT(!is_outpipe, /**/);
279 success = ReadFile(pipe_[i].handle,
280 pipe_[i].readbuf, PIPE_BUFSIZE - 1,
281 &pipe_[i].nbytes, &pipe_[i].overlap);
283 if (success && pipe_[i].nbytes != 0) {
284 // The read operation completed successfully.
285 pipe_[i].pending_io = false;
286 pipe_[i].state = WRITING_STATE;
290 error = GetLastError();
292 if (!success && error == ERROR_IO_PENDING) {
293 // The read operation is still pending.
294 pipe_[i].pending_io = true;
298 success = error == ERROR_BROKEN_PIPE;
300 // Client closed connection (ERROR_BROKEN_PIPE) or
301 // an error occurred; in either case, reset the pipe.
303 lyxerr << "LyXComm: " << errormsg(error) << endl;
304 if (!pipe_[i].iobuf.empty()) {
305 lyxerr << "LyXComm: truncated command: "
306 << pipe_[i].iobuf << endl;
307 pipe_[i].iobuf.erase();
310 if (!resetPipe(i, !success))
316 // The request was successfully read
317 // from the client; commit it.
318 ReadReadyEvent * event = new ReadReadyEvent(i);
319 QCoreApplication::postEvent(this,
320 static_cast<QEvent *>(event));
321 // Wait for completion
322 while (pipe_[i].nbytes && !checkStopServer())
324 pipe_[i].pending_io = false;
325 pipe_[i].state = READING_STATE;
329 // This is an output pipe instance. Initiate the
330 // overlapped write operation or monitor its progress.
332 if (pipe_[i].pending_io) {
333 success = WriteFile(pipe_[i].handle,
334 pipe_[i].iobuf.c_str(),
335 pipe_[i].iobuf.length(),
340 if (success && !pipe_[i].iobuf.empty()
341 && status == pipe_[i].iobuf.length()) {
342 // The write operation completed successfully.
343 pipe_[i].iobuf.erase();
344 pipe_[i].pending_io = false;
350 error = GetLastError();
352 if (success && error == ERROR_IO_PENDING) {
353 // The write operation is still pending.
354 // We get here when a reader is started
355 // well before a reply is ready, so delay
356 // a bit in order to not burden the cpu.
358 pipe_[i].pending_io = true;
362 success = error == ERROR_NO_DATA;
364 // Client closed connection (ERROR_NO_DATA) or
365 // an error occurred; in either case, reset the pipe.
367 lyxerr << "LyXComm: Error sending message: "
368 << pipe_[i].iobuf << "\nLyXComm: "
369 << errormsg(error) << endl;
371 if (!resetPipe(i, !success))
383 void LyXComm::closeHandles()
385 for (int i = 0; i < MAX_PIPES; ++i) {
387 ResetEvent(event_[i]);
388 CloseHandle(event_[i]);
391 if (pipe_[i].handle != INVALID_HANDLE_VALUE) {
392 CloseHandle(pipe_[i].handle);
393 pipe_[i].handle = INVALID_HANDLE_VALUE;
399 bool LyXComm::event(QEvent * e)
401 if (e->type() == QEvent::User) {
402 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
409 bool LyXComm::checkStopServer()
411 return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0;
415 bool LyXComm::startPipe(DWORD index)
417 pipe_[index].pending_io = false;
419 // Overlapped ConnectNamedPipe should return zero.
420 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
421 DWORD const error = GetLastError();
422 lyxerr << "LyXComm: Could not connect pipe "
423 << external_path(pipeName(index))
424 << "\nLyXComm: " << errormsg(error) << endl;
428 switch (GetLastError()) {
429 case ERROR_IO_PENDING:
430 // The overlapped connection is in progress.
431 pipe_[index].pending_io = true;
434 case ERROR_PIPE_CONNECTED:
435 // Client is already connected, so signal an event.
436 if (SetEvent(pipe_[index].overlap.hEvent))
440 // Anything else is an error.
441 DWORD const error = GetLastError();
442 lyxerr << "LyXComm: An error occurred while connecting pipe "
443 << external_path(pipeName(index))
444 << "\nLyXComm: " << errormsg(error) << endl;
452 bool LyXComm::resetPipe(DWORD index, bool close_handle)
454 // This method is called when an error occurs or when a client
455 // closes the connection. We first disconnect the pipe instance,
456 // then reconnect it, ready to wait for another client.
458 if (!DisconnectNamedPipe(pipe_[index].handle)) {
459 DWORD const error = GetLastError();
460 lyxerr << "LyXComm: Could not disconnect pipe "
461 << external_path(pipeName(index))
462 << "\nLyXComm: " << errormsg(error) << endl;
463 // What to do now? Let's try whether re-creating the pipe helps.
467 bool const is_outpipe = index >= MAX_CLIENTS;
470 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
471 : PIPE_ACCESS_INBOUND;
472 string const name = external_path(pipeName(index));
474 CloseHandle(pipe_[index].handle);
476 pipe_[index].iobuf.erase();
477 pipe_[index].handle = CreateNamedPipe(name.c_str(),
478 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
479 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
482 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
483 DWORD const error = GetLastError();
484 lyxerr << "LyXComm: Could not reset pipe " << name
485 << "\nLyXComm: " << errormsg(error) << endl;
490 if (!startPipe(index))
492 pipe_[index].state = pipe_[index].pending_io ?
493 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
499 void LyXComm::openConnection()
501 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
503 // If we are up, that's an error
505 LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
509 if (pipename_.empty()) {
510 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
514 // Check whether the pipe name is being used by some other program.
515 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
516 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
517 << " already exists.\nMaybe another instance of LyX"
518 " is using it." << endl;
523 // Mutex with no initial owner for synchronized access to outbuf_
524 outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
525 if (!outbuf_mutex_) {
526 DWORD const error = GetLastError();
527 lyxerr << "LyXComm: Could not create output buffer mutex"
528 << "\nLyXComm: " << errormsg(error) << endl;
533 // Manual-reset event, initial state = not signaled
534 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
536 DWORD const error = GetLastError();
537 lyxerr << "LyXComm: Could not create stop server event"
538 << "\nLyXComm: " << errormsg(error) << endl;
540 CloseHandle(outbuf_mutex_);
544 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
545 static_cast<void *>(this), 0, NULL);
546 if (!server_thread_) {
547 DWORD const error = GetLastError();
548 lyxerr << "LyXComm: Could not create pipe server thread"
549 << "\nLyXComm: " << errormsg(error) << endl;
551 CloseHandle(stopserver_);
552 CloseHandle(outbuf_mutex_);
559 void LyXComm::closeConnection()
561 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
563 if (pipename_.empty()) {
564 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
569 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
573 SetEvent(stopserver_);
574 // Wait for the pipe server to finish
575 WaitForSingleObject(server_thread_, INFINITE);
576 CloseHandle(server_thread_);
577 ResetEvent(stopserver_);
578 CloseHandle(stopserver_);
579 CloseHandle(outbuf_mutex_);
583 void LyXComm::emergencyCleanup()
586 SetEvent(stopserver_);
587 // Forcibly terminate the pipe server thread if it does
588 // not finish quickly.
589 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0) {
590 TerminateThread(server_thread_, 0);
594 CloseHandle(server_thread_);
595 ResetEvent(stopserver_);
596 CloseHandle(stopserver_);
597 CloseHandle(outbuf_mutex_);
602 void LyXComm::read_ready(DWORD inpipe)
604 // Turn the pipe buffer into a C string
605 DWORD const nbytes = pipe_[inpipe].nbytes;
606 pipe_[inpipe].readbuf[nbytes] = '\0';
608 pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
610 // Commit any commands read
611 while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
612 // split() grabs the entire string if
613 // the delim /wasn't/ found. ?:-P
615 pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
616 cmd = rtrim(cmd, "\r");
617 LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
618 << ", iobuf:" << pipe_[inpipe].iobuf
621 clientcb_(client_, cmd);
624 // Signal that we are done.
625 pipe_[inpipe].nbytes = 0;
629 void LyXComm::send(string const & msg)
632 lyxerr << "LyXComm: Request to send empty string. Ignoring."
637 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
639 if (pipename_.empty())
643 lyxerr << "LyXComm: Pipes are closed. Could not send "
648 // Request ownership of the outbuf_mutex_
649 DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
651 if (result == WAIT_OBJECT_0) {
652 // If a client doesn't care to read a reply (JabRef is one
653 // such client), the output buffer could grow without limit.
654 // So, we empty it when its size is larger than PIPE_BUFSIZE.
655 if (outbuf_.size() > PIPE_BUFSIZE)
658 ReleaseMutex(outbuf_mutex_);
660 // Something is fishy, better resetting the connection.
661 DWORD const error = GetLastError();
662 lyxerr << "LyXComm: Error sending message: " << msg
663 << "\nLyXComm: " << errormsg(error)
664 << "\nLyXComm: Resetting connection" << endl;
665 ReleaseMutex(outbuf_mutex_);
667 if (!checkStopServer())
673 string const LyXComm::pipeName(DWORD index) const
675 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
679 #elif !defined (HAVE_MKFIFO)
680 // We provide a stub class that disables the lyxserver.
682 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
685 void LyXComm::openConnection()
689 void LyXComm::closeConnection()
693 int LyXComm::startPipe(string const & filename, bool write)
699 void LyXComm::endPipe(int & fd, string const & filename, bool write)
703 void LyXComm::emergencyCleanup()
706 void LyXComm::read_ready()
710 void LyXComm::send(string const & msg)
714 #else // defined (HAVE_MKFIFO)
717 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
718 : pipename_(pip), client_(cli), clientcb_(ccb)
725 void LyXComm::openConnection()
727 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
729 // If we are up, that's an error
731 lyxerr << "LyXComm: Already connected" << endl;
734 // We assume that we don't make it
737 if (pipename_.empty()) {
738 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
742 infd_ = startPipe(inPipeName(), false);
746 outfd_ = startPipe(outPipeName(), true);
748 endPipe(infd_, inPipeName(), false);
752 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
753 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
754 << '\n' << strerror(errno) << endl;
760 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
765 void LyXComm::closeConnection()
767 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
769 if (pipename_.empty()) {
770 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
775 LYXERR0("LyXComm: Already disconnected");
779 endPipe(infd_, inPipeName(), false);
780 endPipe(outfd_, outPipeName(), true);
786 int LyXComm::startPipe(string const & file, bool write)
788 static bool stalepipe = false;
789 FileName const filename(file);
790 if (filename.exists()) {
792 // Let's see whether we have a stale pipe.
793 int fd = ::open(filename.toFilesystemEncoding().c_str(),
794 O_WRONLY | O_NONBLOCK);
796 // Another LyX instance is using it.
798 } else if (errno == ENXIO) {
799 // No process is reading from the other end.
801 LYXERR(Debug::LYXSERVER,
802 "LyXComm: trying to remove "
804 filename.removeFile();
806 } else if (stalepipe) {
807 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
809 filename.removeFile();
812 if (filename.exists()) {
813 lyxerr << "LyXComm: Pipe " << filename
814 << " already exists.\nIf no other LyX program"
815 " is active, please delete the pipe by hand"
823 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
824 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
825 << strerror(errno) << endl;
828 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
829 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
832 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
833 << strerror(errno) << endl;
834 filename.removeFile();
839 theApp()->registerSocketCallback(fd,
840 boost::bind(&LyXComm::read_ready, this));
847 void LyXComm::endPipe(int & fd, string const & filename, bool write)
853 theApp()->unregisterSocketCallback(fd);
855 if (::close(fd) < 0) {
856 lyxerr << "LyXComm: Could not close pipe " << filename
857 << '\n' << strerror(errno) << endl;
860 if (FileName(filename).removeFile() < 0) {
861 lyxerr << "LyXComm: Could not remove pipe " << filename
862 << '\n' << strerror(errno) << endl;
869 void LyXComm::emergencyCleanup()
871 if (!pipename_.empty()) {
872 endPipe(infd_, inPipeName(), false);
873 endPipe(outfd_, outPipeName(), true);
878 // Receives messages and sends then to client
879 void LyXComm::read_ready()
881 // FIXME: make read_buffer_ a class-member for multiple sessions
882 static string read_buffer_;
883 read_buffer_.erase();
885 int const charbuf_size = 100;
886 char charbuf[charbuf_size];
888 // As O_NONBLOCK is set, until no data is available for reading,
889 // read() doesn't block but returns -1 and set errno to EAGAIN.
890 // After a client that opened the pipe for writing, closes it
891 // (and no other client is using the pipe), read() would always
892 // return 0 and thus the connection has to be reset.
896 // the single = is intended here.
897 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
900 charbuf[status] = '\0'; // turn it into a c string
901 read_buffer_ += rtrim(charbuf, "\r");
902 // commit any commands read
903 while (read_buffer_.find('\n') != string::npos) {
904 // split() grabs the entire string if
905 // the delim /wasn't/ found. ?:-P
907 read_buffer_= split(read_buffer_, cmd,'\n');
908 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
909 << ", read_buffer_:" << read_buffer_
912 clientcb_(client_, cmd);
916 if (errno == EAGAIN) {
917 // Nothing to read, continue
921 // An error occurred, better bailing out
922 LYXERR0("LyXComm: " << strerror(errno));
923 if (!read_buffer_.empty()) {
924 LYXERR0("LyXComm: truncated command: " << read_buffer_);
925 read_buffer_.erase();
927 break; // reset connection
931 // The connection gets reset when read() returns 0 (meaning that the
932 // last client closed the pipe) or an error occurred, in which case
933 // read() returns -1 and errno != EAGAIN.
940 void LyXComm::send(string const & msg)
943 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
947 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
949 if (pipename_.empty()) return;
952 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
953 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
954 lyxerr << "LyXComm: Error sending message: " << msg
955 << '\n' << strerror(errno)
956 << "\nLyXComm: Resetting connection" << endl;
962 #endif // defined (HAVE_MKFIFO)
965 string const LyXComm::inPipeName() const
967 return pipename_ + ".in";
971 string const LyXComm::outPipeName() const
973 return pipename_ + ".out";
977 /////////////////////////////////////////////////////////////////////
981 /////////////////////////////////////////////////////////////////////
983 void ServerCallback(Server * server, string const & msg)
985 server->callback(msg);
988 Server::Server(LyXFunc * f, string const & pipes)
989 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
995 // say goodbye to clients so they stop sending messages
996 // send as many bye messages as there are clients,
997 // each with client's name.
999 for (int i = 0; i != numclients_; ++i) {
1000 message = "LYXSRV:" + clients_[i] + ":bye\n";
1001 pipes_.send(message);
1006 int compare(char const * a, char const * b, unsigned int len)
1008 using namespace std;
1009 return strncmp(a, b, len);
1013 // Handle data gotten from communication, called by LyXComm
1014 void Server::callback(string const & msg)
1016 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1018 char const * p = msg.c_str();
1020 // --- parse the string --------------------------------------------
1022 // Format: LYXCMD:<client>:<func>:<argstring>\n
1024 bool server_only = false;
1026 // --- 1. check 'header' ---
1028 if (compare(p, "LYXSRV:", 7) == 0) {
1030 } else if (0 != compare(p, "LYXCMD:", 7)) {
1031 lyxerr << "Server: Unknown request \""
1032 << p << '"' << endl;
1037 // --- 2. for the moment ignore the client name ---
1039 while (*p && *p != ':')
1040 client += char(*p++);
1046 // --- 3. get function name ---
1048 while (*p && *p != ':')
1051 // --- 4. parse the argument ---
1053 if (!server_only && *p == ':' && *(++p)) {
1054 while (*p && *p != '\n')
1059 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1060 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1062 // --- lookup and exec the command ------------------
1066 // return the greeting to inform the client that
1067 // we are listening.
1068 if (cmd == "hello") {
1070 if (numclients_ == MAX_CLIENTS) { //paranoid check
1071 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1075 while (!clients_[i].empty() && i < numclients_)
1077 clients_[i] = client;
1079 buf = "LYXSRV:" + client + ":hello\n";
1080 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1082 } else if (cmd == "bye") {
1083 // If clients_ == 0 maybe we should reset the pipes
1084 // to prevent fake callbacks
1085 int i = 0; //look if client is registered
1086 for (; i < numclients_; ++i) {
1087 if (clients_[i] == client)
1090 if (i < numclients_) {
1092 clients_[i].erase();
1093 LYXERR(Debug::LYXSERVER, "Server: Client "
1094 << client << " said goodbye");
1096 LYXERR(Debug::LYXSERVER,
1097 "Server: ignoring bye messge from unregistered client" << client);
1100 LYXERR0("Server: Undefined server command " << cmd << '.');
1106 // which lyxfunc should we let it connect to?
1107 // The correct solution would be to have a
1108 // specialized (non-gui) BufferView. But how do
1109 // we do it now? Probably we should just let it
1110 // connect to the lyxfunc in the single LyXView we
1111 // support currently. (Lgb)
1113 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
1114 string const rval = to_utf8(func_->getMessage());
1116 // all commands produce an INFO or ERROR message
1117 // in the output pipe, even if they do not return
1118 // anything. See chapter 4 of Customization doc.
1120 if (func_->errorStat())
1124 buf += client + ':' + cmd + ':' + rval + '\n';
1127 // !!! we don't do any error checking -
1128 // if the client won't listen, the
1129 // message is lost and others too
1130 // maybe; so the client should empty
1131 // the outpipe before issuing a request.
1139 // Send a notify message to a client, called by WorkAreaKeyPress
1140 void Server::notifyClient(string const & s)
1142 pipes_.send("NOTIFY:" + s + "\n");
1149 #include "moc_Server.cpp"