Skip to content

Commit 3d73146

Browse files
committed
pwait: Add an option to print remaining processes
* On startup, insert all valid PIDs into a tree. * In our main loop, whenever a process terminates, remove its PID from the tree. * On exit, if the -p flag was specified, print the remaining PIDs. MFC after: 3 days Reviewed by: bcr, markj Differential Revision: https://reviews.freebsd.org/D53293
1 parent e12ec5f commit 3d73146

File tree

3 files changed

+107
-35
lines changed

3 files changed

+107
-35
lines changed

bin/pwait/pwait.1

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
.\" USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
3131
.\" OF SUCH DAMAGE.
3232
.\"
33-
.Dd January 21, 2021
33+
.Dd October 22, 2025
3434
.Dt PWAIT 1
3535
.Os
3636
.Sh NAME
@@ -39,7 +39,7 @@
3939
.Sh SYNOPSIS
4040
.Nm
4141
.Op Fl t Ar duration
42-
.Op Fl ov
42+
.Op Fl opv
4343
.Ar pid
4444
\&...
4545
.Sh DESCRIPTION
@@ -51,6 +51,8 @@ The following option is available:
5151
.Bl -tag -width indent
5252
.It Fl o
5353
Exit when any of the given processes has terminated.
54+
.It Fl p
55+
On exit, print a list of processes that have not terminated.
5456
.It Fl t Ar duration
5557
If any process is still running after
5658
.Ar duration ,

bin/pwait/pwait.c

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333

3434
#include <sys/types.h>
3535
#include <sys/event.h>
36+
#include <sys/sysctl.h>
3637
#include <sys/time.h>
38+
#include <sys/tree.h>
3739
#include <sys/wait.h>
3840

3941
#include <err.h>
@@ -46,10 +48,25 @@
4648
#include <sysexits.h>
4749
#include <unistd.h>
4850

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+
4966
static void
5067
usage(void)
5168
{
52-
fprintf(stderr, "usage: pwait [-t timeout] [-ov] pid ...\n");
69+
fprintf(stderr, "usage: pwait [-t timeout] [-opv] pid ...\n");
5370
exit(EX_USAGE);
5471
}
5572

@@ -61,22 +78,28 @@ main(int argc, char *argv[])
6178
{
6279
struct itimerval itv;
6380
struct kevent *e;
81+
struct pid k, *p;
6482
char *end, *s;
6583
double timeout;
84+
size_t sz;
6685
long pid;
6786
pid_t mypid;
68-
int i, kq, n, nleft, opt, status;
69-
bool oflag, tflag, verbose;
87+
int i, kq, n, ndone, nleft, opt, pid_max, ret, status;
88+
bool oflag, pflag, tflag, verbose;
7089

7190
oflag = false;
91+
pflag = false;
7292
tflag = false;
7393
verbose = false;
7494
memset(&itv, 0, sizeof(itv));
7595

76-
while ((opt = getopt(argc, argv, "ot:v")) != -1) {
96+
while ((opt = getopt(argc, argv, "opt:v")) != -1) {
7797
switch (opt) {
7898
case 'o':
79-
oflag = 1;
99+
oflag = true;
100+
break;
101+
case 'p':
102+
pflag = true;
80103
break;
81104
case 't':
82105
tflag = true;
@@ -128,16 +151,17 @@ main(int argc, char *argv[])
128151
usage();
129152
}
130153

131-
kq = kqueue();
132-
if (kq == -1) {
154+
if ((kq = kqueue()) < 0)
133155
err(EX_OSERR, "kqueue");
134-
}
135156

136-
e = malloc((argc + tflag) * sizeof(struct kevent));
137-
if (e == NULL) {
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) {
138162
err(EX_OSERR, "malloc");
139163
}
140-
nleft = 0;
164+
ndone = nleft = 0;
141165
mypid = getpid();
142166
for (n = 0; n < argc; n++) {
143167
s = argv[n];
@@ -147,35 +171,37 @@ main(int argc, char *argv[])
147171
}
148172
errno = 0;
149173
pid = strtol(s, &end, 10);
150-
if (pid < 0 || *end != '\0' || errno != 0) {
174+
if (pid < 0 || pid > pid_max || *end != '\0' || errno != 0) {
151175
warnx("%s: bad process id", s);
152176
continue;
153177
}
154178
if (pid == mypid) {
155179
warnx("%s: skipping my own pid", s);
156180
continue;
157181
}
158-
for (i = 0; i < nleft; i++) {
159-
if (e[i].ident == (uintptr_t)pid) {
160-
break;
161-
}
182+
if ((p = malloc(sizeof(*p))) == NULL) {
183+
err(EX_OSERR, NULL);
162184
}
163-
if (i < nleft) {
185+
p->pid = pid;
186+
if (RB_INSERT(pidtree, &pids, p) != NULL) {
164187
/* Duplicate. */
188+
free(p);
165189
continue;
166190
}
167191
EV_SET(e + nleft, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
168192
if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) {
193+
if (errno != ESRCH)
194+
err(EX_OSERR, "kevent()");
169195
warn("%ld", pid);
170-
if (oflag) {
171-
exit(EX_OK);
172-
}
196+
RB_REMOVE(pidtree, &pids, p);
197+
free(p);
198+
ndone++;
173199
} else {
174200
nleft++;
175201
}
176202
}
177203

178-
if (nleft > 0 && tflag) {
204+
if ((ndone == 0 || !oflag) && nleft > 0 && tflag) {
179205
/*
180206
* Explicitly detect SIGALRM so that an exit status of 124
181207
* can be returned rather than 142.
@@ -190,7 +216,8 @@ main(int argc, char *argv[])
190216
err(EX_OSERR, "setitimer");
191217
}
192218
}
193-
while (nleft > 0) {
219+
ret = EX_OK;
220+
while ((ndone == 0 || !oflag) && ret == EX_OK && nleft > 0) {
194221
n = kevent(kq, NULL, 0, e, nleft + tflag, NULL);
195222
if (n == -1) {
196223
err(EX_OSERR, "kevent");
@@ -200,29 +227,34 @@ main(int argc, char *argv[])
200227
if (verbose) {
201228
printf("timeout\n");
202229
}
203-
exit(124);
230+
ret = 124;
204231
}
232+
pid = e[i].ident;
205233
if (verbose) {
206234
status = e[i].data;
207235
if (WIFEXITED(status)) {
208236
printf("%ld: exited with status %d.\n",
209-
(long)e[i].ident,
210-
WEXITSTATUS(status));
237+
pid, WEXITSTATUS(status));
211238
} else if (WIFSIGNALED(status)) {
212239
printf("%ld: killed by signal %d.\n",
213-
(long)e[i].ident,
214-
WTERMSIG(status));
240+
pid, WTERMSIG(status));
215241
} else {
216-
printf("%ld: terminated.\n",
217-
(long)e[i].ident);
242+
printf("%ld: terminated.\n", pid);
218243
}
219244
}
220-
if (oflag) {
221-
exit(EX_OK);
245+
k.pid = pid;
246+
if ((p = RB_FIND(pidtree, &pids, &k)) != NULL) {
247+
RB_REMOVE(pidtree, &pids, p);
248+
free(p);
249+
ndone++;
222250
}
223251
--nleft;
224252
}
225253
}
226-
227-
exit(EX_OK);
254+
if (pflag) {
255+
RB_FOREACH(p, pidtree, &pids) {
256+
printf("%d\n", p->pid);
257+
}
258+
}
259+
exit(ret);
228260
}

bin/pwait/tests/pwait_test.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,43 @@ or_flag_cleanup()
310310
wait $p2 $p4 $p6 >/dev/null 2>&1
311311
}
312312

313+
atf_test_case print
314+
print_head()
315+
{
316+
atf_set "descr" "Test the -p flag"
317+
}
318+
319+
print_body()
320+
{
321+
sleep 1 &
322+
p1=$!
323+
324+
sleep 5 &
325+
p5=$!
326+
327+
sleep 10 &
328+
p10=$!
329+
330+
atf_check \
331+
-o inline:"$p5\n$p10\n" \
332+
-s exit:124 \
333+
pwait -t 2 -p $p10 $p5 $p1 $p5 $p10
334+
335+
atf_check \
336+
-e inline:"kill: $p1: No such process\n" \
337+
-s exit:1 \
338+
kill -0 $p1
339+
340+
atf_check kill -0 $p5
341+
atf_check kill -0 $p10
342+
}
343+
344+
print_cleanup()
345+
{
346+
kill $p1 $p5 $p10 >/dev/null 2>&1
347+
wait $p1 $p5 $p10 >/dev/null 2>&1
348+
}
349+
313350
atf_init_test_cases()
314351
{
315352
atf_add_test_case basic
@@ -318,4 +355,5 @@ atf_init_test_cases()
318355
atf_add_test_case timeout_no_timeout
319356
atf_add_test_case timeout_many
320357
atf_add_test_case or_flag
358+
atf_add_test_case print
321359
}

0 commit comments

Comments
 (0)