1 /*- 2 * Copyright (c) 2004-2009, Jilles Tjoelker 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with 6 * or without modification, are permitted provided that the 7 * following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above 10 * copyright notice, this list of conditions and the 11 * following disclaimer. 12 * 2. Redistributions in binary form must reproduce the 13 * above copyright notice, this list of conditions and 14 * the following disclaimer in the documentation and/or 15 * other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 18 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 19 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 30 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 31 * OF SUCH DAMAGE. 32 */ 33 34 #include <sys/types.h> 35 #include <sys/event.h> 36 #include <sys/sysctl.h> 37 #include <sys/time.h> 38 #include <sys/tree.h> 39 #include <sys/wait.h> 40 41 #include <err.h> 42 #include <errno.h> 43 #include <signal.h> 44 #include <stdbool.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <sysexits.h> 49 #include <unistd.h> 50 51 struct pid { 52 RB_ENTRY(pid) entry; 53 pid_t pid; 54 }; 55 56 static int 57 pidcmp(const struct pid *a, const struct pid *b) 58 { 59 return (a->pid > b->pid ? 1 : a->pid < b->pid ? -1 : 0); 60 } 61 62 RB_HEAD(pidtree, pid); 63 static struct pidtree pids = RB_INITIALIZER(&pids); 64 RB_GENERATE_STATIC(pidtree, pid, entry, pidcmp); 65 66 static void 67 usage(void) 68 { 69 fprintf(stderr, "usage: pwait [-t timeout] [-opv] pid ...\n"); 70 exit(EX_USAGE); 71 } 72 73 /* 74 * pwait - wait for processes to terminate 75 */ 76 int 77 main(int argc, char *argv[]) 78 { 79 struct itimerval itv; 80 struct kevent *e; 81 struct pid k, *p; 82 char *end, *s; 83 double timeout; 84 size_t sz; 85 long pid; 86 pid_t mypid; 87 int i, kq, n, ndone, nleft, opt, pid_max, ret, status; 88 bool oflag, pflag, tflag, verbose; 89 90 oflag = false; 91 pflag = false; 92 tflag = false; 93 verbose = false; 94 memset(&itv, 0, sizeof(itv)); 95 96 while ((opt = getopt(argc, argv, "opt:v")) != -1) { 97 switch (opt) { 98 case 'o': 99 oflag = true; 100 break; 101 case 'p': 102 pflag = true; 103 break; 104 case 't': 105 tflag = true; 106 errno = 0; 107 timeout = strtod(optarg, &end); 108 if (end == optarg || errno == ERANGE || timeout < 0) { 109 errx(EX_DATAERR, "timeout value"); 110 } 111 switch (*end) { 112 case '\0': 113 break; 114 case 's': 115 end++; 116 break; 117 case 'h': 118 timeout *= 60; 119 /* FALLTHROUGH */ 120 case 'm': 121 timeout *= 60; 122 end++; 123 break; 124 default: 125 errx(EX_DATAERR, "timeout unit"); 126 } 127 if (*end != '\0') { 128 errx(EX_DATAERR, "timeout unit"); 129 } 130 if (timeout > 100000000L) { 131 errx(EX_DATAERR, "timeout value"); 132 } 133 itv.it_value.tv_sec = (time_t)timeout; 134 timeout -= (time_t)timeout; 135 itv.it_value.tv_usec = 136 (suseconds_t)(timeout * 1000000UL); 137 break; 138 case 'v': 139 verbose = true; 140 break; 141 default: 142 usage(); 143 /* NOTREACHED */ 144 } 145 } 146 147 argc -= optind; 148 argv += optind; 149 150 if (argc == 0) { 151 usage(); 152 } 153 154 if ((kq = kqueue()) < 0) 155 err(EX_OSERR, "kqueue"); 156 157 sz = sizeof(pid_max); 158 if (sysctlbyname("kern.pid_max", &pid_max, &sz, NULL, 0) != 0) { 159 pid_max = 99999; 160 } 161 if ((e = malloc((argc + tflag) * sizeof(*e))) == NULL) { 162 err(EX_OSERR, "malloc"); 163 } 164 ndone = nleft = 0; 165 mypid = getpid(); 166 for (n = 0; n < argc; n++) { 167 s = argv[n]; 168 /* Undocumented Solaris compat */ 169 if (strncmp(s, "/proc/", 6) == 0) { 170 s += 6; 171 } 172 errno = 0; 173 pid = strtol(s, &end, 10); 174 if (pid < 0 || pid > pid_max || *end != '\0' || errno != 0) { 175 warnx("%s: bad process id", s); 176 continue; 177 } 178 if (pid == mypid) { 179 warnx("%s: skipping my own pid", s); 180 continue; 181 } 182 if ((p = malloc(sizeof(*p))) == NULL) { 183 err(EX_OSERR, NULL); 184 } 185 p->pid = pid; 186 if (RB_INSERT(pidtree, &pids, p) != NULL) { 187 /* Duplicate. */ 188 free(p); 189 continue; 190 } 191 EV_SET(e + nleft, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); 192 if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) { 193 if (errno != ESRCH) 194 err(EX_OSERR, "kevent()"); 195 warn("%ld", pid); 196 RB_REMOVE(pidtree, &pids, p); 197 free(p); 198 ndone++; 199 } else { 200 nleft++; 201 } 202 } 203 204 if ((ndone == 0 || !oflag) && nleft > 0 && tflag) { 205 /* 206 * Explicitly detect SIGALRM so that an exit status of 124 207 * can be returned rather than 142. 208 */ 209 EV_SET(e + nleft, SIGALRM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 210 if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) { 211 err(EX_OSERR, "kevent"); 212 } 213 /* Ignore SIGALRM to not interrupt kevent(2). */ 214 signal(SIGALRM, SIG_IGN); 215 if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { 216 err(EX_OSERR, "setitimer"); 217 } 218 } 219 ret = EX_OK; 220 while ((ndone == 0 || !oflag) && ret == EX_OK && nleft > 0) { 221 n = kevent(kq, NULL, 0, e, nleft + tflag, NULL); 222 if (n == -1) { 223 err(EX_OSERR, "kevent"); 224 } 225 for (i = 0; i < n; i++) { 226 if (e[i].filter == EVFILT_SIGNAL) { 227 if (verbose) { 228 printf("timeout\n"); 229 } 230 ret = 124; 231 } 232 pid = e[i].ident; 233 if (verbose) { 234 status = e[i].data; 235 if (WIFEXITED(status)) { 236 printf("%ld: exited with status %d.\n", 237 pid, WEXITSTATUS(status)); 238 } else if (WIFSIGNALED(status)) { 239 printf("%ld: killed by signal %d.\n", 240 pid, WTERMSIG(status)); 241 } else { 242 printf("%ld: terminated.\n", pid); 243 } 244 } 245 k.pid = pid; 246 if ((p = RB_FIND(pidtree, &pids, &k)) != NULL) { 247 RB_REMOVE(pidtree, &pids, p); 248 free(p); 249 ndone++; 250 } 251 --nleft; 252 } 253 } 254 if (pflag) { 255 RB_FOREACH(p, pidtree, &pids) { 256 printf("%d\n", p->pid); 257 } 258 } 259 exit(ret); 260 } 261