EVFILT_PROC always returns an EV_EOF event
John Baldwin
jhb at freebsd.org
Thu Jul 25 19:37:13 UTC 2013
A co-worker ran into this undocumented behavior today. If you register an
EVFILT_PROC event but do not set NOTE_EXIT, you can still get an event with
fflags set to 0 but EV_EOF set. This is not documented in the manpage, and
it seems inconsistent to me. If the caller hasn't set NOTE_EXIT, then
presumably they do not wish to know about NOTE_EXIT events.
I have a specific test case below (watch for NOTE_EXEC on a process that only
exits). Is this behavior desired or should this be fixed? If we want it
fixed I have a possible fix (tested with this test case) at
http://people.freebsd.org/~jhb/patches/kevent_proc_eof.patch
#include <sys/types.h>
#include <sys/event.h>
#include <assert.h>
#include <err.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static pid_t master;
static int kq;
static void
watch(uintptr_t ident, short filter, u_short flags, u_int fflags,
intptr_t data, void *udata)
{
struct kevent ev;
EV_SET(&ev, ident, filter, flags, fflags, data, udata);
if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0)
err(1, "kevent");
}
static void
dump_fflags(u_int fflags)
{
int pipe;
assert(fflags != 0);
pipe = 0;
#define DUMP_FLAG(FLAG) do { \
if (fflags & FLAG) { \
printf("%s" #FLAG, pipe ? " | " : ""); \
pipe = 1; \
} \
} while (0)
DUMP_FLAG(NOTE_EXIT);
DUMP_FLAG(NOTE_FORK);
DUMP_FLAG(NOTE_EXEC);
DUMP_FLAG(NOTE_TRACK);
DUMP_FLAG(NOTE_TRACKERR);
DUMP_FLAG(NOTE_CHILD);
fflags &= ~(NOTE_EXIT | NOTE_FORK | NOTE_EXEC | NOTE_TRACK |
NOTE_TRACKERR | NOTE_CHILD);
if (fflags != 0)
printf("%s%u", pipe ? " | " : "", fflags);
}
static void
dump_event(struct kevent *ev)
{
assert(ev->filter == EVFILT_PROC);
printf("pid: %5d%s flags: ", (int)ev->ident,
ev->flags & EV_EOF ? " EV_EOF" : "");
dump_fflags(ev->fflags);
if (ev->data != 0)
printf(" data: %jd", (uintmax_t)ev->data);
printf("\n");
}
static void
child(int fd)
{
pid_t pid;
char c;
if (fd > 0)
(void)read(fd, &c, sizeof(c));
pid = fork();
if (pid == -1)
err(1, "fork");
usleep(5000);
exit(1);
}
static void
waitfor(int count, const char *msg)
{
struct timespec ts;
struct kevent ev;
int rv;
printf("%s:\n", msg);
/* Wait up to 250 ms before timing out. */
ts.tv_sec = 0;
ts.tv_nsec = 250 * 1000 * 1000;
for (;;) {
rv = kevent(kq, NULL, 0, &ev, 1, &ts);
if (rv < 0)
err(1, "kevent");
if (rv == 0)
break;
dump_event(&ev);
--count;
}
if (count > 0)
warnx("%d events missing for %s", count, msg);
else if (count < 0)
warnx("%d extra events for %s", -count, msg);
}
int
main(int ac, char **av)
{
pid_t pid;
int fds[2];
char c;
kq = kqueue();
if (kq < 0)
err(1, "kqueue");
if (pipe(fds) < 0)
err(1, "pipe");
master = getpid();
printf("master: %d\n", (int)master);
/* Test for a dummy EV_EOF event. */
pid = fork();
if (pid == -1)
err(1, "fork");
if (pid == 0)
child(fds[1]);
watch(pid, EVFILT_PROC, EV_ADD, NOTE_EXEC, 0, 0);
write(fds[0], &c, sizeof(c));
/* Should not get any events at all. */
waitfor(0, "dummy EV_EOF");
return (0);
}
--
John Baldwin
More information about the freebsd-arch
mailing list