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