Skip to content

Instantly share code, notes, and snippets.

@dqminh
Last active August 29, 2015 14:15
Show Gist options
  • Save dqminh/9b9643f1144374e2df3f to your computer and use it in GitHub Desktop.
Save dqminh/9b9643f1144374e2df3f to your computer and use it in GitHub Desktop.
test-zsh-subreaper

zsh with subreaper

  • compile the test program with gcc -o test-zsh main.c
  • run it as test-zsh no-subreaper|subreaper|subreaper-reap

There are 3 modes:

  • no-subreaper: do not invoke the program with PR_SET_CHILD_SUBREAPER, so the child process of zsh will be reaped by something else ( either zsh? or PID 1)
  • subreaper: set PR_SET_CHILD_SUBREAPER, but doesnt reap child processes when receiving SIGCHLD
  • subreaper-reap: set PR_SET_CHILD_SUBREAPER and reap child processes when receiving SIGCHLD

In case of sourcing docker-zsh-test, no-subreaper and subreaper-reap will behave correctly i.e. properly reap defunct childs.

#!/bin/zsh
# docker-zsh-test.sh
if [[ "$(echo "the black cat was chased by the brown dog" | sed -e 's/cat/fox/g' | grep fox)" = "" ]]; then
echo "cat"
else
echo "fox"
fi
// 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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment