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