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