git: c8f5e6819d4d - main - pwait: Optionally wait until process is reaped

Dag-Erling Smørgrav <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a67302d.1f998.2405cd66__45229.4046570145$1785147460$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=c8f5e6819d4d81906c4a1641b5c9f02d8730481c

commit c8f5e6819d4d81906c4a1641b5c9f02d8730481c
Author:     Dag-Erling Smørgrav <[email protected]>
AuthorDate: 2026-07-27 10:15:31 +0000
Commit:     Dag-Erling Smørgrav <[email protected]>
CommitDate: 2026-07-27 10:15:31 +0000

    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
---
 bin/pwait/pwait.1 | 17 ++++++++++++++---
 bin/pwait/pwait.c | 41 +++++++++++++++++++++++++++++------------
 2 files changed, 43 insertions(+), 15 deletions(-)

diff --git a/bin/pwait/pwait.1 b/bin/pwait/pwait.1
index d92b829b1d6a..2776d3439a31 100644
--- a/bin/pwait/pwait.1
+++ b/bin/pwait/pwait.1
@@ -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
diff --git a/bin/pwait/pwait.c b/bin/pwait/pwait.c
index 7f134036f1b9..36c6644f5474 100644
--- a/bin/pwait/pwait.c
+++ b/bin/pwait/pwait.c
@@ -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) {
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.