Home | History | Annotate | Download | only in util
      1 /* based on concepts from the mutt filter code...
      2  *
      3  * This code basically does what popen should have been... and what
      4  * popen2/popen3/popen4 in python do... it allows you access to
      5  * as many of stdin/stdout/stderr for a sub program as you want, instead
      6  * of just one (which is what popen is).
      7  */
      8 
      9 #include "cs_config.h"
     10 
     11 #include <unistd.h>
     12 #include <stdio.h>
     13 #include <stdlib.h>
     14 #include <sys/wait.h>
     15 
     16 #include "util/neo_misc.h"
     17 #include "util/neo_err.h"
     18 #include "util/filter.h"
     19 
     20 
     21 NEOERR *filter_wait (pid_t pid, int options, int *exitcode)
     22 {
     23   int r;
     24   pid_t rpid;
     25 
     26   rpid = waitpid (pid, &r, options);
     27   if (WIFEXITED(r))
     28   {
     29     r = WEXITSTATUS(r);
     30     if (exitcode)
     31     {
     32       *exitcode = r;
     33       /* If they're asking for the exit code, we don't generate an error */
     34       return STATUS_OK;
     35     }
     36     if (r == 0) return STATUS_OK;
     37     else return nerr_raise(NERR_SYSTEM, "Child %d returned status %d:", rpid,
     38                            r);
     39   }
     40   if (WIFSIGNALED(r))
     41   {
     42     r = WTERMSIG(r);
     43     return nerr_raise(NERR_SYSTEM, "Child %d died on signal %d:", rpid, r);
     44   }
     45   if (WIFSTOPPED(r))
     46   {
     47     r = WSTOPSIG(r);
     48     return nerr_raise(NERR_SYSTEM, "Child %d stopped on signal %d:", rpid, r);
     49   }
     50 
     51   return nerr_raise(NERR_ASSERT, "ERROR: waitpid(%d, %d) returned (%d, %d)",
     52                     pid, options, rpid, r);
     53 }
     54 
     55 NEOERR *filter_create_fd (const char *cmd, int *fdin, int *fdout, int *fderr,
     56                           pid_t *pid)
     57 {
     58   int pi[2]={-1,-1}, po[2]={-1,-1}, pe[2]={-1,-1};
     59   int rpid;
     60 
     61   *pid = 0;
     62 
     63   if (fdin)
     64   {
     65     *fdin = 0;
     66     if (pipe (pi) == -1)
     67       return nerr_raise_errno(NERR_SYSTEM,
     68                               "Unable to open in pipe for command: %s", cmd);
     69   }
     70 
     71   if (fdout)
     72   {
     73     *fdout = 0;
     74     if (pipe (po) == -1)
     75     {
     76       if (fdin)
     77       {
     78 	close (pi[0]);
     79 	close (pi[1]);
     80       }
     81       return nerr_raise_errno(NERR_SYSTEM,
     82                               "Unable to open out pipe for command: %s", cmd);
     83     }
     84   }
     85 
     86   if (fderr)
     87   {
     88     *fderr = 0;
     89     if (pipe (pe) == -1)
     90     {
     91       if (fdin)
     92       {
     93 	close (pi[0]);
     94 	close (pi[1]);
     95       }
     96       if (fdout)
     97       {
     98 	close (po[0]);
     99 	close (po[1]);
    100       }
    101       return nerr_raise_errno(NERR_SYSTEM, "Unable to open err pipe for command: %s", cmd);
    102     }
    103   }
    104 
    105   /* block signals */
    106 
    107   if ((rpid = fork ()) == 0)
    108   {
    109     /* unblock signals */
    110 
    111     if (fdin)
    112     {
    113       close (pi[1]);
    114       dup2 (pi[0], 0);
    115       close (pi[0]);
    116     }
    117 
    118     if (fdout)
    119     {
    120       close (po[0]);
    121       dup2 (po[1], 1);
    122       close (po[1]);
    123     }
    124 
    125     if (fderr)
    126     {
    127       close (pe[0]);
    128       dup2 (pe[1], 2);
    129       close (pe[1]);
    130     }
    131 
    132     execl ("/bin/sh", "sh", "-c", cmd, (void *)NULL);
    133     _exit (127);
    134   }
    135   else if (rpid == -1)
    136   {
    137     /* unblock signals */
    138     if (fdin)
    139     {
    140       close (pi[0]);
    141       close (pi[1]);
    142     }
    143     if (fdout)
    144     {
    145       close (po[0]);
    146       close (po[1]);
    147     }
    148     if (fderr)
    149     {
    150       close (pe[0]);
    151       close (pe[1]);
    152     }
    153     return nerr_raise_errno(NERR_SYSTEM, "Unable to fork for command: %s", cmd);
    154   }
    155 
    156   if (fdout)
    157   {
    158     close (po[1]);
    159     *fdout = po[0];
    160   }
    161   if (fdin)
    162   {
    163     close (pi[0]);
    164     *fdin = pi[1];
    165   }
    166   if (fderr)
    167   {
    168     close (pe[1]);
    169     *fderr = pe[0];
    170   }
    171   *pid = rpid;
    172 
    173   return STATUS_OK;
    174 }
    175 
    176 NEOERR *filter_create_fp(const char *cmd, FILE **in, FILE **out, FILE **err,
    177                          pid_t *pid)
    178 {
    179   NEOERR *nerr;
    180   int fdin = 0, fdout = 0, fderr = 0;
    181   int *pfdin = NULL, *pfdout = NULL, *pfderr = NULL;
    182 
    183   if (in) pfdin = &fdin;
    184   if (out) pfdout = &fdout;
    185   if (err) pfderr = &fderr;
    186 
    187   nerr = filter_create_fd(cmd, pfdin, pfdout, pfderr, pid);
    188   if (nerr) return nerr_pass(nerr);
    189 
    190   if (in)
    191   {
    192     *in = fdopen (fdin, "w");
    193     if (*in == NULL)
    194       return nerr_raise_errno(NERR_IO, "Unable to fdopen in for command: %s",
    195 	  cmd);
    196   }
    197 
    198   if (out)
    199   {
    200     *out = fdopen (fdout, "r");
    201     if (*out == NULL)
    202     {
    203       if (in) fclose(*in);
    204       return nerr_raise_errno(NERR_IO, "Unable to fdopen out for command: %s",
    205 	  cmd);
    206     }
    207   }
    208 
    209   if (err)
    210   {
    211     *err = fdopen (fderr, "r");
    212     if (*err == NULL)
    213     {
    214       if (in) fclose(*in);
    215       if (out) fclose(*out);
    216       return nerr_raise_errno(NERR_IO, "Unable to fdopen err for command: %s",
    217 	  cmd);
    218     }
    219   }
    220   return STATUS_OK;
    221 }
    222