Add '-p' flag to get signalled when parent dies

Add a new flag '-p', which sets up the parent death signal to `SIGKILL`.
This will cause the kernel to send us a `SIGKILL` as soon as the direct
parent process dies. This is useful e.g. in combination with unshare(1)
from util-linux when using PID namespaces. When unshare forks the child,
which is about to become PID 1, killing the unshare parent will not
cause the child to exit. When executing the command

    $ unshare --pid --fork tini -- <prog>

then killing unshare will not cause tini to be killed. Since util-linux
v2.32, unshare has an option "--kill-child=<SIGNAL>" that will set up
the parent death signal for the forked process. This does not help
though in case either SELinux or AppArmor are in use and credentials of
the forked process change (e.g. by changing its UID), as these LSMs will
clear the parent death signal again. The following example would trigger
that situation:

    $ unshare --pid --fork setpriv --reuid user tini -s -- <prog>

The parent death signal will get reset by the LSMs as soon as `setpriv`
switchets its user ID to that of "user", and killing unshare will again
not result in tini being killed. The new '-p' flag helps that exact
scenario:

    $ unshare --pid --fork setpriv --reuid user tini -s -p SIGKILL -- <prog>

As soon as unshare is getting killed, tini will get signalled SIGKILL
and exit as well, tearing down <prog> with it.
pull/114/head
Patrick Steinhardt 7 years ago
parent 5b117de7f8
commit eb0f6de3a5
  1. 68
      src/tini.c

@ -50,17 +50,51 @@ typedef struct {
struct sigaction* const sigttou_action_ptr;
} signal_configuration_t;
static const struct {
char *const name;
int number;
} signal_names[] = {
{ "SIGHUP", SIGHUP },
{ "SIGINT", SIGINT },
{ "SIGQUIT", SIGQUIT },
{ "SIGILL", SIGILL },
{ "SIGTRAP", SIGTRAP },
{ "SIGABRT", SIGABRT },
{ "SIGBUS", SIGBUS },
{ "SIGFPE", SIGFPE },
{ "SIGKILL", SIGKILL },
{ "SIGUSR1", SIGUSR1 },
{ "SIGSEGV", SIGSEGV },
{ "SIGUSR2", SIGUSR2 },
{ "SIGPIPE", SIGPIPE },
{ "SIGALRM", SIGALRM },
{ "SIGTERM", SIGTERM },
{ "SIGCHLD", SIGCHLD },
{ "SIGCONT", SIGCONT },
{ "SIGSTOP", SIGSTOP },
{ "SIGTSTP", SIGTSTP },
{ "SIGTTIN", SIGTTIN },
{ "SIGTTOU", SIGTTOU },
{ "SIGURG", SIGURG },
{ "SIGXCPU", SIGXCPU },
{ "SIGXFSZ", SIGXFSZ },
{ "SIGVTALRM", SIGVTALRM },
{ "SIGPROF", SIGPROF },
{ "SIGWINCH", SIGWINCH },
{ "SIGSYS", SIGSYS },
};
static unsigned int verbosity = DEFAULT_VERBOSITY;
static int32_t expect_status[(STATUS_MAX - STATUS_MIN + 1) / 32];
#ifdef PR_SET_CHILD_SUBREAPER
#define HAS_SUBREAPER 1
#define OPT_STRING "hvwgle:s"
#define OPT_STRING "p:hvwgle:s"
#define SUBREAPER_ENV_VAR "TINI_SUBREAPER"
#else
#define HAS_SUBREAPER 0
#define OPT_STRING "hvwgle:"
#define OPT_STRING "p:hvwgle:"
#endif
#define VERBOSITY_ENV_VAR "TINI_VERBOSITY"
@ -71,6 +105,7 @@ static int32_t expect_status[(STATUS_MAX - STATUS_MIN + 1) / 32];
#if HAS_SUBREAPER
static unsigned int subreaper = 0;
#endif
static unsigned int parent_death_signal = 0;
static unsigned int kill_process_group = 0;
static unsigned int warn_on_reap = 0;
@ -207,6 +242,7 @@ void print_usage(char* const name, FILE* const file) {
#if HAS_SUBREAPER
fprintf(file, " -s: Register as a process subreaper (requires Linux >= 3.4).\n");
#endif
fprintf(file, " -p SIGNAL: Trigger SIGNAL when parent dies, e.g. \"-p SIGKILL\".\n");
fprintf(file, " -v: Generate more verbose output. Repeat up to 3 times.\n");
fprintf(file, " -w: Print a warning when processes are getting reaped.\n");
fprintf(file, " -g: Send signals to the child's process group.\n");
@ -235,6 +271,20 @@ void print_license(FILE* const file) {
}
}
int set_pdeathsig(char* const arg) {
size_t i;
for (i = 0; i < ARRAY_LEN(signal_names); i++) {
if (strcmp(signal_names[i].name, arg) == 0) {
/* Signals start at value "1" */
parent_death_signal = signal_names[i].number;
return 0;
}
}
return 1;
}
int add_expect_status(char* arg) {
long status = 0;
char* endptr = NULL;
@ -276,6 +326,14 @@ int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[
subreaper++;
break;
#endif
case 'p':
if (set_pdeathsig(optarg)) {
PRINT_FATAL("Not a valid option for -p: %s", optarg);
*parse_fail_exitcode_ptr = 1;
return 1;
}
break;
case 'v':
verbosity++;
break;
@ -575,6 +633,12 @@ int main(int argc, char *argv[]) {
return 1;
}
/* Trigger signal on this process when the parent process exits. */
if (parent_death_signal && prctl(PR_SET_PDEATHSIG, parent_death_signal)) {
PRINT_FATAL("Failed to set up parent death signal");
return 1;
}
#if HAS_SUBREAPER
/* If available and requested, register as a subreaper */
if (register_subreaper()) {

Loading…
Cancel
Save