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 "lyxsocket.h"
20 #include "funcrequest.h"
21 #include "LyXAction.h"
24 #include "frontends/Application.h"
26 #include "support/environment.h"
27 #include "support/filename.h"
28 #include "support/lyxlib.h"
29 #include "support/socktools.h"
31 #include <boost/bind.hpp>
39 using boost::shared_ptr;
48 // Address is the unix address for the socket.
49 // MAX_CLIENTS is the maximum number of clients
50 // that can connect at the same time.
51 LyXServerSocket::LyXServerSocket(LyXFunc * f, string const & addr)
53 fd_(support::socktools::listen(addr, 3)),
57 lyxerr << "lyx: Disabling LyX socket." << endl;
61 // These env vars are used by DVI inverse search
63 support::setEnv("XEDITOR", "lyxclient -g %f %l");
64 // Needed by lyxclient
65 support::setEnv("LYXSOCKET", address_);
67 theApp->registerSocketCallback(
69 boost::bind(&LyXServerSocket::serverCallback, this)
72 lyxerr[Debug::LYXSERVER] << "lyx: New server socket "
73 << fd_ << ' ' << address_ << endl;
77 // Close the socket and remove the address of the filesystem.
78 LyXServerSocket::~LyXServerSocket()
81 BOOST_ASSERT (theApp);
82 theApp->unregisterSocketCallback(fd_);
83 if (::close(fd_) != 0)
84 lyxerr << "lyx: Server socket " << fd_
85 << " IO error on closing: " << strerror(errno);
87 support::unlink(support::FileName(address_));
88 lyxerr[Debug::LYXSERVER] << "lyx: Server socket quitting" << endl;
92 string const & LyXServerSocket::address() const
98 // Creates a new LyXDataSocket and checks to see if the connection
99 // is OK and if the number of clients does not exceed MAX_CLIENTS
100 void LyXServerSocket::serverCallback()
102 int const client_fd = support::socktools::accept(fd_);
105 lyxerr[Debug::LYXSERVER] << "lyx: Failed to accept new client"
110 if (clients.size() >= MAX_CLIENTS) {
111 writeln("BYE:Too many clients connected");
115 // Register the new client.
117 shared_ptr<LyXDataSocket>(new LyXDataSocket(client_fd));
118 theApp->registerSocketCallback(
120 boost::bind(&LyXServerSocket::dataCallback,
126 // Reads and processes input from client and check
127 // if the connection has been closed
128 void LyXServerSocket::dataCallback(int fd)
130 shared_ptr<LyXDataSocket> client = clients[fd];
133 string::size_type pos;
134 bool saidbye = false;
135 while ((!saidbye) && client->readln(line)) {
136 // The protocol must be programmed here
137 // Split the key and the data
138 if ((pos = line.find(':')) == string::npos) {
139 client->writeln("ERROR:" + line + ":malformed message");
143 string const key = line.substr(0, pos);
144 if (key == "LYXCMD") {
145 string const cmd = line.substr(pos + 1);
146 func->dispatch(lyxaction.lookupFunc(cmd));
147 string const rval = to_utf8(func->getMessage());
148 if (func->errorStat()) {
149 client->writeln("ERROR:" + cmd + ':' + rval);
151 client->writeln("INFO:" + cmd + ':' + rval);
153 } else if (key == "HELLO") {
154 // no use for client name!
155 client->writeln("HELLO:");
156 } else if (key == "BYE") {
159 client->writeln("ERROR:unknown key " + key);
163 if (saidbye || (!client->connected())) {
169 void LyXServerSocket::writeln(string const & line)
171 string const linen(line + '\n');
172 int const size = linen.size();
173 int const written = ::write(fd_, linen.c_str(), size);
174 if (written < size) { // Always mean end of connection.
175 if ((written == -1) && (errno == EPIPE)) {
176 // The program will also receive a SIGPIPE
177 // that must be caught
178 lyxerr << "lyx: Server socket " << fd_
179 << " connection closed while writing." << endl;
181 // Anything else, including errno == EAGAIN, must be
182 // considered IO error. EAGAIN should never happen
183 // when line is small
184 lyxerr << "lyx: Server socket " << fd_
185 << " IO error: " << strerror(errno);
191 // void LyXServerSocket::dump() const
193 // lyxerr << "LyXServerSocket debug dump.\n"
194 // << "fd = " << fd_ << ", address = " << address_ << ".\n"
195 // << "Clients: " << clients.size() << ".\n";
196 // std::map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
197 // std::map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
198 // for (; client != end; ++client)
199 // lyxerr << "fd = " << client->first << '\n';
203 LyXDataSocket::LyXDataSocket(int fd)
204 : fd_(fd), connected_(true)
206 lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
210 LyXDataSocket::~LyXDataSocket()
212 if (::close(fd_) != 0)
213 lyxerr << "lyx: Data socket " << fd_
214 << " IO error on closing: " << strerror(errno);
216 theApp->unregisterSocketCallback(fd_);
217 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting."
222 bool LyXDataSocket::connected() const
228 // Returns true if there was a complete line to input
229 bool LyXDataSocket::readln(string & line)
231 int const charbuf_size = 100;
232 char charbuf[charbuf_size]; // buffer for the ::read() system call
235 // read and store characters in buffer
236 while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
237 buffer_.append(charbuf, charbuf + count);
240 // Error conditions. The buffer must still be
241 // processed for lines read
242 if (count == 0) { // EOF -- connection closed
243 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
244 << ": connection closed." << endl;
246 } else if ((count == -1) && (errno != EAGAIN)) { // IO error
247 lyxerr << "lyx: Data socket " << fd_
248 << ": IO error." << endl;
252 // Cut a line from buffer
253 string::size_type pos = buffer_.find('\n');
254 if (pos == string::npos) {
255 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
256 << ": line not completed." << endl;
257 return false; // No complete line stored
259 line = buffer_.substr(0, pos);
260 buffer_.erase(0, pos + 1);
265 // Write a line of the form <key>:<value> to the socket
266 void LyXDataSocket::writeln(string const & line)
268 string const linen(line + '\n');
269 int const size = linen.size();
270 int const written = ::write(fd_, linen.c_str(), size);
271 if (written < size) { // Always mean end of connection.
272 if ((written == -1) && (errno == EPIPE)) {
273 // The program will also receive a SIGPIPE
274 // that must be catched
275 lyxerr << "lyx: Data socket " << fd_
276 << " connection closed while writing." << endl;
278 // Anything else, including errno == EAGAIN, must be
279 // considered IO error. EAGAIN should never happen
280 // when line is small
281 lyxerr << "lyx: Data socket " << fd_
282 << " IO error: " << strerror(errno);