#include <sys/types.h>
#include <sys/event.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <assert.h>
#include <err.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "main.h"
static int do_regress1(void);
static int do_regress2(void);
static int do_regress3(void);
static int do_regress4(void);
static int do_regress5(void);
static int do_regress6(void);
static void make_chain(int);
int
do_regress(int n)
{
switch (n) {
case 1:
return do_regress1();
case 2:
return do_regress2();
case 3:
return do_regress3();
case 4:
return do_regress4();
case 5:
return do_regress5();
case 6:
return do_regress6();
default:
errx(1, "unknown regress test number %d", n);
}
}
static int
do_regress1(void)
{
struct kevent kev[2];
int kq;
ASS((kq = kqueue()) >= 0,
warn("kqueue"));
EV_SET(&kev[0], kq, EVFILT_READ, EV_ADD, 0, 0, NULL);
EV_SET(&kev[1], SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
ASS(kevent(kq, kev, 2, NULL, 0, NULL) == 0,
warn("can't register events on kqueue"));
return 0;
}
static int
do_regress2(void)
{
pid_t pid;
int i, status;
for (i = 0; i < 2; i++) {
pid = fork();
if (pid == -1)
err(1, "fork");
if (pid == 0) {
struct kevent kev[1];
int p0[2], p1[2];
int kq;
if (pipe(p0) == -1)
err(1, "pipe");
if (pipe(p1) == -1)
err(1, "pipe");
kq = kqueue();
if (kq == -1)
err(1, "kqueue");
EV_SET(&kev[0], p0[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
EV_SET(&kev[0], p1[1], EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
EV_SET(&kev[0], p1[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
_exit(0);
}
if (waitpid(pid, &status, 0) == -1)
err(1, "waitpid");
assert(WIFEXITED(status));
assert(WEXITSTATUS(status) == 0);
}
return 0;
}
static int
do_regress3(void)
{
pid_t pid;
int dir, status;
for (dir = 0; dir < 2; dir++) {
pid = fork();
if (pid == -1)
err(1, "fork");
if (pid == 0) {
make_chain(dir);
_exit(0);
}
if (waitpid(pid, &status, 0) == -1)
err(1, "waitpid");
assert(WIFEXITED(status));
assert(WEXITSTATUS(status) == 0);
}
return 0;
}
static void
make_chain(int dir)
{
struct kevent kev[1];
int i, kq, prev;
for (i = 0, prev = -1; i < 120; i++, prev = kq) {
kq = kqueue();
if (kq == -1)
err(1, "kqueue");
if (prev == -1)
continue;
if (dir == 0) {
EV_SET(&kev[0], prev, EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
} else {
EV_SET(&kev[0], kq, EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(prev, kev, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
}
}
}
static int
do_regress4(void)
{
static const int nkqueues = 500;
struct kevent kev[1];
struct rlimit rlim;
struct timespec ts;
int fds[2], i, kq = -1, prev;
if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
err(1, "getrlimit");
if (rlim.rlim_cur < nkqueues + 8) {
rlim.rlim_cur = nkqueues + 8;
if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {
printf("RLIMIT_NOFILE is too low and can't raise it\n");
printf("SKIPPED\n");
exit(0);
}
}
if (pipe(fds) == -1)
err(1, "pipe");
for (i = 0, prev = fds[0]; i < nkqueues; i++, prev = kq) {
kq = kqueue();
if (kq == -1)
err(1, "kqueue");
EV_SET(&kev[0], prev, EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
}
write(fds[1], "x", 1);
ts.tv_sec = 5;
ts.tv_nsec = 0;
assert(kevent(kq, NULL, 0, kev, 1, NULL) == 1);
return 0;
}
static int
do_regress5(void)
{
fd_set fdset;
struct kevent kev[1];
struct pollfd pfd[1];
struct timeval tv;
int fds[2], kq, ret;
if (pipe(fds) == -1)
err(1, "pipe");
kq = kqueue();
if (kq == -1)
err(1, "kqueue");
EV_SET(&kev[0], fds[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
if (kevent(kq, kev, 1, NULL, 0, NULL) == -1)
err(1, "kevent");
FD_ZERO(&fdset);
FD_SET(kq, &fdset);
tv.tv_sec = 0;
tv.tv_usec = 0;
ret = select(kq + 1, &fdset, NULL, NULL, &tv);
if (ret == -1)
err(1, "select");
assert(ret == 0);
pfd[0].fd = kq;
pfd[0].events = POLLIN;
pfd[0].revents = 0;
ret = poll(pfd, 1, 0);
if (ret == -1)
err(1, "poll");
assert(ret == 0);
write(fds[1], "x", 1);
FD_ZERO(&fdset);
FD_SET(kq, &fdset);
tv.tv_sec = 5;
tv.tv_usec = 0;
ret = select(kq + 1, &fdset, NULL, NULL, &tv);
if (ret == -1)
err(1, "select");
assert(ret == 1);
assert(FD_ISSET(kq, &fdset));
pfd[0].fd = kq;
pfd[0].events = POLLIN;
pfd[0].revents = 0;
ret = poll(pfd, 1, 5000);
if (ret == -1)
err(1, "poll");
assert(ret == 1);
assert(pfd[0].revents & POLLIN);
return 0;
}
int
test_regress6(int kq, size_t len)
{
const struct timespec nap_time = { 0, 1 };
int i, kstatus, wstatus;
struct kevent event;
pid_t child, pid;
void *addr;
child = fork();
switch (child) {
case -1:
warn("fork");
return -1;
case 0:
signal(SIGCHLD, SIG_IGN);
for (i = 0; i < 1000; i++) {
if (fork() == 0) {
addr = mmap(NULL, len, PROT_READ|PROT_WRITE,
MAP_ANON, -1, 0);
if (addr == MAP_FAILED)
err(1, "mmap");
memset(addr, 'A', len);
nanosleep(&nap_time, NULL);
_exit(2);
}
}
nanosleep(&nap_time, NULL);
_exit(1);
default:
break;
}
EV_SET(&event, child, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0,
NULL);
if (kevent(kq, &event, 1, &event, 1, NULL) != 1)
err(1, "kevent");
if (event.flags & EV_ERROR)
errx(1, "kevent: %s", strerror(event.data));
if (event.ident != child)
errx(1, "expected child %d, got %lu", child, event.ident);
kstatus = event.data;
if (!WIFEXITED(kstatus))
errx(1, "child did not exit?");
pid = waitpid(child, &wstatus, WNOHANG);
switch (pid) {
case -1:
err(1, "waitpid %d", child);
case 0:
printf("kevent: child %d exited %d\n", child,
WEXITSTATUS(kstatus));
printf("waitpid: child %d not ready\n", child);
break;
default:
if (wstatus != kstatus) {
warnx("kevent status 0x%x != waitpid status 0x%x",
kstatus, wstatus);
}
break;
}
return pid;
}
static int
do_regress6(void)
{
int i, kq, page_size, rc;
struct rlimit rlim;
if (getrlimit(RLIMIT_NPROC, &rlim) == -1)
err(1, "getrlimit(RLIMIT_NPROC)");
rlim.rlim_cur = rlim.rlim_max;
if (setrlimit(RLIMIT_NPROC, &rlim) == -1)
err(1, "setrlimit(RLIMIT_NPROC)");
kq = kqueue();
if (kq == -1)
err(1, "kqueue");
page_size = getpagesize();
for (i = 0; i < 25; i++) {
rc = test_regress6(kq, page_size);
switch (rc) {
case -1:
goto done;
case 0:
printf("child not ready when NOTE_EXIT received");
if (i != 0)
printf(" (%d iterations)", i + 1);
putchar('\n');
goto done;
default:
continue;
}
}
printf("child exited as expected when NOTE_EXIT received\n");
done:
close(kq);
return rc <= 0;
}