diff options
Diffstat (limited to 'bin/pwait')
| -rw-r--r-- | bin/pwait/pwait.1 | 6 | ||||
| -rw-r--r-- | bin/pwait/pwait.c | 98 | ||||
| -rw-r--r-- | 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  } | 
