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 "FuncRequest.h"
20 #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/socktools.h"
30 #include <boost/bind.hpp>
40 using namespace lyx::support
;
42 using boost::shared_ptr
;
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
)
51 fd_(socktools::listen(addr
, 3)),
55 lyxerr
<< "lyx: Disabling LyX socket." << endl
;
59 // These env vars are used by DVI inverse search
61 setEnv("XEDITOR", "lyxclient -g %f %l");
62 // Needed by lyxclient
63 setEnv("LYXSOCKET", address_
.absFilename());
65 theApp()->registerSocketCallback(
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()
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_
);
103 LYXERR(Debug::LYXSERVER
, "lyx: Failed to accept new client");
107 if (clients
.size() >= MAX_CLIENTS
) {
108 writeln("BYE:Too many clients connected");
112 // Register the new client.
114 shared_ptr
<LyXDataSocket
>(new LyXDataSocket(client_fd
));
115 theApp()->registerSocketCallback(
117 boost::bind(&ServerSocket::dataCallback
,
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
];
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");
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
);
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") {
156 client
->writeln("ERROR:unknown key " + key
);
160 if (saidbye
|| !client
->connected()) {
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
;
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
);
188 // void ServerSocket::dump() const
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';
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
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
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.");
242 } else if ((count
== -1) && (errno
!= EAGAIN
)) { // IO error
243 lyxerr
<< "lyx: Data socket " << fd_
244 << ": IO error." << endl
;
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);
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
;
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
);