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