]> git.lyx.org Git - lyx.git/blob - src/LyXServerSocket.cpp
rename MathArray into MathData
[lyx.git] / src / LyXServerSocket.cpp
1 /**
2  * \file LyXServerSocket.cpp
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 "LyXServerSocket.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/FileName.h"
28 #include "support/lyxlib.h"
29 #include "support/socktools.h"
30
31 #include <boost/bind.hpp>
32
33 #include <cerrno>
34
35 #if defined (_WIN32)
36 # include <io.h>
37 #endif
38
39 using boost::shared_ptr;
40
41 using std::auto_ptr;
42 using std::endl;
43 using std::string;
44
45
46 namespace lyx {
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, support::FileName 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_.toFilesystemEncoding());
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_.absFilename() << endl;
74 }
75
76
77 // Close the socket and remove the address of the filesystem.
78 LyXServerSocket::~LyXServerSocket()
79 {
80         if (fd_ != -1) {
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);
86         }
87         support::unlink(address_);
88         LYXERR(Debug::LYXSERVER) << "lyx: Server socket quitting" << endl;
89 }
90
91
92 string const LyXServerSocket::address() const
93 {
94         return address_.absFilename();
95 }
96
97
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()
101 {
102         int const client_fd = support::socktools::accept(fd_);
103
104         if (fd_ == -1) {
105                 LYXERR(Debug::LYXSERVER) << "lyx: Failed to accept new client"
106                                          << endl;
107                 return;
108         }
109
110         if (clients.size() >= MAX_CLIENTS) {
111                 writeln("BYE:Too many clients connected");
112                 return;
113         }
114
115         // Register the new client.
116         clients[client_fd] =
117                 shared_ptr<LyXDataSocket>(new LyXDataSocket(client_fd));
118         theApp()->registerSocketCallback(
119                 client_fd,
120                 boost::bind(&LyXServerSocket::dataCallback,
121                             this, client_fd)
122                 );
123 }
124
125
126 // Reads and processes input from client and check
127 // if the connection has been closed
128 void LyXServerSocket::dataCallback(int fd)
129 {
130         shared_ptr<LyXDataSocket> client = clients[fd];
131
132         string line;
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");
140                         continue;
141                 }
142
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);
150                         } else {
151                                 client->writeln("INFO:" + cmd + ':' + rval);
152                         }
153                 } else if (key == "HELLO") {
154                         // no use for client name!
155                         client->writeln("HELLO:");
156                 } else if (key == "BYE") {
157                         saidbye = true;
158                 } else {
159                         client->writeln("ERROR:unknown key " + key);
160                 }
161         }
162
163         if (saidbye || (!client->connected())) {
164                 clients.erase(fd);
165         }
166 }
167
168
169 void LyXServerSocket::writeln(string const & line)
170 {
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;
180                 } else {
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);
186                 }
187         }
188 }
189
190 // Debug
191 // void LyXServerSocket::dump() const
192 // {
193 //      lyxerr << "LyXServerSocket debug dump.\n"
194 //           << "fd = " << fd_ << ", address = " << address_.absFilename() << ".\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';
200 // }
201
202
203 LyXDataSocket::LyXDataSocket(int fd)
204         : fd_(fd), connected_(true)
205 {
206         LYXERR(Debug::LYXSERVER) << "lyx: New data socket " << fd_ << endl;
207 }
208
209
210 LyXDataSocket::~LyXDataSocket()
211 {
212         if (::close(fd_) != 0)
213                 lyxerr << "lyx: Data socket " << fd_
214                        << " IO error on closing: " << strerror(errno);
215
216         theApp()->unregisterSocketCallback(fd_);
217         LYXERR(Debug::LYXSERVER) << "lyx: Data socket " << fd_ << " quitting."
218                                  << endl;
219 }
220
221
222 bool LyXDataSocket::connected() const
223 {
224         return connected_;
225 }
226
227
228 // Returns true if there was a complete line to input
229 bool LyXDataSocket::readln(string & line)
230 {
231         int const charbuf_size = 100;
232         char charbuf[charbuf_size]; // buffer for the ::read() system call
233         int count;
234
235         // read and store characters in buffer
236         while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
237                 buffer_.append(charbuf, charbuf + count);
238         }
239
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;
245                 connected_ = false;
246         } else if ((count == -1) && (errno != EAGAIN)) { // IO error
247                 lyxerr << "lyx: Data socket " << fd_
248                        << ": IO error." << endl;
249                 connected_ = false;
250         }
251
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
258         }
259         line = buffer_.substr(0, pos);
260         buffer_.erase(0, pos + 1);
261         return true;
262 }
263
264
265 // Write a line of the form <key>:<value> to the socket
266 void LyXDataSocket::writeln(string const & line)
267 {
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;
277                 } else {
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);
283                 }
284                 connected_ = false;
285         }
286 }
287
288
289 } // namespace lyx