1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
12 Docu : To use the lyxserver define the name of the pipe in your
14 \serverpipe "/home/myhome/.lyxpipe"
15 Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
16 Each message consists of a single line in ASCII. Input lines
17 (client -> LyX) have the following format:
18 "LYXCMD:<clientname>:<functionname>:<argument>"
19 Answers from LyX look like this:
20 "INFO:<clientname>:<functionname>:<data>"
21 [asierra970531] Or like this in case of error:
22 "ERROR:<clientname>:<functionname>:<error message>"
23 where <clientname> and <functionname> are just echoed.
24 If LyX notifies about a user defined extension key-sequence,
25 the line looks like this:
26 "NOTIFY:<key-sequence>"
27 [asierra970531] New server-only messages to implement a simple protocol
28 "LYXSRV:<clientname>:<protocol message>"
29 where <protocol message> can be "hello" or "bye". If hello is
30 received LyX will inform the client that it's listening its
31 messages, and 'bye' will inform that lyx is closing.
33 See development/server_monitor.c for an example client.
34 Purpose: implement a client/server lib for LyX
39 #include <sys/types.h>
45 #include "lyxserver.h"
49 #include "support/lstrings.h"
50 #include "support/lyxlib.h"
51 #include "frontends/lyx_gui.h"
56 #define OS2EMX_PLAIN_CHAR
57 #define INCL_DOSNMPIPES
58 #define INCL_DOSERRORS
60 #include "support/os2_errortable.h"
64 using namespace lyx::support;
68 // provide an empty mkfifo() if we do not have one. This disables the
71 int mkfifo(char const * __path, mode_t __mode) {
77 void LyXComm::openConnection()
79 lyxerr[Debug::LYXSERVER] << "LyXComm: Opening connection" << endl;
81 // If we are up, that's an error
83 lyxerr << "LyXComm: Already connected" << endl;
86 // We assume that we don't make it
89 if (pipename.empty()) {
90 lyxerr[Debug::LYXSERVER]
91 << "LyXComm: server is disabled, nothing to do"
96 if ((infd = startPipe(inPipeName(), false)) == -1)
99 if ((outfd = startPipe(outPipeName(), true)) == -1) {
100 endPipe(infd, inPipeName(), false);
104 if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
105 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
106 << '\n' << strerror(errno) << endl;
112 lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl;
117 void LyXComm::closeConnection()
119 lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl;
121 if (pipename.empty()) {
122 lyxerr[Debug::LYXSERVER]
123 << "LyXComm: server is disabled, nothing to do"
129 lyxerr << "LyXComm: Already disconnected" << endl;
133 endPipe(infd, inPipeName(), false);
134 endPipe(outfd, outPipeName(), true);
139 int LyXComm::startPipe(string const & filename, bool write)
147 // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
148 // The current emx implementation of access() won't work with pipes.
149 rc = DosCreateNPipe(filename.c_str(), &os2fd, NP_ACCESS_INBOUND,
150 NP_NOWAIT|0x01, 0600, 0600, 0);
151 if (rc == ERROR_PIPE_BUSY) {
152 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
153 << "If no other LyX program is active, please delete"
154 " the pipe by hand and try again." << endl;
159 if (rc != NO_ERROR) {
160 errnum = TranslateOS2Error(rc);
161 lyxerr <<"LyXComm: Could not create pipe " << filename
162 << strerror(errnum) << endl;
166 rc = DosConnectNPipe(os2fd);
167 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
168 errnum = TranslateOS2Error(rc);
169 lyxerr <<"LyXComm: Could not create pipe " << filename
170 << strerror(errnum) << endl;
173 // Imported handles can be used both with OS/2 APIs and emx
174 // library functions.
175 fd = _imphandle(os2fd);
177 if (::access(filename.c_str(), F_OK) == 0) {
178 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
179 << "If no other LyX program is active, please delete"
180 " the pipe by hand and try again." << endl;
185 if (::mkfifo(filename.c_str(), 0600) < 0) {
186 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
187 << strerror(errno) << endl;
190 fd = ::open(filename.c_str(), write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
194 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
195 << strerror(errno) << endl;
201 lyx_gui::set_read_callback(fd, this);
208 void LyXComm::endPipe(int & fd, string const & filename, bool write)
214 lyx_gui::remove_read_callback(fd);
221 rc = DosDisConnectNPipe(fd);
222 if (rc != NO_ERROR) {
223 errnum = TranslateOS2Error(rc);
224 lyxerr << "LyXComm: Could not disconnect pipe " << filename
225 << '\n' << strerror(errnum) << endl;
230 if (::close(fd) < 0) {
231 lyxerr << "LyXComm: Could not close pipe " << filename
232 << '\n' << strerror(errno) << endl;
235 // OS/2 pipes are deleted automatically
237 if (unlink(filename) < 0) {
238 lyxerr << "LyXComm: Could not remove pipe " << filename
239 << '\n' << strerror(errno) << endl;
247 void LyXComm::emergencyCleanup()
249 if (!pipename.empty()) {
250 endPipe(infd, inPipeName(), false);
251 endPipe(outfd, outPipeName(), true);
256 // Receives messages and sends then to client
257 void LyXComm::read_ready()
259 // nb! make read_buffer_ a class-member for multiple sessions
260 static string read_buffer_;
261 read_buffer_.erase();
263 int const charbuf_size = 100;
264 char charbuf[charbuf_size];
268 // the single = is intended here.
269 while ((status = ::read(infd, charbuf, charbuf_size - 1))) {
272 charbuf[status] = '\0'; // turn it into a c string
273 read_buffer_ += rtrim(charbuf, "\r");
274 // commit any commands read
275 while (read_buffer_.find('\n') != string::npos) {
276 // split() grabs the entire string if
277 // the delim /wasn't/ found. ?:-P
279 read_buffer_= split(read_buffer_, cmd,'\n');
280 lyxerr[Debug::LYXSERVER]
281 << "LyXComm: status:" << status
282 << ", read_buffer_:" << read_buffer_
283 << ", cmd:" << cmd << endl;
285 clientcb(client, cmd);
289 if (errno == EAGAIN) {
294 lyxerr << "LyXComm: " << strerror(errno) << endl;
295 if (!read_buffer_.empty()) {
296 lyxerr << "LyXComm: truncated command: "
297 << read_buffer_ << endl;
298 read_buffer_.erase();
300 break; // reset connection
304 // The connection gets reset in errno != EAGAIN
305 // Why does it need to be reset if errno == 0?
312 void LyXComm::send(string const & msg)
315 lyxerr << "LyXComm: Request to send empty string. Ignoring."
320 if (lyxerr.debugging(Debug::LYXSERVER)) {
321 lyxerr << "LyXComm: Sending '" << msg << '\'' << endl;
324 if (pipename.empty()) return;
327 lyxerr << "LyXComm: Pipes are closed. Could not send "
329 } else if (::write(outfd, msg.c_str(), msg.length()) < 0) {
330 lyxerr << "LyXComm: Error sending message: " << msg
331 << '\n' << strerror(errno)
332 << "\nLyXComm: Resetting connection" << endl;
339 rc = DosResetBuffer(outfd); // To avoid synchronization problems.
340 if (rc != NO_ERROR) {
341 errnum = TranslateOS2Error(rc);
342 lyxerr << "LyXComm: Message could not be flushed: " << msg
343 << '\n' << strerror(errnum) << endl;
351 LyXServer::~LyXServer()
353 // say goodbye to clients so they stop sending messages
354 // modified june 1999 by stefano@zool.su.se to send as many bye
355 // messages as there are clients, each with client's name.
357 for (int i= 0; i<numclients; ++i) {
358 message = "LYXSRV:" + clients[i] + ":bye\n";
364 /* ---F+------------------------------------------------------------------ *\
365 Function : ServerCallback
367 Purpose : handle data gotten from communication
368 \* ---F------------------------------------------------------------------- */
370 void LyXServer::callback(LyXServer * serv, string const & msg)
372 lyxerr[Debug::LYXSERVER] << "LyXServer: Received: '"
373 << msg << '\'' << endl;
375 char const * p = msg.c_str();
377 // --- parse the string --------------------------------------------
379 // Format: LYXCMD:<client>:<func>:<argstring>\n
381 bool server_only = false;
383 // --- 1. check 'header' ---
385 if (compare(p, "LYXSRV:", 7) == 0) {
387 } else if (0 != compare(p, "LYXCMD:", 7)) {
388 lyxerr << "LyXServer: Unknown request \""
394 // --- 2. for the moment ignore the client name ---
396 while (*p && *p != ':')
397 client += char(*p++);
401 // --- 3. get function name ---
403 while (*p && *p != ':')
406 // --- 4. parse the argument ---
408 if (!server_only && *p == ':' && *(++p)) {
409 while (*p && *p != '\n')
414 lyxerr[Debug::LYXSERVER]
415 << "LyXServer: Client: '" << client
416 << "' Command: '" << cmd
417 << "' Argument: '" << arg << '\'' << endl;
419 // --- lookup and exec the command ------------------
423 // return the greeting to inform the client that
425 if (cmd == "hello") {
427 if (serv->numclients == MAX_CLIENTS) { //paranoid check
428 lyxerr[Debug::LYXSERVER]
429 << "LyXServer: too many clients..."
433 int i= 0; //find place in clients[]
434 while (!serv->clients[i].empty()
435 && i<serv->numclients)
437 serv->clients[i] = client;
439 buf = "LYXSRV:" + client + ":hello\n";
440 lyxerr[Debug::LYXSERVER]
441 << "LyXServer: Greeting "
443 serv->pipes.send(buf);
444 } else if (cmd == "bye") {
445 // If clients == 0 maybe we should reset the pipes
446 // to prevent fake callbacks
447 int i = 0; //look if client is registered
448 for (; i < serv->numclients; ++i) {
449 if (serv->clients[i] == client) break;
451 if (i < serv->numclients) {
453 serv->clients[i].erase();
454 lyxerr[Debug::LYXSERVER]
455 << "LyXServer: Client "
456 << client << " said goodbye"
459 lyxerr[Debug::LYXSERVER]
460 << "LyXServer: ignoring bye messge from unregistered client"
464 lyxerr <<"LyXServer: Undefined server command "
465 << cmd << '.' << endl;
471 // which lyxfunc should we let it connect to?
472 // The correct solution would be to have a
473 // specialized (non-gui) BufferView. But how do
474 // we do it now? Probably we should just let it
475 // connect to the lyxfunc in the single LyXView we
476 // support currently. (Lgb)
479 serv->func->dispatch(cmd + ' ' + arg);
480 string const rval = serv->func->getMessage();
482 //modified june 1999 stefano@zool.su.se:
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 (serv->func->errorStat())
491 buf += client + ':' + cmd + ':' + rval + '\n';
492 serv->pipes.send(buf);
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 /* ---F+------------------------------------------------------------------ *\
507 Function : LyXNotifyClient
508 Called by : WorkAreaKeyPress
509 Purpose : send a notify messge to a client
510 Parameters: s - string to send
512 \* ---F------------------------------------------------------------------- */
514 void LyXServer::notifyClient(string const & s)
516 string buf = string("NOTIFY:") + s + "\n";