Merge pull request #60 from krallin/white-label

"White label" when MINIMAL is set
This commit is contained in:
Thomas Orozco 2016-11-04 18:11:12 +01:00 committed by GitHub
commit 4a92b9e201
9 changed files with 185 additions and 76 deletions

View File

@ -5,10 +5,10 @@ language: generic
env:
matrix:
- CC=gcc ARCH_SUFFIX=amd64 ARCH_NATIVE=1 NO_ARGS=
- CC=arm-linux-gnueabihf-gcc ARCH_SUFFIX=armhf ARCH_NATIVE= NO_ARGS=
- CC=aarch64-linux-gnu-gcc ARCH_SUFFIX=arm64 ARCH_NATIVE= NO_ARGS=
- CC=gcc ARCH_SUFFIX=amd64 ARCH_NATIVE=1 NO_ARGS=1
- CC=gcc ARCH_SUFFIX=amd64 ARCH_NATIVE=1 MINIMAL=
- CC=arm-linux-gnueabihf-gcc ARCH_SUFFIX=armhf ARCH_NATIVE= MINIMAL=
- CC=aarch64-linux-gnu-gcc ARCH_SUFFIX=arm64 ARCH_NATIVE= MINIMAL=
- CC=gcc ARCH_SUFFIX=amd64 ARCH_NATIVE=1 MINIMAL=1
global:
- SIGN_BINARIES=1
- secure: "RKF9Z9gLxp6k/xITqn7ma1E9HfpYcDXuJFf4862WeH9EMnK9lDq+TWnGsQfkIlqh8h9goe7U+BvRiTibj9MiD5u7eluLo3dlwsLxPpYtyswYeLeC1wKKdT5LPGAXbRKomvBalRYMI+dDnGIM4w96mHgGGvx2zZXGkiAQhm6fJ3k="
@ -31,4 +31,4 @@ deploy:
on:
repo: krallin/tini
tags: true
condition: '-z "$NO_ARGS"'
condition: '-z "$MINIMAL"'

View File

@ -3,14 +3,14 @@ project (tini C)
# Config
set (tini_VERSION_MAJOR 0)
set (tini_VERSION_MINOR 12)
set (tini_VERSION_MINOR 13)
set (tini_VERSION_PATCH 0)
# Build options
option(NO_ARGS "Disable argument parsing" OFF)
option(MINIMAL "Disable argument parsing and verbose output" OFF)
if(NO_ARGS)
add_definitions(-DTINI_NO_ARGS=1)
if(MINIMAL)
add_definitions(-DTINI_MINIMAL=1)
endif()
# Extract git version and dirty-ness

View File

@ -37,7 +37,7 @@ In Docker, you will want to use an entrypoint so you don't have to remember
to manually invoke Tini:
# Add Tini
ENV TINI_VERSION v0.12.0
ENV TINI_VERSION v0.13.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]
@ -63,7 +63,7 @@ The `tini` and `tini-static` binaries are signed using the key `595E85A6B1B4779E
You can verify their signatures using `gpg` (which you may install using
your package manager):
ENV TINI_VERSION v0.12.0
ENV TINI_VERSION v0.13.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini.asc /tini.asc
RUN gpg --keyserver ha.pool.sks-keyservers.net --recv-keys 595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7 \
@ -214,18 +214,20 @@ Maintainer:
Contributors:
+ [Tianon Gravi][21]
+ [David Wragg][22]
+ [Tianon Gravi][30]
+ [David Wragg][31]
+ [Michael Crosby][32]
Special thanks to:
+ [Danilo Bürger][23] for packaging Tini for Alpine
+ [Asko Soukka][24] for packaging Tini for Nix
+ [Danilo Bürger][40] for packaging Tini for Alpine
+ [Asko Soukka][41] for packaging Tini for Nix
[10]: https://github.com/krallin/tini-images
[20]: https://github.com/krallin/
[21]: https://github.com/tianon
[22]: https://github.com/dpw
[23]: https://github.com/danilobuerger
[24]: https://github.com/datakurre
[30]: https://github.com/tianon
[31]: https://github.com/dpw
[32]: https://github.com/crosbymichael
[40]: https://github.com/danilobuerger
[41]: https://github.com/datakurre

View File

@ -45,8 +45,8 @@ export PATH="${SOURCE_DIR}/ci/util:${PATH}"
# Build
CMAKE_ARGS=(-B"${BUILD_DIR}" -H"${SOURCE_DIR}")
if [[ -n "${NO_ARGS:-}" ]]; then
CMAKE_ARGS+=(-DNO_ARGS=ON)
if [[ -n "${MINIMAL:-}" ]]; then
CMAKE_ARGS+=(-DMINIMAL=ON)
fi
cmake "${CMAKE_ARGS[@]}"
@ -71,10 +71,21 @@ if [[ -n "${ARCH_NATIVE:=}" ]]; then
# Smoke tests (actual tests need Docker to run; they don't run within the CI environment)
for tini in "${BUILD_DIR}/tini" "${BUILD_DIR}/tini-static"; do
echo "Testing ${tini} --version"
"$tini" --version | grep "tini version"
echo "Smoke test for ${tini}"
"$tini" --version
if [[ -n "${NO_ARGS:-}" ]]; then
echo "Testing ${tini} --version"
"$tini" --version | grep -q "tini version"
echo "Testing ${tini} without arguments exits with 1"
! "$tini" 2>/dev/null
echo "Testing ${tini} shows help message"
{
! "$tini" 2>&1
} | grep -q "supervision of a valid init process"
if [[ -n "${MINIMAL:-}" ]]; then
echo "Testing $tini with: true"
"${tini}" true
@ -83,6 +94,11 @@ if [[ -n "${ARCH_NATIVE:=}" ]]; then
exit 1
fi
echo "Testing ${tini} does not reference options that don't exist"
! {
! "$tini" 2>&1
} | grep -q "more verbose"
# We try running binaries named after flags (both valid and invalid
# flags) and test that they run.
for flag in h s x; do
@ -94,15 +110,15 @@ if [[ -n "${ARCH_NATIVE:=}" ]]; then
echo "Testing $tini can run binary --version if args are given"
cp "$(which true)" "${BIN_TEST_DIR}/--version"
if "$tini" "--version" --foo | grep "tini version"; then
if "$tini" "--version" --foo | grep -q "tini version"; then
exit 1
fi
else
echo "Smoke test for $tini"
echo "Testing ${tini} -h"
"${tini}" -h
echo "Testing $tini for license"
"${tini}" -l | grep -i "mit license"
"${tini}" -l | grep -q -i "mit license"
echo "Testing $tini with: true"
"${tini}" -vvv true
@ -112,13 +128,35 @@ if [[ -n "${ARCH_NATIVE:=}" ]]; then
exit 1
fi
# Test stdin / stdout are handed over to child
echo "Testing pipe"
echo "exit 0" | "${tini}" -vvv sh
if [[ ! "$?" -eq "0" ]]; then
echo "Pipe test failed"
exit 1
fi
echo "Testing ${tini} references options that exist"
{
! "$tini" 2>&1
} | grep -q "more verbose"
fi
echo "Testing ${tini} supports TINI_VERBOSITY"
TINI_VERBOSITY=3 "$tini" true 2>&1 | grep -q 'Received SIGCHLD'
echo "Testing ${tini} exits with 127 if the command does not exist"
"$tini" foobar123 && rc="$?" || rc="$?"
if [[ "$rc" != 127 ]]; then
echo "Exit code was: ${rc}"
exit 1
fi
echo "Testing ${tini} exits with 126 if the command is not executable"
"$tini" /etc && rc="$?" || rc="$?"
if [[ "$rc" != 126 ]]; then
echo "Exit code was: ${rc}"
exit 1
fi
# Test stdin / stdout are handed over to child
echo "Testing ${tini} does not break pipes"
echo "exit 0" | "${tini}" sh
if [[ ! "$?" -eq "0" ]]; then
echo "Pipe test failed"
exit 1
fi
echo "Checking hardening on $tini"

View File

@ -24,5 +24,5 @@ docker run -it --rm \
-e CC="${CC:=gcc}" \
-e ARCH_NATIVE="${ARCH_NATIVE-1}" \
-e ARCH_SUFFIX="${ARCH_SUFFIX-}" \
-e NO_ARGS="${NO_ARGS-}" \
-e MINIMAL="${MINIMAL-}" \
"${IMG}" "${SRC}/ci/run_build.sh"

View File

@ -17,11 +17,21 @@
#include "tiniConfig.h"
#include "tiniLicense.h"
#if TINI_MINIMAL
#define PRINT_FATAL(...) fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");
#define PRINT_WARNING(...) if (verbosity > 0) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
#define PRINT_INFO(...) if (verbosity > 1) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define PRINT_DEBUG(...) if (verbosity > 2) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define PRINT_TRACE(...) if (verbosity > 3) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define DEFAULT_VERBOSITY 0
#else
#define PRINT_FATAL(...) fprintf(stderr, "[FATAL tini (%i)] ", getpid()); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");
#define PRINT_WARNING(...) if (verbosity > 0) { fprintf(stderr, "[WARN tini (%i)] ", getpid()); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
#define PRINT_INFO(...) if (verbosity > 1) { fprintf(stdout, "[INFO tini (%i)] ", getpid()); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define PRINT_DEBUG(...) if (verbosity > 2) { fprintf(stdout, "[DEBUG tini (%i)] ", getpid()); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define PRINT_TRACE(...) if (verbosity > 3) { fprintf(stdout, "[TRACE tini (%i)] ", getpid()); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define DEFAULT_VERBOSITY 1
#endif
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
@ -31,6 +41,7 @@ typedef struct {
struct sigaction* const sigttou_action_ptr;
} signal_configuration_t;
static unsigned int verbosity = DEFAULT_VERBOSITY;
#ifdef PR_SET_CHILD_SUBREAPER
#define HAS_SUBREAPER 1
@ -41,13 +52,14 @@ typedef struct {
#define OPT_STRING "hvgl"
#endif
#define VERBOSITY_ENV_VAR "TINI_VERBOSITY"
#define TINI_VERSION_STRING "tini version " TINI_VERSION TINI_GIT
#if HAS_SUBREAPER
static unsigned int subreaper = 0;
#endif
static unsigned int verbosity = 1;
static unsigned int kill_process_group = 0;
static struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
@ -56,13 +68,16 @@ static const char reaper_warning[] = "Tini is not running as PID 1 "
#if HAS_SUBREAPER
"and isn't registered as a child subreaper"
#endif
".\n\
Zombie processes will not be re-parented to Tini, so zombie reaping won't work.\n\
To fix the problem, "
".\n\
Zombie processes will not be re-parented to Tini, so zombie reaping won't work.\n\
To fix the problem, "
#if HAS_SUBREAPER
"use -s or set the environment variable " SUBREAPER_ENV_VAR " to register Tini as a child subreaper, or "
#ifndef TINI_MINIMAL
"use the -s option "
#endif
"run Tini as PID 1.";
"or set the environment variable " SUBREAPER_ENV_VAR " to register Tini as a child subreaper, or "
#endif
"run Tini as PID 1.";
int restore_signals(const signal_configuration_t* const sigconf_ptr) {
if (sigprocmask(SIG_SETMASK, sigconf_ptr->sigmask_ptr, NULL)) {
@ -86,7 +101,7 @@ int restore_signals(const signal_configuration_t* const sigconf_ptr) {
int isolate_child() {
// Put the child into a new process group.
if (setpgid(0, 0) < 0) {
PRINT_FATAL("setpgid failed: '%s'", strerror(errno));
PRINT_FATAL("setpgid failed: %s", strerror(errno));
return 1;
}
@ -102,7 +117,7 @@ int isolate_child() {
if (errno == ENOTTY) {
PRINT_DEBUG("tcsetpgrp failed: no tty (ok to proceed)")
} else {
PRINT_FATAL("tcsetpgrp failed: '%s'", strerror(errno));
PRINT_FATAL("tcsetpgrp failed: %s", strerror(errno));
return 1;
}
}
@ -118,7 +133,7 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i
pid = fork();
if (pid < 0) {
PRINT_FATAL("Fork failed: '%s'", strerror(errno));
PRINT_FATAL("fork failed: %s", strerror(errno));
return 1;
} else if (pid == 0) {
@ -133,8 +148,21 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i
}
execvp(argv[0], argv);
PRINT_FATAL("Executing child process '%s' failed: '%s'", argv[0], strerror(errno));
return 1;
// execvp will only return on an error so make sure that we check the errno
// and exit with the correct return status for the error that we encountered
// See: http://www.tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF
int status = 1;
switch errno {
case ENOENT:
status = 127;
break;
case EACCES:
status = 126;
break;
}
PRINT_FATAL("exec %s failed: %s", argv[0], strerror(errno));
return status;
} else {
// Parent
PRINT_INFO("Spawned child process '%s' with pid '%i'", argv[0], pid);
@ -145,8 +173,20 @@ int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], i
void print_usage(char* const name, FILE* const file) {
fprintf(file, "%s (%s)\n", basename(name), TINI_VERSION_STRING);
fprintf(file, "Usage: %s [OPTIONS] PROGRAM -- [ARGS]\n\n", basename(name));
#if TINI_MINIMAL
fprintf(file, "Usage: %s PROGRAM [ARGS] | --version\n\n", basename(name));
#else
fprintf(file, "Usage: %s [OPTIONS] PROGRAM -- [ARGS] | --version\n\n", basename(name));
#endif
fprintf(file, "Execute a program under the supervision of a valid init process (%s)\n\n", basename(name));
fprintf(file, "Command line options:\n\n");
fprintf(file, " --version: Show version and exit.\n");
#if TINI_MINIMAL
#else
fprintf(file, " -h: Show this help message and exit.\n");
#if HAS_SUBREAPER
fprintf(file, " -s: Register as a process subreaper (requires Linux >= 3.4).\n");
@ -154,6 +194,16 @@ void print_usage(char* const name, FILE* const file) {
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, " -l: Show license and exit.\n");
#endif
fprintf(file, "\n");
fprintf(file, "Environment variables:\n\n");
#if HAS_SUBREAPER
fprintf(file, " %s: Register as a process subreaper (requires Linux >= 3.4)\n", SUBREAPER_ENV_VAR);
#endif
fprintf(file, " %s: Set the verbosity level (default: %d)\n", VERBOSITY_ENV_VAR, DEFAULT_VERBOSITY);
fprintf(file, "\n");
}
@ -172,9 +222,7 @@ int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[
return 1;
}
#if TINI_NO_ARGS
*parse_fail_exitcode_ptr = 0;
#else
#ifndef TINI_MINIMAL
int c;
while ((c = getopt(argc, argv, OPT_STRING)) != -1) {
switch (c) {
@ -237,6 +285,12 @@ int parse_env() {
subreaper++;
}
#endif
char* env_verbosity = getenv(VERBOSITY_ENV_VAR);
if (env_verbosity != NULL) {
verbosity = atoi(env_verbosity);
}
return 0;
}
@ -468,8 +522,9 @@ int main(int argc, char *argv[]) {
reaper_check();
/* Go on */
if (spawn(&child_sigconf, *child_args_ptr, &child_pid)) {
return 1;
int spawn_ret = spawn(&child_sigconf, *child_args_ptr, &child_pid);
if (spawn_ret) {
return spawn_ret;
}
free(child_args_ptr);

View File

@ -29,7 +29,7 @@ def main():
src = os.environ["SOURCE_DIR"]
build = os.environ["BUILD_DIR"]
args_disabled = os.environ.get("NO_ARGS")
args_disabled = os.environ.get("MINIMAL")
proxy = os.path.join(src, "test", "subreaper-proxy.py")
tini = os.path.join(build, "tini")
@ -80,10 +80,14 @@ def main():
p.send_signal(signal.SIGUSR1)
busy_wait(lambda: p.poll() is not None, 10)
# Run failing test
# Run failing test. Force verbosity to 1 so we see the subreaper warning
# regardless of whether MINIMAL is set.
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)
p = subprocess.Popen(
[tini, os.path.join(src, "test", "reaping", "stage_1.py")],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
env={'TINI_VERBOSITY': '1'}
)
out, err = p.communicate()
assert "zombie reaping won't work" in err, "No warning message was output!"
ret = p.wait()

View File

@ -65,15 +65,19 @@ class Command(object):
def attach_and_type_exit_0(name):
print "Attaching to {0} to exit 0".format(name)
p = pexpect.spawn("docker attach {0}".format(name))
p.sendline('')
p.sendline('exit 0')
p.close()
def attach_and_issue_ctrl_c(name):
print "Attaching to {0} to CTRL+C".format(name)
p = pexpect.spawn("docker attach {0}".format(name))
p.expect_exact('#')
p.sendintr()
p.close()
def test_tty_handling(img, name, base_cmd, fail_cmd, container_command, exit_function, expect_exit_code):
@ -83,7 +87,11 @@ def test_tty_handling(img, name, base_cmd, fail_cmd, container_command, exit_fun
shell_ready_event = threading.Event()
def spawn():
p = pexpect.spawn(" ".join(base_cmd + ["--tty", "--interactive", img, "/tini/dist/tini", "-vvv", "--", container_command]))
cmd = base_cmd + ["--tty", "--interactive", img, "/tini/dist/tini"]
if os.environ.get("MINIMAL") is None:
cmd.append("--")
cmd.append(container_command)
p = pexpect.spawn(" ".join(cmd))
p.expect_exact("#")
shell_ready_event.set()
rc.value = p.wait()
@ -112,6 +120,7 @@ def test_tty_handling(img, name, base_cmd, fail_cmd, container_command, exit_fun
def main():
img = sys.argv[1]
name = "{0}-test".format(img)
args_disabled = os.environ.get("MINIMAL")
root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
@ -129,8 +138,8 @@ def main():
for entrypoint in ["/tini/dist/tini", "/tini/dist/tini-static"]:
functional_base_cmd = base_cmd + [
"--entrypoint={0}".format(entrypoint),
"-e", "TINI_VERBOSITY=3",
img,
"-vvv",
]
# Reaping test
@ -139,20 +148,24 @@ def main():
# Signals test
for sig, retcode in [("INT", 1), ("TERM", 143)]:
Command(
functional_base_cmd + ["--", "/tini/test/signals/test.py"],
functional_base_cmd + ["/tini/test/signals/test.py"],
fail_cmd,
["docker", "kill", "-s", sig, name],
2
).run(timeout=10, retcode=retcode)
# Exit code test
Command(functional_base_cmd + ["-z"], fail_cmd).run(retcode=1)
Command(functional_base_cmd + ["--", "zzzz"], fail_cmd).run(retcode=1)
Command(functional_base_cmd + ["-h"], fail_cmd).run()
Command(functional_base_cmd + ["-z"], fail_cmd).run(retcode=127 if args_disabled else 1)
Command(functional_base_cmd + ["-h"], fail_cmd).run(retcode=127 if args_disabled else 0)
Command(functional_base_cmd + ["zzzz"], fail_cmd).run(retcode=127)
# Valgrind test (we only run this on the dynamic version, because otherwise Valgrind may bring up plenty of errors that are
# actually from libc)
Command(functional_base_cmd + ["--", "valgrind", "--leak-check=full", "--error-exitcode=1", "/tini/dist/tini", "-v", "--", "ls"], fail_cmd).run()
Command(base_cmd + [img, "valgrind", "--leak-check=full", "--error-exitcode=1", "/tini/dist/tini", "ls"], fail_cmd).run()
# Test tty handling
test_tty_handling(img, name, base_cmd, fail_cmd, "dash", attach_and_type_exit_0, 0)
test_tty_handling(img, name, base_cmd, fail_cmd, "dash -c 'while true; do echo \#; sleep 0.1; done'", attach_and_issue_ctrl_c, 128 + signal.SIGINT)
# Installation tests (sh -c is used for globbing and &&)
for image, pkg_manager, extension in [
@ -164,10 +177,5 @@ def main():
Command(base_cmd + [image, "sh", "-c", "{0} -i /tini/dist/*.{1} && /usr/bin/tini true".format(pkg_manager, extension)], fail_cmd).run()
# Test tty handling
test_tty_handling(img, name, base_cmd, fail_cmd, "dash", attach_and_type_exit_0, 0)
test_tty_handling(img, name, base_cmd, fail_cmd, "dash -c 'while true; do echo \#; sleep 0.1; done'", attach_and_issue_ctrl_c, 128 + signal.SIGINT)
if __name__ == "__main__":
main()

View File

@ -214,18 +214,20 @@ Maintainer:
Contributors:
+ [Tianon Gravi][21]
+ [David Wragg][22]
+ [Tianon Gravi][30]
+ [David Wragg][31]
+ [Michael Crosby][32]
Special thanks to:
+ [Danilo Bürger][23] for packaging Tini for Alpine
+ [Asko Soukka][24] for packaging Tini for Nix
+ [Danilo Bürger][40] for packaging Tini for Alpine
+ [Asko Soukka][41] for packaging Tini for Nix
[10]: https://github.com/krallin/tini-images
[20]: https://github.com/krallin/
[21]: https://github.com/tianon
[22]: https://github.com/dpw
[23]: https://github.com/danilobuerger
[24]: https://github.com/datakurre
[30]: https://github.com/tianon
[31]: https://github.com/dpw
[32]: https://github.com/crosbymichael
[40]: https://github.com/danilobuerger
[41]: https://github.com/datakurre