Add missing include, due to André LASSERT change
[lyx.git] / src / ServerSocket.cpp
blob47e800a8eb49ca1b39557fa993484c95bb108a09
1 /**
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
9 * \author John Levon
10 * \author João Luis M. Assirati
12 * Full author contact details are available in file CREDITS.
15 #include <config.h>
17 #include "ServerSocket.h"
19 #include "FuncRequest.h"
20 #include "LyXAction.h"
21 #include "LyXFunc.h"
23 #include "frontends/Application.h"
25 #include "support/debug.h"
26 #include "support/environment.h"
27 #include "support/FileName.h"
28 #include "support/socktools.h"
30 #include <boost/bind.hpp>
32 #include <cerrno>
33 #include <ostream>
35 #if defined (_WIN32)
36 # include <io.h>
37 #endif
39 using namespace std;
40 using namespace lyx::support;
42 using boost::shared_ptr;
44 namespace lyx {
46 // Address is the unix address for the socket.
47 // MAX_CLIENTS is the maximum number of clients
48 // that can connect at the same time.
49 ServerSocket::ServerSocket(LyXFunc * f, FileName const & addr)
50 : func(f),
51 fd_(socktools::listen(addr, 3)),
52 address_(addr)
54 if (fd_ == -1) {
55 lyxerr << "lyx: Disabling LyX socket." << endl;
56 return;
59 // These env vars are used by DVI inverse search
60 // Needed by xdvi
61 setEnv("XEDITOR", "lyxclient -g %f %l");
62 // Needed by lyxclient
63 setEnv("LYXSOCKET", address_.absFilename());
65 theApp()->registerSocketCallback(
66 fd_,
67 boost::bind(&ServerSocket::serverCallback, this)
70 LYXERR(Debug::LYXSERVER, "lyx: New server socket "
71 << fd_ << ' ' << address_.absFilename());
75 // Close the socket and remove the address of the filesystem.
76 ServerSocket::~ServerSocket()
78 if (fd_ != -1) {
79 BOOST_ASSERT (theApp());
80 theApp()->unregisterSocketCallback(fd_);
81 if (::close(fd_) != 0)
82 lyxerr << "lyx: Server socket " << fd_
83 << " IO error on closing: " << strerror(errno);
85 address_.removeFile();
86 LYXERR(Debug::LYXSERVER, "lyx: Server socket quitting");
90 string const ServerSocket::address() const
92 return address_.absFilename();
96 // Creates a new LyXDataSocket and checks to see if the connection
97 // is OK and if the number of clients does not exceed MAX_CLIENTS
98 void ServerSocket::serverCallback()
100 int const client_fd = socktools::accept(fd_);
102 if (fd_ == -1) {
103 LYXERR(Debug::LYXSERVER, "lyx: Failed to accept new client");
104 return;
107 if (clients.size() >= MAX_CLIENTS) {
108 writeln("BYE:Too many clients connected");
109 return;
112 // Register the new client.
113 clients[client_fd] =
114 shared_ptr<LyXDataSocket>(new LyXDataSocket(client_fd));
115 theApp()->registerSocketCallback(
116 client_fd,
117 boost::bind(&ServerSocket::dataCallback,
118 this, client_fd)
123 // Reads and processes input from client and check
124 // if the connection has been closed
125 void ServerSocket::dataCallback(int fd)
127 shared_ptr<LyXDataSocket> client = clients[fd];
129 string line;
130 size_t pos;
131 bool saidbye = false;
132 while (!saidbye && client->readln(line)) {
133 // The protocol must be programmed here
134 // Split the key and the data
135 if ((pos = line.find(':')) == string::npos) {
136 client->writeln("ERROR:" + line + ":malformed message");
137 continue;
140 string const key = line.substr(0, pos);
141 if (key == "LYXCMD") {
142 string const cmd = line.substr(pos + 1);
143 func->dispatch(lyxaction.lookupFunc(cmd));
144 string const rval = to_utf8(func->getMessage());
145 if (func->errorStat()) {
146 client->writeln("ERROR:" + cmd + ':' + rval);
147 } else {
148 client->writeln("INFO:" + cmd + ':' + rval);
150 } else if (key == "HELLO") {
151 // no use for client name!
152 client->writeln("HELLO:");
153 } else if (key == "BYE") {
154 saidbye = true;
155 } else {
156 client->writeln("ERROR:unknown key " + key);
160 if (saidbye || !client->connected()) {
161 clients.erase(fd);
166 void ServerSocket::writeln(string const & line)
168 string const linen = line + '\n';
169 int const size = linen.size();
170 int const written = ::write(fd_, linen.c_str(), size);
171 if (written < size) { // Always mean end of connection.
172 if (written == -1 && errno == EPIPE) {
173 // The program will also receive a SIGPIPE
174 // that must be caught
175 lyxerr << "lyx: Server socket " << fd_
176 << " connection closed while writing." << endl;
177 } else {
178 // Anything else, including errno == EAGAIN, must be
179 // considered IO error. EAGAIN should never happen
180 // when line is small
181 lyxerr << "lyx: Server socket " << fd_
182 << " IO error: " << strerror(errno);
187 // Debug
188 // void ServerSocket::dump() const
189 // {
190 // lyxerr << "ServerSocket debug dump.\n"
191 // << "fd = " << fd_ << ", address = " << address_.absFilename() << ".\n"
192 // << "Clients: " << clients.size() << ".\n";
193 // map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
194 // map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
195 // for (; client != end; ++client)
196 // lyxerr << "fd = " << client->first << '\n';
197 // }
200 LyXDataSocket::LyXDataSocket(int fd)
201 : fd_(fd), connected_(true)
203 LYXERR(Debug::LYXSERVER, "lyx: New data socket " << fd_);
207 LyXDataSocket::~LyXDataSocket()
209 if (::close(fd_) != 0)
210 lyxerr << "lyx: Data socket " << fd_
211 << " IO error on closing: " << strerror(errno);
213 theApp()->unregisterSocketCallback(fd_);
214 LYXERR(Debug::LYXSERVER, "lyx: Data socket " << fd_ << " quitting.");
218 bool LyXDataSocket::connected() const
220 return connected_;
224 // Returns true if there was a complete line to input
225 bool LyXDataSocket::readln(string & line)
227 int const charbuf_size = 100;
228 char charbuf[charbuf_size]; // buffer for the ::read() system call
229 int count;
231 // read and store characters in buffer
232 while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
233 buffer_.append(charbuf, charbuf + count);
236 // Error conditions. The buffer must still be
237 // processed for lines read
238 if (count == 0) { // EOF -- connection closed
239 LYXERR(Debug::LYXSERVER, "lyx: Data socket " << fd_
240 << ": connection closed.");
241 connected_ = false;
242 } else if ((count == -1) && (errno != EAGAIN)) { // IO error
243 lyxerr << "lyx: Data socket " << fd_
244 << ": IO error." << endl;
245 connected_ = false;
248 // Cut a line from buffer
249 size_t pos = buffer_.find('\n');
250 if (pos == string::npos) {
251 LYXERR(Debug::LYXSERVER, "lyx: Data socket " << fd_
252 << ": line not completed.");
253 return false; // No complete line stored
255 line = buffer_.substr(0, pos);
256 buffer_.erase(0, pos + 1);
257 return true;
261 // Write a line of the form <key>:<value> to the socket
262 void LyXDataSocket::writeln(string const & line)
264 string const linen = line + '\n';
265 int const size = linen.size();
266 int const written = ::write(fd_, linen.c_str(), size);
267 if (written < size) { // Always mean end of connection.
268 if (written == -1 && errno == EPIPE) {
269 // The program will also receive a SIGPIPE
270 // that must be catched
271 lyxerr << "lyx: Data socket " << fd_
272 << " connection closed while writing." << endl;
273 } else {
274 // Anything else, including errno == EAGAIN, must be
275 // considered IO error. EAGAIN should never happen
276 // when line is small
277 lyxerr << "lyx: Data socket " << fd_
278 << " IO error: " << strerror(errno);
280 connected_ = false;
285 } // namespace lyx