2 /* This file is part of
3 * ======================================================
5 * LyX, The Document Processor
7 * Copyright (C) 1995 Matthias Ettrich
8 * Copyright (C) 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"
61 #define OS2EMX_PLAIN_CHAR
62 #define INCL_DOSNMPIPES
63 #define INCL_DOSERRORS
65 #include "os2_errortable.h"
68 // provide an empty mkfifo() if we do not have one. This disables the
71 int mkfifo( char *__path, mode_t __mode ) {
77 // $Id: lyxserver.C,v 1.1 1999/09/27 18:44:38 larsbj Exp $
79 #if !defined(lint) && !defined(WITH_WARNINGS)
80 static char vcid[] = "$Id: lyxserver.C,v 1.1 1999/09/27 18:44:38 larsbj Exp $";
83 /* === variables ========================================================= */
85 extern LyXAction lyxaction;
90 void LyXComm::openConnection() {
91 lyxerr.debug("LyXComm: Opening connection", Error::LYXSERVER);
93 // If we are up, that's an error
95 lyxerr.print("LyXComm: Already connected");
98 // We assume that we don't make it
101 if (pipename.empty()) return;
103 // --- prepare input pipe ---------------------------------------
105 LString tmp = pipename + ".in";
111 // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
112 // The current emx implementation of access() won't work with pipes.
113 rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_INBOUND,
114 NP_NOWAIT|0x01, 0600, 0600, 0);
115 if (rc == ERROR_PIPE_BUSY) {
117 if (access(tmp.c_str(), F_OK) == 0) {
119 lyxerr.print("LyXComm: Pipe " + tmp + " already exists.");
120 lyxerr.print("If no other LyX program is active, please delete"
121 " the pipe by hand and try again.");
122 pipename = LString();
126 if (mkfifo(tmp.c_str(), 0600) < 0) {
127 lyxerr.print("LyXComm: Could not create pipe " + tmp);
128 lyxerr.print(strerror(errno));
131 infd = open(tmp.c_str(), O_RDONLY|O_NONBLOCK);
133 if (rc != NO_ERROR) {
134 errnum = TranslateOS2Error(rc);
135 lyxerr.print("LyXComm: Could not create pipe " + tmp);
136 lyxerr.print(strerror(errnum));
140 rc = DosConnectNPipe(fd);
141 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
142 errnum = TranslateOS2Error(rc);
143 lyxerr.print("LyXComm: Could not create pipe " + tmp);
144 lyxerr.print(strerror(errnum));
147 // Imported handles can be used both with OS/2 APIs and emx
148 // library functions.
149 infd = _imphandle(fd);
152 lyxerr.print("LyXComm: Could not open pipe " + tmp);
153 lyxerr.print(strerror(errno));
156 fl_add_io_callback(infd, FL_READ, callback, (void*)this);
158 // --- prepare output pipe ---------------------------------------
160 tmp = pipename + ".out";
163 if (access(tmp.c_str(), F_OK) == 0) {
165 rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_DUPLEX,
166 NP_NOWAIT|0x01, 0600, 0600, 0);
168 if (rc == ERROR_PIPE_BUSY) {
170 lyxerr.print("LyXComm: Pipe " + tmp + " already exists.");
171 lyxerr.print("If no other LyX program is active, please delete"
172 " the pipe by hand and try again.");
173 pipename = LString();
177 if (mkfifo(tmp.c_str(), 0600) < 0) {
178 lyxerr.print("LyXComm: Could not create pipe " + tmp);
179 lyxerr.print(strerror(errno));
182 if (access(tmp.c_str(), F_OK) != 0) {
183 lyxerr.print("LyXComm: Pipe " + tmp + " does not exist");
186 outfd = open(tmp.c_str(), O_RDWR);
188 if (rc != NO_ERROR) {
189 errnum = TranslateOS2Error(rc);
190 lyxerr.print("LyXComm: Could not create pipe " + tmp);
191 lyxerr.print(strerror(errnum));
194 rc = DosConnectNPipe(fd);
195 if (rc == ERROR_BAD_PIPE) {
196 lyxerr.print("LyXComm: Pipe " + tmp + " does not exist");
199 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
200 errnum = TranslateOS2Error(rc);
201 lyxerr.print("LyXComm: Could not create pipe " + tmp);
202 lyxerr.print(strerror(errnum));
205 outfd = _imphandle(fd);
208 lyxerr.print("LyXComm: Could not open pipe " + tmp);
209 lyxerr.print(strerror(errno));
212 if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
213 lyxerr.print("LyXComm: Could not set flags on pipe " + tmp);
214 lyxerr.print(strerror(errno));
219 lyxerr.debug("LyXComm: Connection established", Error::LYXSERVER);
223 void LyXComm::closeConnection() {
228 lyxerr.debug("LyXComm: Closing connection", Error::LYXSERVER);
230 if (pipename.empty()) {
235 lyxerr.print("LyXComm: Already disconnected");
240 fl_remove_io_callback(infd, FL_READ, callback);
242 LString tmp = pipename + ".in";
243 #ifdef __EMX__ // Notify the operating system.
244 rc = DosDisConnectNPipe(infd);
245 if (rc != NO_ERROR) {
246 errnum = TranslateOS2Error(rc);
247 lyxerr.print("LyXComm: Could not disconnect pipe " + tmp);
248 lyxerr.print(strerror(errnum));
252 if (close(infd) < 0) {
253 lyxerr.print("LyXComm: Could not close pipe " + tmp);
254 lyxerr.print(strerror(errno));
256 #ifndef __EMX__ // OS/2 named pipes will be automatically removed.
257 if (unlink(tmp.c_str()) < 0){
258 lyxerr.print("LyXComm: Could not remove pipe " + tmp);
259 lyxerr.print(strerror(errno));
264 LString tmp = pipename + ".out";
266 rc = DosDisConnectNPipe(outfd);
267 if (rc != NO_ERROR) {
268 errnum = TranslateOS2Error(rc);
269 lyxerr.print("LyXComm: Could not disconnect pipe " + tmp);
270 lyxerr.print(strerror(errnum));
274 if (close(outfd) < 0) {
275 lyxerr.print("LyXComm: Could not close pipe " + tmp);
276 lyxerr.print(strerror(errno));
279 if (unlink(tmp.c_str()) < 0){
280 lyxerr.print("LyXComm: Could not remove pipe " + tmp);
281 lyxerr.print(strerror(errno));
288 // Receives messages and sends then to client
289 void LyXComm::callback(int fd, void *v)
291 LyXComm * c = (LyXComm *) v;
293 if (lyxerr.debugging(Error::LYXSERVER)) {
294 lyxerr.print(LString("LyXComm: Receiving from fd ") + int(fd));
297 const int CMDBUFLEN = 100;
298 char charbuf[CMDBUFLEN];
300 // nb! make lsbuf a class-member for multiple sessions
301 static LString lsbuf;
305 // the single = is intended here.
306 while((status = read(fd,charbuf,CMDBUFLEN-1)))
307 {// break and return in loop
308 if(status > 0) // got something
310 charbuf[status]='\0'; // turn it into a c string
313 // commit any commands read
314 while(lsbuf.charPos('\n') >= 0) // while still
318 // split() grabs the entire string if
319 // the delim /wasn't/ found. ?:-P
320 lsbuf.split(cmd,'\n');
321 lyxerr.debug(LString("LyXComm: status:")
322 + status + ", lsbuf:" + lsbuf
326 c->clientcb(c->client, cmd);
331 { // EAGAIN is not really an error , it means we're
332 // only reading too fast for the writing process on
333 // the other end of the pipe.
335 return; // up to libforms select-loop (*crunch*)
339 lyxerr.print(LString("LyXComm: ") + strerror(errno));
342 lyxerr.print("LyxComm: truncated command: "
346 break; // reset connection
349 c->closeConnection();
354 void LyXComm::send(LString const & msg) {
356 lyxerr.print("LyXComm: Request to send empty string. Ignoring.");
360 if (lyxerr.debugging(Error::LYXSERVER)) {
361 lyxerr.print("LyXComm: Sending '" + msg + '\'');
364 if (pipename.empty()) return;
367 lyxerr.print("LyXComm: Pipes are closed. Could not send "+ msg);
368 } else if (write(outfd, msg.c_str(), msg.length()) < 0) {
369 lyxerr.print("LyXComm: Error sending message: " + msg);
370 lyxerr.print(strerror(errno));
371 lyxerr.print("LyXComm: Resetting connection");
378 rc = DosResetBuffer(outfd); // To avoid synchronization problems.
379 if (rc != NO_ERROR) {
380 errnum = TranslateOS2Error(rc);
381 lyxerr.print("LyXComm: Message could not be flushed: " +msg);
382 lyxerr.print(strerror(errnum));
390 LyXServer::~LyXServer()
392 // say goodbye to clients so they stop sending messages
393 // modified june 1999 by stefano@zool.su.se to send as many bye
394 // messages as there are clients, each with client's name.
396 for (int i=0; i<numclients; i++) {
397 message = "LYXSRV:" + clients[i] + ":bye\n";
403 /* ---F+------------------------------------------------------------------ *\
404 Function : ServerCallback
406 Purpose : handle data gotten from communication
407 \* ---F------------------------------------------------------------------- */
409 void LyXServer::callback(LyXServer * serv, LString const & msg)
411 lyxerr.debug("LyXServer: Received: '" + msg + '\'', Error::LYXSERVER);
413 char const *p = msg.c_str();
415 // --- parse the string --------------------------------------------
417 // Format: LYXCMD:<client>:<func>:<argstring>\n
419 bool server_only = false;
421 // --- 1. check 'header' ---
422 if (strncmp(p, "LYXSRV:", 7)==0) {
424 } else if(0!=strncmp(p, "LYXCMD:", 7)) {
425 lyxerr.print("LyXServer: Unknown request");
430 // --- 2. for the moment ignore the client name ---
432 while(*p && *p != ':')
433 client += char(*p++);
437 // --- 3. get function name ---
439 while(*p && *p != ':')
442 // --- 4. parse the argument ---
444 if(!server_only && *p == ':' && *(++p)) {
445 while(*p && *p != '\n')
450 lyxerr.debug("LyXServer: Client: '" + client + "' Command: '" + cmd + "' Argument: '" + arg + '\'', Error::LYXSERVER);
452 // --- lookup and exec the command ------------------
456 // return the greeting to inform the client that
458 if (cmd == "hello") {
460 if(serv->numclients==MAX_CLIENTS){ //paranoid check
461 lyxerr.debug("LyXServer: too many clients...", Error::LYXSERVER);
464 int i=0; //find place in clients[]
465 while (!serv->clients[i].empty()
466 && i<serv->numclients)
468 serv->clients[i] = client;
470 buf = "LYXSRV:" + client + ":hello\n";
471 lyxerr.debug("LyXServer: Greeting " + client, Error::LYXSERVER);
472 serv->pipes.send(buf);
473 } else if (cmd == "bye") {
474 // If clients==0 maybe we should reset the pipes
475 // to prevent fake callbacks
476 int i; //look if client is registered
477 for (i=0; i<serv->numclients; i++) {
478 if (serv->clients[i] == client) break;
480 if (i<serv->numclients) {
482 serv->clients[i].clean();
483 lyxerr.debug("LyXServer: Client " + client + " said goodbye",
486 lyxerr.debug("LyXServer: ignoring bye messge from unregistered client" +
487 client + "\n", Error::LYXSERVER);
490 lyxerr.print("LyXServer: Undefined server command " + cmd + ".");
496 // which lyxfunc should we let it connect to?
497 // The correct solution would be to have a
498 // specialized (non-gui) BufferView. But how do
499 // we do it now? Probably we should just let it
500 // connect to the lyxfunc in the single LyXView we
501 // support currently. (Lgb)
503 int action = lyxaction.LookupFunc(cmd.c_str());
508 rval = serv->func->Dispatch(action, arg.c_str());
510 rval = "Unknown command";
513 //modified june 1999 stefano@zool.su.se:
514 //all commands produce an INFO or ERROR message
515 //in the output pipe, even if they do not return
516 //anything. See chapter 4 of Customization doc.
517 if (action<0 || serv->func->errorStat())
521 buf += LString(client) + ":" + cmd + ":" + rval + "\n";
522 serv->pipes.send(buf);
524 // !!! we don't do any error checking -
525 // if the client won't listen, the
526 // message is lost and others too
527 // maybe; so the client should empty
528 // the outpipe before issuing a request.
536 /* ---F+------------------------------------------------------------------ *\
537 Function : LyxNotifyClient
538 Called by : WorkAreaKeyPress
539 Purpose : send a notify messge to a client
540 Parameters: s - string to send
542 \* ---F------------------------------------------------------------------- */
544 void LyXServer::notifyClient(LString const & s)
546 LString buf = LString("NOTIFY:") + s + "\n";