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