* qt_helpers.cpp:
[lyx.git] / src / Server.cpp
blobd7a2e2514c285a26a533bfa67b39ac6bf79c6b5e
1 /**
2 * \file Server.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
7 * \author Jean-Marc Lasgouttes
8 * \author Angus Leeming
9 * \author John Levon
11 * Full author contact details are available in file CREDITS.
14 /**
15 Docu : To use the lyxserver define the name of the pipe in your
16 lyxrc:
17 \serverpipe "/home/myhome/.lyxpipe"
18 Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
19 Each message consists of a single line in ASCII. Input lines
20 (client -> LyX) have the following format:
21 "LYXCMD:<clientname>:<functionname>:<argument>"
22 Answers from LyX look like this:
23 "INFO:<clientname>:<functionname>:<data>"
24 [asierra970531] Or like this in case of error:
25 "ERROR:<clientname>:<functionname>:<error message>"
26 where <clientname> and <functionname> are just echoed.
27 If LyX notifies about a user defined extension key-sequence,
28 the line looks like this:
29 "NOTIFY:<key-sequence>"
30 [asierra970531] New server-only messages to implement a simple protocol
31 "LYXSRV:<clientname>:<protocol message>"
32 where <protocol message> can be "hello" or "bye". If hello is
33 received LyX will inform the client that it's listening its
34 messages, and 'bye' will inform that lyx is closing.
36 See development/server_monitor.c for an example client.
37 Purpose: implement a client/server lib for LyX
40 #include <config.h>
42 #include "Server.h"
43 #include "FuncRequest.h"
44 #include "LyXAction.h"
45 #include "LyXFunc.h"
47 #include "frontends/Application.h"
49 #include "support/debug.h"
50 #include "support/FileName.h"
51 #include "support/lstrings.h"
53 #include <boost/bind.hpp>
55 #include <cerrno>
56 #ifdef HAVE_SYS_STAT_H
57 # include <sys/stat.h>
58 #endif
59 #include <fcntl.h>
61 using namespace std;
62 using namespace lyx::support;
64 namespace lyx {
66 /////////////////////////////////////////////////////////////////////
68 // LyXComm
70 /////////////////////////////////////////////////////////////////////
72 #if !defined (HAVE_MKFIFO)
73 // We provide a stub class that disables the lyxserver.
75 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
78 void LyXComm::openConnection()
82 void LyXComm::closeConnection()
86 int LyXComm::startPipe(string const & filename, bool write)
88 return -1;
92 void LyXComm::endPipe(int & fd, string const & filename, bool write)
96 void LyXComm::emergencyCleanup()
99 void LyXComm::read_ready()
103 void LyXComm::send(string const & msg)
107 #else // defined (HAVE_MKFIFO)
110 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
111 : pipename_(pip), client_(cli), clientcb_(ccb)
113 ready_ = false;
114 openConnection();
118 void LyXComm::openConnection()
120 LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
122 // If we are up, that's an error
123 if (ready_) {
124 lyxerr << "LyXComm: Already connected" << endl;
125 return;
127 // We assume that we don't make it
128 ready_ = false;
130 if (pipename_.empty()) {
131 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
132 return;
135 infd_ = startPipe(inPipeName(), false);
136 if (infd_ == -1)
137 return;
139 outfd_ = startPipe(outPipeName(), true);
140 if (outfd_ == -1) {
141 endPipe(infd_, inPipeName(), false);
142 return;
145 if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
146 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
147 << '\n' << strerror(errno) << endl;
148 return;
151 // We made it!
152 ready_ = true;
153 LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
157 /// Close pipes
158 void LyXComm::closeConnection()
160 LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
162 if (pipename_.empty()) {
163 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
164 return;
167 if (!ready_) {
168 LYXERR0("LyXComm: Already disconnected");
169 return;
172 endPipe(infd_, inPipeName(), false);
173 endPipe(outfd_, outPipeName(), true);
175 ready_ = false;
179 int LyXComm::startPipe(string const & file, bool write)
181 static bool stalepipe = false;
182 FileName const filename(file);
183 if (filename.exists()) {
184 if (!write) {
185 // Let's see whether we have a stale pipe.
186 int fd = ::open(filename.toFilesystemEncoding().c_str(),
187 O_WRONLY | O_NONBLOCK);
188 if (fd >= 0) {
189 // Another LyX instance is using it.
190 ::close(fd);
191 } else if (errno == ENXIO) {
192 // No process is reading from the other end.
193 stalepipe = true;
194 LYXERR(Debug::LYXSERVER,
195 "LyXComm: trying to remove "
196 << filename);
197 filename.removeFile();
199 } else if (stalepipe) {
200 LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
201 << filename);
202 filename.removeFile();
203 stalepipe = false;
205 if (filename.exists()) {
206 lyxerr << "LyXComm: Pipe " << filename
207 << " already exists.\nIf no other LyX program"
208 " is active, please delete the pipe by hand"
209 " and try again."
210 << endl;
211 pipename_.erase();
212 return -1;
216 if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
217 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
218 << strerror(errno) << endl;
219 return -1;
221 int const fd = ::open(filename.toFilesystemEncoding().c_str(),
222 write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
224 if (fd < 0) {
225 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
226 << strerror(errno) << endl;
227 filename.removeFile();
228 return -1;
231 if (!write) {
232 theApp()->registerSocketCallback(fd,
233 boost::bind(&LyXComm::read_ready, this));
236 return fd;
240 void LyXComm::endPipe(int & fd, string const & filename, bool write)
242 if (fd < 0)
243 return;
245 if (!write)
246 theApp()->unregisterSocketCallback(fd);
248 if (::close(fd) < 0) {
249 lyxerr << "LyXComm: Could not close pipe " << filename
250 << '\n' << strerror(errno) << endl;
253 if (FileName(filename).removeFile() < 0) {
254 lyxerr << "LyXComm: Could not remove pipe " << filename
255 << '\n' << strerror(errno) << endl;
258 fd = -1;
262 void LyXComm::emergencyCleanup()
264 if (!pipename_.empty()) {
265 endPipe(infd_, inPipeName(), false);
266 endPipe(outfd_, outPipeName(), true);
271 // Receives messages and sends then to client
272 void LyXComm::read_ready()
274 // FIXME: make read_buffer_ a class-member for multiple sessions
275 static string read_buffer_;
276 read_buffer_.erase();
278 int const charbuf_size = 100;
279 char charbuf[charbuf_size];
281 // As O_NONBLOCK is set, until no data is available for reading,
282 // read() doesn't block but returns -1 and set errno to EAGAIN.
283 // After a client that opened the pipe for writing, closes it
284 // (and no other client is using the pipe), read() would always
285 // return 0 and thus the connection has to be reset.
287 errno = 0;
288 int status;
289 // the single = is intended here.
290 while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
292 if (status > 0) {
293 charbuf[status] = '\0'; // turn it into a c string
294 read_buffer_ += rtrim(charbuf, "\r");
295 // commit any commands read
296 while (read_buffer_.find('\n') != string::npos) {
297 // split() grabs the entire string if
298 // the delim /wasn't/ found. ?:-P
299 string cmd;
300 read_buffer_= split(read_buffer_, cmd,'\n');
301 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
302 << ", read_buffer_:" << read_buffer_
303 << ", cmd:" << cmd);
304 if (!cmd.empty())
305 clientcb_(client_, cmd);
306 //\n or not \n?
308 } else {
309 if (errno == EAGAIN) {
310 // Nothing to read, continue
311 errno = 0;
312 return;
314 // An error occurred, better bailing out
315 LYXERR0("LyXComm: " << strerror(errno));
316 if (!read_buffer_.empty()) {
317 LYXERR0("LyXComm: truncated command: " << read_buffer_);
318 read_buffer_.erase();
320 break; // reset connection
324 // The connection gets reset when read() returns 0 (meaning that the
325 // last client closed the pipe) or an error occurred, in which case
326 // read() returns -1 and errno != EAGAIN.
327 closeConnection();
328 openConnection();
329 errno = 0;
333 void LyXComm::send(string const & msg)
335 if (msg.empty()) {
336 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
337 return;
340 LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
342 if (pipename_.empty()) return;
344 if (!ready_) {
345 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
346 } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
347 lyxerr << "LyXComm: Error sending message: " << msg
348 << '\n' << strerror(errno)
349 << "\nLyXComm: Resetting connection" << endl;
350 closeConnection();
351 openConnection();
355 #endif // defined (HAVE_MKFIFO)
358 string const LyXComm::inPipeName() const
360 return pipename_ + ".in";
364 string const LyXComm::outPipeName() const
366 return pipename_ + ".out";
370 /////////////////////////////////////////////////////////////////////
372 // Server
374 /////////////////////////////////////////////////////////////////////
376 void ServerCallback(Server * server, string const & msg)
378 server->callback(msg);
381 Server::Server(LyXFunc * f, string const & pipes)
382 : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
386 Server::~Server()
388 // say goodbye to clients so they stop sending messages
389 // send as many bye messages as there are clients,
390 // each with client's name.
391 string message;
392 for (int i = 0; i != numclients_; ++i) {
393 message = "LYXSRV:" + clients_[i] + ":bye\n";
394 pipes_.send(message);
399 int compare(char const * a, char const * b, unsigned int len)
401 using namespace std;
402 return strncmp(a, b, len);
406 // Handle data gotten from communication, called by LyXComm
407 void Server::callback(string const & msg)
409 LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
411 char const * p = msg.c_str();
413 // --- parse the string --------------------------------------------
415 // Format: LYXCMD:<client>:<func>:<argstring>\n
417 bool server_only = false;
418 while (*p) {
419 // --- 1. check 'header' ---
421 if (compare(p, "LYXSRV:", 7) == 0) {
422 server_only = true;
423 } else if (0 != compare(p, "LYXCMD:", 7)) {
424 lyxerr << "Server: Unknown request \""
425 << p << '"' << endl;
426 return;
428 p += 7;
430 // --- 2. for the moment ignore the client name ---
431 string client;
432 while (*p && *p != ':')
433 client += char(*p++);
434 if (*p == ':')
435 ++p;
436 if (!*p)
437 return;
439 // --- 3. get function name ---
440 string cmd;
441 while (*p && *p != ':')
442 cmd += char(*p++);
444 // --- 4. parse the argument ---
445 string arg;
446 if (!server_only && *p == ':' && *(++p)) {
447 while (*p && *p != '\n')
448 arg += char(*p++);
449 if (*p) ++p;
452 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
453 << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
455 // --- lookup and exec the command ------------------
457 if (server_only) {
458 string buf;
459 // return the greeting to inform the client that
460 // we are listening.
461 if (cmd == "hello") {
462 // One more client
463 if (numclients_ == MAX_CLIENTS) { //paranoid check
464 LYXERR(Debug::LYXSERVER, "Server: too many clients...");
465 return;
467 int i = 0;
468 while (!clients_[i].empty() && i < numclients_)
469 ++i;
470 clients_[i] = client;
471 ++numclients_;
472 buf = "LYXSRV:" + client + ":hello\n";
473 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
474 pipes_.send(buf);
475 } else if (cmd == "bye") {
476 // If clients_ == 0 maybe we should reset the pipes
477 // to prevent fake callbacks
478 int i = 0; //look if client is registered
479 for (; i < numclients_; ++i) {
480 if (clients_[i] == client)
481 break;
483 if (i < numclients_) {
484 --numclients_;
485 clients_[i].erase();
486 LYXERR(Debug::LYXSERVER, "Server: Client "
487 << client << " said goodbye");
488 } else {
489 LYXERR(Debug::LYXSERVER,
490 "Server: ignoring bye messge from unregistered client" << client);
492 } else {
493 LYXERR0("Server: Undefined server command " << cmd << '.');
495 return;
498 if (!cmd.empty()) {
499 // which lyxfunc should we let it connect to?
500 // The correct solution would be to have a
501 // specialized (non-gui) BufferView. But how do
502 // we do it now? Probably we should just let it
503 // connect to the lyxfunc in the single LyXView we
504 // support currently. (Lgb)
506 func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
507 string const rval = to_utf8(func_->getMessage());
509 // all commands produce an INFO or ERROR message
510 // in the output pipe, even if they do not return
511 // anything. See chapter 4 of Customization doc.
512 string buf;
513 if (func_->errorStat())
514 buf = "ERROR:";
515 else
516 buf = "INFO:";
517 buf += client + ':' + cmd + ':' + rval + '\n';
518 pipes_.send(buf);
520 // !!! we don't do any error checking -
521 // if the client won't listen, the
522 // message is lost and others too
523 // maybe; so the client should empty
524 // the outpipe before issuing a request.
526 // not found
528 } // while *p
532 // Send a notify message to a client, called by WorkAreaKeyPress
533 void Server::notifyClient(string const & s)
535 pipes_.send("NOTIFY:" + s + "\n");
539 } // namespace lyx