mirror of
https://git.freebsd.org/src.git
synced 2026-01-11 19:57:22 +00:00
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
This commit is contained in:
parent
e12ec5f433
commit
3d73146bae
3 changed files with 107 additions and 35 deletions
|
|
@ -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 ,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue