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