Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 26 Jul 2013 11:52:32 -0700
From:      John-Mark Gurney <jmg@funkthat.com>
To:        John Baldwin <jhb@freebsd.org>
Cc:        arch@freebsd.org
Subject:   Re: EVFILT_PROC always returns an EV_EOF event
Message-ID:  <20130726185232.GR26412@funkthat.com>
In-Reply-To: <201307251537.04491.jhb@freebsd.org>
References:  <201307251537.04491.jhb@freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
John Baldwin wrote this message on Thu, Jul 25, 2013 at 15:37 -0400:
> 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.

This is probably to let the consumer know that the process no longer
exists and that there will be no more events delivered for this process..
This allows the process to clean up in this case..  If you look at the
code in filt_proc in kern_event.c, you'll also see that is forces
_ONESHOT to be set, meaning that the knote will be deleted...

It is someone documented:
     EV_EOF         Filters may set this flag to indicate filter-specific EOF
                    condition.

But I do agree that the documentation could be better...

I don't have a strong opinion on which behavior is best.  I do think
that delivering the EOF is best, since on an fd, you get _EOF when the
socket closes, even though you didn't ask for it.. it's implicit..

> 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

Nice addition of drop...  The patch looks good if we decide to go this
route...

> #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
> _______________________________________________
> freebsd-arch@freebsd.org mailing list
> http://lists.freebsd.org/mailman/listinfo/freebsd-arch
> To unsubscribe, send any mail to "freebsd-arch-unsubscribe@freebsd.org"

-- 
  John-Mark Gurney				Voice: +1 415 225 5579

     "All that I will do, has been done, All that I have, has not."



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20130726185232.GR26412>