pwait: Optionally wait until process is reaped

If the new -r option is specified, wait until the target process not
only terminates but is reaped.

MFC after:	1 week
Sponsored by:	Klara, Inc.
Sponsored by:	NetApp, Inc.
Reviewed by:	kib, markj
Differential Revision:	https://reviews.freebsd.org/D58314
This commit is contained in:
Dag-Erling Smørgrav committed 2026-07-27 12:15:31 +02:00
1 parent 2bacbbecb1
commit c8f5e6819d
2 files changed
+43 -15

No files matched your search

+14 -3
View File
@@ -30,7 +30,7 @@
.\" USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
.\" OF SUCH DAMAGE.
.\"
.Dd October 22, 2025
.Dd July 22, 2026
.Dt PWAIT 1
.Os
.Sh NAME
@@ -39,7 +39,7 @@
.Sh SYNOPSIS
.Nm
.Op Fl t Ar duration
.Op Fl opv
.Op Fl oprv
.Ar pid
\&...
.Sh DESCRIPTION
@@ -53,6 +53,17 @@ The following option is available:
Exit when any of the given processes has terminated.
.It Fl p
On exit, print a list of processes that have not terminated.
.It Fl r
Do not exit until target processes have not only terminated, but been
reaped by a call to the
.Xr wait 2
family of functions.
Without this flag, target processes may still exist as zombies when
.Nm
exits.
Beware of deadlocks if a target process's reaper (usually its parent)
is directly or indirectly blocked by
.Nm .
.It Fl t Ar duration
If any process is still running after
.Ar duration ,
@@ -127,8 +138,8 @@ $ sleep 30 & sleep 40 &
[2] 1675
$ pwait -v -t 60 1674 1675
1674: exited with status 0.
1675: exited with status 0.
[1]- Done sleep 30
1675: exited with status 0.
[2]+ Done sleep 40
$ echo $?
0
+29 -12
View File
@@ -40,6 +40,7 @@
#include <sys/tree.h>
#include <sys/wait.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <signal.h>
@@ -68,7 +69,7 @@ RB_GENERATE_STATIC(pidtree, pid, entry, pidcmp);
static void
usage(void)
{
fprintf(stderr, "usage: pwait [-t timeout] [-opv] pid ...\n");
fprintf(stderr, "usage: pwait [-oprv] [-t timeout] pid ...\n");
exit(EX_USAGE);
}
@@ -86,16 +87,17 @@ main(int argc, char *argv[])
size_t sz;
long pid;
pid_t mypid;
int i, kq, n, ndone, nleft, opt, pid_max, ret, status;
bool oflag, pflag, tflag, verbose;
int i, kq, n, ndone, nleft, notes, opt, pid_max, ret, status;
bool oflag, pflag, rflag, tflag, verbose;
oflag = false;
pflag = false;
rflag = false;
tflag = false;
verbose = false;
memset(&itv, 0, sizeof(itv));
while ((opt = getopt(argc, argv, "opt:v")) != -1) {
while ((opt = getopt(argc, argv, "oprt:v")) != -1) {
switch (opt) {
case 'o':
oflag = true;
@@ -103,6 +105,9 @@ main(int argc, char *argv[])
case 'p':
pflag = true;
break;
case 'r':
rflag = true;
break;
case 't':
tflag = true;
errno = 0;
@@ -165,6 +170,9 @@ main(int argc, char *argv[])
}
ndone = nleft = 0;
mypid = getpid();
notes = rflag ? NOTE_REAP : NOTE_EXIT;
if (verbose)
notes |= NOTE_EXIT;
for (n = 0; n < argc; n++) {
s = argv[n];
/* Undocumented Solaris compat */
@@ -190,7 +198,7 @@ main(int argc, char *argv[])
free(p);
continue;
}
EV_SET(e + nleft, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
EV_SET(e + nleft, pid, EVFILT_PROC, EV_ADD, notes, 0, NULL);
if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) {
if (errno != ESRCH)
err(EX_OSERR, "kevent()");
@@ -230,9 +238,11 @@ main(int argc, char *argv[])
printf("timeout\n");
}
ret = 124;
continue;
}
assert(e[i].filter == EVFILT_PROC);
pid = e[i].ident;
if (verbose) {
if ((e[i].fflags & NOTE_EXIT) && verbose) {
status = e[i].data;
if (WIFEXITED(status)) {
printf("%ld: exited with status %d.\n",
@@ -244,13 +254,20 @@ main(int argc, char *argv[])
printf("%ld: terminated.\n", pid);
}
}
k.pid = pid;
if ((p = RB_FIND(pidtree, &pids, &k)) != NULL) {
RB_REMOVE(pidtree, &pids, p);
free(p);
ndone++;
if ((e[i].fflags & NOTE_REAP) && verbose) {
printf("%ld: reaped.\n", pid);
}
if ((e[i].fflags & NOTE_REAP) ||
(!rflag && (e[i].fflags & NOTE_EXIT))) {
/* this process is done */
k.pid = pid;
if ((p = RB_FIND(pidtree, &pids, &k)) != NULL) {
RB_REMOVE(pidtree, &pids, p);
free(p);
ndone++;
}
--nleft;
}
--nleft;
}
}
if (pflag) {