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