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