GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rdist/child.c Lines: 0 173 0.0 %
Date: 2017-11-07 Branches: 0 84 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: child.c,v 1.26 2016/03/30 20:12:18 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
/*
33
 * Functions for rdist related to children
34
 */
35
36
#include <sys/types.h>
37
#include <sys/select.h>
38
#include <sys/wait.h>
39
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
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
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
77
/*
78
 * Remove a child that has died (exited)
79
 * from the list of active children
80
 */
81
static void
82
removechild(CHILD *child)
83
{
84
	CHILD *pc, *prevpc;
85
86
	debugmsg(DM_CALL, "removechild(%s, %d, %d) start",
87
		 child->c_name, child->c_pid, child->c_readfd);
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)
98
		error("RemoveChild called with bad child %s %d %d",
99
		      child->c_name, child->c_pid, child->c_readfd);
100
	else {
101
		/*
102
		 * Remove the child
103
		 */
104
		sigset_t set, oset;
105
106
		sigemptyset(&set);
107
		sigaddset(&set, SIGCHLD);
108
		sigprocmask(SIG_BLOCK, &set, &oset);
109
110
		if (prevpc != NULL)
111
			prevpc->c_next = pc->c_next;
112
		else
113
			childlist = pc->c_next;
114
115
		sigprocmask(SIG_SETMASK, &oset, NULL);
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
 */
129
static CHILD *
130
copychild(CHILD *child)
131
{
132
	CHILD *newc;
133
134
	newc = xmalloc(sizeof *newc);
135
136
	newc->c_name = xstrdup(child->c_name);
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
 */
148
static void
149
addchild(CHILD *child)
150
{
151
	CHILD *pc;
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,
162
		 "addchild() created '%s' pid %d fd %d (active=%d)\n",
163
		 child->c_name, child->c_pid, child->c_readfd, activechildren);
164
}
165
166
/*
167
 * Read input from a child process.
168
 */
169
static void
170
readchild(CHILD *child)
171
{
172
	char rbuf[BUFSIZ];
173
	ssize_t amt;
174
175
	debugmsg(DM_CALL, "[readchild(%s, %d, %d) start]",
176
		 child->c_name, child->c_pid, child->c_readfd);
177
178
	/*
179
	 * Check that this is a valid child.
180
	 */
181
	if (child->c_name == NULL || child->c_readfd <= 0) {
182
		debugmsg(DM_MISC, "[readchild(%s, %d, %d) bad child]",
183
			 child->c_name, child->c_pid, child->c_readfd);
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 */
192
		debugmsg(DM_MISC, "[readchild(%s, %d, %d) got %zd bytes]",
193
			 child->c_name, child->c_pid, child->c_readfd, amt);
194
195
		(void) xwrite(fileno(stdout), rbuf, amt);
196
197
		debugmsg(DM_MISC, "[readchild(%s, %d, %d) write done]",
198
			 child->c_name, child->c_pid, child->c_readfd);
199
	}
200
201
	debugmsg(DM_MISC, "readchild(%s, %d, %d) done: amt = %zd errno = %d\n",
202
		 child->c_name, child->c_pid, child->c_readfd, amt, errno);
203
204
	/*
205
	 * See if we've reached EOF
206
	 */
207
	if (amt == 0)
208
		debugmsg(DM_MISC, "readchild(%s, %d, %d) at EOF\n",
209
			 child->c_name, child->c_pid, child->c_readfd);
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
 */
218
static pid_t
219
waitproc(int *statval, int block)
220
{
221
	int status;
222
	pid_t pid;
223
	int exitval;
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,
235
			 "Child process %d exited with status %d.\n",
236
			 pid, exitval);
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
 */
252
static void
253
reap(int dummy)
254
{
255
	CHILD *pc;
256
	int save_errno = errno;
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,
273
			 "reap() pid = %d status = %d activechildren=%d\n",
274
			 pid, status, activechildren);
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");
304
	errno = save_errno;
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
 */
311
static void
312
childscan(void)
313
{
314
	CHILD *pc, *nextpc;
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
 */
335
void
336
waitup(void)
337
{
338
	int count;
339
	CHILD *pc;
340
	fd_set *rchildfdsp = NULL;
341
	int rchildfdsn = 0;
342
343
	debugmsg(DM_CALL, "waitup() start\n");
344
345
	if (needscan)
346
		childscan();
347
348
	if (activechildren <= 0)
349
		return;
350
351
	/*
352
	 * Set up which children we want to select() on.
353
	 */
354
	for (pc = childlist; pc; pc = pc->c_next)
355
		if (pc->c_readfd > rchildfdsn)
356
			rchildfdsn = pc->c_readfd;
357
	rchildfdsp = xcalloc(howmany(rchildfdsn+1, NFDBITS), sizeof(fd_mask));
358
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);
363
			FD_SET(pc->c_readfd, rchildfdsp);
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
373
	count = select(rchildfdsn+1, rchildfdsp, NULL, NULL, NULL);
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);
386
		free(rchildfdsp);
387
		return;
388
	}
389
390
	/*
391
	 * This should never happen.
392
	 */
393
	if (count == 0) {
394
		error("Select returned an unexpected count of 0.");
395
		free(rchildfdsp);
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,
410
				 "waitup() proc %d (%s) died unexpectedly!",
411
				 pc->c_pid, pc->c_name);
412
			pc->c_state = PSdead;
413
			needscan = TRUE;
414
		}
415
416
		if (pc->c_name == NULL ||
417
		    !FD_ISSET(pc->c_readfd, rchildfdsp))
418
			continue;
419
420
		readchild(pc);
421
		--count;
422
	}
423
	free(rchildfdsp);
424
425
	debugmsg(DM_CALL, "waitup() end\n");
426
}
427
428
/*
429
 * Enable non-blocking I/O.
430
 */
431
static int
432
setnonblocking(int fd)
433
{
434
	int	flags;
435
436
	if ((flags = fcntl(fd, F_GETFL)) < 0)
437
		return (-1);
438
	if (flags & O_NONBLOCK)
439
		return (0);
440
	return (fcntl(fd, F_SETFL, flags | O_NONBLOCK));
441
}
442
443
/*
444
 * Spawn (create) a new child process for "cmd".
445
 */
446
int
447
spawn(struct cmd *cmd, struct cmd *cmdlist)
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 */
483
		if (setnonblocking(newchild.c_readfd) < 0) {
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
}