diff options
author | rwatson <rwatson@FreeBSD.org> | 2009-12-13 20:27:46 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2009-12-13 20:27:46 +0000 |
commit | b8a76a26d7efee89c5b8137ee8c47ded132f83c9 (patch) | |
tree | 6dd819ceb3ef5a8b6b4cd08d72439d0dc034a7ca /tools | |
parent | 0266d1d9310dadac44284b66cf2f8bb3740f75df (diff) | |
download | FreeBSD-src-b8a76a26d7efee89c5b8137ee8c47ded132f83c9.zip FreeBSD-src-b8a76a26d7efee89c5b8137ee8c47ded132f83c9.tar.gz |
Add Mark Heily's libkqueue test suite as a general kqueue test suite to
tools/regression. It tests a number of aspects of kqueue behavior,
although not all currently pass (possibly bugs in the test suite?).
Submitted by: Mark Heily <mark at heily.com>
Obtained from: svn://mark.heily.com/libkqueue/trunk/test (r114)
Diffstat (limited to 'tools')
-rw-r--r-- | tools/regression/kqueue/Makefile | 21 | ||||
-rw-r--r-- | tools/regression/kqueue/common.h | 78 | ||||
-rw-r--r-- | tools/regression/kqueue/config.h | 12 | ||||
-rw-r--r-- | tools/regression/kqueue/main.c | 278 | ||||
-rw-r--r-- | tools/regression/kqueue/proc.c | 241 | ||||
-rw-r--r-- | tools/regression/kqueue/read.c | 324 | ||||
-rw-r--r-- | tools/regression/kqueue/signal.c | 199 | ||||
-rw-r--r-- | tools/regression/kqueue/timer.c | 178 | ||||
-rw-r--r-- | tools/regression/kqueue/user.c | 132 | ||||
-rw-r--r-- | tools/regression/kqueue/vnode.c | 266 |
10 files changed, 1729 insertions, 0 deletions
diff --git a/tools/regression/kqueue/Makefile b/tools/regression/kqueue/Makefile new file mode 100644 index 0000000..b25a17b --- /dev/null +++ b/tools/regression/kqueue/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ +# +# svn://mark.heily.com/libkqueue/trunk/test +# Last update: r114 +# +# libkqueue and test suite by Mark Heily <mark@heily.com> +# + +PROG=kqtest +SRCS= \ + main.c \ + read.c \ + timer.c \ + vnode.c \ + proc.c \ + signal.c \ + user.c +NO_MAN= +WARNS=2 + +.include "bsd.prog.mk" diff --git a/tools/regression/kqueue/common.h b/tools/regression/kqueue/common.h new file mode 100644 index 0000000..aada778 --- /dev/null +++ b/tools/regression/kqueue/common.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2009 Mark Heily <mark@heily.com> + * + * 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$ + */ + +#ifndef _COMMON_H +#define _COMMON_H + + +#if HAVE_ERR_H +# include <err.h> +#else +# define err(rc,msg,...) do { perror(msg); exit(rc); } while (0) +# define errx(rc,msg,...) do { puts(msg); exit(rc); } while (0) +#endif +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include <sys/event.h> + +#include "config.h" + +extern char *cur_test_id; +int vnode_fd; + +extern const char * kevent_to_str(struct kevent *); +struct kevent * kevent_get(int); + + +void kevent_cmp(struct kevent *, struct kevent *); + +void +kevent_add(int kqfd, struct kevent *kev, + uintptr_t ident, + short filter, + u_short flags, + u_int fflags, + intptr_t data, + void *udata); + +/* DEPRECATED: */ +#define KEV_CMP(kev,_ident,_filter,_flags) do { \ + if (kev.ident != (_ident) || \ + kev.filter != (_filter) || \ + kev.flags != (_flags)) \ + err(1, "kevent mismatch: got [%d,%d,%d] but expecting [%d,%d,%d]", \ + (int)_ident, (int)_filter, (int)_flags,\ + (int)kev.ident, kev.filter, kev.flags);\ +} while (0); + +/* Checks if any events are pending, which is an error. */ +extern void test_no_kevents(void); + +extern void test_begin(const char *); +extern void success(void); + +#endif /* _COMMON_H */ diff --git a/tools/regression/kqueue/config.h b/tools/regression/kqueue/config.h new file mode 100644 index 0000000..895872f --- /dev/null +++ b/tools/regression/kqueue/config.h @@ -0,0 +1,12 @@ +/* AUTOMATICALLY GENERATED -- DO NOT EDIT */ +/* $FreeBSD$ */ +#define HAVE_SYS_EVENT_H +#define HAVE_EV_DISPATCH 1 +#define HAVE_EV_RECEIPT 1 +#undef HAVE_NOTE_TRUNCATE +#define HAVE_EVFILT_TIMER 1 +#define HAVE_EVFILT_USER 1 +#define PROGRAM "libkqueue-test" +#define VERSION "0.1" +#define TARGET "freebsd" +#define CFLAGS "-g -O0 -Wall -Werror" diff --git a/tools/regression/kqueue/main.c b/tools/regression/kqueue/main.c new file mode 100644 index 0000000..f1c15a4 --- /dev/null +++ b/tools/regression/kqueue/main.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2009 Mark Heily <mark@heily.com> + * + * 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 <sys/types.h> + +#include "common.h" + +int testnum = 1; +char *cur_test_id = NULL; +int kqfd; + +extern void test_evfilt_read(); +extern void test_evfilt_signal(); +extern void test_evfilt_vnode(); +extern void test_evfilt_timer(); +extern void test_evfilt_proc(); +#if HAVE_EVFILT_USER +extern void test_evfilt_user(); +#endif + +/* Checks if any events are pending, which is an error. */ +void +test_no_kevents(void) +{ + int nfds; + struct timespec timeo; + struct kevent kev; + + puts("confirming that there are no events pending"); + memset(&timeo, 0, sizeof(timeo)); + nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo); + if (nfds != 0) { + puts("\nUnexpected event:"); + puts(kevent_to_str(&kev)); + errx(1, "%d event(s) pending, but none expected:", nfds); + } +} + +/* Retrieve a single kevent */ +struct kevent * +kevent_get(int kqfd) +{ + int nfds; + struct kevent *kev; + + if ((kev = calloc(1, sizeof(*kev))) == NULL) + err(1, "out of memory"); + + nfds = kevent(kqfd, NULL, 0, kev, 1, NULL); + if (nfds < 1) + err(1, "kevent(2)"); + + return (kev); +} + +char * +kevent_fflags_dump(struct kevent *kev) +{ + char *buf; + +#define KEVFFL_DUMP(attrib) \ + if (kev->fflags & attrib) \ + strncat(buf, #attrib" ", 64); + + if ((buf = calloc(1, 1024)) == NULL) + abort(); + + /* Not every filter has meaningful fflags */ + if (kev->filter != EVFILT_VNODE) { + snprintf(buf, 1024, "fflags = %d", kev->fflags); + return (buf); + } + + snprintf(buf, 1024, "fflags = %d (", kev->fflags); + KEVFFL_DUMP(NOTE_DELETE); + KEVFFL_DUMP(NOTE_WRITE); + KEVFFL_DUMP(NOTE_EXTEND); +#if HAVE_NOTE_TRUNCATE + KEVFFL_DUMP(NOTE_TRUNCATE); +#endif + KEVFFL_DUMP(NOTE_ATTRIB); + KEVFFL_DUMP(NOTE_LINK); + KEVFFL_DUMP(NOTE_RENAME); +#if HAVE_NOTE_REVOKE + KEVFFL_DUMP(NOTE_REVOKE); +#endif + buf[strlen(buf) - 1] = ')'; + + return (buf); +} + +char * +kevent_flags_dump(struct kevent *kev) +{ + char *buf; + +#define KEVFL_DUMP(attrib) \ + if (kev->flags & attrib) \ + strncat(buf, #attrib" ", 64); + + if ((buf = calloc(1, 1024)) == NULL) + abort(); + + snprintf(buf, 1024, "flags = %d (", kev->flags); + KEVFL_DUMP(EV_ADD); + KEVFL_DUMP(EV_ENABLE); + KEVFL_DUMP(EV_DISABLE); + KEVFL_DUMP(EV_DELETE); + KEVFL_DUMP(EV_ONESHOT); + KEVFL_DUMP(EV_CLEAR); + KEVFL_DUMP(EV_EOF); + KEVFL_DUMP(EV_ERROR); +#if HAVE_EV_DISPATCH + KEVFL_DUMP(EV_DISPATCH); +#endif +#if HAVE_EV_RECEIPT + KEVFL_DUMP(EV_RECEIPT); +#endif + buf[strlen(buf) - 1] = ')'; + + return (buf); +} + +/* Copied from ../kevent.c kevent_dump() and improved */ +const char * +kevent_to_str(struct kevent *kev) +{ + char buf[512]; + + snprintf(&buf[0], sizeof(buf), + "[ident=%d, filter=%d, %s, %s, data=%d, udata=%p]", + (u_int) kev->ident, + kev->filter, + kevent_flags_dump(kev), + kevent_fflags_dump(kev), + (int) kev->data, + kev->udata); + + return (strdup(buf)); +} + +void +kevent_add(int kqfd, struct kevent *kev, + uintptr_t ident, + short filter, + u_short flags, + u_int fflags, + intptr_t data, + void *udata) +{ + EV_SET(kev, ident, filter, flags, fflags, data, NULL); + if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) { + printf("Unable to add the following kevent:\n%s\n", + kevent_to_str(kev)); + err(1, "kevent(): %s", strerror(errno)); + } +} + +void +kevent_cmp(struct kevent *k1, struct kevent *k2) +{ +/* XXX- + Workaround for inconsistent implementation of kevent(2) + */ +#ifdef __FreeBSD__ + if (k1->flags & EV_ADD) + k2->flags |= EV_ADD; +#endif + if (memcmp(k1, k2, sizeof(*k1)) != 0) { + printf("kevent_cmp: mismatch:\n %s !=\n %s\n", + kevent_to_str(k1), kevent_to_str(k2)); + abort(); + } +} + +void +test_begin(const char *func) +{ + if (cur_test_id) + free(cur_test_id); + cur_test_id = strdup(func); + if (!cur_test_id) + err(1, "strdup failed"); + + printf("\n\nTest %d: %s\n", testnum++, func); +} + +void +success(void) +{ + printf("%-70s %s\n", cur_test_id, "passed"); + free(cur_test_id); + cur_test_id = NULL; +} + +void +test_kqueue(void) +{ + test_begin("kqueue()"); + if ((kqfd = kqueue()) < 0) + err(1, "kqueue()"); + test_no_kevents(); + success(); +} + +void +test_kqueue_close(void) +{ + test_begin("close(kq)"); + if (close(kqfd) < 0) + err(1, "close()"); + success(); +} + +int +main(int argc, char **argv) +{ + int test_proc = 0; /* XXX-FIXME */ + int test_socket = 1; + int test_signal = 1; + int test_vnode = 1; + int test_timer = 1; + int test_user = 1; + + while (argc) { + if (strcmp(argv[0], "--no-proc") == 0) + test_proc = 0; + if (strcmp(argv[0], "--no-socket") == 0) + test_socket = 0; + if (strcmp(argv[0], "--no-timer") == 0) + test_timer = 0; + if (strcmp(argv[0], "--no-signal") == 0) + test_signal = 0; + if (strcmp(argv[0], "--no-vnode") == 0) + test_vnode = 0; + if (strcmp(argv[0], "--no-user") == 0) + test_user = 0; + argv++; + argc--; + } + + test_kqueue(); + test_kqueue_close(); + + if (test_socket) + test_evfilt_read(); + if (test_signal) + test_evfilt_signal(); + if (test_vnode) + test_evfilt_vnode(); +#if HAVE_EVFILT_USER + if (test_user) + test_evfilt_user(); +#endif + if (test_timer) + test_evfilt_timer(); + if (test_proc) + test_evfilt_proc(); + + printf("\n---\n" + "+OK All %d tests completed.\n", testnum - 1); + return (0); +} diff --git a/tools/regression/kqueue/proc.c b/tools/regression/kqueue/proc.c new file mode 100644 index 0000000..d4e863e --- /dev/null +++ b/tools/regression/kqueue/proc.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2009 Mark Heily <mark@heily.com> + * + * 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" + +static int sigusr1_caught = 0; + +int kqfd; + +static void +sig_handler(int signum) +{ + sigusr1_caught = 1; +} + +static void +add_and_delete(void) +{ + struct kevent kev; + pid_t pid; + + /* Create a child that waits to be killed and then exits */ + pid = fork(); + if (pid == 0) { + pause(); + exit(2); + } + printf(" -- child created (pid %d)\n", (int) pid); + + test_begin("kevent(EVFILT_PROC, EV_ADD)"); + + test_no_kevents(); + kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); + test_no_kevents(); + + success(); + + test_begin("kevent(EVFILT_PROC, EV_DELETE)"); + + test_no_kevents(); + kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL); + if (kill(pid, SIGKILL) < 0) + err(1, "kill"); + sleep(1); + test_no_kevents(); + + success(); + +} + +static void +event_trigger(void) +{ + struct kevent kev; + pid_t pid; + + test_begin("kevent(EVFILT_PROC, wait)"); + + /* Create a child that waits to be killed and then exits */ + pid = fork(); + if (pid == 0) { + pause(); + printf(" -- child caught signal, exiting\n"); + exit(2); + } + printf(" -- child created (pid %d)\n", (int) pid); + + test_no_kevents(); + kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); + + /* Cause the child to exit, then retrieve the event */ + printf(" -- killing process %d\n", (int) pid); + if (kill(pid, SIGUSR1) < 0) + err(1, "kill"); + kevent_cmp(&kev, kevent_get(kqfd)); + test_no_kevents(); + + success(); +} + +#ifdef TODO +void +test_kevent_signal_disable(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Block SIGUSR1, then send it to ourselves */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); + if (kill(getpid(), SIGKILL) < 0) + err(1, "kill"); + + test_no_kevents(); + + success(); +} + +void +test_kevent_signal_enable(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Block SIGUSR1, then send it to ourselves */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + + kev.flags = EV_ADD | EV_CLEAR; +#if LIBKQUEUE + kev.data = 1; /* WORKAROUND */ +#else + kev.data = 2; // one extra time from test_kevent_signal_disable() +#endif + kevent_cmp(&kev, kevent_get(kqfd)); + + /* Delete the watch */ + kev.flags = EV_DELETE; + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +void +test_kevent_signal_del(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)"; + struct kevent kev; + + test_begin(test_id); + + /* Delete the kevent */ + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Block SIGUSR1, then send it to ourselves */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + + test_no_kevents(); + success(); +} + +void +test_kevent_signal_oneshot(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Block SIGUSR1, then send it to ourselves */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + + kev.flags |= EV_CLEAR; + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + /* Send another one and make sure we get no events */ + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + test_no_kevents(); + + success(); +} +#endif + +void +test_evfilt_proc() +{ + kqfd = kqueue(); + + signal(SIGUSR1, sig_handler); + + add_and_delete(); + event_trigger(); + + signal(SIGUSR1, SIG_DFL); + +#if TODO + test_kevent_signal_add(); + test_kevent_signal_del(); + test_kevent_signal_get(); + test_kevent_signal_disable(); + test_kevent_signal_enable(); + test_kevent_signal_oneshot(); +#endif + close(kqfd); +} diff --git a/tools/regression/kqueue/read.c b/tools/regression/kqueue/read.c new file mode 100644 index 0000000..cc65427 --- /dev/null +++ b/tools/regression/kqueue/read.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2009 Mark Heily <mark@heily.com> + * + * 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 kqfd; +int sockfd[2]; + +static void +kevent_socket_drain(void) +{ + char buf[1]; + + /* Drain the read buffer, then make sure there are no more events. */ + puts("draining the read buffer"); + if (read(sockfd[0], &buf[0], 1) < 1) + err(1, "read(2)"); +} + +static void +kevent_socket_fill(void) +{ + puts("filling the read buffer"); + if (write(sockfd[1], ".", 1) < 1) + err(1, "write(2)"); +} + + +void +test_kevent_socket_add(void) +{ + const char *test_id = "kevent(EVFILT_READ, EV_ADD)"; + struct kevent kev; + + test_begin(test_id); + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +void +test_kevent_socket_get(void) +{ + const char *test_id = "kevent(EVFILT_READ) wait"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + kevent_socket_fill(); + + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + kevent_socket_drain(); + test_no_kevents(); + + kev.flags = EV_DELETE; + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +void +test_kevent_socket_clear(void) +{ + const char *test_id = "kevent(EVFILT_READ, EV_CLEAR)"; + struct kevent kev; + + test_begin(test_id); + + test_no_kevents(); + + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + kevent_socket_fill(); + kevent_socket_fill(); + + kev.data = 2; + kevent_cmp(&kev, kevent_get(kqfd)); + + /* We filled twice, but drain once. Edge-triggered would not generate + additional events. + */ + kevent_socket_drain(); + test_no_kevents(); + + kevent_socket_drain(); + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +void +test_kevent_socket_disable_and_enable(void) +{ + const char *test_id = "kevent(EVFILT_READ, EV_DISABLE)"; + struct kevent kev; + + test_begin(test_id); + + /* Add an event, then disable it. */ + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DISABLE, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + kevent_socket_fill(); + test_no_kevents(); + + /* Re-enable the knote, then see if an event is generated */ + kev.flags = EV_ENABLE; + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + kev.flags = EV_ADD; + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + kevent_socket_drain(); + + kev.flags = EV_DELETE; + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +void +test_kevent_socket_del(void) +{ + const char *test_id = "kevent(EVFILT_READ, EV_DELETE)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + kevent_socket_fill(); + test_no_kevents(); + kevent_socket_drain(); + + success(); +} + +void +test_kevent_socket_oneshot(void) +{ + const char *test_id = "kevent(EVFILT_READ, EV_ONESHOT)"; + struct kevent kev; + + test_begin(test_id); + + /* Re-add the watch and make sure no events are pending */ + puts("-- re-adding knote"); + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + test_no_kevents(); + + puts("-- getting one event"); + kevent_socket_fill(); + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + puts("-- checking knote disabled"); + test_no_kevents(); + + /* Try to delete the knote, it should already be deleted */ + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == 0) + err(1, "%s", test_id); + + kevent_socket_drain(); + + success(); +} + + +#if HAVE_EV_DISPATCH +void +test_kevent_socket_dispatch(void) +{ + const char *test_id = "kevent(EVFILT_READ, EV_DISPATCH)"; + + test_begin(test_id); + + struct kevent kev; + + /* Re-add the watch and make sure no events are pending */ + puts("-- re-adding knote"); + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_DISPATCH, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + test_no_kevents(); + + /* The event will occur only once, even though EV_CLEAR is not + specified. */ + kevent_socket_fill(); + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + test_no_kevents(); + + /* Since the knote is disabled, the EV_DELETE operation succeeds. */ + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + kevent_socket_drain(); + + success(); +} +#endif /* HAVE_EV_DISPATCH */ + +#if BROKEN +void +test_kevent_socket_lowat(void) +{ + const char *test_id = "kevent(EVFILT_READ, NOTE_LOWAT)"; + struct kevent kev; + + test_begin(test_id); + + /* Re-add the watch and make sure no events are pending */ + puts("-- re-adding knote, setting low watermark to 2 bytes"); + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_ONESHOT, NOTE_LOWAT, 2, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + test_no_kevents(); + + puts("-- checking that one byte does not trigger an event.."); + kevent_socket_fill(); + test_no_kevents(); + + puts("-- checking that two bytes triggers an event.."); + kevent_socket_fill(); + if (kevent(kqfd, NULL, 0, &kev, 1, NULL) != 1) + err(1, "%s", test_id); + KEV_CMP(kev, sockfd[0], EVFILT_READ, 0); + test_no_kevents(); + + kevent_socket_drain(); + kevent_socket_drain(); + + success(); +} +#endif + +void +test_kevent_socket_eof(void) +{ + const char *test_id = "kevent(EVFILT_READ, EV_EOF)"; + struct kevent kev; + + test_begin(test_id); + + /* Re-add the watch and make sure no events are pending */ + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + test_no_kevents(); + + if (close(sockfd[1]) < 0) + err(1, "close(2)"); + + kev.flags |= EV_EOF; + kevent_cmp(&kev, kevent_get(kqfd)); + + /* Delete the watch */ + EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +void +test_evfilt_read() +{ + /* Create a connected pair of full-duplex sockets for testing socket events */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0) + abort(); + + kqfd = kqueue(); + test_kevent_socket_add(); + test_kevent_socket_del(); + test_kevent_socket_get(); + test_kevent_socket_disable_and_enable(); + test_kevent_socket_oneshot(); + test_kevent_socket_clear(); +#if HAVE_EV_DISPATCH + test_kevent_socket_dispatch(); +#endif + test_kevent_socket_eof(); + close(kqfd); +} diff --git a/tools/regression/kqueue/signal.c b/tools/regression/kqueue/signal.c new file mode 100644 index 0000000..14e751d --- /dev/null +++ b/tools/regression/kqueue/signal.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2009 Mark Heily <mark@heily.com> + * + * 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 kqfd; + +void +test_kevent_signal_add(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, EV_ADD)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +void +test_kevent_signal_get(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, wait)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Block SIGUSR1, then send it to ourselves */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + + kev.flags |= EV_CLEAR; + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + success(); +} + +void +test_kevent_signal_disable(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Block SIGUSR1, then send it to ourselves */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + + test_no_kevents(); + + success(); +} + +void +test_kevent_signal_enable(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Block SIGUSR1, then send it to ourselves */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + + kev.flags = EV_ADD | EV_CLEAR; +#if LIBKQUEUE + kev.data = 1; /* WORKAROUND */ +#else + kev.data = 2; // one extra time from test_kevent_signal_disable() +#endif + kevent_cmp(&kev, kevent_get(kqfd)); + + /* Delete the watch */ + kev.flags = EV_DELETE; + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +void +test_kevent_signal_del(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)"; + struct kevent kev; + + test_begin(test_id); + + /* Delete the kevent */ + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Block SIGUSR1, then send it to ourselves */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + + test_no_kevents(); + success(); +} + +void +test_kevent_signal_oneshot(void) +{ + const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Block SIGUSR1, then send it to ourselves */ + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + err(1, "sigprocmask"); + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + + kev.flags |= EV_CLEAR; + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + /* Send another one and make sure we get no events */ + if (kill(getpid(), SIGUSR1) < 0) + err(1, "kill"); + test_no_kevents(); + + success(); +} + +void +test_evfilt_signal() +{ + kqfd = kqueue(); + test_kevent_signal_add(); + test_kevent_signal_del(); + test_kevent_signal_get(); + test_kevent_signal_disable(); + test_kevent_signal_enable(); + test_kevent_signal_oneshot(); + close(kqfd); +} diff --git a/tools/regression/kqueue/timer.c b/tools/regression/kqueue/timer.c new file mode 100644 index 0000000..766125d --- /dev/null +++ b/tools/regression/kqueue/timer.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2009 Mark Heily <mark@heily.com> + * + * 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 kqfd; + +void +test_kevent_timer_add(void) +{ + const char *test_id = "kevent(EVFILT_TIMER, EV_ADD)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +void +test_kevent_timer_del(void) +{ + const char *test_id = "kevent(EVFILT_TIMER, EV_DELETE)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + test_no_kevents(); + + success(); +} + +void +test_kevent_timer_get(void) +{ + const char *test_id = "kevent(EVFILT_TIMER, wait)"; + struct kevent kev; + + test_begin(test_id); + + EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + kev.flags |= EV_CLEAR; + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +static void +test_oneshot(void) +{ + const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT)"; + struct kevent kev; + + test_begin(test_id); + + test_no_kevents(); + + EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500,NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Retrieve the event */ + kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT; + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + /* Check if the event occurs again */ + sleep(3); + test_no_kevents(); + + + success(); +} + +static void +test_periodic(void) +{ + const char *test_id = "kevent(EVFILT_TIMER, periodic)"; + struct kevent kev; + + test_begin(test_id); + + test_no_kevents(); + + EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 1000,NULL); + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + /* Retrieve the event */ + kev.flags = EV_ADD | EV_CLEAR; + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + /* Check if the event occurs again */ + sleep(1); + kevent_cmp(&kev, kevent_get(kqfd)); + + /* Delete the event */ + kev.flags = EV_DELETE; + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + success(); +} + +static void +disable_and_enable(void) +{ + const char *test_id = "kevent(EVFILT_TIMER, EV_DISABLE and EV_ENABLE)"; + struct kevent kev; + + test_begin(test_id); + + test_no_kevents(); + + /* Add the watch and immediately disable it */ + EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 2000,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); + 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); + + kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT; + kev.data = 1; + kevent_cmp(&kev, kevent_get(kqfd)); + + success(); +} + +void +test_evfilt_timer() +{ + kqfd = kqueue(); + test_kevent_timer_add(); + test_kevent_timer_del(); + test_kevent_timer_get(); + test_oneshot(); + test_periodic(); + disable_and_enable(); + close(kqfd); +} diff --git a/tools/regression/kqueue/user.c b/tools/regression/kqueue/user.c new file mode 100644 index 0000000..800ee43 --- /dev/null +++ b/tools/regression/kqueue/user.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2009 Mark Heily <mark@heily.com> + * + * 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 kqfd; + +static void +add_and_delete(void) +{ + const char *test_id = "kevent(EVFILT_USER, EV_ADD and EV_DELETE)"; + struct kevent kev; + + test_begin(test_id); + + kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL); + test_no_kevents(); + + kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DELETE, 0, 0, NULL); + test_no_kevents(); + + success(); +} + +static void +event_wait(void) +{ + const char *test_id = "kevent(EVFILT_USER, wait)"; + struct kevent kev; + + test_begin(test_id); + + /* Add the event, and then trigger it */ + kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL); + kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); + + kev.fflags &= ~NOTE_FFCTRLMASK; + kev.fflags &= ~NOTE_TRIGGER; + kevent_cmp(&kev, kevent_get(kqfd)); + + test_no_kevents(); + + success(); +} + +static void +disable_and_enable(void) +{ + const char *test_id = "kevent(EVFILT_USER, EV_DISABLE and EV_ENABLE)"; + struct kevent kev; + + test_begin(test_id); + + kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL); + kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DISABLE, 0, 0, NULL); + + /* Trigger the event, but since it is disabled, nothing will happen. */ + kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); + test_no_kevents(); + + kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ENABLE, 0, 0, NULL); + kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); + + kev.flags = 0; + kev.fflags &= ~NOTE_FFCTRLMASK; + kev.fflags &= ~NOTE_TRIGGER; + kevent_cmp(&kev, kevent_get(kqfd)); + + success(); +} + +static void +oneshot(void) +{ + const char *test_id = "kevent(EVFILT_USER, EV_ONESHOT)"; + struct kevent kev; + + test_begin(test_id); + + kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_ONESHOT, 0, 0, NULL); + + puts(" -- event 1"); + kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); + + kev.flags = 0; + kev.fflags &= ~NOTE_FFCTRLMASK; + kev.fflags &= ~NOTE_TRIGGER; + kevent_cmp(&kev, kevent_get(kqfd)); + + /* Try to trigger the event again. It is deleted, so that + should be an error. However, on FreeBSD 8 it is not an error, + so the event is ignored. + */ + puts(" -- triggering an event that will be ignored"); + kev.flags = 0; + kev.fflags |= NOTE_TRIGGER; + if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) + err(1, "%s", test_id); + + test_no_kevents(); + + success(); +} + +void +test_evfilt_user() +{ + kqfd = kqueue(); + + add_and_delete(); + event_wait(); + disable_and_enable(); + oneshot(); + /* TODO: try different fflags operations */ + + close(kqfd); +} diff --git a/tools/regression/kqueue/vnode.c b/tools/regression/kqueue/vnode.c new file mode 100644 index 0000000..dfa0b5e --- /dev/null +++ b/tools/regression/kqueue/vnode.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2009 Mark Heily <mark@heily.com> + * + * 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 kqfd; +int vnode_fd; + +void +test_kevent_vnode_add(void) +{ + const char *test_id = "kevent(EVFILT_VNODE, EV_ADD)"; + const char *testfile = "/tmp/kqueue-test.tmp"; + struct kevent kev; + + test_begin(test_id); + + system("touch /tmp/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(); +} + +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("/tmp/kqueue-test.tmp") < 0) + err(1, "unlink"); + + kevent_cmp(&kev, kevent_get(kqfd)); + + success(); +} + +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 >> /tmp/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(); +} + +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 /tmp/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 != 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(); +} + +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 /tmp/kqueue-test.tmp /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 != 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 /tmp/kqueue-test2.tmp /tmp/kqueue-test.tmp") < 0) + err(1, "system"); + + success(); +} + +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(); +} + +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 /tmp/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 /tmp/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 != 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 +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 /tmp/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 != 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 /tmp/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() +{ + 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(); + close(kqfd); +} |