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