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