Merge pull request #16 from krallin/dpw-kill-process-group

Support for kill process group, thanks @dpw!
pull/24/head
Thomas Orozco 9 years ago
commit cf1cb5c4f6
  1. 18
      README.md
  2. 20
      src/tini.c
  3. 18
      test/pgroup/stage_1.py
  4. 26
      test/run_inner_tests.py
  5. 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 {

@ -0,0 +1,18 @@
#!/usr/bin/env python
import os
import subprocess
import signal
def reset_sig_handler():
signal.signal(signal.SIGUSR1, signal.SIG_DFL)
if __name__ == "__main__":
signal.signal(signal.SIGUSR1, signal.SIG_IGN)
p = subprocess.Popen(
["sleep", "1000"],
preexec_fn=reset_sig_handler
)
p.wait()

@ -4,6 +4,20 @@ import os
import sys
import signal
import subprocess
import time
import psutil
def busy_wait(condition_callable, timeout):
checks = 100
increment = float(timeout) / checks
for _ in xrange(checks):
if condition_callable():
return
time.sleep(increment)
assert False, "Condition was never met"
def main():
@ -47,7 +61,17 @@ def main():
sig = getattr(signal, signame)
p.send_signal(sig)
ret = p.wait()
assert ret == - sig, "Signals test failed!"
assert ret == -sig, "Signals test failed!"
# Run the process group test
# This test has Tini spawn a process that ignores SIGUSR1 and spawns a child that doesn't (and waits on the child)
# We send SIGUSR1 to Tini, and expect the grand-child to terminate, then the child, and then Tini.
print "Running process group test"
p = subprocess.Popen([tini, '-g', '--', os.path.join(src, "test", "pgroup", "stage_1.py")], env=dict(os.environ, **env))
busy_wait(lambda: len(psutil.Process(p.pid).children(recursive=True)) == 2, 10)
p.send_signal(signal.SIGUSR1)
busy_wait(lambda: p.poll() is not None, 10)
# Run failing test
print "Running failing test"

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