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