2015-07-30 02:03:55 +03:00
|
|
|
/*
|
|
|
|
* dumb-init is a simple wrapper program designed to run as PID 1 and pass
|
|
|
|
* signals to its children.
|
|
|
|
*
|
|
|
|
* Usage:
|
|
|
|
* ./dumb-init python -c 'while True: pass'
|
|
|
|
*
|
2015-08-26 19:05:54 +03:00
|
|
|
* To get debug output on stderr, run with DUMB_INIT_DEBUG=1
|
2015-07-30 02:03:55 +03:00
|
|
|
*/
|
|
|
|
|
2015-09-05 01:19:17 +03:00
|
|
|
#include <assert.h>
|
2015-08-26 19:05:54 +03:00
|
|
|
#include <errno.h>
|
2015-07-30 02:03:55 +03:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2015-08-26 19:05:54 +03:00
|
|
|
#define DEBUG(...) do { \
|
|
|
|
if (debug) { \
|
|
|
|
fprintf(stderr, __VA_ARGS__); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2015-09-05 01:19:17 +03:00
|
|
|
pid_t child_pid = -1;
|
2015-07-30 02:03:55 +03:00
|
|
|
char debug = 0;
|
2015-09-11 00:05:05 +03:00
|
|
|
char use_setsid = 1;
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-09-17 05:42:09 +03:00
|
|
|
void forward_signal(int signum) {
|
2015-09-05 01:19:17 +03:00
|
|
|
if (child_pid > 0) {
|
|
|
|
kill(use_setsid ? -child_pid : child_pid, signum);
|
2015-09-17 05:42:09 +03:00
|
|
|
DEBUG("Forwarded signal %d to child.\n", signum);
|
2015-08-26 19:05:54 +03:00
|
|
|
} else {
|
2015-09-17 05:42:09 +03:00
|
|
|
DEBUG("Didn't forward signal %d, no child exists yet.\n", signum);
|
2015-08-10 19:32:56 +03:00
|
|
|
}
|
2015-07-30 02:03:55 +03:00
|
|
|
}
|
|
|
|
|
2015-09-17 05:42:09 +03:00
|
|
|
void handle_signal(int signum) {
|
|
|
|
DEBUG("Received signal %d.\n", signum);
|
|
|
|
forward_signal(signum);
|
2015-09-05 01:19:17 +03:00
|
|
|
}
|
|
|
|
|
2015-08-07 00:24:27 +03:00
|
|
|
void print_help(char *argv[]) {
|
2015-08-10 19:32:56 +03:00
|
|
|
fprintf(stderr,
|
|
|
|
"Usage: %s COMMAND [[ARG] ...]\n"
|
|
|
|
"\n"
|
2015-09-04 20:26:47 +03:00
|
|
|
"dumb-init is a simple process designed to run as PID 1 inside Docker\n"
|
|
|
|
"containers and proxy signals to child processes.\n"
|
|
|
|
"\n"
|
2015-08-10 19:32:56 +03:00
|
|
|
"Docker runs your processes as PID1. The kernel doesn't apply default signal\n"
|
|
|
|
"handling to PID1 processes, so if your process doesn't register a custom\n"
|
|
|
|
"signal handler, signals like TERM will just bounce off your process.\n"
|
|
|
|
"\n"
|
|
|
|
"This can result in cases where sending signals to a `docker run` process\n"
|
|
|
|
"results in the run process exiting, but the container continuing in the\n"
|
|
|
|
"background.\n"
|
|
|
|
"\n"
|
|
|
|
"A workaround is to wrap your script in this proxy, which runs as PID1. Your\n"
|
|
|
|
"process then runs as some other PID, and the kernel won't treat the signals\n"
|
|
|
|
"that are proxied to them specially.\n"
|
|
|
|
"\n"
|
|
|
|
"The proxy dies when your process dies, so it must not double-fork or do other\n"
|
|
|
|
"weird things (this is basically a requirement for doing things sanely in\n"
|
2015-08-26 19:05:54 +03:00
|
|
|
"Docker anyway).\n"
|
|
|
|
"\n"
|
2015-09-11 00:05:05 +03:00
|
|
|
"By default, dumb-init starts a process group (and session, see: man 2 setsid)\n"
|
|
|
|
"and signals all processes in it. This is usually useful behavior, but if for\n"
|
|
|
|
"some reason you wish to disable it, run with DUMB_INIT_SETSID=0.\n",
|
2015-08-10 19:32:56 +03:00
|
|
|
argv[0]
|
|
|
|
);
|
2015-08-07 00:24:27 +03:00
|
|
|
}
|
|
|
|
|
2015-07-30 02:03:55 +03:00
|
|
|
int main(int argc, char *argv[]) {
|
2015-09-05 01:19:17 +03:00
|
|
|
int signum;
|
2015-09-11 00:05:05 +03:00
|
|
|
char *debug_env, *setsid_env;
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-08-10 19:32:56 +03:00
|
|
|
if (argc < 2) {
|
|
|
|
print_help(argv);
|
|
|
|
return 1;
|
|
|
|
}
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-08-10 19:32:56 +03:00
|
|
|
debug_env = getenv("DUMB_INIT_DEBUG");
|
2015-08-26 19:05:54 +03:00
|
|
|
if (debug_env && strcmp(debug_env, "1") == 0) {
|
2015-08-10 19:32:56 +03:00
|
|
|
debug = 1;
|
2015-08-26 19:05:54 +03:00
|
|
|
DEBUG("Running in debug mode.\n");
|
|
|
|
}
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-09-11 00:05:05 +03:00
|
|
|
setsid_env = getenv("DUMB_INIT_SETSID");
|
|
|
|
if (setsid_env && strcmp(setsid_env, "0") == 0) {
|
|
|
|
use_setsid = 0;
|
|
|
|
DEBUG("Not running in setsid mode.\n");
|
2015-08-26 19:05:54 +03:00
|
|
|
}
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-08-10 19:32:56 +03:00
|
|
|
/* register signal handlers */
|
|
|
|
for (signum = 1; signum < 32; signum++) {
|
|
|
|
if (signum == SIGKILL || signum == SIGSTOP || signum == SIGCHLD)
|
|
|
|
continue;
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-09-17 05:42:09 +03:00
|
|
|
if (signal(signum, handle_signal) == SIG_ERR) {
|
2015-08-10 19:32:56 +03:00
|
|
|
fprintf(stderr, "Error: Couldn't register signal handler for signal `%d`. Exiting.\n", signum);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-08-10 19:32:56 +03:00
|
|
|
/* launch our process */
|
2015-09-05 01:19:17 +03:00
|
|
|
child_pid = fork();
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-09-05 01:19:17 +03:00
|
|
|
if (child_pid < 0) {
|
2015-08-10 19:32:56 +03:00
|
|
|
fprintf(stderr, "Unable to fork. Exiting.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-09-05 01:19:17 +03:00
|
|
|
if (child_pid == 0) {
|
2015-09-11 00:05:05 +03:00
|
|
|
if (use_setsid) {
|
2015-09-10 01:05:34 +03:00
|
|
|
pid_t result = setsid();
|
|
|
|
if (result == -1) {
|
2015-08-26 19:05:54 +03:00
|
|
|
fprintf(
|
|
|
|
stderr,
|
2015-09-11 00:05:05 +03:00
|
|
|
"Unable to setsid (errno=%d %s). Exiting.\n",
|
2015-08-26 19:05:54 +03:00
|
|
|
errno,
|
|
|
|
strerror(errno)
|
|
|
|
);
|
|
|
|
exit(1);
|
|
|
|
}
|
2015-09-11 00:05:05 +03:00
|
|
|
DEBUG("setsid complete.\n");
|
2015-08-26 19:05:54 +03:00
|
|
|
}
|
|
|
|
|
2015-08-10 19:32:56 +03:00
|
|
|
execvp(argv[1], &argv[1]);
|
|
|
|
} else {
|
2015-09-17 05:42:09 +03:00
|
|
|
pid_t killed_pid;
|
|
|
|
int exit_status, status;
|
|
|
|
|
2015-09-05 01:19:17 +03:00
|
|
|
DEBUG("Child spawned with PID %d.\n", child_pid);
|
2015-09-17 05:42:09 +03:00
|
|
|
|
|
|
|
while ((killed_pid = waitpid(-1, &status, 0))) {
|
|
|
|
exit_status = WEXITSTATUS(status);
|
|
|
|
DEBUG("A child with PID %d exited with exit status %d.\n", killed_pid, exit_status);
|
|
|
|
|
|
|
|
if (killed_pid == child_pid) {
|
|
|
|
// send SIGTERM to any remaining children
|
|
|
|
forward_signal(SIGTERM);
|
|
|
|
|
|
|
|
DEBUG("Child exited with status %d. Goodbye.\n", exit_status);
|
|
|
|
exit(exit_status);
|
|
|
|
}
|
2015-09-05 01:19:17 +03:00
|
|
|
}
|
2015-08-10 19:32:56 +03:00
|
|
|
}
|
2015-07-30 02:03:55 +03:00
|
|
|
|
2015-08-10 19:32:56 +03:00
|
|
|
return 0;
|
2015-07-30 02:03:55 +03:00
|
|
|
}
|