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 
filter_wait(pid_t pid,int options,int * exitcode)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 
filter_create_fd(const char * cmd,int * fdin,int * fdout,int * fderr,pid_t * pid)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 
filter_create_fp(const char * cmd,FILE ** in,FILE ** out,FILE ** err,pid_t * pid)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