1200483Srwatson/*
2200483Srwatson * Copyright (c) 2009 Mark Heily <mark@heily.com>
3200483Srwatson *
4200483Srwatson * Permission to use, copy, modify, and distribute this software for any
5200483Srwatson * purpose with or without fee is hereby granted, provided that the above
6200483Srwatson * copyright notice and this permission notice appear in all copies.
7200483Srwatson *
8200483Srwatson * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9200483Srwatson * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10200483Srwatson * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11200483Srwatson * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12200483Srwatson * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13200483Srwatson * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14200483Srwatson * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15200483Srwatson *
16200483Srwatson * $FreeBSD: stable/10/tests/sys/kqueue/libkqueue/main.c 313507 2017-02-10 02:29:09Z ngie $
17200483Srwatson */
18200483Srwatson
19200483Srwatson#include <sys/types.h>
20200483Srwatson
21223845Sjonathan#include "config.h"
22200483Srwatson#include "common.h"
23200483Srwatson
24200483Srwatsonint testnum = 1;
25200483Srwatsonchar *cur_test_id = NULL;
26200483Srwatsonint kqfd;
27200483Srwatson
28200483Srwatsonextern void test_evfilt_read();
29200483Srwatsonextern void test_evfilt_signal();
30200483Srwatsonextern void test_evfilt_vnode();
31200483Srwatsonextern void test_evfilt_timer();
32200483Srwatsonextern void test_evfilt_proc();
33200483Srwatson#if HAVE_EVFILT_USER
34200483Srwatsonextern void test_evfilt_user();
35200483Srwatson#endif
36200483Srwatson
37200483Srwatson/* Checks if any events are pending, which is an error. */
38200483Srwatsonvoid
39200483Srwatsontest_no_kevents(void)
40200483Srwatson{
41200483Srwatson    int nfds;
42200483Srwatson    struct timespec timeo;
43200483Srwatson    struct kevent kev;
44200483Srwatson
45200483Srwatson    puts("confirming that there are no events pending");
46200483Srwatson    memset(&timeo, 0, sizeof(timeo));
47200483Srwatson    nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
48200483Srwatson    if (nfds != 0) {
49200483Srwatson        puts("\nUnexpected event:");
50200483Srwatson        puts(kevent_to_str(&kev));
51200483Srwatson        errx(1, "%d event(s) pending, but none expected:", nfds);
52200483Srwatson    }
53200483Srwatson}
54200483Srwatson
55200483Srwatson/* Retrieve a single kevent */
56200483Srwatsonstruct kevent *
57200483Srwatsonkevent_get(int kqfd)
58200483Srwatson{
59200483Srwatson    int nfds;
60200483Srwatson    struct kevent *kev;
61200483Srwatson
62200483Srwatson    if ((kev = calloc(1, sizeof(*kev))) == NULL)
63200483Srwatson	err(1, "out of memory");
64200483Srwatson
65200483Srwatson    nfds = kevent(kqfd, NULL, 0, kev, 1, NULL);
66200483Srwatson    if (nfds < 1)
67200483Srwatson        err(1, "kevent(2)");
68200483Srwatson
69200483Srwatson    return (kev);
70200483Srwatson}
71200483Srwatson
72295012Svangyzen/* Retrieve a single kevent, specifying a maximum time to wait for it. */
73295012Svangyzenstruct kevent *
74295012Svangyzenkevent_get_timeout(int kqfd, int seconds)
75295012Svangyzen{
76295012Svangyzen    int nfds;
77295012Svangyzen    struct kevent *kev;
78295012Svangyzen    struct timespec timeout = {seconds, 0};
79295012Svangyzen
80295012Svangyzen    if ((kev = calloc(1, sizeof(*kev))) == NULL)
81295012Svangyzen	err(1, "out of memory");
82295012Svangyzen
83295012Svangyzen    nfds = kevent(kqfd, NULL, 0, kev, 1, &timeout);
84295012Svangyzen    if (nfds < 0) {
85295012Svangyzen        err(1, "kevent(2)");
86295012Svangyzen    } else if (nfds == 0) {
87295012Svangyzen        free(kev);
88295012Svangyzen        kev = NULL;
89295012Svangyzen    }
90295012Svangyzen
91295012Svangyzen    return (kev);
92295012Svangyzen}
93295012Svangyzen
94200483Srwatsonchar *
95200483Srwatsonkevent_fflags_dump(struct kevent *kev)
96200483Srwatson{
97200483Srwatson    char *buf;
98200483Srwatson
99200483Srwatson#define KEVFFL_DUMP(attrib) \
100200483Srwatson    if (kev->fflags & attrib) \
101200483Srwatson	strncat(buf, #attrib" ", 64);
102200483Srwatson
103200483Srwatson    if ((buf = calloc(1, 1024)) == NULL)
104200483Srwatson	abort();
105200483Srwatson
106200483Srwatson    /* Not every filter has meaningful fflags */
107295012Svangyzen    if (kev->filter == EVFILT_PROC) {
108295012Svangyzen        snprintf(buf, 1024, "fflags = %x (", kev->fflags);
109295012Svangyzen        KEVFFL_DUMP(NOTE_EXIT);
110295012Svangyzen        KEVFFL_DUMP(NOTE_FORK);
111295012Svangyzen        KEVFFL_DUMP(NOTE_EXEC);
112295012Svangyzen        KEVFFL_DUMP(NOTE_CHILD);
113295012Svangyzen        KEVFFL_DUMP(NOTE_TRACKERR);
114295012Svangyzen        KEVFFL_DUMP(NOTE_TRACK);
115295012Svangyzen        buf[strlen(buf) - 1] = ')';
116295012Svangyzen    } else if (kev->filter == EVFILT_VNODE) {
117295012Svangyzen        snprintf(buf, 1024, "fflags = %x (", kev->fflags);
118295012Svangyzen        KEVFFL_DUMP(NOTE_DELETE);
119295012Svangyzen        KEVFFL_DUMP(NOTE_WRITE);
120295012Svangyzen        KEVFFL_DUMP(NOTE_EXTEND);
121200483Srwatson#if HAVE_NOTE_TRUNCATE
122295012Svangyzen        KEVFFL_DUMP(NOTE_TRUNCATE);
123200483Srwatson#endif
124295012Svangyzen        KEVFFL_DUMP(NOTE_ATTRIB);
125295012Svangyzen        KEVFFL_DUMP(NOTE_LINK);
126295012Svangyzen        KEVFFL_DUMP(NOTE_RENAME);
127200483Srwatson#if HAVE_NOTE_REVOKE
128295012Svangyzen        KEVFFL_DUMP(NOTE_REVOKE);
129200483Srwatson#endif
130295012Svangyzen        buf[strlen(buf) - 1] = ')';
131295012Svangyzen    } else {
132295012Svangyzen    	snprintf(buf, 1024, "fflags = %x", kev->fflags);
133295012Svangyzen    }
134200483Srwatson
135200483Srwatson    return (buf);
136200483Srwatson}
137200483Srwatson
138200483Srwatsonchar *
139200483Srwatsonkevent_flags_dump(struct kevent *kev)
140200483Srwatson{
141200483Srwatson    char *buf;
142200483Srwatson
143200483Srwatson#define KEVFL_DUMP(attrib) \
144200483Srwatson    if (kev->flags & attrib) \
145200483Srwatson	strncat(buf, #attrib" ", 64);
146200483Srwatson
147200483Srwatson    if ((buf = calloc(1, 1024)) == NULL)
148200483Srwatson	abort();
149200483Srwatson
150200483Srwatson    snprintf(buf, 1024, "flags = %d (", kev->flags);
151200483Srwatson    KEVFL_DUMP(EV_ADD);
152200483Srwatson    KEVFL_DUMP(EV_ENABLE);
153200483Srwatson    KEVFL_DUMP(EV_DISABLE);
154200483Srwatson    KEVFL_DUMP(EV_DELETE);
155200483Srwatson    KEVFL_DUMP(EV_ONESHOT);
156200483Srwatson    KEVFL_DUMP(EV_CLEAR);
157200483Srwatson    KEVFL_DUMP(EV_EOF);
158200483Srwatson    KEVFL_DUMP(EV_ERROR);
159200483Srwatson#if HAVE_EV_DISPATCH
160200483Srwatson    KEVFL_DUMP(EV_DISPATCH);
161200483Srwatson#endif
162200483Srwatson#if HAVE_EV_RECEIPT
163200483Srwatson    KEVFL_DUMP(EV_RECEIPT);
164200483Srwatson#endif
165200483Srwatson    buf[strlen(buf) - 1] = ')';
166200483Srwatson
167200483Srwatson    return (buf);
168200483Srwatson}
169200483Srwatson
170200483Srwatson/* Copied from ../kevent.c kevent_dump() and improved */
171200483Srwatsonconst char *
172200483Srwatsonkevent_to_str(struct kevent *kev)
173200483Srwatson{
174200483Srwatson    char buf[512];
175200483Srwatson
176200483Srwatson    snprintf(&buf[0], sizeof(buf),
177200483Srwatson            "[ident=%d, filter=%d, %s, %s, data=%d, udata=%p]",
178200483Srwatson            (u_int) kev->ident,
179200483Srwatson            kev->filter,
180200483Srwatson            kevent_flags_dump(kev),
181200483Srwatson            kevent_fflags_dump(kev),
182200483Srwatson            (int) kev->data,
183200483Srwatson            kev->udata);
184200483Srwatson
185200483Srwatson    return (strdup(buf));
186200483Srwatson}
187200483Srwatson
188200483Srwatsonvoid
189200483Srwatsonkevent_add(int kqfd, struct kevent *kev,
190200483Srwatson        uintptr_t ident,
191200483Srwatson        short     filter,
192200483Srwatson        u_short   flags,
193200483Srwatson        u_int     fflags,
194200483Srwatson        intptr_t  data,
195200483Srwatson        void      *udata)
196200483Srwatson{
197200483Srwatson    EV_SET(kev, ident, filter, flags, fflags, data, NULL);
198200483Srwatson    if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) {
199200483Srwatson        printf("Unable to add the following kevent:\n%s\n",
200200483Srwatson                kevent_to_str(kev));
201200483Srwatson        err(1, "kevent(): %s", strerror(errno));
202200483Srwatson    }
203200483Srwatson}
204200483Srwatson
205200483Srwatsonvoid
206200483Srwatsonkevent_cmp(struct kevent *k1, struct kevent *k2)
207200483Srwatson{
208200483Srwatson/* XXX-
209200483Srwatson   Workaround for inconsistent implementation of kevent(2)
210200483Srwatson */
211200483Srwatson#ifdef __FreeBSD__
212200483Srwatson    if (k1->flags & EV_ADD)
213200483Srwatson        k2->flags |= EV_ADD;
214200483Srwatson#endif
215200483Srwatson    if (memcmp(k1, k2, sizeof(*k1)) != 0) {
216200483Srwatson        printf("kevent_cmp: mismatch:\n  %s !=\n  %s\n",
217200483Srwatson              kevent_to_str(k1), kevent_to_str(k2));
218200483Srwatson        abort();
219200483Srwatson    }
220200483Srwatson}
221200483Srwatson
222200483Srwatsonvoid
223200483Srwatsontest_begin(const char *func)
224200483Srwatson{
225200483Srwatson    if (cur_test_id)
226200483Srwatson        free(cur_test_id);
227200483Srwatson    cur_test_id = strdup(func);
228200483Srwatson    if (!cur_test_id)
229200483Srwatson        err(1, "strdup failed");
230200483Srwatson
231200483Srwatson    printf("\n\nTest %d: %s\n", testnum++, func);
232200483Srwatson}
233200483Srwatson
234200483Srwatsonvoid
235200483Srwatsonsuccess(void)
236200483Srwatson{
237200483Srwatson    printf("%-70s %s\n", cur_test_id, "passed");
238200483Srwatson    free(cur_test_id);
239200483Srwatson    cur_test_id = NULL;
240200483Srwatson}
241200483Srwatson
242200483Srwatsonvoid
243200483Srwatsontest_kqueue(void)
244200483Srwatson{
245200483Srwatson    test_begin("kqueue()");
246200483Srwatson    if ((kqfd = kqueue()) < 0)
247200483Srwatson        err(1, "kqueue()");
248200483Srwatson    test_no_kevents();
249200483Srwatson    success();
250200483Srwatson}
251200483Srwatson
252200483Srwatsonvoid
253200483Srwatsontest_kqueue_close(void)
254200483Srwatson{
255200483Srwatson    test_begin("close(kq)");
256200483Srwatson    if (close(kqfd) < 0)
257200483Srwatson        err(1, "close()");
258200483Srwatson    success();
259200483Srwatson}
260200483Srwatson
261200483Srwatsonint
262200483Srwatsonmain(int argc, char **argv)
263200483Srwatson{
264223845Sjonathan    int test_proc = 1;
265200483Srwatson    int test_socket = 1;
266200483Srwatson    int test_signal = 1;
267200483Srwatson    int test_vnode = 1;
268200483Srwatson    int test_timer = 1;
269200573Srwatson#ifdef __FreeBSD__
270200483Srwatson    int test_user = 1;
271200573Srwatson#else
272200573Srwatson    /* XXX-FIXME temporary */
273200573Srwatson    int test_user = 0;
274200573Srwatson#endif
275200483Srwatson
276200483Srwatson    while (argc) {
277200483Srwatson        if (strcmp(argv[0], "--no-proc") == 0)
278200483Srwatson            test_proc = 0;
279200483Srwatson        if (strcmp(argv[0], "--no-socket") == 0)
280200483Srwatson            test_socket = 0;
281200483Srwatson        if (strcmp(argv[0], "--no-timer") == 0)
282200483Srwatson            test_timer = 0;
283200483Srwatson        if (strcmp(argv[0], "--no-signal") == 0)
284200483Srwatson            test_signal = 0;
285200483Srwatson        if (strcmp(argv[0], "--no-vnode") == 0)
286200483Srwatson            test_vnode = 0;
287200483Srwatson        if (strcmp(argv[0], "--no-user") == 0)
288200483Srwatson            test_user = 0;
289200483Srwatson        argv++;
290200483Srwatson        argc--;
291200483Srwatson    }
292200483Srwatson
293295012Svangyzen    /*
294295012Svangyzen     * Some tests fork.  If output is fully buffered,
295295012Svangyzen     * the children inherit some buffered data and flush
296295012Svangyzen     * it when they exit, causing some data to be printed twice.
297295012Svangyzen     * Use line buffering to avoid this problem.
298295012Svangyzen     */
299295012Svangyzen    setlinebuf(stdout);
300295012Svangyzen    setlinebuf(stderr);
301295012Svangyzen
302200483Srwatson    test_kqueue();
303200483Srwatson    test_kqueue_close();
304200483Srwatson
305200483Srwatson    if (test_socket)
306200483Srwatson        test_evfilt_read();
307200483Srwatson    if (test_signal)
308200483Srwatson        test_evfilt_signal();
309200483Srwatson    if (test_vnode)
310200483Srwatson        test_evfilt_vnode();
311200483Srwatson#if HAVE_EVFILT_USER
312200483Srwatson    if (test_user)
313200483Srwatson        test_evfilt_user();
314200483Srwatson#endif
315200483Srwatson    if (test_timer)
316200483Srwatson        test_evfilt_timer();
317200483Srwatson    if (test_proc)
318200483Srwatson        test_evfilt_proc();
319200483Srwatson
320200483Srwatson    printf("\n---\n"
321200483Srwatson            "+OK All %d tests completed.\n", testnum - 1);
322200483Srwatson    return (0);
323200483Srwatson}
324