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_);
176 bool LyXComm::event(QEvent * e)
178 if (e->type() == QEvent::User) {
179 read_ready(static_cast<PipeEvent *>(e)->pipe());
186 BOOL LyXComm::checkStopServerEvent()
188 return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0;
192 void LyXComm::openConnection()
194 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
196 // If we are up, that's an error
198 lyxerr << "LyXComm: Already connected" << endl;
201 // We assume that we don't make it
204 if (pipename_.empty()) {
205 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
209 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
211 HANDLE thread = CreateThread(NULL, 0, pipeServerWrapper,
212 static_cast<void *>(this), 0, &tid);
214 lyxerr << "LyXComm: Could not create pipe server thread: "
215 << errormsg() << endl;
222 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
227 void LyXComm::closeConnection()
229 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
231 if (pipename_.empty()) {
232 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
237 LYXERR0("LyXComm: Already disconnected");
241 SetEvent(stopserver_);
242 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
243 GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
244 FILE_ATTRIBUTE_NORMAL, NULL);
245 if (hpipe != INVALID_HANDLE_VALUE)
248 ResetEvent(stopserver_);
249 CloseHandle(stopserver_);
254 void LyXComm::emergencyCleanup()
256 if (!pipename_.empty()) {
257 SetEvent(stopserver_);
258 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
259 GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
260 FILE_ATTRIBUTE_NORMAL, NULL);
261 if (hpipe != INVALID_HANDLE_VALUE)
266 void LyXComm::read_ready(HANDLE inpipe)
269 read_buffer_.erase();
271 int const charbuf_size = 100;
272 char charbuf[charbuf_size];
275 while (ReadFile(inpipe, charbuf, charbuf_size - 1, &status, NULL)) {
277 charbuf[status] = '\0'; // turn it into a c string
278 read_buffer_ += rtrim(charbuf, "\r");
279 // commit any commands read
280 while (read_buffer_.find('\n') != string::npos) {
281 // split() grabs the entire string if
282 // the delim /wasn't/ found. ?:-P
284 read_buffer_= split(read_buffer_, cmd, '\n');
285 cmd = rtrim(cmd, "\r");
286 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
287 << ", read_buffer_:" << read_buffer_
290 clientcb_(client_, cmd);
295 if (GetLastError() != ERROR_BROKEN_PIPE) {
297 LYXERR0("LyXComm: " << errormsg());
298 if (!read_buffer_.empty()) {
299 LYXERR0("LyXComm: truncated command: " << read_buffer_);
300 read_buffer_.erase();
303 // Client closed the pipe, so disconnect and close.
304 DisconnectNamedPipe(inpipe);
306 FlushFileBuffers(outpipe_);
307 DisconnectNamedPipe(outpipe_);
308 // Temporarily change to non-blocking mode otherwise
309 // ConnectNamedPipe() would block waiting for a connection.
310 DWORD mode = PIPE_NOWAIT;
311 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
312 ConnectNamedPipe(outpipe_, NULL);
314 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
318 void LyXComm::send(string const & msg)
321 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
325 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
327 if (pipename_.empty()) return;
330 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
338 success = WriteFile(outpipe_, msg.c_str(), msg.length(), &sent, NULL);
340 DWORD error = GetLastError();
341 if (error == ERROR_NO_DATA) {
342 DisconnectNamedPipe(outpipe_);
343 DWORD mode = PIPE_NOWAIT;
344 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
345 ConnectNamedPipe(outpipe_, NULL);
347 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
348 } else if (error != ERROR_PIPE_LISTENING)
353 } while (!success && count < 100);
356 lyxerr << "LyXComm: Error sending message: " << msg
357 << '\n' << errormsg()
358 << "\nLyXComm: Resetting connection" << endl;
365 #elif !defined (HAVE_MKFIFO)
366 // We provide a stub class that disables the lyxserver.
368 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
371 void LyXComm::openConnection()
375 void LyXComm::closeConnection()
379 int LyXComm::startPipe(string const & filename, bool write)
385 void LyXComm::endPipe(int & fd, string const & filename, bool write)
389 void LyXComm::emergencyCleanup()
392 void LyXComm::read_ready()
396 void LyXComm::send(string const & msg)
400 #else // defined (HAVE_MKFIFO)
403 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
404 : pipename_(pip), client_(cli), clientcb_(ccb)
411 void LyXComm::openConnection()
413 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
415 // If we are up, that's an error
417 lyxerr << "LyXComm: Already connected" << endl;
420 // We assume that we don't make it
423 if (pipename_.empty()) {
424 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
428 infd_ = startPipe(inPipeName(), false);
432 outfd_ = startPipe(outPipeName(), true);
434 endPipe(infd_, inPipeName(), false);
438 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
439 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
440 << '\n' << strerror(errno) << endl;
446 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
451 void LyXComm::closeConnection()
453 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
455 if (pipename_.empty()) {
456 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
461 LYXERR0("LyXComm: Already disconnected");
465 endPipe(infd_, inPipeName(), false);
466 endPipe(outfd_, outPipeName(), true);
472 int LyXComm::startPipe(string const & file, bool write)
474 static bool stalepipe = false;
475 FileName const filename(file);
476 if (filename.exists()) {
478 // Let's see whether we have a stale pipe.
479 int fd = ::open(filename.toFilesystemEncoding().c_str(),
480 O_WRONLY | O_NONBLOCK);
482 // Another LyX instance is using it.
484 } else if (errno == ENXIO) {
485 // No process is reading from the other end.
487 LYXERR(Debug::LYXSERVER,
488 "LyXComm: trying to remove "
490 filename.removeFile();
492 } else if (stalepipe) {
493 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
495 filename.removeFile();
498 if (filename.exists()) {
499 lyxerr << "LyXComm: Pipe " << filename
500 << " already exists.\nIf no other LyX program"
501 " is active, please delete the pipe by hand"
509 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
510 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
511 << strerror(errno) << endl;
514 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
515 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
518 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
519 << strerror(errno) << endl;
520 filename.removeFile();
525 theApp()->registerSocketCallback(fd,
526 boost::bind(&LyXComm::read_ready, this));
533 void LyXComm::endPipe(int & fd, string const & filename, bool write)
539 theApp()->unregisterSocketCallback(fd);
541 if (::close(fd) < 0) {
542 lyxerr << "LyXComm: Could not close pipe " << filename
543 << '\n' << strerror(errno) << endl;
546 if (FileName(filename).removeFile() < 0) {
547 lyxerr << "LyXComm: Could not remove pipe " << filename
548 << '\n' << strerror(errno) << endl;
555 void LyXComm::emergencyCleanup()
557 if (!pipename_.empty()) {
558 endPipe(infd_, inPipeName(), false);
559 endPipe(outfd_, outPipeName(), true);
564 // Receives messages and sends then to client
565 void LyXComm::read_ready()
567 // FIXME: make read_buffer_ a class-member for multiple sessions
568 static string read_buffer_;
569 read_buffer_.erase();
571 int const charbuf_size = 100;
572 char charbuf[charbuf_size];
574 // As O_NONBLOCK is set, until no data is available for reading,
575 // read() doesn't block but returns -1 and set errno to EAGAIN.
576 // After a client that opened the pipe for writing, closes it
577 // (and no other client is using the pipe), read() would always
578 // return 0 and thus the connection has to be reset.
582 // the single = is intended here.
583 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
586 charbuf[status] = '\0'; // turn it into a c string
587 read_buffer_ += rtrim(charbuf, "\r");
588 // commit any commands read
589 while (read_buffer_.find('\n') != string::npos) {
590 // split() grabs the entire string if
591 // the delim /wasn't/ found. ?:-P
593 read_buffer_= split(read_buffer_, cmd,'\n');
594 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
595 << ", read_buffer_:" << read_buffer_
598 clientcb_(client_, cmd);
602 if (errno == EAGAIN) {
603 // Nothing to read, continue
607 // An error occurred, better bailing out
608 LYXERR0("LyXComm: " << strerror(errno));
609 if (!read_buffer_.empty()) {
610 LYXERR0("LyXComm: truncated command: " << read_buffer_);
611 read_buffer_.erase();
613 break; // reset connection
617 // The connection gets reset when read() returns 0 (meaning that the
618 // last client closed the pipe) or an error occurred, in which case
619 // read() returns -1 and errno != EAGAIN.
626 void LyXComm::send(string const & msg)
629 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
633 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
635 if (pipename_.empty()) return;
638 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
639 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
640 lyxerr << "LyXComm: Error sending message: " << msg
641 << '\n' << strerror(errno)
642 << "\nLyXComm: Resetting connection" << endl;
648 #endif // defined (HAVE_MKFIFO)
651 string const LyXComm::inPipeName() const
653 return pipename_ + ".in";
657 string const LyXComm::outPipeName() const
659 return pipename_ + ".out";
663 /////////////////////////////////////////////////////////////////////
667 /////////////////////////////////////////////////////////////////////
669 void ServerCallback(Server * server, string const & msg)
671 server->callback(msg);
674 Server::Server(LyXFunc * f, string const & pipes)
675 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
681 // say goodbye to clients so they stop sending messages
682 // send as many bye messages as there are clients,
683 // each with client's name.
685 for (int i = 0; i != numclients_; ++i) {
686 message = "LYXSRV:" + clients_[i] + ":bye\n";
687 pipes_.send(message);
692 int compare(char const * a, char const * b, unsigned int len)
695 return strncmp(a, b, len);
699 // Handle data gotten from communication, called by LyXComm
700 void Server::callback(string const & msg)
702 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
704 char const * p = msg.c_str();
706 // --- parse the string --------------------------------------------
708 // Format: LYXCMD:<client>:<func>:<argstring>\n
710 bool server_only = false;
712 // --- 1. check 'header' ---
714 if (compare(p, "LYXSRV:", 7) == 0) {
716 } else if (0 != compare(p, "LYXCMD:", 7)) {
717 lyxerr << "Server: Unknown request \""
723 // --- 2. for the moment ignore the client name ---
725 while (*p && *p != ':')
726 client += char(*p++);
732 // --- 3. get function name ---
734 while (*p && *p != ':')
737 // --- 4. parse the argument ---
739 if (!server_only && *p == ':' && *(++p)) {
740 while (*p && *p != '\n')
745 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
746 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
748 // --- lookup and exec the command ------------------
752 // return the greeting to inform the client that
754 if (cmd == "hello") {
756 if (numclients_ == MAX_CLIENTS) { //paranoid check
757 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
761 while (!clients_[i].empty() && i < numclients_)
763 clients_[i] = client;
765 buf = "LYXSRV:" + client + ":hello\n";
766 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
768 } else if (cmd == "bye") {
769 // If clients_ == 0 maybe we should reset the pipes
770 // to prevent fake callbacks
771 int i = 0; //look if client is registered
772 for (; i < numclients_; ++i) {
773 if (clients_[i] == client)
776 if (i < numclients_) {
779 LYXERR(Debug::LYXSERVER, "Server: Client "
780 << client << " said goodbye");
782 LYXERR(Debug::LYXSERVER,
783 "Server: ignoring bye messge from unregistered client" << client);
786 LYXERR0("Server: Undefined server command " << cmd << '.');
792 // which lyxfunc should we let it connect to?
793 // The correct solution would be to have a
794 // specialized (non-gui) BufferView. But how do
795 // we do it now? Probably we should just let it
796 // connect to the lyxfunc in the single LyXView we
797 // support currently. (Lgb)
799 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
800 string const rval = to_utf8(func_->getMessage());
802 // all commands produce an INFO or ERROR message
803 // in the output pipe, even if they do not return
804 // anything. See chapter 4 of Customization doc.
806 if (func_->errorStat())
810 buf += client + ':' + cmd + ':' + rval + '\n';
813 // !!! we don't do any error checking -
814 // if the client won't listen, the
815 // message is lost and others too
816 // maybe; so the client should empty
817 // the outpipe before issuing a request.
825 // Send a notify message to a client, called by WorkAreaKeyPress
826 void Server::notifyClient(string const & s)
828 pipes_.send("NOTIFY:" + s + "\n");
835 #include "moc_Server.cpp"