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
42 #include "lyxserver.h"
44 #include "funcrequest.h"
45 #include "LyXAction.h"
47 #include "support/lstrings.h"
48 #include "support/lyxlib.h"
49 #include "frontends/lyx_gui.h"
51 #include <boost/bind.hpp>
54 #ifdef HAVE_SYS_STAT_H
55 # include <sys/stat.h>
62 # define OS2EMX_PLAIN_CHAR
63 # define INCL_DOSNMPIPES
64 # define INCL_DOSERRORS
66 # include "support/os2_errortable.h"
69 using lyx::support::compare;
70 using lyx::support::rtrim;
71 using lyx::support::split;
72 using lyx::support::unlink;
78 #if !defined (HAVE_MKFIFO)
79 // We provide a stub class that disables the lyxserver.
81 void LyXComm::openConnection()
85 void LyXComm::closeConnection()
89 int LyXComm::startPipe(string const & filename, bool write)
95 void LyXComm::endPipe(int & fd, string const & filename, bool write)
99 void LyXComm::emergencyCleanup()
102 void LyXComm::read_ready()
106 void LyXComm::send(string const & msg)
110 #else // defined (HAVE_MKFIFO)
113 void LyXComm::openConnection()
115 lyxerr[Debug::LYXSERVER] << "LyXComm: Opening connection" << endl;
117 // If we are up, that's an error
119 lyxerr << "LyXComm: Already connected" << endl;
122 // We assume that we don't make it
125 if (pipename.empty()) {
126 lyxerr[Debug::LYXSERVER]
127 << "LyXComm: server is disabled, nothing to do"
132 if ((infd = startPipe(inPipeName(), false)) == -1)
135 if ((outfd = startPipe(outPipeName(), true)) == -1) {
136 endPipe(infd, inPipeName(), false);
140 if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
141 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
142 << '\n' << strerror(errno) << endl;
148 lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl;
153 void LyXComm::closeConnection()
155 lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl;
157 if (pipename.empty()) {
158 lyxerr[Debug::LYXSERVER]
159 << "LyXComm: server is disabled, nothing to do"
165 lyxerr << "LyXComm: Already disconnected" << endl;
169 endPipe(infd, inPipeName(), false);
170 endPipe(outfd, outPipeName(), true);
176 int LyXComm::startPipe(string const & filename, bool write)
182 // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
183 // The current emx implementation of access() won't work with pipes.
184 rc = DosCreateNPipe(filename.c_str(), &os2fd, NP_ACCESS_INBOUND,
185 NP_NOWAIT|0x01, 0600, 0600, 0);
186 if (rc == ERROR_PIPE_BUSY) {
187 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
188 << "If no other LyX program is active, please delete"
189 " the pipe by hand and try again." << endl;
194 if (rc != NO_ERROR) {
195 errnum = TranslateOS2Error(rc);
196 lyxerr <<"LyXComm: Could not create pipe " << filename
197 << strerror(errnum) << endl;
201 rc = DosConnectNPipe(os2fd);
202 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
203 errnum = TranslateOS2Error(rc);
204 lyxerr <<"LyXComm: Could not create pipe " << filename
205 << strerror(errnum) << endl;
208 // Imported handles can be used both with OS/2 APIs and emx
209 // library functions.
210 int const fd = _imphandle(os2fd);
212 if (::access(filename.c_str(), F_OK) == 0) {
213 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
214 << "If no other LyX program is active, please delete"
215 " the pipe by hand and try again." << endl;
220 if (::mkfifo(filename.c_str(), 0600) < 0) {
221 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
222 << strerror(errno) << endl;
225 int const fd = ::open(filename.c_str(),
226 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
230 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
231 << strerror(errno) << endl;
237 lyx_gui::register_socket_callback(fd, boost::bind(&LyXComm::read_ready, this));
244 void LyXComm::endPipe(int & fd, string const & filename, bool write)
250 lyx_gui::unregister_socket_callback(fd);
257 rc = DosDisConnectNPipe(fd);
258 if (rc != NO_ERROR) {
259 errnum = TranslateOS2Error(rc);
260 lyxerr << "LyXComm: Could not disconnect pipe " << filename
261 << '\n' << strerror(errnum) << endl;
266 if (::close(fd) < 0) {
267 lyxerr << "LyXComm: Could not close pipe " << filename
268 << '\n' << strerror(errno) << endl;
271 // OS/2 pipes are deleted automatically
273 if (unlink(filename) < 0) {
274 lyxerr << "LyXComm: Could not remove pipe " << filename
275 << '\n' << strerror(errno) << endl;
283 void LyXComm::emergencyCleanup()
285 if (!pipename.empty()) {
286 endPipe(infd, inPipeName(), false);
287 endPipe(outfd, outPipeName(), true);
292 // Receives messages and sends then to client
293 void LyXComm::read_ready()
295 // nb! make read_buffer_ a class-member for multiple sessions
296 static string read_buffer_;
297 read_buffer_.erase();
299 int const charbuf_size = 100;
300 char charbuf[charbuf_size];
304 // the single = is intended here.
305 while ((status = ::read(infd, charbuf, charbuf_size - 1))) {
308 charbuf[status] = '\0'; // turn it into a c string
309 read_buffer_ += rtrim(charbuf, "\r");
310 // commit any commands read
311 while (read_buffer_.find('\n') != string::npos) {
312 // split() grabs the entire string if
313 // the delim /wasn't/ found. ?:-P
315 read_buffer_= split(read_buffer_, cmd,'\n');
316 lyxerr[Debug::LYXSERVER]
317 << "LyXComm: status:" << status
318 << ", read_buffer_:" << read_buffer_
319 << ", cmd:" << cmd << endl;
321 clientcb(client, cmd);
325 if (errno == EAGAIN) {
330 lyxerr << "LyXComm: " << strerror(errno) << endl;
331 if (!read_buffer_.empty()) {
332 lyxerr << "LyXComm: truncated command: "
333 << read_buffer_ << endl;
334 read_buffer_.erase();
336 break; // reset connection
340 // The connection gets reset in errno != EAGAIN
341 // Why does it need to be reset if errno == 0?
348 void LyXComm::send(string const & msg)
351 lyxerr << "LyXComm: Request to send empty string. Ignoring."
356 if (lyxerr.debugging(Debug::LYXSERVER)) {
357 lyxerr << "LyXComm: Sending '" << msg << '\'' << endl;
360 if (pipename.empty()) return;
363 lyxerr << "LyXComm: Pipes are closed. Could not send "
365 } else if (::write(outfd, msg.c_str(), msg.length()) < 0) {
366 lyxerr << "LyXComm: Error sending message: " << msg
367 << '\n' << strerror(errno)
368 << "\nLyXComm: Resetting connection" << endl;
375 rc = DosResetBuffer(outfd); // To avoid synchronization problems.
376 if (rc != NO_ERROR) {
377 errnum = TranslateOS2Error(rc);
378 lyxerr << "LyXComm: Message could not be flushed: " << msg
379 << '\n' << strerror(errnum) << endl;
384 #endif // defined (HAVE_MKFIFO)
387 string const LyXComm::inPipeName() const
389 return pipename + string(".in");
393 string const LyXComm::outPipeName() const
395 return pipename + string(".out");
401 LyXServer::~LyXServer()
403 // say goodbye to clients so they stop sending messages
404 // modified june 1999 by stefano@zool.su.se to send as many bye
405 // messages as there are clients, each with client's name.
407 for (int i= 0; i<numclients; ++i) {
408 message = "LYXSRV:" + clients[i] + ":bye\n";
414 /* ---F+------------------------------------------------------------------ *\
415 Function : ServerCallback
417 Purpose : handle data gotten from communication
418 \* ---F------------------------------------------------------------------- */
420 void LyXServer::callback(LyXServer * serv, string const & msg)
422 lyxerr[Debug::LYXSERVER] << "LyXServer: Received: '"
423 << msg << '\'' << endl;
425 char const * p = msg.c_str();
427 // --- parse the string --------------------------------------------
429 // Format: LYXCMD:<client>:<func>:<argstring>\n
431 bool server_only = false;
433 // --- 1. check 'header' ---
435 if (compare(p, "LYXSRV:", 7) == 0) {
437 } else if (0 != compare(p, "LYXCMD:", 7)) {
438 lyxerr << "LyXServer: Unknown request \""
444 // --- 2. for the moment ignore the client name ---
446 while (*p && *p != ':')
447 client += char(*p++);
451 // --- 3. get function name ---
453 while (*p && *p != ':')
456 // --- 4. parse the argument ---
458 if (!server_only && *p == ':' && *(++p)) {
459 while (*p && *p != '\n')
464 lyxerr[Debug::LYXSERVER]
465 << "LyXServer: Client: '" << client
466 << "' Command: '" << cmd
467 << "' Argument: '" << arg << '\'' << endl;
469 // --- lookup and exec the command ------------------
473 // return the greeting to inform the client that
475 if (cmd == "hello") {
477 if (serv->numclients == MAX_CLIENTS) { //paranoid check
478 lyxerr[Debug::LYXSERVER]
479 << "LyXServer: too many clients..."
483 int i= 0; //find place in clients[]
484 while (!serv->clients[i].empty()
485 && i<serv->numclients)
487 serv->clients[i] = client;
489 buf = "LYXSRV:" + client + ":hello\n";
490 lyxerr[Debug::LYXSERVER]
491 << "LyXServer: Greeting "
493 serv->pipes.send(buf);
494 } else if (cmd == "bye") {
495 // If clients == 0 maybe we should reset the pipes
496 // to prevent fake callbacks
497 int i = 0; //look if client is registered
498 for (; i < serv->numclients; ++i) {
499 if (serv->clients[i] == client) break;
501 if (i < serv->numclients) {
503 serv->clients[i].erase();
504 lyxerr[Debug::LYXSERVER]
505 << "LyXServer: Client "
506 << client << " said goodbye"
509 lyxerr[Debug::LYXSERVER]
510 << "LyXServer: ignoring bye messge from unregistered client"
514 lyxerr <<"LyXServer: Undefined server command "
515 << cmd << '.' << endl;
521 // which lyxfunc should we let it connect to?
522 // The correct solution would be to have a
523 // specialized (non-gui) BufferView. But how do
524 // we do it now? Probably we should just let it
525 // connect to the lyxfunc in the single LyXView we
526 // support currently. (Lgb)
529 serv->func->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
530 string const rval = serv->func->getMessage();
532 //modified june 1999 stefano@zool.su.se:
533 //all commands produce an INFO or ERROR message
534 //in the output pipe, even if they do not return
535 //anything. See chapter 4 of Customization doc.
537 if (serv->func->errorStat())
541 buf += client + ':' + cmd + ':' + rval + '\n';
542 serv->pipes.send(buf);
544 // !!! we don't do any error checking -
545 // if the client won't listen, the
546 // message is lost and others too
547 // maybe; so the client should empty
548 // the outpipe before issuing a request.
556 /* ---F+------------------------------------------------------------------ *\
557 Function : LyXNotifyClient
558 Called by : WorkAreaKeyPress
559 Purpose : send a notify messge to a client
560 Parameters: s - string to send
562 \* ---F------------------------------------------------------------------- */
564 void LyXServer::notifyClient(string const & s)
566 string buf = string("NOTIFY:") + s + "\n";