Support for killing the whole child process group

tini only kills the immediate child process.  This means that if you
do, for example,

    docker run krallin/ubuntu-tini sh -c 'sleep 10'

and ctrl-C it, nothing happens: SIGINT is sent to the 'sh' process,
but that shell won't react to it while it is waiting for the 'sleep'
to finish.

This change adds a -g option to put the child process of tini into a
new process group, and sends signals to that group, so that every
process in the group gets a signal.  This corresponds more closely to
what happens when you do ctrl-C etc. in a terminal: The signal is sent
to the foreground process group.

So if you try the example above with a container image that passes -g
to tini, the SIGINT will be received by the 'sleep', and the container
promptly exits.
pull/16/head
David Wragg 9 years ago committed by Thomas Orozco
parent 1bfde9c126
commit 109019f917
  1. 18
      README.md
  2. 20
      src/tini.c
  3. 18
      tpl/README.md.in

@ -81,6 +81,24 @@ as PID 1.
and isn't registered as a subreaper. If you don't see a warning, you're fine.*
### Process group killing ###
By default, Tini only kills its immediate child process. This can be
inconvenient if sending a signal to that process does have the desired
effect. For example, if you do
docker run krallin/ubuntu-tini sh -c 'sleep 10'
and ctrl-C it, nothing happens: SIGINT is sent to the 'sh' process,
but that shell won't react to it while it is waiting for the 'sleep'
to finish.
With the `-g` option, Tini kills the child process group , so that
every process in the group gets the signal. This corresponds more
closely to what happens when you do ctrl-C etc. in a terminal: The
signal is sent to the foreground process group.
More
----

@ -27,11 +27,11 @@
#ifdef PR_SET_CHILD_SUBREAPER
#define HAS_SUBREAPER 1
#define OPT_STRING "hsv"
#define OPT_STRING "hsvg"
#define SUBREAPER_ENV_VAR "TINI_SUBREAPER"
#else
#define HAS_SUBREAPER 0
#define OPT_STRING "hv"
#define OPT_STRING "hvg"
#endif
@ -39,6 +39,7 @@
static int subreaper = 0;
#endif
static int verbosity = 1;
static int kill_process_group = 0;
static struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
@ -68,6 +69,13 @@ int spawn(const sigset_t* const child_sigset_ptr, char* const argv[], int* const
PRINT_FATAL("Setting child signal mask failed: '%s'", strerror(errno));
return 1;
}
// Put the child into a new process group
if (setpgid(0, 0) < 0) {
PRINT_FATAL("setpgid failed: '%s'", strerror(errno));
return 1;
}
execvp(argv[0], argv);
PRINT_FATAL("Executing child process '%s' failed: '%s'", argv[0], strerror(errno));
return 1;
@ -89,6 +97,7 @@ void print_usage(char* const name, FILE* const file) {
fprintf(file, " -s: Register as a process subreaper (requires Linux >= 3.4).\n");
#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, "\n");
}
@ -112,6 +121,11 @@ int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[
case 'v':
verbosity++;
break;
case 'g':
kill_process_group++;
break;
case '?':
print_usage(name, stderr);
return 1;
@ -242,7 +256,7 @@ int wait_and_forward_signal(sigset_t const* const parent_sigset_ptr, pid_t const
default:
PRINT_DEBUG("Passing signal: '%s'", strsignal(sig.si_signo));
/* Forward anything else */
if (kill(child_pid, sig.si_signo)) {
if (kill(kill_process_group ? -child_pid : child_pid, sig.si_signo)) {
if (errno == ESRCH) {
PRINT_WARNING("Child was dead when forwarding signal");
} else {

@ -81,6 +81,24 @@ as PID 1.
and isn't registered as a subreaper. If you don't see a warning, you're fine.*
### Process group killing ###
By default, Tini only kills its immediate child process. This can be
inconvenient if sending a signal to that process does have the desired
effect. For example, if you do
docker run krallin/ubuntu-tini sh -c 'sleep 10'
and ctrl-C it, nothing happens: SIGINT is sent to the 'sh' process,
but that shell won't react to it while it is waiting for the 'sleep'
to finish.
With the `-g` option, Tini kills the child process group , so that
every process in the group gets the signal. This corresponds more
closely to what happens when you do ctrl-C etc. in a terminal: The
signal is sent to the foreground process group.
More
----

Loading…
Cancel
Save