From owner-freebsd-arch@FreeBSD.ORG Thu Jul 25 19:37:12 2013 Return-Path: Delivered-To: arch@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id CDFF7707 for ; Thu, 25 Jul 2013 19:37:12 +0000 (UTC) (envelope-from jhb@freebsd.org) Received: from bigwig.baldwin.cx (bigwig.baldwin.cx [IPv6:2001:470:1f11:75::1]) (using TLSv1 with cipher ADH-CAMELLIA256-SHA (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id A7E892BC0 for ; Thu, 25 Jul 2013 19:37:12 +0000 (UTC) Received: from jhbbsd.localnet (unknown [209.249.190.124]) by bigwig.baldwin.cx (Postfix) with ESMTPSA id 5B21BB926 for ; Thu, 25 Jul 2013 15:37:11 -0400 (EDT) From: John Baldwin To: arch@freebsd.org Subject: EVFILT_PROC always returns an EV_EOF event Date: Thu, 25 Jul 2013 15:37:04 -0400 User-Agent: KMail/1.13.5 (FreeBSD/8.2-CBSD-20110714-p25; KDE/4.5.5; amd64; ; ) MIME-Version: 1.0 Content-Type: Text/Plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Message-Id: <201307251537.04491.jhb@freebsd.org> X-Greylist: Sender succeeded SMTP AUTH, not delayed by milter-greylist-4.2.7 (bigwig.baldwin.cx); Thu, 25 Jul 2013 15:37:11 -0400 (EDT) X-BeenThere: freebsd-arch@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Discussion related to FreeBSD architecture List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 25 Jul 2013 19:37:13 -0000 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 #include #include #include #include #include #include #include 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