2 * Copyright (c) 2009 Mark Heily <mark@heily.com>
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.
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.
26 static int sigusr1_caught = 0;
31 sig_handler(int signum)
42 /* Create a child that waits to be killed and then exits */
46 if (fstat(kqfd, &s) != -1)
47 errx(1, "kqueue inherited across fork! (%s() at %s:%d)",
48 __func__, __FILE__, __LINE__);
53 printf(" -- child created (pid %d)\n", (int) pid);
55 test_begin("kevent(EVFILT_PROC, EV_ADD)");
58 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
63 test_begin("kevent(EVFILT_PROC, EV_DELETE)");
67 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
68 if (kill(pid, SIGKILL) < 0)
78 proc_track(int sleep_time)
86 snprintf(test_id, sizeof(test_id),
87 "kevent(EVFILT_PROC, NOTE_TRACK); sleep %d", sleep_time);
92 err(1, "pipe (parent) failed! (%s() at %s:%d)",
93 __func__, __FILE__, __LINE__);
96 /* Create a child to track. */
98 if (pid == 0) { /* Child */
99 pid_t grandchild = -1;
102 * Give the parent a chance to start tracking us.
104 result = read(pipe_fd[1], test_id, 1);
106 err(1, "read from pipe in child failed! (ret %zd) (%s() at %s:%d)",
107 result, __func__, __FILE__, __LINE__);
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
120 if (grandchild == 0) { /* Grandchild */
121 if (sleep_time) sleep(sleep_time);
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);
129 if (sleep_time) sleep(sleep_time);
131 } else if (pid == -1) { /* Error */
132 err(1, "fork (child) failed! (%s() at %s:%d)",
133 __func__, __FILE__, __LINE__);
136 printf(" -- child created (pid %d)\n", (int) pid);
138 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE,
139 NOTE_TRACK | NOTE_EXEC | NOTE_EXIT | NOTE_FORK,
142 printf(" -- tracking child (pid %d)\n", (int) pid);
144 /* Now that we're tracking the child, tell it to proceed. */
145 result = write(pipe_fd[0], test_id, 1);
147 err(1, "write to pipe in parent failed! (ret %zd) (%s() at %s:%d)",
148 result, __func__, __FILE__, __LINE__);
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)
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
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.
173 pid_t gchild_pid = -1;
181 kevp = kevent_get_timeout(kqfd, 5);
185 printf(" -- Received kevent: %s\n", kevent_to_str(kevp));
187 if ((kevp->fflags & NOTE_CHILD) && (kevp->fflags & NOTE_EXIT)) {
188 errx(1, "NOTE_CHILD and NOTE_EXIT in same kevent: %s", kevent_to_str(kevp));
191 if (kevp->fflags & NOTE_CHILD) {
192 if (kevp->data == pid) {
195 gchild_pid = kevp->ident;
198 errx(1, "Spurious NOTE_CHILD: %s", kevent_to_str(kevp));
203 if (kevp->fflags & NOTE_EXIT) {
204 if ((kevp->ident == pid) && (!child_exit)) {
207 } else if ((kevp->ident == gchild_pid) && (!gchild_exit)) {
211 errx(1, "Spurious NOTE_EXIT: %s", kevent_to_str(kevp));
215 if (kevp->fflags & NOTE_FORK) {
216 if ((kevp->ident == pid) && (!child_fork)) {
220 errx(1, "Spurious NOTE_FORK: %s", kevent_to_str(kevp));
225 errx(1, "Spurious kevent: %s", kevent_to_str(kevp));
232 /* Make sure all expected events were received. */
233 if (child_exit && child_fork && gchild_exit && gchild_note) {
234 printf(" -- Received all expected events.\n");
236 errx(1, "Did not receive all expected events.");
250 test_begin("kevent(EVFILT_PROC, wait)");
252 /* Create a child that waits to be killed and then exits */
256 printf(" -- child caught signal, exiting\n");
259 printf(" -- child created (pid %d)\n", (int) pid);
262 kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
264 /* Cause the child to exit, then retrieve the event */
265 printf(" -- killing process %d\n", (int) pid);
266 if (kill(pid, SIGUSR1) < 0)
268 kevent_cmp(&kev, kevent_get(kqfd));
275 test_kevent_signal_disable(void)
277 const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)";
282 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL);
283 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
284 err(1, "%s", test_id);
286 /* Block SIGUSR1, then send it to ourselves */
289 sigaddset(&mask, SIGUSR1);
290 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
291 err(1, "sigprocmask");
292 if (kill(getpid(), SIGKILL) < 0)
301 test_kevent_signal_enable(void)
303 const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)";
308 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL);
309 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
310 err(1, "%s", test_id);
312 /* Block SIGUSR1, then send it to ourselves */
315 sigaddset(&mask, SIGUSR1);
316 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
317 err(1, "sigprocmask");
318 if (kill(getpid(), SIGUSR1) < 0)
321 kev.flags = EV_ADD | EV_CLEAR;
323 kev.data = 1; /* WORKAROUND */
325 kev.data = 2; // one extra time from test_kevent_signal_disable()
327 kevent_cmp(&kev, kevent_get(kqfd));
329 /* Delete the watch */
330 kev.flags = EV_DELETE;
331 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
332 err(1, "%s", test_id);
338 test_kevent_signal_del(void)
340 const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)";
345 /* Delete the kevent */
346 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
347 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
348 err(1, "%s", test_id);
350 /* Block SIGUSR1, then send it to ourselves */
353 sigaddset(&mask, SIGUSR1);
354 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
355 err(1, "sigprocmask");
356 if (kill(getpid(), SIGUSR1) < 0)
364 test_kevent_signal_oneshot(void)
366 const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)";
371 EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL);
372 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
373 err(1, "%s", test_id);
375 /* Block SIGUSR1, then send it to ourselves */
378 sigaddset(&mask, SIGUSR1);
379 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
380 err(1, "sigprocmask");
381 if (kill(getpid(), SIGUSR1) < 0)
384 kev.flags |= EV_CLEAR;
386 kevent_cmp(&kev, kevent_get(kqfd));
388 /* Send another one and make sure we get no events */
389 if (kill(getpid(), SIGUSR1) < 0)
402 signal(SIGUSR1, sig_handler);
405 proc_track(0); /* Run without sleeping before children exit. */
406 proc_track(1); /* Sleep a bit in the children before exiting. */
412 signal(SIGUSR1, SIG_DFL);
415 test_kevent_signal_add();
416 test_kevent_signal_del();
417 test_kevent_signal_get();
418 test_kevent_signal_disable();
419 test_kevent_signal_enable();
420 test_kevent_signal_oneshot();