3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author João Luis M. Assirati
7 * \author Lars Gullik Bjønnes
9 * Full author contact details are available in file CREDITS.
16 #include "support/lstrings.h"
18 #include <boost/filesystem/operations.hpp>
19 #include <boost/lexical_cast.hpp>
20 #include <boost/scoped_ptr.hpp>
22 // getpid(), getppid()
23 #ifdef HAVE_SYS_TYPES_H
24 # include <sys/types.h>
31 #ifdef HAVE_SYS_TIME_H
32 # include <sys/time.h>
36 #ifdef HAVE_SYS_SELECT_H
37 # include <sys/select.h>
40 // socket(), connect()
41 #ifdef HAVE_SYS_SOCKET_H
42 # include <sys/socket.h>
56 using lyx::support::prefixIs;
58 using boost::scoped_ptr;
59 namespace fs = boost::filesystem;
71 string itoa(unsigned int i)
73 return boost::lexical_cast<string>(i);
77 // Parts stolen from lyx::support::DirList()
78 // Returns the absolute pathnames of all lyx local sockets
79 vector<fs::path> lyxSockets(string const & dir, string const & pid)
81 vector<fs::path> dirlist;
83 fs::path dirpath(dir);
85 if (!fs::exists(dirpath) || !fs::is_directory(dirpath)) {
86 lyxerr << dir << " does not exist or is not a directory."
91 fs::directory_iterator beg((fs::path(dir)));
92 fs::directory_iterator end;
94 for (; beg != end; ++beg) {
95 if (prefixIs(beg->leaf(), "lyx_tmpdir" + pid)) {
96 fs::path lyxsocket = *beg / "lyxsocket";
97 if (fs::exists(lyxsocket)) {
98 dirlist.push_back(lyxsocket);
107 namespace socktools {
110 int connect(string const & name)
112 int fd; // File descriptor for the socket
113 sockaddr_un addr; // Structure that hold the socket address
115 // char sun_path[108]
116 string::size_type len = name.size();
118 cerr << "lyxclient: Socket address '" << name
119 << "' too long." << endl;
122 // Synonims for AF_UNIX are AF_LOCAL and AF_FILE
123 addr.sun_family = AF_UNIX;
124 name.copy(addr.sun_path, 107);
125 addr.sun_path[len] = '\0';
127 if ((fd = ::socket(PF_UNIX, SOCK_STREAM, 0))== -1) {
128 cerr << "lyxclient: Could not create socket descriptor: "
129 << strerror(errno) << endl;
133 reinterpret_cast<struct sockaddr *>(&addr),
134 sizeof(addr)) == -1) {
135 cerr << "lyxclient: Could not connect to socket " << name
136 << ": " << strerror(errno) << endl;
140 if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
141 cerr << "lyxclient: Could not set O_NONBLOCK for socket: "
142 << strerror(errno) << endl;
150 } // namespace socktools
151 } // namespace support
155 // Class IOWatch ------------------------------------------------------------
176 void IOWatch::clear()
182 void IOWatch::addfd(int fd)
188 bool IOWatch::wait(double timeout)
191 to.tv_sec = static_cast<long int>(timeout);
192 to.tv_usec = static_cast<long int>((timeout - to.tv_sec)*1E6);
194 return select(FD_SETSIZE, &act,
195 (fd_set *)0, (fd_set *)0, &to);
202 return select(FD_SETSIZE, &act,
203 (fd_set *)0, (fd_set *)0, (timeval *)0);
207 bool IOWatch::isset(int fd) {
208 return FD_ISSET(fd, &act);
210 // ~Class IOWatch ------------------------------------------------------------
213 // Class LyXDataSocket -------------------------------------------------------
214 // Modified LyXDataSocket class for use with the client
215 class LyXDataSocket {
217 LyXDataSocket(string const &);
219 // File descriptor of the connection
222 bool connected() const;
223 // Line buffered input from the socket
224 bool readln(string &);
225 // Write the string + '\n' to the socket
226 void writeln(string const &);
228 // File descriptor for the data socket
230 // True if the connection is up
232 // buffer for input data
237 LyXDataSocket::LyXDataSocket(string const & address)
239 if ((fd_ = support::socktools::connect(address)) == -1) {
247 LyXDataSocket::~LyXDataSocket()
253 int LyXDataSocket::fd() const
259 bool LyXDataSocket::connected() const
265 // Returns true if there was a complete line to input
266 // A line is of the form <key>:<value>
267 // A line not of this form will not be passed
268 // The line read is splitted and stored in 'key' and 'value'
269 bool LyXDataSocket::readln(string & line)
271 int const charbuf_size = 100;
272 char charbuf[charbuf_size]; // buffer for the ::read() system call
274 string::size_type pos;
276 // read and store characters in buffer
277 while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
278 charbuf[count] = '\0'; // turn it into a c string
282 // Error conditions. The buffer must still be
283 // processed for lines read
284 if (count == 0) { // EOF -- connection closed
286 } else if ((count == -1) && (errno != EAGAIN)) { // IO error
287 cerr << "lyxclient: IO error." << endl;
291 // Cut a line from buffer
292 if ((pos = buffer.find('\n')) == string::npos)
293 return false; // No complete line stored
294 line = buffer.substr(0, pos);
295 buffer = buffer.substr(pos + 1);
300 // Write a line of the form <key>:<value> to the socket
301 void LyXDataSocket::writeln(string const & line)
303 string linen(line + '\n');
304 int size = linen.size();
305 int written = ::write(fd_, linen.c_str(), size);
306 if (written < size) { // Allways mean end of connection.
307 if ((written == -1) && (errno == EPIPE)) {
308 // The program will also receive a SIGPIPE
309 // that must be catched
310 cerr << "lyxclient: connection closed while writing."
313 // Anything else, including errno == EAGAIN, must be
314 // considered IO error. EAGAIN should never happen
315 // when line is small
316 cerr << "lyxclient: IO error: " << strerror(errno);
321 // ~Class LyXDataSocket -------------------------------------------------------
324 // Class CmdLineParser -------------------------------------------------------
325 class CmdLineParser {
327 typedef int (*optfunc)(vector<char *> const & args);
328 std::map<string, optfunc> helper;
329 std::map<string, bool> isset;
330 bool parse(int, char * []);
331 vector<char *> nonopt;
335 bool CmdLineParser::parse(int argc, char * argv[])
340 if (helper[argv[opt]]) {
341 isset[argv[opt]] = true;
343 while ((arg < argc) && (!helper[argv[arg]])) {
344 args.push_back(argv[arg]);
347 int taken = helper[argv[opt]](args);
352 if (argv[opt][0] == '-') {
353 if ((argv[opt][1] == '-')
354 && (argv[opt][2]== '\0')) {
357 nonopt.push_back(argv[opt]);
362 cerr << "lyxclient: unknown option "
363 << argv[opt] << endl;
367 nonopt.push_back(argv[opt]);
373 // ~Class CmdLineParser -------------------------------------------------------
381 cerr << "Usage: lyxclient [options]" << endl
382 << "Options are:" << endl
383 << " -a address set address of the lyx socket" << endl
384 << " -t directory set system temporary directory" << endl
385 << " -p pid select a running lyx by pid" << endl
386 << " -c command send a single command and quit" << endl
387 << " -g file row send a command to go to file and row" << endl
388 << " -n name set client name" << endl
389 << " -h name display this help end exit" << endl
390 << "If -a is not used, lyxclient will use the arguments of -t and -p to look for" << endl
391 << "a running lyx. If -t is not set, 'directory' defaults to /tmp. If -p is set," << endl
392 << "lyxclient will connect only to a lyx with the specified pid. Options -c and -g" << endl
393 << "cannot be set simultaneoulsly. If no -c or -g options are given, lyxclient" << endl
394 << "will read commands from standard input and disconnect when command read is BYE:"
399 int h(vector<char *> const &)
406 string clientName(support::itoa(::getppid()) + ">" + support::itoa(::getpid()));
408 int n(vector<char *> const & arg)
410 if (arg.size() < 1) {
411 cerr << "lyxclient: The option -n requires 1 argument."
420 string singleCommand;
423 int c(vector<char *> const & arg)
425 if (arg.size() < 1) {
426 cerr << "lyxclient: The option -c requires 1 argument."
430 singleCommand = arg[0];
435 int g(vector<char *> const & arg)
437 if (arg.size() < 2) {
438 cerr << "lyxclient: The option -g requires 2 arguments."
442 singleCommand = "LYXCMD:server-goto-file-row "
443 + string(arg[0]) + ' '
449 // 0 if LYXSOCKET is not set in the environment
450 char * serverAddress = getenv("LYXSOCKET");
453 int a(vector<char *> const & arg)
455 if (arg.size() < 1) {
456 cerr << "lyxclient: The option -a requires 1 argument."
460 // -a supercedes LYXSOCKET environment variable
461 serverAddress = arg[0];
466 string mainTmp("/tmp");
469 int t(vector<char *> const & arg)
471 if (arg.size() < 1) {
472 cerr << "lyxclient: The option -t requires 1 argument."
481 string serverPid; // Init to empty string
484 int p(vector<char *> const & arg)
486 if (arg.size() < 1) {
487 cerr << "lyxclient: The option -p requires 1 argument."
496 } // namespace cmdline
502 int main(int argc, char * argv[])
504 lyxerr.rdbuf(cerr.rdbuf());
507 args.helper["-h"] = cmdline::h;
508 args.helper["-c"] = cmdline::c;
509 args.helper["-g"] = cmdline::g;
510 args.helper["-n"] = cmdline::n;
511 args.helper["-a"] = cmdline::a;
512 args.helper["-t"] = cmdline::t;
513 args.helper["-p"] = cmdline::p;
515 // Command line failure conditions:
516 if ((!args.parse(argc, argv))
517 || (args.isset["-c"] && args.isset["-g"])
518 || (args.isset["-a"] && args.isset["-p"])) {
523 scoped_ptr<LyXDataSocket> server;
525 if (cmdline::serverAddress) {
526 server.reset(new LyXDataSocket(cmdline::serverAddress));
527 if (!server->connected()) {
528 cerr << "lyxclient: " << "Could not connect to "
529 << cmdline::serverAddress << endl;
533 // We have to look for an address.
534 // serverPid can be empty.
535 vector<fs::path> addrs = support::lyxSockets(cmdline::mainTmp, cmdline::serverPid);
536 vector<fs::path>::const_iterator addr = addrs.begin();
537 vector<fs::path>::const_iterator end = addrs.end();
538 for (; addr != end; ++addr) {
539 server.reset(new LyXDataSocket(addr->string()));
540 if (server->connected())
542 lyxerr << "lyxclient: " << "Could not connect to "
543 << addr->string() << endl;
546 lyxerr << "lyxclient: No suitable server found."
550 cerr << "lyxclient: " << "Connected to " << addr->string() << endl;
553 int const serverfd = server->fd();
556 iowatch.addfd(serverfd);
558 // Used to read from server
562 server->writeln("HELLO:" + cmdline::clientName);
563 // wait at most 2 seconds until server responds
565 if (iowatch.isset(serverfd) && server->readln(answer)) {
566 if (prefixIs(answer, "BYE:")) {
567 cerr << "lyxclient: Server disconnected." << endl;
568 cout << answer << endl;
572 cerr << "lyxclient: No answer from server." << endl;
576 if (args.isset["-g"] || args.isset["-c"]) {
577 server->writeln(cmdline::singleCommand);
579 if (iowatch.isset(serverfd) && server->readln(answer)) {
581 if (prefixIs(answer, "ERROR:"))
585 cerr << "lyxclient: No answer from server." << endl;
590 // Take commands from stdin
591 iowatch.addfd(0); // stdin
592 bool saidbye = false;
593 while ((!saidbye) && server->connected()) {
595 if (iowatch.isset(0)) {
598 if (command == "BYE:") {
599 server->writeln("BYE:");
602 server->writeln("LYXCMD:" + command);
605 if (iowatch.isset(serverfd)) {
606 while(server->readln(answer))
607 cout << answer << endl;