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/cdefs.h>
35 #include <sys/types.h>
36 #include <sys/event.h>
37 #include <sys/time.h>
38 #include <sys/wait.h>
39
40 #include <err.h>
41 #include <errno.h>
42 #include <signal.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sysexits.h>
47 #include <unistd.h>
48
49 static void
usage(void)50 usage(void)
51 {
52
53 fprintf(stderr, "usage: pwait [-t timeout] [-ov] pid ...\n");
54 exit(EX_USAGE);
55 }
56
57 /*
58 * pwait - wait for processes to terminate
59 */
60 int
main(int argc,char * argv[])61 main(int argc, char *argv[])
62 {
63 struct itimerval itv;
64 struct kevent *e;
65 int oflag, tflag, verbose;
66 int i, kq, n, nleft, opt, status;
67 long pid;
68 char *end, *s;
69 double timeout;
70
71 oflag = 0;
72 tflag = 0;
73 verbose = 0;
74 memset(&itv, 0, sizeof(itv));
75
76 while ((opt = getopt(argc, argv, "ot:v")) != -1) {
77 switch (opt) {
78 case 'o':
79 oflag = 1;
80 break;
81 case 't':
82 tflag = 1;
83 errno = 0;
84 timeout = strtod(optarg, &end);
85 if (end == optarg || errno == ERANGE || timeout < 0) {
86 errx(EX_DATAERR, "timeout value");
87 }
88 switch(*end) {
89 case 0:
90 case 's':
91 break;
92 case 'h':
93 timeout *= 60;
94 /* FALLTHROUGH */
95 case 'm':
96 timeout *= 60;
97 break;
98 default:
99 errx(EX_DATAERR, "timeout unit");
100 }
101 if (timeout > 100000000L) {
102 errx(EX_DATAERR, "timeout value");
103 }
104 itv.it_value.tv_sec = (time_t)timeout;
105 timeout -= (time_t)timeout;
106 itv.it_value.tv_usec =
107 (suseconds_t)(timeout * 1000000UL);
108 break;
109 case 'v':
110 verbose = 1;
111 break;
112 default:
113 usage();
114 /* NOTREACHED */
115 }
116 }
117
118 argc -= optind;
119 argv += optind;
120
121 if (argc == 0) {
122 usage();
123 }
124
125 kq = kqueue();
126 if (kq == -1) {
127 err(EX_OSERR, "kqueue");
128 }
129
130 e = malloc((argc + tflag) * sizeof(struct kevent));
131 if (e == NULL) {
132 err(EX_OSERR, "malloc");
133 }
134 nleft = 0;
135 for (n = 0; n < argc; n++) {
136 s = argv[n];
137 /* Undocumented Solaris compat */
138 if (!strncmp(s, "/proc/", 6)) {
139 s += 6;
140 }
141 errno = 0;
142 pid = strtol(s, &end, 10);
143 if (pid < 0 || *end != '\0' || errno != 0) {
144 warnx("%s: bad process id", s);
145 continue;
146 }
147 if (pid == getpid()) {
148 warnx("%s: skipping my own pid", s);
149 continue;
150 }
151 for (i = 0; i < nleft; i++) {
152 if (e[i].ident == (uintptr_t)pid) {
153 break;
154 }
155 }
156 if (i < nleft) {
157 /* Duplicate. */
158 continue;
159 }
160 EV_SET(e + nleft, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
161 if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) {
162 warn("%ld", pid);
163 if (oflag) {
164 exit(EX_OK);
165 }
166 } else {
167 nleft++;
168 }
169 }
170
171 if (nleft > 0 && tflag) {
172 /*
173 * Explicitly detect SIGALRM so that an exit status of 124
174 * can be returned rather than 142.
175 */
176 EV_SET(e + nleft, SIGALRM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
177 if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) {
178 err(EX_OSERR, "kevent");
179 }
180 /* Ignore SIGALRM to not interrupt kevent(2). */
181 signal(SIGALRM, SIG_IGN);
182 if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
183 err(EX_OSERR, "setitimer");
184 }
185 }
186 while (nleft > 0) {
187 n = kevent(kq, NULL, 0, e, nleft + tflag, NULL);
188 if (n == -1) {
189 err(EX_OSERR, "kevent");
190 }
191 for (i = 0; i < n; i++) {
192 if (e[i].filter == EVFILT_SIGNAL) {
193 if (verbose) {
194 printf("timeout\n");
195 }
196 exit(124);
197 }
198 if (verbose) {
199 status = e[i].data;
200 if (WIFEXITED(status)) {
201 printf("%ld: exited with status %d.\n",
202 (long)e[i].ident,
203 WEXITSTATUS(status));
204 } else if (WIFSIGNALED(status)) {
205 printf("%ld: killed by signal %d.\n",
206 (long)e[i].ident,
207 WTERMSIG(status));
208 } else {
209 printf("%ld: terminated.\n",
210 (long)e[i].ident);
211 }
212 }
213 if (oflag) {
214 exit(EX_OK);
215 }
216 --nleft;
217 }
218 }
219
220 exit(EX_OK);
221 }
222