Test that Tini restores signal configuration

Tini ignores certain signals, and blocks others, but in both cases
we restore them before executing the child process.

Add tests to ensure that we actually do that!
This commit is contained in:
Thomas Orozco 2015-10-31 15:31:57 +01:00
parent e3ae587f92
commit a0bf435d2f
4 changed files with 71 additions and 3 deletions

View File

@ -5,4 +5,4 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/*
# Pre-install those here for faster local builds.
RUN CFLAGS="-DPR_SET_CHILD_SUBREAPER=36 -DPR_GET_CHILD_SUBREAPER=37" pip install psutil python-prctl
RUN CFLAGS="-DPR_SET_CHILD_SUBREAPER=36 -DPR_GET_CHILD_SUBREAPER=37" pip install psutil python-prctl bitmap

View File

@ -3,11 +3,15 @@
set -o errexit
set -o nounset
# Default compiler
: ${CC:="gcc"}
# Paths
: ${SOURCE_DIR:="."}
: ${DIST_DIR:="${SOURCE_DIR}/dist"}
: ${BUILD_DIR:="/tmp/build"}
# Make those paths absolute, and export them for the Python tests to consume.
export SOURCE_DIR="$(readlink -f "${SOURCE_DIR}")"
export DIST_DIR="$(readlink -f "${DIST_DIR}")"
@ -80,6 +84,9 @@ for tini in "${BUILD_DIR}/tini" "${BUILD_DIR}/tini-static"; do
fi
done
# Compile test code
"${CC}" -o "${BUILD_DIR}/sigconf-test" "${SOURCE_DIR}/test/sigconf/sigconf-test.c"
# Create virtual environment to run tests.
# Accept system site packages for faster local builds.
VENV="${BUILD_DIR}/venv"
@ -90,7 +97,7 @@ export PATH="${VENV}/bin:${PATH}"
export CFLAGS # We need them to build our test suite, regardless of FORCE_SUBREAPER
# Install test dependencies
pip install psutil python-prctl
pip install psutil python-prctl bitmap
# Run tests
python "${SOURCE_DIR}/test/run_inner_tests.py"

View File

@ -6,6 +6,11 @@ import signal
import subprocess
import time
import psutil
import bitmap
import re
SIGNUM_TO_SIGNAME = dict((v, k) for k,v in signal.__dict__.items() if re.match("^SIG[A-Z]+$", k))
def busy_wait(condition_callable, timeout):
@ -63,6 +68,7 @@ def main():
ret = p.wait()
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.
@ -73,14 +79,40 @@ def main():
p.send_signal(signal.SIGUSR1)
busy_wait(lambda: p.poll() is not None, 10)
# Run failing test
print "Running failing test"
print "Running zombie reaping failure test (Tini should warn)"
p = subprocess.Popen([tini, "--", os.path.join(src, "test", "reaping", "stage_1.py")], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
assert "zombie reaping won't work" in err, "No warning message was output!"
ret = p.wait()
assert ret == 1, "Reaping test succeeded (it should have failed)!"
# Test that the signals are properly in place here.
print "running signal configuration test"
p = subprocess.Popen([os.path.join(build, "sigconf-test"), tini, '-g', '--', "cat", "/proc/self/status"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
# Extract the signal properties, and add a zero at the end.
props = [line.split(":") for line in out.splitlines()]
props = [(k.strip(), v.strip()) for (k, v) in props]
props = [(k, bitmap.BitMap.fromstring(bin(int(v, 16))[2:].zfill(32))) for (k, v) in props if k in ["SigBlk", "SigIgn", "SigCgt"]]
props = dict(props)
# Print actual handling configuration
for k, bmp in props.items():
print "{0}: {1}".format(k, ", ".join(["{0} ({1})".format(SIGNUM_TO_SIGNAME[n+1], n+1) for n in bmp.nonzero()]))
for signal_set_name, signals_to_test_for in [
("SigIgn", [signal.SIGTTOU, signal.SIGSEGV, signal.SIGINT,]),
("SigBlk", [signal.SIGTTIN, signal.SIGILL, signal.SIGTERM,]),
]:
for signum in signals_to_test_for:
# Use signum - 1 because the bitmap is 0-indexed but represents signals strting at 1
assert (signum - 1) in props[signal_set_name].nonzero(), "{0} ({1}) is missing in {2}!".format(SIGNUM_TO_SIGNAME[signum], signum, signal_set_name)
print "---------------------------"
print "All done, tests as expected"
print "---------------------------"

View File

@ -0,0 +1,29 @@
/*
Test program to:
+ Ignore a few signals
+ Block a few signals
+ Exec whatever the test runner asked for
*/
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
// Signals to ignore
signal(SIGTTOU, SIG_IGN); // This one should still be in SigIgn (Tini touches it to ignore it, and should restore it)
signal(SIGSEGV, SIG_IGN); // This one should still be in SigIgn (Tini shouldn't touch it)
signal(SIGINT, SIG_IGN); // This one should still be in SigIgn (Tini should block it to forward it, and restore it)
// Signals to block
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGTTIN); // This one should still be in SigIgn (Tini touches it to ignore it, and should restore it)
sigaddset(&set, SIGILL); // This one should still be in SigIgn (Tini shouldn't touch it)
sigaddset(&set, SIGTERM); // This one should still be in SigIgn (Tini should block it to forward it, and restore it)
sigprocmask(SIG_BLOCK, &set, NULL);
// Run whatever we were asked to run
execvp(argv[1], argv+1);
}