Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/bin/pwait/pwait.c
102493 views
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
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
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
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
262