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)
252 void LyXComm::emergencyCleanup()
254 if (!pipename_.empty()) {
255 SetEvent(stopserver_);
256 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
257 GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
258 FILE_ATTRIBUTE_NORMAL, NULL);
259 if (hpipe != INVALID_HANDLE_VALUE)
264 void LyXComm::read_ready(HANDLE inpipe)
267 read_buffer_.erase();
269 int const charbuf_size = 100;
270 char charbuf[charbuf_size];
273 while (ReadFile(inpipe, charbuf, charbuf_size - 1, &status, NULL)) {
275 charbuf[status] = '\0'; // turn it into a c string
276 read_buffer_ += rtrim(charbuf, "\r");
277 // commit any commands read
278 while (read_buffer_.find('\n') != string::npos) {
279 // split() grabs the entire string if
280 // the delim /wasn't/ found. ?:-P
282 read_buffer_= split(read_buffer_, cmd, '\n');
283 cmd = rtrim(cmd, "\r");
284 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
285 << ", read_buffer_:" << read_buffer_
288 clientcb_(client_, cmd);
293 if (GetLastError() != ERROR_BROKEN_PIPE) {
295 LYXERR0("LyXComm: " << errormsg());
296 if (!read_buffer_.empty()) {
297 LYXERR0("LyXComm: truncated command: " << read_buffer_);
298 read_buffer_.erase();
301 // Client closed the pipe, so disconnect and close.
302 DisconnectNamedPipe(inpipe);
304 FlushFileBuffers(outpipe_);
305 DisconnectNamedPipe(outpipe_);
306 // Temporarily change to non-blocking mode otherwise
307 // ConnectNamedPipe() would block waiting for a connection.
308 DWORD mode = PIPE_NOWAIT;
309 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
310 ConnectNamedPipe(outpipe_, NULL);
312 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
316 void LyXComm::send(string const & msg)
319 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
323 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
325 if (pipename_.empty()) return;
328 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
336 success = WriteFile(outpipe_, msg.c_str(), msg.length(), &sent, NULL);
338 DWORD error = GetLastError();
339 if (error == ERROR_NO_DATA) {
340 DisconnectNamedPipe(outpipe_);
341 DWORD mode = PIPE_NOWAIT;
342 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
343 ConnectNamedPipe(outpipe_, NULL);
345 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
346 } else if (error != ERROR_PIPE_LISTENING)
351 } while (!success && count < 100);
354 lyxerr << "LyXComm: Error sending message: " << msg
355 << '\n' << errormsg()
356 << "\nLyXComm: Resetting connection" << endl;
363 #elif !defined (HAVE_MKFIFO)
364 // We provide a stub class that disables the lyxserver.
366 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
369 void LyXComm::openConnection()
373 void LyXComm::closeConnection()
377 int LyXComm::startPipe(string const & filename, bool write)
383 void LyXComm::endPipe(int & fd, string const & filename, bool write)
387 void LyXComm::emergencyCleanup()
390 void LyXComm::read_ready()
394 void LyXComm::send(string const & msg)
398 #else // defined (HAVE_MKFIFO)
401 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
402 : pipename_(pip), client_(cli), clientcb_(ccb)
409 void LyXComm::openConnection()
411 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
413 // If we are up, that's an error
415 lyxerr << "LyXComm: Already connected" << endl;
418 // We assume that we don't make it
421 if (pipename_.empty()) {
422 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
426 infd_ = startPipe(inPipeName(), false);
430 outfd_ = startPipe(outPipeName(), true);
432 endPipe(infd_, inPipeName(), false);
436 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
437 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
438 << '\n' << strerror(errno) << endl;
444 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
449 void LyXComm::closeConnection()
451 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
453 if (pipename_.empty()) {
454 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
459 LYXERR0("LyXComm: Already disconnected");
463 endPipe(infd_, inPipeName(), false);
464 endPipe(outfd_, outPipeName(), true);
470 int LyXComm::startPipe(string const & file, bool write)
472 static bool stalepipe = false;
473 FileName const filename(file);
474 if (filename.exists()) {
476 // Let's see whether we have a stale pipe.
477 int fd = ::open(filename.toFilesystemEncoding().c_str(),
478 O_WRONLY | O_NONBLOCK);
480 // Another LyX instance is using it.
482 } else if (errno == ENXIO) {
483 // No process is reading from the other end.
485 LYXERR(Debug::LYXSERVER,
486 "LyXComm: trying to remove "
488 filename.removeFile();
490 } else if (stalepipe) {
491 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
493 filename.removeFile();
496 if (filename.exists()) {
497 lyxerr << "LyXComm: Pipe " << filename
498 << " already exists.\nIf no other LyX program"
499 " is active, please delete the pipe by hand"
507 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
508 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
509 << strerror(errno) << endl;
512 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
513 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
516 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
517 << strerror(errno) << endl;
518 filename.removeFile();
523 theApp()->registerSocketCallback(fd,
524 boost::bind(&LyXComm::read_ready, this));
531 void LyXComm::endPipe(int & fd, string const & filename, bool write)
537 theApp()->unregisterSocketCallback(fd);
539 if (::close(fd) < 0) {
540 lyxerr << "LyXComm: Could not close pipe " << filename
541 << '\n' << strerror(errno) << endl;
544 if (FileName(filename).removeFile() < 0) {
545 lyxerr << "LyXComm: Could not remove pipe " << filename
546 << '\n' << strerror(errno) << endl;
553 void LyXComm::emergencyCleanup()
555 if (!pipename_.empty()) {
556 endPipe(infd_, inPipeName(), false);
557 endPipe(outfd_, outPipeName(), true);
562 // Receives messages and sends then to client
563 void LyXComm::read_ready()
565 // FIXME: make read_buffer_ a class-member for multiple sessions
566 static string read_buffer_;
567 read_buffer_.erase();
569 int const charbuf_size = 100;
570 char charbuf[charbuf_size];
572 // As O_NONBLOCK is set, until no data is available for reading,
573 // read() doesn't block but returns -1 and set errno to EAGAIN.
574 // After a client that opened the pipe for writing, closes it
575 // (and no other client is using the pipe), read() would always
576 // return 0 and thus the connection has to be reset.
580 // the single = is intended here.
581 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
584 charbuf[status] = '\0'; // turn it into a c string
585 read_buffer_ += rtrim(charbuf, "\r");
586 // commit any commands read
587 while (read_buffer_.find('\n') != string::npos) {
588 // split() grabs the entire string if
589 // the delim /wasn't/ found. ?:-P
591 read_buffer_= split(read_buffer_, cmd,'\n');
592 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
593 << ", read_buffer_:" << read_buffer_
596 clientcb_(client_, cmd);
600 if (errno == EAGAIN) {
601 // Nothing to read, continue
605 // An error occurred, better bailing out
606 LYXERR0("LyXComm: " << strerror(errno));
607 if (!read_buffer_.empty()) {
608 LYXERR0("LyXComm: truncated command: " << read_buffer_);
609 read_buffer_.erase();
611 break; // reset connection
615 // The connection gets reset when read() returns 0 (meaning that the
616 // last client closed the pipe) or an error occurred, in which case
617 // read() returns -1 and errno != EAGAIN.
624 void LyXComm::send(string const & msg)
627 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
631 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
633 if (pipename_.empty()) return;
636 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
637 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
638 lyxerr << "LyXComm: Error sending message: " << msg
639 << '\n' << strerror(errno)
640 << "\nLyXComm: Resetting connection" << endl;
646 #endif // defined (HAVE_MKFIFO)
649 string const LyXComm::inPipeName() const
651 return pipename_ + ".in";
655 string const LyXComm::outPipeName() const
657 return pipename_ + ".out";
661 /////////////////////////////////////////////////////////////////////
665 /////////////////////////////////////////////////////////////////////
667 void ServerCallback(Server * server, string const & msg)
669 server->callback(msg);
672 Server::Server(LyXFunc * f, string const & pipes)
673 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
679 // say goodbye to clients so they stop sending messages
680 // send as many bye messages as there are clients,
681 // each with client's name.
683 for (int i = 0; i != numclients_; ++i) {
684 message = "LYXSRV:" + clients_[i] + ":bye\n";
685 pipes_.send(message);
690 int compare(char const * a, char const * b, unsigned int len)
693 return strncmp(a, b, len);
697 // Handle data gotten from communication, called by LyXComm
698 void Server::callback(string const & msg)
700 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
702 char const * p = msg.c_str();
704 // --- parse the string --------------------------------------------
706 // Format: LYXCMD:<client>:<func>:<argstring>\n
708 bool server_only = false;
710 // --- 1. check 'header' ---
712 if (compare(p, "LYXSRV:", 7) == 0) {
714 } else if (0 != compare(p, "LYXCMD:", 7)) {
715 lyxerr << "Server: Unknown request \""
721 // --- 2. for the moment ignore the client name ---
723 while (*p && *p != ':')
724 client += char(*p++);
730 // --- 3. get function name ---
732 while (*p && *p != ':')
735 // --- 4. parse the argument ---
737 if (!server_only && *p == ':' && *(++p)) {
738 while (*p && *p != '\n')
743 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
744 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
746 // --- lookup and exec the command ------------------
750 // return the greeting to inform the client that
752 if (cmd == "hello") {
754 if (numclients_ == MAX_CLIENTS) { //paranoid check
755 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
759 while (!clients_[i].empty() && i < numclients_)
761 clients_[i] = client;
763 buf = "LYXSRV:" + client + ":hello\n";
764 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
766 } else if (cmd == "bye") {
767 // If clients_ == 0 maybe we should reset the pipes
768 // to prevent fake callbacks
769 int i = 0; //look if client is registered
770 for (; i < numclients_; ++i) {
771 if (clients_[i] == client)
774 if (i < numclients_) {
777 LYXERR(Debug::LYXSERVER, "Server: Client "
778 << client << " said goodbye");
780 LYXERR(Debug::LYXSERVER,
781 "Server: ignoring bye messge from unregistered client" << client);
784 LYXERR0("Server: Undefined server command " << cmd << '.');
790 // which lyxfunc should we let it connect to?
791 // The correct solution would be to have a
792 // specialized (non-gui) BufferView. But how do
793 // we do it now? Probably we should just let it
794 // connect to the lyxfunc in the single LyXView we
795 // support currently. (Lgb)
797 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
798 string const rval = to_utf8(func_->getMessage());
800 // all commands produce an INFO or ERROR message
801 // in the output pipe, even if they do not return
802 // anything. See chapter 4 of Customization doc.
804 if (func_->errorStat())
808 buf += client + ':' + cmd + ':' + rval + '\n';
811 // !!! we don't do any error checking -
812 // if the client won't listen, the
813 // message is lost and others too
814 // maybe; so the client should empty
815 // the outpipe before issuing a request.
823 // Send a notify message to a client, called by WorkAreaKeyPress
824 void Server::notifyClient(string const & s)
826 pipes_.send("NOTIFY:" + s + "\n");
833 #include "moc_Server.cpp"