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
11 * Full author contact details are available in file CREDITS.
15 Docu : To use the lyxserver define the name of the pipe in your
17 \serverpipe "/home/myhome/.lyxpipe"
18 Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
19 Each message consists of a single line in ASCII. Input lines
20 (client -> LyX) have the following format:
21 "LYXCMD:<clientname>:<functionname>:<argument>"
22 Answers from LyX look like this:
23 "INFO:<clientname>:<functionname>:<data>"
24 [asierra970531] Or like this in case of error:
25 "ERROR:<clientname>:<functionname>:<error message>"
26 where <clientname> and <functionname> are just echoed.
27 If LyX notifies about a user defined extension key-sequence,
28 the line looks like this:
29 "NOTIFY:<key-sequence>"
30 [asierra970531] New server-only messages to implement a simple protocol
31 "LYXSRV:<clientname>:<protocol message>"
32 where <protocol message> can be "hello" or "bye". If hello is
33 received LyX will inform the client that it's listening its
34 messages, and 'bye' will inform that lyx is closing.
36 See development/server_monitor.c for an example client.
37 Purpose: implement a client/server lib for LyX
43 #include "FuncRequest.h"
44 #include "LyXAction.h"
47 #include "frontends/Application.h"
49 #include "support/debug.h"
50 #include "support/FileName.h"
51 #include "support/lstrings.h"
53 #include <boost/bind.hpp>
56 #ifdef HAVE_SYS_STAT_H
57 # include <sys/stat.h>
62 using namespace lyx::support;
66 /////////////////////////////////////////////////////////////////////
70 /////////////////////////////////////////////////////////////////////
72 #if !defined (HAVE_MKFIFO)
73 // We provide a stub class that disables the lyxserver.
75 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
78 void LyXComm::openConnection()
82 void LyXComm::closeConnection()
86 int LyXComm::startPipe(string const & filename, bool write)
92 void LyXComm::endPipe(int & fd, string const & filename, bool write)
96 void LyXComm::emergencyCleanup()
99 void LyXComm::read_ready()
103 void LyXComm::send(string const & msg)
107 #else // defined (HAVE_MKFIFO)
110 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
111 : pipename_(pip), client_(cli), clientcb_(ccb)
118 void LyXComm::openConnection()
120 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
122 // If we are up, that's an error
124 lyxerr << "LyXComm: Already connected" << endl;
127 // We assume that we don't make it
130 if (pipename_.empty()) {
131 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
135 infd_ = startPipe(inPipeName(), false);
139 outfd_ = startPipe(outPipeName(), true);
141 endPipe(infd_, inPipeName(), false);
145 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
146 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
147 << '\n' << strerror(errno) << endl;
153 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
158 void LyXComm::closeConnection()
160 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
162 if (pipename_.empty()) {
163 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
168 LYXERR0("LyXComm: Already disconnected");
172 endPipe(infd_, inPipeName(), false);
173 endPipe(outfd_, outPipeName(), true);
179 int LyXComm::startPipe(string const & file, bool write)
181 FileName const filename(file);
182 if (::access(filename.toFilesystemEncoding().c_str(), F_OK) == 0) {
183 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
184 << "If no other LyX program is active, please delete"
185 " the pipe by hand and try again." << endl;
190 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
191 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
192 << strerror(errno) << endl;
195 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
196 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
199 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
200 << strerror(errno) << endl;
201 filename.removeFile();
206 theApp()->registerSocketCallback(fd,
207 boost::bind(&LyXComm::read_ready, this));
214 void LyXComm::endPipe(int & fd, string const & filename, bool write)
220 theApp()->unregisterSocketCallback(fd);
222 if (::close(fd) < 0) {
223 lyxerr << "LyXComm: Could not close pipe " << filename
224 << '\n' << strerror(errno) << endl;
227 if (FileName(filename).removeFile() < 0) {
228 lyxerr << "LyXComm: Could not remove pipe " << filename
229 << '\n' << strerror(errno) << endl;
236 void LyXComm::emergencyCleanup()
238 if (!pipename_.empty()) {
239 endPipe(infd_, inPipeName(), false);
240 endPipe(outfd_, outPipeName(), true);
245 // Receives messages and sends then to client
246 void LyXComm::read_ready()
248 // FIXME: make read_buffer_ a class-member for multiple sessions
249 static string read_buffer_;
250 read_buffer_.erase();
252 int const charbuf_size = 100;
253 char charbuf[charbuf_size];
255 // As O_NONBLOCK is set, until no data is available for reading,
256 // read() doesn't block but returns -1 and set errno to EAGAIN.
257 // After a client that opened the pipe for writing, closes it
258 // (and no other client is using the pipe), read() would always
259 // return 0 and thus the connection has to be reset.
263 // the single = is intended here.
264 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
267 charbuf[status] = '\0'; // turn it into a c string
268 read_buffer_ += rtrim(charbuf, "\r");
269 // commit any commands read
270 while (read_buffer_.find('\n') != string::npos) {
271 // split() grabs the entire string if
272 // the delim /wasn't/ found. ?:-P
274 read_buffer_= split(read_buffer_, cmd,'\n');
275 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
276 << ", read_buffer_:" << read_buffer_
279 clientcb_(client_, cmd);
283 if (errno == EAGAIN) {
284 // Nothing to read, continue
288 // An error occurred, better bailing out
289 LYXERR0("LyXComm: " << strerror(errno));
290 if (!read_buffer_.empty()) {
291 LYXERR0("LyXComm: truncated command: " << read_buffer_);
292 read_buffer_.erase();
294 break; // reset connection
298 // The connection gets reset when read() returns 0 (meaning that the
299 // last client closed the pipe) or an error occurred, in which case
300 // read() returns -1 and errno != EAGAIN.
307 void LyXComm::send(string const & msg)
310 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
314 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
316 if (pipename_.empty()) return;
319 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
320 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
321 lyxerr << "LyXComm: Error sending message: " << msg
322 << '\n' << strerror(errno)
323 << "\nLyXComm: Resetting connection" << endl;
329 #endif // defined (HAVE_MKFIFO)
332 string const LyXComm::inPipeName() const
334 return pipename_ + ".in";
338 string const LyXComm::outPipeName() const
340 return pipename_ + ".out";
344 /////////////////////////////////////////////////////////////////////
348 /////////////////////////////////////////////////////////////////////
350 void ServerCallback(Server * server, string const & msg)
352 server->callback(msg);
355 Server::Server(LyXFunc * f, string const & pipes)
356 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
362 // say goodbye to clients so they stop sending messages
363 // send as many bye messages as there are clients,
364 // each with client's name.
366 for (int i = 0; i != numclients_; ++i) {
367 message = "LYXSRV:" + clients_[i] + ":bye\n";
368 pipes_.send(message);
373 int compare(char const * a, char const * b, unsigned int len)
376 return strncmp(a, b, len);
380 // Handle data gotten from communication, called by LyXComm
381 void Server::callback(string const & msg)
383 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
385 char const * p = msg.c_str();
387 // --- parse the string --------------------------------------------
389 // Format: LYXCMD:<client>:<func>:<argstring>\n
391 bool server_only = false;
393 // --- 1. check 'header' ---
395 if (compare(p, "LYXSRV:", 7) == 0) {
397 } else if (0 != compare(p, "LYXCMD:", 7)) {
398 lyxerr << "Server: Unknown request \""
404 // --- 2. for the moment ignore the client name ---
406 while (*p && *p != ':')
407 client += char(*p++);
413 // --- 3. get function name ---
415 while (*p && *p != ':')
418 // --- 4. parse the argument ---
420 if (!server_only && *p == ':' && *(++p)) {
421 while (*p && *p != '\n')
426 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
427 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
429 // --- lookup and exec the command ------------------
433 // return the greeting to inform the client that
435 if (cmd == "hello") {
437 if (numclients_ == MAX_CLIENTS) { //paranoid check
438 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
442 while (!clients_[i].empty() && i < numclients_)
444 clients_[i] = client;
446 buf = "LYXSRV:" + client + ":hello\n";
447 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
449 } else if (cmd == "bye") {
450 // If clients_ == 0 maybe we should reset the pipes
451 // to prevent fake callbacks
452 int i = 0; //look if client is registered
453 for (; i < numclients_; ++i) {
454 if (clients_[i] == client)
457 if (i < numclients_) {
460 LYXERR(Debug::LYXSERVER, "Server: Client "
461 << client << " said goodbye");
463 LYXERR(Debug::LYXSERVER,
464 "Server: ignoring bye messge from unregistered client" << client);
467 LYXERR0("Server: Undefined server command " << cmd << '.');
473 // which lyxfunc should we let it connect to?
474 // The correct solution would be to have a
475 // specialized (non-gui) BufferView. But how do
476 // we do it now? Probably we should just let it
477 // connect to the lyxfunc in the single LyXView we
478 // support currently. (Lgb)
480 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
481 string const rval = to_utf8(func_->getMessage());
483 // all commands produce an INFO or ERROR message
484 // in the output pipe, even if they do not return
485 // anything. See chapter 4 of Customization doc.
487 if (func_->errorStat())
491 buf += client + ':' + cmd + ':' + rval + '\n';
494 // !!! we don't do any error checking -
495 // if the client won't listen, the
496 // message is lost and others too
497 // maybe; so the client should empty
498 // the outpipe before issuing a request.
506 // Send a notify message to a client, called by WorkAreaKeyPress
507 void Server::notifyClient(string const & s)
509 pipes_.send("NOTIFY:" + s + "\n");