1 /* $OpenBSD: child.c,v 1.28 2022/12/26 19:16:02 jmc 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
removechild(CHILD * child)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 *
copychild(CHILD * 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
addchild(CHILD * child)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
readchild(CHILD * child)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
waitproc(int * statval,int block)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
reap(int dummy)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
childscan(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
waitup(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) == -1 &&
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
setnonblocking(int fd)432 setnonblocking(int fd)
433 {
434 int flags;
435
436 if ((flags = fcntl(fd, F_GETFL)) == -1)
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
spawn(struct cmd * cmd,struct cmd * cmdlist)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) == -1) {
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 /* Setup 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)) == -1) {
508 error("Cannot duplicate stdout file descriptor: %s",
509 SYSERR);
510 return(-1);
511 }
512 if (dup2(fildes[PIPE_WRITE], (int)fileno(stderr)) == -1) {
513 error("Cannot duplicate stderr file descriptor: %s",
514 SYSERR);
515 return(-1);
516 }
517
518 return(0);
519 }
520 }
521