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