1 /* pr.c -- Process creation and tracking support
2 * Created: Sun Jan 7 13:34:08 1996 by faith@dict.org
3 * Copyright 1996, 2002 Rickard E. Faith (faith@dict.org)
4 * Copyright 2002-2008 Aleksey Cheusov (vle@gmx.net)
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 *
25 * \section{Process Management Routines}
26 *
27 * \intro The process management routines are designed to facilitate the
28 * creation and management of child processes, coprocesses, and associated
29 * pipelines and I/O. Some support for daemons and socket connections is
30 * also provided.
31 *
32 */
33
34 #include "maaP.h"
35 #include <errno.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <sys/param.h>
39 #include <sys/wait.h>
40
41 typedef struct _pr_Obj {
42 int pid;
43 } *_pr_Obj;
44
45 static _pr_Obj _pr_objects = NULL;
46
47 #if 0
48 static void _pr_check(pr_Object object, const char *function)
49 {
50 Obj o = (Obj)object;
51
52 if (!o) err_internal(function, "object is null");
53 #if MAA_MAGIC
54 if (o->magic != PR_MAGIC)
55 err_internal(function,
56 "Magic match failed: 0x%08x (should be 0x%08x)",
57 o->magic,
58 PR_MAGIC);
59 #endif
60 }
61 #endif
62
63 /* The idea for the max_fd call is from W. Richard Stevens; Advanced
64 Programming in the UNIX Environment (Addison-Wesley Publishing Co.,
65 1992); page 43. The implementation here, however, is different from
66 that provided by Stevens for his open_max routine. */
67
max_fd(void)68 static int max_fd(void)
69 {
70 static int maxFd = 0;
71
72 if (maxFd) return maxFd;
73
74 if ((maxFd = sysconf(_SC_OPEN_MAX)) > 0) return maxFd;
75
76 #ifdef NOFILE
77 return maxFd = NOFILE; /* Usually in sys/params.h */
78 #else
79 # ifdef _NFILE
80 return maxFd = _NFILE;
81 # else
82 return maxFd = 256; /* best guess */
83 # endif
84 #endif
85 }
86
_pr_init(void)87 static void _pr_init(void)
88 {
89 if (!_pr_objects)
90 _pr_objects = xcalloc(max_fd(), sizeof(struct _pr_Obj));
91 }
92
_pr_shutdown(void)93 void _pr_shutdown(void)
94 {
95 int i;
96
97 if (_pr_objects) {
98 for (i = 0; i < max_fd(); i++) {
99 /* FIXME: blah */;
100 if (_pr_objects[i].pid) {
101 kill(_pr_objects[i].pid, SIGKILL); /* FIXME! be gentler. */
102 pr_wait(_pr_objects[i].pid);
103 _pr_objects[i].pid = 0;
104 }
105 }
106 xfree(_pr_objects);
107 _pr_objects = NULL;
108 }
109 }
110
pr_open(const char * command,int flags,int * infd,int * outfd,int * errfd)111 int pr_open(const char *command, int flags, int *infd, int *outfd, int *errfd)
112 {
113 int pid;
114 int fdin[2];
115 int fdout[2];
116 int fderr[2];
117 arg_List list;
118 int argc;
119 char **argv;
120 int null;
121
122 _pr_init();
123
124 if (flags & ~(PR_USE_STDIN | PR_USE_STDOUT | PR_USE_STDERR
125 | PR_CREATE_STDIN | PR_CREATE_STDOUT | PR_CREATE_STDERR
126 | PR_STDERR_TO_STDOUT))
127 err_internal(__func__, "Illegal flags: 0x%08x", flags);
128 if ((flags & PR_USE_STDIN) && (flags & PR_CREATE_STDIN))
129 err_internal(__func__, "Cannot both use and create stdin");
130 if ((flags & PR_USE_STDOUT) && (flags & PR_CREATE_STDOUT))
131 err_internal(__func__, "Cannot both use and create stdout");
132 if ((flags & PR_USE_STDERR) && (flags & PR_CREATE_STDERR))
133 err_internal(__func__, "Cannot both use and create stderr");
134 if ((flags & PR_STDERR_TO_STDOUT)
135 && ((flags & PR_USE_STDERR) || (flags & PR_CREATE_STDERR)))
136 err_internal(__func__,
137 "Cannot use/create stderr when duping to stdout");
138
139 list = arg_argify(command, 0);
140 arg_get_vector(list, &argc, &argv);
141 PRINTF(MAA_PR,("Execing %s with \"%s\"\n", argv[0], command));
142
143 if ((flags & PR_CREATE_STDIN) && pipe(fdin) < 0)
144 err_fatal_errno(__func__, "Cannot create pipe for stdin");
145 if ((flags & PR_CREATE_STDOUT) && pipe(fdout) < 0)
146 err_fatal_errno(__func__, "Cannot create pipe for stdout");
147 if ((flags & PR_CREATE_STDERR) && pipe(fderr) < 0)
148 err_fatal_errno(__func__, "Cannot create pipe for stderr");
149
150 if ((pid = fork()) < 0)
151 err_fatal_errno(__func__, "Cannot fork");
152
153 if (pid == 0) { /* child */
154 int i;
155
156 #define CHILD(CREATE,USE,fds,writefd,readfd,fd,FILENO,flag) \
157 if (flags & CREATE) { \
158 close(fds[writefd]); \
159 dup2(fds[readfd], FILENO); \
160 close(fds[readfd]); \
161 } else if (flags & USE) { \
162 if (fd && *fd) { \
163 dup2(*fd, FILENO); \
164 close(*fd); \
165 } else { \
166 if ((null = open("/dev/null", flag)) >= 0) { \
167 dup2(null, FILENO); \
168 close(null); \
169 } \
170 } \
171 }
172
173 CHILD(PR_CREATE_STDIN, PR_USE_STDIN, fdin, 1, 0, infd,
174 STDIN_FILENO, O_RDONLY);
175 CHILD(PR_CREATE_STDOUT, PR_USE_STDOUT, fdout, 0, 1, outfd,
176 STDOUT_FILENO, O_WRONLY);
177 CHILD(PR_CREATE_STDERR, PR_USE_STDERR, fderr, 0, 1, errfd,
178 STDERR_FILENO, O_WRONLY);
179
180 #undef CHILD
181
182 if (flags & PR_STDERR_TO_STDOUT)
183 dup2(STDOUT_FILENO, STDERR_FILENO);
184
185 for (i = 0; i < max_fd(); i++)
186 if (_pr_objects[i].pid > 0) close(i);
187
188 execvp(argv[0], argv);
189 _exit(127);
190 }
191 /* parent */
192 #define PARENT(CREATE,USE,fds,readfd,writefd,fd,flag,name) \
193 if (flags & CREATE) { \
194 close(fds[ readfd ]); \
195 *fd = fds[ writefd ]; \
196 _pr_objects[ *fd ].pid = pid; \
197 PRINTF(MAA_PR,(name " = %d; ",*fd)); \
198 } else if (flags & USE) { \
199 if (fd && *fd) { \
200 PRINTF(MAA_PR,(name " = %d*; ",*fd)); \
201 _pr_objects[ *fd ].pid =0; \
202 close(*fd); \
203 } \
204 }
205
206 PARENT(PR_CREATE_STDIN, PR_USE_STDIN, fdin, 0, 1,
207 infd, "w", "stdin");
208 PARENT(PR_CREATE_STDOUT, PR_USE_STDOUT, fdout, 1, 0,
209 outfd, "r", "stdout");
210 PARENT(PR_CREATE_STDERR, PR_USE_STDERR, fderr, 1, 0,
211 errfd, "r", "stderr");
212
213 #undef PARENT
214
215 PRINTF(MAA_PR,("child pid = %d\n",pid));
216 arg_destroy(list);
217
218 return pid;
219 }
220
pr_wait(int pid)221 int pr_wait(int pid)
222 {
223 int exitStatus = 0;
224 int status;
225
226 PRINTF(MAA_PR,("waiting on pid %d\n",pid));
227
228 while (waitpid(pid, &status, 0) < 0) {
229 if (errno != EINTR) {
230 if (errno == ECHILD) return 0; /* We've already waited */
231 /* This is really bad... */
232 PRINTF(MAA_PR,("waitpid() < 0, errno = %d\n", errno));
233 perror(__func__);
234 return -1;
235 }
236 }
237
238 if (WIFEXITED(status))
239 exitStatus |= WEXITSTATUS(status);
240
241 /* SIGPIPE is ok here, since tar may
242 shutdown early. Anything else is a
243 problem. */
244 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGPIPE)
245 exitStatus |= 128 + WTERMSIG(status); /* like bash :-) */
246
247 PRINTF(MAA_PR,("Child %d exited with status 0x%04x\n",pid,exitStatus));
248
249 return exitStatus;
250 }
251
pr_close_nowait(int fd)252 int pr_close_nowait(int fd)
253 {
254 int pid;
255
256 if (!_pr_objects)
257 err_internal(__func__, "No previous call to pr_open()");
258 if (!(pid = _pr_objects[ fd ].pid))
259 err_internal(__func__, "File (%d) not created by pr_open()", fd);
260
261 _pr_objects[ fd ].pid = 0;
262
263 close(fd);
264 return pid;
265 }
266
pr_close(int fd)267 int pr_close(int fd)
268 {
269 int pid = pr_close_nowait(fd);
270
271 return pr_wait(pid);
272 }
273
pr_readwrite(int in,int out,const char * inBuffer,int inLen,char * outBuffer,int outMaxLen)274 int pr_readwrite(int in, int out,
275 const char *inBuffer, int inLen,
276 char *outBuffer, int outMaxLen)
277 {
278 long flags;
279 const char *inPt = inBuffer;
280 char *outPt = outBuffer;
281 int outLen = 0;
282 fd_set rfds, wfds, efds;
283 struct timeval tv;
284 int n;
285 int count;
286 int retval;
287 int status;
288
289 if ((flags = fcntl(in, F_GETFL)) < 0)
290 err_fatal_errno(__func__, "Can't get flags for output stream");
291 #ifdef O_NONBLOCK
292 flags |= O_NONBLOCK;
293 #else
294 flags |= FNDELAY;
295 #endif
296 fcntl(in, F_SETFL, flags);
297
298 if ((flags = fcntl(out, F_GETFL)) < 0)
299 err_fatal_errno(__func__, "Can't get flags for input stream");
300 #ifdef O_NONBLOCK
301 flags |= O_NONBLOCK;
302 #else
303 flags |= FNDELAY;
304 #endif
305 fcntl(out, F_SETFL, flags);
306
307 n = max(in, out) + 1;
308
309 for (;;) {
310 tv.tv_sec = 5;
311 tv.tv_usec = 0;
312 FD_ZERO(&rfds);
313 FD_ZERO(&wfds);
314 FD_ZERO(&efds);
315 FD_SET(out, &rfds);
316 FD_SET(out, &efds);
317 if (inLen) {
318 FD_SET(in, &wfds);
319 FD_SET(in, &efds);
320 }
321
322 switch ((retval = select(n, &rfds, &wfds, &efds, &tv)))
323 {
324 case -1: err_fatal_errno(__func__, "Filter failed"); break;
325 /* case 0: err_fatal(__func__, "Filter hung\n"); break; */
326 default:
327 if (dbg_test(MAA_PR)) {
328 printf("select(2) returns %d,"
329 " inLen = %d, outLen = %d, outMaxLen = %d\n",
330 retval, inLen, outLen, outMaxLen);
331 if (FD_ISSET(in, &rfds)) printf(" in/read\n");
332 if (FD_ISSET(out, &rfds)) printf(" out/read\n");
333 if (FD_ISSET(in, &wfds)) printf(" in/write\n");
334 if (FD_ISSET(out, &wfds)) printf(" out/write\n");
335 if (FD_ISSET(in, &efds)) printf(" in/error\n");
336 if (FD_ISSET(out, &efds)) printf(" out/error\n");
337 }
338 if (inLen) {
339 if ((count = write(in, inPt, inLen)) <= 0) {
340 if (errno != EAGAIN)
341 err_fatal_errno(__func__, "Error writing to filter");
342 } else {
343 PRINTF(MAA_PR,(" wrote %d\n",count));
344 inLen -= count;
345 inPt += count;
346 if (!inLen) {
347 pr_close_nowait(in);
348 n = out + 1;
349 }
350 }
351 }
352 if ((count = read(out, outPt, outMaxLen)) <= 0) {
353 if (!count) {
354 if (inLen)
355 err_fatal(__func__,
356 "End of output, but input not flushed");
357 if ((status = pr_close(out)))
358 err_warning(__func__,
359 "Filter had non-zero exit status: 0x%x",
360 status);
361 return outLen;
362 } else if (errno != EAGAIN)
363 err_fatal_errno(__func__, "Error reading from filter");
364 } else {
365 PRINTF(MAA_PR,(" read %d\n",count));
366 outLen += count;
367 outPt += count;
368 if ((outMaxLen -= count) < 0)
369 err_fatal(__func__, "Output buffer overflow");
370 }
371 break;
372 }
373 }
374 }
375
pr_filter(const char * command,const char * inBuffer,int inLen,char * outBuffer,int outMaxLen)376 int pr_filter(const char *command,
377 const char *inBuffer, int inLen,
378 char *outBuffer, int outMaxLen)
379 {
380 int in, out;
381
382 pr_open(command, PR_CREATE_STDIN | PR_CREATE_STDOUT,
383 &in, &out, NULL);
384 return pr_readwrite(in, out, inBuffer, inLen, outBuffer, outMaxLen);
385 }
386
pr_spawn(const char * command)387 int pr_spawn(const char *command)
388 {
389 arg_List list;
390 int argc;
391 char **argv;
392 int pid;
393 int status;
394 int exitStatus = 0;
395
396 _pr_init();
397
398 list = arg_argify(command, 0);
399 arg_get_vector(list, &argc, &argv);
400 PRINTF(MAA_PR,("Execing %s with \"%s\"\n", argv[0], command));
401
402 if ((pid = fork()) < 0)
403 err_fatal_errno(__func__, "Cannot fork");
404
405 if (pid == 0) { /* child */
406 execvp(argv[0], argv);
407 _exit(127);
408 }
409
410 /* parent */
411 PRINTF(MAA_PR,("child pid = %d\n",pid));
412 arg_destroy(list);
413
414 PRINTF(MAA_PR,("waiting on pid %d\n",pid));
415 while (waitpid(pid, &status, 0) < 0) {
416 if (errno != EINTR) {
417 if (errno == ECHILD) return 0; /* We've already waited */
418 /* This is really bad... */
419 PRINTF(MAA_PR,("waitpid() < 0, errno = %d\n", errno));
420 perror(__func__);
421 return -1;
422 }
423 }
424
425 if (WIFEXITED(status))
426 exitStatus |= WEXITSTATUS(status);
427
428 /* SIGPIPE is ok here, since tar may
429 shutdown early. Anything else is a
430 problem. */
431 if (WIFSIGNALED(status) && WTERMSIG(status) != SIGPIPE)
432 exitStatus |= 128 + WTERMSIG(status); /* like bash :-) */
433
434 PRINTF(MAA_PR,("Child %d exited with status 0x%04x\n",pid,exitStatus));
435
436 return exitStatus;
437 }
438