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.
19 #include "lyxsocket.h"
22 #if !(defined(HAVE_READ) && defined(HAVE_WRITE) && defined(HAVE_CLOSE))
23 // We provide stub classes to disables the sockets.
25 LyXServerSocket::LyXServerSocket(LyXFunc *, std::string const &)
29 LyXServerSocket::~LyXServerSocket()
33 std::string const & LyXServerSocket::address() const
39 void LyXServerSocket::serverCallback()
43 void LyXServerSocket::dataCallback(int)
47 void LyXServerSocket::writeln(std::string const &)
51 LyXDataSocket::LyXDataSocket(int)
55 LyXDataSocket::~LyXDataSocket()
59 bool LyXDataSocket::connected() const
65 bool LyXDataSocket::readln(std::string &)
71 void LyXDataSocket::writeln(std::string const &)
74 #else // defined(HAVE_READ) && defined(HAVE_WRITE) && defined(HAVE_CLOSE)
78 #include "funcrequest.h"
79 #include "LyXAction.h"
82 #include "frontends/lyx_gui.h"
84 #include "support/environment.h"
85 #include "support/lyxlib.h"
86 #include "support/socktools.h"
88 #include <boost/bind.hpp>
92 using boost::shared_ptr;
99 // Address is the unix address for the socket.
100 // MAX_CLIENTS is the maximum number of clients
101 // that can connect at the same time.
102 LyXServerSocket::LyXServerSocket(LyXFunc * f, string const & addr)
104 fd_(lyx::support::socktools::listen(addr, 3)),
108 lyxerr << "lyx: Disabling LyX socket." << endl;
112 // These env vars are used by DVI inverse search
114 lyx::support::setEnv("XEDITOR", "lyxclient -g %f %l");
115 // Needed by lyxclient
116 lyx::support::setEnv("LYXSOCKET", address_);
118 lyx_gui::register_socket_callback(
120 boost::bind(&LyXServerSocket::serverCallback, this)
123 lyxerr[Debug::LYXSERVER] << "lyx: New server socket "
124 << fd_ << ' ' << address_ << endl;
128 // Close the socket and remove the address of the filesystem.
129 LyXServerSocket::~LyXServerSocket()
131 lyx_gui::unregister_socket_callback(fd_);
133 lyx::support::unlink(address_);
134 lyxerr[Debug::LYXSERVER] << "lyx: Server socket quitting" << endl;
138 string const & LyXServerSocket::address() const
144 // Creates a new LyXDataSocket and checks to see if the connection
145 // is OK and if the number of clients does not exceed MAX_CLIENTS
146 void LyXServerSocket::serverCallback()
148 int const client_fd = lyx::support::socktools::accept(fd_);
151 lyxerr[Debug::LYXSERVER] << "lyx: Failed to accept new client"
156 if (clients.size() >= MAX_CLIENTS) {
157 writeln("BYE:Too many clients connected");
161 // Register the new client.
163 shared_ptr<LyXDataSocket>(new LyXDataSocket(client_fd));
164 lyx_gui::register_socket_callback(
166 boost::bind(&LyXServerSocket::dataCallback,
172 // Reads and processes input from client and check
173 // if the connection has been closed
174 void LyXServerSocket::dataCallback(int fd)
176 shared_ptr<LyXDataSocket> client = clients[fd];
179 string::size_type pos;
180 bool saidbye = false;
181 while ((!saidbye) && client->readln(line)) {
182 // The protocol must be programmed here
183 // Split the key and the data
184 if ((pos = line.find(':')) == string::npos) {
185 client->writeln("ERROR:" + line + ":malformed message");
189 string const key = line.substr(0, pos);
190 if (key == "LYXCMD") {
191 string const cmd = line.substr(pos + 1);
192 func->dispatch(lyxaction.lookupFunc(cmd));
193 string const rval = func->getMessage();
194 if (func->errorStat()) {
195 client->writeln("ERROR:" + cmd + ':' + rval);
197 client->writeln("INFO:" + cmd + ':' + rval);
199 } else if (key == "HELLO") {
200 // no use for client name!
201 client->writeln("HELLO:");
202 } else if (key == "BYE") {
205 client->writeln("ERROR:unknown key " + key);
209 if (saidbye || (!client->connected())) {
215 void LyXServerSocket::writeln(string const & line)
217 string const linen(line + '\n');
218 int const size = linen.size();
219 int const written = ::write(fd_, linen.c_str(), size);
220 if (written < size) { // Allways mean end of connection.
221 if ((written == -1) && (errno == EPIPE)) {
222 // The program will also receive a SIGPIPE
223 // that must be caught
224 lyxerr << "lyx: Server socket " << fd_
225 << " connection closed while writing." << endl;
227 // Anything else, including errno == EAGAIN, must be
228 // considered IO error. EAGAIN should never happen
229 // when line is small
230 lyxerr << "lyx: Server socket " << fd_
231 << " IO error: " << strerror(errno);
237 // void LyXServerSocket::dump() const
239 // lyxerr << "LyXServerSocket debug dump.\n"
240 // << "fd = " << fd_ << ", address = " << address_ << ".\n"
241 // << "Clients: " << clients.size() << ".\n";
242 // std::map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
243 // std::map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
244 // for (; client != end; ++client)
245 // lyxerr << "fd = " << client->first << '\n';
249 LyXDataSocket::LyXDataSocket(int fd)
250 : fd_(fd), connected_(true)
252 lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
256 LyXDataSocket::~LyXDataSocket()
260 lyx_gui::unregister_socket_callback(fd_);
261 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting."
266 bool LyXDataSocket::connected() const
272 // Returns true if there was a complete line to input
273 bool LyXDataSocket::readln(string & line)
275 int const charbuf_size = 100;
276 char charbuf[charbuf_size]; // buffer for the ::read() system call
279 // read and store characters in buffer
280 while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
281 buffer_.append(charbuf, charbuf + count);
284 // Error conditions. The buffer must still be
285 // processed for lines read
286 if (count == 0) { // EOF -- connection closed
287 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
288 << ": connection closed." << endl;
290 } else if ((count == -1) && (errno != EAGAIN)) { // IO error
291 lyxerr << "lyx: Data socket " << fd_
292 << ": IO error." << endl;
296 // Cut a line from buffer
297 string::size_type pos = buffer_.find('\n');
298 if (pos == string::npos) {
299 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
300 << ": line not completed." << endl;
301 return false; // No complete line stored
303 line = buffer_.substr(0, pos);
304 buffer_.erase(0, pos + 1);
309 // Write a line of the form <key>:<value> to the socket
310 void LyXDataSocket::writeln(string const & line)
312 string const linen(line + '\n');
313 int const size = linen.size();
314 int const written = ::write(fd_, linen.c_str(), size);
315 if (written < size) { // Allways mean end of connection.
316 if ((written == -1) && (errno == EPIPE)) {
317 // The program will also receive a SIGPIPE
318 // that must be catched
319 lyxerr << "lyx: Data socket " << fd_
320 << " connection closed while writing." << endl;
322 // Anything else, including errno == EAGAIN, must be
323 // considered IO error. EAGAIN should never happen
324 // when line is small
325 lyxerr << "lyx: Data socket " << fd_
326 << " IO error: " << strerror(errno);
332 #endif // defined(HAVE_READ) && defined(HAVE_WRITE) && defined(HAVE_CLOSE)