|
// formated with indent -linux main.c |
|
|
|
#include <errno.h> |
|
#include <signal.h> |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <sys/prctl.h> |
|
#include <sys/types.h> |
|
#include <sys/wait.h> |
|
#include <unistd.h> |
|
|
|
#define pr_perror(fmt, ...) fprintf(stderr, "error: " fmt ": %m\n", ##__VA_ARGS__) |
|
|
|
void child_handler(int sig) |
|
{ |
|
while (waitpid(-1, NULL, WNOHANG) > 0) { |
|
} |
|
} |
|
|
|
void main(int argc, char *argv[]) |
|
{ |
|
if (argc < 2) { |
|
pr_perror("%s no-subreaper|subreaper|subreaper-reap", argv[0]); |
|
exit(1); |
|
} |
|
|
|
if (strcmp(argv[1], "subreaper") == 0) { |
|
if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) { |
|
pr_perror("Failed to set child subreaper"); |
|
exit(1); |
|
} |
|
} else if (strcmp(argv[1], "subreaper-reap") == 0) { |
|
if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) == -1) { |
|
pr_perror("Failed to set child subreaper"); |
|
exit(1); |
|
} |
|
// setup SIGCHLD handler to reap zombie processes |
|
struct sigaction sa; |
|
sigemptyset(&sa.sa_mask); |
|
sa.sa_handler = &child_handler; |
|
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; |
|
if (sigaction(SIGCHLD, &sa, NULL) == -1) { |
|
pr_perror("Failed to set SIGCHLD handler"); |
|
exit(1); |
|
} |
|
} else if (strcmp(argv[1], "no-subreaper") == 0) { |
|
// do nothing here |
|
} else { |
|
pr_perror("%s no-subreaper|subreaper|subreaper-reap", argv[0]); |
|
exit(1); |
|
} |
|
|
|
// We must fork to actually enter the PID namespace. |
|
int child = fork(); |
|
if (child == -1) { |
|
pr_perror("Unable to fork a process"); |
|
exit(1); |
|
} |
|
if (child == 0) { |
|
char *args[2]; |
|
args[0] = "zsh"; |
|
args[1] = NULL; |
|
execvp("zsh", args); |
|
pr_perror("failed to execvp"); |
|
exit(1); |
|
} else { |
|
// Parent, wait for the child. |
|
int status = 0; |
|
if (waitpid(child, &status, 0) == -1) { |
|
pr_perror("Failed to waitpid with error"); |
|
exit(1); |
|
} |
|
// Forward the child's exit code or re-send its death signal. |
|
if (WIFEXITED(status)) { |
|
exit(WEXITSTATUS(status)); |
|
} else if (WIFSIGNALED(status)) { |
|
kill(getpid(), WTERMSIG(status)); |
|
} |
|
|
|
exit(1); |
|
} |
|
|
|
return; |
|
} |