/*- * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include static int curtest = 1; /*- * This test uses UNIX domain socket pairs to perform some basic exercising * of kqueue functionality on sockets. In particular, testing that for read * and write filters, we see the correct detection of whether reads and * writes should actually be able to occur. * * TODO: * - Test read/write filters for listen/accept sockets. * - Handle the XXXRW below regarding datagram sockets. * - Test that watermark/buffer size "data" fields returned by kqueue are * correct. * - Check that kqueue does something sensible when the remote endpoing is * closed. */ #define OK(testname) printf("ok %d - %s\n", curtest, testname); \ curtest++; static void fail(int error, const char *func, const char *socktype, const char *rest) { printf("not ok %d\n", curtest); if (socktype == NULL) printf("# %s(): %s\n", func, strerror(error)); else if (rest == NULL) printf("# %s(%s): %s\n", func, socktype, strerror(error)); else printf("# %s(%s, %s): %s\n", func, socktype, rest, strerror(error)); exit(-1); } static void fail_assertion(const char *func, const char *socktype, const char *rest, const char *assertion) { printf("not ok %d - %s\n", curtest, assertion); if (socktype == NULL) printf("# %s(): assertion %s failed\n", func, assertion); else if (rest == NULL) printf("# %s(%s): assertion %s failed\n", func, socktype, assertion); else printf("# %s(%s, %s): assertion %s failed\n", func, socktype, rest, assertion); exit(-1); } /* * Test read kevent on a socket pair: check to make sure endpoint 0 isn't * readable when we start, then write to endpoint 1 and confirm that endpoint * 0 is now readable. Drain the write, then check that it's not readable * again. Use non-blocking kqueue operations and socket operations. */ static void test_evfilt_read(int kq, int fd[2], const char *socktype) { struct timespec ts; struct kevent ke; ssize_t len; char ch; int i; EV_SET(&ke, fd[0], EVFILT_READ, EV_ADD, 0, 0, NULL); if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1) fail(errno, "kevent", socktype, "EVFILT_READ, EV_ADD"); OK("EVFILT_READ, EV_ADD"); /* * Confirm not readable to begin with, no I/O yet. */ ts.tv_sec = 0; ts.tv_nsec = 0; i = kevent(kq, NULL, 0, &ke, 1, &ts); if (i == -1) fail(errno, "kevent", socktype, "EVFILT_READ"); OK("EVFILT_READ"); if (i != 0) fail_assertion("kevent", socktype, "EVFILT_READ", "empty socket unreadable"); OK("empty socket unreadable"); /* * Write a byte to one end. */ ch = 'a'; len = write(fd[1], &ch, sizeof(ch)); if (len == -1) fail(errno, "write", socktype, NULL); OK("write one byte"); if (len != sizeof(ch)) fail_assertion("write", socktype, NULL, "write length"); OK("write one byte length"); /* * Other end should now be readable. */ ts.tv_sec = 0; ts.tv_nsec = 0; i = kevent(kq, NULL, 0, &ke, 1, &ts); if (i == -1) fail(errno, "kevent", socktype, "EVFILT_READ"); OK("EVFILT_READ"); if (i != 1) fail_assertion("kevent", socktype, "EVFILT_READ", "non-empty socket unreadable"); OK("non-empty socket unreadable"); /* * Read a byte to clear the readable state. */ len = read(fd[0], &ch, sizeof(ch)); if (len == -1) fail(errno, "read", socktype, NULL); OK("read one byte"); if (len != sizeof(ch)) fail_assertion("read", socktype, NULL, "read length"); OK("read one byte length"); /* * Now re-check for readability. */ ts.tv_sec = 0; ts.tv_nsec = 0; i = kevent(kq, NULL, 0, &ke, 1, &ts); if (i == -1) fail(errno, "kevent", socktype, "EVFILT_READ"); OK("EVFILT_READ"); if (i != 0) fail_assertion("kevent", socktype, "EVFILT_READ", "empty socket unreadable"); OK("empty socket unreadable"); EV_SET(&ke, fd[0], EVFILT_READ, EV_DELETE, 0, 0, NULL); if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1) fail(errno, "kevent", socktype, "EVFILT_READ, EV_DELETE"); OK("EVFILT_READ, EV_DELETE"); } static void test_evfilt_write(int kq, int fd[2], const char *socktype) { struct timespec ts; struct kevent ke; ssize_t len; char ch; int i; EV_SET(&ke, fd[0], EVFILT_WRITE, EV_ADD, 0, 0, NULL); if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1) fail(errno, "kevent", socktype, "EVFILT_WRITE, EV_ADD"); OK("EVFILE_WRITE, EV_ADD"); /* * Confirm writable to begin with, no I/O yet. */ ts.tv_sec = 0; ts.tv_nsec = 0; i = kevent(kq, NULL, 0, &ke, 1, &ts); if (i == -1) fail(errno, "kevent", socktype, "EVFILT_WRITE"); OK("EVFILE_WRITE"); if (i != 1) fail_assertion("kevent", socktype, "EVFILT_WRITE", "empty socket unwritable"); OK("empty socket unwritable"); /* * Write bytes into the socket until we can't write anymore. */ ch = 'a'; while ((len = write(fd[0], &ch, sizeof(ch))) == sizeof(ch)) {}; if (len == -1 && errno != EAGAIN && errno != ENOBUFS) fail(errno, "write", socktype, NULL); OK("write"); if (len != -1 && len != sizeof(ch)) fail_assertion("write", socktype, NULL, "write length"); OK("write length"); /* * Check to make sure the socket is no longer writable. */ ts.tv_sec = 0; ts.tv_nsec = 0; i = kevent(kq, NULL, 0, &ke, 1, &ts); if (i == -1) fail(errno, "kevent", socktype, "EVFILT_WRITE"); OK("EVFILT_WRITE"); if (i != 0) fail_assertion("kevent", socktype, "EVFILT_WRITE", "full socket writable"); OK("full socket writable"); EV_SET(&ke, fd[0], EVFILT_WRITE, EV_DELETE, 0, 0, NULL); if (kevent(kq, &ke, 1, NULL, 0, NULL) == -1) fail(errno, "kevent", socktype, "EVFILT_WRITE, EV_DELETE"); OK("EVFILT_WRITE, EV_DELETE"); } /* * Basic registration exercise for kqueue(2). Create several types/brands of * sockets, and confirm that we can register for various events on them. */ int main(int argc, char *argv[]) { int i, kq, sv[2]; printf("1..49\n"); kq = kqueue(); if (kq == -1) fail(errno, "kqueue", NULL, NULL); OK("kqueue()"); /* * Create a UNIX domain datagram socket, and attach/test/detach a * read filter on it. */ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) == -1) fail(errno, "socketpair", "PF_UNIX, SOCK_DGRAM", NULL); OK("socketpair() 1"); if (fcntl(sv[0], F_SETFL, O_NONBLOCK) != 0) fail(errno, "fcntl", "PF_UNIX, SOCK_DGRAM", "O_NONBLOCK"); OK("fcntl() 1"); if (fcntl(sv[1], F_SETFL, O_NONBLOCK) != 0) fail(errno, "fcntl", "PF_UNIX, SOCK_DGRAM", "O_NONBLOCK"); OK("fnctl() 2"); test_evfilt_read(kq, sv, "PF_UNIX, SOCK_DGRAM"); if (close(sv[0]) == -1) fail(errno, "close", "PF_UNIX/SOCK_DGRAM", "sv[0]"); OK("close() 1"); if (close(sv[1]) == -1) fail(errno, "close", "PF_UNIX/SOCK_DGRAM", "sv[1]"); OK("close() 2"); #if 0 /* * XXXRW: We disable the write test in the case of datagram sockets, * as kqueue can't tell when the remote socket receive buffer is * full, whereas the UNIX domain socket implementation can tell and * returns ENOBUFS. */ /* * Create a UNIX domain datagram socket, and attach/test/detach a * write filter on it. */ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) == -1) fail(errno, "socketpair", "PF_UNIX, SOCK_DGRAM", NULL); if (fcntl(sv[0], F_SETFL, O_NONBLOCK) != 0) fail(errno, "fcntl", "PF_UNIX, SOCK_DGRAM", "O_NONBLOCK"); if (fcntl(sv[1], F_SETFL, O_NONBLOCK) != 0) fail(errno, "fcntl", "PF_UNIX, SOCK_DGRAM", "O_NONBLOCK"); test_evfilt_write(kq, sv, "PF_UNIX, SOCK_DGRAM"); if (close(sv[0]) == -1) fail(errno, "close", "PF_UNIX/SOCK_DGRAM", "sv[0]"); if (close(sv[1]) == -1) fail(errno, "close", "PF_UNIX/SOCK_DGRAM", "sv[1]"); #endif /* * Create a UNIX domain stream socket, and attach/test/detach a * read filter on it. */ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) fail(errno, "socketpair", "PF_UNIX, SOCK_STREAM", NULL); OK("socketpair() 2"); if (fcntl(sv[0], F_SETFL, O_NONBLOCK) != 0) fail(errno, "fcntl", "PF_UNIX, SOCK_STREAM", "O_NONBLOCK"); OK("fcntl() 3"); if (fcntl(sv[1], F_SETFL, O_NONBLOCK) != 0) fail(errno, "fcntl", "PF_UNIX, SOCK_STREAM", "O_NONBLOCK"); OK("fcntl() 4"); test_evfilt_read(kq, sv, "PF_UNIX, SOCK_STREAM"); if (close(sv[0]) == -1) fail(errno, "close", "PF_UNIX/SOCK_STREAM", "sv[0]"); OK("close() 3"); if (close(sv[1]) == -1) fail(errno, "close", "PF_UNIX/SOCK_STREAM", "sv[1]"); OK("close() 4"); /* * Create a UNIX domain stream socket, and attach/test/detach a * write filter on it. */ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) fail(errno, "socketpair", "PF_UNIX, SOCK_STREAM", NULL); OK("socketpair() 3"); if (fcntl(sv[0], F_SETFL, O_NONBLOCK) != 0) fail(errno, "fcntl", "PF_UNIX, SOCK_STREAM", "O_NONBLOCK"); OK("fcntl() 5"); if (fcntl(sv[1], F_SETFL, O_NONBLOCK) != 0) fail(errno, "fcntl", "PF_UNIX, SOCK_STREAM", "O_NONBLOCK"); OK("fcntl() 6"); test_evfilt_write(kq, sv, "PF_UNIX, SOCK_STREAM"); if (close(sv[0]) == -1) fail(errno, "close", "PF_UNIX/SOCK_STREAM", "sv[0]"); OK("close() 5"); if (close(sv[1]) == -1) fail(errno, "close", "PF_UNIX/SOCK_STREAM", "sv[1]"); OK("close() 6"); if (close(kq) == -1) fail(errno, "close", "kq", NULL); OK("close() 7"); return (0); }