]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/pwait/pwait.c
zfs: merge openzfs/zfs@95f71c019
[FreeBSD/FreeBSD.git] / bin / pwait / pwait.c
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
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
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 }