1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * fork.c - safe forking for svc.startd 28 * 29 * fork_configd() and fork_sulogin() are related, special cases that handle the 30 * spawning of specific client processes for svc.startd. 31 */ 32 33 #include <sys/contract/process.h> 34 #include <sys/corectl.h> 35 #include <sys/ctfs.h> 36 #include <sys/stat.h> 37 #include <sys/types.h> 38 #include <sys/uio.h> 39 #include <sys/wait.h> 40 #include <assert.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <libcontract.h> 44 #include <libcontract_priv.h> 45 #include <libscf_priv.h> 46 #include <limits.h> 47 #include <poll.h> 48 #include <port.h> 49 #include <signal.h> 50 #include <stdarg.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <string.h> 54 #include <unistd.h> 55 #include <utmpx.h> 56 #include <spawn.h> 57 58 #include "configd_exit.h" 59 #include "protocol.h" 60 #include "startd.h" 61 62 static struct utmpx *utmpp; /* pointer for getutxent() */ 63 64 pid_t 65 startd_fork1(int *forkerr) 66 { 67 pid_t p; 68 69 /* 70 * prefork stack 71 */ 72 wait_prefork(); 73 74 p = fork1(); 75 76 if (p == -1 && forkerr != NULL) 77 *forkerr = errno; 78 79 /* 80 * postfork stack 81 */ 82 wait_postfork(p); 83 84 return (p); 85 } 86 87 /* 88 * void fork_mount(char *, char *) 89 * Run mount(1M) with the given options and mount point. (mount(1M) has much 90 * hidden knowledge; it's much less correct to reimplement that logic here to 91 * save a fork(2)/exec(2) invocation.) 92 */ 93 int 94 fork_mount(char *path, char *opts) 95 { 96 pid_t pid; 97 uint_t tries = 0; 98 int status; 99 100 for (pid = fork1(); pid == -1; pid = fork1()) { 101 if (++tries > MAX_MOUNT_RETRIES) 102 return (-1); 103 104 (void) sleep(tries); 105 } 106 107 if (pid != 0) { 108 (void) waitpid(pid, &status, 0); 109 110 /* 111 * If our mount(1M) invocation exited by peculiar means, or with 112 * a non-zero status, our mount likelihood is low. 113 */ 114 if (!WIFEXITED(status) || 115 WEXITSTATUS(status) != 0) 116 return (-1); 117 118 return (0); 119 } 120 121 (void) execl("/sbin/mount", "mount", "-o", opts, path, NULL); 122 123 return (-1); 124 } 125 126 /* 127 * pid_t fork_common(...) 128 * Common routine used by fork_sulogin and fork_configd to fork a 129 * process in a contract with the provided terms. Invokes 130 * fork_sulogin (with its no-fork argument set) on errors. 131 */ 132 static pid_t 133 fork_common(const char *name, const char *svc_fmri, int retries, ctid_t *ctidp, 134 uint_t inf, uint_t crit, uint_t fatal, uint_t param, uint64_t cookie) 135 { 136 uint_t tries = 0; 137 int ctfd, err; 138 pid_t pid; 139 140 /* 141 * Establish process contract terms. 142 */ 143 if ((ctfd = open64(CTFS_ROOT "/process/template", O_RDWR)) == -1) { 144 fork_sulogin(B_TRUE, "Could not open process contract template " 145 "for %s: %s\n", name, strerror(errno)); 146 /* NOTREACHED */ 147 } 148 149 err = ct_tmpl_set_critical(ctfd, crit); 150 err |= ct_pr_tmpl_set_fatal(ctfd, fatal); 151 err |= ct_tmpl_set_informative(ctfd, inf); 152 err |= ct_pr_tmpl_set_param(ctfd, param); 153 err |= ct_tmpl_set_cookie(ctfd, cookie); 154 err |= ct_pr_tmpl_set_svc_fmri(ctfd, svc_fmri); 155 err |= ct_pr_tmpl_set_svc_aux(ctfd, name); 156 if (err) { 157 (void) close(ctfd); 158 fork_sulogin(B_TRUE, "Could not set %s process contract " 159 "terms\n", name); 160 /* NOTREACHED */ 161 } 162 163 if (err = ct_tmpl_activate(ctfd)) { 164 (void) close(ctfd); 165 fork_sulogin(B_TRUE, "Could not activate %s process contract " 166 "template: %s\n", name, strerror(err)); 167 /* NOTREACHED */ 168 } 169 170 /* 171 * Attempt to fork "retries" times. 172 */ 173 for (pid = fork1(); pid == -1; pid = fork1()) { 174 if (++tries > retries) { 175 /* 176 * When we exit the sulogin session, init(1M) 177 * will restart svc.startd(1M). 178 */ 179 err = errno; 180 (void) ct_tmpl_clear(ctfd); 181 (void) close(ctfd); 182 fork_sulogin(B_TRUE, "Could not fork to start %s: %s\n", 183 name, strerror(err)); 184 /* NOTREACHED */ 185 } 186 (void) sleep(tries); 187 } 188 189 /* 190 * Clean up, return pid and ctid. 191 */ 192 if (pid != 0 && (errno = contract_latest(ctidp)) != 0) 193 uu_die("Could not get new contract id for %s\n", name); 194 (void) ct_tmpl_clear(ctfd); 195 (void) close(ctfd); 196 197 return (pid); 198 } 199 200 /* 201 * void fork_sulogin(boolean_t, const char *, ...) 202 * When we are invoked with the -s flag from boot (or run into an unfixable 203 * situation), we run a private copy of sulogin. When the sulogin session 204 * is ended, we continue. This is the last fallback action for system 205 * maintenance. 206 * 207 * If immediate is true, fork_sulogin() executes sulogin(1M) directly, without 208 * forking. 209 * 210 * Because fork_sulogin() is needed potentially before we daemonize, we leave 211 * it outside the wait_register() framework. 212 */ 213 /*PRINTFLIKE2*/ 214 void 215 fork_sulogin(boolean_t immediate, const char *format, ...) 216 { 217 va_list args; 218 int fd_console; 219 220 (void) printf("Requesting System Maintenance Mode\n"); 221 222 if (!booting_to_single_user) 223 (void) printf("(See /lib/svc/share/README for more " 224 "information.)\n"); 225 226 va_start(args, format); 227 (void) vprintf(format, args); 228 va_end(args); 229 230 if (!immediate) { 231 ctid_t ctid; 232 pid_t pid; 233 234 pid = fork_common("sulogin", SVC_SULOGIN_FMRI, 235 MAX_SULOGIN_RETRIES, &ctid, CT_PR_EV_HWERR, 0, 236 CT_PR_EV_HWERR, CT_PR_PGRPONLY, SULOGIN_COOKIE); 237 238 if (pid != 0) { 239 (void) waitpid(pid, NULL, 0); 240 contract_abandon(ctid); 241 return; 242 } 243 /* close all inherited fds */ 244 closefrom(0); 245 } else { 246 (void) printf("Directly executing sulogin.\n"); 247 /* 248 * Can't call closefrom() in this MT section 249 * so safely close a minimum set of fds. 250 */ 251 (void) close(STDIN_FILENO); 252 (void) close(STDOUT_FILENO); 253 (void) close(STDERR_FILENO); 254 } 255 256 (void) setpgrp(); 257 258 /* open the console for sulogin */ 259 if ((fd_console = open("/dev/console", O_RDWR)) >= 0) { 260 if (fd_console != STDIN_FILENO) 261 while (dup2(fd_console, STDIN_FILENO) < 0 && 262 errno == EINTR) 263 ; 264 if (fd_console != STDOUT_FILENO) 265 while (dup2(fd_console, STDOUT_FILENO) < 0 && 266 errno == EINTR) 267 ; 268 if (fd_console != STDERR_FILENO) 269 while (dup2(fd_console, STDERR_FILENO) < 0 && 270 errno == EINTR) 271 ; 272 if (fd_console > STDERR_FILENO) 273 (void) close(fd_console); 274 } 275 276 setutxent(); 277 while ((utmpp = getutxent()) != NULL) { 278 if (strcmp(utmpp->ut_user, "LOGIN") != 0) { 279 if (strcmp(utmpp->ut_line, "console") == 0) { 280 (void) kill(utmpp->ut_pid, 9); 281 break; 282 } 283 } 284 } 285 286 (void) execl("/sbin/sulogin", "sulogin", NULL); 287 288 uu_warn("Could not exec() sulogin"); 289 290 exit(1); 291 } 292 293 #define CONFIGD_PATH "/lib/svc/bin/svc.configd" 294 295 /* 296 * void fork_configd(int status) 297 * We are interested in exit events (since the parent's exiting means configd 298 * is ready to run and since the child's exiting indicates an error case) and 299 * in empty events. This means we have a unique template for initiating 300 * configd. 301 */ 302 void 303 fork_configd(int exitstatus) 304 { 305 pid_t pid; 306 ctid_t ctid = -1; 307 int err; 308 char path[PATH_MAX]; 309 310 /* 311 * Checking the existatus for the potential failure of the 312 * daemonized svc.configd. If this is not the first time 313 * through, but a call from the svc.configd monitoring thread 314 * after a failure this is the status that is expected. Other 315 * failures are exposed during initialization or are fixed 316 * by a restart (e.g door closings). 317 * 318 * If this is on-disk database corruption it will also be 319 * caught by a restart but could be cleared before the restart. 320 * 321 * Or this could be internal database corruption due to a 322 * rogue service that needs to be cleared before restart. 323 */ 324 if (WEXITSTATUS(exitstatus) == CONFIGD_EXIT_DATABASE_BAD) { 325 fork_sulogin(B_FALSE, "svc.configd exited with database " 326 "corrupt error after initialization of the repository\n"); 327 } 328 329 retry: 330 log_framework(LOG_DEBUG, "fork_configd trying to start svc.configd\n"); 331 332 /* 333 * If we're retrying, we will have an old contract lying around 334 * from the failure. Since we're going to be creating a new 335 * contract shortly, we abandon the old one now. 336 */ 337 if (ctid != -1) 338 contract_abandon(ctid); 339 ctid = -1; 340 341 pid = fork_common("svc.configd", SCF_SERVICE_CONFIGD, 342 MAX_CONFIGD_RETRIES, &ctid, 0, CT_PR_EV_EXIT, 0, 343 CT_PR_INHERIT | CT_PR_REGENT, CONFIGD_COOKIE); 344 345 if (pid != 0) { 346 int exitstatus; 347 348 st->st_configd_pid = pid; 349 350 if (waitpid(pid, &exitstatus, 0) == -1) { 351 fork_sulogin(B_FALSE, "waitpid on svc.configd " 352 "failed: %s\n", strerror(errno)); 353 } else if (WIFEXITED(exitstatus)) { 354 char *errstr; 355 356 /* 357 * Examine exitstatus. This will eventually get more 358 * complicated, as we will want to teach startd how to 359 * invoke configd with alternate repositories, etc. 360 * 361 * Note that exec(2) failure results in an exit status 362 * of 1, resulting in the default clause below. 363 */ 364 365 /* 366 * Assign readable strings to cases we don't handle, or 367 * have error outcomes that cannot be eliminated. 368 */ 369 switch (WEXITSTATUS(exitstatus)) { 370 case CONFIGD_EXIT_BAD_ARGS: 371 errstr = "bad arguments"; 372 break; 373 374 case CONFIGD_EXIT_DATABASE_BAD: 375 errstr = "database corrupt"; 376 break; 377 378 case CONFIGD_EXIT_DATABASE_LOCKED: 379 errstr = "database locked"; 380 break; 381 case CONFIGD_EXIT_INIT_FAILED: 382 errstr = "initialization failure"; 383 break; 384 case CONFIGD_EXIT_DOOR_INIT_FAILED: 385 errstr = "door initialization failure"; 386 break; 387 case CONFIGD_EXIT_DATABASE_INIT_FAILED: 388 errstr = "database initialization failure"; 389 break; 390 case CONFIGD_EXIT_NO_THREADS: 391 errstr = "no threads available"; 392 break; 393 case CONFIGD_EXIT_LOST_MAIN_DOOR: 394 errstr = "lost door server attachment"; 395 break; 396 case 1: 397 errstr = "execution failure"; 398 break; 399 default: 400 errstr = "unknown error"; 401 break; 402 } 403 404 /* 405 * Remedial actions for various configd failures. 406 */ 407 switch (WEXITSTATUS(exitstatus)) { 408 case CONFIGD_EXIT_OKAY: 409 break; 410 411 case CONFIGD_EXIT_DATABASE_LOCKED: 412 /* attempt remount of / read-write */ 413 if (fs_is_read_only("/", NULL) == 1) { 414 if (fs_remount("/") == -1) 415 fork_sulogin(B_FALSE, 416 "remount of root " 417 "filesystem failed\n"); 418 419 goto retry; 420 } 421 break; 422 423 default: 424 fork_sulogin(B_FALSE, "svc.configd exited " 425 "with status %d (%s)\n", 426 WEXITSTATUS(exitstatus), errstr); 427 goto retry; 428 } 429 } else if (WIFSIGNALED(exitstatus)) { 430 char signame[SIG2STR_MAX]; 431 432 if (sig2str(WTERMSIG(exitstatus), signame)) 433 (void) snprintf(signame, SIG2STR_MAX, 434 "signum %d", WTERMSIG(exitstatus)); 435 436 fork_sulogin(B_FALSE, "svc.configd signalled:" 437 " %s\n", signame); 438 439 goto retry; 440 } else { 441 fork_sulogin(B_FALSE, "svc.configd non-exit " 442 "condition: 0x%x\n", exitstatus); 443 444 goto retry; 445 } 446 447 /* 448 * Announce that we have a valid svc.configd status. 449 */ 450 MUTEX_LOCK(&st->st_configd_live_lock); 451 st->st_configd_lives = 1; 452 err = pthread_cond_broadcast(&st->st_configd_live_cv); 453 assert(err == 0); 454 MUTEX_UNLOCK(&st->st_configd_live_lock); 455 456 log_framework(LOG_DEBUG, "fork_configd broadcasts configd is " 457 "live\n"); 458 return; 459 } 460 461 /* 462 * Set our per-process core file path to leave core files in 463 * /etc/svc/volatile directory, named after the PID to aid in debugging. 464 */ 465 (void) snprintf(path, sizeof (path), 466 "/etc/svc/volatile/core.configd.%%p"); 467 468 (void) core_set_process_path(path, strlen(path) + 1, getpid()); 469 470 log_framework(LOG_DEBUG, "executing svc.configd\n"); 471 472 (void) execl(CONFIGD_PATH, CONFIGD_PATH, NULL); 473 474 /* 475 * Status code is used above to identify configd exec failure. 476 */ 477 exit(1); 478 } 479 480 void * 481 fork_configd_thread(void *vctid) 482 { 483 int fd, err; 484 ctid_t configd_ctid = (ctid_t)vctid; 485 486 if (configd_ctid == -1) { 487 log_framework(LOG_DEBUG, 488 "fork_configd_thread starting svc.configd\n"); 489 fork_configd(0); 490 } else { 491 /* 492 * configd_ctid is known: we broadcast and continue. 493 * test contract for appropriate state by verifying that 494 * there is one or more processes within it? 495 */ 496 log_framework(LOG_DEBUG, 497 "fork_configd_thread accepting svc.configd with CTID %ld\n", 498 configd_ctid); 499 MUTEX_LOCK(&st->st_configd_live_lock); 500 st->st_configd_lives = 1; 501 (void) pthread_cond_broadcast(&st->st_configd_live_cv); 502 MUTEX_UNLOCK(&st->st_configd_live_lock); 503 } 504 505 fd = open64(CTFS_ROOT "/process/pbundle", O_RDONLY); 506 if (fd == -1) 507 uu_die("process bundle open failed"); 508 509 /* 510 * Make sure we get all events (including those generated by configd 511 * before this thread was started). 512 */ 513 err = ct_event_reset(fd); 514 assert(err == 0); 515 516 for (;;) { 517 int efd, sfd; 518 ct_evthdl_t ev; 519 uint32_t type; 520 ctevid_t evid; 521 ct_stathdl_t status; 522 ctid_t ctid; 523 uint64_t cookie; 524 pid_t pid; 525 526 if (err = ct_event_read_critical(fd, &ev)) { 527 assert(err != EINVAL && err != EAGAIN); 528 log_error(LOG_WARNING, 529 "Error reading next contract event: %s", 530 strerror(err)); 531 continue; 532 } 533 534 evid = ct_event_get_evid(ev); 535 ctid = ct_event_get_ctid(ev); 536 type = ct_event_get_type(ev); 537 538 /* Fetch cookie. */ 539 sfd = contract_open(ctid, "process", "status", O_RDONLY); 540 if (sfd < 0) { 541 ct_event_free(ev); 542 continue; 543 } 544 545 if (err = ct_status_read(sfd, CTD_COMMON, &status)) { 546 log_framework(LOG_WARNING, "Could not get status for " 547 "contract %ld: %s\n", ctid, strerror(err)); 548 549 ct_event_free(ev); 550 startd_close(sfd); 551 continue; 552 } 553 554 cookie = ct_status_get_cookie(status); 555 556 ct_status_free(status); 557 558 startd_close(sfd); 559 560 /* 561 * Don't process events from contracts we aren't interested in. 562 */ 563 if (cookie != CONFIGD_COOKIE) { 564 ct_event_free(ev); 565 continue; 566 } 567 568 if (type == CT_PR_EV_EXIT) { 569 int exitstatus; 570 571 (void) ct_pr_event_get_pid(ev, &pid); 572 (void) ct_pr_event_get_exitstatus(ev, 573 &exitstatus); 574 575 if (st->st_configd_pid != pid) { 576 /* 577 * This is the child exiting, so we 578 * abandon the contract and restart 579 * configd. 580 */ 581 contract_abandon(ctid); 582 fork_configd(exitstatus); 583 } 584 } 585 586 efd = contract_open(ctid, "process", "ctl", O_WRONLY); 587 if (efd != -1) { 588 (void) ct_ctl_ack(efd, evid); 589 startd_close(efd); 590 } 591 592 ct_event_free(ev); 593 594 } 595 596 /*NOTREACHED*/ 597 return (NULL); 598 } 599 600 void 601 fork_rc_script(char rl, const char *arg, boolean_t wait) 602 { 603 pid_t pid; 604 int tmpl, err, stat; 605 char path[20] = "/sbin/rc.", log[20] = "rc..log", timebuf[20]; 606 time_t now; 607 struct tm ltime; 608 size_t sz; 609 char *pathenv; 610 char **nenv; 611 612 path[8] = rl; 613 614 tmpl = open64(CTFS_ROOT "/process/template", O_RDWR); 615 if (tmpl >= 0) { 616 err = ct_tmpl_set_critical(tmpl, 0); 617 assert(err == 0); 618 619 err = ct_tmpl_set_informative(tmpl, 0); 620 assert(err == 0); 621 622 err = ct_pr_tmpl_set_fatal(tmpl, 0); 623 assert(err == 0); 624 625 err = ct_tmpl_activate(tmpl); 626 assert(err == 0); 627 628 err = close(tmpl); 629 assert(err == 0); 630 } else { 631 uu_warn("Could not create contract template for %s.\n", path); 632 } 633 634 pid = startd_fork1(NULL); 635 if (pid < 0) { 636 return; 637 } else if (pid != 0) { 638 /* parent */ 639 if (wait) { 640 do 641 err = waitpid(pid, &stat, 0); 642 while (err != 0 && errno == EINTR) 643 ; 644 645 if (!WIFEXITED(stat)) { 646 log_framework(LOG_INFO, 647 "%s terminated with waitpid() status %d.\n", 648 path, stat); 649 } else if (WEXITSTATUS(stat) != 0) { 650 log_framework(LOG_INFO, 651 "%s failed with status %d.\n", path, 652 WEXITSTATUS(stat)); 653 } 654 } 655 656 return; 657 } 658 659 /* child */ 660 661 log[2] = rl; 662 663 setlog(log); 664 665 now = time(NULL); 666 sz = strftime(timebuf, sizeof (timebuf), "%b %e %T", 667 localtime_r(&now, <ime)); 668 assert(sz != 0); 669 670 (void) fprintf(stderr, "%s Executing %s %s\n", timebuf, path, arg); 671 672 if (rl == 'S') 673 pathenv = "PATH=/sbin:/usr/sbin:/usr/bin"; 674 else 675 pathenv = "PATH=/usr/sbin:/usr/bin"; 676 677 nenv = set_smf_env(NULL, 0, pathenv, NULL, NULL); 678 679 (void) execle(path, path, arg, 0, nenv); 680 681 perror("exec"); 682 exit(0); 683 } 684 685 extern char **environ; 686 687 /* 688 * A local variation on system(3c) which accepts a timeout argument. This 689 * allows us to better ensure that the system will actually shut down. 690 * 691 * gracetime specifies an amount of time in seconds which the routine must wait 692 * after the command exits, to allow for asynchronous effects (like sent 693 * signals) to take effect. This can be zero. 694 */ 695 void 696 fork_with_timeout(const char *cmd, uint_t gracetime, uint_t timeout) 697 { 698 int err = 0; 699 pid_t pid; 700 char *argv[4]; 701 posix_spawnattr_t attr; 702 posix_spawn_file_actions_t factions; 703 704 sigset_t mask, savemask; 705 uint_t msec_timeout; 706 uint_t msec_spent = 0; 707 uint_t msec_gracetime; 708 int status; 709 710 msec_timeout = timeout * 1000; 711 msec_gracetime = gracetime * 1000; 712 713 /* 714 * See also system(3c) in libc. This is very similar, except 715 * that we avoid some unneeded complexity. 716 */ 717 err = posix_spawnattr_init(&attr); 718 if (err == 0) 719 err = posix_spawnattr_setflags(&attr, 720 POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | 721 POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP | 722 POSIX_SPAWN_NOEXECERR_NP); 723 724 /* 725 * We choose to close fd's above 2, a deviation from system. 726 */ 727 if (err == 0) 728 err = posix_spawn_file_actions_init(&factions); 729 if (err == 0) 730 err = posix_spawn_file_actions_addclosefrom_np(&factions, 731 STDERR_FILENO + 1); 732 733 (void) sigemptyset(&mask); 734 (void) sigaddset(&mask, SIGCHLD); 735 (void) thr_sigsetmask(SIG_BLOCK, &mask, &savemask); 736 737 argv[0] = "/bin/sh"; 738 argv[1] = "-c"; 739 argv[2] = (char *)cmd; 740 argv[3] = NULL; 741 742 if (err == 0) 743 err = posix_spawn(&pid, "/bin/sh", &factions, &attr, 744 (char *const *)argv, (char *const *)environ); 745 746 (void) posix_spawnattr_destroy(&attr); 747 (void) posix_spawn_file_actions_destroy(&factions); 748 749 if (err) { 750 uu_warn("Failed to spawn %s: %s\n", cmd, strerror(err)); 751 } else { 752 for (;;) { 753 int w; 754 w = waitpid(pid, &status, WNOHANG); 755 if (w == -1 && errno != EINTR) 756 break; 757 if (w > 0) { 758 /* 759 * Command succeeded, so give it gracetime 760 * seconds for it to have an effect. 761 */ 762 if (status == 0 && msec_gracetime != 0) 763 (void) poll(NULL, 0, msec_gracetime); 764 break; 765 } 766 767 (void) poll(NULL, 0, 100); 768 msec_spent += 100; 769 /* 770 * If we timed out, kill off the process, then try to 771 * wait for it-- it's possible that we could accumulate 772 * a zombie here since we don't allow waitpid to hang, 773 * but it's better to let that happen and continue to 774 * make progress. 775 */ 776 if (msec_spent >= msec_timeout) { 777 uu_warn("'%s' timed out after %d " 778 "seconds. Killing.\n", cmd, 779 timeout); 780 (void) kill(pid, SIGTERM); 781 (void) poll(NULL, 0, 100); 782 (void) kill(pid, SIGKILL); 783 (void) poll(NULL, 0, 100); 784 (void) waitpid(pid, &status, WNOHANG); 785 break; 786 } 787 } 788 } 789 (void) thr_sigsetmask(SIG_BLOCK, &savemask, NULL); 790 } 791