diff options
Diffstat (limited to 'bin/pwait/pwait.c')
| -rw-r--r-- | bin/pwait/pwait.c | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/bin/pwait/pwait.c b/bin/pwait/pwait.c new file mode 100644 index 000000000000..59bf0eb93ced --- /dev/null +++ b/bin/pwait/pwait.c @@ -0,0 +1,260 @@ +/*- + * Copyright (c) 2004-2009, Jilles Tjoelker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the + * following conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * 2. Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#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> +#include <errno.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#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] [-opv] pid ...\n"); + exit(EX_USAGE); +} + +/* + * pwait - wait for processes to terminate + */ +int +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, 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, "opt:v")) != -1) { + switch (opt) { + case 'o': + oflag = true; + break; + case 'p': + pflag = true; + break; + case 't': + tflag = true; + errno = 0; + timeout = strtod(optarg, &end); + if (end == optarg || errno == ERANGE || timeout < 0) { + errx(EX_DATAERR, "timeout value"); + } + switch (*end) { + case '\0': + break; + case 's': + end++; + break; + case 'h': + timeout *= 60; + /* FALLTHROUGH */ + case 'm': + timeout *= 60; + end++; + break; + default: + errx(EX_DATAERR, "timeout unit"); + } + if (*end != '\0') { + errx(EX_DATAERR, "timeout unit"); + } + if (timeout > 100000000L) { + errx(EX_DATAERR, "timeout value"); + } + itv.it_value.tv_sec = (time_t)timeout; + timeout -= (time_t)timeout; + itv.it_value.tv_usec = + (suseconds_t)(timeout * 1000000UL); + break; + case 'v': + verbose = true; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) { + usage(); + } + + if ((kq = kqueue()) < 0) + err(EX_OSERR, "kqueue"); + + 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"); + } + ndone = nleft = 0; + mypid = getpid(); + for (n = 0; n < argc; n++) { + s = argv[n]; + /* Undocumented Solaris compat */ + if (strncmp(s, "/proc/", 6) == 0) { + s += 6; + } + errno = 0; + pid = strtol(s, &end, 10); + if (pid < 0 || pid > pid_max || *end != '\0' || errno != 0) { + warnx("%s: bad process id", s); + continue; + } + if (pid == mypid) { + warnx("%s: skipping my own pid", s); + continue; + } + if ((p = malloc(sizeof(*p))) == NULL) { + err(EX_OSERR, NULL); + } + 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); + RB_REMOVE(pidtree, &pids, p); + free(p); + ndone++; + } else { + nleft++; + } + } + + if ((ndone == 0 || !oflag) && nleft > 0 && tflag) { + /* + * Explicitly detect SIGALRM so that an exit status of 124 + * can be returned rather than 142. + */ + EV_SET(e + nleft, SIGALRM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); + if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) { + err(EX_OSERR, "kevent"); + } + /* Ignore SIGALRM to not interrupt kevent(2). */ + signal(SIGALRM, SIG_IGN); + if (setitimer(ITIMER_REAL, &itv, NULL) == -1) { + err(EX_OSERR, "setitimer"); + } + } + 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"); + } + for (i = 0; i < n; i++) { + if (e[i].filter == EVFILT_SIGNAL) { + if (verbose) { + printf("timeout\n"); + } + ret = 124; + } + pid = e[i].ident; + if (verbose) { + status = e[i].data; + if (WIFEXITED(status)) { + printf("%ld: exited with status %d.\n", + pid, WEXITSTATUS(status)); + } else if (WIFSIGNALED(status)) { + printf("%ld: killed by signal %d.\n", + pid, WTERMSIG(status)); + } else { + printf("%ld: terminated.\n", pid); + } + } + k.pid = pid; + if ((p = RB_FIND(pidtree, &pids, &k)) != NULL) { + RB_REMOVE(pidtree, &pids, p); + free(p); + ndone++; + } + --nleft; + } + } + if (pflag) { + RB_FOREACH(p, pidtree, &pids) { + printf("%d\n", p->pid); + } + } + exit(ret); +} |
