1 /*
2 * Copyright (c) 2009 Mark Heily <[email protected]>
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 * $FreeBSD$
17 */
18
19 #include <sys/stat.h>
20
21 #include <err.h>
22
23 #include "config.h"
24 #include "common.h"
25
26 static int sigusr1_caught = 0;
27
28 int kqfd;
29
30 static void
sig_handler(int signum)31 sig_handler(int signum)
32 {
33 sigusr1_caught = 1;
34 }
35
36 static void
add_and_delete(void)37 add_and_delete(void)
38 {
39 struct kevent kev;
40 pid_t pid;
41
42 /* Create a child that waits to be killed and then exits */
43 pid = fork();
44 if (pid == 0) {
45 struct stat s;
46 if (fstat(kqfd, &s) != -1)
47 errx(1, "kqueue inherited across fork! (%s() at %s:%d)",
48 __func__, __FILE__, __LINE__);
49
50 pause();
51 exit(2);
52 }
53 printf(" -- child created (pid %d)\n", (int) pid);
54
55 test_begin("kevent(EVFILT_PROC, EV_ADD)");
56
57 test_no_kevents();
58 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
59 test_no_kevents();
60
61 success();
62
63 test_begin("kevent(EVFILT_PROC, EV_DELETE)");
64
65 sleep(1);
66 test_no_kevents();
67 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
68 if (kill(pid, SIGKILL) < 0)
69 err(1, "kill");
70 sleep(1);
71 test_no_kevents();
72
73 success();
74
75 }
76
77 static void
proc_track(int sleep_time)78 proc_track(int sleep_time)
79 {
80 char test_id[64];
81 struct kevent kev;
82 pid_t pid;
83 int pipe_fd[2];
84 ssize_t result;
85
86 snprintf(test_id, sizeof(test_id),
87 "kevent(EVFILT_PROC, NOTE_TRACK); sleep %d", sleep_time);
88 test_begin(test_id);
89 test_no_kevents();
90
91 if (pipe(pipe_fd)) {
92 err(1, "pipe (parent) failed! (%s() at %s:%d)",
93 __func__, __FILE__, __LINE__);
94 }
95
96 /* Create a child to track. */
97 pid = fork();
98 if (pid == 0) { /* Child */
99 pid_t grandchild = -1;
100
101 /*
102 * Give the parent a chance to start tracking us.
103 */
104 result = read(pipe_fd[1], test_id, 1);
105 if (result != 1) {
106 err(1, "read from pipe in child failed! (ret %zd) (%s() at %s:%d)",
107 result, __func__, __FILE__, __LINE__);
108 }
109
110 /*
111 * Spawn a grandchild that will immediately exit. If the kernel has bug
112 * 180385, the parent will see a kevent with both NOTE_CHILD and
113 * NOTE_EXIT. If that bug is fixed, it will see two separate kevents
114 * for those notes. Note that this triggers the conditions for
115 * detecting the bug quite reliably on a 1 CPU system (or if the test
116 * process is restricted to a single CPU), but may not trigger it on a
117 * multi-CPU system.
118 */
119 grandchild = fork();
120 if (grandchild == 0) { /* Grandchild */
121 if (sleep_time) sleep(sleep_time);
122 exit(1);
123 } else if (grandchild == -1) { /* Error */
124 err(1, "fork (grandchild) failed! (%s() at %s:%d)",
125 __func__, __FILE__, __LINE__);
126 } else { /* Child (Grandchild Parent) */
127 printf(" -- grandchild created (pid %d)\n", (int) grandchild);
128 }
129 if (sleep_time) sleep(sleep_time);
130 exit(0);
131 } else if (pid == -1) { /* Error */
132 err(1, "fork (child) failed! (%s() at %s:%d)",
133 __func__, __FILE__, __LINE__);
134 }
135
136 printf(" -- child created (pid %d)\n", (int) pid);
137
138 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE,
139 NOTE_TRACK | NOTE_EXEC | NOTE_EXIT | NOTE_FORK,
140 0, NULL);
141
142 printf(" -- tracking child (pid %d)\n", (int) pid);
143
144 /* Now that we're tracking the child, tell it to proceed. */
145 result = write(pipe_fd[0], test_id, 1);
146 if (result != 1) {
147 err(1, "write to pipe in parent failed! (ret %zd) (%s() at %s:%d)",
148 result, __func__, __FILE__, __LINE__);
149 }
150
151 /*
152 * Several events should be received:
153 * - NOTE_FORK (from child)
154 * - NOTE_CHILD (from grandchild)
155 * - NOTE_EXIT (from grandchild)
156 * - NOTE_EXIT (from child)
157 *
158 * The NOTE_FORK and NOTE_EXIT from the child could be combined into a
159 * single event, but the NOTE_CHILD and NOTE_EXIT from the grandchild must
160 * not be combined.
161 *
162 * The loop continues until no events are received within a 5 second
163 * period, at which point it is assumed that no more will be coming. The
164 * loop is deliberately designed to attempt to get events even after all
165 * the expected ones are received in case some spurious events are
166 * generated as well as the expected ones.
167 */
168 {
169 int child_exit = 0;
170 int child_fork = 0;
171 int gchild_exit = 0;
172 int gchild_note = 0;
173 pid_t gchild_pid = -1;
174 int done = 0;
175 char *kev_str;
176
177 while (!done)
178 {
179 int handled = 0;
180 struct kevent *kevp;
181
182 kevp = kevent_get_timeout(kqfd, 5);
183 if (kevp == NULL) {
184 done = 1;
185 } else {
186 kev_str = kevent_to_str(kevp);
187 printf(" -- Received kevent: %s\n", kev_str);
188 free(kev_str);
189
190 if ((kevp->fflags & NOTE_CHILD) && (kevp->fflags & NOTE_EXIT)) {
191 errx(1, "NOTE_CHILD and NOTE_EXIT in same kevent: %s", kevent_to_str(kevp));
192 }
193
194 if (kevp->fflags & NOTE_CHILD) {
195 if (kevp->data == pid) {
196 if (!gchild_note) {
197 ++gchild_note;
198 gchild_pid = kevp->ident;
199 ++handled;
200 } else {
201 errx(1, "Spurious NOTE_CHILD: %s", kevent_to_str(kevp));
202 }
203 }
204 }
205
206 if (kevp->fflags & NOTE_EXIT) {
207 if ((kevp->ident == pid) && (!child_exit)) {
208 ++child_exit;
209 ++handled;
210 } else if ((kevp->ident == gchild_pid) && (!gchild_exit)) {
211 ++gchild_exit;
212 ++handled;
213 } else {
214 errx(1, "Spurious NOTE_EXIT: %s", kevent_to_str(kevp));
215 }
216 }
217
218 if (kevp->fflags & NOTE_FORK) {
219 if ((kevp->ident == pid) && (!child_fork)) {
220 ++child_fork;
221 ++handled;
222 } else {
223 errx(1, "Spurious NOTE_FORK: %s", kevent_to_str(kevp));
224 }
225 }
226
227 if (!handled) {
228 errx(1, "Spurious kevent: %s", kevent_to_str(kevp));
229 }
230
231 free(kevp);
232 }
233 }
234
235 /* Make sure all expected events were received. */
236 if (child_exit && child_fork && gchild_exit && gchild_note) {
237 printf(" -- Received all expected events.\n");
238 } else {
239 errx(1, "Did not receive all expected events.");
240 }
241 }
242
243 success();
244 }
245
246 #ifdef TODO
247 static void
event_trigger(void)248 event_trigger(void)
249 {
250 struct kevent kev;
251 pid_t pid;
252
253 test_begin("kevent(EVFILT_PROC, wait)");
254
255 /* Create a child that waits to be killed and then exits */
256 pid = fork();
257 if (pid == 0) {
258 pause();
259 printf(" -- child caught signal, exiting\n");
260 exit(2);
261 }
262 printf(" -- child created (pid %d)\n", (int) pid);
263
264 test_no_kevents();
265 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
266
267 /* Cause the child to exit, then retrieve the event */
268 printf(" -- killing process %d\n", (int) pid);
269 if (kill(pid, SIGUSR1) < 0)
270 err(1, "kill");
271 kevent_cmp(&kev, kevent_get(kqfd));
272 test_no_kevents();
273
274 success();
275 }
276
277 void
test_kevent_signal_disable(void)278 test_kevent_signal_disable(void)
279 {
280 const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)";
281 struct kevent kev;
282
283 test_begin(test_id);
284
285 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL);
286 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
287 err(1, "%s", test_id);
288
289 /* Block SIGUSR1, then send it to ourselves */
290 sigset_t mask;
291 sigemptyset(&mask);
292 sigaddset(&mask, SIGUSR1);
293 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
294 err(1, "sigprocmask");
295 if (kill(getpid(), SIGKILL) < 0)
296 err(1, "kill");
297
298 test_no_kevents();
299
300 success();
301 }
302
303 void
test_kevent_signal_enable(void)304 test_kevent_signal_enable(void)
305 {
306 const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)";
307 struct kevent kev;
308
309 test_begin(test_id);
310
311 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL);
312 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
313 err(1, "%s", test_id);
314
315 /* Block SIGUSR1, then send it to ourselves */
316 sigset_t mask;
317 sigemptyset(&mask);
318 sigaddset(&mask, SIGUSR1);
319 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
320 err(1, "sigprocmask");
321 if (kill(getpid(), SIGUSR1) < 0)
322 err(1, "kill");
323
324 kev.flags = EV_ADD | EV_CLEAR;
325 #if LIBKQUEUE
326 kev.data = 1; /* WORKAROUND */
327 #else
328 kev.data = 2; // one extra time from test_kevent_signal_disable()
329 #endif
330 kevent_cmp(&kev, kevent_get(kqfd));
331
332 /* Delete the watch */
333 kev.flags = EV_DELETE;
334 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
335 err(1, "%s", test_id);
336
337 success();
338 }
339
340 void
test_kevent_signal_del(void)341 test_kevent_signal_del(void)
342 {
343 const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)";
344 struct kevent kev;
345
346 test_begin(test_id);
347
348 /* Delete the kevent */
349 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
350 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
351 err(1, "%s", test_id);
352
353 /* Block SIGUSR1, then send it to ourselves */
354 sigset_t mask;
355 sigemptyset(&mask);
356 sigaddset(&mask, SIGUSR1);
357 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
358 err(1, "sigprocmask");
359 if (kill(getpid(), SIGUSR1) < 0)
360 err(1, "kill");
361
362 test_no_kevents();
363 success();
364 }
365
366 void
test_kevent_signal_oneshot(void)367 test_kevent_signal_oneshot(void)
368 {
369 const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)";
370 struct kevent kev;
371
372 test_begin(test_id);
373
374 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL);
375 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
376 err(1, "%s", test_id);
377
378 /* Block SIGUSR1, then send it to ourselves */
379 sigset_t mask;
380 sigemptyset(&mask);
381 sigaddset(&mask, SIGUSR1);
382 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
383 err(1, "sigprocmask");
384 if (kill(getpid(), SIGUSR1) < 0)
385 err(1, "kill");
386
387 kev.flags |= EV_CLEAR;
388 kev.data = 1;
389 kevent_cmp(&kev, kevent_get(kqfd));
390
391 /* Send another one and make sure we get no events */
392 if (kill(getpid(), SIGUSR1) < 0)
393 err(1, "kill");
394 test_no_kevents();
395
396 success();
397 }
398 #endif
399
400 void
test_evfilt_proc()401 test_evfilt_proc()
402 {
403 kqfd = kqueue();
404
405 signal(SIGUSR1, sig_handler);
406
407 add_and_delete();
408 proc_track(0); /* Run without sleeping before children exit. */
409 proc_track(1); /* Sleep a bit in the children before exiting. */
410
411 #if TODO
412 event_trigger();
413 #endif
414
415 signal(SIGUSR1, SIG_DFL);
416
417 #if TODO
418 test_kevent_signal_add();
419 test_kevent_signal_del();
420 test_kevent_signal_get();
421 test_kevent_signal_disable();
422 test_kevent_signal_enable();
423 test_kevent_signal_oneshot();
424 #endif
425 close(kqfd);
426 }
427