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