#include <sys/types.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <err.h>
int nprocs, nsigs;
pid_t *pids;
pid_t next, prev;
sig_atomic_t usr1, usr2;
void
sighand(int sig)
{
if (sig == SIGUSR1 && ++usr1 <= nsigs) {
if (kill(next, sig))
_exit(1);
}
if (sig == SIGUSR2 && ++usr2 <= nsigs) {
if (kill(prev, sig))
_exit(1);
}
}
void
do_child(void)
{
int i;
sigset_t mask, oldmask;
raise(SIGSTOP);
for (i = 0; i < nprocs; i++) {
if (pids[i] != getpid())
continue;
if (i + 1 == nprocs)
next = pids[0];
else
next = pids[i + 1];
if (i == 0)
prev = pids[nprocs - 1];
else
prev = pids[i - 1];
}
signal(SIGUSR1, sighand);
signal(SIGUSR2, sighand);
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_BLOCK, &mask, &oldmask);
raise(SIGSTOP);
while (usr1 < nsigs || usr2 < nsigs)
sigsuspend(&oldmask);
raise(SIGSTOP);
}
void
wait_stopped(pid_t pid)
{
int status;
if (waitpid(pid, &status, WUNTRACED) != pid)
err(1, "waitpid");
if (!WIFSTOPPED(status))
errx(1, "child %d not stopped", pid);
}
void
cleanup(void)
{
int i;
for (i = 0; i < nprocs; i++)
kill(pids[i], 9);
}
void
alrmhand(int sig)
{
cleanup();
_exit(1);
}
int
main()
{
int i;
pid_t pid;
nprocs = 35;
nsigs = 1000;
if ((pids = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE,
MAP_ANON|MAP_SHARED, -1, 0)) == MAP_FAILED)
err(1, "mmap");
for (i = 0; i < nprocs; i++) {
switch((pid = fork())) {
case 0:
do_child();
_exit(0);
case -1:
err(1, "fork");
}
pids[i] = pid;
}
atexit(cleanup);
signal(SIGALRM, alrmhand);
alarm(120);
for (i = 0; i < nprocs; i++)
wait_stopped(pids[i]);
for (i = 0; i < nprocs; i++)
kill(pids[i], SIGCONT);
for (i = 0; i < nprocs; i++)
wait_stopped(pids[i]);
for (i = 0; i < nprocs; i++)
kill(pids[i], SIGCONT);
kill(pids[arc4random_uniform(nprocs)], SIGUSR1);
kill(pids[arc4random_uniform(nprocs)], SIGUSR2);
for (i = 0; i < 10 * nprocs; i++) {
pid_t pid = pids[arc4random_uniform(nprocs)];
kill(pid, SIGSTOP);
wait_stopped(pid);
kill(pid, SIGCONT);
}
for (i = 0; i < nprocs; i++)
wait_stopped(pids[i]);
for (i = 0; i < nprocs; i++)
kill(pids[i], SIGCONT);
for (i = 0; i < nprocs; i++) {
int status;
if (waitpid(pids[i], &status, WUNTRACED) != pids[i])
err(1, "waitpid");
if (!WIFEXITED(status))
errx(1, "child %d not stopped (%d)", pids[i], status);
if (WEXITSTATUS(status) != 0)
warnx("child %d status: %d", i, status);
}
return (0);
}