The branch stable/15 has been updated by des:

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

commit 955650f1814257070f8414b6b6e8f42496f26409
Author:     Dag-Erling Smørgrav <[email protected]>
AuthorDate: 2025-10-28 11:56:36 +0000
Commit:     Dag-Erling Smørgrav <[email protected]>
CommitDate: 2025-10-30 10:37:35 +0000

    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
    
    (cherry picked from commit 3d73146baeb933fe955c7496572b483a9f92914c)
---
 bin/pwait/pwait.1             |  6 ++-
 bin/pwait/pwait.c             | 98 ++++++++++++++++++++++++++++---------------
 bin/pwait/tests/pwait_test.sh | 38 +++++++++++++++++
 3 files changed, 107 insertions(+), 35 deletions(-)

diff --git a/bin/pwait/pwait.1 b/bin/pwait/pwait.1
index 83ac8bcef317..d92b829b1d6a 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 January 21, 2021
+.Dd October 22, 2025
 .Dt PWAIT 1
 .Os
 .Sh NAME
@@ -39,7 +39,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl t Ar duration
-.Op Fl ov
+.Op Fl opv
 .Ar pid
 \&...
 .Sh DESCRIPTION
@@ -51,6 +51,8 @@ The following option is available:
 .Bl -tag -width indent
 .It Fl o
 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 t Ar duration
 If any process is still running after
 .Ar duration ,
diff --git a/bin/pwait/pwait.c b/bin/pwait/pwait.c
index 27f4c8e9858d..59bf0eb93ced 100644
--- a/bin/pwait/pwait.c
+++ b/bin/pwait/pwait.c
@@ -33,7 +33,9 @@
 
 #include <sys/types.h>
 #include <sys/event.h>
+#include <sys/sysctl.h>
 #include <sys/time.h>
+#include <sys/tree.h>
 #include <sys/wait.h>
 
 #include <err.h>
@@ -46,10 +48,25 @@
 #include <sysexits.h>
 #include <unistd.h>
 
+struct pid {
+       RB_ENTRY(pid) entry;
+       pid_t pid;
+};
+
+static int
+pidcmp(const struct pid *a, const struct pid *b)
+{
+       return (a->pid > b->pid ? 1 : a->pid < b->pid ? -1 : 0);
+}
+
+RB_HEAD(pidtree, pid);
+static struct pidtree pids = RB_INITIALIZER(&pids);
+RB_GENERATE_STATIC(pidtree, pid, entry, pidcmp);
+
 static void
 usage(void)
 {
-       fprintf(stderr, "usage: pwait [-t timeout] [-ov] pid ...\n");
+       fprintf(stderr, "usage: pwait [-t timeout] [-opv] pid ...\n");
        exit(EX_USAGE);
 }
 
@@ -61,22 +78,28 @@ main(int argc, char *argv[])
 {
        struct itimerval itv;
        struct kevent *e;
+       struct pid k, *p;
        char *end, *s;
        double timeout;
+       size_t sz;
        long pid;
        pid_t mypid;
-       int i, kq, n, nleft, opt, status;
-       bool oflag, tflag, verbose;
+       int i, kq, n, ndone, nleft, opt, pid_max, ret, status;
+       bool oflag, pflag, tflag, verbose;
 
        oflag = false;
+       pflag = false;
        tflag = false;
        verbose = false;
        memset(&itv, 0, sizeof(itv));
 
-       while ((opt = getopt(argc, argv, "ot:v")) != -1) {
+       while ((opt = getopt(argc, argv, "opt:v")) != -1) {
                switch (opt) {
                case 'o':
-                       oflag = 1;
+                       oflag = true;
+                       break;
+               case 'p':
+                       pflag = true;
                        break;
                case 't':
                        tflag = true;
@@ -128,16 +151,17 @@ main(int argc, char *argv[])
                usage();
        }
 
-       kq = kqueue();
-       if (kq == -1) {
+       if ((kq = kqueue()) < 0)
                err(EX_OSERR, "kqueue");
-       }
 
-       e = malloc((argc + tflag) * sizeof(struct kevent));
-       if (e == NULL) {
+       sz = sizeof(pid_max);
+       if (sysctlbyname("kern.pid_max", &pid_max, &sz, NULL, 0) != 0) {
+               pid_max = 99999;
+       }
+       if ((e = malloc((argc + tflag) * sizeof(*e))) == NULL) {
                err(EX_OSERR, "malloc");
        }
-       nleft = 0;
+       ndone = nleft = 0;
        mypid = getpid();
        for (n = 0; n < argc; n++) {
                s = argv[n];
@@ -147,7 +171,7 @@ main(int argc, char *argv[])
                }
                errno = 0;
                pid = strtol(s, &end, 10);
-               if (pid < 0 || *end != '\0' || errno != 0) {
+               if (pid < 0 || pid > pid_max || *end != '\0' || errno != 0) {
                        warnx("%s: bad process id", s);
                        continue;
                }
@@ -155,27 +179,29 @@ main(int argc, char *argv[])
                        warnx("%s: skipping my own pid", s);
                        continue;
                }
-               for (i = 0; i < nleft; i++) {
-                       if (e[i].ident == (uintptr_t)pid) {
-                               break;
-                       }
+               if ((p = malloc(sizeof(*p))) == NULL) {
+                       err(EX_OSERR, NULL);
                }
-               if (i < nleft) {
+               p->pid = pid;
+               if (RB_INSERT(pidtree, &pids, p) != NULL) {
                        /* Duplicate. */
+                       free(p);
                        continue;
                }
                EV_SET(e + nleft, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
                if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) {
+                       if (errno != ESRCH)
+                               err(EX_OSERR, "kevent()");
                        warn("%ld", pid);
-                       if (oflag) {
-                               exit(EX_OK);
-                       }
+                       RB_REMOVE(pidtree, &pids, p);
+                       free(p);
+                       ndone++;
                } else {
                        nleft++;
                }
        }
 
-       if (nleft > 0 && tflag) {
+       if ((ndone == 0 || !oflag) && nleft > 0 && tflag) {
                /*
                 * Explicitly detect SIGALRM so that an exit status of 124
                 * can be returned rather than 142.
@@ -190,7 +216,8 @@ main(int argc, char *argv[])
                        err(EX_OSERR, "setitimer");
                }
        }
-       while (nleft > 0) {
+       ret = EX_OK;
+       while ((ndone == 0 || !oflag) && ret == EX_OK && nleft > 0) {
                n = kevent(kq, NULL, 0, e, nleft + tflag, NULL);
                if (n == -1) {
                        err(EX_OSERR, "kevent");
@@ -200,29 +227,34 @@ main(int argc, char *argv[])
                                if (verbose) {
                                        printf("timeout\n");
                                }
-                               exit(124);
+                               ret = 124;
                        }
+                       pid = e[i].ident;
                        if (verbose) {
                                status = e[i].data;
                                if (WIFEXITED(status)) {
                                        printf("%ld: exited with status %d.\n",
-                                           (long)e[i].ident,
-                                           WEXITSTATUS(status));
+                                           pid, WEXITSTATUS(status));
                                } else if (WIFSIGNALED(status)) {
                                        printf("%ld: killed by signal %d.\n",
-                                           (long)e[i].ident,
-                                           WTERMSIG(status));
+                                           pid, WTERMSIG(status));
                                } else {
-                                       printf("%ld: terminated.\n",
-                                           (long)e[i].ident);
+                                       printf("%ld: terminated.\n", pid);
                                }
                        }
-                       if (oflag) {
-                               exit(EX_OK);
+                       k.pid = pid;
+                       if ((p = RB_FIND(pidtree, &pids, &k)) != NULL) {
+                               RB_REMOVE(pidtree, &pids, p);
+                               free(p);
+                               ndone++;
                        }
                        --nleft;
                }
        }
-
-       exit(EX_OK);
+       if (pflag) {
+               RB_FOREACH(p, pidtree, &pids) {
+                       printf("%d\n", p->pid);
+               }
+       }
+       exit(ret);
 }
diff --git a/bin/pwait/tests/pwait_test.sh b/bin/pwait/tests/pwait_test.sh
index 66bdd6981704..d31ca21cff93 100644
--- a/bin/pwait/tests/pwait_test.sh
+++ b/bin/pwait/tests/pwait_test.sh
@@ -310,6 +310,43 @@ or_flag_cleanup()
        wait $p2 $p4 $p6 >/dev/null 2>&1
 }
 
+atf_test_case print
+print_head()
+{
+       atf_set "descr" "Test the -p flag"
+}
+
+print_body()
+{
+       sleep 1 &
+       p1=$!
+
+       sleep 5 &
+       p5=$!
+
+       sleep 10 &
+       p10=$!
+
+       atf_check \
+               -o inline:"$p5\n$p10\n" \
+               -s exit:124 \
+               pwait -t 2 -p $p10 $p5 $p1 $p5 $p10
+
+       atf_check \
+               -e inline:"kill: $p1: No such process\n" \
+               -s exit:1 \
+               kill -0 $p1
+
+       atf_check kill -0 $p5
+       atf_check kill -0 $p10
+}
+
+print_cleanup()
+{
+       kill $p1 $p5 $p10 >/dev/null 2>&1
+       wait $p1 $p5 $p10 >/dev/null 2>&1
+}
+
 atf_init_test_cases()
 {
        atf_add_test_case basic
@@ -318,4 +355,5 @@ atf_init_test_cases()
        atf_add_test_case timeout_no_timeout
        atf_add_test_case timeout_many
        atf_add_test_case or_flag
+       atf_add_test_case print
 }

Reply via email to