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)
156 // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
157 // The current emx implementation of access() won't work with pipes.
158 rc = DosCreateNPipe(filename.c_str(), &os2fd, NP_ACCESS_INBOUND,
159 NP_NOWAIT|0x01, 0600, 0600, 0);
160 if (rc == ERROR_PIPE_BUSY) {
161 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
162 << "If no other LyX program is active, please delete"
163 " the pipe by hand and try again." << endl;
168 if (rc != NO_ERROR) {
169 errnum = TranslateOS2Error(rc);
170 lyxerr <<"LyXComm: Could not create pipe " << filename
171 << strerror(errnum) << endl;
175 rc = DosConnectNPipe(os2fd);
176 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
177 errnum = TranslateOS2Error(rc);
178 lyxerr <<"LyXComm: Could not create pipe " << filename
179 << strerror(errnum) << endl;
182 // Imported handles can be used both with OS/2 APIs and emx
183 // library functions.
184 fd = _imphandle(os2fd);
186 if (::access(filename.c_str(), F_OK) == 0) {
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 (::mkfifo(filename.c_str(), 0600) < 0) {
195 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
196 << strerror(errno) << endl;
199 fd = ::open(filename.c_str(), write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
203 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
204 << strerror(errno) << endl;
210 lyx_gui::register_socket_callback(fd, boost::bind(&LyXComm::read_ready, this));
217 void LyXComm::endPipe(int & fd, string const & filename, bool write)
223 lyx_gui::unregister_socket_callback(fd);
230 rc = DosDisConnectNPipe(fd);
231 if (rc != NO_ERROR) {
232 errnum = TranslateOS2Error(rc);
233 lyxerr << "LyXComm: Could not disconnect pipe " << filename
234 << '\n' << strerror(errnum) << endl;
239 if (::close(fd) < 0) {
240 lyxerr << "LyXComm: Could not close pipe " << filename
241 << '\n' << strerror(errno) << endl;
244 // OS/2 pipes are deleted automatically
246 if (unlink(filename) < 0) {
247 lyxerr << "LyXComm: Could not remove pipe " << filename
248 << '\n' << strerror(errno) << endl;
256 void LyXComm::emergencyCleanup()
258 if (!pipename.empty()) {
259 endPipe(infd, inPipeName(), false);
260 endPipe(outfd, outPipeName(), true);
265 // Receives messages and sends then to client
266 void LyXComm::read_ready()
268 // nb! make read_buffer_ a class-member for multiple sessions
269 static string read_buffer_;
270 read_buffer_.erase();
272 int const charbuf_size = 100;
273 char charbuf[charbuf_size];
277 // the single = is intended here.
278 while ((status = ::read(infd, charbuf, charbuf_size - 1))) {
281 charbuf[status] = '\0'; // turn it into a c string
282 read_buffer_ += rtrim(charbuf, "\r");
283 // commit any commands read
284 while (read_buffer_.find('\n') != string::npos) {
285 // split() grabs the entire string if
286 // the delim /wasn't/ found. ?:-P
288 read_buffer_= split(read_buffer_, cmd,'\n');
289 lyxerr[Debug::LYXSERVER]
290 << "LyXComm: status:" << status
291 << ", read_buffer_:" << read_buffer_
292 << ", cmd:" << cmd << endl;
294 clientcb(client, cmd);
298 if (errno == EAGAIN) {
303 lyxerr << "LyXComm: " << strerror(errno) << endl;
304 if (!read_buffer_.empty()) {
305 lyxerr << "LyXComm: truncated command: "
306 << read_buffer_ << endl;
307 read_buffer_.erase();
309 break; // reset connection
313 // The connection gets reset in errno != EAGAIN
314 // Why does it need to be reset if errno == 0?
321 void LyXComm::send(string const & msg)
324 lyxerr << "LyXComm: Request to send empty string. Ignoring."
329 if (lyxerr.debugging(Debug::LYXSERVER)) {
330 lyxerr << "LyXComm: Sending '" << msg << '\'' << endl;
333 if (pipename.empty()) return;
336 lyxerr << "LyXComm: Pipes are closed. Could not send "
338 } else if (::write(outfd, msg.c_str(), msg.length()) < 0) {
339 lyxerr << "LyXComm: Error sending message: " << msg
340 << '\n' << strerror(errno)
341 << "\nLyXComm: Resetting connection" << endl;
348 rc = DosResetBuffer(outfd); // To avoid synchronization problems.
349 if (rc != NO_ERROR) {
350 errnum = TranslateOS2Error(rc);
351 lyxerr << "LyXComm: Message could not be flushed: " << msg
352 << '\n' << strerror(errnum) << endl;
360 LyXServer::~LyXServer()
362 // say goodbye to clients so they stop sending messages
363 // modified june 1999 by stefano@zool.su.se to send as many bye
364 // messages as there are clients, each with client's name.
366 for (int i= 0; i<numclients; ++i) {
367 message = "LYXSRV:" + clients[i] + ":bye\n";
373 /* ---F+------------------------------------------------------------------ *\
374 Function : ServerCallback
376 Purpose : handle data gotten from communication
377 \* ---F------------------------------------------------------------------- */
379 void LyXServer::callback(LyXServer * serv, string const & msg)
381 lyxerr[Debug::LYXSERVER] << "LyXServer: Received: '"
382 << msg << '\'' << endl;
384 char const * p = msg.c_str();
386 // --- parse the string --------------------------------------------
388 // Format: LYXCMD:<client>:<func>:<argstring>\n
390 bool server_only = false;
392 // --- 1. check 'header' ---
394 if (compare(p, "LYXSRV:", 7) == 0) {
396 } else if (0 != compare(p, "LYXCMD:", 7)) {
397 lyxerr << "LyXServer: Unknown request \""
403 // --- 2. for the moment ignore the client name ---
405 while (*p && *p != ':')
406 client += char(*p++);
410 // --- 3. get function name ---
412 while (*p && *p != ':')
415 // --- 4. parse the argument ---
417 if (!server_only && *p == ':' && *(++p)) {
418 while (*p && *p != '\n')
423 lyxerr[Debug::LYXSERVER]
424 << "LyXServer: Client: '" << client
425 << "' Command: '" << cmd
426 << "' Argument: '" << arg << '\'' << endl;
428 // --- lookup and exec the command ------------------
432 // return the greeting to inform the client that
434 if (cmd == "hello") {
436 if (serv->numclients == MAX_CLIENTS) { //paranoid check
437 lyxerr[Debug::LYXSERVER]
438 << "LyXServer: too many clients..."
442 int i= 0; //find place in clients[]
443 while (!serv->clients[i].empty()
444 && i<serv->numclients)
446 serv->clients[i] = client;
448 buf = "LYXSRV:" + client + ":hello\n";
449 lyxerr[Debug::LYXSERVER]
450 << "LyXServer: Greeting "
452 serv->pipes.send(buf);
453 } else if (cmd == "bye") {
454 // If clients == 0 maybe we should reset the pipes
455 // to prevent fake callbacks
456 int i = 0; //look if client is registered
457 for (; i < serv->numclients; ++i) {
458 if (serv->clients[i] == client) break;
460 if (i < serv->numclients) {
462 serv->clients[i].erase();
463 lyxerr[Debug::LYXSERVER]
464 << "LyXServer: Client "
465 << client << " said goodbye"
468 lyxerr[Debug::LYXSERVER]
469 << "LyXServer: ignoring bye messge from unregistered client"
473 lyxerr <<"LyXServer: Undefined server command "
474 << cmd << '.' << endl;
480 // which lyxfunc should we let it connect to?
481 // The correct solution would be to have a
482 // specialized (non-gui) BufferView. But how do
483 // we do it now? Probably we should just let it
484 // connect to the lyxfunc in the single LyXView we
485 // support currently. (Lgb)
488 serv->func->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
489 string const rval = serv->func->getMessage();
491 //modified june 1999 stefano@zool.su.se:
492 //all commands produce an INFO or ERROR message
493 //in the output pipe, even if they do not return
494 //anything. See chapter 4 of Customization doc.
496 if (serv->func->errorStat())
500 buf += client + ':' + cmd + ':' + rval + '\n';
501 serv->pipes.send(buf);
503 // !!! we don't do any error checking -
504 // if the client won't listen, the
505 // message is lost and others too
506 // maybe; so the client should empty
507 // the outpipe before issuing a request.
515 /* ---F+------------------------------------------------------------------ *\
516 Function : LyXNotifyClient
517 Called by : WorkAreaKeyPress
518 Purpose : send a notify messge to a client
519 Parameters: s - string to send
521 \* ---F------------------------------------------------------------------- */
523 void LyXServer::notifyClient(string const & s)
525 string buf = string("NOTIFY:") + s + "\n";