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