xref: /openbsd-src/usr.bin/rdist/child.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: child.c,v 1.23 2014/07/05 07:57:43 guenther 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/select.h>
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 
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 
72 /*
73  * Remove a child that has died (exited)
74  * from the list of active children
75  */
76 static void
77 removechild(CHILD *child)
78 {
79 	CHILD *pc, *prevpc;
80 
81 	debugmsg(DM_CALL, "removechild(%s, %d, %d) start",
82 		 child->c_name, child->c_pid, child->c_readfd);
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)
93 		error("RemoveChild called with bad child %s %d %d",
94 		      child->c_name, child->c_pid, child->c_readfd);
95 	else {
96 		/*
97 		 * Remove the child
98 		 */
99 		sigset_t set, oset;
100 
101 		sigemptyset(&set);
102 		sigaddset(&set, SIGCHLD);
103 		sigprocmask(SIG_BLOCK, &set, &oset);
104 
105 		if (prevpc != NULL)
106 			prevpc->c_next = pc->c_next;
107 		else
108 			childlist = pc->c_next;
109 
110 		sigprocmask(SIG_SETMASK, &oset, NULL);
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  */
124 static CHILD *
125 copychild(CHILD *child)
126 {
127 	CHILD *newc;
128 
129 	newc = xmalloc(sizeof *newc);
130 
131 	newc->c_name = xstrdup(child->c_name);
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  */
143 static void
144 addchild(CHILD *child)
145 {
146 	CHILD *pc;
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,
157 		 "addchild() created '%s' pid %d fd %d (active=%d)\n",
158 		 child->c_name, child->c_pid, child->c_readfd, activechildren);
159 }
160 
161 /*
162  * Read input from a child process.
163  */
164 static void
165 readchild(CHILD *child)
166 {
167 	char rbuf[BUFSIZ];
168 	ssize_t amt;
169 
170 	debugmsg(DM_CALL, "[readchild(%s, %d, %d) start]",
171 		 child->c_name, child->c_pid, child->c_readfd);
172 
173 	/*
174 	 * Check that this is a valid child.
175 	 */
176 	if (child->c_name == NULL || child->c_readfd <= 0) {
177 		debugmsg(DM_MISC, "[readchild(%s, %d, %d) bad child]",
178 			 child->c_name, child->c_pid, child->c_readfd);
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 */
187 		debugmsg(DM_MISC, "[readchild(%s, %d, %d) got %zd bytes]",
188 			 child->c_name, child->c_pid, child->c_readfd, amt);
189 
190 		(void) xwrite(fileno(stdout), rbuf, amt);
191 
192 		debugmsg(DM_MISC, "[readchild(%s, %d, %d) write done]",
193 			 child->c_name, child->c_pid, child->c_readfd);
194 	}
195 
196 	debugmsg(DM_MISC, "readchild(%s, %d, %d) done: amt = %zd errno = %d\n",
197 		 child->c_name, child->c_pid, child->c_readfd, amt, errno);
198 
199 	/*
200 	 * See if we've reached EOF
201 	 */
202 	if (amt == 0)
203 		debugmsg(DM_MISC, "readchild(%s, %d, %d) at EOF\n",
204 			 child->c_name, child->c_pid, child->c_readfd);
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  */
213 static pid_t
214 waitproc(int *statval, int block)
215 {
216 	int status;
217 	pid_t pid;
218 	int exitval;
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,
230 			 "Child process %d exited with status %d.\n",
231 			 pid, exitval);
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  */
247 static void
248 reap(int dummy)
249 {
250 	CHILD *pc;
251 	int save_errno = errno;
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,
268 			 "reap() pid = %d status = %d activechildren=%d\n",
269 			 pid, status, activechildren);
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");
299 	errno = save_errno;
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  */
306 static void
307 childscan(void)
308 {
309 	CHILD *pc, *nextpc;
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  */
330 void
331 waitup(void)
332 {
333 	int count;
334 	CHILD *pc;
335 	fd_set *rchildfdsp = NULL;
336 	int rchildfdsn = 0;
337 
338 	debugmsg(DM_CALL, "waitup() start\n");
339 
340 	if (needscan)
341 		childscan();
342 
343 	if (activechildren <= 0)
344 		return;
345 
346 	/*
347 	 * Set up which children we want to select() on.
348 	 */
349 	for (pc = childlist; pc; pc = pc->c_next)
350 		if (pc->c_readfd > rchildfdsn)
351 			rchildfdsn = pc->c_readfd;
352 	rchildfdsp = xcalloc(howmany(rchildfdsn+1, NFDBITS), sizeof(fd_mask));
353 
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);
358 			FD_SET(pc->c_readfd, rchildfdsp);
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 
368 	count = select(rchildfdsn+1, (SELECT_FD_TYPE *) rchildfdsp,
369 		       NULL, NULL, NULL);
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);
382 		free(rchildfdsp);
383 		return;
384 	}
385 
386 	/*
387 	 * This should never happen.
388 	 */
389 	if (count == 0) {
390 		error("Select returned an unexpected count of 0.");
391 		free(rchildfdsp);
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,
406 				 "waitup() proc %d (%s) died unexpectedly!",
407 				 pc->c_pid, pc->c_name);
408 			pc->c_state = PSdead;
409 			needscan = TRUE;
410 		}
411 
412 		if (pc->c_name == NULL ||
413 		    !FD_ISSET(pc->c_readfd, rchildfdsp))
414 			continue;
415 
416 		readchild(pc);
417 		--count;
418 	}
419 	free(rchildfdsp);
420 
421 	debugmsg(DM_CALL, "waitup() end\n");
422 }
423 
424 /*
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 /*
440  * Spawn (create) a new child process for "cmd".
441  */
442 int
443 spawn(struct cmd *cmd, struct cmd *cmdlist)
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 */
479 		if (setnonblocking(newchild.c_readfd) < 0) {
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 }
517