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.
pull/7/head
Thomas Orozco 10 years ago
parent 98311caf52
commit bcb8a4b870
  1. 20
      ci/run_build.sh
  2. 98
      src/tini.c
  3. 35
      test/0001-Add-PR_SET_CHILD_SUBREAPER.patch

@ -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"

@ -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,6 +298,13 @@ 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.
/* Parse command line arguments */
char* (*child_args_ptr)[];
int parse_args_ret = parse_args(argc, argv, &child_args_ptr, &parse_exitcode);
if (parse_args_ret) {
return parse_exitcode;
}
/* Prepare sigmask */
sigset_t parent_sigset;
sigset_t child_sigset;
@ -240,13 +312,17 @@ int main(int argc, char *argv[]) {
return 1;
}
/* Parse command line arguments */
char* (*child_args_ptr)[];
int parse_args_ret = parse_args(argc, argv, &child_args_ptr, &parse_exitcode);
if (parse_args_ret) {
return parse_exitcode;
}
#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…
Cancel
Save