Add standard command-line flags
This commit is contained in:
parent
bac1bf99b0
commit
6b7f0ce3fa
8 changed files with 99 additions and 34 deletions
|
@ -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`.
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
include dumb-init.c
|
include dumb-init.c
|
||||||
|
include VERSION
|
||||||
|
include VERSION.h
|
||||||
|
|
7
Makefile
7
Makefile
|
@ -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
1
VERSION
Normal file
|
@ -0,0 +1 @@
|
||||||
|
0.4.0
|
6
VERSION.h
Normal file
6
VERSION.h
Normal 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;
|
84
dumb-init.c
84
dumb-init.c
|
@ -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));
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -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',
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue