#include <sys/select.h>
#include <sys/wait.h>
#include <err.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <atf-c.h>
static volatile sig_atomic_t caught[NSIG];
static void
handler(int signo)
{
caught[signo]++;
}
static void
child(int rd, bool poll)
{
struct timespec timeout = { .tv_nsec = 1000000 };
sigset_t set0, set1;
int ret;
sigemptyset(&set0);
sigemptyset(&set1);
sigaddset(&set1, SIGINT);
sigprocmask(SIG_BLOCK, &set1, NULL);
signal(SIGINT, handler);
close(rd);
for (;;) {
ret = poll ? ppoll(NULL, 0, &timeout, &set0) :
pselect(0, NULL, NULL, NULL, &timeout, &set0);
if (ret != 0 && errno != EINTR)
err(1, "p%s()", poll ? "poll" : "select");
if (ret == 0 && caught[SIGINT]) {
signal(SIGINT, SIG_DFL);
raise(SIGINT);
}
caught[SIGINT] = 0;
}
}
static void
prace(bool poll)
{
int pd[2], status;
pid_t pid;
if (pipe(pd) != 0)
err(1, "pipe()");
if ((pid = fork()) < 0)
err(1, "fork()");
if (pid == 0) {
close(pd[0]);
child(pd[1], poll);
}
close(pd[1]);
(void)read(pd[0], &pd[0], sizeof(pd[0]));
close(pd[0]);
for (useconds_t timeout = 1100; timeout > 900; timeout--) {
usleep(timeout);
if (kill(pid, SIGINT) != 0) {
if (errno != ENOENT)
err(1, "kill()");
break;
}
}
(void)kill(pid, SIGKILL);
if (waitpid(pid, &status, 0) < 0)
err(1, "waitpid()");
ATF_REQUIRE(WIFSIGNALED(status));
ATF_REQUIRE_MSG(WTERMSIG(status) == SIGKILL,
"child caught SIG%s", sys_signame[WTERMSIG(status)]);
}
ATF_TC_WITHOUT_HEAD(ppoll_race);
ATF_TC_BODY(ppoll_race, tc)
{
prace(true);
}
ATF_TC_WITHOUT_HEAD(pselect_race);
ATF_TC_BODY(pselect_race, tc)
{
prace(false);
}
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, ppoll_race);
ATF_TP_ADD_TC(tp, pselect_race);
return (atf_no_error());
}