]> git.lyx.org Git - lyx.git/blob - src/Server.cpp
On Windows, use true asynchronous I/O for the pipe server.
[lyx.git] / src / Server.cpp
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.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author Enrico Forestieri
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 /**
16   Docu   : To use the lyxserver define the name of the pipe in your
17            lyxrc:
18            \serverpipe "/home/myhome/.lyxpipe"
19            Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
20            Each message consists of a single line in ASCII. Input lines
21            (client -> LyX) have the following format:
22             "LYXCMD:<clientname>:<functionname>:<argument>"
23            Answers from LyX look like this:
24            "INFO:<clientname>:<functionname>:<data>"
25  [asierra970531] Or like this in case of error:
26            "ERROR:<clientname>:<functionname>:<error message>"
27            where <clientname> and <functionname> are just echoed.
28            If LyX notifies about a user defined extension key-sequence,
29            the line looks like this:
30            "NOTIFY:<key-sequence>"
31  [asierra970531] New server-only messages to implement a simple protocol
32            "LYXSRV:<clientname>:<protocol message>"
33            where <protocol message> can be "hello" or "bye". If hello is
34            received LyX will inform the client that it's listening its
35            messages, and 'bye' will inform that lyx is closing.
36
37            See development/server_monitor.c for an example client.
38   Purpose: implement a client/server lib for LyX
39 */
40
41 #include <config.h>
42
43 #include "Server.h"
44 #include "FuncRequest.h"
45 #include "LyXAction.h"
46 #include "LyXFunc.h"
47
48 #include "frontends/Application.h"
49
50 #include "support/debug.h"
51 #include "support/FileName.h"
52 #include "support/lassert.h"
53 #include "support/lstrings.h"
54 #include "support/os.h"
55
56 #include <boost/bind.hpp>
57
58 #ifdef _WIN32
59 #include <QCoreApplication>
60 #endif
61
62 #include <cerrno>
63 #ifdef HAVE_SYS_STAT_H
64 # include <sys/stat.h>
65 #endif
66 #include <fcntl.h>
67
68 using namespace std;
69 using namespace lyx::support;
70 using os::external_path;
71
72 namespace lyx {
73
74 /////////////////////////////////////////////////////////////////////
75 //
76 // LyXComm
77 //
78 /////////////////////////////////////////////////////////////////////
79
80 #if defined(_WIN32)
81
82 class ReadReadyEvent : public QEvent {
83 public:
84         ///
85         ReadReadyEvent(DWORD inpipe) : QEvent(QEvent::User), inpipe_(inpipe)
86         {}
87         ///
88         DWORD inpipe() const { return inpipe_; }
89
90 private:
91         DWORD inpipe_;
92 };
93
94 namespace {
95
96 bool closing_ = false;
97
98 void closing()
99 {
100         closing_ = true;
101 }
102
103
104 char * errormsg()
105 {
106         void * msgbuf;
107         DWORD error = GetLastError();
108         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
109                       FORMAT_MESSAGE_FROM_SYSTEM |
110                       FORMAT_MESSAGE_IGNORE_INSERTS,
111                       NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
112                       (LPTSTR) &msgbuf, 0, NULL);
113         return static_cast<char *>(msgbuf);
114 }
115
116
117 extern "C" {
118
119 DWORD WINAPI pipeServerWrapper(void * arg)
120 {
121         LyXComm * lyxcomm = reinterpret_cast<LyXComm *>(arg);
122         lyxcomm->pipeServer();
123         return 1;
124 }
125
126 }
127
128 } // namespace anon
129
130 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
131         : pipename_(pip), client_(cli), clientcb_(ccb), stopserver_(0)
132 {
133         // Ask Qt to notify us on quit.
134         qAddPostRoutine(closing);
135         ready_ = false;
136         openConnection();
137 }
138
139
140 void LyXComm::pipeServer()
141 {
142         DWORD i;
143
144         for (i = 0; i <= MAX_PIPES; ++i) {
145                 DWORD open_mode;
146                 string const pipename = external_path(pipeName(i));
147
148                 if (i < MAX_PIPES) {
149                         open_mode = PIPE_ACCESS_INBOUND;
150                         readbuf_[i].erase();
151                 } else {
152                         open_mode = PIPE_ACCESS_OUTBOUND;
153                         writebuf_.erase();
154                 }
155
156                 // Manual-reset event, initial state = signaled
157                 event_[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
158
159                 if (!event_[i]) {
160                         LYXERR0("LyXComm: Could not create event for pipe "
161                                 << pipename.c_str() << '\n' << errormsg());
162                         closeHandles(i);
163                         return;
164                 }
165
166                 pipe_[i].overlap.hEvent = event_[i];
167                 pipe_[i].handle = CreateNamedPipe(pipename.c_str(),
168                                 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
169                                 MAX_PIPES, PIPE_BUFSIZE, PIPE_BUFSIZE,
170                                 PIPE_TIMEOUT, NULL);
171
172                 if (pipe_[i].handle == INVALID_HANDLE_VALUE) {
173                         LYXERR0("LyXComm: Could not create pipe "
174                                 << pipename.c_str() << '\n' << errormsg());
175                         closeHandles(i);
176                         return;
177                 }
178
179                 startPipe(i);
180                 pipe_[i].state = pipe_[i].pending_io ?
181                         CONNECTING_STATE : (i < MAX_PIPES ? READING_STATE
182                                                           : WRITING_STATE);
183         }
184
185         // Add the stopserver_ event
186         event_[MAX_PIPES + 1] = stopserver_;
187
188         // We made it!
189         LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
190         ready_ = true;
191         DWORD status;
192         bool success;
193
194         while (!checkStopServer()) {
195                 // Indefinitely wait for the completion of an overlapped
196                 // read, write, or connect operation.
197                 DWORD wait = WaitForMultipleObjects(MAX_PIPES + 2, event_,
198                                                     FALSE, INFINITE);
199
200                 // Determine which pipe instance completed the operation.
201                 i = wait - WAIT_OBJECT_0;
202                 LASSERT(i >= 0 && i <= MAX_PIPES + 1, /**/);
203
204                 // Check whether we were waked up for stopping the pipe server.
205                 if (i == MAX_PIPES + 1)
206                         break;
207
208                 // Get the result if the operation was pending.
209                 if (pipe_[i].pending_io) {
210                         success = GetOverlappedResult(pipe_[i].handle,
211                                         &pipe_[i].overlap, &status, FALSE);
212
213                         switch (pipe_[i].state) {
214                         case CONNECTING_STATE:
215                                 // Pending connect operation
216                                 if (!success) {
217                                         DWORD const err = GetLastError();
218                                         if (i == MAX_PIPES
219                                             && err == ERROR_IO_INCOMPLETE) {
220                                                 // A reply on the output pipe
221                                                 // has not been read, still.
222                                                 // As we have only one instance
223                                                 // for output, we risk a stalled
224                                                 // pipe if no one reads it.
225                                                 // So, if a reader doesn't
226                                                 // appear within about 5 or 6
227                                                 // seconds, we reset it.
228                                                 static int count = 0;
229                                                 Sleep(100);
230                                                 if (++count == 50) {
231                                                         count = 0;
232                                                         resetPipe(i, true);
233                                                 }
234                                         } else
235                                                 LYXERR0("LyXComm: " << errormsg());
236                                         continue;
237                                 }
238                                 pipe_[i].state = i < MAX_PIPES ? READING_STATE
239                                                                : WRITING_STATE;
240                                 break;
241
242                         case READING_STATE:
243                                 // Pending read operation
244                                 LASSERT(i < MAX_PIPES, /**/);
245                                 if (!success || status == 0) {
246                                         resetPipe(i);
247                                         continue;
248                                 }
249                                 pipe_[i].nbytes = status;
250                                 pipe_[i].state = WRITING_STATE;
251                                 break;
252
253                         case WRITING_STATE:
254                                 // Pending write operation
255                                 LASSERT(i == MAX_PIPES, /**/);
256                                 if (!success || status != writebuf_.length()) {
257                                         resetPipe(i);
258                                         continue;
259                                 }
260                                 break;
261                         }
262                 }
263
264                 // Operate according to the pipe state
265                 switch (pipe_[i].state) {
266                 case READING_STATE:
267                         // The pipe instance is connected to a client
268                         // and is ready to read a request.
269                         LASSERT(i < MAX_PIPES, /**/);
270                         success = ReadFile(pipe_[i].handle,
271                                         pipe_[i].pipebuf, PIPE_BUFSIZE - 1,
272                                         &pipe_[i].nbytes, &pipe_[i].overlap);
273
274                         if (success && pipe_[i].nbytes != 0) {
275                                 // The read operation completed successfully.
276                                 pipe_[i].pending_io = false;
277                                 pipe_[i].state = WRITING_STATE;
278                                 continue;
279                         }
280
281                         if (!success && GetLastError() == ERROR_IO_PENDING) {
282                                 // The read operation is still pending.
283                                 pipe_[i].pending_io = true;
284                                 continue;
285                         }
286
287                         // Client closed connection (ERROR_BROKEN_PIPE) or
288                         // an error occurred; in either case, reset the pipe.
289                         if (GetLastError() != ERROR_BROKEN_PIPE) {
290                                 LYXERR0("LyXComm: " << errormsg());
291                                 if (!readbuf_[i].empty()) {
292                                         LYXERR0("LyXComm: truncated command: "
293                                                 << readbuf_[i]);
294                                         readbuf_[i].erase();
295                                 }
296                                 resetPipe(i, true);
297                         } else
298                                 resetPipe(i);
299                         break;
300
301                 case WRITING_STATE:
302                         if (i < MAX_PIPES) {
303                                 // The request was successfully read
304                                 // from the client; commit it.
305                                 ReadReadyEvent * event = new ReadReadyEvent(i);
306                                 QCoreApplication::postEvent(this,
307                                                 static_cast<QEvent *>(event));
308                                 // Wait for completion
309                                 while (pipe_[i].nbytes && !checkStopServer())
310                                         Sleep(100);
311                                 pipe_[i].pending_io = false;
312                                 pipe_[i].state = READING_STATE;
313                                 continue;
314                         }
315                         // Let's see whether we have a reply.
316                         if (writebuf_.length() == 0) {
317                                 // No, nothing to do.
318                                 pipe_[i].pending_io = false;
319                                 continue;
320                         }
321                         // Yep, deliver it.
322                         success = WriteFile(pipe_[i].handle,
323                                         writebuf_.c_str(), writebuf_.length(),
324                                         &status, &pipe_[i].overlap);
325
326                         if (success && status == writebuf_.length()) {
327                                 // The write operation completed successfully.
328                                 writebuf_.erase();
329                                 pipe_[i].pending_io = false;
330                                 resetPipe(i);
331                                 continue;
332                         }
333
334                         if (!success && (GetLastError() == ERROR_IO_PENDING)) {
335                                 // The write operation is still pending.
336                                 pipe_[i].pending_io = true;
337                                 continue;
338                         }
339
340                         // Client closed connection (ERROR_NO_DATA) or
341                         // an error occurred; in either case, reset the pipe.
342                         if (GetLastError() != ERROR_NO_DATA) {
343                                 LYXERR0("LyXComm: Error sending message: "
344                                         << writebuf_ << '\n' << errormsg());
345                                 resetPipe(i, true);
346                         } else
347                                 resetPipe(i);
348                         break;
349                 }
350         }
351
352         ready_ = false;
353         closeHandles(MAX_PIPES + 1);
354 }
355
356
357 void LyXComm::closeHandles(DWORD index)
358 {
359         for (int i = 0; i <= index; ++i) {
360                 if (event_[i]) {
361                         ResetEvent(event_[i]);
362                         CloseHandle(event_[i]);
363                 }
364                 if (pipe_[i].handle != INVALID_HANDLE_VALUE)
365                         CloseHandle(pipe_[i].handle);
366         }
367 }
368
369
370 bool LyXComm::event(QEvent * e)
371 {
372         if (e->type() == QEvent::User) {
373                 read_ready(static_cast<ReadReadyEvent *>(e)->inpipe());
374                 return true;
375         }
376         return false;
377 }
378
379
380 BOOL LyXComm::checkStopServer()
381 {
382         return WaitForSingleObject(stopserver_, 0) == WAIT_OBJECT_0 || closing_;
383 }
384
385
386 void LyXComm::startPipe(DWORD index)
387 {
388         pipe_[index].pending_io = false;
389
390         // Overlapped ConnectNamedPipe should return zero.
391         if (ConnectNamedPipe(pipe_[index].handle, &pipe_[index].overlap)) {
392                 // FIXME: What to do? Maybe the pipe server should be reset.
393                 LYXERR0("LyXComm: Could not connect pipe "
394                         << external_path(pipeName(index)) << '\n'
395                         << errormsg());
396                 return;
397         }
398
399         switch (GetLastError()) {
400         case ERROR_IO_PENDING:
401                 // The overlapped connection is in progress.
402                 pipe_[index].pending_io = true;
403                 break;
404
405         case ERROR_PIPE_CONNECTED:
406                 // Client is already connected, so signal an event.
407                 if (SetEvent(pipe_[index].overlap.hEvent))
408                         break;
409         default:
410                 // Anything else is an error.
411                 // FIXME: What to do? Maybe the pipe server should be reset.
412                 LYXERR0("LyXComm: An error occurred while connecting pipe "
413                         << external_path(pipeName(index)) << '\n'
414                         << errormsg());
415         }
416 }
417
418
419 void LyXComm::resetPipe(DWORD index, bool close_handle)
420 {
421         // This method is called when an error occurs or when a client
422         // closes the connection. We first disconnect the pipe instance,
423         // then reconnect it, ready to wait for another client.
424
425         if (!DisconnectNamedPipe(pipe_[index].handle)) {
426                 LYXERR0("LyXComm: Could not disconnect pipe "
427                         << external_path(pipeName(index)) << '\n'
428                         << errormsg());
429                 // What to do now? Let's try whether re-creating the pipe helps.
430                 close_handle = true;
431         }
432         if (close_handle) {
433                 DWORD const open_mode = index < MAX_PIPES ?
434                                 PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND;
435                 string const name = external_path(pipeName(index));
436
437                 CloseHandle(pipe_[index].handle);
438
439                 pipe_[index].handle = CreateNamedPipe(name.c_str(),
440                                 open_mode | FILE_FLAG_OVERLAPPED, PIPE_WAIT,
441                                 MAX_PIPES, PIPE_BUFSIZE, PIPE_BUFSIZE,
442                                 PIPE_TIMEOUT, NULL);
443
444                 if (pipe_[index].handle == INVALID_HANDLE_VALUE) {
445                         LYXERR0("LyXComm: Could not reset pipe "
446                                 << name << '\n' << errormsg());
447                         return;
448                 }
449                 if (index == MAX_PIPES)
450                         writebuf_.erase();
451                 else
452                         readbuf_[index].erase();
453         }
454         startPipe(index);
455         pipe_[index].state = pipe_[index].pending_io ?
456                         CONNECTING_STATE : (index < MAX_PIPES ? READING_STATE
457                                                               : WRITING_STATE);
458 }
459
460
461 void LyXComm::openConnection()
462 {
463         LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
464
465         // If we are up, that's an error
466         if (ready_) {
467                 LYXERR0("LyXComm: Already connected");
468                 return;
469         }
470         // We assume that we don't make it
471         ready_ = false;
472
473         if (pipename_.empty()) {
474                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
475                 return;
476         }
477
478         // Check whether the pipe name is being used by some other program.
479         if (!stopserver_ && WaitNamedPipe(inPipeName().c_str(), 0)) {
480                 LYXERR0("LyXComm: Pipe " << external_path(inPipeName())
481                         << " already exists.\nMaybe another instance of LyX"
482                            " is using it.");
483                 pipename_.erase();
484                 return;
485         }
486
487         // Manual-reset event, initial state = not signaled
488         stopserver_ = CreateEvent(NULL, TRUE, FALSE, NULL);
489         server_thread_ = CreateThread(NULL, 0, pipeServerWrapper,
490                                      static_cast<void *>(this), 0, NULL);
491         if (!server_thread_) {
492                 LYXERR0("LyXComm: Could not create pipe server thread\n"
493                         << errormsg());
494                 pipename_.erase();
495                 return;
496         }
497 }
498
499
500 /// Close pipes
501 void LyXComm::closeConnection()
502 {
503         LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
504
505         if (pipename_.empty()) {
506                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
507                 return;
508         }
509
510         if (!ready_) {
511                 LYXERR0("LyXComm: Already disconnected");
512                 return;
513         }
514
515         emergencyCleanup();
516 }
517
518
519 void LyXComm::emergencyCleanup()
520 {
521         if (ready_) {
522                 SetEvent(stopserver_);
523                 // Wait for the pipe server to finish
524                 WaitForSingleObject(server_thread_, INFINITE);
525                 CloseHandle(server_thread_);
526                 ResetEvent(stopserver_);
527                 CloseHandle(stopserver_);
528         }
529 }
530
531
532 void LyXComm::read_ready(DWORD inpipe)
533 {
534         // Turn the pipe buffer into a C string
535         DWORD const nbytes = pipe_[inpipe].nbytes;
536         pipe_[inpipe].pipebuf[nbytes] = '\0';
537
538         readbuf_[inpipe] += rtrim(pipe_[inpipe].pipebuf, "\r");
539
540         // Commit any commands read
541         while (readbuf_[inpipe].find('\n') != string::npos) {
542                 // split() grabs the entire string if
543                 // the delim /wasn't/ found. ?:-P
544                 string cmd;
545                 readbuf_[inpipe] = split(readbuf_[inpipe], cmd, '\n');
546                 cmd = rtrim(cmd, "\r");
547                 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << nbytes
548                         << ", readbuf_:" << readbuf_[inpipe]
549                         << ", cmd:" << cmd);
550                 if (!cmd.empty())
551                         clientcb_(client_, cmd);
552                         //\n or not \n?
553         }
554         // Signal that we are done.
555         pipe_[inpipe].nbytes = 0;
556 }
557
558
559 void LyXComm::send(string const & msg)
560 {
561         if (msg.empty()) {
562                 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
563                 return;
564         }
565
566         LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
567
568         if (pipename_.empty())
569                 return;
570
571         if (!ready_) {
572                 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
573                 return;
574         }
575
576         // Wait a couple of secs for completion of a previous write operation.
577         for (int count = 0; writebuf_.length() && count < 20; ++count)
578                 Sleep(100);
579
580         if (!writebuf_.length()) {
581                 writebuf_ = msg;
582                 // Tell the pipe server he has a job to do.
583                 SetEvent(pipe_[MAX_PIPES].overlap.hEvent);
584         } else {
585                 // Nope, output pipe is still busy. Most likely, a bad client
586                 // did not care to read the answer (JabRef is one such client).
587                 // Let's do a reset, otherwise the output pipe could remain
588                 // stalled if the pipe server failed to reset it.
589                 // This will remedy the output pipe stall, but the client will
590                 // get a broken pipe error.
591                 LYXERR0("LyXComm: Error sending message: " << msg
592                         << "\nLyXComm: Output pipe is stalled\n"
593                            "LyXComm: Resetting connection");
594                 closeConnection();
595                 if (!checkStopServer())
596                         openConnection();
597         }
598 }
599
600
601 string const LyXComm::pipeName(DWORD index) const
602 {
603         return index < MAX_PIPES ? inPipeName() : outPipeName();
604 }
605
606
607 #elif !defined (HAVE_MKFIFO)
608 // We provide a stub class that disables the lyxserver.
609
610 LyXComm::LyXComm(string const &, Server *, ClientCallbackfct)
611 {}
612
613 void LyXComm::openConnection()
614 {}
615
616
617 void LyXComm::closeConnection()
618 {}
619
620
621 int LyXComm::startPipe(string const & filename, bool write)
622 {
623         return -1;
624 }
625
626
627 void LyXComm::endPipe(int & fd, string const & filename, bool write)
628 {}
629
630
631 void LyXComm::emergencyCleanup()
632 {}
633
634 void LyXComm::read_ready()
635 {}
636
637
638 void LyXComm::send(string const & msg)
639 {}
640
641
642 #else // defined (HAVE_MKFIFO)
643
644
645 LyXComm::LyXComm(string const & pip, Server * cli, ClientCallbackfct ccb)
646         : pipename_(pip), client_(cli), clientcb_(ccb)
647 {
648         ready_ = false;
649         openConnection();
650 }
651
652
653 void LyXComm::openConnection()
654 {
655         LYXERR(Debug::LYXSERVER, "LyXComm: Opening connection");
656
657         // If we are up, that's an error
658         if (ready_) {
659                 lyxerr << "LyXComm: Already connected" << endl;
660                 return;
661         }
662         // We assume that we don't make it
663         ready_ = false;
664
665         if (pipename_.empty()) {
666                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
667                 return;
668         }
669
670         infd_ = startPipe(inPipeName(), false);
671         if (infd_ == -1)
672                 return;
673
674         outfd_ = startPipe(outPipeName(), true);
675         if (outfd_ == -1) {
676                 endPipe(infd_, inPipeName(), false);
677                 return;
678         }
679
680         if (fcntl(outfd_, F_SETFL, O_NONBLOCK) < 0) {
681                 lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName()
682                        << '\n' << strerror(errno) << endl;
683                 return;
684         }
685
686         // We made it!
687         ready_ = true;
688         LYXERR(Debug::LYXSERVER, "LyXComm: Connection established");
689 }
690
691
692 /// Close pipes
693 void LyXComm::closeConnection()
694 {
695         LYXERR(Debug::LYXSERVER, "LyXComm: Closing connection");
696
697         if (pipename_.empty()) {
698                 LYXERR(Debug::LYXSERVER, "LyXComm: server is disabled, nothing to do");
699                 return;
700         }
701
702         if (!ready_) {
703                 LYXERR0("LyXComm: Already disconnected");
704                 return;
705         }
706
707         endPipe(infd_, inPipeName(), false);
708         endPipe(outfd_, outPipeName(), true);
709
710         ready_ = false;
711 }
712
713
714 int LyXComm::startPipe(string const & file, bool write)
715 {
716         static bool stalepipe = false;
717         FileName const filename(file);
718         if (filename.exists()) {
719                 if (!write) {
720                         // Let's see whether we have a stale pipe.
721                         int fd = ::open(filename.toFilesystemEncoding().c_str(),
722                                         O_WRONLY | O_NONBLOCK);
723                         if (fd >= 0) {
724                                 // Another LyX instance is using it.
725                                 ::close(fd);
726                         } else if (errno == ENXIO) {
727                                 // No process is reading from the other end.
728                                 stalepipe = true;
729                                 LYXERR(Debug::LYXSERVER,
730                                         "LyXComm: trying to remove "
731                                         << filename);
732                                 filename.removeFile();
733                         }
734                 } else if (stalepipe) {
735                         LYXERR(Debug::LYXSERVER, "LyXComm: trying to remove "
736                                 << filename);
737                         filename.removeFile();
738                         stalepipe = false;
739                 }
740                 if (filename.exists()) {
741                         lyxerr << "LyXComm: Pipe " << filename
742                                << " already exists.\nIf no other LyX program"
743                                   " is active, please delete the pipe by hand"
744                                   " and try again."
745                                << endl;
746                         pipename_.erase();
747                         return -1;
748                 }
749         }
750
751         if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) {
752                 lyxerr << "LyXComm: Could not create pipe " << filename << '\n'
753                        << strerror(errno) << endl;
754                 return -1;
755         }
756         int const fd = ::open(filename.toFilesystemEncoding().c_str(),
757                               write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK));
758
759         if (fd < 0) {
760                 lyxerr << "LyXComm: Could not open pipe " << filename << '\n'
761                        << strerror(errno) << endl;
762                 filename.removeFile();
763                 return -1;
764         }
765
766         if (!write) {
767                 theApp()->registerSocketCallback(fd,
768                         boost::bind(&LyXComm::read_ready, this));
769         }
770
771         return fd;
772 }
773
774
775 void LyXComm::endPipe(int & fd, string const & filename, bool write)
776 {
777         if (fd < 0)
778                 return;
779
780         if (!write)
781                 theApp()->unregisterSocketCallback(fd);
782
783         if (::close(fd) < 0) {
784                 lyxerr << "LyXComm: Could not close pipe " << filename
785                        << '\n' << strerror(errno) << endl;
786         }
787
788         if (FileName(filename).removeFile() < 0) {
789                 lyxerr << "LyXComm: Could not remove pipe " << filename
790                        << '\n' << strerror(errno) << endl;
791         }
792
793         fd = -1;
794 }
795
796
797 void LyXComm::emergencyCleanup()
798 {
799         if (!pipename_.empty()) {
800                 endPipe(infd_, inPipeName(), false);
801                 endPipe(outfd_, outPipeName(), true);
802         }
803 }
804
805
806 // Receives messages and sends then to client
807 void LyXComm::read_ready()
808 {
809         // FIXME: make read_buffer_ a class-member for multiple sessions
810         static string read_buffer_;
811         read_buffer_.erase();
812
813         int const charbuf_size = 100;
814         char charbuf[charbuf_size];
815
816         // As O_NONBLOCK is set, until no data is available for reading,
817         // read() doesn't block but returns -1 and set errno to EAGAIN.
818         // After a client that opened the pipe for writing, closes it
819         // (and no other client is using the pipe), read() would always
820         // return 0 and thus the connection has to be reset.
821
822         errno = 0;
823         int status;
824         // the single = is intended here.
825         while ((status = ::read(infd_, charbuf, charbuf_size - 1))) {
826
827                 if (status > 0) {
828                         charbuf[status] = '\0'; // turn it into a c string
829                         read_buffer_ += rtrim(charbuf, "\r");
830                         // commit any commands read
831                         while (read_buffer_.find('\n') != string::npos) {
832                                 // split() grabs the entire string if
833                                 // the delim /wasn't/ found. ?:-P
834                                 string cmd;
835                                 read_buffer_= split(read_buffer_, cmd,'\n');
836                                 LYXERR(Debug::LYXSERVER, "LyXComm: status:" << status
837                                         << ", read_buffer_:" << read_buffer_
838                                         << ", cmd:" << cmd);
839                                 if (!cmd.empty())
840                                         clientcb_(client_, cmd);
841                                         //\n or not \n?
842                         }
843                 } else {
844                         if (errno == EAGAIN) {
845                                 // Nothing to read, continue
846                                 errno = 0;
847                                 return;
848                         }
849                         // An error occurred, better bailing out
850                         LYXERR0("LyXComm: " << strerror(errno));
851                         if (!read_buffer_.empty()) {
852                                 LYXERR0("LyXComm: truncated command: " << read_buffer_);
853                                 read_buffer_.erase();
854                         }
855                         break; // reset connection
856                 }
857         }
858
859         // The connection gets reset when read() returns 0 (meaning that the
860         // last client closed the pipe) or an error occurred, in which case
861         // read() returns -1 and errno != EAGAIN.
862         closeConnection();
863         openConnection();
864         errno = 0;
865 }
866
867
868 void LyXComm::send(string const & msg)
869 {
870         if (msg.empty()) {
871                 LYXERR0("LyXComm: Request to send empty string. Ignoring.");
872                 return;
873         }
874
875         LYXERR(Debug::LYXSERVER, "LyXComm: Sending '" << msg << '\'');
876
877         if (pipename_.empty()) return;
878
879         if (!ready_) {
880                 LYXERR0("LyXComm: Pipes are closed. Could not send " << msg);
881         } else if (::write(outfd_, msg.c_str(), msg.length()) < 0) {
882                 lyxerr << "LyXComm: Error sending message: " << msg
883                        << '\n' << strerror(errno)
884                        << "\nLyXComm: Resetting connection" << endl;
885                 closeConnection();
886                 openConnection();
887         }
888 }
889
890 #endif // defined (HAVE_MKFIFO)
891
892
893 string const LyXComm::inPipeName() const
894 {
895         return pipename_ + ".in";
896 }
897
898
899 string const LyXComm::outPipeName() const
900 {
901         return pipename_ + ".out";
902 }
903
904
905 /////////////////////////////////////////////////////////////////////
906 //
907 // Server
908 //
909 /////////////////////////////////////////////////////////////////////
910
911 void ServerCallback(Server * server, string const & msg)
912 {
913         server->callback(msg);
914 }
915
916 Server::Server(LyXFunc * f, string const & pipes)
917         : numclients_(0), func_(f), pipes_(pipes, this, &ServerCallback)
918 {}
919
920
921 Server::~Server()
922 {
923         // say goodbye to clients so they stop sending messages
924         // send as many bye messages as there are clients,
925         // each with client's name.
926         string message;
927         for (int i = 0; i != numclients_; ++i) {
928                 message = "LYXSRV:" + clients_[i] + ":bye\n";
929                 pipes_.send(message);
930         }
931 }
932
933
934 int compare(char const * a, char const * b, unsigned int len)
935 {
936         using namespace std;
937         return strncmp(a, b, len);
938 }
939
940
941 // Handle data gotten from communication, called by LyXComm
942 void Server::callback(string const & msg)
943 {
944         LYXERR(Debug::LYXSERVER, "Server: Received: '" << msg << '\'');
945
946         char const * p = msg.c_str();
947
948         // --- parse the string --------------------------------------------
949         //
950         //  Format: LYXCMD:<client>:<func>:<argstring>\n
951         //
952         bool server_only = false;
953         while (*p) {
954                 // --- 1. check 'header' ---
955
956                 if (compare(p, "LYXSRV:", 7) == 0) {
957                         server_only = true;
958                 } else if (0 != compare(p, "LYXCMD:", 7)) {
959                         lyxerr << "Server: Unknown request \""
960                                << p << '"' << endl;
961                         return;
962                 }
963                 p += 7;
964
965                 // --- 2. for the moment ignore the client name ---
966                 string client;
967                 while (*p && *p != ':')
968                         client += char(*p++);
969                 if (*p == ':')
970                         ++p;
971                 if (!*p)
972                         return;
973
974                 // --- 3. get function name ---
975                 string cmd;
976                 while (*p && *p != ':')
977                         cmd += char(*p++);
978
979                 // --- 4. parse the argument ---
980                 string arg;
981                 if (!server_only && *p == ':' && *(++p)) {
982                         while (*p && *p != '\n')
983                                 arg += char(*p++);
984                         if (*p) ++p;
985                 }
986
987                 LYXERR(Debug::LYXSERVER, "Server: Client: '" << client
988                         << "' Command: '" << cmd << "' Argument: '" << arg << '\'');
989
990                 // --- lookup and exec the command ------------------
991
992                 if (server_only) {
993                         string buf;
994                         // return the greeting to inform the client that
995                         // we are listening.
996                         if (cmd == "hello") {
997                                 // One more client
998                                 if (numclients_ == MAX_CLIENTS) { //paranoid check
999                                         LYXERR(Debug::LYXSERVER, "Server: too many clients...");
1000                                         return;
1001                                 }
1002                                 int i = 0;
1003                                 while (!clients_[i].empty() && i < numclients_)
1004                                         ++i;
1005                                 clients_[i] = client;
1006                                 ++numclients_;
1007                                 buf = "LYXSRV:" + client + ":hello\n";
1008                                 LYXERR(Debug::LYXSERVER, "Server: Greeting " << client);
1009                                 pipes_.send(buf);
1010                         } else if (cmd == "bye") {
1011                                 // If clients_ == 0 maybe we should reset the pipes
1012                                 // to prevent fake callbacks
1013                                 int i = 0; //look if client is registered
1014                                 for (; i < numclients_; ++i) {
1015                                         if (clients_[i] == client)
1016                                                 break;
1017                                 }
1018                                 if (i < numclients_) {
1019                                         --numclients_;
1020                                         clients_[i].erase();
1021                                         LYXERR(Debug::LYXSERVER, "Server: Client "
1022                                                 << client << " said goodbye");
1023                                 } else {
1024                                         LYXERR(Debug::LYXSERVER,
1025                                                 "Server: ignoring bye messge from unregistered client" << client);
1026                                 }
1027                         } else {
1028                                 LYXERR0("Server: Undefined server command " << cmd << '.');
1029                         }
1030                         return;
1031                 }
1032
1033                 if (!cmd.empty()) {
1034                         // which lyxfunc should we let it connect to?
1035                         // The correct solution would be to have a
1036                         // specialized (non-gui) BufferView. But how do
1037                         // we do it now? Probably we should just let it
1038                         // connect to the lyxfunc in the single LyXView we
1039                         // support currently. (Lgb)
1040
1041                         func_->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg));
1042                         string const rval = to_utf8(func_->getMessage());
1043
1044                         // all commands produce an INFO or ERROR message
1045                         // in the output pipe, even if they do not return
1046                         // anything. See chapter 4 of Customization doc.
1047                         string buf;
1048                         if (func_->errorStat())
1049                                 buf = "ERROR:";
1050                         else
1051                                 buf = "INFO:";
1052                         buf += client + ':' + cmd + ':' +  rval + '\n';
1053                         pipes_.send(buf);
1054
1055                         // !!! we don't do any error checking -
1056                         //  if the client won't listen, the
1057                         //  message is lost and others too
1058                         //  maybe; so the client should empty
1059                         //  the outpipe before issuing a request.
1060
1061                         // not found
1062                 }
1063         }  // while *p
1064 }
1065
1066
1067 // Send a notify message to a client, called by WorkAreaKeyPress
1068 void Server::notifyClient(string const & s)
1069 {
1070         pipes_.send("NOTIFY:" + s + "\n");
1071 }
1072
1073
1074 } // namespace lyx
1075
1076 #ifdef _WIN32
1077 #include "moc_Server.cpp"
1078 #endif