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_; }
98 DWORD error = GetLastError();
99 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
100 FORMAT_MESSAGE_FROM_SYSTEM |
101 FORMAT_MESSAGE_IGNORE_INSERTS,
102 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
103 (LPTSTR) &msgbuf, 0, NULL);
104 return static_cast<char *>(msgbuf);
110 DWORD WINAPI pipeServerWrapper(void * arg)
112 LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
113 lyxcomm->pipeServer();
121 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
122 : pipename_(pip), client_(cli), clientcb_(ccb)
129 void LyXComm::pipeServer()
131 int const bufsize = 1024;
134 outpipe_ = CreateNamedPipe(external_path(outPipeName()).c_str(),
135 PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT,
136 PIPE_UNLIMITED_INSTANCES,
137 bufsize, 0, 0, NULL);
138 if (outpipe_ == INVALID_HANDLE_VALUE) {
139 lyxerr << "LyXComm: Could not create pipe "
140 << outPipeName() << '\n' << errormsg() << endl;
143 ConnectNamedPipe(outpipe_, NULL);
144 // Now change to blocking mode
145 DWORD mode = PIPE_WAIT;
146 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
148 while (!checkStopServerEvent()) {
149 inpipe = CreateNamedPipe(external_path(inPipeName()).c_str(),
150 PIPE_ACCESS_INBOUND, PIPE_WAIT,
151 PIPE_UNLIMITED_INSTANCES,
152 0, bufsize, 0, NULL);
153 if (inpipe == INVALID_HANDLE_VALUE) {
154 lyxerr << "LyXComm: Could not create pipe "
155 << inPipeName() << '\n' << errormsg() << endl;
159 BOOL connected = ConnectNamedPipe(inpipe, NULL);
160 // Check whether we are signaled to shutdown the pipe server.
161 if (checkStopServerEvent()) {
165 if (connected || GetLastError() == ERROR_PIPE_CONNECTED) {
166 PipeEvent * event = new PipeEvent(inpipe);
167 QCoreApplication::postEvent(this,
168 static_cast<QEvent *>(event));
172 CloseHandle(outpipe_);
173 CloseHandle(stopserver_);
177 bool LyXComm::event(QEvent * e)
179 if (e->type() == QEvent::User) {
180 read_ready(static_cast<PipeEvent *>(e)->pipe());
187 BOOL LyXComm::checkStopServerEvent()
189 return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0;
193 void LyXComm::openConnection()
195 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
197 // If we are up, that's an error
199 lyxerr << "LyXComm: Already connected" << endl;
202 // We assume that we don't make it
205 if (pipename_.empty()) {
206 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
210 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
212 HANDLE thread = CreateThread(NULL, 0, pipeServerWrapper,
213 static_cast<void *>(this), 0, &tid);
215 lyxerr << "LyXComm: Could not create pipe server thread: "
216 << errormsg() << endl;
223 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
228 void LyXComm::closeConnection()
230 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
232 if (pipename_.empty()) {
233 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
238 LYXERR0("LyXComm: Already disconnected");
242 SetEvent(stopserver_);
243 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
244 GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
245 FILE_ATTRIBUTE_NORMAL, NULL);
246 if (hpipe != INVALID_HANDLE_VALUE)
253 void LyXComm::emergencyCleanup()
255 if (!pipename_.empty()) {
256 SetEvent(stopserver_);
257 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
258 GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
259 FILE_ATTRIBUTE_NORMAL, NULL);
260 if (hpipe != INVALID_HANDLE_VALUE)
265 void LyXComm::read_ready(HANDLE inpipe)
268 read_buffer_.erase();
270 int const charbuf_size = 100;
271 char charbuf[charbuf_size];
274 while (ReadFile(inpipe, charbuf, charbuf_size - 1, &status, NULL)) {
276 charbuf[status] = '\0'; // turn it into a c string
277 read_buffer_ += rtrim(charbuf, "\r");
278 // commit any commands read
279 while (read_buffer_.find('\n') != string::npos) {
280 // split() grabs the entire string if
281 // the delim /wasn't/ found. ?:-P
283 read_buffer_= split(read_buffer_, cmd, '\n');
284 cmd = rtrim(cmd, "\r");
285 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
286 << ", read_buffer_:" << read_buffer_
289 clientcb_(client_, cmd);
294 if (GetLastError() != ERROR_BROKEN_PIPE) {
296 LYXERR0("LyXComm: " << errormsg());
297 if (!read_buffer_.empty()) {
298 LYXERR0("LyXComm: truncated command: " << read_buffer_);
299 read_buffer_.erase();
302 // Client closed the pipe, so disconnect and close.
303 DisconnectNamedPipe(inpipe);
305 FlushFileBuffers(outpipe_);
306 DisconnectNamedPipe(outpipe_);
307 // Temporarily change to non-blocking mode otherwise
308 // ConnectNamedPipe() would block waiting for a connection.
309 DWORD mode = PIPE_NOWAIT;
310 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
311 ConnectNamedPipe(outpipe_, NULL);
313 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
317 void LyXComm::send(string const & msg)
320 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
324 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
326 if (pipename_.empty()) return;
329 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
337 success = WriteFile(outpipe_, msg.c_str(), msg.length(), &sent, NULL);
339 DWORD error = GetLastError();
340 if (error == ERROR_NO_DATA) {
341 DisconnectNamedPipe(outpipe_);
342 DWORD mode = PIPE_NOWAIT;
343 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
344 ConnectNamedPipe(outpipe_, NULL);
346 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
347 } else if (error != ERROR_PIPE_LISTENING)
352 } while (!success && count < 100);
355 lyxerr << "LyXComm: Error sending message: " << msg
356 << '\n' << errormsg()
357 << "\nLyXComm: Resetting connection" << endl;
364 #elif !defined (HAVE_MKFIFO)
365 // We provide a stub class that disables the lyxserver.
367 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
370 void LyXComm::openConnection()
374 void LyXComm::closeConnection()
378 int LyXComm::startPipe(string const & filename, bool write)
384 void LyXComm::endPipe(int & fd, string const & filename, bool write)
388 void LyXComm::emergencyCleanup()
391 void LyXComm::read_ready()
395 void LyXComm::send(string const & msg)
399 #else // defined (HAVE_MKFIFO)
402 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
403 : pipename_(pip), client_(cli), clientcb_(ccb)
410 void LyXComm::openConnection()
412 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
414 // If we are up, that's an error
416 lyxerr << "LyXComm: Already connected" << endl;
419 // We assume that we don't make it
422 if (pipename_.empty()) {
423 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
427 infd_ = startPipe(inPipeName(), false);
431 outfd_ = startPipe(outPipeName(), true);
433 endPipe(infd_, inPipeName(), false);
437 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
438 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
439 << '\n' << strerror(errno) << endl;
445 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
450 void LyXComm::closeConnection()
452 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
454 if (pipename_.empty()) {
455 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
460 LYXERR0("LyXComm: Already disconnected");
464 endPipe(infd_, inPipeName(), false);
465 endPipe(outfd_, outPipeName(), true);
471 int LyXComm::startPipe(string const & file, bool write)
473 static bool stalepipe = false;
474 FileName const filename(file);
475 if (filename.exists()) {
477 // Let's see whether we have a stale pipe.
478 int fd = ::open(filename.toFilesystemEncoding().c_str(),
479 O_WRONLY | O_NONBLOCK);
481 // Another LyX instance is using it.
483 } else if (errno == ENXIO) {
484 // No process is reading from the other end.
486 LYXERR(Debug::LYXSERVER,
487 "LyXComm: trying to remove "
489 filename.removeFile();
491 } else if (stalepipe) {
492 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
494 filename.removeFile();
497 if (filename.exists()) {
498 lyxerr << "LyXComm: Pipe " << filename
499 << " already exists.\nIf no other LyX program"
500 " is active, please delete the pipe by hand"
508 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
509 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
510 << strerror(errno) << endl;
513 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
514 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
517 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
518 << strerror(errno) << endl;
519 filename.removeFile();
524 theApp()->registerSocketCallback(fd,
525 boost::bind(&LyXComm::read_ready, this));
532 void LyXComm::endPipe(int & fd, string const & filename, bool write)
538 theApp()->unregisterSocketCallback(fd);
540 if (::close(fd) < 0) {
541 lyxerr << "LyXComm: Could not close pipe " << filename
542 << '\n' << strerror(errno) << endl;
545 if (FileName(filename).removeFile() < 0) {
546 lyxerr << "LyXComm: Could not remove pipe " << filename
547 << '\n' << strerror(errno) << endl;
554 void LyXComm::emergencyCleanup()
556 if (!pipename_.empty()) {
557 endPipe(infd_, inPipeName(), false);
558 endPipe(outfd_, outPipeName(), true);
563 // Receives messages and sends then to client
564 void LyXComm::read_ready()
566 // FIXME: make read_buffer_ a class-member for multiple sessions
567 static string read_buffer_;
568 read_buffer_.erase();
570 int const charbuf_size = 100;
571 char charbuf[charbuf_size];
573 // As O_NONBLOCK is set, until no data is available for reading,
574 // read() doesn't block but returns -1 and set errno to EAGAIN.
575 // After a client that opened the pipe for writing, closes it
576 // (and no other client is using the pipe), read() would always
577 // return 0 and thus the connection has to be reset.
581 // the single = is intended here.
582 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
585 charbuf[status] = '\0'; // turn it into a c string
586 read_buffer_ += rtrim(charbuf, "\r");
587 // commit any commands read
588 while (read_buffer_.find('\n') != string::npos) {
589 // split() grabs the entire string if
590 // the delim /wasn't/ found. ?:-P
592 read_buffer_= split(read_buffer_, cmd,'\n');
593 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
594 << ", read_buffer_:" << read_buffer_
597 clientcb_(client_, cmd);
601 if (errno == EAGAIN) {
602 // Nothing to read, continue
606 // An error occurred, better bailing out
607 LYXERR0("LyXComm: " << strerror(errno));
608 if (!read_buffer_.empty()) {
609 LYXERR0("LyXComm: truncated command: " << read_buffer_);
610 read_buffer_.erase();
612 break; // reset connection
616 // The connection gets reset when read() returns 0 (meaning that the
617 // last client closed the pipe) or an error occurred, in which case
618 // read() returns -1 and errno != EAGAIN.
625 void LyXComm::send(string const & msg)
628 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
632 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
634 if (pipename_.empty()) return;
637 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
638 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
639 lyxerr << "LyXComm: Error sending message: " << msg
640 << '\n' << strerror(errno)
641 << "\nLyXComm: Resetting connection" << endl;
647 #endif // defined (HAVE_MKFIFO)
650 string const LyXComm::inPipeName() const
652 return pipename_ + ".in";
656 string const LyXComm::outPipeName() const
658 return pipename_ + ".out";
662 /////////////////////////////////////////////////////////////////////
666 /////////////////////////////////////////////////////////////////////
668 void ServerCallback(Server * server, string const & msg)
670 server->callback(msg);
673 Server::Server(LyXFunc * f, string const & pipes)
674 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
680 // say goodbye to clients so they stop sending messages
681 // send as many bye messages as there are clients,
682 // each with client's name.
684 for (int i = 0; i != numclients_; ++i) {
685 message = "LYXSRV:" + clients_[i] + ":bye\n";
686 pipes_.send(message);
691 int compare(char const * a, char const * b, unsigned int len)
694 return strncmp(a, b, len);
698 // Handle data gotten from communication, called by LyXComm
699 void Server::callback(string const & msg)
701 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
703 char const * p = msg.c_str();
705 // --- parse the string --------------------------------------------
707 // Format: LYXCMD:<client>:<func>:<argstring>\n
709 bool server_only = false;
711 // --- 1. check 'header' ---
713 if (compare(p, "LYXSRV:", 7) == 0) {
715 } else if (0 != compare(p, "LYXCMD:", 7)) {
716 lyxerr << "Server: Unknown request \""
722 // --- 2. for the moment ignore the client name ---
724 while (*p && *p != ':')
725 client += char(*p++);
731 // --- 3. get function name ---
733 while (*p && *p != ':')
736 // --- 4. parse the argument ---
738 if (!server_only && *p == ':' && *(++p)) {
739 while (*p && *p != '\n')
744 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
745 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
747 // --- lookup and exec the command ------------------
751 // return the greeting to inform the client that
753 if (cmd == "hello") {
755 if (numclients_ == MAX_CLIENTS) { //paranoid check
756 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
760 while (!clients_[i].empty() && i < numclients_)
762 clients_[i] = client;
764 buf = "LYXSRV:" + client + ":hello\n";
765 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
767 } else if (cmd == "bye") {
768 // If clients_ == 0 maybe we should reset the pipes
769 // to prevent fake callbacks
770 int i = 0; //look if client is registered
771 for (; i < numclients_; ++i) {
772 if (clients_[i] == client)
775 if (i < numclients_) {
778 LYXERR(Debug::LYXSERVER, "Server: Client "
779 << client << " said goodbye");
781 LYXERR(Debug::LYXSERVER,
782 "Server: ignoring bye messge from unregistered client" << client);
785 LYXERR0("Server: Undefined server command " << cmd << '.');
791 // which lyxfunc should we let it connect to?
792 // The correct solution would be to have a
793 // specialized (non-gui) BufferView. But how do
794 // we do it now? Probably we should just let it
795 // connect to the lyxfunc in the single LyXView we
796 // support currently. (Lgb)
798 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
799 string const rval = to_utf8(func_->getMessage());
801 // all commands produce an INFO or ERROR message
802 // in the output pipe, even if they do not return
803 // anything. See chapter 4 of Customization doc.
805 if (func_->errorStat())
809 buf += client + ':' + cmd + ':' + rval + '\n';
812 // !!! we don't do any error checking -
813 // if the client won't listen, the
814 // message is lost and others too
815 // maybe; so the client should empty
816 // the outpipe before issuing a request.
824 // Send a notify message to a client, called by WorkAreaKeyPress
825 void Server::notifyClient(string const & s)
827 pipes_.send("NOTIFY:" + s + "\n");
834 #include "moc_Server.cpp"