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