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