From 7527a65f2216fe01ea4543bc40ac4223ff711418 Mon Sep 17 00:00:00 2001 From: Olivier Doucet Date: Fri, 26 Mar 2021 17:52:31 +0100 Subject: [PATCH 1/4] initial patch from fkluknav --- dumb-init.c | 95 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 17 deletions(-) diff --git a/dumb-init.c b/dumb-init.c index a97ab41..d2ac9cf 100644 --- a/dumb-init.c +++ b/dumb-init.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "VERSION.h" #define PRINTERR(...) do { \ @@ -45,6 +47,7 @@ char signal_temporary_ignores[MAXSIG + 1] = {[0 ... MAXSIG] = 0}; pid_t child_pid = -1; char debug = 0; char use_setsid = 1; +static char survive_bereaving = 0; int translate_signal(int signum) { if (signum <= 0 || signum > MAXSIG) { @@ -70,6 +73,43 @@ void forward_signal(int signum) { } } +/* + * Read /proc and see if there are processes except init(PIDs) + */ +signed int process_count() { + DIR *dp; + struct dirent *ep; + char nonnumber; + signed int count = 0; + + dp = opendir ("/proc"); + if (dp != NULL) + { + while ((ep = readdir (dp)) != NULL) { + nonnumber = 0; + for (int i = 0; ep->d_name[i] != 0; ++i) { + if (!isdigit(ep->d_name[i])) { + nonnumber = 1; + break; + } + } + if (!nonnumber) { + DEBUG("/proc/%s is a process\n", ep->d_name); + ++count; + if (count > 1) { + closedir(dp); + return 2; //2 is enough, do not count further + } + } + } + closedir(dp); + } else { + PRINTERR("Could not open /proc.\n"); + return -1; + } + return count; +} + /* * The dumb-init signal handler. * @@ -91,6 +131,7 @@ void forward_signal(int signum) { * */ void handle_signal(int signum) { + static char bereaved = 0; DEBUG("Received signal %d.\n", signum); if (signal_temporary_ignores[signum] == 1) { @@ -110,11 +151,26 @@ void handle_signal(int signum) { } if (killed_pid == child_pid) { - forward_signal(SIGTERM); // send SIGTERM to any remaining children - DEBUG("Child exited with status %d. Goodbye.\n", exit_status); + bereaved = 1; + if (!survive_bereaving) { + forward_signal(SIGTERM); // send SIGTERM to any remaining children + DEBUG("Child exited with status %d. Goodbye.\n", exit_status); + exit(exit_status); + } else { + DEBUG("Child exited with status %d. Stay alive for your grandchildren.\n", exit_status); + } + } + } + + if ((bereaved == 1) && survive_bereaving) { + signed int pc = process_count(); + DEBUG("Process count: %d\n", pc); + if (pc <= 1) { + DEBUG("No process left, exitting.\n"); exit(exit_status); } } + } else { forward_signal(signum); if (signum == SIGTSTP || signum == SIGTTOU || signum == SIGTTIN) { @@ -133,15 +189,16 @@ void print_help(char *argv[]) { "It is designed to run as PID1 in minimal container environments.\n" "\n" "Optional arguments:\n" - " -c, --single-child Run in single-child mode.\n" - " In this mode, signals are only proxied to the\n" - " direct child and not any of its descendants.\n" - " -r, --rewrite s:r Rewrite received signal s to new signal r before proxying.\n" - " To ignore (not proxy) a signal, rewrite it to 0.\n" - " This option can be specified multiple times.\n" - " -v, --verbose Print debugging information to stderr.\n" - " -h, --help Print this help message and exit.\n" - " -V, --version Print the current version and exit.\n" + " -c, --single-child Run in single-child mode.\n" + " In this mode, signals are only proxied to the\n" + " direct child and not any of its descendants.\n" + " -b, --survive-bereaving Do not quit when the direct child dies.\n" + " -r, --rewrite s:r Rewrite received signal s to new signal r before proxying.\n" + " To ignore (not proxy) a signal, rewrite it to 0.\n" + " This option can be specified multiple times.\n" + " -v, --verbose Print debugging information to stderr.\n" + " -h, --help Print this help message and exit.\n" + " -V, --version Print the current version and exit.\n" "\n" "Full help is available online at https://github.com/Yelp/dumb-init\n", VERSION_len, VERSION, @@ -183,14 +240,15 @@ void set_rewrite_to_sigstop_if_not_defined(int signum) { char **parse_command(int argc, char *argv[]) { int opt; struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, - {"single-child", no_argument, NULL, 'c'}, - {"rewrite", required_argument, NULL, 'r'}, - {"verbose", no_argument, NULL, 'v'}, - {"version", no_argument, NULL, 'V'}, + {"help", no_argument, NULL, 'h'}, + {"single-child", no_argument, NULL, 'c'}, + {"rewrite", required_argument, NULL, 'r'}, + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"survive-bereaving",no_argument, NULL, 'b'}, {NULL, 0, NULL, 0}, }; - while ((opt = getopt_long(argc, argv, "+hvVcr:", long_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "+hvVcbr:", long_options, NULL)) != -1) { switch (opt) { case 'h': print_help(argv); @@ -207,6 +265,9 @@ char **parse_command(int argc, char *argv[]) { case 'r': parse_rewrite_signum(optarg); break; + case 'b': + survive_bereaving = 1; + break; default: exit(1); } From b10b7368dc7d04b68d7dc682a969993288af0eaa Mon Sep 17 00:00:00 2001 From: Olivier Doucet Date: Fri, 26 Mar 2021 17:58:27 +0100 Subject: [PATCH 2/4] modifications requested in merge request --- dumb-init.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/dumb-init.c b/dumb-init.c index d2ac9cf..5e442ad 100644 --- a/dumb-init.c +++ b/dumb-init.c @@ -76,18 +76,17 @@ void forward_signal(int signum) { /* * Read /proc and see if there are processes except init(PIDs) */ -signed int process_count() { +signed int is_only_process_running() { DIR *dp; struct dirent *ep; char nonnumber; signed int count = 0; dp = opendir ("/proc"); - if (dp != NULL) - { + if (dp != NULL) { while ((ep = readdir (dp)) != NULL) { nonnumber = 0; - for (int i = 0; ep->d_name[i] != 0; ++i) { + for (int i = 0; ep->d_name[i] != '\0'; ++i) { if (!isdigit(ep->d_name[i])) { nonnumber = 1; break; @@ -138,7 +137,7 @@ void handle_signal(int signum) { DEBUG("Ignoring tty hand-off signal %d.\n", signum); signal_temporary_ignores[signum] = 0; } else if (signum == SIGCHLD) { - int status, exit_status; + int status, exit_status = 0; pid_t killed_pid; while ((killed_pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status)) { @@ -163,10 +162,10 @@ void handle_signal(int signum) { } if ((bereaved == 1) && survive_bereaving) { - signed int pc = process_count(); + signed int pc = is_only_process_running(); DEBUG("Process count: %d\n", pc); if (pc <= 1) { - DEBUG("No process left, exitting.\n"); + DEBUG("No process left, exiting.\n"); exit(exit_status); } } @@ -193,6 +192,9 @@ void print_help(char *argv[]) { " In this mode, signals are only proxied to the\n" " direct child and not any of its descendants.\n" " -b, --survive-bereaving Do not quit when the direct child dies.\n" + " In this mode, dumb-init will quit when there are no\n" + " other processes running on the host.\n" + " This mode requires dumb-init to be running as PID 1.\n" " -r, --rewrite s:r Rewrite received signal s to new signal r before proxying.\n" " To ignore (not proxy) a signal, rewrite it to 0.\n" " This option can be specified multiple times.\n" @@ -283,6 +285,11 @@ char **parse_command(int argc, char *argv[]) { exit(1); } + if (survive_bereaving && getpid() != 1) { + PRINTERR("--survive-bereaving is only valid when dumb-init is running as PID 1.\n"); + exit(1); + } + char *debug_env = getenv("DUMB_INIT_DEBUG"); if (debug_env && strcmp(debug_env, "1") == 0) { debug = 1; From fdb7d78783d3f861c1e4f34b9f25c62bf9e43b5f Mon Sep 17 00:00:00 2001 From: Olivier Doucet Date: Mon, 29 Mar 2021 12:21:19 +0200 Subject: [PATCH 3/4] fix unit tests --- tests/cli_test.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/cli_test.py b/tests/cli_test.py index 27d7bf2..f2b7743 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -51,15 +51,16 @@ def test_help_message(flag, current_version): b'It is designed to run as PID1 in minimal container environments.\n' b'\n' b'Optional arguments:\n' - b' -c, --single-child Run in single-child mode.\n' - b' In this mode, signals are only proxied to the\n' - b' direct child and not any of its descendants.\n' - b' -r, --rewrite s:r Rewrite received signal s to new signal r before proxying.\n' - b' To ignore (not proxy) a signal, rewrite it to 0.\n' - b' This option can be specified multiple times.\n' - b' -v, --verbose Print debugging information to stderr.\n' - b' -h, --help Print this help message and exit.\n' - b' -V, --version Print the current version and exit.\n' + b' -c, --single-child Run in single-child mode.\n' + b' In this mode, signals are only proxied to the\n' + b' direct child and not any of its descendants.\n' + b' -b, --survive-bereaving Do not quit when the direct child dies.\n' + b' -r, --rewrite s:r Rewrite received signal s to new signal r before proxying.\n' + b' To ignore (not proxy) a signal, rewrite it to 0.\n' + b' This option can be specified multiple times.\n' + b' -v, --verbose Print debugging information to stderr.\n' + b' -h, --help Print this help message and exit.\n' + b' -V, --version Print the current version and exit.\n' b'\n' b'Full help is available online at https://github.com/Yelp/dumb-init\n' ) From 2545cb619d60edb4f7e4c95c790085b02814fc7d Mon Sep 17 00:00:00 2001 From: Olivier Doucet Date: Mon, 29 Mar 2021 14:16:43 +0200 Subject: [PATCH 4/4] fix test bis --- tests/cli_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/cli_test.py b/tests/cli_test.py index f2b7743..602c269 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -55,6 +55,9 @@ def test_help_message(flag, current_version): b' In this mode, signals are only proxied to the\n' b' direct child and not any of its descendants.\n' b' -b, --survive-bereaving Do not quit when the direct child dies.\n' + b' In this mode, dumb-init will quit when there are no\n' + b' other processes running on the host.\n' + b' This mode requires dumb-init to be running as PID 1.\n' b' -r, --rewrite s:r Rewrite received signal s to new signal r before proxying.\n' b' To ignore (not proxy) a signal, rewrite it to 0.\n' b' This option can be specified multiple times.\n'