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