/* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "common.h" int vnode_fd; static void test_kevent_vnode_add(void) { const char *test_id = "kevent(EVFILT_VNODE, EV_ADD)"; const char *testfile = "./kqueue-test.tmp"; struct kevent kev; test_begin(test_id); system("touch ./kqueue-test.tmp"); vnode_fd = open(testfile, O_RDONLY); if (vnode_fd < 0) err(1, "open of %s", testfile); else printf("vnode_fd = %d\n", vnode_fd); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD, NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME | NOTE_DELETE, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } static void test_kevent_vnode_note_delete(void) { const char *test_id = "kevent(EVFILT_VNODE, NOTE_DELETE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (unlink("./kqueue-test.tmp") < 0) err(1, "unlink"); kevent_cmp(&kev, kevent_get(kqfd)); success(); } static void test_kevent_vnode_note_delete_fifo(void) { const char *test_id = "kevent(EVFILT_VNODE, NOTE_DELETE, FIFO)"; const char *fifo_path = "./kqueue-fifo.tmp"; struct kevent kev; int fd; pid_t pid; test_begin(test_id); if (mkfifo(fifo_path, 0600) != 0) err(1, "mkfifo"); pid = fork(); if (pid == -1) err(1, "fork"); if (pid == 0) { char buf[4]; fd = open(fifo_path, O_RDONLY); if (fd == -1) _exit(1); while (read(fd, buf, sizeof(buf)) != 0) { } _exit(0); } sleep(1); if (waitpid(pid, NULL, WNOHANG) == pid) { unlink(fifo_path); err(1, "open"); } fd = open(fifo_path, O_WRONLY); if (fd < 0) { unlink(fifo_path); err(1, "open"); } EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) { unlink(fifo_path); err(1, "%s", test_id); } if (unlink(fifo_path) < 0) err(1, "unlink"); kevent_cmp(&kev, kevent_get(kqfd)); close(fd); success(); } static void test_kevent_vnode_note_write(void) { const char *test_id = "kevent(EVFILT_VNODE, NOTE_WRITE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_WRITE, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("echo hello >> ./kqueue-test.tmp") < 0) err(1, "system"); /* BSD kqueue adds NOTE_EXTEND even though it was not requested */ /* BSD kqueue removes EV_ENABLE */ kev.flags &= ~EV_ENABLE; // XXX-FIXME compatibility issue kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue kevent_cmp(&kev, kevent_get(kqfd)); success(); } static void test_kevent_vnode_note_attrib(void) { const char *test_id = "kevent(EVFILT_VNODE, NOTE_ATTRIB)"; struct kevent kev; int nfds; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) err(1, "%s", test_id); if (kev.ident != (uintptr_t)vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_ATTRIB) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); success(); } static void test_kevent_vnode_note_rename(void) { const char *test_id = "kevent(EVFILT_VNODE, NOTE_RENAME)"; struct kevent kev; int nfds; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_RENAME, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("mv ./kqueue-test.tmp ./kqueue-test2.tmp") < 0) err(1, "system"); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) err(1, "%s", test_id); if (kev.ident != (uintptr_t)vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_RENAME) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); if (system("mv ./kqueue-test2.tmp ./kqueue-test.tmp") < 0) err(1, "system"); success(); } static void test_kevent_vnode_del(void) { const char *test_id = "kevent(EVFILT_VNODE, EV_DELETE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } static void test_kevent_vnode_disable_and_enable(void) { const char *test_id = "kevent(EVFILT_VNODE, EV_DISABLE and EV_ENABLE)"; struct kevent kev; int nfds; test_begin(test_id); test_no_kevents(); /* Add the watch and immediately disable it */ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kev.flags = EV_DISABLE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Confirm that the watch is disabled */ if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); test_no_kevents(); /* Re-enable and check again */ kev.flags = EV_ENABLE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) err(1, "%s", test_id); if (kev.ident != (uintptr_t)vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_ATTRIB) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); success(); } #if HAVE_EV_DISPATCH static void test_kevent_vnode_dispatch(void) { const char *test_id = "kevent(EVFILT_VNODE, EV_DISPATCH)"; struct kevent kev; int nfds; test_begin(test_id); test_no_kevents(); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_DISPATCH, NOTE_ATTRIB, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) err(1, "%s", test_id); if (kev.ident != (uintptr_t)vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_ATTRIB) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); /* Confirm that the watch is disabled automatically */ puts("-- checking that watch is disabled"); if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); test_no_kevents(); /* Delete the watch */ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, NOTE_ATTRIB, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "remove watch failed: %s", test_id); success(); } #endif /* HAVE_EV_DISPATCH */ void test_evfilt_vnode(void) { kqfd = kqueue(); test_kevent_vnode_add(); test_kevent_vnode_del(); test_kevent_vnode_disable_and_enable(); #if HAVE_EV_DISPATCH test_kevent_vnode_dispatch(); #endif test_kevent_vnode_note_write(); test_kevent_vnode_note_attrib(); test_kevent_vnode_note_rename(); test_kevent_vnode_note_delete(); test_kevent_vnode_note_delete_fifo(); close(kqfd); }