1 /*	$OpenBSD: kqueue-process.c,v 1.5 2010/08/04 05:55:29 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 <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 	if (wait(&status) < 0)
109 		err(1, "wait");
110 
111 	/* make sure we get an exit note */
112 	ASS(kevent(kq, NULL, 0, &ke, 1, &ts) == 1,
113 	    warnx("didn't receive event"));
114 	ASSX(ke.filter == EVFILT_PROC);
115 	switch (ke.fflags) {
116 	case NOTE_EXIT:
117 		didchild = 1;
118 		ASSX((pid_t)ke.ident == pid);
119 		fprintf(stderr, "exit %d\n", pid);
120 		break;
121 	default:
122 		errx(1, "kevent returned weird event 0x%x pid %d",
123 		    ke.fflags, (pid_t)ke.ident);
124 	}
125 
126 	if (!WIFEXITED(status))
127 		errx(1, "child didn't exit?");
128 
129 	close(kq);
130 	return (WEXITSTATUS(status) != 0);
131 }
132 
133 static int
134 process_child(void)
135 {
136 	signal(SIGCHLD, SIG_IGN);	/* ignore our children. */
137 
138 	pause();
139 
140 	/* fork and see if tracking works. */
141 	switch (fork()) {
142 	case -1:
143 		err(1, "fork");
144 	case 0:
145 		/* sync 2.1 */
146 		pause();
147 		execl("/usr/bin/true", "true", (void *)NULL);
148 		err(1, "execl(true)");
149 	}
150 
151 	/* sync 2 */
152 	pause();
153 
154 	return 0;
155 }
156