xref: /openbsd-src/regress/sys/kern/kqueue/kqueue-process.c (revision 1e522b7ed1a0747f70c841dcf5b973f4d8de556d)
1 /*	$OpenBSD: kqueue-process.c,v 1.13 2018/08/03 15:19:44 visa 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/time.h>
9 #include <sys/wait.h>
10 
11 #include <err.h>
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 
17 #include "main.h"
18 
19 static int process_child(void);
20 
21 static int pfd1[2];
22 static int pfd2[2];
23 
24 int
do_process(void)25 do_process(void)
26 {
27 	struct kevent ke;
28 	struct timespec ts;
29 	int kq, status;
30 	pid_t pid, pid2;
31 	int didfork, didchild;
32 	int i;
33 	char ch = 0;
34 
35 	/*
36 	 * Timeout in case something doesn't work.
37 	 */
38 	ts.tv_sec = 10;
39 	ts.tv_nsec = 0;
40 
41 	ASS((kq = kqueue()) >= 0,
42 	    warn("kqueue"));
43 
44 	/* Open pipes for synchronizing the children with the parent. */
45 	if (pipe(pfd1) == -1)
46 		err(1, "pipe 1");
47 	if (pipe(pfd2) == -1)
48 		err(1, "pipe 2");
49 
50 	switch ((pid = fork())) {
51 	case -1:
52 		err(1, "fork");
53 	case 0:
54 		_exit(process_child());
55 	}
56 
57 	EV_SET(&ke, pid, EVFILT_PROC, EV_ADD|EV_ENABLE|EV_CLEAR,
58 	    NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_TRACK, 0, NULL);
59 	ASS(kevent(kq, &ke, 1, NULL, 0, NULL) == 0,
60 	    warn("can't register events on kqueue"));
61 
62 	/* negative case */
63 	EV_SET(&ke, pid + (1ULL << 30), 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 	    warnx("can register bogus pid on kqueue"));
67 	ASS(errno == ESRCH,
68 	    warn("register bogus pid on kqueue returned wrong error"));
69 
70 	ASS(write(pfd1[1], &ch, 1) == 1,
71 	    warn("write sync 1"));
72 
73 	didfork = didchild = 0;
74 
75 	pid2 = -1;
76 	for (i = 0; i < 2; i++) {
77 		ASS(kevent(kq, NULL, 0, &ke, 1, &ts) == 1,
78 		    warnx("didn't receive event"));
79 		ASSX(ke.filter == EVFILT_PROC);
80 		switch (ke.fflags) {
81 		case NOTE_CHILD:
82 			didchild = 1;
83 			ASSX((pid_t)ke.data == pid);
84 			pid2 = ke.ident;
85 			fprintf(stderr, "child %d (from %d)\n", pid2, pid);
86 			break;
87 		case NOTE_FORK:
88 			didfork = 1;
89 			ASSX(ke.ident == pid);
90 			fprintf(stderr, "fork\n");
91 			break;
92 		case NOTE_TRACKERR:
93 			errx(1, "child tracking failed due to resource shortage");
94 		default:
95 			errx(1, "kevent returned weird event 0x%x pid %d",
96 			    ke.fflags, (pid_t)ke.ident);
97 		}
98 	}
99 
100 	ASSX(pid2 != -1);
101 
102 	/* Both children now sleeping. */
103 
104 	ASSX(didchild == 1);
105 	ASSX(didfork == 1);
106 
107 	ASS(write(pfd2[1], &ch, 1) == 1,
108 	    warn("write sync 2.1"));
109 	ASS(write(pfd1[1], &ch, 1) == 1,
110 	    warn("write sync 2"));
111 
112 	/*
113 	 * Wait for child's exit. It also implies child-child has exited.
114 	 * This should ensure that NOTE_EXIT has been posted for both children.
115 	 * Child-child's events should get aggregated.
116 	 */
117 	if (wait(&status) < 0)
118 		err(1, "wait");
119 
120 	for (i = 0; i < 2; i++) {
121 		ASS(kevent(kq, NULL, 0, &ke, 1, &ts) == 1,
122 		    warnx("didn't receive event"));
123 		ASSX(ke.filter == EVFILT_PROC);
124 		switch (ke.fflags) {
125 		case NOTE_EXIT:
126 			ASSX((pid_t)ke.ident == pid);
127 			fprintf(stderr, "child exit %d\n", pid);
128 			break;
129 		case NOTE_EXEC | NOTE_EXIT:
130 			ASSX((pid_t)ke.ident == pid2);
131 			fprintf(stderr, "child-child exec/exit %d\n", pid2);
132 			break;
133 		default:
134 			errx(1, "kevent returned weird event 0x%x pid %d",
135 			    ke.fflags, (pid_t)ke.ident);
136 		}
137 	}
138 
139 	if (!WIFEXITED(status))
140 		errx(1, "child didn't exit?");
141 
142 	close(kq);
143 	return (WEXITSTATUS(status) != 0);
144 }
145 
146 static int
process_child(void)147 process_child(void)
148 {
149 	int status;
150 	char ch;
151 
152 	ASS(read(pfd1[0], &ch, 1) == 1,
153 	    warn("read sync 1"));
154 
155 	/* fork and see if tracking works. */
156 	switch (fork()) {
157 	case -1:
158 		err(1, "fork");
159 	case 0:
160 		ASS(read(pfd2[0], &ch, 1) == 1,
161 		    warn("read sync 2.1"));
162 		execl("/usr/bin/true", "true", (char *)NULL);
163 		err(1, "execl(true)");
164 	}
165 
166 	ASS(read(pfd1[0], &ch, 1) == 1,
167 	    warn("read sync 2"));
168 
169 	if (wait(&status) < 0)
170 		err(1, "wait 2");
171 	if (!WIFEXITED(status))
172 		errx(1, "child-child didn't exit?");
173 
174 	return 0;
175 }
176