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 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 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