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)
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 << outPipeName() << '\n' << errormsg() << endl;
153 ConnectNamedPipe(outpipe_, NULL);
154 // Now change to blocking mode
155 DWORD mode = PIPE_WAIT;
156 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
158 while (!checkStopServerEvent()) {
159 inpipe = CreateNamedPipe(external_path(inPipeName()).c_str(),
160 PIPE_ACCESS_INBOUND, PIPE_WAIT,
161 PIPE_UNLIMITED_INSTANCES,
162 0, bufsize, 0, NULL);
163 if (inpipe == INVALID_HANDLE_VALUE) {
164 lyxerr << "LyXComm: Could not create pipe "
165 << inPipeName() << '\n' << errormsg() << endl;
169 BOOL connected = ConnectNamedPipe(inpipe, NULL);
170 // Check whether we are signaled to shutdown the pipe server.
171 if (checkStopServerEvent()) {
175 if (connected || GetLastError() == ERROR_PIPE_CONNECTED) {
176 PipeEvent * event = new PipeEvent(inpipe);
177 QCoreApplication::postEvent(this,
178 static_cast<QEvent *>(event));
182 CloseHandle(outpipe_);
186 bool LyXComm::event(QEvent * e)
188 if (e->type() == QEvent::User) {
189 read_ready(static_cast<PipeEvent *>(e)->pipe());
196 BOOL LyXComm::checkStopServerEvent()
198 return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0 || closing_;
202 void LyXComm::openConnection()
204 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
206 // If we are up, that's an error
208 lyxerr << "LyXComm: Already connected" << endl;
211 // We assume that we don't make it
214 if (pipename_.empty()) {
215 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
219 stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
221 HANDLE thread = CreateThread(NULL, 0, pipeServerWrapper,
222 static_cast<void *>(this), 0, &tid);
224 lyxerr << "LyXComm: Could not create pipe server thread: "
225 << errormsg() << endl;
232 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
237 void LyXComm::closeConnection()
239 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
241 if (pipename_.empty()) {
242 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
247 LYXERR0("LyXComm: Already disconnected");
251 SetEvent(stopserver_);
252 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
253 GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
254 FILE_ATTRIBUTE_NORMAL, NULL);
255 if (hpipe != INVALID_HANDLE_VALUE)
258 ResetEvent(stopserver_);
259 CloseHandle(stopserver_);
264 void LyXComm::emergencyCleanup()
266 if (!pipename_.empty()) {
267 SetEvent(stopserver_);
268 HANDLE hpipe = CreateFile(external_path(inPipeName()).c_str(),
269 GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
270 FILE_ATTRIBUTE_NORMAL, NULL);
271 if (hpipe != INVALID_HANDLE_VALUE)
274 ResetEvent(stopserver_);
275 CloseHandle(stopserver_);
279 void LyXComm::read_ready(HANDLE inpipe)
282 read_buffer_.erase();
284 int const charbuf_size = 100;
285 char charbuf[charbuf_size];
288 while (ReadFile(inpipe, charbuf, charbuf_size - 1, &status, NULL)) {
290 charbuf[status] = '\0'; // turn it into a c string
291 read_buffer_ += rtrim(charbuf, "\r");
292 // commit any commands read
293 while (read_buffer_.find('\n') != string::npos) {
294 // split() grabs the entire string if
295 // the delim /wasn't/ found. ?:-P
297 read_buffer_= split(read_buffer_, cmd, '\n');
298 cmd = rtrim(cmd, "\r");
299 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
300 << ", read_buffer_:" << read_buffer_
303 clientcb_(client_, cmd);
308 if (GetLastError() != ERROR_BROKEN_PIPE) {
310 LYXERR0("LyXComm: " << errormsg());
311 if (!read_buffer_.empty()) {
312 LYXERR0("LyXComm: truncated command: " << read_buffer_);
313 read_buffer_.erase();
316 // Client closed the pipe, so disconnect and close.
317 DisconnectNamedPipe(inpipe);
319 FlushFileBuffers(outpipe_);
320 DisconnectNamedPipe(outpipe_);
321 // Temporarily change to non-blocking mode otherwise
322 // ConnectNamedPipe() would block waiting for a connection.
323 DWORD mode = PIPE_NOWAIT;
324 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
325 ConnectNamedPipe(outpipe_, NULL);
327 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
331 void LyXComm::send(string const & msg)
334 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
338 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
340 if (pipename_.empty()) return;
343 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
351 success = WriteFile(outpipe_, msg.c_str(), msg.length(), &sent, NULL);
353 DWORD error = GetLastError();
354 if (error == ERROR_NO_DATA) {
355 DisconnectNamedPipe(outpipe_);
356 DWORD mode = PIPE_NOWAIT;
357 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
358 ConnectNamedPipe(outpipe_, NULL);
360 SetNamedPipeHandleState(outpipe_, &mode, NULL, NULL);
361 } else if (error != ERROR_PIPE_LISTENING)
366 } while (!success && count < 100 && !checkStopServerEvent());
369 lyxerr << "LyXComm: Error sending message: " << msg
370 << '\n' << errormsg()
371 << "\nLyXComm: Resetting connection" << endl;
373 if (!checkStopServerEvent())
379 #elif !defined (HAVE_MKFIFO)
380 // We provide a stub class that disables the lyxserver.
382 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
385 void LyXComm::openConnection()
389 void LyXComm::closeConnection()
393 int LyXComm::startPipe(string const & filename, bool write)
399 void LyXComm::endPipe(int & fd, string const & filename, bool write)
403 void LyXComm::emergencyCleanup()
406 void LyXComm::read_ready()
410 void LyXComm::send(string const & msg)
414 #else // defined (HAVE_MKFIFO)
417 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
418 : pipename_(pip), client_(cli), clientcb_(ccb)
425 void LyXComm::openConnection()
427 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
429 // If we are up, that's an error
431 lyxerr << "LyXComm: Already connected" << endl;
434 // We assume that we don't make it
437 if (pipename_.empty()) {
438 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
442 infd_ = startPipe(inPipeName(), false);
446 outfd_ = startPipe(outPipeName(), true);
448 endPipe(infd_, inPipeName(), false);
452 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
453 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
454 << '\n' << strerror(errno) << endl;
460 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
465 void LyXComm::closeConnection()
467 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
469 if (pipename_.empty()) {
470 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
475 LYXERR0("LyXComm: Already disconnected");
479 endPipe(infd_, inPipeName(), false);
480 endPipe(outfd_, outPipeName(), true);
486 int LyXComm::startPipe(string const & file, bool write)
488 static bool stalepipe = false;
489 FileName const filename(file);
490 if (filename.exists()) {
492 // Let's see whether we have a stale pipe.
493 int fd = ::open(filename.toFilesystemEncoding().c_str(),
494 O_WRONLY | O_NONBLOCK);
496 // Another LyX instance is using it.
498 } else if (errno == ENXIO) {
499 // No process is reading from the other end.
501 LYXERR(Debug::LYXSERVER,
502 "LyXComm: trying to remove "
504 filename.removeFile();
506 } else if (stalepipe) {
507 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
509 filename.removeFile();
512 if (filename.exists()) {
513 lyxerr << "LyXComm: Pipe " << filename
514 << " already exists.\nIf no other LyX program"
515 " is active, please delete the pipe by hand"
523 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
524 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
525 << strerror(errno) << endl;
528 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
529 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
532 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
533 << strerror(errno) << endl;
534 filename.removeFile();
539 theApp()->registerSocketCallback(fd,
540 boost::bind(&LyXComm::read_ready, this));
547 void LyXComm::endPipe(int & fd, string const & filename, bool write)
553 theApp()->unregisterSocketCallback(fd);
555 if (::close(fd) < 0) {
556 lyxerr << "LyXComm: Could not close pipe " << filename
557 << '\n' << strerror(errno) << endl;
560 if (FileName(filename).removeFile() < 0) {
561 lyxerr << "LyXComm: Could not remove pipe " << filename
562 << '\n' << strerror(errno) << endl;
569 void LyXComm::emergencyCleanup()
571 if (!pipename_.empty()) {
572 endPipe(infd_, inPipeName(), false);
573 endPipe(outfd_, outPipeName(), true);
578 // Receives messages and sends then to client
579 void LyXComm::read_ready()
581 // FIXME: make read_buffer_ a class-member for multiple sessions
582 static string read_buffer_;
583 read_buffer_.erase();
585 int const charbuf_size = 100;
586 char charbuf[charbuf_size];
588 // As O_NONBLOCK is set, until no data is available for reading,
589 // read() doesn't block but returns -1 and set errno to EAGAIN.
590 // After a client that opened the pipe for writing, closes it
591 // (and no other client is using the pipe), read() would always
592 // return 0 and thus the connection has to be reset.
596 // the single = is intended here.
597 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
600 charbuf[status] = '\0'; // turn it into a c string
601 read_buffer_ += rtrim(charbuf, "\r");
602 // commit any commands read
603 while (read_buffer_.find('\n') != string::npos) {
604 // split() grabs the entire string if
605 // the delim /wasn't/ found. ?:-P
607 read_buffer_= split(read_buffer_, cmd,'\n');
608 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
609 << ", read_buffer_:" << read_buffer_
612 clientcb_(client_, cmd);
616 if (errno == EAGAIN) {
617 // Nothing to read, continue
621 // An error occurred, better bailing out
622 LYXERR0("LyXComm: " << strerror(errno));
623 if (!read_buffer_.empty()) {
624 LYXERR0("LyXComm: truncated command: " << read_buffer_);
625 read_buffer_.erase();
627 break; // reset connection
631 // The connection gets reset when read() returns 0 (meaning that the
632 // last client closed the pipe) or an error occurred, in which case
633 // read() returns -1 and errno != EAGAIN.
640 void LyXComm::send(string const & msg)
643 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
647 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
649 if (pipename_.empty()) return;
652 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
653 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
654 lyxerr << "LyXComm: Error sending message: " << msg
655 << '\n' << strerror(errno)
656 << "\nLyXComm: Resetting connection" << endl;
662 #endif // defined (HAVE_MKFIFO)
665 string const LyXComm::inPipeName() const
667 return pipename_ + ".in";
671 string const LyXComm::outPipeName() const
673 return pipename_ + ".out";
677 /////////////////////////////////////////////////////////////////////
681 /////////////////////////////////////////////////////////////////////
683 void ServerCallback(Server * server, string const & msg)
685 server->callback(msg);
688 Server::Server(LyXFunc * f, string const & pipes)
689 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
695 // say goodbye to clients so they stop sending messages
696 // send as many bye messages as there are clients,
697 // each with client's name.
699 for (int i = 0; i != numclients_; ++i) {
700 message = "LYXSRV:" + clients_[i] + ":bye\n";
701 pipes_.send(message);
706 int compare(char const * a, char const * b, unsigned int len)
709 return strncmp(a, b, len);
713 // Handle data gotten from communication, called by LyXComm
714 void Server::callback(string const & msg)
716 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
718 char const * p = msg.c_str();
720 // --- parse the string --------------------------------------------
722 // Format: LYXCMD:<client>:<func>:<argstring>\n
724 bool server_only = false;
726 // --- 1. check 'header' ---
728 if (compare(p, "LYXSRV:", 7) == 0) {
730 } else if (0 != compare(p, "LYXCMD:", 7)) {
731 lyxerr << "Server: Unknown request \""
737 // --- 2. for the moment ignore the client name ---
739 while (*p && *p != ':')
740 client += char(*p++);
746 // --- 3. get function name ---
748 while (*p && *p != ':')
751 // --- 4. parse the argument ---
753 if (!server_only && *p == ':' && *(++p)) {
754 while (*p && *p != '\n')
759 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
760 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
762 // --- lookup and exec the command ------------------
766 // return the greeting to inform the client that
768 if (cmd == "hello") {
770 if (numclients_ == MAX_CLIENTS) { //paranoid check
771 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
775 while (!clients_[i].empty() && i < numclients_)
777 clients_[i] = client;
779 buf = "LYXSRV:" + client + ":hello\n";
780 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
782 } else if (cmd == "bye") {
783 // If clients_ == 0 maybe we should reset the pipes
784 // to prevent fake callbacks
785 int i = 0; //look if client is registered
786 for (; i < numclients_; ++i) {
787 if (clients_[i] == client)
790 if (i < numclients_) {
793 LYXERR(Debug::LYXSERVER, "Server: Client "
794 << client << " said goodbye");
796 LYXERR(Debug::LYXSERVER,
797 "Server: ignoring bye messge from unregistered client" << client);
800 LYXERR0("Server: Undefined server command " << cmd << '.');
806 // which lyxfunc should we let it connect to?
807 // The correct solution would be to have a
808 // specialized (non-gui) BufferView. But how do
809 // we do it now? Probably we should just let it
810 // connect to the lyxfunc in the single LyXView we
811 // support currently. (Lgb)
813 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
814 string const rval = to_utf8(func_->getMessage());
816 // all commands produce an INFO or ERROR message
817 // in the output pipe, even if they do not return
818 // anything. See chapter 4 of Customization doc.
820 if (func_->errorStat())
824 buf += client + ':' + cmd + ':' + rval + '\n';
827 // !!! we don't do any error checking -
828 // if the client won't listen, the
829 // message is lost and others too
830 // maybe; so the client should empty
831 // the outpipe before issuing a request.
839 // Send a notify message to a client, called by WorkAreaKeyPress
840 void Server::notifyClient(string const & s)
842 pipes_.send("NOTIFY:" + s + "\n");
849 #include "moc_Server.cpp"