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(100))
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.
357 checkStopServer(100);
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(DWORD timeout)
411 return WaitForSingleObject(stopserver_, timeout) == 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_);
672 string const LyXComm::pipeName(DWORD index) const
674 return index < MAX_CLIENTS ? inPipeName() : outPipeName();
678 #elif !defined (HAVE_MKFIFO)
679 // We provide a stub class that disables the lyxserver.
681 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
684 void LyXComm::openConnection()
688 void LyXComm::closeConnection()
692 int LyXComm::startPipe(string const & filename, bool write)
698 void LyXComm::endPipe(int & fd, string const & filename, bool write)
702 void LyXComm::emergencyCleanup()
705 void LyXComm::read_ready()
709 void LyXComm::send(string const & msg)
713 #else // defined (HAVE_MKFIFO)
716 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
717 : pipename_(pip), client_(cli), clientcb_(ccb)
724 void LyXComm::openConnection()
726 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
728 // If we are up, that's an error
730 lyxerr << "LyXComm: Already connected" << endl;
733 // We assume that we don't make it
736 if (pipename_.empty()) {
737 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
741 infd_ = startPipe(inPipeName(), false);
745 outfd_ = startPipe(outPipeName(), true);
747 endPipe(infd_, inPipeName(), false);
751 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
752 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
753 << '\n' << strerror(errno) << endl;
759 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
764 void LyXComm::closeConnection()
766 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
768 if (pipename_.empty()) {
769 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
774 LYXERR0("LyXComm: Already disconnected");
778 endPipe(infd_, inPipeName(), false);
779 endPipe(outfd_, outPipeName(), true);
785 int LyXComm::startPipe(string const & file, bool write)
787 static bool stalepipe = false;
788 FileName const filename(file);
789 if (filename.exists()) {
791 // Let's see whether we have a stale pipe.
792 int fd = ::open(filename.toFilesystemEncoding().c_str(),
793 O_WRONLY | O_NONBLOCK);
795 // Another LyX instance is using it.
797 } else if (errno == ENXIO) {
798 // No process is reading from the other end.
800 LYXERR(Debug::LYXSERVER,
801 "LyXComm: trying to remove "
803 filename.removeFile();
805 } else if (stalepipe) {
806 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
808 filename.removeFile();
811 if (filename.exists()) {
812 lyxerr << "LyXComm: Pipe " << filename
813 << " already exists.\nIf no other LyX program"
814 " is active, please delete the pipe by hand"
822 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
823 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
824 << strerror(errno) << endl;
827 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
828 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
831 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
832 << strerror(errno) << endl;
833 filename.removeFile();
838 theApp()->registerSocketCallback(fd,
839 boost::bind(&LyXComm::read_ready, this));
846 void LyXComm::endPipe(int & fd, string const & filename, bool write)
852 theApp()->unregisterSocketCallback(fd);
854 if (::close(fd) < 0) {
855 lyxerr << "LyXComm: Could not close pipe " << filename
856 << '\n' << strerror(errno) << endl;
859 if (FileName(filename).removeFile() < 0) {
860 lyxerr << "LyXComm: Could not remove pipe " << filename
861 << '\n' << strerror(errno) << endl;
868 void LyXComm::emergencyCleanup()
870 if (!pipename_.empty()) {
871 endPipe(infd_, inPipeName(), false);
872 endPipe(outfd_, outPipeName(), true);
877 // Receives messages and sends then to client
878 void LyXComm::read_ready()
880 // FIXME: make read_buffer_ a class-member for multiple sessions
881 static string read_buffer_;
882 read_buffer_.erase();
884 int const charbuf_size = 100;
885 char charbuf[charbuf_size];
887 // As O_NONBLOCK is set, until no data is available for reading,
888 // read() doesn't block but returns -1 and set errno to EAGAIN.
889 // After a client that opened the pipe for writing, closes it
890 // (and no other client is using the pipe), read() would always
891 // return 0 and thus the connection has to be reset.
895 // the single = is intended here.
896 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
899 charbuf[status] = '\0'; // turn it into a c string
900 read_buffer_ += rtrim(charbuf, "\r");
901 // commit any commands read
902 while (read_buffer_.find('\n') != string::npos) {
903 // split() grabs the entire string if
904 // the delim /wasn't/ found. ?:-P
906 read_buffer_= split(read_buffer_, cmd,'\n');
907 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
908 << ", read_buffer_:" << read_buffer_
911 clientcb_(client_, cmd);
915 if (errno == EAGAIN) {
916 // Nothing to read, continue
920 // An error occurred, better bailing out
921 LYXERR0("LyXComm: " << strerror(errno));
922 if (!read_buffer_.empty()) {
923 LYXERR0("LyXComm: truncated command: " << read_buffer_);
924 read_buffer_.erase();
926 break; // reset connection
930 // The connection gets reset when read() returns 0 (meaning that the
931 // last client closed the pipe) or an error occurred, in which case
932 // read() returns -1 and errno != EAGAIN.
939 void LyXComm::send(string const & msg)
942 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
946 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
948 if (pipename_.empty()) return;
951 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
952 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
953 lyxerr << "LyXComm: Error sending message: " << msg
954 << '\n' << strerror(errno)
955 << "\nLyXComm: Resetting connection" << endl;
961 #endif // defined (HAVE_MKFIFO)
964 string const LyXComm::inPipeName() const
966 return pipename_ + ".in";
970 string const LyXComm::outPipeName() const
972 return pipename_ + ".out";
976 /////////////////////////////////////////////////////////////////////
980 /////////////////////////////////////////////////////////////////////
982 void ServerCallback(Server * server, string const & msg)
984 server->callback(msg);
987 Server::Server(LyXFunc * f, string const & pipes)
988 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
994 // say goodbye to clients so they stop sending messages
995 // send as many bye messages as there are clients,
996 // each with client's name.
998 for (int i = 0; i != numclients_; ++i) {
999 message = "LYXSRV:" + clients_[i] + ":bye\n";
1000 pipes_.send(message);
1005 int compare(char const * a, char const * b, unsigned int len)
1007 using namespace std;
1008 return strncmp(a, b, len);
1012 // Handle data gotten from communication, called by LyXComm
1013 void Server::callback(string const & msg)
1015 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
1017 char const * p = msg.c_str();
1019 // --- parse the string --------------------------------------------
1021 // Format: LYXCMD:<client>:<func>:<argstring>\n
1023 bool server_only = false;
1025 // --- 1. check 'header' ---
1027 if (compare(p, "LYXSRV:", 7) == 0) {
1029 } else if (0 != compare(p, "LYXCMD:", 7)) {
1030 lyxerr << "Server: Unknown request \""
1031 << p << '"' << endl;
1036 // --- 2. for the moment ignore the client name ---
1038 while (*p && *p != ':')
1039 client += char(*p++);
1045 // --- 3. get function name ---
1047 while (*p && *p != ':')
1050 // --- 4. parse the argument ---
1052 if (!server_only && *p == ':' && *(++p)) {
1053 while (*p && *p != '\n')
1058 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
1059 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
1061 // --- lookup and exec the command ------------------
1065 // return the greeting to inform the client that
1066 // we are listening.
1067 if (cmd == "hello") {
1069 if (numclients_ == MAX_CLIENTS) { //paranoid check
1070 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1074 while (!clients_[i].empty() && i < numclients_)
1076 clients_[i] = client;
1078 buf = "LYXSRV:" + client + ":hello\n";
1079 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1081 } else if (cmd == "bye") {
1082 // If clients_ == 0 maybe we should reset the pipes
1083 // to prevent fake callbacks
1084 int i = 0; //look if client is registered
1085 for (; i < numclients_; ++i) {
1086 if (clients_[i] == client)
1089 if (i < numclients_) {
1091 clients_[i].erase();
1092 LYXERR(Debug::LYXSERVER, "Server: Client "
1093 << client << " said goodbye");
1095 LYXERR(Debug::LYXSERVER,
1096 "Server: ignoring bye messge from unregistered client" << client);
1099 LYXERR0("Server: Undefined server command " << cmd << '.');
1105 // which lyxfunc should we let it connect to?
1106 // The correct solution would be to have a
1107 // specialized (non-gui) BufferView. But how do
1108 // we do it now? Probably we should just let it
1109 // connect to the lyxfunc in the single LyXView we
1110 // support currently. (Lgb)
1112 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
1113 string const rval = to_utf8(func_->getMessage());
1115 // all commands produce an INFO or ERROR message
1116 // in the output pipe, even if they do not return
1117 // anything. See chapter 4 of Customization doc.
1119 if (func_->errorStat())
1123 buf += client + ':' + cmd + ':' + rval + '\n';
1126 // !!! we don't do any error checking -
1127 // if the client won't listen, the
1128 // message is lost and others too
1129 // maybe; so the client should empty
1130 // the outpipe before issuing a request.
1138 // Send a notify message to a client, called by WorkAreaKeyPress
1139 void Server::notifyClient(string const & s)
1141 pipes_.send("NOTIFY:" + s + "\n");
1148 #include "moc_Server.cpp"