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, &current_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  }