]> git.lyx.org Git - lyx.git/blob - src/ServerSocket.cpp
prepare Qt 5.6 builds
[lyx.git] / src / ServerSocket.cpp
1 /**
2  * \file ServerSocket.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 "ServerSocket.h"
18
19 #include "DispatchResult.h"
20 #include "FuncRequest.h"
21 #include "LyX.h"
22 #include "LyXAction.h"
23
24 #include "frontends/Application.h"
25
26 #include "support/debug.h"
27 #include "support/environment.h"
28 #include "support/FileName.h"
29 #include "support/socktools.h"
30
31 #include "support/bind.h"
32
33 #include <cerrno>
34 #include <ostream>
35
36 #if defined (_WIN32)
37 # include <io.h>
38 #endif
39
40 using namespace std;
41 using namespace lyx::support;
42
43
44 namespace lyx {
45
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(FileName const & addr)
50         : fd_(socktools::listen(addr, 3)),
51           address_(addr)
52 {
53         if (fd_ == -1) {
54                 LYXERR(Debug::LYXSERVER, "lyx: Disabling LyX socket.");
55                 return;
56         }
57
58         // These env vars are used by DVI inverse search
59         // Needed by xdvi
60         setEnv("XEDITOR", "lyxclient -g %f %l");
61         // Needed by lyxclient
62         setEnv("LYXSOCKET", address_.absFileName());
63
64         theApp()->registerSocketCallback(
65                 fd_,
66                 bind(&ServerSocket::serverCallback, this)
67                 );
68
69         LYXERR(Debug::LYXSERVER, "lyx: New server socket "
70                                  << fd_ << ' ' << address_.absFileName());
71 }
72
73
74 // Close the socket and remove the address of the filesystem.
75 ServerSocket::~ServerSocket()
76 {
77         if (fd_ != -1) {
78                 BOOST_ASSERT (theApp());
79                 theApp()->unregisterSocketCallback(fd_);
80                 if (::close(fd_) != 0)
81                         lyxerr << "lyx: Server socket " << fd_
82                                << " IO error on closing: " << strerror(errno)
83                                << endl;
84         }
85         address_.removeFile();
86         LYXERR(Debug::LYXSERVER, "lyx: Server socket quitting");
87 }
88
89
90 string const ServerSocket::address() const
91 {
92         return address_.absFileName();
93 }
94
95
96 // Creates a new LyXDataSocket and checks to see if the connection
97 // is OK and if the number of clients does not exceed MAX_CLIENTS
98 void ServerSocket::serverCallback()
99 {
100         if (clients.size() >= MAX_CLIENTS) {
101                 writeln("BYE:Too many clients connected");
102                 return;
103         }
104
105         int const client_fd = socktools::accept(fd_);
106
107         if (fd_ == -1) {
108                 LYXERR(Debug::LYXSERVER, "lyx: Failed to accept new client");
109                 return;
110         }
111
112         // Register the new client.
113         clients[client_fd] =
114                 shared_ptr<LyXDataSocket>(new LyXDataSocket(client_fd));
115         theApp()->registerSocketCallback(
116                 client_fd,
117                 bind(&ServerSocket::dataCallback,
118                             this, client_fd)
119                 );
120 }
121
122
123 // Reads and processes input from client and check
124 // if the connection has been closed
125 void ServerSocket::dataCallback(int fd)
126 {
127         map<int, shared_ptr<LyXDataSocket> >::const_iterator it = clients.find(fd);
128         if (it == clients.end())
129                 return;
130         shared_ptr<LyXDataSocket> client = it->second;
131         string line;
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                 size_t pos;
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                         FuncRequest fr(lyxaction.lookupFunc(cmd));
146                         fr.setOrigin(FuncRequest::LYXSERVER);
147                         DispatchResult dr;
148                         theApp()->dispatch(fr, dr);
149                         string const rval = to_utf8(dr.message());
150                         if (dr.error())
151                                 client->writeln("ERROR:" + cmd + ':' + rval);
152                         else
153                                 client->writeln("INFO:" + cmd + ':' + rval);
154                 } else if (key == "HELLO") {
155                         // no use for client name!
156                         client->writeln("HELLO:");
157                 } else if (key == "BYE") {
158                         saidbye = true;
159                 } else {
160                         client->writeln("ERROR:unknown key " + key);
161                 }
162         }
163
164         if (saidbye || !client->connected()) {
165                 clients.erase(fd);
166         }
167 }
168
169
170 void ServerSocket::writeln(string const & line)
171 {
172         string const linen = line + '\n';
173         int const size = linen.size();
174         int const written = ::write(fd_, linen.c_str(), size);
175         if (written < size) { // Always mean end of connection.
176                 if (written == -1 && errno == EPIPE) {
177                         // The program will also receive a SIGPIPE
178                         // that must be caught
179                         lyxerr << "lyx: Server socket " << fd_
180                                << " connection closed while writing." << endl;
181                 } else {
182                         // Anything else, including errno == EAGAIN, must be
183                         // considered IO error. EAGAIN should never happen
184                         // when line is small
185                         lyxerr << "lyx: Server socket " << fd_
186                              << " IO error: " << strerror(errno);
187                 }
188         }
189 }
190
191 // Debug
192 // void ServerSocket::dump() const
193 // {
194 //      lyxerr << "ServerSocket debug dump.\n"
195 //           << "fd = " << fd_ << ", address = " << address_.absFileName() << ".\n"
196 //           << "Clients: " << clients.size() << ".\n";
197 //      map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
198 //      map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
199 //      for (; client != end; ++client)
200 //              lyxerr << "fd = " << client->first << '\n';
201 // }
202
203
204 LyXDataSocket::LyXDataSocket(int fd)
205         : fd_(fd), connected_(true)
206 {
207         LYXERR(Debug::LYXSERVER, "lyx: New data socket " << fd_);
208 }
209
210
211 LyXDataSocket::~LyXDataSocket()
212 {
213         if (::close(fd_) != 0)
214                 lyxerr << "lyx: Data socket " << fd_
215                        << " IO error on closing: " << strerror(errno);
216
217         theApp()->unregisterSocketCallback(fd_);
218         LYXERR(Debug::LYXSERVER, "lyx: Data socket " << fd_ << " quitting.");
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.");
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         size_t pos = buffer_.find('\n');
254         if (pos == string::npos) {
255                 LYXERR(Debug::LYXSERVER, "lyx: Data socket " << fd_
256                                          << ": line not completed.");
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