X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Flyxserver.C;h=a3d781edadf3555f0ab80803d6a2a789531bbe65;hb=dab43e77955e5b21fa556f53143d42e09e439f40;hp=76a7d19975fad00cf8799e8c3b40c494c1004946;hpb=d6665cba427b04ae37f42c846398cad518d2be0f;p=lyx.git diff --git a/src/lyxserver.C b/src/lyxserver.C index 76a7d19975..a3d781edad 100644 --- a/src/lyxserver.C +++ b/src/lyxserver.C @@ -1,43 +1,41 @@ -// -*- C++ -*- /* This file is part of - * ====================================================== - * + * ====================================================== + * * LyX, The Document Processor - * + * * Copyright 1995 Matthias Ettrich - * Copyright 1995-2000 The LyX Team. + * Copyright 1995-2001 The LyX Team. * * ====================================================== */ /** Docu : To use the lyxserver define the name of the pipe in your - lyxrc: - \serverpipe "/home/myhome/.lyxpipe" - Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX. - Each message consists of a single line in ASCII. Input lines - (client -> LyX) have the following format: - "LYXCMD:::" - Answers from LyX look like this: - "INFO:::" + lyxrc: + \serverpipe "/home/myhome/.lyxpipe" + Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX. + Each message consists of a single line in ASCII. Input lines + (client -> LyX) have the following format: + "LYXCMD:::" + Answers from LyX look like this: + "INFO:::" [asierra970531] Or like this in case of error: - "ERROR:::" - where and are just echoed. - If LyX notifies about a user defined extension key-sequence, - the line looks like this: - "NOTIFY:" + "ERROR:::" + where and are just echoed. + If LyX notifies about a user defined extension key-sequence, + the line looks like this: + "NOTIFY:" [asierra970531] New server-only messages to implement a simple protocol - "LYXSRV::" - where can be "hello" or "bye". If hello is - received LyX will inform the client that it's listening its - messages, and 'bye' will inform that lyx is closing. + "LYXSRV::" + where can be "hello" or "bye". If hello is + received LyX will inform the client that it's listening its + messages, and 'bye' will inform that lyx is closing. - See development/server_monitor.c for an example client. + See development/server_monitor.c for an example client. Purpose: implement a client/server lib for LyX */ #include -#include #include #include #include @@ -50,10 +48,10 @@ #endif #include "lyxserver.h" -#include "lyxfunc.h" #include "lyx_main.h" #include "debug.h" #include "LyXAction.h" +#include "lyxfunc.h" #include "support/lstrings.h" #include "support/lyxlib.h" @@ -64,15 +62,15 @@ #define INCL_DOSNMPIPES #define INCL_DOSERRORS #include -#include "os2_errortable.h" +#include "support/os2_errortable.h" #endif using std::endl; // provide an empty mkfifo() if we do not have one. This disables the -// lyxserver. +// lyxserver. #ifndef HAVE_MKFIFO -int mkfifo( char *__path, mode_t __mode ) { +int mkfifo(char const * __path, mode_t __mode) { return 0; } #endif @@ -82,16 +80,26 @@ int mkfifo( char *__path, mode_t __mode ) { extern LyXAction lyxaction; -// C wrapper -extern "C" void C_LyXComm_callback(int fd, void *v); + +extern "C" { + + // C wrapper + static + void C_LyXComm_callback(int fd, void *v) + { + LyXComm::callback(fd, v); + } + +} // LyXComm class - - // Open pipes -void LyXComm::openConnection() { + +// Open pipes +void LyXComm::openConnection() +{ lyxerr[Debug::LYXSERVER] << "LyXComm: Opening connection" << endl; - + // If we are up, that's an error if (ready) { lyxerr << "LyXComm: Already connected" << endl; @@ -99,250 +107,223 @@ void LyXComm::openConnection() { } // We assume that we don't make it ready = false; - - if (pipename.empty()) return; - // --- prepare input pipe --------------------------------------- - - string tmp = pipename + ".in"; - + if (pipename.empty()) { + lyxerr[Debug::LYXSERVER] + << "LyXComm: server is disabled, nothing to do" + << endl; + return; + } + + if ((infd = startPipe(inPipeName(), false)) == -1) + return; + + if ((outfd = startPipe(outPipeName(), true)) == -1) { + endPipe(infd, inPipeName()); + return; + } + + if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) { + lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName() + << '\n' << strerror(errno) << endl; + return; + } + + // We made it! + ready = true; + lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl; +} + + +/// Close pipes +void LyXComm::closeConnection() +{ + lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl; + + if (pipename.empty()) { + lyxerr[Debug::LYXSERVER] + << "LyXComm: server is disabled, nothing to do" + << endl; + return; + } + + if (!ready) { + lyxerr << "LyXComm: Already disconnected" << endl; + return; + } + + endPipe(infd, inPipeName()); + endPipe(outfd, outPipeName()); + + ready = false; +} + +int LyXComm::startPipe(string const & filename, bool write) +{ + int fd; + #ifdef __EMX__ - HPIPE fd; + HPIPE os2fd; APIRET rc; int errnum; // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK. // The current emx implementation of access() won't work with pipes. - rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_INBOUND, + rc = DosCreateNPipe(filename.c_str(), &os2fd, NP_ACCESS_INBOUND, NP_NOWAIT|0x01, 0600, 0600, 0); if (rc == ERROR_PIPE_BUSY) { -#else - if (::access(tmp.c_str(), F_OK) == 0) { -#endif - lyxerr << "LyXComm: Pipe " << tmp << " already exists.\n" + lyxerr << "LyXComm: Pipe " << filename << " already exists.\n" << "If no other LyX program is active, please delete" " the pipe by hand and try again." << endl; pipename.erase(); - return; + return -1; } -#ifndef __EMX__ - if (::mkfifo(tmp.c_str(), 0600) < 0) { - lyxerr << "LyXComm: Could not create pipe " << tmp << '\n' - << strerror(errno) << endl; - return; - }; - infd = ::open(tmp.c_str(), O_RDONLY|O_NONBLOCK); -#else + if (rc != NO_ERROR) { errnum = TranslateOS2Error(rc); - lyxerr <<"LyXComm: Could not create pipe " << tmp + lyxerr <<"LyXComm: Could not create pipe " << filename << strerror(errnum) << endl; - return; + return -1; }; // Listen to it. - rc = DosConnectNPipe(fd); + rc = DosConnectNPipe(os2fd); if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) { errnum = TranslateOS2Error(rc); - lyxerr <<"LyXComm: Could not create pipe " << tmp + lyxerr <<"LyXComm: Could not create pipe " << filename << strerror(errnum) << endl; - return; + return -1; }; // Imported handles can be used both with OS/2 APIs and emx - // library functions. - infd = _imphandle(fd); -#endif - if (infd < 0) { - lyxerr << "LyXComm: Could not open pipe " << tmp << '\n' - << strerror(errno) << endl; - return; - } - fl_add_io_callback(infd, FL_READ, C_LyXComm_callback, this); - - // --- prepare output pipe --------------------------------------- - - tmp = pipename + ".out"; - -#ifndef __EMX__ - if (::access(tmp.c_str(), F_OK) == 0) { + // library functions. + fd = _imphandle(os2fd); #else - rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_DUPLEX, - NP_NOWAIT|0x01, 0600, 0600, 0); - - if (rc == ERROR_PIPE_BUSY) { -#endif - lyxerr << "LyXComm: Pipe " << tmp << " already exists.\n" + if (::access(filename.c_str(), F_OK) == 0) { + lyxerr << "LyXComm: Pipe " << filename << " already exists.\n" << "If no other LyX program is active, please delete" " the pipe by hand and try again." << endl; pipename.erase(); - return; + return -1; } -#ifndef __EMX__ - if (::mkfifo(tmp.c_str(), 0600) < 0) { - lyxerr << "LyXComm: Could not create pipe " << tmp << '\n' + + if (::mkfifo(filename.c_str(), 0600) < 0) { + lyxerr << "LyXComm: Could not create pipe " << filename << '\n' << strerror(errno) << endl; - return; + return -1; }; - if (::access(tmp.c_str(), F_OK) != 0) { - lyxerr << "LyXComm: Pipe " << tmp - << " does not exist" << endl; - return; - } - outfd = ::open(tmp.c_str(), O_RDWR); -#else - if (rc != NO_ERROR) { - errnum = TranslateOS2Error(rc); - lyxerr << "LyXComm: Could not create pipe " << tmp << '\n' - << strerror(errnum) << endl; - return; - } - rc = DosConnectNPipe(fd); - if (rc == ERROR_BAD_PIPE) { - lyxerr << "LyXComm: Pipe " << tmp - << " does not exist" << endl; - return; - } - if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) { - errnum = TranslateOS2Error(rc); - lyxerr << "LyXComm: Could not create pipe " << tmp << '\n' - << strerror(errnum) << endl; - return; - } - outfd = _imphandle(fd); + fd = ::open(filename.c_str(), write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK)); #endif - if (outfd < 0) { - lyxerr << "LyXComm: Could not open pipe " << tmp << '\n' + + if (fd < 0) { + lyxerr << "LyXComm: Could not open pipe " << filename << '\n' << strerror(errno) << endl; - return; + lyx::unlink(filename); + return -1; } - if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) { - lyxerr << "LyXComm: Could not set flags on pipe " << tmp - << '\n' << strerror(errno) << endl; - return; - }; - // We made it! - ready = true; - lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl; + + if (!write) + fl_add_io_callback(fd, FL_READ, C_LyXComm_callback, this); + + return fd; } - -/// Close pipes -void LyXComm::closeConnection() { + + +void LyXComm::endPipe(int & fd, string const & filename) +{ + if (fd < 0) + return; + #ifdef __EMX__ APIRET rc; int errnum; -#endif - lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl; - if (pipename.empty()) { - return; - } - - if (!ready) { - lyxerr << "LyXComm: Already disconnected" << endl; + rc = DosDisConnectNPipe(fd); + if (rc != NO_ERROR) { + errnum = TranslateOS2Error(rc); + lyxerr << "LyXComm: Could not disconnect pipe " << filename + << '\n' << strerror(errnum) << endl; return; } - - if(infd > -1) { - fl_remove_io_callback(infd, FL_READ, C_LyXComm_callback); - - string tmp = pipename + ".in"; -#ifdef __EMX__ // Notify the operating system. - rc = DosDisConnectNPipe(infd); - if (rc != NO_ERROR) { - errnum = TranslateOS2Error(rc); - lyxerr << "LyXComm: Could not disconnect pipe " << tmp - << '\n' << strerror(errnum) << endl; - return; - } -#endif - if (close(infd) < 0) { - lyxerr << "LyXComm: Could not close pipe " << tmp - << '\n' << strerror(errno) << endl; - } -#ifndef __EMX__ // OS/2 named pipes will be automatically removed. - if (lyx::unlink(tmp) < 0){ - lyxerr << "LyXComm: Could not remove pipe " << tmp - << '\n' << strerror(errno) << endl; - }; #endif + + if (::close(fd) < 0) { + lyxerr << "LyXComm: Could not close pipe " << filename + << '\n' << strerror(errno) << endl; } - if(outfd > -1) { - string tmp = pipename + ".out"; -#ifdef __EMX__ - rc = DosDisConnectNPipe(outfd); - if (rc != NO_ERROR) { - errnum = TranslateOS2Error(rc); - lyxerr << "LyXComm: Could not disconnect pipe " << tmp - << '\n' << strerror(errnum) << endl; - return; - } -#endif - if (::close(outfd) < 0) { - lyxerr << "LyXComm: Could not close pipe " << tmp - << '\n' << strerror(errno) << endl; - } + +// OS/2 pipes are deleted automatically #ifndef __EMX__ - if (lyx::unlink(tmp) < 0){ - lyxerr << "LyXComm: Could not remove pipe " << tmp - << '\n' << strerror(errno) << endl; - }; + if (lyx::unlink(filename) < 0) { + lyxerr << "LyXComm: Could not remove pipe " << filename + << '\n' << strerror(errno) << endl; + }; #endif + + fd = -1; +} + + +void LyXComm::emergencyCleanup() +{ + if (!pipename.empty()) { + endPipe(infd, inPipeName()); + endPipe(outfd, outPipeName()); } - ready = false; } - + + // Receives messages and sends then to client void LyXComm::callback(int fd, void *v) { LyXComm * c = static_cast(v); - + if (lyxerr.debugging(Debug::LYXSERVER)) { lyxerr << "LyXComm: Receiving from fd " << fd << endl; } - - const int CMDBUFLEN = 100; + + const int CMDBUFLEN = 100; char charbuf[CMDBUFLEN]; - string cmd; + string cmd; // nb! make lsbuf a class-member for multiple sessions static string lsbuf; errno = 0; int status; // the single = is intended here. - while((status = read(fd, charbuf, CMDBUFLEN-1))) + while ((status = read(fd, charbuf, CMDBUFLEN-1))) {// break and return in loop - if(status > 0) // got something + if (status > 0) // got something { charbuf[status]= '\0'; // turn it into a c string lsbuf += strip(charbuf, '\r'); // commit any commands read - while(lsbuf.find('\n') != string::npos) // while still + while (lsbuf.find('\n') != string::npos) // while still // commands - // left + // left { // split() grabs the entire string if - // the delim /wasn't/ found. ?:-P + // the delim /wasn't/ found. ?:-P lsbuf= split(lsbuf, cmd,'\n'); lyxerr[Debug::LYXSERVER] << "LyXComm: status:" << status - << ", lsbuf:" << lsbuf + << ", lsbuf:" << lsbuf << ", cmd:" << cmd << endl; - if(!cmd.empty()) - c->clientcb(c->client, cmd); - //\n or not \n? + if (!cmd.empty()) + c->clientcb(c->client, cmd); + //\n or not \n? } } - if(errno == EAGAIN) + if (errno == EAGAIN) { // EAGAIN is not really an error , it means we're // only reading too fast for the writing process on - // the other end of the pipe. + // the other end of the pipe. errno = 0; return; // up to libforms select-loop (*crunch*) } - if(errno != 0 ) + if (errno != 0) { lyxerr << "LyXComm: " << strerror(errno) << endl; - if(!lsbuf.empty()) + if (!lsbuf.empty()) { - lyxerr << "LyxComm: truncated command: " + lyxerr << "LyxComm: truncated command: " << lsbuf << endl; lsbuf.erase(); } @@ -354,13 +335,9 @@ void LyXComm::callback(int fd, void *v) errno= 0; } -extern "C" void C_LyXComm_callback(int fd, void *v) -{ - LyXComm::callback(fd, v); -} - -void LyXComm::send(string const & msg) { +void LyXComm::send(string const & msg) +{ if (msg.empty()) { lyxerr << "LyXComm: Request to send empty string. Ignoring." << endl; @@ -421,67 +398,67 @@ void LyXServer::callback(LyXServer * serv, string const & msg) { lyxerr[Debug::LYXSERVER] << "LyXServer: Received: '" << msg << '\'' << endl; - + char const * p = msg.c_str(); - + // --- parse the string -------------------------------------------- // // Format: LYXCMD:::\n // bool server_only = false; - while(*p) { + while (*p) { // --- 1. check 'header' --- - if (strncmp(p, "LYXSRV:", 7) == 0) { - server_only = true; - } else if(0!= strncmp(p, "LYXCMD:", 7)) { - lyxerr << "LyXServer: Unknown request" << endl; + if (compare(p, "LYXSRV:", 7) == 0) { + server_only = true; + } else if (0 != compare(p, "LYXCMD:", 7)) { + lyxerr << "LyXServer: Unknown request \"" << p << "\"" << endl; return; } p += 7; - + // --- 2. for the moment ignore the client name --- string client; - while(*p && *p != ':') + while (*p && *p != ':') client += char(*p++); - if(*p == ':') ++p; - if(!*p) return; - + if (*p == ':') ++p; + if (!*p) return; + // --- 3. get function name --- string cmd; - while(*p && *p != ':') + while (*p && *p != ':') cmd += char(*p++); - + // --- 4. parse the argument --- string arg; - if(!server_only && *p == ':' && *(++p)) { - while(*p && *p != '\n') + if (!server_only && *p == ':' && *(++p)) { + while (*p && *p != '\n') arg += char(*p++); - if(*p) ++p; + if (*p) ++p; } - + lyxerr[Debug::LYXSERVER] << "LyXServer: Client: '" << client << "' Command: '" << cmd << "' Argument: '" << arg << '\'' << endl; - + // --- lookup and exec the command ------------------ - + if (server_only) { string buf; - // return the greeting to inform the client that + // return the greeting to inform the client that // we are listening. if (cmd == "hello") { // One more client - if(serv->numclients == MAX_CLIENTS){ //paranoid check + if (serv->numclients == MAX_CLIENTS) { //paranoid check lyxerr[Debug::LYXSERVER] << "LyXServer: too many clients..." << endl; return; } int i= 0; //find place in clients[] - while (!serv->clients[i].empty() - && inumclients) + while (!serv->clients[i].empty() + && inumclients) ++i; serv->clients[i] = client; serv->numclients++; @@ -515,7 +492,7 @@ void LyXServer::callback(LyXServer * serv, string const & msg) } return; } - + if (!cmd.empty()) { // which lyxfunc should we let it connect to? // The correct solution would be to have a @@ -524,12 +501,12 @@ void LyXServer::callback(LyXServer * serv, string const & msg) // connect to the lyxfunc in the single LyXView we // support currently. (Lgb) - int action = lyxaction.LookupFunc(cmd); + kb_action action = static_cast(lyxaction.LookupFunc(cmd)); //int action = -1; string rval, buf; - + if (action>= 0) { - rval = serv->func->Dispatch(action, arg); + rval = serv->func->dispatch(action, arg); } else { rval = "Unknown command"; } @@ -542,7 +519,7 @@ void LyXServer::callback(LyXServer * serv, string const & msg) buf = "ERROR:"; else buf = "INFO:"; - buf += string(client) + ":" + cmd + ":" + rval + "\n"; + buf += string(client) + ":" + cmd + ":" + rval + "\n"; serv->pipes.send(buf); // !!! we don't do any error checking - @@ -550,7 +527,7 @@ void LyXServer::callback(LyXServer * serv, string const & msg) // message is lost and others too // maybe; so the client should empty // the outpipe before issuing a request. - + // not found } } /* while *p */ @@ -570,4 +547,3 @@ void LyXServer::notifyClient(string const & s) string buf = string("NOTIFY:") + s + "\n"; pipes.send(buf); } -