Add standard command-line flags

This commit is contained in:
Chris Kuehl 2015-10-02 13:31:35 -07:00
parent bac1bf99b0
commit 6b7f0ce3fa
8 changed files with 99 additions and 34 deletions

View file

@ -19,7 +19,8 @@ of that after merging!
The process to release a new version is: The process to release a new version is:
1. Update the version in `setup.py` 1. Update the version in `VERSION`
2. Run `make VERSION.h`
2. Update the Debian changelog with `dch -v {new version}`. 2. Update the Debian changelog with `dch -v {new version}`.
3. Update the `wget` url in the README to point to the new version. 3. Update the `wget` url in the README to point to the new version.
4. Commit the changes and tag the commit like `v1.0.0`. 4. Commit the changes and tag the commit like `v1.0.0`.

View file

@ -1 +1,3 @@
include dumb-init.c include dumb-init.c
include VERSION
include VERSION.h

View file

@ -48,9 +48,14 @@ DOCKER_TOX_TEST := sh -uexc ' \
&& tox \ && tox \
' '
.PHONY: build .PHONY: build
build: build: VERSION.h
$(CC) $(CFLAGS) -o dumb-init dumb-init.c $(CC) $(CFLAGS) -o dumb-init dumb-init.c
VERSION.h: VERSION
echo '// THIS FILE IS AUTOMATICALLY GENERATED' > VERSION.h
echo '// Run `make VERSION.h` to update it after modifying VERSION.' >> VERSION.h
xxd -i VERSION >> VERSION.h
.PHONY: clean .PHONY: clean
clean: clean-tox clean: clean-tox
rm -rf dumb-init dist/ *.deb rm -rf dumb-init dist/ *.deb

1
VERSION Normal file
View file

@ -0,0 +1 @@
0.4.0

6
VERSION.h Normal file
View file

@ -0,0 +1,6 @@
// THIS FILE IS AUTOMATICALLY GENERATED
// Run `make VERSION.h` to update it after modifying VERSION.
unsigned char VERSION[] = {
0x30, 0x2e, 0x34, 0x2e, 0x30, 0x0a
};
unsigned int VERSION_len = 6;

View file

@ -5,11 +5,12 @@
* Usage: * Usage:
* ./dumb-init python -c 'while True: pass' * ./dumb-init python -c 'while True: pass'
* *
* To get debug output on stderr, run with DUMB_INIT_DEBUG=1 * To get debug output on stderr, run with '-v'.
*/ */
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <getopt.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -17,6 +18,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include "VERSION.h"
#define PRINTERR(...) do { \ #define PRINTERR(...) do { \
fprintf(stderr, "[dumb-init] " __VA_ARGS__); \ fprintf(stderr, "[dumb-init] " __VA_ARGS__); \
@ -99,50 +101,72 @@ void handle_signal(int signum) {
void print_help(char *argv[]) { void print_help(char *argv[]) {
fprintf(stderr, fprintf(stderr,
"Usage: %s COMMAND [[ARG] ...]\n" "dumb-init v%s"
"Usage: %s [option] command [[arg] ...]\n"
"\n" "\n"
"dumb-init is a simple process designed to run as PID 1 inside Docker\n" "dumb-init is a simple process supervisor that forwards signals to children.\n"
"containers and proxy signals to child processes.\n" "It is designed to run as PID1 in minimal container environments.\n"
"\n" "\n"
"Docker runs your processes as PID1. The kernel doesn't apply default signal\n" "Optional arguments:\n"
"handling to PID1 processes, so if your process doesn't register a custom\n" " -c, --single-child Run in single-child mode.\n"
"signal handler, signals like TERM will just bounce off your process.\n" " In this mode, signals are only proxies to the\n"
" direct child and not any of its ancestors.\n"
" -v, --verbose Print debugging information to stderr.\n"
" -h, --help Print this help message and exit.\n"
" -V, --version Print the current version and exit.\n"
"\n" "\n"
"This can result in cases where sending signals to a `docker run` process\n" "Full help is available online at https://github.com/Yelp/dumb-init\n",
"results in the run process exiting, but the container continuing in the\n" VERSION,
"background.\n"
"\n"
"A workaround is to wrap your script in this proxy, which runs as PID1. Your\n"
"process then runs as some other PID, and the kernel won't treat the signals\n"
"that are proxied to them specially.\n"
"\n"
"The proxy dies when your process dies, so it must not double-fork or do other\n"
"weird things (this is basically a requirement for doing things sanely in\n"
"Docker anyway).\n"
"\n"
"By default, dumb-init starts a process group (and session, see: man 2 setsid)\n"
"and signals all processes in it. This is usually useful behavior, but if for\n"
"some reason you wish to disable it, run with DUMB_INIT_SETSID=0.\n",
argv[0] argv[0]
); );
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
int signum; int signum, opt;
char *debug_env, *setsid_env;
if (argc < 2) { struct option long_options[] = {
print_help(argv); {"help", no_argument, NULL, 'h'},
return 1; {"single-child", no_argument, NULL, 'c'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
};
while ((opt = getopt_long(argc, argv, "+hvVc", long_options, NULL)) != -1) {
switch (opt) {
case 'h':
print_help(argv);
return 0;
case 'v':
debug = 1;
break;
case 'V':
fprintf(stderr, "dumb-init v%s", VERSION);
return 0;
case 'c':
use_setsid = 0;
break;
default:
return 1;
}
} }
debug_env = getenv("DUMB_INIT_DEBUG"); if (optind >= argc) {
fprintf(
stderr,
"Usage: %s [option] program [args]\n"
"Try %s --help for full usage.\n",
argv[0], argv[0]
);
return 1;
}
char **cmd = &argv[optind];
char *debug_env = getenv("DUMB_INIT_DEBUG");
if (debug_env && strcmp(debug_env, "1") == 0) { if (debug_env && strcmp(debug_env, "1") == 0) {
debug = 1; debug = 1;
DEBUG("Running in debug mode.\n"); DEBUG("Running in debug mode.\n");
} }
setsid_env = getenv("DUMB_INIT_SETSID"); char *setsid_env = getenv("DUMB_INIT_SETSID");
if (setsid_env && strcmp(setsid_env, "0") == 0) { if (setsid_env && strcmp(setsid_env, "0") == 0) {
use_setsid = 0; use_setsid = 0;
DEBUG("Not running in setsid mode.\n"); DEBUG("Not running in setsid mode.\n");
@ -181,7 +205,7 @@ int main(int argc, char *argv[]) {
DEBUG("setsid complete.\n"); DEBUG("setsid complete.\n");
} }
execvp(argv[1], &argv[1]); execvp(cmd[0], &cmd[0]);
// if this point is reached, exec failed, so we should exit nonzero // if this point is reached, exec failed, so we should exit nonzero
PRINTERR("%s: %s\n", argv[1], strerror(errno)); PRINTERR("%s: %s\n", argv[1], strerror(errno));

View file

@ -87,10 +87,9 @@ class build_cexe(Command):
setup( setup(
name='dumb-init', name='dumb-init',
description='Simple wrapper script which proxies signals to a child', description='Simple wrapper script which proxies signals to a child',
version='0.4.0', version=open('VERSION').read().strip(),
author='Yelp', author='Yelp',
platforms='linux', platforms='linux',
c_executables=[ c_executables=[
Extension( Extension(
'dumb-init', 'dumb-init',

View file

@ -1,6 +1,8 @@
from subprocess import PIPE from subprocess import PIPE
from subprocess import Popen from subprocess import Popen
import pytest
def test_exit_status(both_debug_modes, both_setsid_modes): def test_exit_status(both_debug_modes, both_setsid_modes):
"""dumb-init should say something useful when called with no arguments, and """dumb-init should say something useful when called with no arguments, and
@ -9,4 +11,29 @@ def test_exit_status(both_debug_modes, both_setsid_modes):
proc = Popen(('dumb-init'), stderr=PIPE) proc = Popen(('dumb-init'), stderr=PIPE)
_, stderr = proc.communicate() _, stderr = proc.communicate()
assert proc.returncode != 0 assert proc.returncode != 0
assert stderr == (
b'Usage: dumb-init [option] program [args]\n'
b'Try dumb-init --help for full usage.\n'
)
@pytest.mark.parametrize('flag', ['-h', '--help'])
def test_help_message(flag, both_debug_modes, both_setsid_modes):
"""dumb-init should say something useful when called with the help flag,
and exit zero.
"""
proc = Popen(('dumb-init', flag), stderr=PIPE)
_, stderr = proc.communicate()
assert proc.returncode == 0
assert len(stderr) >= 50 assert len(stderr) >= 50
@pytest.mark.parametrize('flag', ['-V', '--version'])
def test_version_message(flag, both_debug_modes, both_setsid_modes):
"""dumb-init should print its version when asked to."""
version = open('VERSION', 'rb').read()
proc = Popen(('dumb-init', flag), stderr=PIPE)
_, stderr = proc.communicate()
assert proc.returncode == 0
assert stderr == b'dumb-init v' + version