Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify signal handling using sigwait, eliminate some undefined behavior #72

Merged
merged 1 commit into from
Apr 30, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 43 additions & 54 deletions dumb-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,8 @@ char debug = 0;
char use_setsid = 1;

void forward_signal(int signum) {
if (child_pid > 0) {
kill(use_setsid ? -child_pid : child_pid, signum);
DEBUG("Forwarded signal %d to children.\n", signum);
} else {
DEBUG("Didn't forward signal %d, no children exist yet.\n", signum);
}
kill(use_setsid ? -child_pid : child_pid, signum);
DEBUG("Forwarded signal %d to children.\n", signum);
}

/*
Expand Down Expand Up @@ -78,8 +74,26 @@ void forward_signal(int signum) {
*/
void handle_signal(int signum) {
DEBUG("Received signal %d.\n", signum);
if (signum == SIGCHLD) {
int status, exit_status;
pid_t killed_pid;
while ((killed_pid = waitpid(-1, &status, WNOHANG)) > 0) {
if (WIFEXITED(status)) {
exit_status = WEXITSTATUS(status);
DEBUG("A child with PID %d exited with exit status %d.\n", killed_pid, exit_status);
} else {
assert(WIFSIGNALED(status));
exit_status = 128 + WTERMSIG(status);
DEBUG("A child with PID %d was terminated by signal %d.\n", killed_pid, exit_status - 128);
}

if (
if (killed_pid == child_pid) {
forward_signal(SIGTERM); // send SIGTERM to any remaining children
DEBUG("Child exited with status %d. Goodbye.\n", exit_status);
exit(exit_status);
}
}
} else if (
signum == SIGTSTP || // tty: background yourself
signum == SIGTTIN || // tty: stop reading
signum == SIGTTOU // tty: stop writing
Expand Down Expand Up @@ -121,9 +135,9 @@ void print_help(char *argv[]) {
);
}

int main(int argc, char *argv[]) {
int signum, opt;

char **parse_command(int argc, char *argv[]) {
int opt;
struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"single-child", no_argument, NULL, 'c'},
Expand All @@ -134,18 +148,18 @@ int main(int argc, char *argv[]) {
switch (opt) {
case 'h':
print_help(argv);
return 0;
exit(0);
case 'v':
debug = 1;
break;
case 'V':
fprintf(stderr, "dumb-init v%s", VERSION);
return 0;
exit(0);
case 'c':
use_setsid = 0;
break;
default:
return 1;
exit(1);
}
}

Expand All @@ -156,9 +170,8 @@ int main(int argc, char *argv[]) {
"Try %s --help for full usage.\n",
argv[0], argv[0]
);
return 1;
exit(1);
}
char **cmd = &argv[optind];

char *debug_env = getenv("DUMB_INIT_DEBUG");
if (debug_env && strcmp(debug_env, "1") == 0) {
Expand All @@ -172,29 +185,20 @@ int main(int argc, char *argv[]) {
DEBUG("Not running in setsid mode.\n");
}

/* register signal handlers */
for (signum = 1; signum < 32; signum++) {
if (signum == SIGKILL || signum == SIGSTOP || signum == SIGCHLD)
continue;
return &argv[optind];
}

if (signal(signum, handle_signal) == SIG_ERR) {
PRINTERR("Couldn't register signal handler for signal `%d`. Exiting.\n", signum);
return 1;
}
}
int main(int argc, char *argv[]) {
char **cmd = parse_command(argc, argv);

/* launch our process */
child_pid = fork();

if (child_pid < 0) {
PRINTERR("Unable to fork. Exiting.\n");
return 1;
}

if (child_pid == 0) {
} else if (child_pid == 0) {
/* child */
if (use_setsid) {
pid_t result = setsid();
if (result == -1) {
if (setsid() == -1) {
PRINTERR(
"Unable to setsid (errno=%d %s). Exiting.\n",
errno,
Expand All @@ -204,37 +208,22 @@ int main(int argc, char *argv[]) {
}
DEBUG("setsid complete.\n");
}

execvp(cmd[0], &cmd[0]);

// if this point is reached, exec failed, so we should exit nonzero
PRINTERR("%s: %s\n", argv[1], strerror(errno));
exit(2);
return 2;
} else {
pid_t killed_pid;
int exit_status, status;
/* parent */
int signum;
sigset_t all_signals;
sigfillset(&all_signals);
sigprocmask(SIG_BLOCK, &all_signals, NULL);

DEBUG("Child spawned with PID %d.\n", child_pid);

while ((killed_pid = waitpid(-1, &status, 0))) {
if (WIFEXITED(status)) {
exit_status = WEXITSTATUS(status);
DEBUG("A child with PID %d exited with exit status %d.\n", killed_pid, exit_status);
} else {
assert(WIFSIGNALED(status));
exit_status = 128 + WTERMSIG(status);
DEBUG("A child with PID %d was terminated by signal %d.\n", killed_pid, exit_status - 128);
}

if (killed_pid == child_pid) {
// send SIGTERM to any remaining children
forward_signal(SIGTERM);

DEBUG("Child exited with status %d. Goodbye.\n", exit_status);
exit(exit_status);
}
for (;;) {
sigwait(&all_signals, &signum);
handle_signal(signum);
}
}

return 0;
}
2 changes: 2 additions & 0 deletions tests/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def test_verbose(flag):
(
b'^\[dumb-init\] Child spawned with PID [0-9]+\.\n'
b'\[dumb-init\] setsid complete\.\n'
b'\[dumb-init\] Received signal 17\.\n'
b'\[dumb-init\] A child with PID [0-9]+ exited with exit status 0.\n'
b'\[dumb-init\] Forwarded signal 15 to children\.\n'
b'\[dumb-init\] Child exited with status 0\. Goodbye\.\n$'
Expand All @@ -88,6 +89,7 @@ def test_verbose_and_single_child(flag1, flag2):
assert re.match(
(
b'^\[dumb-init\] Child spawned with PID [0-9]+\.\n'
b'\[dumb-init\] Received signal 17\.\n'
b'\[dumb-init\] A child with PID [0-9]+ exited with exit status 0.\n'
b'\[dumb-init\] Forwarded signal 15 to children\.\n'
b'\[dumb-init\] Child exited with status 0\. Goodbye\.\n$'
Expand Down