1/*
2 * Copyright (c) 2009 Mark Heily <mark@heily.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "common.h"
18
19extern int kqfd;
20
21/* Checks if any events are pending, which is an error. */
22void
23test_no_kevents(int kqfd)
24{
25    int nfds;
26    struct timespec timeo;
27    struct kevent kev;
28
29    memset(&timeo, 0, sizeof(timeo));
30    nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
31    if (nfds < 0)
32        die("kevent(2)");
33    if (nfds > 0) {
34        puts("\nUnexpected event:");
35        die(kevent_to_str(&kev));
36    }
37}
38
39/* Retrieve a single kevent */
40struct kevent *
41kevent_get(int kqfd)
42{
43    int nfds;
44    static struct kevent __thread kev;
45
46    nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
47    if (nfds < 1)
48        die("kevent(2)");
49
50    return (&kev);
51}
52
53char *
54kevent_fflags_dump(struct kevent *kev)
55{
56    char *buf;
57
58#define KEVFFL_DUMP(attrib) \
59    if (kev->fflags & attrib) \
60	strncat(buf, #attrib" ", 64);
61
62    if ((buf = calloc(1, 1024)) == NULL)
63	abort();
64
65    /* Not every filter has meaningful fflags */
66    if (kev->filter != EVFILT_VNODE) {
67    	snprintf(buf, 1024, "fflags = %d", kev->fflags);
68	return (buf);
69    }
70
71    snprintf(buf, 1024, "fflags = %d (", kev->fflags);
72    KEVFFL_DUMP(NOTE_DELETE);
73    KEVFFL_DUMP(NOTE_WRITE);
74    KEVFFL_DUMP(NOTE_EXTEND);
75#if HAVE_NOTE_TRUNCATE
76    KEVFFL_DUMP(NOTE_TRUNCATE);
77#endif
78    KEVFFL_DUMP(NOTE_ATTRIB);
79    KEVFFL_DUMP(NOTE_LINK);
80    KEVFFL_DUMP(NOTE_RENAME);
81#if HAVE_NOTE_REVOKE
82    KEVFFL_DUMP(NOTE_REVOKE);
83#endif
84    buf[strlen(buf) - 1] = ')';
85
86    return (buf);
87}
88
89char *
90kevent_flags_dump(struct kevent *kev)
91{
92    char *buf;
93
94#define KEVFL_DUMP(attrib) \
95    if (kev->flags & attrib) \
96	strncat(buf, #attrib" ", 64);
97
98    if ((buf = calloc(1, 1024)) == NULL)
99	abort();
100
101    snprintf(buf, 1024, "flags = %d (", kev->flags);
102    KEVFL_DUMP(EV_ADD);
103    KEVFL_DUMP(EV_ENABLE);
104    KEVFL_DUMP(EV_DISABLE);
105    KEVFL_DUMP(EV_DELETE);
106    KEVFL_DUMP(EV_ONESHOT);
107    KEVFL_DUMP(EV_CLEAR);
108    KEVFL_DUMP(EV_EOF);
109    KEVFL_DUMP(EV_ERROR);
110#if HAVE_EV_DISPATCH
111    KEVFL_DUMP(EV_DISPATCH);
112#endif
113#if HAVE_EV_RECEIPT
114    KEVFL_DUMP(EV_RECEIPT);
115#endif
116    buf[strlen(buf) - 1] = ')';
117
118    return (buf);
119}
120
121/* TODO - backport changes from src/common/kevent.c kevent_dump() */
122const char *
123kevent_to_str(struct kevent *kev)
124{
125    char buf[512];
126
127    snprintf(&buf[0], sizeof(buf),
128            "[ident=%d, filter=%d, %s, %s, data=%d, udata=%p]",
129            (u_int) kev->ident,
130            kev->filter,
131            kevent_flags_dump(kev),
132            kevent_fflags_dump(kev),
133            (int) kev->data,
134            kev->udata);
135
136    return (strdup(buf));
137}
138
139void
140kevent_update(int kqfd, struct kevent *kev)
141{
142    if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) {
143        printf("Unable to add the following kevent:\n%s\n",
144                kevent_to_str(kev));
145        die("kevent");
146    }
147}
148
149void
150kevent_add(int kqfd, struct kevent *kev,
151        uintptr_t ident,
152        short     filter,
153        u_short   flags,
154        u_int     fflags,
155        intptr_t  data,
156        void      *udata)
157{
158    EV_SET(kev, ident, filter, flags, fflags, data, NULL);
159    if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) {
160        printf("Unable to add the following kevent:\n%s\n",
161                kevent_to_str(kev));
162        die("kevent");
163    }
164}
165
166void
167kevent_cmp(struct kevent *k1, struct kevent *k2)
168{
169/* XXX-
170   Workaround for inconsistent implementation of kevent(2)
171 */
172#if defined (__FreeBSD_kernel__) || defined (__FreeBSD__)
173    if (k1->flags & EV_ADD)
174        k2->flags |= EV_ADD;
175#endif
176    if (memcmp(k1, k2, sizeof(*k1)) != 0) {
177        printf("kevent_cmp: mismatch:\n  expected %s\n  but got  %s\n",
178              kevent_to_str(k1), kevent_to_str(k2));
179        abort();
180    }
181}
182