X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FServer.cpp;h=d7a2e2514c285a26a533bfa67b39ac6bf79c6b5e;hb=70a24259f8e3eb75677178ef5e28ecbb51c2935b;hp=f6b3821dbd35ca967e63bbddd1e61042a5b51ecf;hpb=d079c908b371284bfb32f1c3f60781f34a99b3e5;p=lyx.git diff --git a/src/Server.cpp b/src/Server.cpp index f6b3821dbd..d7a2e2514c 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -3,7 +3,7 @@ * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * - * \author Lars Gullik Bjønnes + * \author Lars Gullik Bjønnes * \author Jean-Marc Lasgouttes * \author Angus Leeming * \author John Levon @@ -40,15 +40,15 @@ #include #include "Server.h" -#include "debug.h" #include "FuncRequest.h" #include "LyXAction.h" #include "LyXFunc.h" + #include "frontends/Application.h" +#include "support/debug.h" #include "support/FileName.h" #include "support/lstrings.h" -#include "support/lyxlib.h" #include @@ -58,22 +58,23 @@ #endif #include +using namespace std; +using namespace lyx::support; namespace lyx { -using support::compare; -using support::FileName; -using support::rtrim; -using support::split; -using support::unlink; - -using std::endl; -using std::string; - +///////////////////////////////////////////////////////////////////// +// +// LyXComm +// +///////////////////////////////////////////////////////////////////// #if !defined (HAVE_MKFIFO) // We provide a stub class that disables the lyxserver. +LyXComm::LyXComm(string const &, Server *, ClientCallbackfct) +{} + void LyXComm::openConnection() {} @@ -106,92 +107,124 @@ void LyXComm::send(string const & msg) #else // defined (HAVE_MKFIFO) +LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb) + : pipename_(pip), client_(cli), clientcb_(ccb) +{ + ready_ = false; + openConnection(); +} + + void LyXComm::openConnection() { - LYXERR(Debug::LYXSERVER) << "LyXComm: Opening connection" << endl; + LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection"); // If we are up, that's an error - if (ready) { + if (ready_) { lyxerr << "LyXComm: Already connected" << endl; return; } // We assume that we don't make it - ready = false; + ready_ = false; - if (pipename.empty()) { - LYXERR(Debug::LYXSERVER) - << "LyXComm: server is disabled, nothing to do" - << endl; + if (pipename_.empty()) { + LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do"); return; } - if ((infd = startPipe(inPipeName(), false)) == -1) + infd_ = startPipe(inPipeName(), false); + if (infd_ == -1) return; - if ((outfd = startPipe(outPipeName(), true)) == -1) { - endPipe(infd, inPipeName(), false); + outfd_ = startPipe(outPipeName(), true); + if (outfd_ == -1) { + endPipe(infd_, inPipeName(), false); return; } - if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) { + 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; + ready_ = true; + LYXERR(Debug::LYXSERVER, "LyXComm: Connection established"); } /// Close pipes void LyXComm::closeConnection() { - LYXERR(Debug::LYXSERVER) << "LyXComm: Closing connection" << endl; + LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection"); - if (pipename.empty()) { - LYXERR(Debug::LYXSERVER) - << "LyXComm: server is disabled, nothing to do" - << endl; + if (pipename_.empty()) { + LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do"); return; } - if (!ready) { - lyxerr << "LyXComm: Already disconnected" << endl; + if (!ready_) { + LYXERR0("LyXComm: Already disconnected"); return; } - endPipe(infd, inPipeName(), false); - endPipe(outfd, outPipeName(), true); + endPipe(infd_, inPipeName(), false); + endPipe(outfd_, outPipeName(), true); - ready = false; + ready_ = false; } int LyXComm::startPipe(string const & file, bool write) { + static bool stalepipe = false; FileName const filename(file); - if (::access(filename.toFilesystemEncoding().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 -1; + if (filename.exists()) { + if (!write) { + // Let's see whether we have a stale pipe. + int fd = ::open(filename.toFilesystemEncoding().c_str(), + O_WRONLY | O_NONBLOCK); + if (fd >= 0) { + // Another LyX instance is using it. + ::close(fd); + } else if (errno == ENXIO) { + // No process is reading from the other end. + stalepipe = true; + LYXERR(Debug::LYXSERVER, + "LyXComm: trying to remove " + << filename); + filename.removeFile(); + } + } else if (stalepipe) { + LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove " + << filename); + filename.removeFile(); + stalepipe = false; + } + if (filename.exists()) { + lyxerr << "LyXComm: Pipe " << filename + << " already exists.\nIf no other LyX program" + " is active, please delete the pipe by hand" + " and try again." + << endl; + pipename_.erase(); + return -1; + } } if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) { lyxerr << "LyXComm: Could not create pipe " << filename << '\n' << strerror(errno) << endl; return -1; - }; + } int const fd = ::open(filename.toFilesystemEncoding().c_str(), write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK)); if (fd < 0) { lyxerr << "LyXComm: Could not open pipe " << filename << '\n' << strerror(errno) << endl; - unlink(filename); + filename.removeFile(); return -1; } @@ -209,19 +242,18 @@ void LyXComm::endPipe(int & fd, string const & filename, bool write) if (fd < 0) return; - if (!write) { + if (!write) theApp()->unregisterSocketCallback(fd); - } if (::close(fd) < 0) { lyxerr << "LyXComm: Could not close pipe " << filename << '\n' << strerror(errno) << endl; } - if (unlink(FileName(filename)) < 0) { + if (FileName(filename).removeFile() < 0) { lyxerr << "LyXComm: Could not remove pipe " << filename << '\n' << strerror(errno) << endl; - }; + } fd = -1; } @@ -229,9 +261,9 @@ void LyXComm::endPipe(int & fd, string const & filename, bool write) void LyXComm::emergencyCleanup() { - if (!pipename.empty()) { - endPipe(infd, inPipeName(), false); - endPipe(outfd, outPipeName(), true); + if (!pipename_.empty()) { + endPipe(infd_, inPipeName(), false); + endPipe(outfd_, outPipeName(), true); } } @@ -239,17 +271,23 @@ void LyXComm::emergencyCleanup() // Receives messages and sends then to client void LyXComm::read_ready() { - // nb! make read_buffer_ a class-member for multiple sessions + // FIXME: make read_buffer_ a class-member for multiple sessions static string read_buffer_; read_buffer_.erase(); int const charbuf_size = 100; char charbuf[charbuf_size]; + // As O_NONBLOCK is set, until no data is available for reading, + // read() doesn't block but returns -1 and set errno to EAGAIN. + // After a client that opened the pipe for writing, closes it + // (and no other client is using the pipe), read() would always + // return 0 and thus the connection has to be reset. + errno = 0; int status; // the single = is intended here. - while ((status = ::read(infd, charbuf, charbuf_size - 1))) { + while ((status = ::read(infd_, charbuf, charbuf_size - 1))) { if (status > 0) { charbuf[status] = '\0'; // turn it into a c string @@ -260,32 +298,32 @@ void LyXComm::read_ready() // the delim /wasn't/ found. ?:-P string cmd; read_buffer_= split(read_buffer_, cmd,'\n'); - LYXERR(Debug::LYXSERVER) - << "LyXComm: status:" << status + LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status << ", read_buffer_:" << read_buffer_ - << ", cmd:" << cmd << endl; + << ", cmd:" << cmd); if (!cmd.empty()) - clientcb(client, cmd); + clientcb_(client_, cmd); //\n or not \n? } - } - if (errno == EAGAIN) { - errno = 0; - return; - } - if (errno != 0) { - lyxerr << "LyXComm: " << strerror(errno) << endl; + } else { + if (errno == EAGAIN) { + // Nothing to read, continue + errno = 0; + return; + } + // An error occurred, better bailing out + LYXERR0("LyXComm: " << strerror(errno)); if (!read_buffer_.empty()) { - lyxerr << "LyXComm: truncated command: " - << read_buffer_ << endl; + LYXERR0("LyXComm: truncated command: " << read_buffer_); read_buffer_.erase(); } break; // reset connection } } - // The connection gets reset in errno != EAGAIN - // Why does it need to be reset if errno == 0? + // The connection gets reset when read() returns 0 (meaning that the + // last client closed the pipe) or an error occurred, in which case + // read() returns -1 and errno != EAGAIN. closeConnection(); openConnection(); errno = 0; @@ -295,19 +333,17 @@ void LyXComm::read_ready() void LyXComm::send(string const & msg) { if (msg.empty()) { - lyxerr << "LyXComm: Request to send empty string. Ignoring." - << endl; + LYXERR0("LyXComm: Request to send empty string. Ignoring."); return; } - LYXERR(Debug::LYXSERVER) << "LyXComm: Sending '" << msg << '\'' << endl; + LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\''); - if (pipename.empty()) return; + if (pipename_.empty()) return; - if (!ready) { - lyxerr << "LyXComm: Pipes are closed. Could not send " - << msg << endl; - } else if (::write(outfd, msg.c_str(), msg.length()) < 0) { + if (!ready_) { + LYXERR0("LyXComm: Pipes are closed. Could not send " << msg); + } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) { lyxerr << "LyXComm: Error sending message: " << msg << '\n' << strerror(errno) << "\nLyXComm: Resetting connection" << endl; @@ -321,41 +357,56 @@ void LyXComm::send(string const & msg) string const LyXComm::inPipeName() const { - return pipename + string(".in"); + return pipename_ + ".in"; } string const LyXComm::outPipeName() const { - return pipename + string(".out"); + return pipename_ + ".out"; } -// Server class +///////////////////////////////////////////////////////////////////// +// +// Server +// +///////////////////////////////////////////////////////////////////// + +void ServerCallback(Server * server, string const & msg) +{ + server->callback(msg); +} + +Server::Server(LyXFunc * f, string const & pipes) + : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback) +{} + Server::~Server() { // say goodbye to clients so they stop sending messages - // modified june 1999 by stefano@zool.su.se to send as many bye - // messages as there are clients, each with client's name. + // send as many bye messages as there are clients, + // each with client's name. string message; - for (int i= 0; inumclients == MAX_CLIENTS) { //paranoid check - LYXERR(Debug::LYXSERVER) - << "Server: too many clients..." - << endl; + if (numclients_ == MAX_CLIENTS) { //paranoid check + LYXERR(Debug::LYXSERVER, "Server: too many clients..."); return; } - int i= 0; //find place in clients[] - while (!serv->clients[i].empty() - && inumclients) + int i = 0; + while (!clients_[i].empty() && i < numclients_) ++i; - serv->clients[i] = client; - serv->numclients++; + clients_[i] = client; + ++numclients_; buf = "LYXSRV:" + client + ":hello\n"; - LYXERR(Debug::LYXSERVER) - << "Server: Greeting " - << client << endl; - serv->pipes.send(buf); + LYXERR(Debug::LYXSERVER, "Server: Greeting " << client); + pipes_.send(buf); } else if (cmd == "bye") { - // If clients == 0 maybe we should reset the pipes + // If clients_ == 0 maybe we should reset the pipes // to prevent fake callbacks int i = 0; //look if client is registered - for (; i < serv->numclients; ++i) { - if (serv->clients[i] == client) break; + for (; i < numclients_; ++i) { + if (clients_[i] == client) + break; } - if (i < serv->numclients) { - serv->numclients--; - serv->clients[i].erase(); - LYXERR(Debug::LYXSERVER) - << "Server: Client " - << client << " said goodbye" - << endl; + if (i < numclients_) { + --numclients_; + clients_[i].erase(); + LYXERR(Debug::LYXSERVER, "Server: Client " + << client << " said goodbye"); } else { - LYXERR(Debug::LYXSERVER) - << "Server: ignoring bye messge from unregistered client" - << client << endl; + LYXERR(Debug::LYXSERVER, + "Server: ignoring bye messge from unregistered client" << client); } } else { - lyxerr <<"Server: Undefined server command " - << cmd << '.' << endl; + LYXERR0("Server: Undefined server command " << cmd << '.'); } return; } @@ -460,21 +503,19 @@ void Server::callback(Server * serv, string const & msg) // connect to the lyxfunc in the single LyXView we // support currently. (Lgb) + func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg)); + string const rval = to_utf8(func_->getMessage()); - serv->func->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg)); - string const rval = to_utf8(serv->func->getMessage()); - - //modified june 1999 stefano@zool.su.se: - //all commands produce an INFO or ERROR message - //in the output pipe, even if they do not return - //anything. See chapter 4 of Customization doc. + // all commands produce an INFO or ERROR message + // in the output pipe, even if they do not return + // anything. See chapter 4 of Customization doc. string buf; - if (serv->func->errorStat()) + if (func_->errorStat()) buf = "ERROR:"; else buf = "INFO:"; buf += client + ':' + cmd + ':' + rval + '\n'; - serv->pipes.send(buf); + pipes_.send(buf); // !!! we don't do any error checking - // if the client won't listen, the @@ -484,22 +525,14 @@ void Server::callback(Server * serv, string const & msg) // not found } - } /* while *p */ + } // while *p } -/* ---F+------------------------------------------------------------------ *\ - Function : LyXNotifyClient - Called by : WorkAreaKeyPress - Purpose : send a notify messge to a client - Parameters: s - string to send - Returns : nothing - \* ---F------------------------------------------------------------------- */ - +// Send a notify message to a client, called by WorkAreaKeyPress void Server::notifyClient(string const & s) { - string buf = string("NOTIFY:") + s + "\n"; - pipes.send(buf); + pipes_.send("NOTIFY:" + s + "\n"); }