/*- * 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static void usage(void) { fprintf(stderr, "usage: pwait [-t timeout] [-ov] pid ...\n"); exit(EX_USAGE); } /* * pwait - wait for processes to terminate */ int main(int argc, char *argv[]) { struct itimerval itv; struct kevent *e; int oflag, tflag, verbose; int i, kq, n, nleft, opt, status; long pid; char *end, *s; double timeout; oflag = 0; tflag = 0; verbose = 0; memset(&itv, 0, sizeof(itv)); while ((opt = getopt(argc, argv, "ot:v")) != -1) { switch (opt) { case 'o': oflag = 1; break; case 't': tflag = 1; errno = 0; timeout = strtod(optarg, &end); if (end == optarg || errno == ERANGE || timeout < 0) { errx(EX_DATAERR, "timeout value"); } switch(*end) { case 0: case 's': break; case 'h': timeout *= 60; /* FALLTHROUGH */ case 'm': timeout *= 60; break; default: 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 = 1; break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc == 0) { usage(); } kq = kqueue(); if (kq == -1) { err(EX_OSERR, "kqueue"); } e = malloc((argc + tflag) * sizeof(struct kevent)); if (e == NULL) { err(EX_OSERR, "malloc"); } nleft = 0; for (n = 0; n < argc; n++) { s = argv[n]; /* Undocumented Solaris compat */ if (!strncmp(s, "/proc/", 6)) { s += 6; } errno = 0; pid = strtol(s, &end, 10); if (pid < 0 || *end != '\0' || errno != 0) { warnx("%s: bad process id", s); continue; } if (pid == getpid()) { warnx("%s: skipping my own pid", s); continue; } for (i = 0; i < nleft; i++) { if (e[i].ident == (uintptr_t)pid) { break; } } if (i < nleft) { /* Duplicate. */ continue; } EV_SET(e + nleft, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) { warn("%ld", pid); if (oflag) { exit(EX_OK); } } else { nleft++; } } if (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"); } } while (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"); } exit(124); } if (verbose) { status = e[i].data; if (WIFEXITED(status)) { printf("%ld: exited with status %d.\n", (long)e[i].ident, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { printf("%ld: killed by signal %d.\n", (long)e[i].ident, WTERMSIG(status)); } else { printf("%ld: terminated.\n", (long)e[i].ident); } } if (oflag) { exit(EX_OK); } --nleft; } } exit(EX_OK); }