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/environment.h"
25 #include "support/lyxlib.h"
26 #include "support/socktools.h"
28 #include <boost/bind.hpp>
32 using boost::shared_ptr;
39 // Address is the unix address for the socket.
40 // MAX_CLIENTS is the maximum number of clients
41 // that can connect at the same time.
42 LyXServerSocket::LyXServerSocket(LyXFunc * f, string const & addr)
44 fd_(lyx::support::socktools::listen(addr, 3)),
48 lyxerr << "lyx: Disabling LyX socket." << endl;
52 // These env vars are used by DVI inverse search
54 lyx::support::setEnv("XEDITOR", "lyxclient -g %f %l");
55 // Needed by lyxclient
56 lyx::support::setEnv("LYXSOCKET", address_);
58 lyx_gui::register_socket_callback(
60 boost::bind(&LyXServerSocket::serverCallback, this)
63 lyxerr[Debug::LYXSERVER] << "lyx: New server socket "
64 << fd_ << ' ' << address_ << endl;
68 // Close the socket and remove the address of the filesystem.
69 LyXServerSocket::~LyXServerSocket()
71 lyx_gui::unregister_socket_callback(fd_);
73 lyx::support::unlink(address_);
74 lyxerr[Debug::LYXSERVER] << "lyx: Server socket quitting" << endl;
78 string const & LyXServerSocket::address() const
84 // Creates a new LyXDataSocket and checks to see if the connection
85 // is OK and if the number of clients does not exceed MAX_CLIENTS
86 void LyXServerSocket::serverCallback()
88 int const client_fd = lyx::support::socktools::accept(fd_);
91 lyxerr[Debug::LYXSERVER] << "lyx: Failed to accept new client"
96 if (clients.size() >= MAX_CLIENTS) {
97 writeln("BYE:Too many clients connected");
101 // Register the new client.
103 shared_ptr<LyXDataSocket>(new LyXDataSocket(client_fd));
104 lyx_gui::register_socket_callback(
106 boost::bind(&LyXServerSocket::dataCallback,
112 // Reads and processes input from client and check
113 // if the connection has been closed
114 void LyXServerSocket::dataCallback(int fd)
116 shared_ptr<LyXDataSocket> client = clients[fd];
119 string::size_type pos;
120 bool saidbye = false;
121 while ((!saidbye) && client->readln(line)) {
122 // The protocol must be programmed here
123 // Split the key and the data
124 if ((pos = line.find(':')) == string::npos) {
125 client->writeln("ERROR:" + line + ":malformed message");
129 string const key = line.substr(0, pos);
130 if (key == "LYXCMD") {
131 string const cmd = line.substr(pos + 1);
132 func->dispatch(lyxaction.lookupFunc(cmd));
133 string const rval = func->getMessage();
134 if (func->errorStat()) {
135 client->writeln("ERROR:" + cmd + ':' + rval);
137 client->writeln("INFO:" + cmd + ':' + rval);
139 } else if (key == "HELLO") {
140 // no use for client name!
141 client->writeln("HELLO:");
142 } else if (key == "BYE") {
145 client->writeln("ERROR:unknown key " + key);
149 if (saidbye || (!client->connected())) {
155 void LyXServerSocket::writeln(string const & line)
157 string const linen(line + '\n');
158 int const size = linen.size();
159 int const written = ::write(fd_, linen.c_str(), size);
160 if (written < size) { // Allways mean end of connection.
161 if ((written == -1) && (errno == EPIPE)) {
162 // The program will also receive a SIGPIPE
163 // that must be caught
164 lyxerr << "lyx: Server socket " << fd_
165 << " connection closed while writing." << endl;
167 // Anything else, including errno == EAGAIN, must be
168 // considered IO error. EAGAIN should never happen
169 // when line is small
170 lyxerr << "lyx: Server socket " << fd_
171 << " IO error: " << strerror(errno);
177 // void LyXServerSocket::dump() const
179 // lyxerr << "LyXServerSocket debug dump.\n"
180 // << "fd = " << fd_ << ", address = " << address_ << ".\n"
181 // << "Clients: " << clients.size() << ".\n";
182 // std::map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
183 // std::map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
184 // for (; client != end; ++client)
185 // lyxerr << "fd = " << client->first << '\n';
189 LyXDataSocket::LyXDataSocket(int fd)
190 : fd_(fd), connected_(true)
192 lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
196 LyXDataSocket::~LyXDataSocket()
200 lyx_gui::unregister_socket_callback(fd_);
201 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting."
206 bool LyXDataSocket::connected() const
212 // Returns true if there was a complete line to input
213 bool LyXDataSocket::readln(string & line)
215 int const charbuf_size = 100;
216 char charbuf[charbuf_size]; // buffer for the ::read() system call
219 // read and store characters in buffer
220 while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
221 buffer_.append(charbuf, charbuf + count);
224 // Error conditions. The buffer must still be
225 // processed for lines read
226 if (count == 0) { // EOF -- connection closed
227 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
228 << ": connection closed." << endl;
230 } else if ((count == -1) && (errno != EAGAIN)) { // IO error
231 lyxerr << "lyx: Data socket " << fd_
232 << ": IO error." << endl;
236 // Cut a line from buffer
237 string::size_type pos = buffer_.find('\n');
238 if (pos == string::npos) {
239 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
240 << ": line not completed." << endl;
241 return false; // No complete line stored
243 line = buffer_.substr(0, pos);
244 buffer_.erase(0, pos + 1);
249 // Write a line of the form <key>:<value> to the socket
250 void LyXDataSocket::writeln(string const & line)
252 string const linen(line + '\n');
253 int const size = linen.size();
254 int const written = ::write(fd_, linen.c_str(), size);
255 if (written < size) { // Allways mean end of connection.
256 if ((written == -1) && (errno == EPIPE)) {
257 // The program will also receive a SIGPIPE
258 // that must be catched
259 lyxerr << "lyx: Data socket " << fd_
260 << " connection closed while writing." << endl;
262 // Anything else, including errno == EAGAIN, must be
263 // considered IO error. EAGAIN should never happen
264 // when line is small
265 lyxerr << "lyx: Data socket " << fd_
266 << " IO error: " << strerror(errno);