Add -e flag to a expect a given exit code

Passing this flag causes Tini to remap the given exit code to 0 when
forwarding it.

Fixes: #69
expect-status-code
Thomas Orozco 8 years ago
parent 8574e10c2a
commit 4466cecec2
  1. 16
      README.md
  2. 2
      ci/run_build.sh
  3. 53
      src/tini.c
  4. 25
      test/run_inner_tests.py
  5. 16
      tpl/README.md.in

@ -141,6 +141,22 @@ as PID 1.
and isn't registered as a subreaper. If you don't see a warning, you're fine.*
### Remapping exit codes ###
Tini will reuse the child's exit code when exiting, but occasionally, this may
not be exactly what you want (e.g. if your child exits with 143 after receiving
SIGTERM). Notably, this can be an issue with Java apps.
In this case, you can use the `-e` flag to remap an arbitrary exit code to 0.
You can pass the flag multiple times if needed.
For example:
```
tini -e 143 -- ...
```
### Process group killing ###
By default, Tini only kills its immediate child process. This can be

@ -37,6 +37,7 @@ fi
echo "CC=${CC}"
echo "CFLAGS=${CFLAGS}"
echo "MINIMAL=${MINIMAL-}"
echo "ARCH_SUFFIX=${ARCH_SUFFIX-}"
echo "ARCH_NATIVE=${ARCH_NATIVE-}"
@ -64,7 +65,6 @@ popd
pkg_version="$(cat "${BUILD_DIR}/VERSION")"
if [[ -n "${ARCH_NATIVE-}" ]]; then
echo "Built native package (ARCH_NATIVE=${ARCH_NATIVE-})"
echo "Running smoke and internal tests"

@ -5,6 +5,7 @@
#include <sys/wait.h>
#include <sys/prctl.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
@ -33,7 +34,15 @@
#define DEFAULT_VERBOSITY 1
#endif
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
#define ARRAY_LEN(x) (sizeof(x) / sizeof((x)[0]))
#define INT32_BITFIELD_SET(F, i) ( F[(i / 32)] |= (1 << (i % 32)) )
#define INT32_BITFIELD_CLEAR(F, i) ( F[(i / 32)] &= ~(1 << (i % 32)) )
#define INT32_BITFIELD_TEST(F, i) ( F[(i / 32)] & (1 << (i % 32)) )
#define INT32_BITFIELD_CHECK_BOUNDS(F, i) do { assert(i >= 0); assert(ARRAY_LEN(F) > (uint) (i / 32)); } while(0)
#define STATUS_MAX 255
#define STATUS_MIN 0
typedef struct {
sigset_t* const sigmask_ptr;
@ -43,13 +52,15 @@ typedef struct {
static unsigned int verbosity = DEFAULT_VERBOSITY;
static int32_t expect_status[(STATUS_MAX - STATUS_MIN + 1) / 32];
#ifdef PR_SET_CHILD_SUBREAPER
#define HAS_SUBREAPER 1
#define OPT_STRING "hsvwgl"
#define OPT_STRING "hvwgle:s"
#define SUBREAPER_ENV_VAR "TINI_SUBREAPER"
#else
#define HAS_SUBREAPER 0
#define OPT_STRING "hvwgl"
#define OPT_STRING "hvwgle:"
#endif
#define VERBOSITY_ENV_VAR "TINI_VERBOSITY"
@ -199,6 +210,7 @@ void print_usage(char* const name, FILE* const file) {
fprintf(file, " -v: Generate more verbose output. Repeat up to 3 times.\n");
fprintf(file, " -w: Print a warning when processes are getting reaped.\n");
fprintf(file, " -g: Send signals to the child's process group.\n");
fprintf(file, " -e EXIT_CODE: Remap EXIT_CODE (from 0 to 255) to 0.\n");
fprintf(file, " -l: Show license and exit.\n");
#endif
@ -223,6 +235,24 @@ void print_license(FILE* const file) {
}
}
int add_expect_status(char* arg) {
long status = 0;
char* endptr = NULL;
status = strtol(arg, &endptr, 10);
if ((endptr == NULL) || (*endptr != 0)) {
return 1;
}
if ((status < STATUS_MIN) || (status > STATUS_MAX)) {
return 1;
}
INT32_BITFIELD_CHECK_BOUNDS(expect_status, status);
INT32_BITFIELD_SET(expect_status, status);
return 0;
}
int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[], int* const parse_fail_exitcode_ptr) {
char* name = argv[0];
@ -258,6 +288,14 @@ int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[
kill_process_group++;
break;
case 'e':
if (add_expect_status(optarg)) {
PRINT_FATAL("Not a valid option for -e: %s", optarg);
*parse_fail_exitcode_ptr = 1;
return 1;
}
break;
case 'l':
print_license(stdout);
*parse_fail_exitcode_ptr = 0;
@ -477,6 +515,15 @@ int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
PRINT_FATAL("Main child exited for unknown reason");
return 1;
}
// Be safe, ensure the status code is indeed between 0 and 255.
*child_exitcode_ptr = *child_exitcode_ptr % (STATUS_MAX - STATUS_MIN + 1);
// If this exitcode was remapped, then set it to 0.
INT32_BITFIELD_CHECK_BOUNDS(expect_status, *child_exitcode_ptr);
if (INT32_BITFIELD_TEST(expect_status, *child_exitcode_ptr)) {
*child_exitcode_ptr = 0;
}
} else if (warn_on_reap > 0) {
PRINT_WARNING("Reaped zombie process with pid=%i", current_pid);
}

@ -8,7 +8,9 @@ import time
import psutil
import bitmap
import re
import itertools
DEVNULL = open(os.devnull, 'wb')
SIGNUM_TO_SIGNAME = dict((v, k) for k,v in signal.__dict__.items() if re.match("^SIG[A-Z]+$", k))
@ -36,6 +38,29 @@ def main():
subreaper_support = bool(int(os.environ["FORCE_SUBREAPER"]))
# Run the exit code test. We use POSIXLY_CORRECT here to not need --
# until that's the default in Tini anyways.
if not args_disabled:
print "Running exit code test for {0}".format(tini)
for code in range(0, 256):
p = subprocess.Popen(
[tini, '-e', str(code), '--', 'sh', '-c', "exit {0}".format(code)],
stdout=DEVNULL, stderr=DEVNULL
)
ret = p.wait()
assert ret == 0, "Inclusive exit code test failed for %s, exit: %s" % (code, ret)
other_codes = [x for x in range(0, 256) if x != code]
args = list(itertools.chain(*[['-e', str(x)] for x in other_codes]))
p = subprocess.Popen(
[tini] + args + ['sh', '-c', "exit {0}".format(code)],
env=dict(os.environ, POSIXLY_CORRECT="1"),
stdout=DEVNULL, stderr=DEVNULL
)
ret = p.wait()
assert ret == code, "Exclusive exit code test failed for %s, exit: %s" % (code, ret)
tests = [([proxy, tini], {}),]
if subreaper_support:

@ -141,6 +141,22 @@ as PID 1.
and isn't registered as a subreaper. If you don't see a warning, you're fine.*
### Remapping exit codes ###
Tini will reuse the child's exit code when exiting, but occasionally, this may
not be exactly what you want (e.g. if your child exits with 143 after receiving
SIGTERM). Notably, this can be an issue with Java apps.
In this case, you can use the `-e` flag to remap an arbitrary exit code to 0.
You can pass the flag multiple times if needed.
For example:
```
tini -e 143 -- ...
```
### Process group killing ###
By default, Tini only kills its immediate child process. This can be

Loading…
Cancel
Save