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"
54 #include "support/lstrings.h"
55 #include "support/lyxlib.h"
60 #define OS2EMX_PLAIN_CHAR
61 #define INCL_DOSNMPIPES
62 #define INCL_DOSERRORS
64 #include "support/os2_errortable.h"
69 // provide an empty mkfifo() if we do not have one. This disables the
72 int mkfifo(char const * __path, mode_t __mode) {
78 /* === variables ========================================================= */
84 void C_LyXComm_callback(int fd, void *v)
86 LyXComm::callback(fd, v);
95 void LyXComm::openConnection()
97 lyxerr[Debug::LYXSERVER] << "LyXComm: Opening connection" << endl;
99 // If we are up, that's an error
101 lyxerr << "LyXComm: Already connected" << endl;
104 // We assume that we don't make it
107 if (pipename.empty()) {
108 lyxerr[Debug::LYXSERVER]
109 << "LyXComm: server is disabled, nothing to do"
114 if ((infd = startPipe(inPipeName(), false)) == -1)
117 if ((outfd = startPipe(outPipeName(), true)) == -1) {
118 endPipe(infd, inPipeName());
122 if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
123 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
124 << '\n' << strerror(errno) << endl;
130 lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl;
135 void LyXComm::closeConnection()
137 lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl;
139 if (pipename.empty()) {
140 lyxerr[Debug::LYXSERVER]
141 << "LyXComm: server is disabled, nothing to do"
147 lyxerr << "LyXComm: Already disconnected" << endl;
151 endPipe(infd, inPipeName());
152 endPipe(outfd, outPipeName());
157 int LyXComm::startPipe(string const & filename, bool write)
165 // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
166 // The current emx implementation of access() won't work with pipes.
167 rc = DosCreateNPipe(filename.c_str(), &os2fd, NP_ACCESS_INBOUND,
168 NP_NOWAIT|0x01, 0600, 0600, 0);
169 if (rc == ERROR_PIPE_BUSY) {
170 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
171 << "If no other LyX program is active, please delete"
172 " the pipe by hand and try again." << endl;
177 if (rc != NO_ERROR) {
178 errnum = TranslateOS2Error(rc);
179 lyxerr <<"LyXComm: Could not create pipe " << filename
180 << strerror(errnum) << endl;
184 rc = DosConnectNPipe(os2fd);
185 if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
186 errnum = TranslateOS2Error(rc);
187 lyxerr <<"LyXComm: Could not create pipe " << filename
188 << strerror(errnum) << endl;
191 // Imported handles can be used both with OS/2 APIs and emx
192 // library functions.
193 fd = _imphandle(os2fd);
195 if (::access(filename.c_str(), F_OK) == 0) {
196 lyxerr << "LyXComm: Pipe " << filename << " already exists.\n"
197 << "If no other LyX program is active, please delete"
198 " the pipe by hand and try again." << endl;
203 if (::mkfifo(filename.c_str(), 0600) < 0) {
204 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
205 << strerror(errno) << endl;
208 fd = ::open(filename.c_str(), write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
212 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
213 << strerror(errno) << endl;
214 lyx::unlink(filename);
219 fl_add_io_callback(fd, FL_READ, C_LyXComm_callback, this);
225 void LyXComm::endPipe(int & fd, string const & filename)
234 rc = DosDisConnectNPipe(fd);
235 if (rc != NO_ERROR) {
236 errnum = TranslateOS2Error(rc);
237 lyxerr << "LyXComm: Could not disconnect pipe " << filename
238 << '\n' << strerror(errnum) << endl;
243 if (::close(fd) < 0) {
244 lyxerr << "LyXComm: Could not close pipe " << filename
245 << '\n' << strerror(errno) << endl;
248 // OS/2 pipes are deleted automatically
250 if (lyx::unlink(filename) < 0) {
251 lyxerr << "LyXComm: Could not remove pipe " << filename
252 << '\n' << strerror(errno) << endl;
260 void LyXComm::emergencyCleanup()
262 if (!pipename.empty()) {
263 endPipe(infd, inPipeName());
264 endPipe(outfd, outPipeName());
269 // Receives messages and sends then to client
270 void LyXComm::callback(int fd, void *v)
272 LyXComm * c = static_cast<LyXComm*>(v);
274 if (lyxerr.debugging(Debug::LYXSERVER)) {
275 lyxerr << "LyXComm: Receiving from fd " << fd << endl;
278 const int CMDBUFLEN = 100;
279 char charbuf[CMDBUFLEN];
281 // nb! make lsbuf a class-member for multiple sessions
286 // the single = is intended here.
287 while ((status = read(fd, charbuf, CMDBUFLEN-1)))
288 {// break and return in loop
289 if (status > 0) // got something
291 charbuf[status]= '\0'; // turn it into a c string
292 lsbuf += strip(charbuf, '\r');
293 // commit any commands read
294 while (lsbuf.find('\n') != string::npos) // while still
298 // split() grabs the entire string if
299 // the delim /wasn't/ found. ?:-P
300 lsbuf= split(lsbuf, cmd,'\n');
301 lyxerr[Debug::LYXSERVER]
302 << "LyXComm: status:" << status
303 << ", lsbuf:" << lsbuf
304 << ", cmd:" << cmd << endl;
306 c->clientcb(c->client, cmd);
311 { // EAGAIN is not really an error , it means we're
312 // only reading too fast for the writing process on
313 // the other end of the pipe.
315 return; // up to libforms select-loop (*crunch*)
319 lyxerr << "LyXComm: " << strerror(errno) << endl;
322 lyxerr << "LyxComm: truncated command: "
326 break; // reset connection
329 c->closeConnection();
335 void LyXComm::send(string const & msg)
338 lyxerr << "LyXComm: Request to send empty string. Ignoring."
343 if (lyxerr.debugging(Debug::LYXSERVER)) {
344 lyxerr << "LyXComm: Sending '" << msg << '\'' << endl;
347 if (pipename.empty()) return;
350 lyxerr << "LyXComm: Pipes are closed. Could not send "
352 } else if (::write(outfd, msg.c_str(), msg.length()) < 0) {
353 lyxerr << "LyXComm: Error sending message: " << msg
354 << '\n' << strerror(errno)
355 << "\nLyXComm: Resetting connection" << endl;
362 rc = DosResetBuffer(outfd); // To avoid synchronization problems.
363 if (rc != NO_ERROR) {
364 errnum = TranslateOS2Error(rc);
365 lyxerr << "LyXComm: Message could not be flushed: " << msg
366 << '\n' << strerror(errnum) << endl;
374 LyXServer::~LyXServer()
376 // say goodbye to clients so they stop sending messages
377 // modified june 1999 by stefano@zool.su.se to send as many bye
378 // messages as there are clients, each with client's name.
380 for (int i= 0; i<numclients; ++i) {
381 message = "LYXSRV:" + clients[i] + ":bye\n";
387 /* ---F+------------------------------------------------------------------ *\
388 Function : ServerCallback
390 Purpose : handle data gotten from communication
391 \* ---F------------------------------------------------------------------- */
393 void LyXServer::callback(LyXServer * serv, string const & msg)
395 lyxerr[Debug::LYXSERVER] << "LyXServer: Received: '"
396 << msg << '\'' << endl;
398 char const * p = msg.c_str();
400 // --- parse the string --------------------------------------------
402 // Format: LYXCMD:<client>:<func>:<argstring>\n
404 bool server_only = false;
406 // --- 1. check 'header' ---
408 if (compare(p, "LYXSRV:", 7) == 0) {
410 } else if (0 != compare(p, "LYXCMD:", 7)) {
411 lyxerr << "LyXServer: Unknown request \"" << p << "\"" << endl;
416 // --- 2. for the moment ignore the client name ---
418 while (*p && *p != ':')
419 client += char(*p++);
423 // --- 3. get function name ---
425 while (*p && *p != ':')
428 // --- 4. parse the argument ---
430 if (!server_only && *p == ':' && *(++p)) {
431 while (*p && *p != '\n')
436 lyxerr[Debug::LYXSERVER]
437 << "LyXServer: Client: '" << client
438 << "' Command: '" << cmd
439 << "' Argument: '" << arg << '\'' << endl;
441 // --- lookup and exec the command ------------------
445 // return the greeting to inform the client that
447 if (cmd == "hello") {
449 if (serv->numclients == MAX_CLIENTS) { //paranoid check
450 lyxerr[Debug::LYXSERVER]
451 << "LyXServer: too many clients..."
455 int i= 0; //find place in clients[]
456 while (!serv->clients[i].empty()
457 && i<serv->numclients)
459 serv->clients[i] = client;
461 buf = "LYXSRV:" + client + ":hello\n";
462 lyxerr[Debug::LYXSERVER]
463 << "LyXServer: Greeting "
465 serv->pipes.send(buf);
466 } else if (cmd == "bye") {
467 // If clients == 0 maybe we should reset the pipes
468 // to prevent fake callbacks
469 int i = 0; //look if client is registered
470 for (; i < serv->numclients; ++i) {
471 if (serv->clients[i] == client) break;
473 if (i < serv->numclients) {
475 serv->clients[i].erase();
476 lyxerr[Debug::LYXSERVER]
477 << "LyXServer: Client "
478 << client << " said goodbye"
481 lyxerr[Debug::LYXSERVER]
482 << "LyXServer: ignoring bye messge from unregistered client"
486 lyxerr <<"LyXServer: Undefined server command "
487 << cmd << "." << endl;
493 // which lyxfunc should we let it connect to?
494 // The correct solution would be to have a
495 // specialized (non-gui) BufferView. But how do
496 // we do it now? Probably we should just let it
497 // connect to the lyxfunc in the single LyXView we
498 // support currently. (Lgb)
501 serv->func->verboseDispatch(cmd + ' ' + arg, false);
502 string const rval = serv->func->getMessage();
504 //modified june 1999 stefano@zool.su.se:
505 //all commands produce an INFO or ERROR message
506 //in the output pipe, even if they do not return
507 //anything. See chapter 4 of Customization doc.
509 if (serv->func->errorStat())
513 buf += client + ":" + cmd + ":" + rval + "\n";
514 serv->pipes.send(buf);
516 // !!! we don't do any error checking -
517 // if the client won't listen, the
518 // message is lost and others too
519 // maybe; so the client should empty
520 // the outpipe before issuing a request.
528 /* ---F+------------------------------------------------------------------ *\
529 Function : LyxNotifyClient
530 Called by : WorkAreaKeyPress
531 Purpose : send a notify messge to a client
532 Parameters: s - string to send
534 \* ---F------------------------------------------------------------------- */
536 void LyXServer::notifyClient(string const & s)
538 string buf = string("NOTIFY:") + s + "\n";