]> git.lyx.org Git - lyx.git/blob - src/lyxsocket.C
73f8b210694e6467c7a7075bc31c6750bf1f6c7c
[lyx.git] / src / lyxsocket.C
1 /**
2  * \file lyxsocket.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
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
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include <config.h>
18
19 #include "lyxsocket.h"
20
21
22 #if !(defined(HAVE_READ) && defined(HAVE_WRITE) && defined(HAVE_CLOSE))
23 // We provide stub classes to disables the sockets.
24
25 LyXServerSocket::LyXServerSocket(LyXFunc *, std::string const &)
26 {}
27
28
29 LyXServerSocket::~LyXServerSocket()
30 {}
31
32
33 std::string const & LyXServerSocket::address() const
34 {
35         return address_;
36 }
37
38
39 void LyXServerSocket::serverCallback()
40 {}
41
42
43 void LyXServerSocket::dataCallback(int)
44 {}
45
46
47 void LyXServerSocket::writeln(std::string const &)
48 {}
49
50
51 LyXDataSocket::LyXDataSocket(int)
52 {}
53
54
55 LyXDataSocket::~LyXDataSocket()
56 {}
57
58
59 bool LyXDataSocket::connected() const
60 {
61         return false;
62 }
63
64
65 bool LyXDataSocket::readln(std::string &)
66 {
67         return false;
68 }
69
70
71 void LyXDataSocket::writeln(std::string const &)
72 {}
73
74 #else // defined(HAVE_READ) && defined(HAVE_WRITE) && defined(HAVE_CLOSE)
75
76
77 #include "debug.h"
78 #include "funcrequest.h"
79 #include "LyXAction.h"
80 #include "lyxfunc.h"
81
82 #include "frontends/lyx_gui.h"
83
84 #include "support/environment.h"
85 #include "support/lyxlib.h"
86 #include "support/socktools.h"
87
88 #include <boost/bind.hpp>
89
90 #include <cerrno>
91
92 using boost::shared_ptr;
93
94 using std::auto_ptr;
95 using std::endl;
96 using std::string;
97
98
99 // Address is the unix address for the socket.
100 // MAX_CLIENTS is the maximum number of clients
101 // that can connect at the same time.
102 LyXServerSocket::LyXServerSocket(LyXFunc * f, string const & addr)
103         : func(f),
104           fd_(lyx::support::socktools::listen(addr, 3)),
105           address_(addr)
106 {
107         if (fd_ == -1) {
108                 lyxerr << "lyx: Disabling LyX socket." << endl;
109                 return;
110         }
111
112         // These env vars are used by DVI inverse search
113         // Needed by xdvi
114         lyx::support::setEnv("XEDITOR", "lyxclient -g %f %l");
115         // Needed by lyxclient
116         lyx::support::setEnv("LYXSOCKET", address_);
117
118         lyx_gui::register_socket_callback(
119                 fd_,
120                 boost::bind(&LyXServerSocket::serverCallback, this)
121                 );
122
123         lyxerr[Debug::LYXSERVER] << "lyx: New server socket "
124                                  << fd_ << ' ' << address_ << endl;
125 }
126
127
128 // Close the socket and remove the address of the filesystem.
129 LyXServerSocket::~LyXServerSocket()
130 {
131         lyx_gui::unregister_socket_callback(fd_);
132         ::close(fd_);
133         lyx::support::unlink(address_);
134         lyxerr[Debug::LYXSERVER] << "lyx: Server socket quitting" << endl;
135 }
136
137
138 string const & LyXServerSocket::address() const
139 {
140         return address_;
141 }
142
143
144 // Creates a new LyXDataSocket and checks to see if the connection
145 // is OK and if the number of clients does not exceed MAX_CLIENTS
146 void LyXServerSocket::serverCallback()
147 {
148         int const client_fd = lyx::support::socktools::accept(fd_);
149
150         if (fd_ == -1) {
151                 lyxerr[Debug::LYXSERVER] << "lyx: Failed to accept new client"
152                                          << endl;
153                 return;
154         }
155
156         if (clients.size() >= MAX_CLIENTS) {
157                 writeln("BYE:Too many clients connected");
158                 return;
159         }
160
161         // Register the new client.
162         clients[client_fd] =
163                 shared_ptr<LyXDataSocket>(new LyXDataSocket(client_fd));
164         lyx_gui::register_socket_callback(
165                 client_fd,
166                 boost::bind(&LyXServerSocket::dataCallback,
167                             this, client_fd)
168                 );
169 }
170
171
172 // Reads and processes input from client and check
173 // if the connection has been closed
174 void LyXServerSocket::dataCallback(int fd)
175 {
176         shared_ptr<LyXDataSocket> client = clients[fd];
177
178         string line;
179         string::size_type pos;
180         bool saidbye = false;
181         while ((!saidbye) && client->readln(line)) {
182                 // The protocol must be programmed here
183                 // Split the key and the data
184                 if ((pos = line.find(':')) == string::npos) {
185                         client->writeln("ERROR:" + line + ":malformed message");
186                         continue;
187                 }
188
189                 string const key = line.substr(0, pos);
190                 if (key == "LYXCMD") {
191                         string const cmd = line.substr(pos + 1);
192                         func->dispatch(lyxaction.lookupFunc(cmd));
193                         string const rval = func->getMessage();
194                         if (func->errorStat()) {
195                                 client->writeln("ERROR:" + cmd + ':' + rval);
196                         } else {
197                                 client->writeln("INFO:" + cmd + ':' + rval);
198                         }
199                 } else if (key == "HELLO") {
200                         // no use for client name!
201                         client->writeln("HELLO:");
202                 } else if (key == "BYE") {
203                         saidbye = true;
204                 } else {
205                         client->writeln("ERROR:unknown key " + key);
206                 }
207         }
208
209         if (saidbye || (!client->connected())) {
210                 clients.erase(fd);
211         }
212 }
213
214
215 void LyXServerSocket::writeln(string const & line)
216 {
217         string const linen(line + '\n');
218         int const size = linen.size();
219         int const written = ::write(fd_, linen.c_str(), size);
220         if (written < size) { // Allways mean end of connection.
221                 if ((written == -1) && (errno == EPIPE)) {
222                         // The program will also receive a SIGPIPE
223                         // that must be caught
224                         lyxerr << "lyx: Server socket " << fd_
225                                << " connection closed while writing." << endl;
226                 } else {
227                         // Anything else, including errno == EAGAIN, must be
228                         // considered IO error. EAGAIN should never happen
229                         // when line is small
230                         lyxerr << "lyx: Server socket " << fd_
231                              << " IO error: " << strerror(errno);
232                 }
233         }
234 }
235
236 // Debug
237 // void LyXServerSocket::dump() const
238 // {
239 //      lyxerr << "LyXServerSocket debug dump.\n"
240 //           << "fd = " << fd_ << ", address = " << address_ << ".\n"
241 //           << "Clients: " << clients.size() << ".\n";
242 //      std::map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
243 //      std::map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
244 //      for (; client != end; ++client)
245 //              lyxerr << "fd = " << client->first << '\n';
246 // }
247
248
249 LyXDataSocket::LyXDataSocket(int fd)
250         : fd_(fd), connected_(true)
251 {
252         lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
253 }
254
255
256 LyXDataSocket::~LyXDataSocket()
257 {
258         ::close(fd_);
259
260         lyx_gui::unregister_socket_callback(fd_);
261         lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting."
262                                  << endl;
263 }
264
265
266 bool LyXDataSocket::connected() const
267 {
268         return connected_;
269 }
270
271
272 // Returns true if there was a complete line to input
273 bool LyXDataSocket::readln(string & line)
274 {
275         int const charbuf_size = 100;
276         char charbuf[charbuf_size]; // buffer for the ::read() system call
277         int count;
278
279         // read and store characters in buffer
280         while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
281                 buffer_.append(charbuf, charbuf + count);
282         }
283
284         // Error conditions. The buffer must still be
285         // processed for lines read
286         if (count == 0) { // EOF -- connection closed
287                 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
288                                          << ": connection closed." << endl;
289                 connected_ = false;
290         } else if ((count == -1) && (errno != EAGAIN)) { // IO error
291                 lyxerr << "lyx: Data socket " << fd_
292                        << ": IO error." << endl;
293                 connected_ = false;
294         }
295
296         // Cut a line from buffer
297         string::size_type pos = buffer_.find('\n');
298         if (pos == string::npos) {
299                 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
300                                          << ": line not completed." << endl;
301                 return false; // No complete line stored
302         }
303         line = buffer_.substr(0, pos);
304         buffer_.erase(0, pos + 1);
305         return true;
306 }
307
308
309 // Write a line of the form <key>:<value> to the socket
310 void LyXDataSocket::writeln(string const & line)
311 {
312         string const linen(line + '\n');
313         int const size = linen.size();
314         int const written = ::write(fd_, linen.c_str(), size);
315         if (written < size) { // Allways mean end of connection.
316                 if ((written == -1) && (errno == EPIPE)) {
317                         // The program will also receive a SIGPIPE
318                         // that must be catched
319                         lyxerr << "lyx: Data socket " << fd_
320                                << " connection closed while writing." << endl;
321                 } else {
322                         // Anything else, including errno == EAGAIN, must be
323                         // considered IO error. EAGAIN should never happen
324                         // when line is small
325                         lyxerr << "lyx: Data socket " << fd_
326                              << " IO error: " << strerror(errno);
327                 }
328                 connected_ = false;
329         }
330 }
331
332 #endif // defined(HAVE_READ) && defined(HAVE_WRITE) && defined(HAVE_CLOSE)