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_SELECT_H
32 # include <sys/select.h>
35 // socket(), connect()
36 #ifdef HAVE_SYS_SOCKET_H
37 # include <sys/socket.h>
51 using lyx::support::prefixIs;
53 using boost::scoped_ptr;
54 namespace fs = boost::filesystem;
66 string itoa(unsigned int i)
68 return boost::lexical_cast<string>(i);
72 // Parts stolen from lyx::support::DirList()
73 // Returns the absolute pathnames of all lyx local sockets
74 vector<fs::path> lyxSockets(string const & dir, string const & pid)
76 vector<fs::path> dirlist;
78 fs::path dirpath(dir);
80 if (!fs::exists(dirpath) || !fs::is_directory(dirpath)) {
81 lyxerr << dir << " does not exist or is not a directory."
86 fs::directory_iterator beg((fs::path(dir)));
87 fs::directory_iterator end;
89 for (; beg != end; ++beg) {
90 if (prefixIs(beg->leaf(), "lyx_tmpdir" + pid)) {
91 fs::path lyxsocket = *beg / "lyxsocket";
92 if (fs::exists(lyxsocket)) {
93 dirlist.push_back(lyxsocket);
102 namespace socktools {
105 int connect(string const & name)
107 int fd; // File descriptor for the socket
108 sockaddr_un addr; // Structure that hold the socket address
110 // char sun_path[108]
111 string::size_type len = name.size();
113 cerr << "lyxclient: Socket address '" << name
114 << "' too long." << endl;
117 // Synonims for AF_UNIX are AF_LOCAL and AF_FILE
118 addr.sun_family = AF_UNIX;
119 name.copy(addr.sun_path, 107);
120 addr.sun_path[len] = '\0';
122 if ((fd = ::socket(PF_UNIX, SOCK_STREAM, 0))== -1) {
123 cerr << "lyxclient: Could not create socket descriptor: "
124 << strerror(errno) << endl;
128 reinterpret_cast<struct sockaddr *>(&addr),
129 sizeof(addr)) == -1) {
130 cerr << "lyxclient: Could not connect to socket " << name
131 << ": " << strerror(errno) << endl;
135 if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
136 cerr << "lyxclient: Could not set O_NONBLOCK for socket: "
137 << strerror(errno) << endl;
145 } // namespace socktools
146 } // namespace support
150 // Class IOWatch ------------------------------------------------------------
171 void IOWatch::clear()
177 void IOWatch::addfd(int fd)
183 bool IOWatch::wait(double timeout)
186 to.tv_sec = static_cast<long int>(timeout);
187 to.tv_usec = static_cast<long int>((timeout - to.tv_sec)*1E6);
189 return select(FD_SETSIZE, &act,
190 (fd_set *)0, (fd_set *)0, &to);
197 return select(FD_SETSIZE, &act,
198 (fd_set *)0, (fd_set *)0, (timeval *)0);
202 bool IOWatch::isset(int fd) {
203 return FD_ISSET(fd, &act);
205 // ~Class IOWatch ------------------------------------------------------------
208 // Class LyXDataSocket -------------------------------------------------------
209 // Modified LyXDataSocket class for use with the client
210 class LyXDataSocket {
212 LyXDataSocket(string const &);
214 // File descriptor of the connection
217 bool connected() const;
218 // Line buffered input from the socket
219 bool readln(string &);
220 // Write the string + '\n' to the socket
221 void writeln(string const &);
223 // File descriptor for the data socket
225 // True if the connection is up
227 // buffer for input data
232 LyXDataSocket::LyXDataSocket(string const & address)
234 if ((fd_ = support::socktools::connect(address)) == -1) {
242 LyXDataSocket::~LyXDataSocket()
248 int LyXDataSocket::fd() const
254 bool LyXDataSocket::connected() const
260 // Returns true if there was a complete line to input
261 // A line is of the form <key>:<value>
262 // A line not of this form will not be passed
263 // The line read is splitted and stored in 'key' and 'value'
264 bool LyXDataSocket::readln(string & line)
266 int const charbuf_size = 100;
267 char charbuf[charbuf_size]; // buffer for the ::read() system call
269 string::size_type pos;
271 // read and store characters in buffer
272 while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
273 charbuf[count] = '\0'; // turn it into a c string
277 // Error conditions. The buffer must still be
278 // processed for lines read
279 if (count == 0) { // EOF -- connection closed
281 } else if ((count == -1) && (errno != EAGAIN)) { // IO error
282 cerr << "lyxclient: IO error." << endl;
286 // Cut a line from buffer
287 if ((pos = buffer.find('\n')) == string::npos)
288 return false; // No complete line stored
289 line = buffer.substr(0, pos);
290 buffer = buffer.substr(pos + 1);
295 // Write a line of the form <key>:<value> to the socket
296 void LyXDataSocket::writeln(string const & line)
298 string linen(line + '\n');
299 int size = linen.size();
300 int written = ::write(fd_, linen.c_str(), size);
301 if (written < size) { // Allways mean end of connection.
302 if ((written == -1) && (errno == EPIPE)) {
303 // The program will also receive a SIGPIPE
304 // that must be catched
305 cerr << "lyxclient: connection closed while writing."
308 // Anything else, including errno == EAGAIN, must be
309 // considered IO error. EAGAIN should never happen
310 // when line is small
311 cerr << "lyxclient: IO error: " << strerror(errno);
316 // ~Class LyXDataSocket -------------------------------------------------------
319 // Class CmdLineParser -------------------------------------------------------
320 class CmdLineParser {
322 typedef int (*optfunc)(vector<char *> const & args);
323 std::map<string, optfunc> helper;
324 std::map<string, bool> isset;
325 bool parse(int, char * []);
326 vector<char *> nonopt;
330 bool CmdLineParser::parse(int argc, char * argv[])
335 if (helper[argv[opt]]) {
336 isset[argv[opt]] = true;
338 while ((arg < argc) && (!helper[argv[arg]])) {
339 args.push_back(argv[arg]);
342 int taken = helper[argv[opt]](args);
347 if (argv[opt][0] == '-') {
348 if ((argv[opt][1] == '-')
349 && (argv[opt][2]== '\0')) {
352 nonopt.push_back(argv[opt]);
357 cerr << "lyxclient: unknown option "
358 << argv[opt] << endl;
362 nonopt.push_back(argv[opt]);
368 // ~Class CmdLineParser -------------------------------------------------------
376 cerr << "Usage: lyxclient [options]" << endl
377 << "Options are:" << endl
378 << " -a address set address of the lyx socket" << endl
379 << " -t directory set system temporary directory" << endl
380 << " -p pid select a running lyx by pid" << endl
381 << " -c command send a single command and quit" << endl
382 << " -g file row send a command to go to file and row" << endl
383 << " -n name set client name" << endl
384 << " -h name display this help end exit" << endl
385 << "If -a is not used, lyxclient will use the arguments of -t and -p to look for" << endl
386 << "a running lyx. If -t is not set, 'directory' defaults to /tmp. If -p is set," << endl
387 << "lyxclient will connect only to a lyx with the specified pid. Options -c and -g" << endl
388 << "cannot be set simultaneoulsly. If no -c or -g options are given, lyxclient" << endl
389 << "will read commands from standard input and disconnect when command read is BYE:"
394 int h(vector<char *> const &)
401 string clientName(support::itoa(::getppid()) + ">" + support::itoa(::getpid()));
403 int n(vector<char *> const & arg)
405 if (arg.size() < 1) {
406 cerr << "lyxclient: The option -n requires 1 argument."
415 string singleCommand;
418 int c(vector<char *> const & arg)
420 if (arg.size() < 1) {
421 cerr << "lyxclient: The option -c requires 1 argument."
425 singleCommand = arg[0];
430 int g(vector<char *> const & arg)
432 if (arg.size() < 2) {
433 cerr << "lyxclient: The option -g requires 2 arguments."
437 singleCommand = "LYXCMD:server-goto-file-row "
438 + string(arg[0]) + ' '
444 // 0 if LYXSOCKET is not set in the environment
445 char * serverAddress = getenv("LYXSOCKET");
448 int a(vector<char *> const & arg)
450 if (arg.size() < 1) {
451 cerr << "lyxclient: The option -a requires 1 argument."
455 // -a supercedes LYXSOCKET environment variable
456 serverAddress = arg[0];
461 string mainTmp(package().temp_dir());
464 int t(vector<char *> const & arg)
466 if (arg.size() < 1) {
467 cerr << "lyxclient: The option -t requires 1 argument."
476 string serverPid; // Init to empty string
479 int p(vector<char *> const & arg)
481 if (arg.size() < 1) {
482 cerr << "lyxclient: The option -p requires 1 argument."
491 } // namespace cmdline
497 int main(int argc, char * argv[])
499 lyxerr.rdbuf(cerr.rdbuf());
502 args.helper["-h"] = cmdline::h;
503 args.helper["-c"] = cmdline::c;
504 args.helper["-g"] = cmdline::g;
505 args.helper["-n"] = cmdline::n;
506 args.helper["-a"] = cmdline::a;
507 args.helper["-t"] = cmdline::t;
508 args.helper["-p"] = cmdline::p;
510 // Command line failure conditions:
511 if ((!args.parse(argc, argv))
512 || (args.isset["-c"] && args.isset["-g"])
513 || (args.isset["-a"] && args.isset["-p"])) {
518 scoped_ptr<LyXDataSocket> server;
520 if (cmdline::serverAddress) {
521 server.reset(new LyXDataSocket(cmdline::serverAddress));
522 if (!server->connected()) {
523 cerr << "lyxclient: " << "Could not connect to "
524 << cmdline::serverAddress << endl;
528 // We have to look for an address.
529 // serverPid can be empty.
530 vector<fs::path> addrs = support::lyxSockets(cmdline::mainTmp, cmdline::serverPid);
531 vector<fs::path>::const_iterator addr = addrs.begin();
532 vector<fs::path>::const_iterator end = addrs.end();
533 for (; addr != end; ++addr) {
534 server.reset(new LyXDataSocket(addr->string()));
535 if (server->connected())
537 lyxerr << "lyxclient: " << "Could not connect to "
538 << addr->string() << endl;
541 lyxerr << "lyxclient: No suitable server found."
545 cerr << "lyxclient: " << "Connected to " << addr->string() << endl;
548 int const serverfd = server->fd();
551 iowatch.addfd(serverfd);
553 // Used to read from server
557 server->writeln("HELLO:" + cmdline::clientName);
558 // wait at most 2 seconds until server responds
560 if (iowatch.isset(serverfd) && server->readln(answer)) {
561 if (prefixIs(answer, "BYE:")) {
562 cerr << "lyxclient: Server disconnected." << endl;
563 cout << answer << endl;
567 cerr << "lyxclient: No answer from server." << endl;
571 if (args.isset["-g"] || args.isset["-c"]) {
572 server->writeln(cmdline::singleCommand);
574 if (iowatch.isset(serverfd) && server->readln(answer)) {
576 if (prefixIs(answer, "ERROR:"))
580 cerr << "lyxclient: No answer from server." << endl;
585 // Take commands from stdin
586 iowatch.addfd(0); // stdin
587 bool saidbye = false;
588 while ((!saidbye) && server->connected()) {
590 if (iowatch.isset(0)) {
593 if (command == "BYE:") {
594 server->writeln("BYE:");
597 server->writeln("LYXCMD:" + command);
600 if (iowatch.isset(serverfd)) {
601 while(server->readln(answer))
602 cout << answer << endl;