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