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>
44 #include FORMS_H_LOCATION
47 #pragma implementation
50 #include "lyxserver.h"
53 #include "LyXAction.h"
55 #include "support/lstrings.h"
56 #include "support/lyxlib.h"
61 #define OS2EMX_PLAIN_CHAR
62 #define INCL_DOSNMPIPES
63 #define INCL_DOSERRORS
65 #include "os2_errortable.h"
70 // provide an empty mkfifo() if we do not have one. This disables the
73 int mkfifo(char const * __path, mode_t __mode) {
79 /* === variables ========================================================= */
81 extern LyXAction lyxaction;
88 void C_LyXComm_callback(int fd, void *v)
90 LyXComm::callback(fd, v);
99 void LyXComm::openConnection()
101 lyxerr[Debug::LYXSERVER] << "LyXComm: Opening connection" << endl;
103 // If we are up, that's an error
105 lyxerr << "LyXComm: Already connected" << endl;
108 // We assume that we don't make it
111 if (pipename.empty()) {
112 lyxerr[Debug::LYXSERVER]
113 << "LyXComm: server is disabled, nothing to do"
118 if ((infd = startPipe(inPipeName(), false)) == -1)
121 if ((outfd = startPipe(outPipeName(), true)) == -1) {
122 endPipe(infd, inPipeName());
126 if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
127 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
128 << '\n' << strerror(errno) << endl;
134 lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl;
139 void LyXComm::closeConnection()
141 lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl;
143 if (pipename.empty()) {
144 lyxerr[Debug::LYXSERVER]
145 << "LyXComm: server is disabled, nothing to do"
151 lyxerr << "LyXComm: Already disconnected" << endl;
155 endPipe(infd, inPipeName());
156 endPipe(outfd, outPipeName());
161 int LyXComm::startPipe(string const & filename, bool write)
169 // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
170 // The current emx implementation of access() won't work with pipes.
171 rc = DosCreateNPipe(filename.c_str(), &os2fd, NP_ACCESS_INBOUND,
172 NP_NOWAIT|0x01, 0600, 0600, 0);
173 if (rc == ERROR_PIPE_BUSY) {
174 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
175 << "If no other LyX program is active, please delete"
176 " the pipe by hand and try again." << endl;
181 if (rc != NO_ERROR) {
182 errnum = TranslateOS2Error(rc);
183 lyxerr <<"LyXComm: Could not create pipe " << filename
184 << strerror(errnum) << endl;
188 rc = DosConnectNPipe(os2fd);
189 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
190 errnum = TranslateOS2Error(rc);
191 lyxerr <<"LyXComm: Could not create pipe " << filename
192 << strerror(errnum) << endl;
195 // Imported handles can be used both with OS/2 APIs and emx
196 // library functions.
197 fd = _imphandle(os2fd);
199 if (::access(filename.c_str(), F_OK) == 0) {
200 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
201 << "If no other LyX program is active, please delete"
202 " the pipe by hand and try again." << endl;
207 if (::mkfifo(filename.c_str(), 0600) < 0) {
208 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
209 << strerror(errno) << endl;
212 fd = ::open(filename.c_str(), write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
216 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
217 << strerror(errno) << endl;
218 lyx::unlink(filename);
223 fl_add_io_callback(fd, FL_READ, C_LyXComm_callback, this);
229 void LyXComm::endPipe(int & fd, string const & filename)
238 rc = DosDisConnectNPipe(fd);
239 if (rc != NO_ERROR) {
240 errnum = TranslateOS2Error(rc);
241 lyxerr << "LyXComm: Could not disconnect pipe " << filename
242 << '\n' << strerror(errnum) << endl;
247 if (::close(fd) < 0) {
248 lyxerr << "LyXComm: Could not close pipe " << filename
249 << '\n' << strerror(errno) << endl;
252 // OS/2 pipes are deleted automatically
254 if (lyx::unlink(filename) < 0){
255 lyxerr << "LyXComm: Could not remove pipe " << filename
256 << '\n' << strerror(errno) << endl;
264 void LyXComm::emergencyCleanup()
266 if (!pipename.empty()) {
267 endPipe(infd, inPipeName());
268 endPipe(outfd, outPipeName());
273 // Receives messages and sends then to client
274 void LyXComm::callback(int fd, void *v)
276 LyXComm * c = static_cast<LyXComm*>(v);
278 if (lyxerr.debugging(Debug::LYXSERVER)) {
279 lyxerr << "LyXComm: Receiving from fd " << fd << endl;
282 const int CMDBUFLEN = 100;
283 char charbuf[CMDBUFLEN];
285 // nb! make lsbuf a class-member for multiple sessions
290 // the single = is intended here.
291 while((status = read(fd, charbuf, CMDBUFLEN-1)))
292 {// break and return in loop
293 if (status > 0) // got something
295 charbuf[status]= '\0'; // turn it into a c string
296 lsbuf += strip(charbuf, '\r');
297 // commit any commands read
298 while(lsbuf.find('\n') != string::npos) // while still
302 // split() grabs the entire string if
303 // the delim /wasn't/ found. ?:-P
304 lsbuf= split(lsbuf, cmd,'\n');
305 lyxerr[Debug::LYXSERVER]
306 << "LyXComm: status:" << status
307 << ", lsbuf:" << lsbuf
308 << ", cmd:" << cmd << endl;
310 c->clientcb(c->client, cmd);
315 { // EAGAIN is not really an error , it means we're
316 // only reading too fast for the writing process on
317 // the other end of the pipe.
319 return; // up to libforms select-loop (*crunch*)
323 lyxerr << "LyXComm: " << strerror(errno) << endl;
326 lyxerr << "LyxComm: truncated command: "
330 break; // reset connection
333 c->closeConnection();
339 void LyXComm::send(string const & msg)
342 lyxerr << "LyXComm: Request to send empty string. Ignoring."
347 if (lyxerr.debugging(Debug::LYXSERVER)) {
348 lyxerr << "LyXComm: Sending '" << msg << '\'' << endl;
351 if (pipename.empty()) return;
354 lyxerr << "LyXComm: Pipes are closed. Could not send "
356 } else if (::write(outfd, msg.c_str(), msg.length()) < 0) {
357 lyxerr << "LyXComm: Error sending message: " << msg
358 << '\n' << strerror(errno)
359 << "\nLyXComm: Resetting connection" << endl;
366 rc = DosResetBuffer(outfd); // To avoid synchronization problems.
367 if (rc != NO_ERROR) {
368 errnum = TranslateOS2Error(rc);
369 lyxerr << "LyXComm: Message could not be flushed: " << msg
370 << '\n' << strerror(errnum) << endl;
378 LyXServer::~LyXServer()
380 // say goodbye to clients so they stop sending messages
381 // modified june 1999 by stefano@zool.su.se to send as many bye
382 // messages as there are clients, each with client's name.
384 for (int i= 0; i<numclients; ++i) {
385 message = "LYXSRV:" + clients[i] + ":bye\n";
391 /* ---F+------------------------------------------------------------------ *\
392 Function : ServerCallback
394 Purpose : handle data gotten from communication
395 \* ---F------------------------------------------------------------------- */
397 void LyXServer::callback(LyXServer * serv, string const & msg)
399 lyxerr[Debug::LYXSERVER] << "LyXServer: Received: '"
400 << msg << '\'' << endl;
402 char const * p = msg.c_str();
404 // --- parse the string --------------------------------------------
406 // Format: LYXCMD:<client>:<func>:<argstring>\n
408 bool server_only = false;
410 // --- 1. check 'header' ---
412 if (compare(p, "LYXSRV:", 7) == 0) {
414 } else if (0 != compare(p, "LYXCMD:", 7)) {
415 lyxerr << "LyXServer: Unknown request \"" << p << "\"" << endl;
420 // --- 2. for the moment ignore the client name ---
422 while(*p && *p != ':')
423 client += char(*p++);
427 // --- 3. get function name ---
429 while(*p && *p != ':')
432 // --- 4. parse the argument ---
434 if (!server_only && *p == ':' && *(++p)) {
435 while(*p && *p != '\n')
440 lyxerr[Debug::LYXSERVER]
441 << "LyXServer: Client: '" << client
442 << "' Command: '" << cmd
443 << "' Argument: '" << arg << '\'' << endl;
445 // --- lookup and exec the command ------------------
449 // return the greeting to inform the client that
451 if (cmd == "hello") {
453 if (serv->numclients == MAX_CLIENTS){ //paranoid check
454 lyxerr[Debug::LYXSERVER]
455 << "LyXServer: too many clients..."
459 int i= 0; //find place in clients[]
460 while (!serv->clients[i].empty()
461 && i<serv->numclients)
463 serv->clients[i] = client;
465 buf = "LYXSRV:" + client + ":hello\n";
466 lyxerr[Debug::LYXSERVER]
467 << "LyXServer: Greeting "
469 serv->pipes.send(buf);
470 } else if (cmd == "bye") {
471 // If clients == 0 maybe we should reset the pipes
472 // to prevent fake callbacks
473 int i = 0; //look if client is registered
474 for (; i < serv->numclients; ++i) {
475 if (serv->clients[i] == client) break;
477 if (i < serv->numclients) {
479 serv->clients[i].erase();
480 lyxerr[Debug::LYXSERVER]
481 << "LyXServer: Client "
482 << client << " said goodbye"
485 lyxerr[Debug::LYXSERVER]
486 << "LyXServer: ignoring bye messge from unregistered client"
490 lyxerr <<"LyXServer: Undefined server command "
491 << cmd << "." << endl;
497 // which lyxfunc should we let it connect to?
498 // The correct solution would be to have a
499 // specialized (non-gui) BufferView. But how do
500 // we do it now? Probably we should just let it
501 // connect to the lyxfunc in the single LyXView we
502 // support currently. (Lgb)
504 int action = lyxaction.LookupFunc(cmd);
509 rval = serv->func->dispatch(action, arg);
511 rval = "Unknown command";
514 //modified june 1999 stefano@zool.su.se:
515 //all commands produce an INFO or ERROR message
516 //in the output pipe, even if they do not return
517 //anything. See chapter 4 of Customization doc.
518 if (action<0 || serv->func->errorStat())
522 buf += string(client) + ":" + cmd + ":" + rval + "\n";
523 serv->pipes.send(buf);
525 // !!! we don't do any error checking -
526 // if the client won't listen, the
527 // message is lost and others too
528 // maybe; so the client should empty
529 // the outpipe before issuing a request.
537 /* ---F+------------------------------------------------------------------ *\
538 Function : LyxNotifyClient
539 Called by : WorkAreaKeyPress
540 Purpose : send a notify messge to a client
541 Parameters: s - string to send
543 \* ---F------------------------------------------------------------------- */
545 void LyXServer::notifyClient(string const & s)
547 string buf = string("NOTIFY:") + s + "\n";