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