3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
8 * \author Angus Leeming
10 * \author João Luis M. Assirati
12 * Full author contact details are available in file CREDITS.
15 #include "lyxsocket.h"
18 #include "funcrequest.h"
19 #include "LyXAction.h"
22 #include "frontends/lyx_gui.h"
24 #include "support/lyxlib.h"
25 #include "support/socktools.h"
36 // Address is the unix address for the socket.
37 // MAX_CLIENTS is the maximum number of clients
38 // that can connect at the same time.
39 LyXServerSocket::LyXServerSocket(LyXFunc * f, string const & addr)
41 fd_(lyx::support::socktools::listen(addr, MAX_CLIENTS)),
45 lyxerr << "lyx: Disabling LyX socket." << endl;
49 // These env vars are used by DVI inverse search
51 lyx::support::putenv("XEDITOR", "lyxclient -g %f %l");
52 // Needed by lyxclient
53 lyx::support::putenv("LYXSOCKET", address_);
55 lyx_gui::set_serversocket_callback(this);
56 lyxerr[Debug::LYXSERVER] << "lyx: New server socket "
57 << fd_ << ' ' << address_ << endl;
61 // Close the socket and remove the address of the filesystem.
62 LyXServerSocket::~LyXServerSocket()
65 lyx::support::unlink(address_);
66 while (!clients.empty()) close(*clients.rbegin());
67 lyxerr[Debug::LYXSERVER] << "lyx: Server socket quitting" << endl;
71 int LyXServerSocket::fd() const
77 string const & LyXServerSocket::address() const
83 // Creates a new LyXDataSocket and checks to see if the connection
84 // is OK and if the number of clients does not exceed MAX_CLIENTS
85 void LyXServerSocket::serverCallback()
87 auto_ptr<LyXDataSocket> client(new LyXDataSocket(this));
88 if (client->connected()) {
89 if (clients.size() == MAX_CLIENTS) {
90 client->writeln("BYE:Too many clients connected");
92 lyx_gui::set_datasocket_callback(client.get());
93 clients.insert(client.release());
100 // Reads and processes input from client and check
101 // if the connection has been closed
102 void LyXServerSocket::dataCallback(LyXDataSocket * client)
105 string::size_type pos;
106 bool saidbye = false;
107 while ((!saidbye) && client->readln(line)) {
108 // The protocol must be programmed here
109 // Split the key and the data
110 if ((pos = line.find(':')) == string::npos) {
111 client->writeln("ERROR:" + line + ":malformed message");
115 string const key = line.substr(0, pos);
116 if (key == "LYXCMD") {
117 string const cmd = line.substr(pos + 1);
118 func->dispatch(lyxaction.lookupFunc(cmd));
119 string const rval = func->getMessage();
120 if (func->errorStat()) {
121 client->writeln("ERROR:" + cmd + ':' + rval);
123 client->writeln("INFO:" + cmd + ':' + rval);
125 } else if (key == "HELLO") {
126 // no use for client name!
127 client->writeln("HELLO:");
128 } else if (key == "BYE") {
131 client->writeln("ERROR:unknown key " + key);
135 if (saidbye || (!client->connected())) {
141 // Removes client callback and deletes client object
142 void LyXServerSocket::close(LyXDataSocket * client)
144 lyx_gui::remove_datasocket_callback(client);
145 clients.erase(client);
150 // void LyXServerSocket::dump() const
152 // lyxerr << "LyXServerSocket debug dump.\n"
153 // << "fd = " << fd_ << ", address = " << address_ << ".\n"
154 // << "Clients: " << clients.size() << ".\n";
155 // if (!clients.empty()) {
156 // std::set<LyXDataSocket *>::const_iterator client = clients.begin();
157 // std::set<LyXDataSocket *>::const_iterator end = clients.end();
158 // for (; client != end; ++client)
159 // lyxerr << "fd = " << (*client)->fd() << "\n";
164 LyXDataSocket::LyXDataSocket(LyXServerSocket * serv)
166 fd_(lyx::support::socktools::accept(serv->fd()))
171 lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
177 LyXDataSocket::~LyXDataSocket()
180 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting." << endl;
184 LyXServerSocket * LyXDataSocket::server() const
190 int LyXDataSocket::fd() const
196 bool LyXDataSocket::connected() const
202 // Returns true if there was a complete line to input
203 bool LyXDataSocket::readln(string & line)
205 int const charbuf_size = 100;
206 char charbuf[charbuf_size]; // buffer for the ::read() system call
208 string::size_type pos;
210 // read and store characters in buffer
211 while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
212 charbuf[count] = '\0'; // turn it into a c string
216 // Error conditions. The buffer must still be
217 // processed for lines read
218 if (count == 0) { // EOF -- connection closed
219 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
220 << ": connection closed." << endl;
222 } else if ((count == -1) && (errno != EAGAIN)) { // IO error
223 lyxerr << "lyx: Data socket " << fd_
224 << ": IO error." << endl;
228 // Cut a line from buffer
229 if ((pos = buffer.find('\n')) == string::npos) {
230 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
231 << ": line not completed." << endl;
232 return false; // No complete line stored
234 line = buffer.substr(0, pos);
235 buffer = buffer.substr(pos + 1);
240 // Write a line of the form <key>:<value> to the socket
241 void LyXDataSocket::writeln(string const & line)
243 string linen(line + '\n');
244 int size = linen.size();
245 int written = ::write(fd_, linen.c_str(), size);
246 if (written < size) { // Allways mean end of connection.
247 if ((written == -1) && (errno == EPIPE)) {
248 // The program will also receive a SIGPIPE
249 // that must be catched
250 lyxerr << "lyx: Data socket " << fd_
251 << " connection closed while writing." << endl;
253 // Anything else, including errno == EAGAIN, must be
254 // considered IO error. EAGAIN should never happen
255 // when line is small
256 lyxerr << "lyx: Data socket " << fd_
257 << " IO error: " << strerror(errno);