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"
65 // provide an empty mkfifo() if we do not have one. This disables the
68 int mkfifo(char const * __path, mode_t __mode) {
74 void LyXComm::openConnection()
76 lyxerr[Debug::LYXSERVER] << "LyXComm: Opening connection" << endl;
78 // If we are up, that's an error
80 lyxerr << "LyXComm: Already connected" << endl;
83 // We assume that we don't make it
86 if (pipename.empty()) {
87 lyxerr[Debug::LYXSERVER]
88 << "LyXComm: server is disabled, nothing to do"
93 if ((infd = startPipe(inPipeName(), false)) == -1)
96 if ((outfd = startPipe(outPipeName(), true)) == -1) {
97 endPipe(infd, inPipeName(), false);
101 if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
102 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
103 << '\n' << strerror(errno) << endl;
109 lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl;
114 void LyXComm::closeConnection()
116 lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl;
118 if (pipename.empty()) {
119 lyxerr[Debug::LYXSERVER]
120 << "LyXComm: server is disabled, nothing to do"
126 lyxerr << "LyXComm: Already disconnected" << endl;
130 endPipe(infd, inPipeName(), false);
131 endPipe(outfd, outPipeName(), true);
136 int LyXComm::startPipe(string const & filename, bool write)
144 // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
145 // The current emx implementation of access() won't work with pipes.
146 rc = DosCreateNPipe(filename.c_str(), &os2fd, NP_ACCESS_INBOUND,
147 NP_NOWAIT|0x01, 0600, 0600, 0);
148 if (rc == ERROR_PIPE_BUSY) {
149 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
150 << "If no other LyX program is active, please delete"
151 " the pipe by hand and try again." << endl;
156 if (rc != NO_ERROR) {
157 errnum = TranslateOS2Error(rc);
158 lyxerr <<"LyXComm: Could not create pipe " << filename
159 << strerror(errnum) << endl;
163 rc = DosConnectNPipe(os2fd);
164 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
165 errnum = TranslateOS2Error(rc);
166 lyxerr <<"LyXComm: Could not create pipe " << filename
167 << strerror(errnum) << endl;
170 // Imported handles can be used both with OS/2 APIs and emx
171 // library functions.
172 fd = _imphandle(os2fd);
174 if (::access(filename.c_str(), F_OK) == 0) {
175 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
176 << "If no other LyX program is active, please delete"
177 " the pipe by hand and try again." << endl;
182 if (::mkfifo(filename.c_str(), 0600) < 0) {
183 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
184 << strerror(errno) << endl;
187 fd = ::open(filename.c_str(), write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
191 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
192 << strerror(errno) << endl;
193 lyx::unlink(filename);
198 lyx_gui::set_read_callback(fd, this);
205 void LyXComm::endPipe(int & fd, string const & filename, bool write)
211 lyx_gui::remove_read_callback(fd);
218 rc = DosDisConnectNPipe(fd);
219 if (rc != NO_ERROR) {
220 errnum = TranslateOS2Error(rc);
221 lyxerr << "LyXComm: Could not disconnect pipe " << filename
222 << '\n' << strerror(errnum) << endl;
227 if (::close(fd) < 0) {
228 lyxerr << "LyXComm: Could not close pipe " << filename
229 << '\n' << strerror(errno) << endl;
232 // OS/2 pipes are deleted automatically
234 if (lyx::unlink(filename) < 0) {
235 lyxerr << "LyXComm: Could not remove pipe " << filename
236 << '\n' << strerror(errno) << endl;
244 void LyXComm::emergencyCleanup()
246 if (!pipename.empty()) {
247 endPipe(infd, inPipeName(), false);
248 endPipe(outfd, outPipeName(), true);
253 // Receives messages and sends then to client
254 void LyXComm::read_ready()
256 // nb! make read_buffer_ a class-member for multiple sessions
257 static string read_buffer_;
258 read_buffer_.erase();
260 int const charbuf_size = 100;
261 char charbuf[charbuf_size];
265 // the single = is intended here.
266 while ((status = ::read(infd, charbuf, charbuf_size - 1))) {
269 charbuf[status] = '\0'; // turn it into a c string
270 read_buffer_ += rtrim(charbuf, "\r");
271 // commit any commands read
272 while (read_buffer_.find('\n') != string::npos) {
273 // split() grabs the entire string if
274 // the delim /wasn't/ found. ?:-P
276 read_buffer_= split(read_buffer_, cmd,'\n');
277 lyxerr[Debug::LYXSERVER]
278 << "LyXComm: status:" << status
279 << ", read_buffer_:" << read_buffer_
280 << ", cmd:" << cmd << endl;
282 clientcb(client, cmd);
286 if (errno == EAGAIN) {
291 lyxerr << "LyXComm: " << strerror(errno) << endl;
292 if (!read_buffer_.empty()) {
293 lyxerr << "LyXComm: truncated command: "
294 << read_buffer_ << endl;
295 read_buffer_.erase();
297 break; // reset connection
301 // The connection gets reset in errno != EAGAIN
302 // Why does it need to be reset if errno == 0?
309 void LyXComm::send(string const & msg)
312 lyxerr << "LyXComm: Request to send empty string. Ignoring."
317 if (lyxerr.debugging(Debug::LYXSERVER)) {
318 lyxerr << "LyXComm: Sending '" << msg << '\'' << endl;
321 if (pipename.empty()) return;
324 lyxerr << "LyXComm: Pipes are closed. Could not send "
326 } else if (::write(outfd, msg.c_str(), msg.length()) < 0) {
327 lyxerr << "LyXComm: Error sending message: " << msg
328 << '\n' << strerror(errno)
329 << "\nLyXComm: Resetting connection" << endl;
336 rc = DosResetBuffer(outfd); // To avoid synchronization problems.
337 if (rc != NO_ERROR) {
338 errnum = TranslateOS2Error(rc);
339 lyxerr << "LyXComm: Message could not be flushed: " << msg
340 << '\n' << strerror(errnum) << endl;
348 LyXServer::~LyXServer()
350 // say goodbye to clients so they stop sending messages
351 // modified june 1999 by stefano@zool.su.se to send as many bye
352 // messages as there are clients, each with client's name.
354 for (int i= 0; i<numclients; ++i) {
355 message = "LYXSRV:" + clients[i] + ":bye\n";
361 /* ---F+------------------------------------------------------------------ *\
362 Function : ServerCallback
364 Purpose : handle data gotten from communication
365 \* ---F------------------------------------------------------------------- */
367 void LyXServer::callback(LyXServer * serv, string const & msg)
369 lyxerr[Debug::LYXSERVER] << "LyXServer: Received: '"
370 << msg << '\'' << endl;
372 char const * p = msg.c_str();
374 // --- parse the string --------------------------------------------
376 // Format: LYXCMD:<client>:<func>:<argstring>\n
378 bool server_only = false;
380 // --- 1. check 'header' ---
382 if (compare(p, "LYXSRV:", 7) == 0) {
384 } else if (0 != compare(p, "LYXCMD:", 7)) {
385 lyxerr << "LyXServer: Unknown request \""
391 // --- 2. for the moment ignore the client name ---
393 while (*p && *p != ':')
394 client += char(*p++);
398 // --- 3. get function name ---
400 while (*p && *p != ':')
403 // --- 4. parse the argument ---
405 if (!server_only && *p == ':' && *(++p)) {
406 while (*p && *p != '\n')
411 lyxerr[Debug::LYXSERVER]
412 << "LyXServer: Client: '" << client
413 << "' Command: '" << cmd
414 << "' Argument: '" << arg << '\'' << endl;
416 // --- lookup and exec the command ------------------
420 // return the greeting to inform the client that
422 if (cmd == "hello") {
424 if (serv->numclients == MAX_CLIENTS) { //paranoid check
425 lyxerr[Debug::LYXSERVER]
426 << "LyXServer: too many clients..."
430 int i= 0; //find place in clients[]
431 while (!serv->clients[i].empty()
432 && i<serv->numclients)
434 serv->clients[i] = client;
436 buf = "LYXSRV:" + client + ":hello\n";
437 lyxerr[Debug::LYXSERVER]
438 << "LyXServer: Greeting "
440 serv->pipes.send(buf);
441 } else if (cmd == "bye") {
442 // If clients == 0 maybe we should reset the pipes
443 // to prevent fake callbacks
444 int i = 0; //look if client is registered
445 for (; i < serv->numclients; ++i) {
446 if (serv->clients[i] == client) break;
448 if (i < serv->numclients) {
450 serv->clients[i].erase();
451 lyxerr[Debug::LYXSERVER]
452 << "LyXServer: Client "
453 << client << " said goodbye"
456 lyxerr[Debug::LYXSERVER]
457 << "LyXServer: ignoring bye messge from unregistered client"
461 lyxerr <<"LyXServer: Undefined server command "
462 << cmd << '.' << endl;
468 // which lyxfunc should we let it connect to?
469 // The correct solution would be to have a
470 // specialized (non-gui) BufferView. But how do
471 // we do it now? Probably we should just let it
472 // connect to the lyxfunc in the single LyXView we
473 // support currently. (Lgb)
476 serv->func->dispatch(cmd + ' ' + arg);
477 string const rval = serv->func->getMessage();
479 //modified june 1999 stefano@zool.su.se:
480 //all commands produce an INFO or ERROR message
481 //in the output pipe, even if they do not return
482 //anything. See chapter 4 of Customization doc.
484 if (serv->func->errorStat())
488 buf += client + ':' + cmd + ':' + rval + '\n';
489 serv->pipes.send(buf);
491 // !!! we don't do any error checking -
492 // if the client won't listen, the
493 // message is lost and others too
494 // maybe; so the client should empty
495 // the outpipe before issuing a request.
503 /* ---F+------------------------------------------------------------------ *\
504 Function : LyXNotifyClient
505 Called by : WorkAreaKeyPress
506 Purpose : send a notify messge to a client
507 Parameters: s - string to send
509 \* ---F------------------------------------------------------------------- */
511 void LyXServer::notifyClient(string const & s)
513 string buf = string("NOTIFY:") + s + "\n";