Hello, I wrote a simple local socket interface for lyx that compiles and runs well in Linux with gcc 3.3 (Debian unstable) with the purpose of getting inverse dvi search working, for witch I will send a patch in my next e-mail.
It consists of two classes: LyXDataSocket - this class has a socket in the connected state and two functions readln() and writeln() for line buffered IO. LyXServerSocket - this class has a socket in the listening state. Each time a new connection arrives, it allocates a new LyXDataSocket. These are intended to substitute the lyxserver based on pipes, but can coexist with it. Up to now, the socket server is used only by the xforms frontend. Of course, to substitute the pipe lixserver, it has to be ported to qt and also run on all the operating systems the pipe lyxserver runs. To compile, apply the patch lyxsocket.diff, copy LyXSocket.{C,h} in src socktools.{C,h} in src/support and run autogen and configure. Also attached is the corresponding client lyxclient.C. Compile it with g++ -o lyxclient lyxclient.C Once lyx starts a socket special file named lyxsocket will be created on its temporary directory, say /tmp/lyx_tmpdir511223hyx2/lyxsocket To send only one comand to this running lyx, use lyxclient -a /tmp/lyx_tmpdir511223hyx2/lyxsocket -c <command> Several lyxes can run at the same time! To send more than one command, don't use the switch -c and type directly on lyxclient standard input. If the -a switch is not used, lyxclient will try to find a running lyx and connect to it. 'lyxclient -h' should provide a comprehensive help. If this patch gets accepted, I will provide Changelog entries and also write the lyxclient manpage. Regards, Joćo.
/** * \file lyxclient.C * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Joćo Luis M. Assirati * * Full author contact details are available in file CREDITS. */ #include <string> #include <vector> #include <map> #include <iostream> // getpid(), getppid() #include <sys/types.h> #include <unistd.h> // select() #include <sys/select.h> // opendir(), closedir(), readdir() #include <sys/types.h> #include <dirent.h> // stat() #include <sys/stat.h> // socket(), connect() #include <sys/socket.h> #include <sys/un.h> // fcntl() #include <fcntl.h> using std::string; using std::vector; using std::cout; using std::cerr; using std::cin; using std::endl; // support ------------------------------------------------------------ namespace support { string itoa(unsigned int i) { string str; if(!i) { str = "0"; } else { while((0 < i) && (i <= 9)) { str = static_cast<char>('0' + i % 10) + str; i /= 10; } } return str; } bool prefixIs(string const & a, string const & pre) { char const * p_a = a.c_str(); char const * p_pre = pre.c_str(); while ((*p_a != '\0') && (*p_pre != '\0') && (*p_a == *p_pre)) { ++p_a; ++p_pre; } if (*p_pre == '\0') return true; return false; } // Parts stolen from lyx::support::DirList() // Returns pathnames begining with dir and ending with // pathname separator (/ in unix) vector<string> lyxSockets(string const & dir, string const & pid) { vector<string> dirlist; DIR * dirp = ::opendir(dir.c_str()); if (!dirp) { cerr << "lyxclient: Could not read dir " << dir << ": " << strerror(errno); return dirlist; } dirent * dire; while ((dire = ::readdir(dirp))) { string const fil = dire->d_name; if (prefixIs(fil, "lyx_tmpdir" + pid)) { string lyxsocket = dir + '/' + fil + "/lyxsocket"; struct stat status; // not checking if it is a socket -- just if it exists if (!::stat(lyxsocket.c_str(), &status)) { dirlist.push_back(lyxsocket); } } } ::closedir(dirp); return dirlist; } namespace socktools { int connect(string const & name) { int fd; // File descriptor for the socket sockaddr_un addr; // Structure that hold the socket address // char sun_path[108] string::size_type len = name.size(); if(len > 107) { cerr << "lyxclient: Socket address '" << name << "' too long." << endl; return -1; } // Synonims for AF_UNIX are AF_LOCAL and AF_FILE addr.sun_family = AF_UNIX; name.copy(addr.sun_path, 107); addr.sun_path[len] = '\0'; if((fd = ::socket(PF_UNIX, SOCK_STREAM, 0))== -1) { cerr << "lyxclient: Could not create socket: " << strerror(errno) << endl; return -1; } if(::connect(fd, (struct sockaddr *)&addr, SUN_LEN(&addr)) == -1) { cerr << "lyxclient: Could not connect to socket " << name << ": " << strerror(errno) << endl; ::close(fd); return -1; } if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { cerr << "lyxclient: Could not set O_NONBLOCK for socket: " << strerror(errno) << endl; ::close(fd); return -1; } return fd; } } // namespace socktools } // support ------------------------------------------------------------ // Class IOWatch ------------------------------------------------------------ class IOWatch { public: IOWatch(); void clear(); void addfd(int); bool wait(double); bool wait(); bool isset(int fd); private: fd_set des; fd_set act; }; IOWatch::IOWatch() { clear(); } void IOWatch::clear() { FD_ZERO(&des); } void IOWatch::addfd(int fd) { FD_SET(fd, &des); } bool IOWatch::wait(double timeout) { timeval to; to.tv_sec = static_cast<long int>(timeout); to.tv_usec = static_cast<long int>((timeout - to.tv_sec)*1E6); act = des; return select(FD_SETSIZE, &act, (fd_set *)0, (fd_set *)0, &to); } bool IOWatch::wait() { act = des; return select(FD_SETSIZE, &act, (fd_set *)0, (fd_set *)0, (timeval *)0); } bool IOWatch::isset(int fd) { return FD_ISSET(fd, &act); } // ~Class IOWatch ------------------------------------------------------------ // Class LyXDataSocket ------------------------------------------------------- // Modified LyXDataSocket class for use with the client class LyXDataSocket { public: LyXDataSocket(string const &); ~LyXDataSocket(); // File descriptor of the connection int fd() const; // Connection status bool connected() const; // Line buffered input from the socket bool readln(string &); // Write the string + '\n' to the socket void writeln(string const &); private: // File descriptor for the data socket int fd_; // True if the connection is up bool connected_; // buffer for input data string buffer; }; LyXDataSocket::LyXDataSocket(string const & address) { if ((fd_ = support::socktools::connect(address)) == -1) { connected_ = false; } else { connected_ = true; } } LyXDataSocket::~LyXDataSocket() { ::close(fd_); } int LyXDataSocket::fd() const { return fd_; } bool LyXDataSocket::connected() const { return connected_; } // Returns true if there was a complete line to input // A line is of the form <key>:<value> // A line not of this form will not be passed // The line read is splitted and stored in 'key' and 'value' bool LyXDataSocket::readln(string & line) { int const charbuf_size = 100; char charbuf[charbuf_size]; // buffer for the ::read() system call int count; string::size_type pos; // read and store characters in buffer while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) { charbuf[count] = '\0'; // turn it into a c string buffer += charbuf; } // Error conditions. The buffer must still be // processed for lines read if (count == 0) { // EOF -- connection closed connected_ = false; } else if ((count == -1) && (errno != EAGAIN)) { // IO error cerr << "lyxclient: IO error." << endl; connected_ = false; } // Cut a line from buffer if ((pos = buffer.find('\n')) == string::npos) return false; // No complete line stored line = buffer.substr(0, pos); buffer = buffer.substr(pos + 1); return true; } // Write a line of the form <key>:<value> to the socket void LyXDataSocket::writeln(string const & line) { string linen(line + '\n'); int size = linen.size(); int written = ::write(fd_, linen.c_str(), size); if (written < size) { // Allways mean end of connection. if ((written == -1) && (errno == EPIPE)) { // The program will also receive a SIGPIPE // that must be catched cerr << "lyxclient: connection closed while writing." << endl; } else { // Anything else, including errno == EAGAIN, must be // considered IO error. EAGAIN should never happen // when line is small cerr << "lyxclient: IO error: " << strerror(errno); } connected_ = false; } } // ~Class LyXDataSocket ------------------------------------------------------- // Class CmdLineParser ------------------------------------------------------- class CmdLineParser { public: typedef int (*optfunc)(vector<char *> const & args); std::map<string, optfunc> helper; std::map<string, bool> isset; bool parse(int, char * []); vector<char *> nonopt; }; bool CmdLineParser::parse(int argc, char * argv[]) { int opt = 1; while(opt < argc) { vector<char *> args; if(helper[argv[opt]]) { isset[argv[opt]] = true; int arg = opt + 1; while((arg < argc) && (!helper[argv[arg]])) { args.push_back(argv[arg]); ++arg; } int taken = helper[argv[opt]](args); if(taken == -1) return false; opt += 1 + taken; } else { if(argv[opt][0] == '-') { if((argv[opt][1] == '-') && (argv[opt][2]== '\0')) { ++opt; while(opt < argc) { nonopt.push_back(argv[opt]); ++opt; } return true; } else { cerr << "lyxclient: unknown option " << argv[opt] << endl; return false; } } nonopt.push_back(argv[opt]); ++opt; } } return true; } // ~Class CmdLineParser ------------------------------------------------------- namespace cmdline { void usage() { cerr << "Usage: lyxclient [options]" << endl << "Options are:" << endl << " -a address set address of the lyx socket" << endl << " -t directory set system temporary directory" << endl << " -p pid select a running lyx by pid" << endl << " -c command send a single command and quit" << endl << " -g file row send a command to go to file and row" << endl << " -n name set client name" << endl << " -h name display this help end exit" << endl << "If -a is not used, lyxclient will use the arguments of -t and -p to look for" << endl << "a running lyx. If -t is not set, 'directory' defaults to /tmp. If -p is set," << endl << "lyxclient will connect only to a lyx with the specified pid. Options -c and -g" << endl << "cannot be set simultaneoulsly. If no -c or -g options are given, lyxclient" << endl << "will read commands from standard input and disconnect when command read is BYE:" << endl; } int h(vector<char *> const & arg) { usage(); exit(0); } string clientName(support::itoa(::getppid()) + ">" + support::itoa(::getpid())); int n(vector<char *> const & arg) { if(arg.size() < 1) { cerr << "lyxclient: The option -n requires 1 argument." << endl; return -1; } clientName = arg[0]; return 1; } string singleCommand; int c(vector<char *> const & arg) { if(arg.size() < 1) { cerr << "lyxclient: The option -c requires 1 argument." << endl; return -1; } singleCommand = arg[0]; return 1; } int g(vector<char *> const & arg) { if(arg.size() < 2) { cerr << "lyxclient: The option -g requires 2 arguments." << endl; return -1; } singleCommand = "LYXCMD:server-goto-file-row " + static_cast<string>(arg[0]) + ' ' + static_cast<string>(arg[1]); return 2; } char * serverAddress; int a(vector<char *> const & arg) { if(arg.size() < 1) { cerr << "lyxclient: The option -a requires 1 argument." << endl; return -1; } serverAddress = arg[0]; return 1; } string mainTmp("/tmp"); int t(vector<char *> const & arg) { if(arg.size() < 1) { cerr << "lyxclient: The option -t requires 1 argument." << endl; return -1; } mainTmp = arg[0]; return 1; } string serverPid; // Init to empty string int p(vector<char *> const & arg) { if(arg.size() < 1) { cerr << "lyxclient: The option -p requires 1 argument." << endl; return -1; } serverPid = arg[0]; return 1; } } // namespace cmdline using support::prefixIs; int main(int argc, char * argv[]) { CmdLineParser parser; parser.helper["-h"] = cmdline::h; parser.helper["-c"] = cmdline::c; parser.helper["-g"] = cmdline::g; parser.helper["-n"] = cmdline::n; parser.helper["-a"] = cmdline::a; parser.helper["-t"] = cmdline::t; parser.helper["-p"] = cmdline::p; // Command line failure conditions: if((!parser.parse(argc, argv)) || (parser.isset["-c"] && parser.isset["-g"]) || (parser.isset["-a"] && parser.isset["-p"])) { cmdline::usage(); return 1; } LyXDataSocket * server; if(parser.isset["-a"]) { server = new LyXDataSocket(cmdline::serverAddress); if(!server->connected()) { cerr << "lyxclient: " << "Could not connect to " << cmdline::serverAddress << endl; return 1; } } else { // We have to look for an address. // serverPid can be empty. vector<string> addrs = support::lyxSockets(cmdline::mainTmp, cmdline::serverPid); vector<string>::iterator addr = addrs.begin(); vector<string>::iterator end = addrs.end(); while (addr < end) { server = new LyXDataSocket(*addr); if(server->connected()) break; cerr << "lyxclient: " << "Could not connect to " << *addr << endl; delete server; ++addr; } if(addr == end) { cerr << "lyxclient: No suitable server found." << endl; return 1; } cerr << "lyxclient: " << "Connected to " << *addr << endl; } int const serverfd = server->fd(); IOWatch iowatch; iowatch.addfd(serverfd); // Used to read from server string answer; // Send greeting server->writeln("HELLO:" + cmdline::clientName); // wait at most 2 seconds until server responds iowatch.wait(2.0); if(iowatch.isset(serverfd) && server->readln(answer)) { if(prefixIs(answer, "BYE:")) { cerr << "lyxclient: Server disconnected." << endl; cout << answer << endl; return 1; } } else { cerr << "lyxclient: No answer from server." << endl; return 1; } if(parser.isset["-g"] || parser.isset["-c"]) { server->writeln(cmdline::singleCommand); iowatch.wait(2.0); if(iowatch.isset(serverfd) && server->readln(answer)) { cout << answer; if(prefixIs(answer, "ERROR:")) return 1; return 0; } else { cerr << "lyxclient: No answer from server." << endl; return 1; } } // Take commands from stdin iowatch.addfd(0); // stdin bool saidbye = false; while((!saidbye) && server->connected()) { iowatch.wait(); if(iowatch.isset(0)) { string command; cin >> command; if(command == "BYE:") { server->writeln("BYE:"); saidbye = true; } else { server->writeln("LYXCMD:" + command); } } if(iowatch.isset(serverfd)) { while(server->readln(answer)) cout << answer << endl; } } return 0; }
// -*- C++ -*- /** * \file socktools.h * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Joćo Luis M. Assirati * * Full author contact details are available in file CREDITS. */ #ifndef SOCKTOOLS_H #define SOCKTOOLS_H #include <string> using std::string; namespace lyx { namespace support { namespace socktools { int listen(string const &, int); int accept(int); } } } #endif // SOCKTOOLS_H
/** * \file socktools.C * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Joćo Luis M. Assirati * * Full author contact details are available in file CREDITS. */ #include "socktools.h" #include "debug.h" #include "lyxlib.h" #include <sys/socket.h> #include <sys/un.h> #include <fcntl.h> #include <cerrno> using std::strerror; using std::endl; namespace lyx { namespace support { namespace socktools { // Returns a local socket already in the "listen" state (or -1 in case // of error). The first argument is the socket address, the second // is the length of the queue for connections. If successful, a socket // special file 'name' will be created in the filesystem. int listen(string const & name, int queue) { int fd; // File descriptor for the socket sockaddr_un addr; // Structure that hold the socket address // We use 'name' to fill 'addr' string::size_type len = name.size(); // the field sun_path in sockaddr_un is a char[108] if (len > 107) { lyxerr << "lyx: Socket address '" << name << "' too long." << endl; return -1; } // Synonims for AF_UNIX are AF_LOCAL and AF_FILE addr.sun_family = AF_UNIX; name.copy(addr.sun_path, 107); addr.sun_path[len] = '\0'; // This creates a file descriptor for the socket // Synonims for PF_UNIX are PF_LOCAL and PF_FILE // For local sockets, the protocol is always 0 // socket() returns -1 in case of error if ((fd = ::socket(PF_UNIX, SOCK_STREAM, 0))== -1) { lyxerr << "lyx: Could not create socket descriptor: " << strerror(errno) << endl; return -1; } // Set NONBLOCK mode for the file descriptor if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { lyxerr << "lyx: Could not set NONBLOCK mode for socket descriptor: " << strerror(errno) << endl; ::close(fd); return -1; } // bind() gives the local address 'name' for 'fd', also creating // the socket special file in the filesystem. bind() returns -1 // in case of error if ((::bind (fd, reinterpret_cast<sockaddr *>(&addr), SUN_LEN(&addr))) == -1) { lyxerr << "lyx: Could not bind address '" << name << "' to socket descriptor: " << strerror(errno) << endl; ::close(fd); lyx::support::unlink(name); return -1; } // Puts the socket in listen state, that is, ready to accept // connections. The second parameter of listen() defines the // maximum length the queue of pending connections may grow to. // It is not a restriction on the number of connections the socket // can accept. Returns -1 in case of error if (::listen (fd, queue) == -1) { lyxerr << "lyx: Could not put socket in 'listen' state: " << strerror(errno) << endl; ::close(fd); lyx::support::unlink(name); return -1; } return fd; } // Returns a file descriptor for a new connection from the socket // descriptor 'sd' (or -1 in case of error) int accept(int sd) { int fd; // Returns the new file descriptor or -1 in case of error // Using null pointers for the second and third arguments // dismiss all information about the connecting client if ((fd = accept(sd, reinterpret_cast<sockaddr *>(0), reinterpret_cast<socklen_t *>(0))) == -1) { lyxerr << "lyx: Could not accept connection: " << strerror(errno) << endl; return -1; } // Sets NONBLOCK mode for the file descriptor if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { lyxerr << "lyx: Could not set NONBLOCK mode for connection: " << strerror(errno) << endl; ::close(fd); return -1; } return fd; } } // namespace socktools } // namespace support } // namespace lyx
// -*- C++ -*- /** * \file LyXSocket.h * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Lars Gullik Bjųnnes * \author Jean-Marc Lasgouttes * \author Joćo Luis M. Assirati * * Full author contact details are available in file CREDITS. */ #ifndef LYXSOCKET_H #define LYXSOCKET_H #include "support/socktools.h" #include "lyxfunc.h" #include <string> #include <set> using std::string; class LyXServerSocket; class LyXDataSocket; //typedef void (*serversocket_callback_t)(LyXServerSocket *); //typedef void (*datasocket_callback_t)(LyXDataSocket *); //void add_serversocket_callback(serversocket_callback_t, LyXServerSocket *); //void add_datasocket_callback(datasocket_callback_t, LyXDataSocket *); //void remove_datasocket_callback(LyXDataSocket *); // Sockets can be in two states: listening and connected. // Connected sockets are used to transfer data, and will therefore // be called Data Sockets. Listening sockets are used to create // Data Sockets when clients connect, and therefore will be called // Server Sockets. // This class encapsulates local (unix) server socket operations and // manages LyXDataSockets objects that are created when clients connect. class LyXServerSocket { public: LyXServerSocket(LyXFunc *, string const &); ~LyXServerSocket(); // File descriptor of the socket int fd() const; // Address of the local socket string const & address() const; // To be called when there is activity in the server socket void serverCallback(); // To be called when there is activity in the data socket void dataCallback(LyXDataSocket *); private: // Close the connection to the argument client void close(LyXDataSocket *); LyXFunc * func; // File descriptor for the server socket int fd_; // Stores the socket filename string address_; // Maximum number of simultaneous clients enum { MAX_CLIENTS = 10 }; // All connections std::set<LyXDataSocket *> clients; //void dump() const; }; // This class encapsulates data socket operations. // It provides read and write IO operations on the socket. class LyXDataSocket { public: LyXDataSocket(LyXServerSocket *); ~LyXDataSocket(); // The object that allocated us LyXServerSocket * server() const; // File descriptor of the connection int fd() const; // Connection status bool connected() const; // Line buffered input from the socket bool readln(string &); // Write the string + '\n' to the socket void writeln(string const &); private: LyXServerSocket * server_; // File descriptor for the data socket int fd_; // True if the connection is up bool connected_; // buffer for input data string buffer; }; #endif // LYXSOCKET_H
/** * \file LyXSocket.C * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Lars Gullik Bjųnnes * \author Jean-Marc Lasgouttes * \author Angus Leeming * \author John Levon * \author Joćo Luis M. Assirati * * Full author contact details are available in file CREDITS. */ #include "support/socktools.h" #include "support/lyxlib.h" #include "frontends/lyx_gui.h" #include "debug.h" #include "lyxfunc.h" #include "LyXSocket.h" #include <iostream> using std::endl; // Address is the unix address for the socket. // MAX_CLIENTS is the maximum number of clients // that can connect at the same time. LyXServerSocket::LyXServerSocket(LyXFunc * f, string const & addr) : func(f), fd_(lyx::support::socktools::listen(addr, MAX_CLIENTS)), address_(addr) { if (fd_ == -1) { lyxerr << "lyx: Disabling LyX socket." << endl; return; } lyx_gui::set_serversocket_callback(this); lyxerr[Debug::LYXSERVER] << "lyx: New server socket " << fd_ << ' ' << address_ << endl; } // Close the socket and remove the address of the filesystem. LyXServerSocket::~LyXServerSocket() { ::close(fd_); lyx::support::unlink(address_); while (!clients.empty()) close(*clients.rbegin()); lyxerr[Debug::LYXSERVER] << "lyx: Server socket quitting" << endl; } int LyXServerSocket::fd() const { return fd_; } string const & LyXServerSocket::address() const { return address_; } // Creates a new LyXDataSocket and checks to see if the connection // is OK and if the number of clients does not exceed MAX_CLIENTS void LyXServerSocket::serverCallback() { LyXDataSocket * client = new LyXDataSocket(this); if (client->connected()) { if (clients.size() == MAX_CLIENTS) { client->writeln("BYE:Too many clients connected"); } else { clients.insert(client); lyx_gui::set_datasocket_callback(client); return; } } delete client; } // Reads and processes input from client and check // if the connection has been closed void LyXServerSocket::dataCallback(LyXDataSocket * client) { //dump(); string line; string::size_type pos; bool saidbye = false; while ((!saidbye) && client->readln(line)) { // The protocol must be programmed here // Split the key and the data if ((pos = line.find(':')) == string::npos) { client->writeln("ERROR:" + line + ":malformed message"); } else { string const key = line.substr(0, pos); string const value = line.substr(pos + 1); if (key == "LYXCMD") { func->dispatch(value); string const rval = func->getMessage(); if (func->errorStat()) { client->writeln("ERROR:" + value + ':' + rval); } else { client->writeln("INFO:" + value + ':' + rval); } } else if (key == "HELLO") { // no use for client name! client->writeln("HELLO:"); } else if (key == "BYE") { saidbye = true; } else { client->writeln("ERROR:unknown key " + key); } } } if (saidbye || (!client->connected())) { close(client); } } // Removes client callback and deletes client object void LyXServerSocket::close(LyXDataSocket * client) { lyx_gui::remove_datasocket_callback(client); clients.erase(client); delete client; } // Debug // void LyXServerSocket::dump() const // { // lyxerr << "LyXServerSocket debug dump.\n" // << "fd = " << fd_ << ", address = " << address_ << ".\n" // << "Clients: " << clients.size() << ".\n"; // if (!clients.empty()) { // std::set<LyXDataSocket *>::const_iterator client = clients.begin(); // std::set<LyXDataSocket *>::const_iterator end = clients.end(); // for (; client != end; ++client) // lyxerr << "fd = " << (*client)->fd() << "\n"; // } // } LyXDataSocket::LyXDataSocket(LyXServerSocket * serv) :server_(serv), fd_(lyx::support::socktools::accept(serv->fd())) { if (fd_ == -1) { connected_ = false; } else { lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl; connected_ = true; } } LyXDataSocket::~LyXDataSocket() { ::close(fd_); lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting." << endl; } LyXServerSocket * LyXDataSocket::server() const { return server_; } int LyXDataSocket::fd() const { return fd_; } bool LyXDataSocket::connected() const { return connected_; } // Returns true if there was a complete line to input bool LyXDataSocket::readln(string & line) { int const charbuf_size = 100; char charbuf[charbuf_size]; // buffer for the ::read() system call int count; string::size_type pos; // read and store characters in buffer while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) { charbuf[count] = '\0'; // turn it into a c string buffer += charbuf; } // Error conditions. The buffer must still be // processed for lines read if (count == 0) { // EOF -- connection closed lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << ": connection closed." << endl; connected_ = false; } else if ((count == -1) && (errno != EAGAIN)) { // IO error lyxerr << "lyx: Data socket " << fd_ << ": IO error." << endl; connected_ = false; } // Cut a line from buffer if ((pos = buffer.find('\n')) == string::npos) { lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << ": line not completed." << endl; return false; // No complete line stored } line = buffer.substr(0, pos); buffer = buffer.substr(pos + 1); return true; } // Write a line of the form <key>:<value> to the socket void LyXDataSocket::writeln(string const & line) { string linen(line + '\n'); int size = linen.size(); int written = ::write(fd_, linen.c_str(), size); if (written < size) { // Allways mean end of connection. if ((written == -1) && (errno == EPIPE)) { // The program will also receive a SIGPIPE // that must be catched lyxerr << "lyx: Data socket " << fd_ << " connection closed while writing." << endl; } else { // Anything else, including errno == EAGAIN, must be // considered IO error. EAGAIN should never happen // when line is small lyxerr << "lyx: Data socket " << fd_ << " IO error: " << strerror(errno); } connected_ = false; } }
Index: src/lyx_main.C =================================================================== RCS file: /cvs/lyx/lyx-devel/src/lyx_main.C,v retrieving revision 1.178 diff -u -r1.178 lyx_main.C --- src/lyx_main.C 2003/10/06 15:42:20 1.178 +++ src/lyx_main.C 2003/10/08 05:08:52 @@ -214,6 +214,13 @@ case SIGTERM: // no comments break; + case SIGPIPE: + // This will be received if lyx tries to write to a socket + // whose reading end was closed. It can safely be ignored, + // as in this case the ::write() system call will return -1 + // and errno will be set to EPIPE + return; + //break; } // Deinstall the signal handlers @@ -222,6 +229,7 @@ signal(SIGFPE, SIG_DFL); signal(SIGSEGV, SIG_DFL); signal(SIGTERM, SIG_DFL); + signal(SIGPIPE, SIG_DFL); LyX::emergencyCleanup(); @@ -250,6 +258,7 @@ signal(SIGSEGV, error_handler); signal(SIGINT, error_handler); signal(SIGTERM, error_handler); + signal(SIGPIPE, error_handler); bool const explicit_userdir = setLyxPaths(); Index: src/Makefile.am =================================================================== RCS file: /cvs/lyx/lyx-devel/src/Makefile.am,v retrieving revision 1.192 diff -u -r1.192 Makefile.am --- src/Makefile.am 2003/09/17 16:44:51 1.192 +++ src/Makefile.am 2003/10/08 05:08:52 @@ -88,6 +88,8 @@ latexrunparams.h \ LyXAction.C \ LyXAction.h \ + LyXSocket.C \ + LyXSocket.h \ MenuBackend.C \ MenuBackend.h \ paragraph_funcs.C \ Index: src/support/Makefile.am =================================================================== RCS file: /cvs/lyx/lyx-devel/src/support/Makefile.am,v retrieving revision 1.70 diff -u -r1.70 Makefile.am --- src/support/Makefile.am 2003/10/07 13:32:17 1.70 +++ src/support/Makefile.am 2003/10/08 05:08:52 @@ -65,6 +65,8 @@ rmdir.C \ snprintf.h \ snprintf.c \ + socktools.C \ + socktools.h \ sstream.h \ std_istream.h \ std_ostream.h \ Index: src/frontends/lyx_gui.h =================================================================== RCS file: /cvs/lyx/lyx-devel/src/frontends/lyx_gui.h,v retrieving revision 1.23 diff -u -r1.23 lyx_gui.h --- src/frontends/lyx_gui.h 2003/10/07 06:45:24 1.23 +++ src/frontends/lyx_gui.h 2003/10/08 05:08:55 @@ -22,6 +22,8 @@ class LColor_color; class LyXFont; class LyXComm; +class LyXDataSocket; +class LyXServerSocket; class FuncRequest; /// GUI interaction @@ -94,12 +96,16 @@ * add a callback for I/O read notification */ void set_read_callback(int fd, LyXComm * comm); +void set_datasocket_callback(LyXDataSocket *); +void set_serversocket_callback(LyXServerSocket *); /** * remove a I/O read callback * @param fd file descriptor */ void remove_read_callback(int fd); +void remove_datasocket_callback(LyXDataSocket *); +void remove_serversocket_callback(LyXServerSocket *); } // namespace lyx_gui Index: src/frontends/xforms/lyx_gui.C =================================================================== RCS file: /cvs/lyx/lyx-devel/src/frontends/xforms/lyx_gui.C,v retrieving revision 1.54 diff -u -r1.54 lyx_gui.C --- src/frontends/xforms/lyx_gui.C 2003/10/06 15:42:57 1.54 +++ src/frontends/xforms/lyx_gui.C 2003/10/08 05:08:56 @@ -27,6 +27,7 @@ #include "lyxfunc.h" #include "lyxrc.h" #include "lyxserver.h" +#include "LyXSocket.h" #include "graphics/LoaderQueue.h" @@ -67,6 +68,7 @@ // FIXME: wrong place ! LyXServer * lyxserver; +LyXServerSocket * lyxsocket; namespace { @@ -287,6 +289,8 @@ // FIXME: some code below needs moving lyxserver = new LyXServer(&view.getLyXFunc(), lyxrc.lyxpipes); + lyxsocket = new LyXServerSocket(&view.getLyXFunc(), + os::slashify_path(os::getTmpDir() + "/lyxsocket")); vector<string>::const_iterator cit = files.begin(); vector<string>::const_iterator end = files.end(); @@ -311,6 +315,7 @@ } // FIXME: breaks emergencyCleanup + delete lyxsocket; delete lyxserver; } @@ -380,6 +385,20 @@ comm->read_ready(); } +extern "C" +void C_datasocket_callback(int, void * data) +{ + LyXDataSocket * client = static_cast<LyXDataSocket *>(data); + client->server()->dataCallback(client); +} + +extern "C" +void C_serversocket_callback(int, void * data) +{ + LyXServerSocket * server = static_cast<LyXServerSocket *>(data); + server->serverCallback(); +} + } void set_read_callback(int fd, LyXComm * comm) @@ -387,12 +406,30 @@ fl_add_io_callback(fd, FL_READ, C_read_callback, comm); } - void remove_read_callback(int fd) { fl_remove_io_callback(fd, FL_READ, C_read_callback); } +void set_datasocket_callback(LyXDataSocket * p) +{ + fl_add_io_callback(p->fd(), FL_READ, C_datasocket_callback, p); +} + +void remove_datasocket_callback(LyXDataSocket * p) +{ + fl_remove_io_callback(p->fd(), FL_READ, C_datasocket_callback); +} + +void set_serversocket_callback(LyXServerSocket * p) +{ + fl_add_io_callback(p->fd(), FL_READ, C_serversocket_callback, p); +} + +void remove_serversocket_callback(LyXServerSocket * p) +{ + fl_remove_io_callback(p->fd(), FL_READ, C_serversocket_callback); +} string const roman_font_name() {