mirror of https://github.com/krallin/tini.git
Allow users to enable sub-reaping
This allows users that don't control PID 1 in their container to still use Tini to reap zombies.
This commit is contained in:
parent
98311caf52
commit
bcb8a4b870
|
@ -14,6 +14,11 @@ export DIST_DIR="$(readlink -f "${DIST_DIR}")"
|
|||
export BUILD_DIR="$(readlink -f "${BUILD_DIR}")"
|
||||
|
||||
|
||||
# Our build platform doesn't have those newer Linux flags, but we want Tini to have subreaper support
|
||||
# We also use those in our tests
|
||||
export CFLAGS="-DPR_SET_CHILD_SUBREAPER=36 -DPR_GET_CHILD_SUBREAPER=37"
|
||||
|
||||
|
||||
# Ensure Python output is not buffered (to make tests output clearer)
|
||||
export PYTHONUNBUFFERED=1
|
||||
|
||||
|
@ -65,20 +70,7 @@ virtualenv "${VENV}"
|
|||
export PATH="${VENV}/bin:${PATH}"
|
||||
|
||||
# Install test dependencies
|
||||
|
||||
# We need a patched version because Travis only gives us Ubuntu Precise
|
||||
# (whose Linux headers don't include PR_SET_CHILD_SUBREAPER), but actually
|
||||
# runs a newer Linux Kernel (because we're actually in Docker) that has the
|
||||
# PR_SET_CHILD_SUBREAPER prctl call.
|
||||
pushd /tmp
|
||||
pip install python-prctl==1.6.1 --download="."
|
||||
tar -xvf /tmp/python-prctl-1.6.1.tar.gz
|
||||
cd python-prctl-1.6.1
|
||||
patch -p1 < "${SOURCE_DIR}/test/0001-Add-PR_SET_CHILD_SUBREAPER.patch"
|
||||
python setup.py install
|
||||
popd
|
||||
|
||||
pip install psutil
|
||||
pip install psutil python-prctl
|
||||
|
||||
# Run tests
|
||||
python "${SOURCE_DIR}/test/run_inner_tests.py"
|
||||
|
|
100
src/tini.c
100
src/tini.c
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
@ -24,6 +25,18 @@
|
|||
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
|
||||
#ifdef PR_SET_CHILD_SUBREAPER
|
||||
#define HAS_SUBREAPER 1
|
||||
#define OPT_STRING "hsv"
|
||||
#else
|
||||
#define HAS_SUBREAPER 0
|
||||
#define OPT_STRING "hv"
|
||||
#endif
|
||||
|
||||
|
||||
#if HAS_SUBREAPER
|
||||
static int subreaper = 0;
|
||||
#endif
|
||||
static int verbosity = 0;
|
||||
static struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
|
||||
|
||||
|
@ -58,6 +71,9 @@ void print_usage(char* const name, FILE* const file) {
|
|||
fprintf(file, "Usage: %s [OPTIONS] PROGRAM -- [ARGS]\n\n", basename(name));
|
||||
fprintf(file, "Execute a program under the supervision of a valid init process (%s)\n\n", basename(name));
|
||||
fprintf(file, " -h: Show this help message and exit.\n");
|
||||
#if HAS_SUBREAPER
|
||||
fprintf(file, " -s: Register as a process subreaper (requires Linux >= 3.4).\n");
|
||||
#endif
|
||||
fprintf(file, " -v: Generate more verbose output. Repeat up to 4 times.\n");
|
||||
fprintf(file, "\n");
|
||||
}
|
||||
|
@ -67,13 +83,18 @@ int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[
|
|||
char* name = argv[0];
|
||||
|
||||
int c;
|
||||
while ((c = getopt (argc, argv, "hv")) != -1) {
|
||||
while ((c = getopt(argc, argv, OPT_STRING)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
/* TODO - Shouldn't cause exit with -1 ..*/
|
||||
print_usage(name, stdout);
|
||||
*parse_fail_exitcode_ptr = 0;
|
||||
return 1;
|
||||
#if HAS_SUBREAPER
|
||||
case 's':
|
||||
subreaper++;
|
||||
break;
|
||||
#endif
|
||||
case 'v':
|
||||
verbosity++;
|
||||
break;
|
||||
|
@ -107,6 +128,50 @@ int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if HAS_SUBREAPER
|
||||
int register_subreaper () {
|
||||
if (subreaper > 0) {
|
||||
if (prctl(PR_SET_CHILD_SUBREAPER)) {
|
||||
if (errno == EINVAL) {
|
||||
PRINT_FATAL("PR_SET_CHILD_SUBREAPER is unavailable on this platform. Are you using Linux >= 3.4?")
|
||||
} else {
|
||||
PRINT_FATAL("Failed to register as child subreaper: %s", strerror(errno))
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
PRINT_TRACE("Registered as child subreaper");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void reaper_check () {
|
||||
/* Check that we can properly reap zombies */
|
||||
#if HAS_SUBREAPER
|
||||
int bit = 0;
|
||||
#endif
|
||||
|
||||
if (getpid() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if HAS_SUBREAPER
|
||||
if (prctl(PR_GET_CHILD_SUBREAPER, &bit)) {
|
||||
PRINT_DEBUG("Failed to read child subreaper attribute: %s", strerror(errno));
|
||||
} else if (bit == 1) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
PRINT_WARNING("Tini is not running as PID 1 and isn't registered as a child subreaper.\n\
|
||||
Zombie processes will not be re-parented to Tini, so zombie reaping won't work.\n\
|
||||
Use -s or run Tini as PID 1 to fix the problem.");
|
||||
}
|
||||
|
||||
|
||||
int prepare_sigmask(sigset_t* const parent_sigset_ptr, sigset_t* const child_sigset_ptr) {
|
||||
/* Prepare signals to block; make sure we don't block program error signals. */
|
||||
if (sigfillset(parent_sigset_ptr)) {
|
||||
|
@ -182,14 +247,14 @@ int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
|
|||
|
||||
case -1:
|
||||
if (errno == ECHILD) {
|
||||
PRINT_TRACE("No child to wait.");
|
||||
PRINT_TRACE("No child to wait");
|
||||
break;
|
||||
}
|
||||
PRINT_FATAL("Error while waiting for pids: '%s'", strerror(errno));
|
||||
return 1;
|
||||
|
||||
case 0:
|
||||
PRINT_TRACE("No child to reap.");
|
||||
PRINT_TRACE("No child to reap");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -209,7 +274,7 @@ int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
|
|||
PRINT_INFO("Main child exited with signal (with signal '%s')", strsignal(WTERMSIG(current_status)));
|
||||
*child_exitcode_ptr = 128 + WTERMSIG(current_status);
|
||||
} else {
|
||||
PRINT_FATAL("Main child exited for unknown reason!");
|
||||
PRINT_FATAL("Main child exited for unknown reason");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -233,13 +298,6 @@ int main(int argc, char *argv[]) {
|
|||
int child_exitcode = -1; // This isn't a valid exitcode, and lets us tell whether the child has exited.
|
||||
int parse_exitcode = 1; // By default, we exit with 1 if parsing fails.
|
||||
|
||||
/* Prepare sigmask */
|
||||
sigset_t parent_sigset;
|
||||
sigset_t child_sigset;
|
||||
if (prepare_sigmask(&parent_sigset, &child_sigset)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Parse command line arguments */
|
||||
char* (*child_args_ptr)[];
|
||||
int parse_args_ret = parse_args(argc, argv, &child_args_ptr, &parse_exitcode);
|
||||
|
@ -247,6 +305,24 @@ int main(int argc, char *argv[]) {
|
|||
return parse_exitcode;
|
||||
}
|
||||
|
||||
/* Prepare sigmask */
|
||||
sigset_t parent_sigset;
|
||||
sigset_t child_sigset;
|
||||
if (prepare_sigmask(&parent_sigset, &child_sigset)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if HAS_SUBREAPER
|
||||
/* If available and requested, register as a subreaper */
|
||||
if (register_subreaper()) {
|
||||
return 1;
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Are we going to reap zombies properly? If not, warn. */
|
||||
reaper_check();
|
||||
|
||||
/* Go on */
|
||||
if (spawn(&child_sigset, *child_args_ptr, &child_pid)) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -264,7 +340,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
if (child_exitcode != -1) {
|
||||
PRINT_TRACE("Child has exited. Exiting");
|
||||
PRINT_TRACE("Exiting: child has exited");
|
||||
return child_exitcode;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
From b8c6ccd4575837e3901bbdee7b219ef951dc2065 Mon Sep 17 00:00:00 2001
|
||||
From: Thomas Orozco <thomas@orozco.fr>
|
||||
Date: Sun, 28 Jun 2015 15:25:37 +0200
|
||||
Subject: [PATCH] Add PR_SET_CHILD_SUBREAPER
|
||||
|
||||
---
|
||||
_prctlmodule.c | 12 ++++++++++++
|
||||
1 file changed, 12 insertions(+)
|
||||
|
||||
diff --git a/_prctlmodule.c b/_prctlmodule.c
|
||||
index 14121c3..19ad141 100644
|
||||
--- a/_prctlmodule.c
|
||||
+++ b/_prctlmodule.c
|
||||
@@ -15,6 +15,18 @@
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/signal.h>
|
||||
|
||||
+/* Our builds run in a Docker environment that has those, but they are
|
||||
+ * not in the kernel headers. Add them.
|
||||
+ */
|
||||
+
|
||||
+#ifndef PR_SET_CHILD_SUBREAPER
|
||||
+#define PR_SET_CHILD_SUBREAPER 36
|
||||
+#endif
|
||||
+
|
||||
+#ifndef PR_GET_CHILD_SUBREAPER
|
||||
+#define PR_GET_CHILD_SUBREAPER 37
|
||||
+#endif
|
||||
+
|
||||
/* New in 2.6.32, but named and implemented inconsistently. The linux
|
||||
* implementation has two ways of setting the policy to the default, and thus
|
||||
* needs an extra argument. We ignore the first argument and always call
|
||||
--
|
||||
2.4.3
|
||||
|
Loading…
Reference in New Issue