Back to index

texmacs  1.0.7.15
pipe_link.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : pipe_link.cpp
00004 * DESCRIPTION: TeXmacs links by pipes
00005 * COPYRIGHT  : (C) 2000  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "basic.hpp"
00013 
00014 #if !(defined (QTTEXMACS) && (defined (__MINGW__) || defined (__MINGW32__) || defined (QTPIPES)))
00015 
00016 #include "tm_link.hpp"
00017 #include "socket_notifier.hpp"
00018 #include "sys_utils.hpp"
00019 #include "hashset.hpp"
00020 #include "iterator.hpp"
00021 #include "timer.hpp"
00022 #include <stdio.h>
00023 #include <string.h>
00024 #ifdef __MINGW32__
00025 //#define PATTERN WIN_PATTERN
00026 //#include <winsock.h>
00027 //#undef PATTERN
00028 #else
00029 #include <unistd.h>
00030 #include <signal.h>
00031 #include <sys/wait.h>
00032 #endif
00033 #ifndef __APPLE__
00034 #include <malloc.h>
00035 #endif
00036 
00037 hashset<pointer> pipe_link_set;
00038 void pipe_callback (void *obj, void *info);
00039 extern char **environ;
00040 
00041 #define STDIN 0
00042 #define STDOUT 1
00043 #define STDERR 2
00044 #define IN 0
00045 #define OUT 1
00046 #define TERMCHAR '\1'
00047 
00048 /******************************************************************************
00049 * The pipe_link class
00050 ******************************************************************************/
00051 
00052 struct pipe_link_rep: tm_link_rep {
00053   string cmd;           // command for launching the pipe
00054   int    pid;           // process identifier of the child
00055   int    pp_in [2];     // for data going to the child
00056   int    pp_out[2];     // for data coming from the child
00057   int    pp_err[2];     // for error messages coming from the child
00058   int    in;            // file descriptor for data going to the child
00059   int    out;           // file descriptor for data coming from the child
00060   int    err;           // file descriptor for errors coming from the child
00061 
00062   string outbuf;        // pending output from plugin
00063   string errbuf;        // pending errors from plugin
00064 
00065   socket_notifier snout, snerr;
00066   
00067 public:
00068   pipe_link_rep (string cmd);
00069   ~pipe_link_rep ();
00070 
00071   string  start ();
00072   void    write (string s, int channel);
00073   string& watch (int channel);
00074   string  read (int channel);
00075   void    listen (int msecs);
00076   void    interrupt ();
00077   void    stop ();
00078 
00079   void    feed (int channel);
00080 };
00081 
00082 pipe_link_rep::pipe_link_rep (string cmd2): cmd (cmd2) {
00083   pipe_link_set->insert ((pointer) this);
00084   in     = pp_in [0]= pp_in [1]= -1;
00085   out    = pp_out[0]= pp_out[1]= -1;
00086   err    = pp_err[0]= pp_err[1]= -1;
00087   outbuf = "";
00088   errbuf = "";
00089   alive  = false;
00090 }
00091 
00092 pipe_link_rep::~pipe_link_rep () {
00093   stop ();
00094   pipe_link_set->remove ((pointer) this);
00095 }
00096 
00097 tm_link
00098 make_pipe_link (string cmd) {
00099   return tm_new<pipe_link_rep> (cmd);
00100 }
00101 
00102 void
00103 close_all_pipes () {
00104 #ifndef __MINGW32__
00105   iterator<pointer> it= iterate (pipe_link_set);
00106   while (it->busy()) {
00107     pipe_link_rep* con= (pipe_link_rep*) it->next();
00108     if (con->alive) {
00109       if (-1 != killpg(con->pid,SIGTERM)) {
00110        sleep(2);
00111        killpg(con->pid,SIGKILL);
00112       }
00113       con->alive= false;
00114     }
00115   }
00116 #endif
00117 }
00118 
00119 void
00120 process_all_pipes () {
00121   iterator<pointer> it= iterate (pipe_link_set);
00122   while (it->busy()) {
00123     pipe_link_rep* con= (pipe_link_rep*) it->next();
00124     if (con->alive) con->apply_command ();
00125   }
00126 }
00127 
00128 /******************************************************************************
00129 * Routines for pipe_links
00130 ******************************************************************************/
00131 
00132 #ifndef __MINGW32__
00133 void
00134 execute_shell (string s) {
00135   char *_s= as_charp (s);
00136   char *argv[4];
00137   argv[0] = const_cast<char*> ("sh");
00138   argv[1] = const_cast<char*> ("-c");
00139   argv[2] = _s;
00140   argv[3] = NULL;
00141   execve ("/bin/sh", argv, environ);
00142   tm_delete_array (_s);
00143 }
00144 #endif
00145 
00146 string
00147 pipe_link_rep::start () {
00148 #ifndef __MINGW32__
00149   if (alive) return "busy";
00150   if (DEBUG_AUTO) cout << "TeXmacs] Launching '" << cmd << "'\n";
00151 
00152   int e1= pipe (pp_in ); (void) e1;
00153   int e2= pipe (pp_out); (void) e2;
00154   int e3= pipe (pp_err); (void) e3;
00155   pid= fork ();
00156   if (pid==0) { // the child
00157     setsid();
00158     close (pp_in  [OUT]);
00159     close (pp_out [IN ]);
00160     close (pp_err [IN ]);
00161     dup2  (pp_in  [IN ], STDIN );
00162     close (pp_in  [IN ]);
00163     dup2  (pp_out [OUT], STDOUT);
00164     close (pp_out [OUT]);
00165     dup2  (pp_err [OUT], STDERR);
00166     close (pp_err [OUT]);
00167 
00168     execute_shell (cmd);
00169     exit (127);
00170     // exit (system (cmd) != 0);
00171   }
00172   else { // the main process
00173     in = pp_in  [OUT];
00174     close (pp_in [IN]);
00175     out= pp_out [IN ];
00176     close (pp_out [OUT]);
00177     err= pp_err [IN ];
00178     close (pp_err [OUT]);
00179 
00180     alive= true;
00181     snout = socket_notifier (out, &pipe_callback, this, NULL);
00182     snerr = socket_notifier (err, &pipe_callback, this, NULL);
00183     add_notifier (snout);
00184     add_notifier (snerr);
00185     
00186     if (/* !banner */ true) return "ok";
00187     else {
00188       int r;
00189       char outbuf[1024];
00190       r= ::read (out, outbuf, 1024);
00191       if (r == 1 && outbuf[0] == TERMCHAR) return "ok";
00192       alive= false;
00193       if (-1 != killpg(pid,SIGTERM)) {
00194        sleep(2);
00195        killpg(pid,SIGKILL);
00196       }
00197       wait (NULL);
00198       if (r == -1) return "Error: the application does not reply";
00199       else
00200        return "Error: the application did not send its usual startup banner";
00201     }
00202   }
00203 #else
00204   return "Error: pipes not implemented";
00205 #endif
00206 }
00207 
00208 #ifndef __MINGW32__
00209 static string
00210 debug_io_string (string s) {
00211   int i, n= N(s);
00212   string r;
00213   for (i=0; i<n; i++) {
00214     unsigned char c= (unsigned char) s[i];
00215     if (c == DATA_BEGIN) r << "[BEGIN]";
00216     else if (c == DATA_END) r << "[END]";
00217     else if (c == DATA_COMMAND) r << "[COMMAND]";
00218     else if (c == DATA_ESCAPE) r << "[ESCAPE]";
00219     else r << s[i];
00220   }
00221   return r;
00222 }
00223 #endif
00224 
00225 void
00226 pipe_link_rep::write (string s, int channel) {
00227 #ifndef __MINGW32__
00228   if ((!alive) || (channel != LINK_IN)) return;
00229   if (DEBUG_IO) cout << "[INPUT]" << debug_io_string (s);
00230   char* _s= as_charp (s);
00231   int err= ::write (in, _s, N(s));
00232   (void) err;
00233   tm_delete_array (_s);
00234 #endif
00235 }
00236 
00237 void
00238 pipe_link_rep::feed (int channel) {
00239 #ifndef __MINGW32__
00240   if ((!alive) || ((channel != LINK_OUT) && (channel != LINK_ERR))) return;
00241   int r;
00242   char tempout[1024];
00243   if (channel == LINK_OUT) r = ::read (out, tempout, 1024);
00244   else r = ::read (err, tempout, 1024);
00245   if (r == -1) {
00246     cerr << "TeXmacs] read failed for#'" << cmd << "'\n";
00247     wait (NULL);
00248   }
00249   else if (r == 0) {
00250     if (-1 != killpg(pid,SIGTERM)) {
00251       sleep(2);
00252       killpg(pid,SIGKILL);
00253     }
00254 
00255     alive= false;
00256     remove_notifier (snout);      
00257     remove_notifier (snerr);      
00258   }
00259   else {
00260     if (DEBUG_IO) cout << debug_io_string (string (tempout, r));
00261     if (channel == LINK_OUT) outbuf << string (tempout, r);
00262     else errbuf << string (tempout, r);
00263   }
00264 #endif
00265 }
00266 
00267 string&
00268 pipe_link_rep::watch (int channel) {
00269   static string empty_string= "";
00270   if (channel == LINK_OUT) return outbuf;
00271   else if (channel == LINK_ERR) return errbuf;
00272   else return empty_string;
00273 }
00274 
00275 string
00276 pipe_link_rep::read (int channel) {
00277   if (channel == LINK_OUT) {
00278     string r= outbuf;
00279     outbuf= "";
00280     return r;
00281   }
00282   else if (channel == LINK_ERR) {
00283     string r= errbuf;
00284     errbuf= "";
00285     return r;
00286   }
00287   else return string("");
00288 }
00289 
00290 void
00291 pipe_link_rep::listen (int msecs) {
00292   if (!alive) return;
00293   time_t wait_until= texmacs_time () + msecs;
00294   while ((outbuf == "") && (errbuf == "")) {
00295     fd_set rfds;
00296     FD_ZERO (&rfds);
00297     FD_SET (out, &rfds);
00298     FD_SET (err, &rfds);
00299     struct timeval tv;
00300     tv.tv_sec  = msecs / 1000;
00301     tv.tv_usec = 1000 * (msecs % 1000);
00302     int nr= select (max (out, err) + 1, &rfds, NULL, NULL, &tv);
00303     if (nr != 0 && FD_ISSET (out, &rfds)) feed (LINK_OUT);
00304     if (nr != 0 && FD_ISSET (err, &rfds)) feed (LINK_ERR);
00305     if (texmacs_time () - wait_until > 0) break;
00306   }
00307 }
00308 
00309 void
00310 pipe_link_rep::interrupt () {
00311 #ifndef __MINGW32__
00312   if (!alive) return;
00313   killpg (pid, SIGINT);
00314 #endif
00315 }
00316 
00317 void
00318 pipe_link_rep::stop () {
00319 #ifndef __MINGW32__
00320   if (!alive) return;
00321   if (-1 != killpg(pid,SIGTERM)) {
00322     sleep(2);
00323     killpg(pid,SIGKILL);
00324   }
00325   alive= false;    
00326   close (in);
00327   alive= false;
00328   wait (NULL);
00329 
00330   remove_notifier (snout);
00331   remove_notifier (snerr);
00332 #endif
00333 }
00334 
00335 /******************************************************************************
00336 * Call back for new information on pipe
00337 ******************************************************************************/
00338 
00339 void pipe_callback (void *obj, void *info) {
00340 #ifndef __MINGW32__
00341   (void) info;
00342   pipe_link_rep* con= (pipe_link_rep*) obj;  
00343   bool busy= true;
00344   bool news= false;
00345   while (busy) {
00346     fd_set rfds;
00347     FD_ZERO (&rfds);
00348     int max_fd= max (con->err, con->out) + 1;
00349     FD_SET (con->out, &rfds);
00350     FD_SET (con->err, &rfds);
00351   
00352     struct timeval tv;
00353     tv.tv_sec  = 0;
00354     tv.tv_usec = 0;
00355     select (max_fd, &rfds, NULL, NULL, &tv);
00356 
00357     busy= false;
00358     if (con->alive && FD_ISSET (con->out, &rfds)) {
00359       //cout << "pipe_callback OUT" << LF;
00360       con->feed (LINK_OUT);
00361       busy= news= true;
00362     }
00363     if (con->alive && FD_ISSET (con->err, &rfds)) {
00364       //cout << "pipe_callback ERR" << LF;
00365       con->feed (LINK_ERR);
00366       busy= news= true;
00367     }
00368   }
00369   /* FIXME: find out the appropriate place to call the callback
00370      Currently, the callback is called in tm_server_rep::interpose_handler */
00371   if (!is_nil (con->feed_cmd) && news) {
00372     //cout << "pipe_callback APPLY" << LF;
00373     if (!is_nil (con->feed_cmd))
00374       con->feed_cmd->apply (); // call the data processor
00375   }
00376 #endif
00377 }
00378 
00379 #endif // !(defined (QTTEXMACS) && (defined (__MINGW__) || defined (__MINGW32__)))