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