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