2 * \file ServerSocket.cpp
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.
17 #include "ServerSocket.h"
19 #include "DispatchResult.h"
20 #include "FuncRequest.h"
21 #include "LyXAction.h"
23 #include "frontends/Application.h"
25 #include "support/debug.h"
26 #include "support/environment.h"
27 #include "support/FileName.h"
28 #include "support/lassert.h"
29 #include "support/socktools.h"
31 #include <boost/assert.hpp>
46 using namespace lyx::support;
51 // Address is the unix address for the socket.
52 // MAX_CLIENTS is the maximum number of clients
53 // that can connect at the same time.
54 ServerSocket::ServerSocket(FileName const & addr)
55 : fd_(socktools::listen(addr, 3)),
59 LYXERR(Debug::LYXSERVER, "lyx: Disabling LyX socket.");
63 // These env vars are used by DVI inverse search
65 setEnv("XEDITOR", "lyxclient -g %f %l");
66 // Needed by lyxclient
67 setEnv("LYXSOCKET", address_.absFileName());
69 theApp()->registerSocketCallback(
71 bind(&ServerSocket::serverCallback, this)
74 LYXERR(Debug::LYXSERVER, "lyx: New server socket "
75 << fd_ << ' ' << address_.absFileName());
79 // Close the socket and remove the address of the filesystem.
80 ServerSocket::~ServerSocket()
83 BOOST_ASSERT (theApp());
84 theApp()->unregisterSocketCallback(fd_);
85 if (::close(fd_) != 0)
86 lyxerr << "lyx: Server socket " << fd_
87 << " IO error on closing: " << strerror(errno)
90 address_.removeFile();
91 LYXERR(Debug::LYXSERVER, "lyx: Server socket quitting");
95 string const ServerSocket::address() const
97 return address_.absFileName();
101 // Creates a new LyXDataSocket and checks to see if the connection
102 // is OK and if the number of clients does not exceed MAX_CLIENTS
103 void ServerSocket::serverCallback()
105 if (clients.size() >= MAX_CLIENTS) {
106 writeln("BYE:Too many clients connected");
110 int const client_fd = socktools::accept(fd_);
113 LYXERR(Debug::LYXSERVER, "lyx: Failed to accept new client");
117 // Register the new client.
118 clients[client_fd] = make_shared<LyXDataSocket>(client_fd);
119 theApp()->registerSocketCallback(
121 bind(&ServerSocket::dataCallback,
127 // Reads and processes input from client and check
128 // if the connection has been closed
129 void ServerSocket::dataCallback(int fd)
131 map<int, shared_ptr<LyXDataSocket> >::const_iterator it = clients.find(fd);
132 if (it == clients.end())
134 shared_ptr<LyXDataSocket> client = it->second;
136 bool saidbye = false;
137 while (!saidbye && client->readln(line)) {
138 // The protocol must be programmed here
139 // Split the key and the data
141 if ((pos = line.find(':')) == string::npos) {
142 client->writeln("ERROR:" + line + ":malformed message");
146 string const key = line.substr(0, pos);
147 if (key == "LYXCMD") {
148 string const cmd = line.substr(pos + 1);
149 FuncRequest fr(lyxaction.lookupFunc(cmd));
150 fr.setOrigin(FuncRequest::LYXSERVER);
152 theApp()->dispatch(fr, dr);
153 string const rval = to_utf8(dr.message());
155 client->writeln("ERROR:" + cmd + ':' + rval);
157 client->writeln("INFO:" + cmd + ':' + rval);
158 } else if (key == "HELLO") {
159 // no use for client name!
160 client->writeln("HELLO:");
161 } else if (key == "BYE") {
164 client->writeln("ERROR:unknown key " + key);
168 if (saidbye || !client->connected()) {
174 void ServerSocket::writeln(string const & line)
176 string const linen = line + '\n';
177 int const size = linen.size();
178 int const written = ::write(fd_, linen.c_str(), size);
179 if (written < size) { // Always mean end of connection.
180 if (written == -1 && errno == EPIPE) {
181 // The program will also receive a SIGPIPE
182 // that must be caught
183 lyxerr << "lyx: Server socket " << fd_
184 << " connection closed while writing." << endl;
186 // Anything else, including errno == EAGAIN, must be
187 // considered IO error. EAGAIN should never happen
188 // when line is small
189 lyxerr << "lyx: Server socket " << fd_
190 << " IO error: " << strerror(errno);
196 // void ServerSocket::dump() const
198 // lyxerr << "ServerSocket debug dump.\n"
199 // << "fd = " << fd_ << ", address = " << address_.absFileName() << ".\n"
200 // << "Clients: " << clients.size() << ".\n";
201 // map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
202 // map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
203 // for (; client != end; ++client)
204 // lyxerr << "fd = " << client->first << '\n';
208 LyXDataSocket::LyXDataSocket(int fd)
209 : fd_(fd), connected_(true)
211 LYXERR(Debug::LYXSERVER, "lyx: New data socket " << fd_);
215 LyXDataSocket::~LyXDataSocket()
217 if (::close(fd_) != 0)
218 lyxerr << "lyx: Data socket " << fd_
219 << " IO error on closing: " << strerror(errno);
221 theApp()->unregisterSocketCallback(fd_);
222 LYXERR(Debug::LYXSERVER, "lyx: Data socket " << fd_ << " quitting.");
226 bool LyXDataSocket::connected() const
232 // Returns true if there was a complete line to input
233 bool LyXDataSocket::readln(string & line)
235 int const charbuf_size = 100;
236 char charbuf[charbuf_size]; // buffer for the ::read() system call
239 // read and store characters in buffer
240 while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
241 buffer_.append(charbuf, charbuf + count);
244 // Error conditions. The buffer must still be
245 // processed for lines read
246 if (count == 0) { // EOF -- connection closed
247 LYXERR(Debug::LYXSERVER, "lyx: Data socket " << fd_
248 << ": connection closed.");
250 } else if ((count == -1) && (errno != EAGAIN)) { // IO error
251 lyxerr << "lyx: Data socket " << fd_
252 << ": IO error." << endl;
256 // Cut a line from buffer
257 size_t pos = buffer_.find('\n');
258 if (pos == string::npos) {
259 LYXERR(Debug::LYXSERVER, "lyx: Data socket " << fd_
260 << ": line not completed.");
261 return false; // No complete line stored
263 line = buffer_.substr(0, pos);
264 buffer_.erase(0, pos + 1);
269 // Write a line of the form <key>:<value> to the socket
270 void LyXDataSocket::writeln(string const & line)
272 string const linen = line + '\n';
273 int const size = linen.size();
274 int const written = ::write(fd_, linen.c_str(), size);
275 if (written < size) { // Always mean end of connection.
276 if (written == -1 && errno == EPIPE) {
277 // The program will also receive a SIGPIPE
278 // that must be catched
279 lyxerr << "lyx: Data socket " << fd_
280 << " connection closed while writing." << endl;
282 // Anything else, including errno == EAGAIN, must be
283 // considered IO error. EAGAIN should never happen
284 // when line is small
285 lyxerr << "lyx: Data socket " << fd_
286 << " IO error: " << strerror(errno);