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
19 int vnode_fd;
20
21 static void
test_kevent_vnode_add(void)22 test_kevent_vnode_add(void)
23 {
24 const char *test_id = "kevent(EVFILT_VNODE, EV_ADD)";
25 const char *testfile = "./kqueue-test.tmp";
26 struct kevent kev;
27
28 test_begin(test_id);
29
30 system("touch ./kqueue-test.tmp");
31 vnode_fd = open(testfile, O_RDONLY);
32 if (vnode_fd < 0)
33 err(1, "open of %s", testfile);
34 else
35 printf("vnode_fd = %d\n", vnode_fd);
36
37 EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD,
38 NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME | NOTE_DELETE, 0, NULL);
39 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
40 err(1, "%s", test_id);
41
42 success();
43 }
44
45 static void
test_kevent_vnode_note_delete(void)46 test_kevent_vnode_note_delete(void)
47 {
48 const char *test_id = "kevent(EVFILT_VNODE, NOTE_DELETE)";
49 struct kevent kev;
50
51 test_begin(test_id);
52
53 EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL);
54 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
55 err(1, "%s", test_id);
56
57 if (unlink("./kqueue-test.tmp") < 0)
58 err(1, "unlink");
59
60 kevent_cmp(&kev, kevent_get(kqfd));
61
62 success();
63 }
64
65 static void
test_kevent_vnode_note_delete_fifo(void)66 test_kevent_vnode_note_delete_fifo(void)
67 {
68 const char *test_id = "kevent(EVFILT_VNODE, NOTE_DELETE, FIFO)";
69 const char *fifo_path = "./kqueue-fifo.tmp";
70 struct kevent kev;
71 int fd;
72 pid_t pid;
73
74 test_begin(test_id);
75
76 if (mkfifo(fifo_path, 0600) != 0)
77 err(1, "mkfifo");
78
79 pid = fork();
80 if (pid == -1)
81 err(1, "fork");
82
83 if (pid == 0) {
84 char buf[4];
85
86 fd = open(fifo_path, O_RDONLY);
87 if (fd == -1)
88 _exit(1);
89
90 while (read(fd, buf, sizeof(buf)) != 0) {
91 }
92
93 _exit(0);
94 }
95
96 sleep(1);
97 if (waitpid(pid, NULL, WNOHANG) == pid) {
98 unlink(fifo_path);
99 err(1, "open");
100 }
101
102 fd = open(fifo_path, O_WRONLY);
103 if (fd < 0) {
104 unlink(fifo_path);
105 err(1, "open");
106 }
107
108 EV_SET(&kev, fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL);
109 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) {
110 unlink(fifo_path);
111 err(1, "%s", test_id);
112 }
113
114 if (unlink(fifo_path) < 0)
115 err(1, "unlink");
116
117 kevent_cmp(&kev, kevent_get(kqfd));
118 close(fd);
119
120 success();
121 }
122
123 static void
test_kevent_vnode_note_write(void)124 test_kevent_vnode_note_write(void)
125 {
126 const char *test_id = "kevent(EVFILT_VNODE, NOTE_WRITE)";
127 struct kevent kev;
128
129 test_begin(test_id);
130
131 EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_WRITE, 0, NULL);
132 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
133 err(1, "%s", test_id);
134
135 if (system("echo hello >> ./kqueue-test.tmp") < 0)
136 err(1, "system");
137
138 /* BSD kqueue adds NOTE_EXTEND even though it was not requested */
139 /* BSD kqueue removes EV_ENABLE */
140 kev.flags &= ~EV_ENABLE; // XXX-FIXME compatibility issue
141 kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue
142 kevent_cmp(&kev, kevent_get(kqfd));
143
144 success();
145 }
146
147 static void
test_kevent_vnode_note_attrib(void)148 test_kevent_vnode_note_attrib(void)
149 {
150 const char *test_id = "kevent(EVFILT_VNODE, NOTE_ATTRIB)";
151 struct kevent kev;
152 int nfds;
153
154 test_begin(test_id);
155
156 EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
157 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
158 err(1, "%s", test_id);
159
160 if (system("touch ./kqueue-test.tmp") < 0)
161 err(1, "system");
162
163 nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
164 if (nfds < 1)
165 err(1, "%s", test_id);
166 if (kev.ident != (uintptr_t)vnode_fd ||
167 kev.filter != EVFILT_VNODE ||
168 kev.fflags != NOTE_ATTRIB)
169 err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
170 test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
171
172 success();
173 }
174
175 static void
test_kevent_vnode_note_rename(void)176 test_kevent_vnode_note_rename(void)
177 {
178 const char *test_id = "kevent(EVFILT_VNODE, NOTE_RENAME)";
179 struct kevent kev;
180 int nfds;
181
182 test_begin(test_id);
183
184 EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_RENAME, 0, NULL);
185 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
186 err(1, "%s", test_id);
187
188 if (system("mv ./kqueue-test.tmp ./kqueue-test2.tmp") < 0)
189 err(1, "system");
190
191 nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
192 if (nfds < 1)
193 err(1, "%s", test_id);
194 if (kev.ident != (uintptr_t)vnode_fd ||
195 kev.filter != EVFILT_VNODE ||
196 kev.fflags != NOTE_RENAME)
197 err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
198 test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
199
200 if (system("mv ./kqueue-test2.tmp ./kqueue-test.tmp") < 0)
201 err(1, "system");
202
203 success();
204 }
205
206 static void
test_kevent_vnode_del(void)207 test_kevent_vnode_del(void)
208 {
209 const char *test_id = "kevent(EVFILT_VNODE, EV_DELETE)";
210 struct kevent kev;
211
212 test_begin(test_id);
213
214 EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL);
215 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
216 err(1, "%s", test_id);
217
218 success();
219 }
220
221 static void
test_kevent_vnode_disable_and_enable(void)222 test_kevent_vnode_disable_and_enable(void)
223 {
224 const char *test_id = "kevent(EVFILT_VNODE, EV_DISABLE and EV_ENABLE)";
225 struct kevent kev;
226 int nfds;
227
228 test_begin(test_id);
229
230 test_no_kevents();
231
232 /* Add the watch and immediately disable it */
233 EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL);
234 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
235 err(1, "%s", test_id);
236 kev.flags = EV_DISABLE;
237 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
238 err(1, "%s", test_id);
239
240 /* Confirm that the watch is disabled */
241 if (system("touch ./kqueue-test.tmp") < 0)
242 err(1, "system");
243 test_no_kevents();
244
245 /* Re-enable and check again */
246 kev.flags = EV_ENABLE;
247 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
248 err(1, "%s", test_id);
249 if (system("touch ./kqueue-test.tmp") < 0)
250 err(1, "system");
251 nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
252 if (nfds < 1)
253 err(1, "%s", test_id);
254 if (kev.ident != (uintptr_t)vnode_fd ||
255 kev.filter != EVFILT_VNODE ||
256 kev.fflags != NOTE_ATTRIB)
257 err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
258 test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
259
260 success();
261 }
262
263 #if HAVE_EV_DISPATCH
264 static void
test_kevent_vnode_dispatch(void)265 test_kevent_vnode_dispatch(void)
266 {
267 const char *test_id = "kevent(EVFILT_VNODE, EV_DISPATCH)";
268 struct kevent kev;
269 int nfds;
270
271 test_begin(test_id);
272
273 test_no_kevents();
274
275 EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_DISPATCH, NOTE_ATTRIB, 0, NULL);
276 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
277 err(1, "%s", test_id);
278
279 if (system("touch ./kqueue-test.tmp") < 0)
280 err(1, "system");
281
282 nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
283 if (nfds < 1)
284 err(1, "%s", test_id);
285 if (kev.ident != (uintptr_t)vnode_fd ||
286 kev.filter != EVFILT_VNODE ||
287 kev.fflags != NOTE_ATTRIB)
288 err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
289 test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
290
291 /* Confirm that the watch is disabled automatically */
292 puts("-- checking that watch is disabled");
293 if (system("touch ./kqueue-test.tmp") < 0)
294 err(1, "system");
295 test_no_kevents();
296
297 /* Delete the watch */
298 EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, NOTE_ATTRIB, 0, NULL);
299 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
300 err(1, "remove watch failed: %s", test_id);
301
302 success();
303 }
304 #endif /* HAVE_EV_DISPATCH */
305
306 void
test_evfilt_vnode(void)307 test_evfilt_vnode(void)
308 {
309 kqfd = kqueue();
310 test_kevent_vnode_add();
311 test_kevent_vnode_del();
312 test_kevent_vnode_disable_and_enable();
313 #if HAVE_EV_DISPATCH
314 test_kevent_vnode_dispatch();
315 #endif
316 test_kevent_vnode_note_write();
317 test_kevent_vnode_note_attrib();
318 test_kevent_vnode_note_rename();
319 test_kevent_vnode_note_delete();
320 test_kevent_vnode_note_delete_fifo();
321 close(kqfd);
322 }
323