Annotation of src/usr.bin/rdist/child.c, Revision 1.14
1.14 ! deraadt 1: /* $OpenBSD: child.c,v 1.13 2003/06/03 02:56:14 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"
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:
141: newc = (CHILD *) xmalloc(sizeof(CHILD));
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];
180: int amt;
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.12 millert 199: debugmsg(DM_MISC, "[readchild(%s, %d, %d) got %d bytes]",
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.12 millert 208: debugmsg(DM_MISC, "readchild(%s, %d, %d) done: amt = %d errno = %d\n",
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.7 millert 366: size_t bytes;
1.1 dm 367:
368: debugmsg(DM_CALL, "waitup() start\n");
369:
370: if (needscan)
371: childscan();
372:
373: if (activechildren <= 0)
374: return;
375:
376: /*
377: * Settup which children we want to select() on.
378: */
1.6 deraadt 379: for (pc = childlist; pc; pc = pc->c_next)
380: if (pc->c_readfd > rchildfdsn)
381: rchildfdsn = pc->c_readfd;
382: bytes = howmany(rchildfdsn+1, NFDBITS) * sizeof(fd_mask);
383: if ((rchildfdsp = (fd_set *)malloc(bytes)) == NULL)
384: return;
385:
386: memset(rchildfdsp, 0, bytes);
1.1 dm 387: for (pc = childlist; pc; pc = pc->c_next)
388: if (pc->c_readfd > 0) {
389: debugmsg(DM_MISC, "waitup() select on %d (%s)\n",
390: pc->c_readfd, pc->c_name);
1.6 deraadt 391: FD_SET(pc->c_readfd, rchildfdsp);
1.1 dm 392: }
393:
394: /*
395: * Actually call select()
396: */
397: /* XXX remove debugmsg() calls */
398: debugmsg(DM_MISC, "waitup() Call select(), activechildren=%d\n",
399: activechildren);
400:
1.9 millert 401: count = select(rchildfdsn+1, (SELECT_FD_TYPE *) rchildfdsp,
1.7 millert 402: NULL, NULL, NULL);
1.1 dm 403:
404: debugmsg(DM_MISC, "waitup() select returned %d activechildren = %d\n",
405: count, activechildren);
406:
407: /*
408: * select() will return count < 0 and errno == EINTR when
409: * there are no active children left.
410: */
411: if (count < 0) {
412: if (errno != EINTR)
413: error("Select failed reading children input: %s",
414: SYSERR);
1.6 deraadt 415: free(rchildfdsp);
1.1 dm 416: return;
417: }
418:
419: /*
420: * This should never happen.
421: */
422: if (count == 0) {
423: error("Select returned an unexpected count of 0.");
1.6 deraadt 424: free(rchildfdsp);
1.1 dm 425: return;
426: }
427:
428: /*
429: * Go through the list of children and read from each child
430: * which select() detected as ready for reading.
431: */
432: for (pc = childlist; pc && count > 0; pc = pc->c_next) {
433: /*
434: * Make sure child still exists
435: */
436: if (pc->c_name && kill(pc->c_pid, 0) < 0 &&
437: errno == ESRCH) {
438: debugmsg(DM_MISC,
1.12 millert 439: "waitup() proc %d (%s) died unexpectedly!",
440: pc->c_pid, pc->c_name);
1.1 dm 441: pc->c_state = PSdead;
442: needscan = TRUE;
443: }
444:
445: if (pc->c_name == NULL ||
1.6 deraadt 446: !FD_ISSET(pc->c_readfd, rchildfdsp))
1.1 dm 447: continue;
448:
449: readchild(pc);
450: --count;
451: }
1.7 millert 452: free(rchildfdsp);
1.1 dm 453:
454: #else /* !defined(HAVE_SELECT) */
455:
456: /*
457: * The non-select() version of waitproc()
458: */
459: debugmsg(DM_CALL, "waitup() start\n");
460:
1.7 millert 461: if (waitproc(NULL, TRUE) > 0)
1.1 dm 462: --activechildren;
463:
464: #endif /* defined(HAVE_SELECT) */
465: debugmsg(DM_CALL, "waitup() end\n");
466: }
467:
468: /*
469: * Spawn (create) a new child process for "cmd".
470: */
1.12 millert 471: int
472: spawn(struct cmd *cmd, struct cmd *cmdlist)
1.1 dm 473: {
474: pid_t pid;
475: int fildes[2];
476: char *childname = cmd->c_name;
477:
478: if (pipe(fildes) < 0) {
479: error("Cannot create pipe for %s: %s", childname, SYSERR);
480: return(-1);
481: }
482:
483: pid = fork();
484: if (pid == (pid_t)-1) {
485: error("Cannot spawn child for %s: fork failed: %s",
486: childname, SYSERR);
487: return(-1);
488: } else if (pid > 0) {
489: /*
490: * Parent
491: */
492: static CHILD newchild;
493:
494: #if defined(FORK_MISSES)
495: /*
496: * XXX Some OS's have a bug where fork does not
497: * always return properly to the parent
498: * when a number of forks are done very quicky.
499: */
500: sleep(2);
501: #endif /* FORK_MISSES */
502:
503: /* Receive notification when the child exits */
504: (void) signal(SIGCHLD, reap);
505:
506: /* Settup the new child */
507: newchild.c_next = NULL;
508: newchild.c_name = childname;
509: newchild.c_readfd = fildes[PIPE_READ];
510: newchild.c_pid = pid;
511: newchild.c_state = PSrunning;
512:
513: /* We're not going to write to the child */
514: (void) close(fildes[PIPE_WRITE]);
515:
516: /* Set non-blocking I/O */
517: if (setnonblocking(newchild.c_readfd, TRUE) < 0) {
518: error("Set nonblocking I/O failed: %s", SYSERR);
519: return(-1);
520: }
521:
522: /* Add new child to child list */
523: addchild(&newchild);
524:
525: /* Mark all other entries for this host as assigned */
526: markassigned(cmd, cmdlist);
527:
528: debugmsg(DM_CALL,
529: "spawn() Forked child %d for host %s active = %d\n",
530: pid, childname, activechildren);
531: return(pid);
532: } else {
533: /*
534: * Child
535: */
536:
537: /* We're not going to read from our parent */
538: (void) close(fildes[PIPE_READ]);
539:
540: /* Make stdout and stderr go to PIPE_WRITE (our parent) */
541: if (dup2(fildes[PIPE_WRITE], (int)fileno(stdout)) < 0) {
542: error("Cannot duplicate stdout file descriptor: %s",
543: SYSERR);
544: return(-1);
545: }
546: if (dup2(fildes[PIPE_WRITE], (int)fileno(stderr)) < 0) {
547: error("Cannot duplicate stderr file descriptor: %s",
548: SYSERR);
549: return(-1);
550: }
551:
552: return(0);
553: }
554: }
555:
556:
557: /*
558: * Enable or disable non-blocking I/O mode.
559: *
560: * Code is from INN by Rich Salz.
561: */
562: #if NBIO_TYPE == NBIO_IOCTL
563: #include <sys/ioctl.h>
564:
1.12 millert 565: int
566: setnonblocking(int fd, int flag)
1.1 dm 567: {
568: int state;
569:
570: state = flag ? 1 : 0;
571: return(ioctl(fd, FIONBIO, (char *)&state));
572: }
573:
574: #endif /* NBIO_IOCTL */
575:
576:
577: #if NBIO_TYPE == NBIO_FCNTL
1.12 millert 578: int
579: setnonblocking(int fd, int flag)
1.1 dm 580: {
581: int mode;
582:
583: if ((mode = fcntl(fd, F_GETFL, 0)) < 0)
584: return(-1);
585: if (flag)
586: mode |= FNDELAY;
587: else
588: mode &= ~FNDELAY;
589: return(fcntl(fd, F_SETFL, mode));
590: }
591: #endif /* NBIO_FCNTL */