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