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 <err.h> 44 #include <errno.h> 45 #include <signal.h> 46 #include <stdbool.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <sysexits.h> 51 #include <unistd.h> 52 53 struct pid { 54 RB_ENTRY(pid) entry; 55 pid_t pid; 56 }; 57 58 static int 59 pidcmp(const struct pid *a, const struct pid *b) 60 { 61 return (a->pid > b->pid ? 1 : a->pid < b->pid ? -1 : 0); 62 } 63 64 RB_HEAD(pidtree, pid); 65 static struct pidtree pids = RB_INITIALIZER(&pids); 66 RB_GENERATE_STATIC(pidtree, pid, entry, pidcmp); 67 68 static void 69 usage(void) 70 { 71 fprintf(stderr, "usage: pwait [-t timeout] [-opv] pid ...\n"); 72 exit(EX_USAGE); 73 } 74 75 /* 76 * pwait - wait for processes to terminate 77 */ 78 int 79 main(int argc, char *argv[]) 80 { 81 struct itimerval itv; 82 struct kevent *e; 83 struct pid k, *p; 84 char *end, *s; 85 double timeout; 86 size_t sz; 87 long pid; 88 pid_t mypid; 89 int i, kq, n, ndone, nleft, opt, pid_max, ret, status; 90 bool oflag, pflag, tflag, verbose; 91 92 oflag = false; 93 pflag = false; 94 tflag = false; 95 verbose = false; 96 memset(&itv, 0, sizeof(itv)); 97 98 while ((opt = getopt(argc, argv, "opt:v")) != -1) { 99 switch (opt) { 100 case 'o': 101 oflag = true; 102 break; 103 case 'p': 104 pflag = true; 105 break; 106 case 't': 107 tflag = true; 108 errno = 0; 109 timeout = strtod(optarg, &end); 110 if (end == optarg || errno == ERANGE || timeout < 0) { 111 errx(EX_DATAERR, "timeout value"); 112 } 113 switch (*end) { 114 case '\0': 115 break; 116 case 's': 117 end++; 118 break; 119 case 'h': 120 timeout *= 60; 121 /* FALLTHROUGH */ 122 case 'm': 123 timeout *= 60; 124 end++; 125 break; 126 default: 127 errx(EX_DATAERR, "timeout unit"); 128 } 129 if (*end != '\0') { 130 errx(EX_DATAERR, "timeout unit"); 131 } 132 if (timeout > 100000000L) { 133 errx(EX_DATAERR, "timeout value"); 134 } 135 itv.it_value.tv_sec = (time_t)timeout; 136 timeout -= (time_t)timeout; 137 itv.it_value.tv_usec = 138 (suseconds_t)(timeout * 1000000UL); 139 break; 140 case 'v': 141 verbose = true; 142 break; 143 default: 144 usage(); 145 /* NOTREACHED */ 146 } 147 } 148 149 argc -= optind; 150 argv += optind; 151 152 if (argc == 0) { 153 usage(); 154 } 155 156 if ((kq = kqueue()) < 0) 157 err(EX_OSERR, "kqueue"); 158 159 sz = sizeof(pid_max); 160 if (sysctlbyname("kern.pid_max", &pid_max, &sz, NULL, 0) != 0) { 161 pid_max = 99999; 162 } 163 if ((e = malloc((argc + tflag) * sizeof(*e))) == NULL) { 164 err(EX_OSERR, "malloc"); 165 } 166 ndone = nleft = 0; 167 mypid = getpid(); 168 for (n = 0; n < argc; n++) { 169 s = argv[n]; 170 /* Undocumented Solaris compat */ 171 if (strncmp(s, "/proc/", 6) == 0) { 172 s += 6; 173 } 174 errno = 0; 175 pid = strtol(s, &end, 10); 176 if (pid < 0 || pid > pid_max || *end != '\0' || errno != 0) { 177 warnx("%s: bad process id", s); 178 continue; 179 } 180 if (pid == mypid) { 181 warnx("%s: skipping my own pid", s); 182 continue; 183 } 184 if ((p = malloc(sizeof(*p))) == NULL) { 185 err(EX_OSERR, NULL); 186 } 187 p->pid = pid; 188 if (RB_INSERT(pidtree, &pids, p) != NULL) { 189 /* Duplicate. */ 190 free(p); 191 continue; 192 } 193 EV_SET(e + nleft, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); 194 if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) { 195 if (errno != ESRCH) 196 err(EX_OSERR, "kevent()"); 197 warn("%ld", pid); 198 RB_REMOVE(pidtree, &pids, p); 199 free(p); 200 ndone++; 201 } else { 202 nleft++; 203 } 204 } 205 206 if ((ndone == 0 || !oflag) && nleft > 0 && tflag) { 207 /* 208 * Explicitly detect SIGALRM so that an exit status of 124 209 * can be returned rather than 142. 210 */ 211 EV_SET(e + nleft, SIGALRM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 212 if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) { 213 err(EX_OSERR, "kevent"); 214 } 215 /* Ignore SIGALRM to not interrupt kevent(2). */ 216 signal(SIGALRM, SIG_IGN); 217 if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { 218 err(EX_OSERR, "setitimer"); 219 } 220 } 221 ret = EX_OK; 222 while ((ndone == 0 || !oflag) && ret == EX_OK && nleft > 0) { 223 n = kevent(kq, NULL, 0, e, nleft + tflag, NULL); 224 if (n == -1) { 225 err(EX_OSERR, "kevent"); 226 } 227 for (i = 0; i < n; i++) { 228 if (e[i].filter == EVFILT_SIGNAL) { 229 if (verbose) { 230 printf("timeout\n"); 231 } 232 ret = 124; 233 } 234 pid = e[i].ident; 235 if (verbose) { 236 status = e[i].data; 237 if (WIFEXITED(status)) { 238 printf("%ld: exited with status %d.\n", 239 pid, WEXITSTATUS(status)); 240 } else if (WIFSIGNALED(status)) { 241 printf("%ld: killed by signal %d.\n", 242 pid, WTERMSIG(status)); 243 } else { 244 printf("%ld: terminated.\n", pid); 245 } 246 } 247 k.pid = pid; 248 if ((p = RB_FIND(pidtree, &pids, &k)) != NULL) { 249 RB_REMOVE(pidtree, &pids, p); 250 free(p); 251 ndone++; 252 } 253 --nleft; 254 } 255 } 256 if (pflag) { 257 RB_FOREACH(p, pidtree, &pids) { 258 printf("%d\n", p->pid); 259 } 260 } 261 exit(ret); 262 } 263