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_; }
99 DWORD error = GetLastError();
100 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
101 FORMAT_MESSAGE_FROM_SYSTEM |
102 FORMAT_MESSAGE_IGNORE_INSERTS,
103 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
104 (LPTSTR) &msgbuf, 0, NULL);
105 return static_cast<char *>(msgbuf);
111 DWORD WINAPI pipeServerWrapper(void * arg)
113 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
114 lyxcomm->pipeServer();
122 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
123 : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
130 void LyXComm::pipeServer()
134 for (i = 0; i < MAX_PIPES; ++i) {
135 bool const is_outpipe = i >= MAX_CLIENTS;
136 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
137 : PIPE_ACCESS_INBOUND;
138 string const pipename = external_path(pipeName(i));
140 // Manual-reset event, initial state = signaled
141 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
143 lyxerr << "LyXComm: Could not create event for pipe "
144 << pipename.c_str() << "\nLyXComm: "
145 << errormsg() << endl;
150 pipe_[i].overlap.hEvent = event_[i];
151 pipe_[i].iobuf.erase();
152 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
153 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
154 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
157 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
158 lyxerr << "LyXComm: Could not create pipe "
159 << pipename.c_str() << "\nLyXComm: "
160 << errormsg() << endl;
166 pipe_[i].state = pipe_[i].pending_io ?
167 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
171 // Add the stopserver_ event
172 event_[MAX_PIPES] = stopserver_;
175 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
181 while (!checkStopServer()) {
182 // Indefinitely wait for the completion of an overlapped
183 // read, write, or connect operation.
184 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 1, event_,
187 // Determine which pipe instance completed the operation.
188 i = wait - WAIT_OBJECT_0;
189 LASSERT(i >= 0 && i <= MAX_PIPES, /**/);
191 // Check whether we were waked up for stopping the pipe server.
195 bool const is_outpipe = i >= MAX_CLIENTS;
197 // Get the result if the operation was pending.
198 if (pipe_[i].pending_io) {
199 success = GetOverlappedResult(pipe_[i].handle,
200 &pipe_[i].overlap, &status, FALSE);
202 switch (pipe_[i].state) {
203 case CONNECTING_STATE:
204 // Pending connect operation
206 lyxerr << "LyXComm: "
207 << errormsg() << endl;
211 pipe_[i].state = is_outpipe ? WRITING_STATE
216 // Pending read operation
217 LASSERT(!is_outpipe, /**/);
218 if (!success || status == 0) {
222 pipe_[i].nbytes = status;
223 pipe_[i].state = WRITING_STATE;
227 // Pending write operation
228 LASSERT(is_outpipe, /**/);
229 // Let's see whether we have a reply
230 if (!outbuf_.empty()) {
231 // Yep. Deliver it to all pipe
232 // instances if we get ownership
233 // of the mutex, otherwise we'll
234 // try again the next round.
235 DWORD result = WaitForSingleObject(
237 if (result == WAIT_OBJECT_0) {
238 DWORD j = MAX_CLIENTS;
239 while (j < MAX_PIPES) {
240 pipe_[j].iobuf = outbuf_;
245 ReleaseMutex(outbuf_mutex_);
247 if (pipe_[i].iobuf.empty())
248 pipe_[i].pending_io = false;
253 // Operate according to the pipe state
254 switch (pipe_[i].state) {
256 // The pipe instance is connected to a client
257 // and is ready to read a request.
258 LASSERT(!is_outpipe, /**/);
259 success = ReadFile(pipe_[i].handle,
260 pipe_[i].readbuf, PIPE_BUFSIZE - 1,
261 &pipe_[i].nbytes, &pipe_[i].overlap);
263 if (success && pipe_[i].nbytes != 0) {
264 // The read operation completed successfully.
265 pipe_[i].pending_io = false;
266 pipe_[i].state = WRITING_STATE;
270 if (!success && GetLastError() == ERROR_IO_PENDING) {
271 // The read operation is still pending.
272 pipe_[i].pending_io = true;
276 // Client closed connection (ERROR_BROKEN_PIPE) or
277 // an error occurred; in either case, reset the pipe.
278 if (GetLastError() != ERROR_BROKEN_PIPE) {
279 lyxerr << "LyXComm: " << errormsg() << endl;
280 if (!pipe_[i].iobuf.empty()) {
281 lyxerr << "LyXComm: truncated command: "
282 << pipe_[i].iobuf << endl;
283 pipe_[i].iobuf.erase();
292 // The request was successfully read
293 // from the client; commit it.
294 ReadReadyEvent * event = new ReadReadyEvent(i);
295 QCoreApplication::postEvent(this,
296 static_cast<QEvent *>(event));
297 // Wait for completion
298 while (pipe_[i].nbytes && !checkStopServer())
300 pipe_[i].pending_io = false;
301 pipe_[i].state = READING_STATE;
305 // This is an output pipe instance. Initiate the
306 // overlapped write operation or monitor its progress.
308 if (pipe_[i].pending_io) {
309 success = WriteFile(pipe_[i].handle,
310 pipe_[i].iobuf.c_str(),
311 pipe_[i].iobuf.length(),
316 if (success && !pipe_[i].iobuf.empty()
317 && status == pipe_[i].iobuf.length()) {
318 // The write operation completed successfully.
319 pipe_[i].iobuf.erase();
320 pipe_[i].pending_io = false;
325 if (GetLastError() == ERROR_IO_PENDING) {
326 // The write operation is still pending.
327 // We get here when a reader is started
328 // well before a reply is ready, so delay
329 // a bit in order to not burden the cpu.
331 pipe_[i].pending_io = true;
335 // Client closed connection (ERROR_NO_DATA) or
336 // an error occurred; in either case, reset the pipe.
337 if (GetLastError() != ERROR_NO_DATA) {
338 lyxerr << "LyXComm: Error sending message: "
339 << pipe_[i].iobuf << "\nLyXComm: "
340 << errormsg() << endl;
349 closeHandles(MAX_PIPES - 1);
353 void LyXComm::closeHandles(DWORD index)
355 for (int i = 0; i <= index; ++i) {
357 ResetEvent(event_[i]);
358 CloseHandle(event_[i]);
360 if (pipe_[i].handle != INVALID_HANDLE_VALUE)
361 CloseHandle(pipe_[i].handle);
366 bool LyXComm::event(QEvent * e)
368 if (e->type() == QEvent::User) {
369 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
376 BOOL LyXComm::checkStopServer()
378 return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0;
382 void LyXComm::startPipe(DWORD index)
384 pipe_[index].pending_io = false;
386 // Overlapped ConnectNamedPipe should return zero.
387 if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
388 // FIXME: What to do? Maybe the pipe server should be reset.
389 lyxerr << "LyXComm: Could not connect pipe "
390 << external_path(pipeName(index))
391 << "\nLyXComm: " << errormsg() << endl;
395 switch (GetLastError()) {
396 case ERROR_IO_PENDING:
397 // The overlapped connection is in progress.
398 pipe_[index].pending_io = true;
401 case ERROR_PIPE_CONNECTED:
402 // Client is already connected, so signal an event.
403 if (SetEvent(pipe_[index].overlap.hEvent))
406 // Anything else is an error.
407 // FIXME: What to do? Maybe the pipe server should be reset.
408 lyxerr << "LyXComm: An error occurred while connecting pipe "
409 << external_path(pipeName(index))
410 << "\nLyXComm: " << errormsg() << endl;
415 void LyXComm::resetPipe(DWORD index, bool close_handle)
417 // This method is called when an error occurs or when a client
418 // closes the connection. We first disconnect the pipe instance,
419 // then reconnect it, ready to wait for another client.
421 if (!DisconnectNamedPipe(pipe_[index].handle)) {
422 lyxerr << "LyXComm: Could not disconnect pipe "
423 << external_path(pipeName(index))
424 << "\nLyXComm: " << errormsg() << endl;
425 // What to do now? Let's try whether re-creating the pipe helps.
429 bool const is_outpipe = index >= MAX_CLIENTS;
432 DWORD const open_mode = is_outpipe ? PIPE_ACCESS_OUTBOUND
433 : PIPE_ACCESS_INBOUND;
434 string const name = external_path(pipeName(index));
436 CloseHandle(pipe_[index].handle);
438 pipe_[index].iobuf.erase();
439 pipe_[index].handle = CreateNamedPipe(name.c_str(),
440 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
441 MAX_CLIENTS, PIPE_BUFSIZE, PIPE_BUFSIZE,
444 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
445 lyxerr << "LyXComm: Could not reset pipe " << name
446 << "\nLyXComm: " << errormsg() << endl;
452 pipe_[index].state = pipe_[index].pending_io ?
453 CONNECTING_STATE : (is_outpipe ? WRITING_STATE
458 void LyXComm::openConnection()
460 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
462 // If we are up, that's an error
464 LYXERR(Debug::LYXSERVER, "LyXComm: Already connected");
467 // We assume that we don't make it
470 if (pipename_.empty()) {
471 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
475 // Check whether the pipe name is being used by some other program.
476 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
477 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
478 << " already exists.\nMaybe another instance of LyX"
479 " is using it." << endl;
484 // Mutex with no initial owner for synchronized access to outbuf_
485 outbuf_mutex_ = CreateMutex(NULL, FALSE, NULL);
486 if (!outbuf_mutex_) {
487 lyxerr << "LyXComm: Could not create output buffer mutex"
488 << "\nLyXComm: " << errormsg() << endl;
493 // Manual-reset event, initial state = not signaled
494 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
496 lyxerr << "LyXComm: Could not create stop server event"
497 << "\nLyXComm: " << errormsg() << endl;
499 CloseHandle(outbuf_mutex_);
503 server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
504 static_cast<void *>(this), 0, NULL);
505 if (!server_thread_) {
506 lyxerr << "LyXComm: Could not create pipe server thread"
507 << "\nLyXComm: " << errormsg() << endl;
509 CloseHandle(stopserver_);
510 CloseHandle(outbuf_mutex_);
517 void LyXComm::closeConnection()
519 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
521 if (pipename_.empty()) {
522 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
527 LYXERR(Debug::LYXSERVER, "LyXComm: Already disconnected");
531 SetEvent(stopserver_);
532 // Wait for the pipe server to finish
533 WaitForSingleObject(server_thread_, INFINITE);
534 CloseHandle(server_thread_);
535 ResetEvent(stopserver_);
536 CloseHandle(stopserver_);
537 CloseHandle(outbuf_mutex_);
541 void LyXComm::emergencyCleanup()
544 SetEvent(stopserver_);
545 // Forcibly terminate the pipe server thread if it does
546 // not finish quickly.
547 if (WaitForSingleObject(server_thread_, 200) != WAIT_OBJECT_0)
548 TerminateThread(server_thread_, 0);
549 CloseHandle(server_thread_);
550 ResetEvent(stopserver_);
551 CloseHandle(stopserver_);
552 CloseHandle(outbuf_mutex_);
557 void LyXComm::read_ready(DWORD inpipe)
559 // Turn the pipe buffer into a C string
560 DWORD const nbytes = pipe_[inpipe].nbytes;
561 pipe_[inpipe].readbuf[nbytes] = '\0';
563 pipe_[inpipe].iobuf += rtrim(pipe_[inpipe].readbuf, "\r");
565 // Commit any commands read
566 while (pipe_[inpipe].iobuf.find('\n') != string::npos) {
567 // split() grabs the entire string if
568 // the delim /wasn't/ found. ?:-P
570 pipe_[inpipe].iobuf = split(pipe_[inpipe].iobuf, cmd, '\n');
571 cmd = rtrim(cmd, "\r");
572 LYXERR(Debug::LYXSERVER, "LyXComm: nbytes:" << nbytes
573 << ", iobuf:" << pipe_[inpipe].iobuf
576 clientcb_(client_, cmd);
579 // Signal that we are done.
580 pipe_[inpipe].nbytes = 0;
584 void LyXComm::send(string const & msg)
587 lyxerr << "LyXComm: Request to send empty string. Ignoring."
592 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
594 if (pipename_.empty())
598 lyxerr << "LyXComm: Pipes are closed. Could not send "
603 // Request ownership of the outbuf_mutex_
604 DWORD result = WaitForSingleObject(outbuf_mutex_, PIPE_TIMEOUT);
606 if (result == WAIT_OBJECT_0) {
607 // If a client doesn't care to read a reply (JabRef is one
608 // such client), the output buffer could grow without limit.
609 // So, we empty it when its size is larger than PIPE_BUFSIZE.
610 if (outbuf_.size() > PIPE_BUFSIZE)
613 ReleaseMutex(outbuf_mutex_);
615 // Something is fishy, better resetting the connection.
616 lyxerr << "LyXComm: Error sending message: " << msg
617 << "\nLyXComm: " << errormsg()
618 << "LyXComm: Resetting connection" << endl;
619 ReleaseMutex(outbuf_mutex_);
621 if (!checkStopServer())
627 string const LyXComm::pipeName(DWORD index) const
629 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
633 #elif !defined (HAVE_MKFIFO)
634 // We provide a stub class that disables the lyxserver.
636 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
639 void LyXComm::openConnection()
643 void LyXComm::closeConnection()
647 int LyXComm::startPipe(string const & filename, bool write)
653 void LyXComm::endPipe(int & fd, string const & filename, bool write)
657 void LyXComm::emergencyCleanup()
660 void LyXComm::read_ready()
664 void LyXComm::send(string const & msg)
668 #else // defined (HAVE_MKFIFO)
671 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
672 : pipename_(pip), client_(cli), clientcb_(ccb)
679 void LyXComm::openConnection()
681 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
683 // If we are up, that's an error
685 lyxerr << "LyXComm: Already connected" << endl;
688 // We assume that we don't make it
691 if (pipename_.empty()) {
692 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
696 infd_ = startPipe(inPipeName(), false);
700 outfd_ = startPipe(outPipeName(), true);
702 endPipe(infd_, inPipeName(), false);
706 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
707 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
708 << '\n' << strerror(errno) << endl;
714 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
719 void LyXComm::closeConnection()
721 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
723 if (pipename_.empty()) {
724 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
729 LYXERR0("LyXComm: Already disconnected");
733 endPipe(infd_, inPipeName(), false);
734 endPipe(outfd_, outPipeName(), true);
740 int LyXComm::startPipe(string const & file, bool write)
742 static bool stalepipe = false;
743 FileName const filename(file);
744 if (filename.exists()) {
746 // Let's see whether we have a stale pipe.
747 int fd = ::open(filename.toFilesystemEncoding().c_str(),
748 O_WRONLY | O_NONBLOCK);
750 // Another LyX instance is using it.
752 } else if (errno == ENXIO) {
753 // No process is reading from the other end.
755 LYXERR(Debug::LYXSERVER,
756 "LyXComm: trying to remove "
758 filename.removeFile();
760 } else if (stalepipe) {
761 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
763 filename.removeFile();
766 if (filename.exists()) {
767 lyxerr << "LyXComm: Pipe " << filename
768 << " already exists.\nIf no other LyX program"
769 " is active, please delete the pipe by hand"
777 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
778 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
779 << strerror(errno) << endl;
782 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
783 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
786 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
787 << strerror(errno) << endl;
788 filename.removeFile();
793 theApp()->registerSocketCallback(fd,
794 boost::bind(&LyXComm::read_ready, this));
801 void LyXComm::endPipe(int & fd, string const & filename, bool write)
807 theApp()->unregisterSocketCallback(fd);
809 if (::close(fd) < 0) {
810 lyxerr << "LyXComm: Could not close pipe " << filename
811 << '\n' << strerror(errno) << endl;
814 if (FileName(filename).removeFile() < 0) {
815 lyxerr << "LyXComm: Could not remove pipe " << filename
816 << '\n' << strerror(errno) << endl;
823 void LyXComm::emergencyCleanup()
825 if (!pipename_.empty()) {
826 endPipe(infd_, inPipeName(), false);
827 endPipe(outfd_, outPipeName(), true);
832 // Receives messages and sends then to client
833 void LyXComm::read_ready()
835 // FIXME: make read_buffer_ a class-member for multiple sessions
836 static string read_buffer_;
837 read_buffer_.erase();
839 int const charbuf_size = 100;
840 char charbuf[charbuf_size];
842 // As O_NONBLOCK is set, until no data is available for reading,
843 // read() doesn't block but returns -1 and set errno to EAGAIN.
844 // After a client that opened the pipe for writing, closes it
845 // (and no other client is using the pipe), read() would always
846 // return 0 and thus the connection has to be reset.
850 // the single = is intended here.
851 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
854 charbuf[status] = '\0'; // turn it into a c string
855 read_buffer_ += rtrim(charbuf, "\r");
856 // commit any commands read
857 while (read_buffer_.find('\n') != string::npos) {
858 // split() grabs the entire string if
859 // the delim /wasn't/ found. ?:-P
861 read_buffer_= split(read_buffer_, cmd,'\n');
862 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
863 << ", read_buffer_:" << read_buffer_
866 clientcb_(client_, cmd);
870 if (errno == EAGAIN) {
871 // Nothing to read, continue
875 // An error occurred, better bailing out
876 LYXERR0("LyXComm: " << strerror(errno));
877 if (!read_buffer_.empty()) {
878 LYXERR0("LyXComm: truncated command: " << read_buffer_);
879 read_buffer_.erase();
881 break; // reset connection
885 // The connection gets reset when read() returns 0 (meaning that the
886 // last client closed the pipe) or an error occurred, in which case
887 // read() returns -1 and errno != EAGAIN.
894 void LyXComm::send(string const & msg)
897 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
901 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
903 if (pipename_.empty()) return;
906 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
907 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
908 lyxerr << "LyXComm: Error sending message: " << msg
909 << '\n' << strerror(errno)
910 << "\nLyXComm: Resetting connection" << endl;
916 #endif // defined (HAVE_MKFIFO)
919 string const LyXComm::inPipeName() const
921 return pipename_ + ".in";
925 string const LyXComm::outPipeName() const
927 return pipename_ + ".out";
931 /////////////////////////////////////////////////////////////////////
935 /////////////////////////////////////////////////////////////////////
937 void ServerCallback(Server * server, string const & msg)
939 server->callback(msg);
942 Server::Server(LyXFunc * f, string const & pipes)
943 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
949 // say goodbye to clients so they stop sending messages
950 // send as many bye messages as there are clients,
951 // each with client's name.
953 for (int i = 0; i != numclients_; ++i) {
954 message = "LYXSRV:" + clients_[i] + ":bye\n";
955 pipes_.send(message);
960 int compare(char const * a, char const * b, unsigned int len)
963 return strncmp(a, b, len);
967 // Handle data gotten from communication, called by LyXComm
968 void Server::callback(string const & msg)
970 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
972 char const * p = msg.c_str();
974 // --- parse the string --------------------------------------------
976 // Format: LYXCMD:<client>:<func>:<argstring>\n
978 bool server_only = false;
980 // --- 1. check 'header' ---
982 if (compare(p, "LYXSRV:", 7) == 0) {
984 } else if (0 != compare(p, "LYXCMD:", 7)) {
985 lyxerr << "Server: Unknown request \""
991 // --- 2. for the moment ignore the client name ---
993 while (*p && *p != ':')
994 client += char(*p++);
1000 // --- 3. get function name ---
1002 while (*p && *p != ':')
1005 // --- 4. parse the argument ---
1007 if (!server_only && *p == ':' && *(++p)) {
1008 while (*p && *p != '\n')
1013 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1014 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1016 // --- lookup and exec the command ------------------
1020 // return the greeting to inform the client that
1021 // we are listening.
1022 if (cmd == "hello") {
1024 if (numclients_ == MAX_CLIENTS) { //paranoid check
1025 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1029 while (!clients_[i].empty() && i < numclients_)
1031 clients_[i] = client;
1033 buf = "LYXSRV:" + client + ":hello\n";
1034 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1036 } else if (cmd == "bye") {
1037 // If clients_ == 0 maybe we should reset the pipes
1038 // to prevent fake callbacks
1039 int i = 0; //look if client is registered
1040 for (; i < numclients_; ++i) {
1041 if (clients_[i] == client)
1044 if (i < numclients_) {
1046 clients_[i].erase();
1047 LYXERR(Debug::LYXSERVER, "Server: Client "
1048 << client << " said goodbye");
1050 LYXERR(Debug::LYXSERVER,
1051 "Server: ignoring bye messge from unregistered client" << client);
1054 LYXERR0("Server: Undefined server command " << cmd << '.');
1060 // which lyxfunc should we let it connect to?
1061 // The correct solution would be to have a
1062 // specialized (non-gui) BufferView. But how do
1063 // we do it now? Probably we should just let it
1064 // connect to the lyxfunc in the single LyXView we
1065 // support currently. (Lgb)
1067 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
1068 string const rval = to_utf8(func_->getMessage());
1070 // all commands produce an INFO or ERROR message
1071 // in the output pipe, even if they do not return
1072 // anything. See chapter 4 of Customization doc.
1074 if (func_->errorStat())
1078 buf += client + ':' + cmd + ':' + rval + '\n';
1081 // !!! we don't do any error checking -
1082 // if the client won't listen, the
1083 // message is lost and others too
1084 // maybe; so the client should empty
1085 // the outpipe before issuing a request.
1093 // Send a notify message to a client, called by WorkAreaKeyPress
1094 void Server::notifyClient(string const & s)
1096 pipes_.send("NOTIFY:" + s + "\n");
1103 #include "moc_Server.cpp"