mirror of https://github.com/krallin/tini.git
Merge pull request #16 from krallin/dpw-kill-process-group
Support for kill process group, thanks @dpw!
This commit is contained in:
commit
cf1cb5c4f6
18
README.md
18
README.md
|
@ -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
|
||||
----
|
||||
|
||||
|
|
20
src/tini.c
20
src/tini.c
|
@ -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…
Reference in New Issue