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(Debug::LYXSERVER
, "lyx: Disabling LyX socket.");
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
)
86 address_
.removeFile();
87 LYXERR(Debug::LYXSERVER
, "lyx: Server socket quitting");
91 string
const ServerSocket::address() const
93 return address_
.absFilename();
97 // Creates a new LyXDataSocket and checks to see if the connection
98 // is OK and if the number of clients does not exceed MAX_CLIENTS
99 void ServerSocket::serverCallback()
101 int const client_fd
= socktools::accept(fd_
);
104 LYXERR(Debug::LYXSERVER
, "lyx: Failed to accept new client");
108 if (clients
.size() >= MAX_CLIENTS
) {
109 writeln("BYE:Too many clients connected");
113 // Register the new client.
115 shared_ptr
<LyXDataSocket
>(new LyXDataSocket(client_fd
));
116 theApp()->registerSocketCallback(
118 boost::bind(&ServerSocket::dataCallback
,
124 // Reads and processes input from client and check
125 // if the connection has been closed
126 void ServerSocket::dataCallback(int fd
)
128 shared_ptr
<LyXDataSocket
> client
= clients
[fd
];
132 bool saidbye
= false;
133 while (!saidbye
&& client
->readln(line
)) {
134 // The protocol must be programmed here
135 // Split the key and the data
136 if ((pos
= line
.find(':')) == string::npos
) {
137 client
->writeln("ERROR:" + line
+ ":malformed message");
141 string
const key
= line
.substr(0, pos
);
142 if (key
== "LYXCMD") {
143 string
const cmd
= line
.substr(pos
+ 1);
144 func
->dispatch(lyxaction
.lookupFunc(cmd
));
145 string
const rval
= to_utf8(func
->getMessage());
146 if (func
->errorStat()) {
147 client
->writeln("ERROR:" + cmd
+ ':' + rval
);
149 client
->writeln("INFO:" + cmd
+ ':' + rval
);
151 } else if (key
== "HELLO") {
152 // no use for client name!
153 client
->writeln("HELLO:");
154 } else if (key
== "BYE") {
157 client
->writeln("ERROR:unknown key " + key
);
161 if (saidbye
|| !client
->connected()) {
167 void ServerSocket::writeln(string
const & line
)
169 string
const linen
= line
+ '\n';
170 int const size
= linen
.size();
171 int const written
= ::write(fd_
, linen
.c_str(), size
);
172 if (written
< size
) { // Always mean end of connection.
173 if (written
== -1 && errno
== EPIPE
) {
174 // The program will also receive a SIGPIPE
175 // that must be caught
176 lyxerr
<< "lyx: Server socket " << fd_
177 << " connection closed while writing." << endl
;
179 // Anything else, including errno == EAGAIN, must be
180 // considered IO error. EAGAIN should never happen
181 // when line is small
182 lyxerr
<< "lyx: Server socket " << fd_
183 << " IO error: " << strerror(errno
);
189 // void ServerSocket::dump() const
191 // lyxerr << "ServerSocket debug dump.\n"
192 // << "fd = " << fd_ << ", address = " << address_.absFilename() << ".\n"
193 // << "Clients: " << clients.size() << ".\n";
194 // map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
195 // map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
196 // for (; client != end; ++client)
197 // lyxerr << "fd = " << client->first << '\n';
201 LyXDataSocket::LyXDataSocket(int fd
)
202 : fd_(fd
), connected_(true)
204 LYXERR(Debug::LYXSERVER
, "lyx: New data socket " << fd_
);
208 LyXDataSocket::~LyXDataSocket()
210 if (::close(fd_
) != 0)
211 lyxerr
<< "lyx: Data socket " << fd_
212 << " IO error on closing: " << strerror(errno
);
214 theApp()->unregisterSocketCallback(fd_
);
215 LYXERR(Debug::LYXSERVER
, "lyx: Data socket " << fd_
<< " quitting.");
219 bool LyXDataSocket::connected() const
225 // Returns true if there was a complete line to input
226 bool LyXDataSocket::readln(string
& line
)
228 int const charbuf_size
= 100;
229 char charbuf
[charbuf_size
]; // buffer for the ::read() system call
232 // read and store characters in buffer
233 while ((count
= ::read(fd_
, charbuf
, charbuf_size
- 1)) > 0) {
234 buffer_
.append(charbuf
, charbuf
+ count
);
237 // Error conditions. The buffer must still be
238 // processed for lines read
239 if (count
== 0) { // EOF -- connection closed
240 LYXERR(Debug::LYXSERVER
, "lyx: Data socket " << fd_
241 << ": connection closed.");
243 } else if ((count
== -1) && (errno
!= EAGAIN
)) { // IO error
244 lyxerr
<< "lyx: Data socket " << fd_
245 << ": IO error." << endl
;
249 // Cut a line from buffer
250 size_t pos
= buffer_
.find('\n');
251 if (pos
== string::npos
) {
252 LYXERR(Debug::LYXSERVER
, "lyx: Data socket " << fd_
253 << ": line not completed.");
254 return false; // No complete line stored
256 line
= buffer_
.substr(0, pos
);
257 buffer_
.erase(0, pos
+ 1);
262 // Write a line of the form <key>:<value> to the socket
263 void LyXDataSocket::writeln(string
const & line
)
265 string
const linen
= line
+ '\n';
266 int const size
= linen
.size();
267 int const written
= ::write(fd_
, linen
.c_str(), size
);
268 if (written
< size
) { // Always mean end of connection.
269 if (written
== -1 && errno
== EPIPE
) {
270 // The program will also receive a SIGPIPE
271 // that must be catched
272 lyxerr
<< "lyx: Data socket " << fd_
273 << " connection closed while writing." << endl
;
275 // Anything else, including errno == EAGAIN, must be
276 // considered IO error. EAGAIN should never happen
277 // when line is small
278 lyxerr
<< "lyx: Data socket " << fd_
279 << " IO error: " << strerror(errno
);