xref: /openbsd-src/regress/sys/kern/kqueue/kqueue-process.c (revision b261876b05beae09af1ac260b35bb38a79ca10da)
1 /*	$OpenBSD: kqueue-process.c,v 1.10 2016/07/14 05:55:08 guenther Exp $	*/
2 /*
3  *	Written by Artur Grabowski <art@openbsd.org> 2002 Public Domain
4  */
5 
6 #include <sys/types.h>
7 #include <sys/event.h>
8 #include <sys/wait.h>
9 
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <err.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <signal.h>
16 
17 static int process_child(void);
18 
19 #define ASS(cond, mess) do { if (!(cond)) { mess; return 1; } } while (0)
20 
21 #define ASSX(cond) ASS(cond, warnx("assertion " #cond " failed on line %d", __LINE__))
22 
23 static void
24 usr1handler(int signum)
25 {
26 	/* nada */
27 }
28 
29 int do_process(void);
30 
31 int
32 do_process(void)
33 {
34 	struct kevent ke;
35 	int kq, status;
36 	pid_t pid, pid2;
37 	int didfork, didchild;
38 	int i;
39 	struct timespec ts;
40 
41 	/*
42 	 * Timeout in case something doesn't work.
43 	 */
44 	ts.tv_sec = 10;
45 	ts.tv_nsec = 0;
46 
47 	ASS((kq = kqueue()) >= 0, warn("kqueue"));
48 
49 	/*
50 	 * Install a signal handler so that we can use pause() to synchronize
51 	 * with the child with the parent.
52 	 */
53 	signal(SIGUSR1, usr1handler);
54 
55 	switch ((pid = fork())) {
56 	case -1:
57 		err(1, "fork");
58 	case 0:
59 		_exit(process_child());
60 	}
61 
62 	sleep(2);	/* wait for child to settle down. */
63 
64 	EV_SET(&ke, pid, EVFILT_PROC, EV_ADD|EV_ENABLE|EV_CLEAR,
65 	    NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_TRACK, 0, NULL);
66 	ASS(kevent(kq, &ke, 1, NULL, 0, NULL) == 0,
67 	    warn("can't register events on kqueue"));
68 
69 	/* negative case */
70 	EV_SET(&ke, pid + (1ULL << 32), EVFILT_PROC, EV_ADD|EV_ENABLE|EV_CLEAR,
71 	    NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_TRACK, 0, NULL);
72 	ASS(kevent(kq, &ke, 1, NULL, 0, NULL) != 0,
73 	    warnx("can register bogus pid on kqueue"));
74 	ASS(errno == ESRCH,
75 	    warn("register bogus pid on kqueue returned wrong error"));
76 
77 	kill(pid, SIGUSR1);	/* sync 1 */
78 
79 	didfork = didchild = 0;
80 
81 	pid2 = -1;
82 	for (i = 0; i < 2; i++) {
83 		ASS(kevent(kq, NULL, 0, &ke, 1, &ts) == 1,
84 		    warnx("didn't receive event"));
85 		ASSX(ke.filter == EVFILT_PROC);
86 		switch (ke.fflags) {
87 		case NOTE_CHILD:
88 			didchild = 1;
89 			ASSX((pid_t)ke.data == pid);
90 			pid2 = ke.ident;
91 			fprintf(stderr, "child %d (from %d)\n", pid2, pid);
92 			break;
93 		case NOTE_FORK:
94 			didfork = 1;
95 			ASSX(ke.ident == pid);
96 			fprintf(stderr, "fork\n");
97 			break;
98 		case NOTE_TRACKERR:
99 			errx(1, "child tracking failed due to resource shortage");
100 		default:
101 			errx(1, "kevent returned weird event 0x%x pid %d",
102 			    ke.fflags, (pid_t)ke.ident);
103 		}
104 	}
105 
106 	if (pid2 == -1)
107 		return (1);
108 
109 	/* Both children now sleeping. */
110 
111 	ASSX(didchild == 1);
112 	ASSX(didfork == 1);
113 
114 	kill(pid2, SIGUSR1);	/* sync 2.1 */
115 	kill(pid, SIGUSR1);	/* sync 2 */
116 
117 	/* Wait for child's exit. */
118 	if (wait(&status) < 0)
119 		err(1, "wait");
120 	/* Wait for child-child's exec/exit to receive two events at once. */
121 	sleep(1);
122 
123 	for (i = 0; i < 2; i++) {
124 		ASS(kevent(kq, NULL, 0, &ke, 1, &ts) == 1,
125 		    warnx("didn't receive event"));
126 		ASSX(ke.filter == EVFILT_PROC);
127 		switch (ke.fflags) {
128 		case NOTE_EXIT:
129 			ASSX((pid_t)ke.ident == pid);
130 			fprintf(stderr, "child exit %d\n", pid);
131 			break;
132 		case NOTE_EXEC | NOTE_EXIT:
133 			ASSX((pid_t)ke.ident == pid2);
134 			fprintf(stderr, "child-child exec/exit %d\n", pid2);
135 			break;
136 		default:
137 			errx(1, "kevent returned weird event 0x%x pid %d",
138 			    ke.fflags, (pid_t)ke.ident);
139 		}
140 	}
141 
142 	if (!WIFEXITED(status))
143 		errx(1, "child didn't exit?");
144 
145 	close(kq);
146 	return (WEXITSTATUS(status) != 0);
147 }
148 
149 static int
150 process_child(void)
151 {
152 	signal(SIGCHLD, SIG_IGN);	/* ignore our children. */
153 
154 	pause();
155 
156 	/* fork and see if tracking works. */
157 	switch (fork()) {
158 	case -1:
159 		err(1, "fork");
160 	case 0:
161 		/* sync 2.1 */
162 		pause();
163 		execl("/usr/bin/true", "true", (char *)NULL);
164 		err(1, "execl(true)");
165 	}
166 
167 	/* sync 2 */
168 	pause();
169 
170 	return 0;
171 }
172