Annotation of src/usr.bin/rdist/child.c, Revision 1.5
1.5 ! deraadt 1: /* $OpenBSD: child.c,v 1.4 1996/07/25 05:30:59 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.
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.5 ! deraadt 38: "$OpenBSD: child.c,v 1.4 1996/07/25 05:30:59 millert 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;
1.5 ! deraadt 278: int save_errno = errno;
1.1 dm 279: int status = 0;
280: pid_t pid;
281:
282: debugmsg(DM_CALL, "reap() called\n");
283:
284: /*
285: * Reap every child that has exited. Break out of the
286: * loop as soon as we run out of children that have
287: * exited so far.
288: */
289: for ( ; ; ) {
290: /*
291: * Do a non-blocking check for exiting processes
292: */
293: pid = waitproc(&status, FALSE);
294: debugmsg(DM_MISC,
295: "reap() pid = %d status = %d activechildren=%d\n",
296: pid, status, activechildren);
297:
298: /*
299: * See if a child really exited
300: */
301: if (pid == 0)
302: break;
303: if (pid < 0) {
304: if (errno != ECHILD)
305: error("Wait failed: %s", SYSERR);
306: break;
307: }
308:
309: /*
310: * Find the process (pid) and mark it as dead.
311: */
312: for (pc = childlist; pc; pc = pc->c_next)
313: if (pc->c_pid == pid) {
314: needscan = TRUE;
315: pc->c_state = PSdead;
316: }
317:
318: }
319:
320: /*
321: * Reset signals
322: */
323: (void) signal(SIGCHLD, reap);
324:
325: debugmsg(DM_CALL, "reap() done\n");
1.5 ! deraadt 326: errno = save_errno;
1.1 dm 327: }
328:
329: /*
330: * Scan the children list to find the child that just exited,
331: * read any unread input, then remove it from the list of active children.
332: */
333: static void childscan()
334: {
335: register CHILD *pc, *nextpc;
336:
337: debugmsg(DM_CALL, "childscan() start");
338:
339: for (pc = childlist; pc; pc = nextpc) {
340: nextpc = pc->c_next;
341: if (pc->c_state == PSdead) {
342: readchild(pc);
343: removechild(pc);
344: }
345: }
346:
347: needscan = FALSE;
348: debugmsg(DM_CALL, "childscan() end");
349: }
350:
351: /*
352: #if defined HAVE_SELECT
353: *
354: * Wait for children to send output for us to read.
355: *
356: #else !HAVE_SELECT
357: *
358: * Wait up for children to exit.
359: *
360: #endif
361: */
362: extern void waitup()
363: {
364: #if defined(HAVE_SELECT)
365: register int count;
366: register CHILD *pc;
367: fd_set rchildfds;
368:
369: debugmsg(DM_CALL, "waitup() start\n");
370:
371: if (needscan)
372: childscan();
373:
374: if (activechildren <= 0)
375: return;
376:
377: /*
378: * Settup which children we want to select() on.
379: */
380: FD_ZERO(&rchildfds);
381: for (pc = childlist; pc; pc = pc->c_next)
382: if (pc->c_readfd > 0) {
383: debugmsg(DM_MISC, "waitup() select on %d (%s)\n",
384: pc->c_readfd, pc->c_name);
385: FD_SET(pc->c_readfd, &rchildfds);
386: }
387:
388: /*
389: * Actually call select()
390: */
391: /* XXX remove debugmsg() calls */
392: debugmsg(DM_MISC, "waitup() Call select(), activechildren=%d\n",
393: activechildren);
394:
1.4 millert 395: count = select(FD_SETSIZE, (SELECT_FD_TYPE *) &rchildfds,
396: (SELECT_FD_TYPE *) NULL, (SELECT_FD_TYPE *) NULL,
397: (struct timeval *) NULL);
1.1 dm 398:
399: debugmsg(DM_MISC, "waitup() select returned %d activechildren = %d\n",
400: count, activechildren);
401:
402: /*
403: * select() will return count < 0 and errno == EINTR when
404: * there are no active children left.
405: */
406: if (count < 0) {
407: if (errno != EINTR)
408: error("Select failed reading children input: %s",
409: SYSERR);
410: return;
411: }
412:
413: /*
414: * This should never happen.
415: */
416: if (count == 0) {
417: error("Select returned an unexpected count of 0.");
418: return;
419: }
420:
421: /*
422: * Go through the list of children and read from each child
423: * which select() detected as ready for reading.
424: */
425: for (pc = childlist; pc && count > 0; pc = pc->c_next) {
426: /*
427: * Make sure child still exists
428: */
429: if (pc->c_name && kill(pc->c_pid, 0) < 0 &&
430: errno == ESRCH) {
431: debugmsg(DM_MISC,
432: "waitup() proc %d (%s) died unexpectedly!",
433: pc->c_pid, pc->c_name);
434: pc->c_state = PSdead;
435: needscan = TRUE;
436: }
437:
438: if (pc->c_name == NULL ||
439: !FD_ISSET(pc->c_readfd, &rchildfds))
440: continue;
441:
442: readchild(pc);
443: --count;
444: }
445:
446: #else /* !defined(HAVE_SELECT) */
447:
448: /*
449: * The non-select() version of waitproc()
450: */
451: debugmsg(DM_CALL, "waitup() start\n");
452:
453: if (waitproc((int *) NULL, TRUE) > 0)
454: --activechildren;
455:
456: #endif /* defined(HAVE_SELECT) */
457: debugmsg(DM_CALL, "waitup() end\n");
458: }
459:
460: /*
461: * Spawn (create) a new child process for "cmd".
462: */
463: extern int spawn(cmd, cmdlist)
464: struct cmd *cmd;
465: struct cmd *cmdlist;
466: {
467: pid_t pid;
468: int fildes[2];
469: char *childname = cmd->c_name;
470:
471: if (pipe(fildes) < 0) {
472: error("Cannot create pipe for %s: %s", childname, SYSERR);
473: return(-1);
474: }
475:
476: pid = fork();
477: if (pid == (pid_t)-1) {
478: error("Cannot spawn child for %s: fork failed: %s",
479: childname, SYSERR);
480: return(-1);
481: } else if (pid > 0) {
482: /*
483: * Parent
484: */
485: static CHILD newchild;
486:
487: #if defined(FORK_MISSES)
488: /*
489: * XXX Some OS's have a bug where fork does not
490: * always return properly to the parent
491: * when a number of forks are done very quicky.
492: */
493: sleep(2);
494: #endif /* FORK_MISSES */
495:
496: /* Receive notification when the child exits */
497: (void) signal(SIGCHLD, reap);
498:
499: /* Settup the new child */
500: newchild.c_next = NULL;
501: newchild.c_name = childname;
502: newchild.c_readfd = fildes[PIPE_READ];
503: newchild.c_pid = pid;
504: newchild.c_state = PSrunning;
505:
506: /* We're not going to write to the child */
507: (void) close(fildes[PIPE_WRITE]);
508:
509: /* Set non-blocking I/O */
510: if (setnonblocking(newchild.c_readfd, TRUE) < 0) {
511: error("Set nonblocking I/O failed: %s", SYSERR);
512: return(-1);
513: }
514:
515: /* Add new child to child list */
516: addchild(&newchild);
517:
518: /* Mark all other entries for this host as assigned */
519: markassigned(cmd, cmdlist);
520:
521: debugmsg(DM_CALL,
522: "spawn() Forked child %d for host %s active = %d\n",
523: pid, childname, activechildren);
524: return(pid);
525: } else {
526: /*
527: * Child
528: */
529:
530: /* We're not going to read from our parent */
531: (void) close(fildes[PIPE_READ]);
532:
533: /* Make stdout and stderr go to PIPE_WRITE (our parent) */
534: if (dup2(fildes[PIPE_WRITE], (int)fileno(stdout)) < 0) {
535: error("Cannot duplicate stdout file descriptor: %s",
536: SYSERR);
537: return(-1);
538: }
539: if (dup2(fildes[PIPE_WRITE], (int)fileno(stderr)) < 0) {
540: error("Cannot duplicate stderr file descriptor: %s",
541: SYSERR);
542: return(-1);
543: }
544:
545: return(0);
546: }
547: }
548:
549:
550: /*
551: * Enable or disable non-blocking I/O mode.
552: *
553: * Code is from INN by Rich Salz.
554: */
555: #if NBIO_TYPE == NBIO_IOCTL
556: #include <sys/ioctl.h>
557:
558: int setnonblocking(fd, flag)
559: int fd;
560: int flag;
561: {
562: int state;
563:
564: state = flag ? 1 : 0;
565: return(ioctl(fd, FIONBIO, (char *)&state));
566: }
567:
568: #endif /* NBIO_IOCTL */
569:
570:
571: #if NBIO_TYPE == NBIO_FCNTL
572: int setnonblocking(fd, flag)
573: int fd;
574: int flag;
575: {
576: int mode;
577:
578: if ((mode = fcntl(fd, F_GETFL, 0)) < 0)
579: return(-1);
580: if (flag)
581: mode |= FNDELAY;
582: else
583: mode &= ~FNDELAY;
584: return(fcntl(fd, F_SETFL, mode));
585: }
586: #endif /* NBIO_FCNTL */