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