|
|
9b65e1 |
From 6d1619802269db2f62c6b51b03a049c3d3a23a2c Mon Sep 17 00:00:00 2001
|
|
|
9b65e1 |
From: Frantisek Kluknavsky <fkluknav@redhat.com>
|
|
|
9b65e1 |
Date: Mon, 21 Aug 2017 16:44:19 +0200
|
|
|
9b65e1 |
Subject: [PATCH] rhel6 features
|
|
|
9b65e1 |
|
|
|
9b65e1 |
---
|
|
|
9b65e1 |
dumb-init.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++---------
|
|
|
9b65e1 |
1 file changed, 171 insertions(+), 28 deletions(-)
|
|
|
9b65e1 |
|
|
|
9b65e1 |
diff --git a/dumb-init.c b/dumb-init.c
|
|
|
9b65e1 |
index 65e69ef..3893b8c 100644
|
|
|
9b65e1 |
--- a/dumb-init.c
|
|
|
9b65e1 |
+++ b/dumb-init.c
|
|
|
9b65e1 |
@@ -19,6 +19,8 @@
|
|
|
9b65e1 |
#include <sys/types.h>
|
|
|
9b65e1 |
#include <sys/wait.h>
|
|
|
9b65e1 |
#include <unistd.h>
|
|
|
9b65e1 |
+#include <dirent.h>
|
|
|
9b65e1 |
+#include <ctype.h>
|
|
|
9b65e1 |
#include "VERSION.h"
|
|
|
9b65e1 |
|
|
|
9b65e1 |
#define PRINTERR(...) do { \
|
|
|
9b65e1 |
@@ -34,14 +36,18 @@
|
|
|
9b65e1 |
// Signals we care about are numbered from 1 to 31, inclusive.
|
|
|
9b65e1 |
// (32 and above are real-time signals.)
|
|
|
9b65e1 |
// TODO: this is likely not portable outside of Linux, or on strange architectures
|
|
|
9b65e1 |
-#define MAXSIG 31
|
|
|
9b65e1 |
+// EDIT: we actually care about some real-time signals as well
|
|
|
9b65e1 |
+// SIGRTMAX is not a constant, use 64
|
|
|
9b65e1 |
+#define MAXSIG 64
|
|
|
9b65e1 |
|
|
|
9b65e1 |
// Indices are one-indexed (signal 1 is at index 1). Index zero is unused.
|
|
|
9b65e1 |
int signal_rewrite[MAXSIG + 1] = {[0 ... MAXSIG] = -1};
|
|
|
9b65e1 |
+char *signal_action[MAXSIG + 1] = {[0 ... MAXSIG] = ""};
|
|
|
9b65e1 |
|
|
|
9b65e1 |
pid_t child_pid = -1;
|
|
|
9b65e1 |
char debug = 0;
|
|
|
9b65e1 |
char use_setsid = 1;
|
|
|
9b65e1 |
+static char survive_bereaving = 0;
|
|
|
9b65e1 |
|
|
|
9b65e1 |
int translate_signal(int signum) {
|
|
|
9b65e1 |
if (signum <= 0 || signum > MAXSIG) {
|
|
|
9b65e1 |
@@ -57,17 +63,75 @@ int translate_signal(int signum) {
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
|
|
|
9b65e1 |
+void do_action(int signum) {
|
|
|
9b65e1 |
+ DEBUG("Action for signal %d: running %s\n", signum, signal_action[signum]);
|
|
|
9b65e1 |
+ int child_pid = fork();
|
|
|
9b65e1 |
+ if (child_pid < 0) {
|
|
|
9b65e1 |
+ PRINTERR("Unable to fork. Exiting.\n");
|
|
|
9b65e1 |
+ exit(1);
|
|
|
9b65e1 |
+ } else if (child_pid == 0) {
|
|
|
9b65e1 |
+ /* child */
|
|
|
9b65e1 |
+ sigset_t all_signals;
|
|
|
9b65e1 |
+ sigfillset(&all_signals);
|
|
|
9b65e1 |
+ sigprocmask(SIG_UNBLOCK, &all_signals, NULL);
|
|
|
9b65e1 |
+ execlp("/bin/bash", "/bin/bash", "-c", signal_action[signum], (char *)NULL);
|
|
|
9b65e1 |
+
|
|
|
9b65e1 |
+ // if this point is reached, exec failed, so we should exit nonzero
|
|
|
9b65e1 |
+ PRINTERR("Could not exec %s: %s\n", signal_action[signum], strerror(errno));
|
|
|
9b65e1 |
+ exit(1);
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+}
|
|
|
9b65e1 |
+
|
|
|
9b65e1 |
void forward_signal(int signum) {
|
|
|
9b65e1 |
- signum = translate_signal(signum);
|
|
|
9b65e1 |
- if (signum != -1) {
|
|
|
9b65e1 |
- kill(use_setsid ? -child_pid : child_pid, signum);
|
|
|
9b65e1 |
- DEBUG("Forwarded signal %d to children.\n", signum);
|
|
|
9b65e1 |
+ int new_signum = translate_signal(signum);
|
|
|
9b65e1 |
+ if (new_signum == -2) {
|
|
|
9b65e1 |
+ do_action(signum);
|
|
|
9b65e1 |
+ } else if (new_signum != -1) {
|
|
|
9b65e1 |
+ kill(use_setsid ? -child_pid : child_pid, new_signum);
|
|
|
9b65e1 |
+ DEBUG("Forwarded signal %d to children.\n", new_signum);
|
|
|
9b65e1 |
} else {
|
|
|
9b65e1 |
- DEBUG("Not forwarding signal %d to children (ignored).\n", signum);
|
|
|
9b65e1 |
+ DEBUG("Not forwarding signal %d to children (ignored).\n", new_signum);
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
|
|
|
9b65e1 |
/*
|
|
|
9b65e1 |
+ * Read /proc and see if there are processes except init(PIDs)
|
|
|
9b65e1 |
+ */
|
|
|
9b65e1 |
+signed int process_count() {
|
|
|
9b65e1 |
+ DIR *dp;
|
|
|
9b65e1 |
+ struct dirent *ep;
|
|
|
9b65e1 |
+ char nonnumber;
|
|
|
9b65e1 |
+ signed int count = 0;
|
|
|
9b65e1 |
+
|
|
|
9b65e1 |
+ dp = opendir ("/proc");
|
|
|
9b65e1 |
+ if (dp != NULL)
|
|
|
9b65e1 |
+ {
|
|
|
9b65e1 |
+ while ((ep = readdir (dp)) != NULL) {
|
|
|
9b65e1 |
+ nonnumber = 0;
|
|
|
9b65e1 |
+ for (int i = 0; ep->d_name[i] != 0; ++i) {
|
|
|
9b65e1 |
+ if (!isdigit(ep->d_name[i])) {
|
|
|
9b65e1 |
+ nonnumber = 1;
|
|
|
9b65e1 |
+ break;
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+ if (!nonnumber) {
|
|
|
9b65e1 |
+ DEBUG("/proc/%s is a process\n", ep->d_name);
|
|
|
9b65e1 |
+ ++count;
|
|
|
9b65e1 |
+ if (count > 1) {
|
|
|
9b65e1 |
+ closedir(dp);
|
|
|
9b65e1 |
+ return 2; //2 is enough, do not count further
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+ closedir(dp);
|
|
|
9b65e1 |
+ } else {
|
|
|
9b65e1 |
+ PRINTERR("Could not open /proc.\n");
|
|
|
9b65e1 |
+ return -1;
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+ return count;
|
|
|
9b65e1 |
+}
|
|
|
9b65e1 |
+
|
|
|
9b65e1 |
+/*
|
|
|
9b65e1 |
* The dumb-init signal handler.
|
|
|
9b65e1 |
*
|
|
|
9b65e1 |
* The main job of this signal handler is to forward signals along to our child
|
|
|
9b65e1 |
@@ -88,6 +152,7 @@ void forward_signal(int signum) {
|
|
|
9b65e1 |
*
|
|
|
9b65e1 |
*/
|
|
|
9b65e1 |
void handle_signal(int signum) {
|
|
|
9b65e1 |
+ static char bereaved = 0;
|
|
|
9b65e1 |
DEBUG("Received signal %d.\n", signum);
|
|
|
9b65e1 |
if (signum == SIGCHLD) {
|
|
|
9b65e1 |
int status, exit_status;
|
|
|
9b65e1 |
@@ -103,11 +168,26 @@ void handle_signal(int signum) {
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
|
|
|
9b65e1 |
if (killed_pid == child_pid) {
|
|
|
9b65e1 |
- forward_signal(SIGTERM); // send SIGTERM to any remaining children
|
|
|
9b65e1 |
- DEBUG("Child exited with status %d. Goodbye.\n", exit_status);
|
|
|
9b65e1 |
- exit(exit_status);
|
|
|
9b65e1 |
+ bereaved = 1;
|
|
|
9b65e1 |
+ if (!survive_bereaving) {
|
|
|
9b65e1 |
+ forward_signal(SIGTERM); // send SIGTERM to any remaining children
|
|
|
9b65e1 |
+ DEBUG("Child exited with status %d. Goodbye.\n", exit_status);
|
|
|
9b65e1 |
+ exit(exit_status);
|
|
|
9b65e1 |
+ } else {
|
|
|
9b65e1 |
+ DEBUG("Child exited with status %d. Stay alive for your grandchildren.\n", exit_status);
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
+
|
|
|
9b65e1 |
+ if ((bereaved == 1) && survive_bereaving) {
|
|
|
9b65e1 |
+ signed int pc = process_count();
|
|
|
9b65e1 |
+ DEBUG("Process count: %d\n", pc);
|
|
|
9b65e1 |
+ if (pc <= 1) {
|
|
|
9b65e1 |
+ DEBUG("No process left, exitting.\n");
|
|
|
9b65e1 |
+ exit(0);
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+
|
|
|
9b65e1 |
} else {
|
|
|
9b65e1 |
forward_signal(signum);
|
|
|
9b65e1 |
if (signum == SIGTSTP || signum == SIGTTOU || signum == SIGTTIN) {
|
|
|
9b65e1 |
@@ -126,15 +206,21 @@ void print_help(char *argv[]) {
|
|
|
9b65e1 |
"It is designed to run as PID1 in minimal container environments.\n"
|
|
|
9b65e1 |
"\n"
|
|
|
9b65e1 |
"Optional arguments:\n"
|
|
|
9b65e1 |
- " -c, --single-child Run in single-child mode.\n"
|
|
|
9b65e1 |
- " In this mode, signals are only proxied to the\n"
|
|
|
9b65e1 |
- " direct child and not any of its descendants.\n"
|
|
|
9b65e1 |
- " -r, --rewrite s:r Rewrite received signal s to new signal r before proxying.\n"
|
|
|
9b65e1 |
- " To ignore (not proxy) a signal, rewrite it to 0.\n"
|
|
|
9b65e1 |
- " This option can be specified multiple times.\n"
|
|
|
9b65e1 |
- " -v, --verbose Print debugging information to stderr.\n"
|
|
|
9b65e1 |
- " -h, --help Print this help message and exit.\n"
|
|
|
9b65e1 |
- " -V, --version Print the current version and exit.\n"
|
|
|
9b65e1 |
+ " -c, --single-child Run in single-child mode.\n"
|
|
|
9b65e1 |
+ " In this mode, signals are only proxied to the\n"
|
|
|
9b65e1 |
+ " direct child and not any of its descendants.\n"
|
|
|
9b65e1 |
+ " -b, --survive-bereaving Do not quit when the direct child dies.\n"
|
|
|
9b65e1 |
+ " -r, --rewrite s:r Rewrite received signal s to new signal r before proxying.\n"
|
|
|
9b65e1 |
+ " To ignore (not proxy) a signal, rewrite it to 0.\n"
|
|
|
9b65e1 |
+ " To rewrite all signals, rewrite (otherwise nonexistent) signal 0.\n"
|
|
|
9b65e1 |
+ " (Useful to ignore all signals, use '--rewrite 0:0').\n"
|
|
|
9b65e1 |
+ " This option can be specified multiple times.\n"
|
|
|
9b65e1 |
+ " -a, --action s:exe Run exe after receiving sinal s.\n"
|
|
|
9b65e1 |
+ " For example, -a '2:echo hi there'.\n"
|
|
|
9b65e1 |
+ " This option can be specified multiple times.\n"
|
|
|
9b65e1 |
+ " -v, --verbose Print debugging information to stderr.\n"
|
|
|
9b65e1 |
+ " -h, --help Print this help message and exit.\n"
|
|
|
9b65e1 |
+ " -V, --version Print the current version and exit.\n"
|
|
|
9b65e1 |
"\n"
|
|
|
9b65e1 |
"Full help is available online at https://github.com/Yelp/dumb-init\n",
|
|
|
9b65e1 |
VERSION,
|
|
|
9b65e1 |
@@ -146,11 +232,24 @@ void print_rewrite_signum_help() {
|
|
|
9b65e1 |
fprintf(
|
|
|
9b65e1 |
stderr,
|
|
|
9b65e1 |
"Usage: -r option takes <signum>:<signum>, where <signum> "
|
|
|
9b65e1 |
+ "is between 0 and %d.\n"
|
|
|
9b65e1 |
+ "This option can be specified multiple times.\n"
|
|
|
9b65e1 |
+ "Use --help for full usage.\n",
|
|
|
9b65e1 |
+ MAXSIG
|
|
|
9b65e1 |
+ );
|
|
|
9b65e1 |
+ exit(1);
|
|
|
9b65e1 |
+}
|
|
|
9b65e1 |
+
|
|
|
9b65e1 |
+void print_action_help() {
|
|
|
9b65e1 |
+ fprintf(
|
|
|
9b65e1 |
+ stderr,
|
|
|
9b65e1 |
+ "Usage: -a option takes <signum>:<path>, where <signum> "
|
|
|
9b65e1 |
"is between 1 and %d.\n"
|
|
|
9b65e1 |
"This option can be specified multiple times.\n"
|
|
|
9b65e1 |
"Use --help for full usage.\n",
|
|
|
9b65e1 |
MAXSIG
|
|
|
9b65e1 |
);
|
|
|
9b65e1 |
+
|
|
|
9b65e1 |
exit(1);
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
|
|
|
9b65e1 |
@@ -158,15 +257,36 @@ void parse_rewrite_signum(char *arg) {
|
|
|
9b65e1 |
int signum, replacement;
|
|
|
9b65e1 |
if (
|
|
|
9b65e1 |
sscanf(arg, "%d:%d", &signum, &replacement) == 2 &&
|
|
|
9b65e1 |
- (signum >= 1 && signum <= MAXSIG) &&
|
|
|
9b65e1 |
+ (signum >= 0 && signum <= MAXSIG) &&
|
|
|
9b65e1 |
(replacement >= 0 && replacement <= MAXSIG)
|
|
|
9b65e1 |
) {
|
|
|
9b65e1 |
- signal_rewrite[signum] = replacement;
|
|
|
9b65e1 |
+ if (signum == 0) {
|
|
|
9b65e1 |
+ for (int i = 0; i <= MAXSIG; ++i) {
|
|
|
9b65e1 |
+ signal_rewrite[i] = replacement;
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+ } else {
|
|
|
9b65e1 |
+ signal_rewrite[signum] = replacement;
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
} else {
|
|
|
9b65e1 |
print_rewrite_signum_help();
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
|
|
|
9b65e1 |
+void parse_action(char *arg) {
|
|
|
9b65e1 |
+ int signum;
|
|
|
9b65e1 |
+ int status;
|
|
|
9b65e1 |
+ int position;
|
|
|
9b65e1 |
+ if (
|
|
|
9b65e1 |
+ (status = sscanf(arg, "%d:%n", &signum, &position)) == 1 &&
|
|
|
9b65e1 |
+ (signum >= 0 && signum <= MAXSIG)
|
|
|
9b65e1 |
+ ) {
|
|
|
9b65e1 |
+ DEBUG("signal action: %d, position %d\n", signum, position);
|
|
|
9b65e1 |
+ signal_action[signum] = &(arg[position]);
|
|
|
9b65e1 |
+ signal_rewrite[signum] = -2;
|
|
|
9b65e1 |
+ } else {
|
|
|
9b65e1 |
+ print_action_help();
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+}
|
|
|
9b65e1 |
void set_rewrite_to_sigstop_if_not_defined(int signum) {
|
|
|
9b65e1 |
if (signal_rewrite[signum] == -1)
|
|
|
9b65e1 |
signal_rewrite[signum] = SIGSTOP;
|
|
|
9b65e1 |
@@ -175,14 +295,16 @@ void set_rewrite_to_sigstop_if_not_defined(int signum) {
|
|
|
9b65e1 |
char **parse_command(int argc, char *argv[]) {
|
|
|
9b65e1 |
int opt;
|
|
|
9b65e1 |
struct option long_options[] = {
|
|
|
9b65e1 |
- {"help", no_argument, NULL, 'h'},
|
|
|
9b65e1 |
- {"single-child", no_argument, NULL, 'c'},
|
|
|
9b65e1 |
- {"rewrite", required_argument, NULL, 'r'},
|
|
|
9b65e1 |
- {"verbose", no_argument, NULL, 'v'},
|
|
|
9b65e1 |
- {"version", no_argument, NULL, 'V'},
|
|
|
9b65e1 |
+ {"help", no_argument, NULL, 'h'},
|
|
|
9b65e1 |
+ {"single-child", no_argument, NULL, 'c'},
|
|
|
9b65e1 |
+ {"rewrite", required_argument, NULL, 'r'},
|
|
|
9b65e1 |
+ {"verbose", no_argument, NULL, 'v'},
|
|
|
9b65e1 |
+ {"version", no_argument, NULL, 'V'},
|
|
|
9b65e1 |
+ {"survive-bereaving",no_argument, NULL, 'b'},
|
|
|
9b65e1 |
+ {"action", required_argument, NULL, 'a'},
|
|
|
9b65e1 |
{NULL, 0, NULL, 0},
|
|
|
9b65e1 |
};
|
|
|
9b65e1 |
- while ((opt = getopt_long(argc, argv, "+hvVcr:", long_options, NULL)) != -1) {
|
|
|
9b65e1 |
+ while ((opt = getopt_long(argc, argv, "+hvVcbr:a:", long_options, NULL)) != -1) {
|
|
|
9b65e1 |
switch (opt) {
|
|
|
9b65e1 |
case 'h':
|
|
|
9b65e1 |
print_help(argv);
|
|
|
9b65e1 |
@@ -199,7 +321,14 @@ char **parse_command(int argc, char *argv[]) {
|
|
|
9b65e1 |
case 'r':
|
|
|
9b65e1 |
parse_rewrite_signum(optarg);
|
|
|
9b65e1 |
break;
|
|
|
9b65e1 |
+ case 'a':
|
|
|
9b65e1 |
+ parse_action(optarg);
|
|
|
9b65e1 |
+ break;
|
|
|
9b65e1 |
+ case 'b':
|
|
|
9b65e1 |
+ survive_bereaving = 1;
|
|
|
9b65e1 |
+ break;
|
|
|
9b65e1 |
default:
|
|
|
9b65e1 |
+ PRINTERR("Error while parsing arguments.\n");
|
|
|
9b65e1 |
exit(1);
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
@@ -295,8 +424,22 @@ int main(int argc, char *argv[]) {
|
|
|
9b65e1 |
/* parent */
|
|
|
9b65e1 |
DEBUG("Child spawned with PID %d.\n", child_pid);
|
|
|
9b65e1 |
for (;;) {
|
|
|
9b65e1 |
- int signum;
|
|
|
9b65e1 |
- sigwait(&all_signals, &signum);
|
|
|
9b65e1 |
+ struct timespec timeout = {1, 0};
|
|
|
9b65e1 |
+ int signum = sigtimedwait(&all_signals, NULL, &timeout);
|
|
|
9b65e1 |
+ if (signum == -1) {
|
|
|
9b65e1 |
+ switch (errno) {
|
|
|
9b65e1 |
+ case EINVAL:
|
|
|
9b65e1 |
+ PRINTERR("Invalid timeout, report this as a bug!\n");
|
|
|
9b65e1 |
+ exit(1);
|
|
|
9b65e1 |
+ case EINTR:
|
|
|
9b65e1 |
+ PRINTERR("Wait interrupted by a signal. This should never happen. Report this as a bug!\n");
|
|
|
9b65e1 |
+ exit(1);
|
|
|
9b65e1 |
+ case EAGAIN:
|
|
|
9b65e1 |
+ //pretend timeout to be SIGCHLD, check if we want to continue running
|
|
|
9b65e1 |
+ signum = SIGCHLD;
|
|
|
9b65e1 |
+ DEBUG("Heartbeat...\n");
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
+ }
|
|
|
9b65e1 |
handle_signal(signum);
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
}
|
|
|
9b65e1 |
--
|
|
|
9b65e1 |
2.13.3
|
|
|
9b65e1 |
|