]> git.lyx.org Git - lyx.git/blob - src/lyxsocket.C
hopefully fix tex2lyx linking.
[lyx.git] / src / lyxsocket.C
1 /**
2  * \file lyxsocket.C
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 "lyxsocket.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/lyxlib.h"
28 #include "support/socktools.h"
29
30 #include <boost/bind.hpp>
31
32 #include <cerrno>
33
34 #if defined (_WIN32)
35 # include <io.h>
36 #endif
37
38 using boost::shared_ptr;
39
40 using std::auto_ptr;
41 using std::endl;
42 using std::string;
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 LyXServerSocket::LyXServerSocket(LyXFunc * f, string const & addr)
51         : func(f),
52           fd_(support::socktools::listen(addr, 3)),
53           address_(addr)
54 {
55         if (fd_ == -1) {
56                 lyxerr << "lyx: Disabling LyX socket." << endl;
57                 return;
58         }
59
60         // These env vars are used by DVI inverse search
61         // Needed by xdvi
62         support::setEnv("XEDITOR", "lyxclient -g %f %l");
63         // Needed by lyxclient
64         support::setEnv("LYXSOCKET", address_);
65
66         theApp->registerSocketCallback(
67                 fd_,
68                 boost::bind(&LyXServerSocket::serverCallback, this)
69                 );
70
71         lyxerr[Debug::LYXSERVER] << "lyx: New server socket "
72                                  << fd_ << ' ' << address_ << endl;
73 }
74
75
76 // Close the socket and remove the address of the filesystem.
77 LyXServerSocket::~LyXServerSocket()
78 {
79         if (fd_ != -1) {
80                 theApp->unregisterSocketCallback(fd_);
81                 if (::close(fd_) != 0)
82                         lyxerr << "lyx: Server socket " << fd_
83                                << " IO error on closing: " << strerror(errno);
84         }
85         support::unlink(address_);
86         lyxerr[Debug::LYXSERVER] << "lyx: Server socket quitting" << endl;
87 }
88
89
90 string const & LyXServerSocket::address() const
91 {
92         return address_;
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 LyXServerSocket::serverCallback()
99 {
100         int const client_fd = support::socktools::accept(fd_);
101
102         if (fd_ == -1) {
103                 lyxerr[Debug::LYXSERVER] << "lyx: Failed to accept new client"
104                                          << endl;
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                 boost::bind(&LyXServerSocket::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 LyXServerSocket::dataCallback(int fd)
127 {
128         shared_ptr<LyXDataSocket> client = clients[fd];
129
130         string line;
131         string::size_type pos;
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                 if ((pos = line.find(':')) == string::npos) {
137                         client->writeln("ERROR:" + line + ":malformed message");
138                         continue;
139                 }
140
141                 string const key = line.substr(0, pos);
142                 if (key == "LYXCMD") {
143                         string const cmd = line.substr(pos + 1);
144                         func->dispatch(lyxaction.lookupFunc(cmd));
145                         string const rval = to_utf8(func->getMessage());
146                         if (func->errorStat()) {
147                                 client->writeln("ERROR:" + cmd + ':' + rval);
148                         } else {
149                                 client->writeln("INFO:" + cmd + ':' + rval);
150                         }
151                 } else if (key == "HELLO") {
152                         // no use for client name!
153                         client->writeln("HELLO:");
154                 } else if (key == "BYE") {
155                         saidbye = true;
156                 } else {
157                         client->writeln("ERROR:unknown key " + key);
158                 }
159         }
160
161         if (saidbye || (!client->connected())) {
162                 clients.erase(fd);
163         }
164 }
165
166
167 void LyXServerSocket::writeln(string const & line)
168 {
169         string const linen(line + '\n');
170         int const size = linen.size();
171         int const written = ::write(fd_, linen.c_str(), size);
172         if (written < size) { // Always mean end of connection.
173                 if ((written == -1) && (errno == EPIPE)) {
174                         // The program will also receive a SIGPIPE
175                         // that must be caught
176                         lyxerr << "lyx: Server socket " << fd_
177                                << " connection closed while writing." << endl;
178                 } else {
179                         // Anything else, including errno == EAGAIN, must be
180                         // considered IO error. EAGAIN should never happen
181                         // when line is small
182                         lyxerr << "lyx: Server socket " << fd_
183                              << " IO error: " << strerror(errno);
184                 }
185         }
186 }
187
188 // Debug
189 // void LyXServerSocket::dump() const
190 // {
191 //      lyxerr << "LyXServerSocket debug dump.\n"
192 //           << "fd = " << fd_ << ", address = " << address_ << ".\n"
193 //           << "Clients: " << clients.size() << ".\n";
194 //      std::map<int, shared_ptr<LyXDataSocket> >::const_iterator client = clients.begin();
195 //      std::map<int, shared_ptr<LyXDataSocket> >::const_iterator end = clients.end();
196 //      for (; client != end; ++client)
197 //              lyxerr << "fd = " << client->first << '\n';
198 // }
199
200
201 LyXDataSocket::LyXDataSocket(int fd)
202         : fd_(fd), connected_(true)
203 {
204         lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
205 }
206
207
208 LyXDataSocket::~LyXDataSocket()
209 {
210         if (::close(fd_) != 0)
211                 lyxerr << "lyx: Data socket " << fd_
212                        << " IO error on closing: " << strerror(errno);
213
214         theApp->unregisterSocketCallback(fd_);
215         lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting."
216                                  << endl;
217 }
218
219
220 bool LyXDataSocket::connected() const
221 {
222         return connected_;
223 }
224
225
226 // Returns true if there was a complete line to input
227 bool LyXDataSocket::readln(string & line)
228 {
229         int const charbuf_size = 100;
230         char charbuf[charbuf_size]; // buffer for the ::read() system call
231         int count;
232
233         // read and store characters in buffer
234         while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
235                 buffer_.append(charbuf, charbuf + count);
236         }
237
238         // Error conditions. The buffer must still be
239         // processed for lines read
240         if (count == 0) { // EOF -- connection closed
241                 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
242                                          << ": connection closed." << endl;
243                 connected_ = false;
244         } else if ((count == -1) && (errno != EAGAIN)) { // IO error
245                 lyxerr << "lyx: Data socket " << fd_
246                        << ": IO error." << endl;
247                 connected_ = false;
248         }
249
250         // Cut a line from buffer
251         string::size_type pos = buffer_.find('\n');
252         if (pos == string::npos) {
253                 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
254                                          << ": line not completed." << endl;
255                 return false; // No complete line stored
256         }
257         line = buffer_.substr(0, pos);
258         buffer_.erase(0, pos + 1);
259         return true;
260 }
261
262
263 // Write a line of the form <key>:<value> to the socket
264 void LyXDataSocket::writeln(string const & line)
265 {
266         string const linen(line + '\n');
267         int const size = linen.size();
268         int const written = ::write(fd_, linen.c_str(), size);
269         if (written < size) { // Always mean end of connection.
270                 if ((written == -1) && (errno == EPIPE)) {
271                         // The program will also receive a SIGPIPE
272                         // that must be catched
273                         lyxerr << "lyx: Data socket " << fd_
274                                << " connection closed while writing." << endl;
275                 } else {
276                         // Anything else, including errno == EAGAIN, must be
277                         // considered IO error. EAGAIN should never happen
278                         // when line is small
279                         lyxerr << "lyx: Data socket " << fd_
280                              << " IO error: " << strerror(errno);
281                 }
282                 connected_ = false;
283         }
284 }
285
286
287 } // namespace lyx