Annotation of src/usr.bin/rdist/child.c, Revision 1.19
1.19 ! guenther 1: /* $OpenBSD: child.c,v 1.18 2014/07/05 05:05:51 guenther Exp $ */
1.3 deraadt 2:
1.1 dm 3: /*
4: * Copyright (c) 1983 Regents of the University of California.
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
1.13 millert 15: * 3. Neither the name of the University nor the names of its contributors
1.1 dm 16: * may be used to endorse or promote products derived from this software
17: * without specific prior written permission.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29: * SUCH DAMAGE.
30: */
31:
1.12 millert 32: #include "defs.h"
1.1 dm 33:
34: /*
35: * Functions for rdist related to children
36: */
37:
38: #include <sys/types.h>
39: #include <sys/wait.h>
40: #if defined(NEED_SYS_SELECT_H)
41: #include <sys/select.h>
42: #endif /* NEED_SYS_SELECT_H */
43:
44: typedef enum _PROCSTATE {
45: PSrunning,
46: PSdead
47: } PROCSTATE;
48:
49: /*
50: * Structure for child rdist processes mainted by the parent
51: */
52: struct _child {
53: char *c_name; /* Name of child */
54: int c_readfd; /* Read file descriptor */
55: pid_t c_pid; /* Process ID */
56: PROCSTATE c_state; /* Running? */
57: struct _child *c_next; /* Next entry */
58: };
59: typedef struct _child CHILD;
60:
61: static CHILD *childlist = NULL; /* List of children */
62: int activechildren = 0; /* Number of active children */
63: extern int maxchildren; /* Max active children */
64: static int needscan = FALSE; /* Need to scan children */
65:
1.12 millert 66: static void removechild(CHILD *);
67: static CHILD *copychild(CHILD *);
68: static void addchild(CHILD *);
69: static void readchild(CHILD *);
70: static pid_t waitproc(int *, int);
71: static void reap(int);
72: static void childscan(void);
73:
1.1 dm 74: /*
75: * Remove a child that has died (exited)
76: * from the list of active children
77: */
1.12 millert 78: static void
79: removechild(CHILD *child)
1.1 dm 80: {
1.10 mpech 81: CHILD *pc, *prevpc;
1.1 dm 82:
1.12 millert 83: debugmsg(DM_CALL, "removechild(%s, %d, %d) start",
84: child->c_name, child->c_pid, child->c_readfd);
1.1 dm 85:
86: /*
87: * Find the child in the list
88: */
89: for (pc = childlist, prevpc = NULL; pc != NULL;
90: prevpc = pc, pc = pc->c_next)
91: if (pc == child)
92: break;
93:
94: if (pc == NULL)
1.12 millert 95: error("RemoveChild called with bad child %s %d %d",
96: child->c_name, child->c_pid, child->c_readfd);
1.1 dm 97: else {
98: /*
99: * Remove the child
100: */
101: #if defined(POSIX_SIGNALS)
102: sigset_t set;
103:
104: sigemptyset(&set);
105: sigaddset(&set, SIGCHLD);
1.7 millert 106: sigprocmask(SIG_BLOCK, &set, NULL);
1.1 dm 107: #else /* !POSIX_SIGNALS */
108: int oldmask;
109:
110: oldmask = sigblock(sigmask(SIGCHLD));
111: #endif /* POSIX_SIGNALS */
112:
113: if (prevpc != NULL)
114: prevpc->c_next = pc->c_next;
115: else
116: childlist = pc->c_next;
117:
118: #if defined(POSIX_SIGNALS)
1.7 millert 119: sigprocmask(SIG_UNBLOCK, &set, NULL);
1.1 dm 120: #else
121: sigsetmask(oldmask);
122: #endif /* POSIX_SIGNALS */
123:
124: (void) free(child->c_name);
125: --activechildren;
126: (void) close(child->c_readfd);
127: (void) free(pc);
128: }
129:
130: debugmsg(DM_CALL, "removechild() end");
131: }
132:
133: /*
134: * Create a totally new copy of a child.
135: */
1.12 millert 136: static CHILD *
137: copychild(CHILD *child)
1.1 dm 138: {
1.10 mpech 139: CHILD *newc;
1.1 dm 140:
1.18 guenther 141: newc = xmalloc(sizeof *newc);
1.1 dm 142:
1.8 millert 143: newc->c_name = xstrdup(child->c_name);
1.1 dm 144: newc->c_readfd = child->c_readfd;
145: newc->c_pid = child->c_pid;
146: newc->c_state = child->c_state;
147: newc->c_next = NULL;
148:
149: return(newc);
150: }
151:
152: /*
153: * Add a child to the list of children.
154: */
1.12 millert 155: static void
156: addchild(CHILD *child)
1.1 dm 157: {
1.10 mpech 158: CHILD *pc;
1.1 dm 159:
160: debugmsg(DM_CALL, "addchild() start\n");
161:
162: pc = copychild(child);
163: pc->c_next = childlist;
164: childlist = pc;
165:
166: ++activechildren;
167:
168: debugmsg(DM_MISC,
1.12 millert 169: "addchild() created '%s' pid %d fd %d (active=%d)\n",
170: child->c_name, child->c_pid, child->c_readfd, activechildren);
1.1 dm 171: }
172:
173: /*
174: * Read input from a child process.
175: */
1.12 millert 176: static void
177: readchild(CHILD *child)
1.1 dm 178: {
179: char rbuf[BUFSIZ];
1.15 krw 180: ssize_t amt;
1.1 dm 181:
1.12 millert 182: debugmsg(DM_CALL, "[readchild(%s, %d, %d) start]",
183: child->c_name, child->c_pid, child->c_readfd);
1.1 dm 184:
185: /*
186: * Check that this is a valid child.
187: */
188: if (child->c_name == NULL || child->c_readfd <= 0) {
1.12 millert 189: debugmsg(DM_MISC, "[readchild(%s, %d, %d) bad child]",
190: child->c_name, child->c_pid, child->c_readfd);
1.1 dm 191: return;
192: }
193:
194: /*
195: * Read from child and display the result.
196: */
197: while ((amt = read(child->c_readfd, rbuf, sizeof(rbuf))) > 0) {
198: /* XXX remove these debug calls */
1.16 krw 199: debugmsg(DM_MISC, "[readchild(%s, %d, %d) got %zd bytes]",
1.12 millert 200: child->c_name, child->c_pid, child->c_readfd, amt);
1.1 dm 201:
202: (void) xwrite(fileno(stdout), rbuf, amt);
203:
1.12 millert 204: debugmsg(DM_MISC, "[readchild(%s, %d, %d) write done]",
205: child->c_name, child->c_pid, child->c_readfd);
1.1 dm 206: }
207:
1.16 krw 208: debugmsg(DM_MISC, "readchild(%s, %d, %d) done: amt = %zd errno = %d\n",
1.12 millert 209: child->c_name, child->c_pid, child->c_readfd, amt, errno);
1.1 dm 210:
211: /*
212: * See if we've reached EOF
213: */
214: if (amt == 0)
1.12 millert 215: debugmsg(DM_MISC, "readchild(%s, %d, %d) at EOF\n",
216: child->c_name, child->c_pid, child->c_readfd);
1.1 dm 217: }
218:
219: /*
220: * Wait for processes to exit. If "block" is true, then we block
221: * until a process exits. Otherwise, we return right away. If
222: * a process does exit, then the pointer "statval" is set to the
223: * exit status of the exiting process, if statval is not NULL.
224: */
1.12 millert 225: static pid_t
226: waitproc(int *statval, int block)
1.1 dm 227: {
228: WAIT_ARG_TYPE status;
1.12 millert 229: pid_t pid;
1.11 mpech 230: int exitval;
1.1 dm 231:
232: debugmsg(DM_CALL, "waitproc() %s, active children = %d...\n",
233: (block) ? "blocking" : "nonblocking", activechildren);
234:
235: #if WAIT_TYPE == WAIT_WAITPID
236: pid = waitpid(-1, &status, (block) ? 0 : WNOHANG);
237: #else
238: #if WAIT_TYPE == WAIT_WAIT3
239: pid = wait3(&status, (block) ? 0 : WNOHANG, NULL);
240: #endif /* WAIT_WAIT3 */
241: #endif /* WAIT_WAITPID */
242:
243: #if defined(WEXITSTATUS)
244: exitval = WEXITSTATUS(status);
245: #else
246: exitval = status.w_retcode;
247: #endif /* defined(WEXITSTATUS) */
248:
249: if (pid > 0 && exitval != 0) {
250: nerrs++;
251: debugmsg(DM_MISC,
1.12 millert 252: "Child process %d exited with status %d.\n",
253: pid, exitval);
1.1 dm 254: }
255:
256: if (statval)
257: *statval = exitval;
258:
259: debugmsg(DM_CALL, "waitproc() done (activechildren = %d)\n",
260: activechildren);
261:
262: return(pid);
263: }
264:
265: /*
266: * Check to see if any children have exited, and if so, read any unread
267: * input and then remove the child from the list of children.
268: */
1.12 millert 269: static void
270: reap(int dummy)
1.1 dm 271: {
1.10 mpech 272: CHILD *pc;
1.5 deraadt 273: int save_errno = errno;
1.1 dm 274: int status = 0;
275: pid_t pid;
276:
277: debugmsg(DM_CALL, "reap() called\n");
278:
279: /*
280: * Reap every child that has exited. Break out of the
281: * loop as soon as we run out of children that have
282: * exited so far.
283: */
284: for ( ; ; ) {
285: /*
286: * Do a non-blocking check for exiting processes
287: */
288: pid = waitproc(&status, FALSE);
289: debugmsg(DM_MISC,
1.12 millert 290: "reap() pid = %d status = %d activechildren=%d\n",
291: pid, status, activechildren);
1.1 dm 292:
293: /*
294: * See if a child really exited
295: */
296: if (pid == 0)
297: break;
298: if (pid < 0) {
299: if (errno != ECHILD)
300: error("Wait failed: %s", SYSERR);
301: break;
302: }
303:
304: /*
305: * Find the process (pid) and mark it as dead.
306: */
307: for (pc = childlist; pc; pc = pc->c_next)
308: if (pc->c_pid == pid) {
309: needscan = TRUE;
310: pc->c_state = PSdead;
311: }
312:
313: }
314:
315: /*
316: * Reset signals
317: */
318: (void) signal(SIGCHLD, reap);
319:
320: debugmsg(DM_CALL, "reap() done\n");
1.5 deraadt 321: errno = save_errno;
1.1 dm 322: }
323:
324: /*
325: * Scan the children list to find the child that just exited,
326: * read any unread input, then remove it from the list of active children.
327: */
1.12 millert 328: static void
329: childscan(void)
1.1 dm 330: {
1.10 mpech 331: CHILD *pc, *nextpc;
1.1 dm 332:
333: debugmsg(DM_CALL, "childscan() start");
334:
335: for (pc = childlist; pc; pc = nextpc) {
336: nextpc = pc->c_next;
337: if (pc->c_state == PSdead) {
338: readchild(pc);
339: removechild(pc);
340: }
341: }
342:
343: needscan = FALSE;
344: debugmsg(DM_CALL, "childscan() end");
345: }
346:
347: /*
348: #if defined HAVE_SELECT
349: *
350: * Wait for children to send output for us to read.
351: *
352: #else !HAVE_SELECT
353: *
354: * Wait up for children to exit.
355: *
356: #endif
357: */
1.12 millert 358: void
359: waitup(void)
1.1 dm 360: {
361: #if defined(HAVE_SELECT)
1.10 mpech 362: int count;
363: CHILD *pc;
1.6 deraadt 364: fd_set *rchildfdsp = NULL;
365: int rchildfdsn = 0;
1.1 dm 366:
367: debugmsg(DM_CALL, "waitup() start\n");
368:
369: if (needscan)
370: childscan();
371:
372: if (activechildren <= 0)
373: return;
374:
375: /*
1.17 guenther 376: * Set up which children we want to select() on.
1.1 dm 377: */
1.6 deraadt 378: for (pc = childlist; pc; pc = pc->c_next)
379: if (pc->c_readfd > rchildfdsn)
380: rchildfdsn = pc->c_readfd;
1.19 ! guenther 381: rchildfdsp = xcalloc(howmany(rchildfdsn+1, NFDBITS), sizeof(fd_mask));
1.6 deraadt 382:
1.1 dm 383: for (pc = childlist; pc; pc = pc->c_next)
384: if (pc->c_readfd > 0) {
385: debugmsg(DM_MISC, "waitup() select on %d (%s)\n",
386: pc->c_readfd, pc->c_name);
1.6 deraadt 387: FD_SET(pc->c_readfd, rchildfdsp);
1.1 dm 388: }
389:
390: /*
391: * Actually call select()
392: */
393: /* XXX remove debugmsg() calls */
394: debugmsg(DM_MISC, "waitup() Call select(), activechildren=%d\n",
395: activechildren);
396:
1.9 millert 397: count = select(rchildfdsn+1, (SELECT_FD_TYPE *) rchildfdsp,
1.7 millert 398: NULL, NULL, NULL);
1.1 dm 399:
400: debugmsg(DM_MISC, "waitup() select returned %d activechildren = %d\n",
401: count, activechildren);
402:
403: /*
404: * select() will return count < 0 and errno == EINTR when
405: * there are no active children left.
406: */
407: if (count < 0) {
408: if (errno != EINTR)
409: error("Select failed reading children input: %s",
410: SYSERR);
1.6 deraadt 411: free(rchildfdsp);
1.1 dm 412: return;
413: }
414:
415: /*
416: * This should never happen.
417: */
418: if (count == 0) {
419: error("Select returned an unexpected count of 0.");
1.6 deraadt 420: free(rchildfdsp);
1.1 dm 421: return;
422: }
423:
424: /*
425: * Go through the list of children and read from each child
426: * which select() detected as ready for reading.
427: */
428: for (pc = childlist; pc && count > 0; pc = pc->c_next) {
429: /*
430: * Make sure child still exists
431: */
432: if (pc->c_name && kill(pc->c_pid, 0) < 0 &&
433: errno == ESRCH) {
434: debugmsg(DM_MISC,
1.12 millert 435: "waitup() proc %d (%s) died unexpectedly!",
436: pc->c_pid, pc->c_name);
1.1 dm 437: pc->c_state = PSdead;
438: needscan = TRUE;
439: }
440:
441: if (pc->c_name == NULL ||
1.6 deraadt 442: !FD_ISSET(pc->c_readfd, rchildfdsp))
1.1 dm 443: continue;
444:
445: readchild(pc);
446: --count;
447: }
1.7 millert 448: free(rchildfdsp);
1.1 dm 449:
450: #else /* !defined(HAVE_SELECT) */
451:
452: /*
453: * The non-select() version of waitproc()
454: */
455: debugmsg(DM_CALL, "waitup() start\n");
456:
1.7 millert 457: if (waitproc(NULL, TRUE) > 0)
1.1 dm 458: --activechildren;
459:
460: #endif /* defined(HAVE_SELECT) */
461: debugmsg(DM_CALL, "waitup() end\n");
462: }
463:
464: /*
465: * Spawn (create) a new child process for "cmd".
466: */
1.12 millert 467: int
468: spawn(struct cmd *cmd, struct cmd *cmdlist)
1.1 dm 469: {
470: pid_t pid;
471: int fildes[2];
472: char *childname = cmd->c_name;
473:
474: if (pipe(fildes) < 0) {
475: error("Cannot create pipe for %s: %s", childname, SYSERR);
476: return(-1);
477: }
478:
479: pid = fork();
480: if (pid == (pid_t)-1) {
481: error("Cannot spawn child for %s: fork failed: %s",
482: childname, SYSERR);
483: return(-1);
484: } else if (pid > 0) {
485: /*
486: * Parent
487: */
488: static CHILD newchild;
489:
490: #if defined(FORK_MISSES)
491: /*
492: * XXX Some OS's have a bug where fork does not
493: * always return properly to the parent
494: * when a number of forks are done very quicky.
495: */
496: sleep(2);
497: #endif /* FORK_MISSES */
498:
499: /* Receive notification when the child exits */
500: (void) signal(SIGCHLD, reap);
501:
502: /* Settup the new child */
503: newchild.c_next = NULL;
504: newchild.c_name = childname;
505: newchild.c_readfd = fildes[PIPE_READ];
506: newchild.c_pid = pid;
507: newchild.c_state = PSrunning;
508:
509: /* We're not going to write to the child */
510: (void) close(fildes[PIPE_WRITE]);
511:
512: /* Set non-blocking I/O */
513: if (setnonblocking(newchild.c_readfd, TRUE) < 0) {
514: error("Set nonblocking I/O failed: %s", SYSERR);
515: return(-1);
516: }
517:
518: /* Add new child to child list */
519: addchild(&newchild);
520:
521: /* Mark all other entries for this host as assigned */
522: markassigned(cmd, cmdlist);
523:
524: debugmsg(DM_CALL,
525: "spawn() Forked child %d for host %s active = %d\n",
526: pid, childname, activechildren);
527: return(pid);
528: } else {
529: /*
530: * Child
531: */
532:
533: /* We're not going to read from our parent */
534: (void) close(fildes[PIPE_READ]);
535:
536: /* Make stdout and stderr go to PIPE_WRITE (our parent) */
537: if (dup2(fildes[PIPE_WRITE], (int)fileno(stdout)) < 0) {
538: error("Cannot duplicate stdout file descriptor: %s",
539: SYSERR);
540: return(-1);
541: }
542: if (dup2(fildes[PIPE_WRITE], (int)fileno(stderr)) < 0) {
543: error("Cannot duplicate stderr file descriptor: %s",
544: SYSERR);
545: return(-1);
546: }
547:
548: return(0);
549: }
550: }
551:
552:
553: /*
554: * Enable or disable non-blocking I/O mode.
555: *
556: * Code is from INN by Rich Salz.
557: */
558: #if NBIO_TYPE == NBIO_IOCTL
559: #include <sys/ioctl.h>
560:
1.12 millert 561: int
562: setnonblocking(int fd, int flag)
1.1 dm 563: {
564: int state;
565:
566: state = flag ? 1 : 0;
567: return(ioctl(fd, FIONBIO, (char *)&state));
568: }
569:
570: #endif /* NBIO_IOCTL */
571:
572:
573: #if NBIO_TYPE == NBIO_FCNTL
1.12 millert 574: int
575: setnonblocking(int fd, int flag)
1.1 dm 576: {
577: int mode;
578:
579: if ((mode = fcntl(fd, F_GETFL, 0)) < 0)
580: return(-1);
581: if (flag)
582: mode |= FNDELAY;
583: else
584: mode &= ~FNDELAY;
585: return(fcntl(fd, F_SETFL, mode));
586: }
587: #endif /* NBIO_FCNTL */