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/lstrings.h"
53 #include "support/os.h"
55 #include <boost/bind.hpp>
58 #include <QCoreApplication>
62 #ifdef HAVE_SYS_STAT_H
63 # include <sys/stat.h>
68 using namespace lyx::support;
69 using os::external_path;
73 /////////////////////////////////////////////////////////////////////
77 /////////////////////////////////////////////////////////////////////
81 class PipeEvent : public QEvent {
84 PipeEvent(HANDLE inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
87 HANDLE pipe() const { return inpipe_; }
95 bool closing_ = false;
106 DWORD error = GetLastError();
107 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
108 FORMAT_MESSAGE_FROM_SYSTEM |
109 FORMAT_MESSAGE_IGNORE_INSERTS,
110 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
111 (LPTSTR) &msgbuf, 0, NULL);
112 return static_cast<char *>(msgbuf);
118 DWORD WINAPI pipeServerWrapper(void * arg)
120 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
121 lyxcomm->pipeServer();
129 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
130 : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
132 // Ask Qt to notify us on quit.
133 qAddPostRoutine(closing);
139 void LyXComm::pipeServer()
141 int const bufsize = 1024;
144 outpipe_ = CreateNamedPipe(external_path(outPipeName()).c_str(),
145 PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT,
146 PIPE_UNLIMITED_INSTANCES,
147 bufsize, 0, 0, NULL);
148 if (outpipe_ == INVALID_HANDLE_VALUE) {
149 lyxerr << "LyXComm: Could not create pipe "
150 << external_path(outPipeName()) << '\n'
151 << errormsg() << endl;
154 ConnectNamedPipe(outpipe_, NULL);
155 // Now change to blocking mode
156 DWORD mode = PIPE_WAIT;
157 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
159 while (!checkStopServerEvent()) {
160 inpipe = CreateNamedPipe(external_path(inPipeName()).c_str(),
161 PIPE_ACCESS_INBOUND, PIPE_WAIT,
162 PIPE_UNLIMITED_INSTANCES,
163 0, bufsize, 0, NULL);
164 if (inpipe == INVALID_HANDLE_VALUE) {
165 lyxerr << "LyXComm: Could not create pipe "
166 << external_path(inPipeName()) << '\n'
167 << errormsg() << endl;
171 BOOL connected = ConnectNamedPipe(inpipe, NULL);
172 // Check whether we are signaled to shutdown the pipe server.
173 if (checkStopServerEvent()) {
177 if (connected || GetLastError() == ERROR_PIPE_CONNECTED) {
178 PipeEvent * event = new PipeEvent(inpipe);
179 QCoreApplication::postEvent(this,
180 static_cast<QEvent *>(event));
184 CloseHandle(outpipe_);
188 bool LyXComm::event(QEvent * e)
190 if (e->type() == QEvent::User) {
191 read_ready(static_cast<PipeEvent *>(e)->pipe());
198 BOOL LyXComm::checkStopServerEvent()
200 return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0 || closing_;
204 void LyXComm::openConnection()
206 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
208 // If we are up, that's an error
210 lyxerr << "LyXComm: Already connected" << endl;
213 // We assume that we don't make it
216 if (pipename_.empty()) {
217 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
221 // Check whether the pipe is being used by some other program.
222 if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
223 lyxerr << "LyXComm: Pipe " << external_path(inPipeName())
224 << " already exists.\nMaybe another instance of LyX"
231 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
233 HANDLE thread = CreateThread(NULL, 0, pipeServerWrapper,
234 static_cast<void *>(this), 0, &tid);
236 lyxerr << "LyXComm: Could not create pipe server thread: "
237 << errormsg() << endl;
244 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
249 void LyXComm::closeConnection()
251 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
253 if (pipename_.empty()) {
254 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
259 LYXERR0("LyXComm: Already disconnected");
263 SetEvent(stopserver_);
264 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
265 GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
266 FILE_ATTRIBUTE_NORMAL, NULL);
267 if (hpipe != INVALID_HANDLE_VALUE)
270 ResetEvent(stopserver_);
271 CloseHandle(stopserver_);
276 void LyXComm::emergencyCleanup()
278 if (!pipename_.empty()) {
279 SetEvent(stopserver_);
280 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
281 GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
282 FILE_ATTRIBUTE_NORMAL, NULL);
283 if (hpipe != INVALID_HANDLE_VALUE)
286 ResetEvent(stopserver_);
287 CloseHandle(stopserver_);
291 void LyXComm::read_ready(HANDLE inpipe)
294 read_buffer_.erase();
296 int const charbuf_size = 100;
297 char charbuf[charbuf_size];
300 while (ReadFile(inpipe, charbuf, charbuf_size - 1, &status, NULL)) {
302 charbuf[status] = '\0'; // turn it into a c string
303 read_buffer_ += rtrim(charbuf, "\r");
304 // commit any commands read
305 while (read_buffer_.find('\n') != string::npos) {
306 // split() grabs the entire string if
307 // the delim /wasn't/ found. ?:-P
309 read_buffer_= split(read_buffer_, cmd, '\n');
310 cmd = rtrim(cmd, "\r");
311 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
312 << ", read_buffer_:" << read_buffer_
315 clientcb_(client_, cmd);
320 if (GetLastError() != ERROR_BROKEN_PIPE) {
322 LYXERR0("LyXComm: " << errormsg());
323 if (!read_buffer_.empty()) {
324 LYXERR0("LyXComm: truncated command: " << read_buffer_);
325 read_buffer_.erase();
328 // Client closed the pipe, so disconnect and close.
329 DisconnectNamedPipe(inpipe);
331 FlushFileBuffers(outpipe_);
332 DisconnectNamedPipe(outpipe_);
333 // Temporarily change to non-blocking mode otherwise
334 // ConnectNamedPipe() would block waiting for a connection.
335 DWORD mode = PIPE_NOWAIT;
336 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
337 ConnectNamedPipe(outpipe_, NULL);
339 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
343 void LyXComm::send(string const & msg)
346 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
350 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
352 if (pipename_.empty()) return;
355 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
363 success = WriteFile(outpipe_, msg.c_str(), msg.length(), &sent, NULL);
365 DWORD error = GetLastError();
366 if (error == ERROR_NO_DATA) {
367 DisconnectNamedPipe(outpipe_);
368 DWORD mode = PIPE_NOWAIT;
369 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
370 ConnectNamedPipe(outpipe_, NULL);
372 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
373 } else if (error != ERROR_PIPE_LISTENING)
378 } while (!success && count < 100 && !checkStopServerEvent());
381 lyxerr << "LyXComm: Error sending message: " << msg
382 << '\n' << errormsg()
383 << "\nLyXComm: Resetting connection" << endl;
385 if (!checkStopServerEvent())
391 #elif !defined (HAVE_MKFIFO)
392 // We provide a stub class that disables the lyxserver.
394 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
397 void LyXComm::openConnection()
401 void LyXComm::closeConnection()
405 int LyXComm::startPipe(string const & filename, bool write)
411 void LyXComm::endPipe(int & fd, string const & filename, bool write)
415 void LyXComm::emergencyCleanup()
418 void LyXComm::read_ready()
422 void LyXComm::send(string const & msg)
426 #else // defined (HAVE_MKFIFO)
429 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
430 : pipename_(pip), client_(cli), clientcb_(ccb)
437 void LyXComm::openConnection()
439 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
441 // If we are up, that's an error
443 lyxerr << "LyXComm: Already connected" << endl;
446 // We assume that we don't make it
449 if (pipename_.empty()) {
450 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
454 infd_ = startPipe(inPipeName(), false);
458 outfd_ = startPipe(outPipeName(), true);
460 endPipe(infd_, inPipeName(), false);
464 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
465 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
466 << '\n' << strerror(errno) << endl;
472 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
477 void LyXComm::closeConnection()
479 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
481 if (pipename_.empty()) {
482 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
487 LYXERR0("LyXComm: Already disconnected");
491 endPipe(infd_, inPipeName(), false);
492 endPipe(outfd_, outPipeName(), true);
498 int LyXComm::startPipe(string const & file, bool write)
500 static bool stalepipe = false;
501 FileName const filename(file);
502 if (filename.exists()) {
504 // Let's see whether we have a stale pipe.
505 int fd = ::open(filename.toFilesystemEncoding().c_str(),
506 O_WRONLY | O_NONBLOCK);
508 // Another LyX instance is using it.
510 } else if (errno == ENXIO) {
511 // No process is reading from the other end.
513 LYXERR(Debug::LYXSERVER,
514 "LyXComm: trying to remove "
516 filename.removeFile();
518 } else if (stalepipe) {
519 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
521 filename.removeFile();
524 if (filename.exists()) {
525 lyxerr << "LyXComm: Pipe " << filename
526 << " already exists.\nIf no other LyX program"
527 " is active, please delete the pipe by hand"
535 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
536 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
537 << strerror(errno) << endl;
540 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
541 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
544 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
545 << strerror(errno) << endl;
546 filename.removeFile();
551 theApp()->registerSocketCallback(fd,
552 boost::bind(&LyXComm::read_ready, this));
559 void LyXComm::endPipe(int & fd, string const & filename, bool write)
565 theApp()->unregisterSocketCallback(fd);
567 if (::close(fd) < 0) {
568 lyxerr << "LyXComm: Could not close pipe " << filename
569 << '\n' << strerror(errno) << endl;
572 if (FileName(filename).removeFile() < 0) {
573 lyxerr << "LyXComm: Could not remove pipe " << filename
574 << '\n' << strerror(errno) << endl;
581 void LyXComm::emergencyCleanup()
583 if (!pipename_.empty()) {
584 endPipe(infd_, inPipeName(), false);
585 endPipe(outfd_, outPipeName(), true);
590 // Receives messages and sends then to client
591 void LyXComm::read_ready()
593 // FIXME: make read_buffer_ a class-member for multiple sessions
594 static string read_buffer_;
595 read_buffer_.erase();
597 int const charbuf_size = 100;
598 char charbuf[charbuf_size];
600 // As O_NONBLOCK is set, until no data is available for reading,
601 // read() doesn't block but returns -1 and set errno to EAGAIN.
602 // After a client that opened the pipe for writing, closes it
603 // (and no other client is using the pipe), read() would always
604 // return 0 and thus the connection has to be reset.
608 // the single = is intended here.
609 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
612 charbuf[status] = '\0'; // turn it into a c string
613 read_buffer_ += rtrim(charbuf, "\r");
614 // commit any commands read
615 while (read_buffer_.find('\n') != string::npos) {
616 // split() grabs the entire string if
617 // the delim /wasn't/ found. ?:-P
619 read_buffer_= split(read_buffer_, cmd,'\n');
620 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
621 << ", read_buffer_:" << read_buffer_
624 clientcb_(client_, cmd);
628 if (errno == EAGAIN) {
629 // Nothing to read, continue
633 // An error occurred, better bailing out
634 LYXERR0("LyXComm: " << strerror(errno));
635 if (!read_buffer_.empty()) {
636 LYXERR0("LyXComm: truncated command: " << read_buffer_);
637 read_buffer_.erase();
639 break; // reset connection
643 // The connection gets reset when read() returns 0 (meaning that the
644 // last client closed the pipe) or an error occurred, in which case
645 // read() returns -1 and errno != EAGAIN.
652 void LyXComm::send(string const & msg)
655 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
659 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
661 if (pipename_.empty()) return;
664 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
665 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
666 lyxerr << "LyXComm: Error sending message: " << msg
667 << '\n' << strerror(errno)
668 << "\nLyXComm: Resetting connection" << endl;
674 #endif // defined (HAVE_MKFIFO)
677 string const LyXComm::inPipeName() const
679 return pipename_ + ".in";
683 string const LyXComm::outPipeName() const
685 return pipename_ + ".out";
689 /////////////////////////////////////////////////////////////////////
693 /////////////////////////////////////////////////////////////////////
695 void ServerCallback(Server * server, string const & msg)
697 server->callback(msg);
700 Server::Server(LyXFunc * f, string const & pipes)
701 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
707 // say goodbye to clients so they stop sending messages
708 // send as many bye messages as there are clients,
709 // each with client's name.
711 for (int i = 0; i != numclients_; ++i) {
712 message = "LYXSRV:" + clients_[i] + ":bye\n";
713 pipes_.send(message);
718 int compare(char const * a, char const * b, unsigned int len)
721 return strncmp(a, b, len);
725 // Handle data gotten from communication, called by LyXComm
726 void Server::callback(string const & msg)
728 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
730 char const * p = msg.c_str();
732 // --- parse the string --------------------------------------------
734 // Format: LYXCMD:<client>:<func>:<argstring>\n
736 bool server_only = false;
738 // --- 1. check 'header' ---
740 if (compare(p, "LYXSRV:", 7) == 0) {
742 } else if (0 != compare(p, "LYXCMD:", 7)) {
743 lyxerr << "Server: Unknown request \""
749 // --- 2. for the moment ignore the client name ---
751 while (*p && *p != ':')
752 client += char(*p++);
758 // --- 3. get function name ---
760 while (*p && *p != ':')
763 // --- 4. parse the argument ---
765 if (!server_only && *p == ':' && *(++p)) {
766 while (*p && *p != '\n')
771 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
772 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
774 // --- lookup and exec the command ------------------
778 // return the greeting to inform the client that
780 if (cmd == "hello") {
782 if (numclients_ == MAX_CLIENTS) { //paranoid check
783 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
787 while (!clients_[i].empty() && i < numclients_)
789 clients_[i] = client;
791 buf = "LYXSRV:" + client + ":hello\n";
792 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
794 } else if (cmd == "bye") {
795 // If clients_ == 0 maybe we should reset the pipes
796 // to prevent fake callbacks
797 int i = 0; //look if client is registered
798 for (; i < numclients_; ++i) {
799 if (clients_[i] == client)
802 if (i < numclients_) {
805 LYXERR(Debug::LYXSERVER, "Server: Client "
806 << client << " said goodbye");
808 LYXERR(Debug::LYXSERVER,
809 "Server: ignoring bye messge from unregistered client" << client);
812 LYXERR0("Server: Undefined server command " << cmd << '.');
818 // which lyxfunc should we let it connect to?
819 // The correct solution would be to have a
820 // specialized (non-gui) BufferView. But how do
821 // we do it now? Probably we should just let it
822 // connect to the lyxfunc in the single LyXView we
823 // support currently. (Lgb)
825 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
826 string const rval = to_utf8(func_->getMessage());
828 // all commands produce an INFO or ERROR message
829 // in the output pipe, even if they do not return
830 // anything. See chapter 4 of Customization doc.
832 if (func_->errorStat())
836 buf += client + ':' + cmd + ':' + rval + '\n';
839 // !!! we don't do any error checking -
840 // if the client won't listen, the
841 // message is lost and others too
842 // maybe; so the client should empty
843 // the outpipe before issuing a request.
851 // Send a notify message to a client, called by WorkAreaKeyPress
852 void Server::notifyClient(string const & s)
854 pipes_.send("NOTIFY:" + s + "\n");
861 #include "moc_Server.cpp"