github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/tini/src/tini.c (about) 1 /* See LICENSE file for copyright and license details. */ 2 #define _GNU_SOURCE 3 4 #include <sys/types.h> 5 #include <sys/wait.h> 6 #include <sys/prctl.h> 7 8 #include <assert.h> 9 #include <errno.h> 10 #include <signal.h> 11 #include <string.h> 12 #include <time.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <unistd.h> 16 #include <stdbool.h> 17 18 #include "tiniConfig.h" 19 #include "tiniLicense.h" 20 21 #if TINI_MINIMAL 22 #define PRINT_FATAL(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); 23 #define PRINT_WARNING(...) if (verbosity > 0) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } 24 #define PRINT_INFO(...) if (verbosity > 1) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); } 25 #define PRINT_DEBUG(...) if (verbosity > 2) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); } 26 #define PRINT_TRACE(...) if (verbosity > 3) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); } 27 #define DEFAULT_VERBOSITY 0 28 #else 29 #define PRINT_FATAL(...) fprintf(stderr, "[FATAL tini (%i)] ", getpid()); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); 30 #define PRINT_WARNING(...) if (verbosity > 0) { fprintf(stderr, "[WARN tini (%i)] ", getpid()); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } 31 #define PRINT_INFO(...) if (verbosity > 1) { fprintf(stdout, "[INFO tini (%i)] ", getpid()); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); } 32 #define PRINT_DEBUG(...) if (verbosity > 2) { fprintf(stdout, "[DEBUG tini (%i)] ", getpid()); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); } 33 #define PRINT_TRACE(...) if (verbosity > 3) { fprintf(stdout, "[TRACE tini (%i)] ", getpid()); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); } 34 #define DEFAULT_VERBOSITY 1 35 #endif 36 37 #define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0])) 38 39 #define INT32_BITFIELD_SET(F, i) ( F[(i / 32)] |= (1 << (i % 32)) ) 40 #define INT32_BITFIELD_CLEAR(F, i) ( F[(i / 32)] &= ~(1 << (i % 32)) ) 41 #define INT32_BITFIELD_TEST(F, i) ( F[(i / 32)] & (1 << (i % 32)) ) 42 #define INT32_BITFIELD_CHECK_BOUNDS(F, i) do { assert(i >= 0); assert(ARRAY_LEN(F) > (uint) (i / 32)); } while(0) 43 44 #define STATUS_MAX 255 45 #define STATUS_MIN 0 46 47 typedef struct { 48 sigset_t* const sigmask_ptr; 49 struct sigaction* const sigttin_action_ptr; 50 struct sigaction* const sigttou_action_ptr; 51 } signal_configuration_t; 52 53 static const struct { 54 char *const name; 55 int number; 56 } signal_names[] = { 57 { "SIGHUP", SIGHUP }, 58 { "SIGINT", SIGINT }, 59 { "SIGQUIT", SIGQUIT }, 60 { "SIGILL", SIGILL }, 61 { "SIGTRAP", SIGTRAP }, 62 { "SIGABRT", SIGABRT }, 63 { "SIGBUS", SIGBUS }, 64 { "SIGFPE", SIGFPE }, 65 { "SIGKILL", SIGKILL }, 66 { "SIGUSR1", SIGUSR1 }, 67 { "SIGSEGV", SIGSEGV }, 68 { "SIGUSR2", SIGUSR2 }, 69 { "SIGPIPE", SIGPIPE }, 70 { "SIGALRM", SIGALRM }, 71 { "SIGTERM", SIGTERM }, 72 { "SIGCHLD", SIGCHLD }, 73 { "SIGCONT", SIGCONT }, 74 { "SIGSTOP", SIGSTOP }, 75 { "SIGTSTP", SIGTSTP }, 76 { "SIGTTIN", SIGTTIN }, 77 { "SIGTTOU", SIGTTOU }, 78 { "SIGURG", SIGURG }, 79 { "SIGXCPU", SIGXCPU }, 80 { "SIGXFSZ", SIGXFSZ }, 81 { "SIGVTALRM", SIGVTALRM }, 82 { "SIGPROF", SIGPROF }, 83 { "SIGWINCH", SIGWINCH }, 84 { "SIGSYS", SIGSYS }, 85 }; 86 87 static unsigned int verbosity = DEFAULT_VERBOSITY; 88 89 static int32_t expect_status[(STATUS_MAX - STATUS_MIN + 1) / 32]; 90 91 #ifdef PR_SET_CHILD_SUBREAPER 92 #define HAS_SUBREAPER 1 93 #define OPT_STRING "p:hvwgle:s" 94 #define SUBREAPER_ENV_VAR "TINI_SUBREAPER" 95 #else 96 #define HAS_SUBREAPER 0 97 #define OPT_STRING "p:hvwgle:" 98 #endif 99 100 #define VERBOSITY_ENV_VAR "TINI_VERBOSITY" 101 #define KILL_PROCESS_GROUP_GROUP_ENV_VAR "TINI_KILL_PROCESS_GROUP" 102 103 #define TINI_VERSION_STRING "tini version " TINI_VERSION TINI_GIT 104 105 106 #if HAS_SUBREAPER 107 static unsigned int subreaper = 0; 108 #endif 109 static unsigned int parent_death_signal = 0; 110 static unsigned int kill_process_group = 0; 111 112 static unsigned int warn_on_reap = 0; 113 114 static struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 }; 115 116 static const char reaper_warning[] = "Tini is not running as PID 1 " 117 #if HAS_SUBREAPER 118 "and isn't registered as a child subreaper" 119 #endif 120 ".\n\ 121 Zombie processes will not be re-parented to Tini, so zombie reaping won't work.\n\ 122 To fix the problem, " 123 #if HAS_SUBREAPER 124 #ifndef TINI_MINIMAL 125 "use the -s option or " 126 #endif 127 "set the environment variable " SUBREAPER_ENV_VAR " to register Tini as a child subreaper, or " 128 #endif 129 "run Tini as PID 1."; 130 131 int restore_signals(const signal_configuration_t* const sigconf_ptr) { 132 if (sigprocmask(SIG_SETMASK, sigconf_ptr->sigmask_ptr, NULL)) { 133 PRINT_FATAL("Restoring child signal mask failed: '%s'", strerror(errno)); 134 return 1; 135 } 136 137 if (sigaction(SIGTTIN, sigconf_ptr->sigttin_action_ptr, NULL)) { 138 PRINT_FATAL("Restoring SIGTTIN handler failed: '%s'", strerror((errno))); 139 return 1; 140 } 141 142 if (sigaction(SIGTTOU, sigconf_ptr->sigttou_action_ptr, NULL)) { 143 PRINT_FATAL("Restoring SIGTTOU handler failed: '%s'", strerror((errno))); 144 return 1; 145 } 146 147 return 0; 148 } 149 150 int isolate_child() { 151 // Put the child into a new process group. 152 if (setpgid(0, 0) < 0) { 153 PRINT_FATAL("setpgid failed: %s", strerror(errno)); 154 return 1; 155 } 156 157 // If there is a tty, allocate it to this new process group. We 158 // can do this in the child process because we're blocking 159 // SIGTTIN / SIGTTOU. 160 161 // Doing it in the child process avoids a race condition scenario 162 // if Tini is calling Tini (in which case the grandparent may make the 163 // parent the foreground process group, and the actual child ends up... 164 // in the background!) 165 if (tcsetpgrp(STDIN_FILENO, getpgrp())) { 166 if (errno == ENOTTY) { 167 PRINT_DEBUG("tcsetpgrp failed: no tty (ok to proceed)"); 168 } else if (errno == ENXIO) { 169 // can occur on lx-branded zones 170 PRINT_DEBUG("tcsetpgrp failed: no such device (ok to proceed)"); 171 } else { 172 PRINT_FATAL("tcsetpgrp failed: %s", strerror(errno)); 173 return 1; 174 } 175 } 176 177 return 0; 178 } 179 180 181 int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], int* const child_pid_ptr) { 182 pid_t pid; 183 184 // TODO: check if tini was a foreground process to begin with (it's not OK to "steal" the foreground!") 185 186 pid = fork(); 187 if (pid < 0) { 188 PRINT_FATAL("fork failed: %s", strerror(errno)); 189 return 1; 190 } else if (pid == 0) { 191 192 // Put the child in a process group and make it the foreground process if there is a tty. 193 if (isolate_child()) { 194 return 1; 195 } 196 197 // Restore all signal handlers to the way they were before we touched them. 198 if (restore_signals(sigconf_ptr)) { 199 return 1; 200 } 201 202 execvp(argv[0], argv); 203 204 // execvp will only return on an error so make sure that we check the errno 205 // and exit with the correct return status for the error that we encountered 206 // See: http://www.tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF 207 int status = 1; 208 switch (errno) { 209 case ENOENT: 210 status = 127; 211 break; 212 case EACCES: 213 status = 126; 214 break; 215 } 216 PRINT_FATAL("exec %s failed: %s", argv[0], strerror(errno)); 217 return status; 218 } else { 219 // Parent 220 PRINT_INFO("Spawned child process '%s' with pid '%i'", argv[0], pid); 221 *child_pid_ptr = pid; 222 return 0; 223 } 224 } 225 226 void print_usage(char* const name, FILE* const file) { 227 fprintf(file, "%s (%s)\n", basename(name), TINI_VERSION_STRING); 228 229 #if TINI_MINIMAL 230 fprintf(file, "Usage: %s PROGRAM [ARGS] | --version\n\n", basename(name)); 231 #else 232 fprintf(file, "Usage: %s [OPTIONS] PROGRAM -- [ARGS] | --version\n\n", basename(name)); 233 #endif 234 fprintf(file, "Execute a program under the supervision of a valid init process (%s)\n\n", basename(name)); 235 236 fprintf(file, "Command line options:\n\n"); 237 238 fprintf(file, " --version: Show version and exit.\n"); 239 240 #if TINI_MINIMAL 241 #else 242 fprintf(file, " -h: Show this help message and exit.\n"); 243 #if HAS_SUBREAPER 244 fprintf(file, " -s: Register as a process subreaper (requires Linux >= 3.4).\n"); 245 #endif 246 fprintf(file, " -p SIGNAL: Trigger SIGNAL when parent dies, e.g. \"-p SIGKILL\".\n"); 247 fprintf(file, " -v: Generate more verbose output. Repeat up to 3 times.\n"); 248 fprintf(file, " -w: Print a warning when processes are getting reaped.\n"); 249 fprintf(file, " -g: Send signals to the child's process group.\n"); 250 fprintf(file, " -e EXIT_CODE: Remap EXIT_CODE (from 0 to 255) to 0.\n"); 251 fprintf(file, " -l: Show license and exit.\n"); 252 #endif 253 254 fprintf(file, "\n"); 255 256 fprintf(file, "Environment variables:\n\n"); 257 #if HAS_SUBREAPER 258 fprintf(file, " %s: Register as a process subreaper (requires Linux >= 3.4).\n", SUBREAPER_ENV_VAR); 259 #endif 260 fprintf(file, " %s: Set the verbosity level (default: %d).\n", VERBOSITY_ENV_VAR, DEFAULT_VERBOSITY); 261 fprintf(file, " %s: Send signals to the child's process group.\n", KILL_PROCESS_GROUP_GROUP_ENV_VAR); 262 263 fprintf(file, "\n"); 264 } 265 266 void print_license(FILE* const file) { 267 if(LICENSE_len > fwrite(LICENSE, sizeof(char), LICENSE_len, file)) { 268 // Don't handle this error for now, since parse_args won't care 269 // about the return value. We do need to check it to compile with 270 // older glibc, though. 271 // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509 272 // See: http://sourceware.org/bugzilla/show_bug.cgi?id=11959 273 } 274 } 275 276 int set_pdeathsig(char* const arg) { 277 size_t i; 278 279 for (i = 0; i < ARRAY_LEN(signal_names); i++) { 280 if (strcmp(signal_names[i].name, arg) == 0) { 281 /* Signals start at value "1" */ 282 parent_death_signal = signal_names[i].number; 283 return 0; 284 } 285 } 286 287 return 1; 288 } 289 290 int add_expect_status(char* arg) { 291 long status = 0; 292 char* endptr = NULL; 293 status = strtol(arg, &endptr, 10); 294 295 if ((endptr == NULL) || (*endptr != 0)) { 296 return 1; 297 } 298 299 if ((status < STATUS_MIN) || (status > STATUS_MAX)) { 300 return 1; 301 } 302 303 INT32_BITFIELD_CHECK_BOUNDS(expect_status, status); 304 INT32_BITFIELD_SET(expect_status, status); 305 return 0; 306 } 307 308 int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[], int* const parse_fail_exitcode_ptr) { 309 char* name = argv[0]; 310 311 // We handle --version if it's the *only* argument provided. 312 if (argc == 2 && strcmp("--version", argv[1]) == 0) { 313 *parse_fail_exitcode_ptr = 0; 314 fprintf(stdout, "%s\n", TINI_VERSION_STRING); 315 return 1; 316 } 317 318 #ifndef TINI_MINIMAL 319 int c; 320 while ((c = getopt(argc, argv, OPT_STRING)) != -1) { 321 switch (c) { 322 case 'h': 323 print_usage(name, stdout); 324 *parse_fail_exitcode_ptr = 0; 325 return 1; 326 #if HAS_SUBREAPER 327 case 's': 328 subreaper++; 329 break; 330 #endif 331 case 'p': 332 if (set_pdeathsig(optarg)) { 333 PRINT_FATAL("Not a valid option for -p: %s", optarg); 334 *parse_fail_exitcode_ptr = 1; 335 return 1; 336 } 337 break; 338 339 case 'v': 340 verbosity++; 341 break; 342 343 case 'w': 344 warn_on_reap++; 345 break; 346 347 case 'g': 348 kill_process_group++; 349 break; 350 351 case 'e': 352 if (add_expect_status(optarg)) { 353 PRINT_FATAL("Not a valid option for -e: %s", optarg); 354 *parse_fail_exitcode_ptr = 1; 355 return 1; 356 } 357 break; 358 359 case 'l': 360 print_license(stdout); 361 *parse_fail_exitcode_ptr = 0; 362 return 1; 363 364 case '?': 365 print_usage(name, stderr); 366 return 1; 367 default: 368 /* Should never happen */ 369 return 1; 370 } 371 } 372 #endif 373 374 *child_args_ptr_ptr = calloc(argc-optind+1, sizeof(char*)); 375 if (*child_args_ptr_ptr == NULL) { 376 PRINT_FATAL("Failed to allocate memory for child args: '%s'", strerror(errno)); 377 return 1; 378 } 379 380 int i; 381 for (i = 0; i < argc - optind; i++) { 382 (**child_args_ptr_ptr)[i] = argv[optind+i]; 383 } 384 (**child_args_ptr_ptr)[i] = NULL; 385 386 if (i == 0) { 387 /* User forgot to provide args! */ 388 print_usage(name, stderr); 389 return 1; 390 } 391 392 return 0; 393 } 394 395 int parse_env() { 396 #if HAS_SUBREAPER 397 if (getenv(SUBREAPER_ENV_VAR) != NULL) { 398 subreaper++; 399 } 400 #endif 401 402 if (getenv(KILL_PROCESS_GROUP_GROUP_ENV_VAR) != NULL) { 403 kill_process_group++; 404 } 405 406 char* env_verbosity = getenv(VERBOSITY_ENV_VAR); 407 if (env_verbosity != NULL) { 408 verbosity = atoi(env_verbosity); 409 } 410 411 return 0; 412 } 413 414 415 #if HAS_SUBREAPER 416 int register_subreaper () { 417 if (subreaper > 0) { 418 if (prctl(PR_SET_CHILD_SUBREAPER, 1)) { 419 if (errno == EINVAL) { 420 PRINT_FATAL("PR_SET_CHILD_SUBREAPER is unavailable on this platform. Are you using Linux >= 3.4?") 421 } else { 422 PRINT_FATAL("Failed to register as child subreaper: %s", strerror(errno)) 423 } 424 return 1; 425 } else { 426 PRINT_TRACE("Registered as child subreaper"); 427 } 428 } 429 return 0; 430 } 431 #endif 432 433 434 void reaper_check () { 435 /* Check that we can properly reap zombies */ 436 #if HAS_SUBREAPER 437 int bit = 0; 438 #endif 439 440 if (getpid() == 1) { 441 return; 442 } 443 444 #if HAS_SUBREAPER 445 if (prctl(PR_GET_CHILD_SUBREAPER, &bit)) { 446 PRINT_DEBUG("Failed to read child subreaper attribute: %s", strerror(errno)); 447 } else if (bit == 1) { 448 return; 449 } 450 #endif 451 452 PRINT_WARNING(reaper_warning); 453 } 454 455 456 int configure_signals(sigset_t* const parent_sigset_ptr, const signal_configuration_t* const sigconf_ptr) { 457 /* Block all signals that are meant to be collected by the main loop */ 458 if (sigfillset(parent_sigset_ptr)) { 459 PRINT_FATAL("sigfillset failed: '%s'", strerror(errno)); 460 return 1; 461 } 462 463 // These ones shouldn't be collected by the main loop 464 uint i; 465 int signals_for_tini[] = {SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGABRT, SIGTRAP, SIGSYS, SIGTTIN, SIGTTOU}; 466 for (i = 0; i < ARRAY_LEN(signals_for_tini); i++) { 467 if (sigdelset(parent_sigset_ptr, signals_for_tini[i])) { 468 PRINT_FATAL("sigdelset failed: '%i'", signals_for_tini[i]); 469 return 1; 470 } 471 } 472 473 if (sigprocmask(SIG_SETMASK, parent_sigset_ptr, sigconf_ptr->sigmask_ptr)) { 474 PRINT_FATAL("sigprocmask failed: '%s'", strerror(errno)); 475 return 1; 476 } 477 478 // Handle SIGTTIN and SIGTTOU separately. Since Tini makes the child process group 479 // the foreground process group, there's a chance Tini can end up not controlling the tty. 480 // If TOSTOP is set on the tty, this could block Tini on writing debug messages. We don't 481 // want that. Ignore those signals. 482 struct sigaction ign_action; 483 memset(&ign_action, 0, sizeof ign_action); 484 485 ign_action.sa_handler = SIG_IGN; 486 sigemptyset(&ign_action.sa_mask); 487 488 if (sigaction(SIGTTIN, &ign_action, sigconf_ptr->sigttin_action_ptr)) { 489 PRINT_FATAL("Failed to ignore SIGTTIN"); 490 return 1; 491 } 492 493 if (sigaction(SIGTTOU, &ign_action, sigconf_ptr->sigttou_action_ptr)) { 494 PRINT_FATAL("Failed to ignore SIGTTOU"); 495 return 1; 496 } 497 498 return 0; 499 } 500 501 int wait_and_forward_signal(sigset_t const* const parent_sigset_ptr, pid_t const child_pid) { 502 siginfo_t sig; 503 504 if (sigtimedwait(parent_sigset_ptr, &sig, &ts) == -1) { 505 switch (errno) { 506 case EAGAIN: 507 break; 508 case EINTR: 509 break; 510 default: 511 PRINT_FATAL("Unexpected error in sigtimedwait: '%s'", strerror(errno)); 512 return 1; 513 } 514 } else { 515 /* There is a signal to handle here */ 516 switch (sig.si_signo) { 517 case SIGCHLD: 518 /* Special-cased, as we don't forward SIGCHLD. Instead, we'll 519 * fallthrough to reaping processes. 520 */ 521 PRINT_DEBUG("Received SIGCHLD"); 522 break; 523 default: 524 PRINT_DEBUG("Passing signal: '%s'", strsignal(sig.si_signo)); 525 /* Forward anything else */ 526 if (kill(kill_process_group ? -child_pid : child_pid, sig.si_signo)) { 527 if (errno == ESRCH) { 528 PRINT_WARNING("Child was dead when forwarding signal"); 529 } else { 530 PRINT_FATAL("Unexpected error when forwarding signal: '%s'", strerror(errno)); 531 return 1; 532 } 533 } 534 break; 535 } 536 } 537 538 return 0; 539 } 540 541 int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) { 542 pid_t current_pid; 543 int current_status; 544 545 while (1) { 546 current_pid = waitpid(-1, ¤t_status, WNOHANG); 547 548 switch (current_pid) { 549 550 case -1: 551 if (errno == ECHILD) { 552 PRINT_TRACE("No child to wait"); 553 break; 554 } 555 PRINT_FATAL("Error while waiting for pids: '%s'", strerror(errno)); 556 return 1; 557 558 case 0: 559 PRINT_TRACE("No child to reap"); 560 break; 561 562 default: 563 /* A child was reaped. Check whether it's the main one. If it is, then 564 * set the exit_code, which will cause us to exit once we've reaped everyone else. 565 */ 566 PRINT_DEBUG("Reaped child with pid: '%i'", current_pid); 567 if (current_pid == child_pid) { 568 if (WIFEXITED(current_status)) { 569 /* Our process exited normally. */ 570 PRINT_INFO("Main child exited normally (with status '%i')", WEXITSTATUS(current_status)); 571 *child_exitcode_ptr = WEXITSTATUS(current_status); 572 } else if (WIFSIGNALED(current_status)) { 573 /* Our process was terminated. Emulate what sh / bash 574 * would do, which is to return 128 + signal number. 575 */ 576 PRINT_INFO("Main child exited with signal (with signal '%s')", strsignal(WTERMSIG(current_status))); 577 *child_exitcode_ptr = 128 + WTERMSIG(current_status); 578 } else { 579 PRINT_FATAL("Main child exited for unknown reason"); 580 return 1; 581 } 582 583 // Be safe, ensure the status code is indeed between 0 and 255. 584 *child_exitcode_ptr = *child_exitcode_ptr % (STATUS_MAX - STATUS_MIN + 1); 585 586 // If this exitcode was remapped, then set it to 0. 587 INT32_BITFIELD_CHECK_BOUNDS(expect_status, *child_exitcode_ptr); 588 if (INT32_BITFIELD_TEST(expect_status, *child_exitcode_ptr)) { 589 *child_exitcode_ptr = 0; 590 } 591 } else if (warn_on_reap > 0) { 592 PRINT_WARNING("Reaped zombie process with pid=%i", current_pid); 593 } 594 595 // Check if other childs have been reaped. 596 continue; 597 } 598 599 /* If we make it here, that's because we did not continue in the switch case. */ 600 break; 601 } 602 603 return 0; 604 } 605 606 607 int main(int argc, char *argv[]) { 608 pid_t child_pid; 609 610 // Those are passed to functions to get an exitcode back. 611 int child_exitcode = -1; // This isn't a valid exitcode, and lets us tell whether the child has exited. 612 int parse_exitcode = 1; // By default, we exit with 1 if parsing fails. 613 614 /* Parse command line arguments */ 615 char* (*child_args_ptr)[]; 616 int parse_args_ret = parse_args(argc, argv, &child_args_ptr, &parse_exitcode); 617 if (parse_args_ret) { 618 return parse_exitcode; 619 } 620 621 /* Parse environment */ 622 if (parse_env()) { 623 return 1; 624 } 625 626 /* Configure signals */ 627 sigset_t parent_sigset, child_sigset; 628 struct sigaction sigttin_action, sigttou_action; 629 memset(&sigttin_action, 0, sizeof sigttin_action); 630 memset(&sigttou_action, 0, sizeof sigttou_action); 631 632 signal_configuration_t child_sigconf = { 633 .sigmask_ptr = &child_sigset, 634 .sigttin_action_ptr = &sigttin_action, 635 .sigttou_action_ptr = &sigttou_action, 636 }; 637 638 if (configure_signals(&parent_sigset, &child_sigconf)) { 639 return 1; 640 } 641 642 /* Trigger signal on this process when the parent process exits. */ 643 if (parent_death_signal && prctl(PR_SET_PDEATHSIG, parent_death_signal)) { 644 PRINT_FATAL("Failed to set up parent death signal"); 645 return 1; 646 } 647 648 #if HAS_SUBREAPER 649 /* If available and requested, register as a subreaper */ 650 if (register_subreaper()) { 651 return 1; 652 }; 653 #endif 654 655 /* Are we going to reap zombies properly? If not, warn. */ 656 reaper_check(); 657 658 /* Go on */ 659 int spawn_ret = spawn(&child_sigconf, *child_args_ptr, &child_pid); 660 if (spawn_ret) { 661 return spawn_ret; 662 } 663 free(child_args_ptr); 664 665 while (1) { 666 /* Wait for one signal, and forward it */ 667 if (wait_and_forward_signal(&parent_sigset, child_pid)) { 668 return 1; 669 } 670 671 /* Now, reap zombies */ 672 if (reap_zombies(child_pid, &child_exitcode)) { 673 return 1; 674 } 675 676 if (child_exitcode != -1) { 677 PRINT_TRACE("Exiting: child has exited"); 678 return child_exitcode; 679 } 680 } 681 }