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