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