2 /* This file is part of
3 * ======================================================
5 * LyX, The Document Processor
7 * Copyright 1995 Matthias Ettrich
8 * Copyright 1995-1998 The LyX Team.
10 * ======================================================*/
13 Docu : To use the lyxserver define the name of the pipe in your
15 \serverpipe "/home/myhome/.lyxpipe"
16 Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
17 Each message consists of a single line in ASCII. Input lines
18 (client -> LyX) have the following format:
19 "LYXCMD:<clientname>:<functionname>:<argument>"
20 Answers from LyX look like this:
21 "INFO:<clientname>:<functionname>:<data>"
22 [asierra970531] Or like this in case of error:
23 "ERROR:<clientname>:<functionname>:<error message>"
24 where <clientname> and <functionname> are just echoed.
25 If LyX notifies about a user defined extension key-sequence,
26 the line looks like this:
27 "NOTIFY:<key-sequence>"
28 [asierra970531] New server-only messages to implement a simple protocol
29 "LYXSRV:<clientname>:<protocol message>"
30 where <protocol message> can be "hello" or "bye". If hello is
31 received LyX will inform the client that it's listening its
32 messages, and 'bye' will inform that lyx is closing.
34 See development/server_monitor.c for an example client.
35 Purpose: implement a client/server lib for LyX
42 #include <sys/types.h>
47 #include FORMS_H_LOCATION
50 #pragma implementation
53 #include "lyxserver.h"
57 #include "support/lstrings.h"
62 #define OS2EMX_PLAIN_CHAR
63 #define INCL_DOSNMPIPES
64 #define INCL_DOSERRORS
66 #include "os2_errortable.h"
69 // provide an empty mkfifo() if we do not have one. This disables the
72 int mkfifo( char *__path, mode_t __mode ) {
78 /* === variables ========================================================= */
80 extern LyXAction lyxaction;
85 void LyXComm::openConnection() {
86 lyxerr.debug("LyXComm: Opening connection", Error::LYXSERVER);
88 // If we are up, that's an error
90 lyxerr.print("LyXComm: Already connected");
93 // We assume that we don't make it
96 if (pipename.empty()) return;
98 // --- prepare input pipe ---------------------------------------
100 string tmp = pipename + ".in";
106 // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
107 // The current emx implementation of access() won't work with pipes.
108 rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_INBOUND,
109 NP_NOWAIT|0x01, 0600, 0600, 0);
110 if (rc == ERROR_PIPE_BUSY) {
112 if (access(tmp.c_str(), F_OK) == 0) {
114 lyxerr.print("LyXComm: Pipe " + tmp + " already exists.");
115 lyxerr.print("If no other LyX program is active, please delete"
116 " the pipe by hand and try again.");
121 if (mkfifo(tmp.c_str(), 0600) < 0) {
122 lyxerr.print("LyXComm: Could not create pipe " + tmp);
123 lyxerr.print(strerror(errno));
126 infd = open(tmp.c_str(), O_RDONLY|O_NONBLOCK);
128 if (rc != NO_ERROR) {
129 errnum = TranslateOS2Error(rc);
130 lyxerr.print("LyXComm: Could not create pipe " + tmp);
131 lyxerr.print(strerror(errnum));
135 rc = DosConnectNPipe(fd);
136 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
137 errnum = TranslateOS2Error(rc);
138 lyxerr.print("LyXComm: Could not create pipe " + tmp);
139 lyxerr.print(strerror(errnum));
142 // Imported handles can be used both with OS/2 APIs and emx
143 // library functions.
144 infd = _imphandle(fd);
147 lyxerr.print("LyXComm: Could not open pipe " + tmp);
148 lyxerr.print(strerror(errno));
151 fl_add_io_callback(infd, FL_READ, callback, (void*)this);
153 // --- prepare output pipe ---------------------------------------
155 tmp = pipename + ".out";
158 if (access(tmp.c_str(), F_OK) == 0) {
160 rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_DUPLEX,
161 NP_NOWAIT|0x01, 0600, 0600, 0);
163 if (rc == ERROR_PIPE_BUSY) {
165 lyxerr.print("LyXComm: Pipe " + tmp + " already exists.");
166 lyxerr.print("If no other LyX program is active, please delete"
167 " the pipe by hand and try again.");
172 if (mkfifo(tmp.c_str(), 0600) < 0) {
173 lyxerr.print("LyXComm: Could not create pipe " + tmp);
174 lyxerr.print(strerror(errno));
177 if (access(tmp.c_str(), F_OK) != 0) {
178 lyxerr.print("LyXComm: Pipe " + tmp + " does not exist");
181 outfd = open(tmp.c_str(), O_RDWR);
183 if (rc != NO_ERROR) {
184 errnum = TranslateOS2Error(rc);
185 lyxerr.print("LyXComm: Could not create pipe " + tmp);
186 lyxerr.print(strerror(errnum));
189 rc = DosConnectNPipe(fd);
190 if (rc == ERROR_BAD_PIPE) {
191 lyxerr.print("LyXComm: Pipe " + tmp + " does not exist");
194 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
195 errnum = TranslateOS2Error(rc);
196 lyxerr.print("LyXComm: Could not create pipe " + tmp);
197 lyxerr.print(strerror(errnum));
200 outfd = _imphandle(fd);
203 lyxerr.print("LyXComm: Could not open pipe " + tmp);
204 lyxerr.print(strerror(errno));
207 if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
208 lyxerr.print("LyXComm: Could not set flags on pipe " + tmp);
209 lyxerr.print(strerror(errno));
214 lyxerr.debug("LyXComm: Connection established", Error::LYXSERVER);
218 void LyXComm::closeConnection() {
223 lyxerr.debug("LyXComm: Closing connection", Error::LYXSERVER);
225 if (pipename.empty()) {
230 lyxerr.print("LyXComm: Already disconnected");
235 fl_remove_io_callback(infd, FL_READ, callback);
237 string tmp = pipename + ".in";
238 #ifdef __EMX__ // Notify the operating system.
239 rc = DosDisConnectNPipe(infd);
240 if (rc != NO_ERROR) {
241 errnum = TranslateOS2Error(rc);
242 lyxerr.print("LyXComm: Could not disconnect pipe " + tmp);
243 lyxerr.print(strerror(errnum));
247 if (close(infd) < 0) {
248 lyxerr.print("LyXComm: Could not close pipe " + tmp);
249 lyxerr.print(strerror(errno));
251 #ifndef __EMX__ // OS/2 named pipes will be automatically removed.
252 if (unlink(tmp.c_str()) < 0){
253 lyxerr.print("LyXComm: Could not remove pipe " + tmp);
254 lyxerr.print(strerror(errno));
259 string tmp = pipename + ".out";
261 rc = DosDisConnectNPipe(outfd);
262 if (rc != NO_ERROR) {
263 errnum = TranslateOS2Error(rc);
264 lyxerr.print("LyXComm: Could not disconnect pipe " + tmp);
265 lyxerr.print(strerror(errnum));
269 if (close(outfd) < 0) {
270 lyxerr.print("LyXComm: Could not close pipe " + tmp);
271 lyxerr.print(strerror(errno));
274 if (unlink(tmp.c_str()) < 0){
275 lyxerr.print("LyXComm: Could not remove pipe " + tmp);
276 lyxerr.print(strerror(errno));
283 // Receives messages and sends then to client
284 void LyXComm::callback(int fd, void *v)
286 LyXComm * c = (LyXComm *) v;
288 if (lyxerr.debugging(Error::LYXSERVER)) {
289 lyxerr.print(string("LyXComm: Receiving from fd ") + tostr(fd));
292 const int CMDBUFLEN = 100;
293 char charbuf[CMDBUFLEN];
295 // nb! make lsbuf a class-member for multiple sessions
300 // the single = is intended here.
301 while((status = read(fd,charbuf,CMDBUFLEN-1)))
302 {// break and return in loop
303 if(status > 0) // got something
305 charbuf[status]='\0'; // turn it into a c string
306 lsbuf += strip(charbuf, '\r');
307 // commit any commands read
308 while(lsbuf.find('\n') != string::npos) // while still
312 // split() grabs the entire string if
313 // the delim /wasn't/ found. ?:-P
314 lsbuf=split(lsbuf, cmd,'\n');
315 lyxerr.debug(string("LyXComm: status:")
316 + tostr(status) + ", lsbuf:" + lsbuf
320 c->clientcb(c->client, cmd);
325 { // EAGAIN is not really an error , it means we're
326 // only reading too fast for the writing process on
327 // the other end of the pipe.
329 return; // up to libforms select-loop (*crunch*)
333 lyxerr.print(string("LyXComm: ") + strerror(errno));
336 lyxerr.print("LyxComm: truncated command: "
340 break; // reset connection
343 c->closeConnection();
348 void LyXComm::send(string const & msg) {
350 lyxerr.print("LyXComm: Request to send empty string. Ignoring.");
354 if (lyxerr.debugging(Error::LYXSERVER)) {
355 lyxerr.print("LyXComm: Sending '" + msg + '\'');
358 if (pipename.empty()) return;
361 lyxerr.print("LyXComm: Pipes are closed. Could not send "+ msg);
362 } else if (write(outfd, msg.c_str(), msg.length()) < 0) {
363 lyxerr.print("LyXComm: Error sending message: " + msg);
364 lyxerr.print(strerror(errno));
365 lyxerr.print("LyXComm: Resetting connection");
372 rc = DosResetBuffer(outfd); // To avoid synchronization problems.
373 if (rc != NO_ERROR) {
374 errnum = TranslateOS2Error(rc);
375 lyxerr.print("LyXComm: Message could not be flushed: " +msg);
376 lyxerr.print(strerror(errnum));
384 LyXServer::~LyXServer()
386 // say goodbye to clients so they stop sending messages
387 // modified june 1999 by stefano@zool.su.se to send as many bye
388 // messages as there are clients, each with client's name.
390 for (int i=0; i<numclients; i++) {
391 message = "LYXSRV:" + clients[i] + ":bye\n";
397 /* ---F+------------------------------------------------------------------ *\
398 Function : ServerCallback
400 Purpose : handle data gotten from communication
401 \* ---F------------------------------------------------------------------- */
403 void LyXServer::callback(LyXServer * serv, string const & msg)
405 lyxerr.debug("LyXServer: Received: '" + msg + '\'', Error::LYXSERVER);
407 char const *p = msg.c_str();
409 // --- parse the string --------------------------------------------
411 // Format: LYXCMD:<client>:<func>:<argstring>\n
413 bool server_only = false;
415 // --- 1. check 'header' ---
416 if (strncmp(p, "LYXSRV:", 7)==0) {
418 } else if(0!=strncmp(p, "LYXCMD:", 7)) {
419 lyxerr.print("LyXServer: Unknown request");
424 // --- 2. for the moment ignore the client name ---
426 while(*p && *p != ':')
427 client += char(*p++);
431 // --- 3. get function name ---
433 while(*p && *p != ':')
436 // --- 4. parse the argument ---
438 if(!server_only && *p == ':' && *(++p)) {
439 while(*p && *p != '\n')
444 lyxerr.debug("LyXServer: Client: '" + client + "' Command: '" + cmd + "' Argument: '" + arg + '\'', Error::LYXSERVER);
446 // --- lookup and exec the command ------------------
450 // return the greeting to inform the client that
452 if (cmd == "hello") {
454 if(serv->numclients==MAX_CLIENTS){ //paranoid check
455 lyxerr.debug("LyXServer: too many clients...", Error::LYXSERVER);
458 int i=0; //find place in clients[]
459 while (!serv->clients[i].empty()
460 && i<serv->numclients)
462 serv->clients[i] = client;
464 buf = "LYXSRV:" + client + ":hello\n";
465 lyxerr.debug("LyXServer: Greeting " + client, Error::LYXSERVER);
466 serv->pipes.send(buf);
467 } else if (cmd == "bye") {
468 // If clients==0 maybe we should reset the pipes
469 // to prevent fake callbacks
470 int i; //look if client is registered
471 for (i=0; i<serv->numclients; i++) {
472 if (serv->clients[i] == client) break;
474 if (i<serv->numclients) {
476 serv->clients[i].erase();
477 lyxerr.debug("LyXServer: Client " + client + " said goodbye",
480 lyxerr.debug("LyXServer: ignoring bye messge from unregistered client" +
481 client + "\n", Error::LYXSERVER);
484 lyxerr.print("LyXServer: Undefined server command " + cmd + ".");
490 // which lyxfunc should we let it connect to?
491 // The correct solution would be to have a
492 // specialized (non-gui) BufferView. But how do
493 // we do it now? Probably we should just let it
494 // connect to the lyxfunc in the single LyXView we
495 // support currently. (Lgb)
497 int action = lyxaction.LookupFunc(cmd.c_str());
502 rval = serv->func->Dispatch(action, arg.c_str());
504 rval = "Unknown command";
507 //modified june 1999 stefano@zool.su.se:
508 //all commands produce an INFO or ERROR message
509 //in the output pipe, even if they do not return
510 //anything. See chapter 4 of Customization doc.
511 if (action<0 || serv->func->errorStat())
515 buf += string(client) + ":" + cmd + ":" + rval + "\n";
516 serv->pipes.send(buf);
518 // !!! we don't do any error checking -
519 // if the client won't listen, the
520 // message is lost and others too
521 // maybe; so the client should empty
522 // the outpipe before issuing a request.
530 /* ---F+------------------------------------------------------------------ *\
531 Function : LyxNotifyClient
532 Called by : WorkAreaKeyPress
533 Purpose : send a notify messge to a client
534 Parameters: s - string to send
536 \* ---F------------------------------------------------------------------- */
538 void LyXServer::notifyClient(string const & s)
540 string buf = string("NOTIFY:") + s + "\n";