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