Skip to content

Commit

Permalink
Add -e flag to a expect a given exit code
Browse files Browse the repository at this point in the history
Passing this flag causes Tini to remap the given exit code to 0 when
forwarding it.

Fixes: #69
  • Loading branch information
krallin committed Feb 19, 2017
1 parent 2c02e14 commit 6a39ffe
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 4 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,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
Expand Down
2 changes: 1 addition & 1 deletion ci/run_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ fi

echo "CC=${CC}"
echo "CFLAGS=${CFLAGS}"
echo "MINIMAL=${MINIMAL-}"
echo "ARCH_SUFFIX=${ARCH_SUFFIX-}"
echo "ARCH_NATIVE=${ARCH_NATIVE-}"

Expand Down Expand Up @@ -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"
Expand Down
50 changes: 47 additions & 3 deletions src/tini.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <sys/wait.h>
#include <sys/prctl.h>

#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
Expand Down Expand Up @@ -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;
Expand All @@ -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 "hsvgl"
#define OPT_STRING "hvgle:s"
#define SUBREAPER_ENV_VAR "TINI_SUBREAPER"
#else
#define HAS_SUBREAPER 0
#define OPT_STRING "hvgl"
#define OPT_STRING "hvgle:"
#endif

#define VERBOSITY_ENV_VAR "TINI_VERBOSITY"
Expand Down Expand Up @@ -196,6 +207,7 @@ void print_usage(char* const name, FILE* const file) {
#endif
fprintf(file, " -v: Generate more verbose output. Repeat up to 3 times.\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

Expand All @@ -220,6 +232,24 @@ void print_license(FILE* const file) {
}
}

int add_expect_status(char* arg) {
long status = NULL;
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];

Expand Down Expand Up @@ -251,6 +281,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;
Expand Down Expand Up @@ -470,6 +508,12 @@ int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
PRINT_FATAL("Main child exited for unknown reason");
return 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;
}
}

// Check if other childs have been reaped.
Expand Down
25 changes: 25 additions & 0 deletions test/run_inner_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
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))

Expand Down Expand Up @@ -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:
Expand Down
16 changes: 16 additions & 0 deletions tpl/README.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,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
Expand Down

0 comments on commit 6a39ffe

Please sign in to comment.