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