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
pidcmp(const struct pid * a,const struct pid * b)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
usage(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
main(int argc,char * argv[])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