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>
60 #define OS2EMX_PLAIN_CHAR
61 #define INCL_DOSNMPIPES
62 #define INCL_DOSERRORS
64 #include "support/os2_errortable.h"
67 using lyx::support::compare;
68 using lyx::support::rtrim;
69 using lyx::support::split;
70 using lyx::support::unlink;
76 // provide an empty mkfifo() if we do not have one. This disables the
79 int mkfifo(char const * __path, mode_t __mode) {
85 void LyXComm::openConnection()
87 lyxerr[Debug::LYXSERVER] << "LyXComm: Opening connection" << endl;
89 // If we are up, that's an error
91 lyxerr << "LyXComm: Already connected" << endl;
94 // We assume that we don't make it
97 if (pipename.empty()) {
98 lyxerr[Debug::LYXSERVER]
99 << "LyXComm: server is disabled, nothing to do"
104 if ((infd = startPipe(inPipeName(), false)) == -1)
107 if ((outfd = startPipe(outPipeName(), true)) == -1) {
108 endPipe(infd, inPipeName(), false);
112 if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
113 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
114 << '\n' << strerror(errno) << endl;
120 lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl;
125 void LyXComm::closeConnection()
127 lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl;
129 if (pipename.empty()) {
130 lyxerr[Debug::LYXSERVER]
131 << "LyXComm: server is disabled, nothing to do"
137 lyxerr << "LyXComm: Already disconnected" << endl;
141 endPipe(infd, inPipeName(), false);
142 endPipe(outfd, outPipeName(), true);
148 int LyXComm::startPipe(string const & filename, bool write)
154 // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
155 // The current emx implementation of access() won't work with pipes.
156 rc = DosCreateNPipe(filename.c_str(), &os2fd, NP_ACCESS_INBOUND,
157 NP_NOWAIT|0x01, 0600, 0600, 0);
158 if (rc == ERROR_PIPE_BUSY) {
159 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
160 << "If no other LyX program is active, please delete"
161 " the pipe by hand and try again." << endl;
166 if (rc != NO_ERROR) {
167 errnum = TranslateOS2Error(rc);
168 lyxerr <<"LyXComm: Could not create pipe " << filename
169 << strerror(errnum) << endl;
173 rc = DosConnectNPipe(os2fd);
174 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
175 errnum = TranslateOS2Error(rc);
176 lyxerr <<"LyXComm: Could not create pipe " << filename
177 << strerror(errnum) << endl;
180 // Imported handles can be used both with OS/2 APIs and emx
181 // library functions.
182 int const fd = _imphandle(os2fd);
184 if (::access(filename.c_str(), F_OK) == 0) {
185 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
186 << "If no other LyX program is active, please delete"
187 " the pipe by hand and try again." << endl;
192 if (::mkfifo(filename.c_str(), 0600) < 0) {
193 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
194 << strerror(errno) << endl;
197 int const fd = ::open(filename.c_str(),
198 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
202 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
203 << strerror(errno) << endl;
209 lyx_gui::register_socket_callback(fd, boost::bind(&LyXComm::read_ready, this));
216 void LyXComm::endPipe(int & fd, string const & filename, bool write)
222 lyx_gui::unregister_socket_callback(fd);
229 rc = DosDisConnectNPipe(fd);
230 if (rc != NO_ERROR) {
231 errnum = TranslateOS2Error(rc);
232 lyxerr << "LyXComm: Could not disconnect pipe " << filename
233 << '\n' << strerror(errnum) << endl;
238 if (::close(fd) < 0) {
239 lyxerr << "LyXComm: Could not close pipe " << filename
240 << '\n' << strerror(errno) << endl;
243 // OS/2 pipes are deleted automatically
245 if (unlink(filename) < 0) {
246 lyxerr << "LyXComm: Could not remove pipe " << filename
247 << '\n' << strerror(errno) << endl;
255 void LyXComm::emergencyCleanup()
257 if (!pipename.empty()) {
258 endPipe(infd, inPipeName(), false);
259 endPipe(outfd, outPipeName(), true);
264 // Receives messages and sends then to client
265 void LyXComm::read_ready()
267 // nb! make read_buffer_ a class-member for multiple sessions
268 static string read_buffer_;
269 read_buffer_.erase();
271 int const charbuf_size = 100;
272 char charbuf[charbuf_size];
276 // the single = is intended here.
277 while ((status = ::read(infd, charbuf, charbuf_size - 1))) {
280 charbuf[status] = '\0'; // turn it into a c string
281 read_buffer_ += rtrim(charbuf, "\r");
282 // commit any commands read
283 while (read_buffer_.find('\n') != string::npos) {
284 // split() grabs the entire string if
285 // the delim /wasn't/ found. ?:-P
287 read_buffer_= split(read_buffer_, cmd,'\n');
288 lyxerr[Debug::LYXSERVER]
289 << "LyXComm: status:" << status
290 << ", read_buffer_:" << read_buffer_
291 << ", cmd:" << cmd << endl;
293 clientcb(client, cmd);
297 if (errno == EAGAIN) {
302 lyxerr << "LyXComm: " << strerror(errno) << endl;
303 if (!read_buffer_.empty()) {
304 lyxerr << "LyXComm: truncated command: "
305 << read_buffer_ << endl;
306 read_buffer_.erase();
308 break; // reset connection
312 // The connection gets reset in errno != EAGAIN
313 // Why does it need to be reset if errno == 0?
320 void LyXComm::send(string const & msg)
323 lyxerr << "LyXComm: Request to send empty string. Ignoring."
328 if (lyxerr.debugging(Debug::LYXSERVER)) {
329 lyxerr << "LyXComm: Sending '" << msg << '\'' << endl;
332 if (pipename.empty()) return;
335 lyxerr << "LyXComm: Pipes are closed. Could not send "
337 } else if (::write(outfd, msg.c_str(), msg.length()) < 0) {
338 lyxerr << "LyXComm: Error sending message: " << msg
339 << '\n' << strerror(errno)
340 << "\nLyXComm: Resetting connection" << endl;
347 rc = DosResetBuffer(outfd); // To avoid synchronization problems.
348 if (rc != NO_ERROR) {
349 errnum = TranslateOS2Error(rc);
350 lyxerr << "LyXComm: Message could not be flushed: " << msg
351 << '\n' << strerror(errnum) << endl;
357 string const LyXComm::inPipeName() const
359 return pipename + string(".in");
363 string const LyXComm::outPipeName() const
365 return pipename + string(".out");
371 LyXServer::~LyXServer()
373 // say goodbye to clients so they stop sending messages
374 // modified june 1999 by stefano@zool.su.se to send as many bye
375 // messages as there are clients, each with client's name.
377 for (int i= 0; i<numclients; ++i) {
378 message = "LYXSRV:" + clients[i] + ":bye\n";
384 /* ---F+------------------------------------------------------------------ *\
385 Function : ServerCallback
387 Purpose : handle data gotten from communication
388 \* ---F------------------------------------------------------------------- */
390 void LyXServer::callback(LyXServer * serv, string const & msg)
392 lyxerr[Debug::LYXSERVER] << "LyXServer: Received: '"
393 << msg << '\'' << endl;
395 char const * p = msg.c_str();
397 // --- parse the string --------------------------------------------
399 // Format: LYXCMD:<client>:<func>:<argstring>\n
401 bool server_only = false;
403 // --- 1. check 'header' ---
405 if (compare(p, "LYXSRV:", 7) == 0) {
407 } else if (0 != compare(p, "LYXCMD:", 7)) {
408 lyxerr << "LyXServer: Unknown request \""
414 // --- 2. for the moment ignore the client name ---
416 while (*p && *p != ':')
417 client += char(*p++);
421 // --- 3. get function name ---
423 while (*p && *p != ':')
426 // --- 4. parse the argument ---
428 if (!server_only && *p == ':' && *(++p)) {
429 while (*p && *p != '\n')
434 lyxerr[Debug::LYXSERVER]
435 << "LyXServer: Client: '" << client
436 << "' Command: '" << cmd
437 << "' Argument: '" << arg << '\'' << endl;
439 // --- lookup and exec the command ------------------
443 // return the greeting to inform the client that
445 if (cmd == "hello") {
447 if (serv->numclients == MAX_CLIENTS) { //paranoid check
448 lyxerr[Debug::LYXSERVER]
449 << "LyXServer: too many clients..."
453 int i= 0; //find place in clients[]
454 while (!serv->clients[i].empty()
455 && i<serv->numclients)
457 serv->clients[i] = client;
459 buf = "LYXSRV:" + client + ":hello\n";
460 lyxerr[Debug::LYXSERVER]
461 << "LyXServer: Greeting "
463 serv->pipes.send(buf);
464 } else if (cmd == "bye") {
465 // If clients == 0 maybe we should reset the pipes
466 // to prevent fake callbacks
467 int i = 0; //look if client is registered
468 for (; i < serv->numclients; ++i) {
469 if (serv->clients[i] == client) break;
471 if (i < serv->numclients) {
473 serv->clients[i].erase();
474 lyxerr[Debug::LYXSERVER]
475 << "LyXServer: Client "
476 << client << " said goodbye"
479 lyxerr[Debug::LYXSERVER]
480 << "LyXServer: ignoring bye messge from unregistered client"
484 lyxerr <<"LyXServer: Undefined server command "
485 << cmd << '.' << endl;
491 // which lyxfunc should we let it connect to?
492 // The correct solution would be to have a
493 // specialized (non-gui) BufferView. But how do
494 // we do it now? Probably we should just let it
495 // connect to the lyxfunc in the single LyXView we
496 // support currently. (Lgb)
499 serv->func->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
500 string const rval = serv->func->getMessage();
502 //modified june 1999 stefano@zool.su.se:
503 //all commands produce an INFO or ERROR message
504 //in the output pipe, even if they do not return
505 //anything. See chapter 4 of Customization doc.
507 if (serv->func->errorStat())
511 buf += client + ':' + cmd + ':' + rval + '\n';
512 serv->pipes.send(buf);
514 // !!! we don't do any error checking -
515 // if the client won't listen, the
516 // message is lost and others too
517 // maybe; so the client should empty
518 // the outpipe before issuing a request.
526 /* ---F+------------------------------------------------------------------ *\
527 Function : LyXNotifyClient
528 Called by : WorkAreaKeyPress
529 Purpose : send a notify messge to a client
530 Parameters: s - string to send
532 \* ---F------------------------------------------------------------------- */
534 void LyXServer::notifyClient(string const & s)
536 string buf = string("NOTIFY:") + s + "\n";