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