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