10e5e4167SJilles Tjoelker /*- 20e5e4167SJilles Tjoelker * Copyright (c) 2004-2009, Jilles Tjoelker 30e5e4167SJilles Tjoelker * All rights reserved. 40e5e4167SJilles Tjoelker * 50e5e4167SJilles Tjoelker * Redistribution and use in source and binary forms, with 60e5e4167SJilles Tjoelker * or without modification, are permitted provided that the 70e5e4167SJilles Tjoelker * following conditions are met: 80e5e4167SJilles Tjoelker * 90e5e4167SJilles Tjoelker * 1. Redistributions of source code must retain the above 100e5e4167SJilles Tjoelker * copyright notice, this list of conditions and the 110e5e4167SJilles Tjoelker * following disclaimer. 120e5e4167SJilles Tjoelker * 2. Redistributions in binary form must reproduce the 130e5e4167SJilles Tjoelker * above copyright notice, this list of conditions and 140e5e4167SJilles Tjoelker * the following disclaimer in the documentation and/or 150e5e4167SJilles Tjoelker * other materials provided with the distribution. 160e5e4167SJilles Tjoelker * 170e5e4167SJilles Tjoelker * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 180e5e4167SJilles Tjoelker * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 190e5e4167SJilles Tjoelker * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 200e5e4167SJilles Tjoelker * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 210e5e4167SJilles Tjoelker * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 220e5e4167SJilles Tjoelker * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY 230e5e4167SJilles Tjoelker * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 240e5e4167SJilles Tjoelker * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 250e5e4167SJilles Tjoelker * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 260e5e4167SJilles Tjoelker * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 270e5e4167SJilles Tjoelker * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 280e5e4167SJilles Tjoelker * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 290e5e4167SJilles Tjoelker * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 300e5e4167SJilles Tjoelker * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 310e5e4167SJilles Tjoelker * OF SUCH DAMAGE. 320e5e4167SJilles Tjoelker */ 330e5e4167SJilles Tjoelker 340e5e4167SJilles Tjoelker #include <sys/cdefs.h> 350e5e4167SJilles Tjoelker __FBSDID("$FreeBSD$"); 360e5e4167SJilles Tjoelker 370e5e4167SJilles Tjoelker #include <sys/types.h> 380e5e4167SJilles Tjoelker #include <sys/event.h> 390e5e4167SJilles Tjoelker #include <sys/time.h> 400e5e4167SJilles Tjoelker #include <sys/wait.h> 410e5e4167SJilles Tjoelker 420e5e4167SJilles Tjoelker #include <err.h> 430e5e4167SJilles Tjoelker #include <errno.h> 440e5e4167SJilles Tjoelker #include <fcntl.h> 450e5e4167SJilles Tjoelker #include <signal.h> 460e5e4167SJilles Tjoelker #include <stdio.h> 470e5e4167SJilles Tjoelker #include <stdlib.h> 480e5e4167SJilles Tjoelker #include <string.h> 490e5e4167SJilles Tjoelker #include <sysexits.h> 500e5e4167SJilles Tjoelker #include <unistd.h> 510e5e4167SJilles Tjoelker 520e5e4167SJilles Tjoelker static void 530e5e4167SJilles Tjoelker usage(void) 540e5e4167SJilles Tjoelker { 550e5e4167SJilles Tjoelker 56532b3f47SPawel Jakub Dawidek errx(EX_USAGE, "usage: pwait [-t timeout] [-v] pid ..."); 570e5e4167SJilles Tjoelker } 580e5e4167SJilles Tjoelker 590e5e4167SJilles Tjoelker /* 600e5e4167SJilles Tjoelker * pwait - wait for processes to terminate 610e5e4167SJilles Tjoelker */ 620e5e4167SJilles Tjoelker int 630e5e4167SJilles Tjoelker main(int argc, char *argv[]) 640e5e4167SJilles Tjoelker { 65b06b52baSBryan Drewery struct itimerval itv; 660e5e4167SJilles Tjoelker int kq; 670e5e4167SJilles Tjoelker struct kevent *e; 68b06b52baSBryan Drewery int tflag, verbose; 693f50bbafSPawel Jakub Dawidek int opt, nleft, n, i, status; 700e5e4167SJilles Tjoelker long pid; 710e5e4167SJilles Tjoelker char *s, *end; 72b06b52baSBryan Drewery double timeout; 730e5e4167SJilles Tjoelker 74b06b52baSBryan Drewery tflag = verbose = 0; 75b06b52baSBryan Drewery memset(&itv, 0, sizeof(itv)); 76b06b52baSBryan Drewery while ((opt = getopt(argc, argv, "t:v")) != -1) { 770e5e4167SJilles Tjoelker switch (opt) { 78b06b52baSBryan Drewery case 't': 79b06b52baSBryan Drewery tflag = 1; 80b06b52baSBryan Drewery errno = 0; 81b06b52baSBryan Drewery timeout = strtod(optarg, &end); 82b06b52baSBryan Drewery if (end == optarg || errno == ERANGE || 83b06b52baSBryan Drewery timeout < 0) 84b06b52baSBryan Drewery errx(EX_DATAERR, "timeout value"); 85b06b52baSBryan Drewery switch(*end) { 86b06b52baSBryan Drewery case 0: 87b06b52baSBryan Drewery case 's': 88b06b52baSBryan Drewery break; 89b06b52baSBryan Drewery case 'h': 90b06b52baSBryan Drewery timeout *= 60; 91b06b52baSBryan Drewery /* FALLTHROUGH */ 92b06b52baSBryan Drewery case 'm': 93b06b52baSBryan Drewery timeout *= 60; 94b06b52baSBryan Drewery break; 95b06b52baSBryan Drewery default: 96b06b52baSBryan Drewery errx(EX_DATAERR, "timeout unit"); 97b06b52baSBryan Drewery } 98b06b52baSBryan Drewery if (timeout > 100000000L) 99b06b52baSBryan Drewery errx(EX_DATAERR, "timeout value"); 100b06b52baSBryan Drewery itv.it_value.tv_sec = (time_t)timeout; 101b06b52baSBryan Drewery timeout -= (time_t)timeout; 102b06b52baSBryan Drewery itv.it_value.tv_usec = 103b06b52baSBryan Drewery (suseconds_t)(timeout * 1000000UL); 104b06b52baSBryan Drewery break; 1050e5e4167SJilles Tjoelker case 'v': 1060e5e4167SJilles Tjoelker verbose = 1; 1070e5e4167SJilles Tjoelker break; 1080e5e4167SJilles Tjoelker default: 1090e5e4167SJilles Tjoelker usage(); 1100e5e4167SJilles Tjoelker /* NOTREACHED */ 1110e5e4167SJilles Tjoelker } 1120e5e4167SJilles Tjoelker } 1130e5e4167SJilles Tjoelker 1140e5e4167SJilles Tjoelker argc -= optind; 1150e5e4167SJilles Tjoelker argv += optind; 1160e5e4167SJilles Tjoelker 1170e5e4167SJilles Tjoelker if (argc == 0) 1180e5e4167SJilles Tjoelker usage(); 1190e5e4167SJilles Tjoelker 1200e5e4167SJilles Tjoelker kq = kqueue(); 1210e5e4167SJilles Tjoelker if (kq == -1) 122532b3f47SPawel Jakub Dawidek err(EX_OSERR, "kqueue"); 1230e5e4167SJilles Tjoelker 124b06b52baSBryan Drewery e = malloc((argc + tflag) * sizeof(struct kevent)); 1250e5e4167SJilles Tjoelker if (e == NULL) 126532b3f47SPawel Jakub Dawidek err(EX_OSERR, "malloc"); 1270e5e4167SJilles Tjoelker nleft = 0; 1280e5e4167SJilles Tjoelker for (n = 0; n < argc; n++) { 1290e5e4167SJilles Tjoelker s = argv[n]; 1300e5e4167SJilles Tjoelker if (!strncmp(s, "/proc/", 6)) /* Undocumented Solaris compat */ 1310e5e4167SJilles Tjoelker s += 6; 1320e5e4167SJilles Tjoelker errno = 0; 1330e5e4167SJilles Tjoelker pid = strtol(s, &end, 10); 1340e5e4167SJilles Tjoelker if (pid < 0 || *end != '\0' || errno != 0) { 1350e5e4167SJilles Tjoelker warnx("%s: bad process id", s); 1360e5e4167SJilles Tjoelker continue; 1370e5e4167SJilles Tjoelker } 1383f50bbafSPawel Jakub Dawidek for (i = 0; i < nleft; i++) { 1390e5e4167SJilles Tjoelker if (e[i].ident == (uintptr_t)pid) 1403f50bbafSPawel Jakub Dawidek break; 1413f50bbafSPawel Jakub Dawidek } 1423f50bbafSPawel Jakub Dawidek if (i < nleft) { 1433f50bbafSPawel Jakub Dawidek /* Duplicate. */ 1443f50bbafSPawel Jakub Dawidek continue; 1453f50bbafSPawel Jakub Dawidek } 1463f50bbafSPawel Jakub Dawidek EV_SET(e + nleft, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); 1470e5e4167SJilles Tjoelker if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) 1480e5e4167SJilles Tjoelker warn("%ld", pid); 1490e5e4167SJilles Tjoelker else 1500e5e4167SJilles Tjoelker nleft++; 1510e5e4167SJilles Tjoelker } 1520e5e4167SJilles Tjoelker 153*5e2e2222SPawel Jakub Dawidek if (nleft > 0 && tflag) { 154b06b52baSBryan Drewery /* 155b06b52baSBryan Drewery * Explicitly detect SIGALRM so that an exit status of 124 156b06b52baSBryan Drewery * can be returned rather than 142. 157b06b52baSBryan Drewery */ 158b06b52baSBryan Drewery EV_SET(e + nleft, SIGALRM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); 159b06b52baSBryan Drewery if (kevent(kq, e + nleft, 1, NULL, 0, NULL) == -1) 160b06b52baSBryan Drewery err(EX_OSERR, "kevent"); 161b06b52baSBryan Drewery /* Ignore SIGALRM to not interrupt kevent(2). */ 162b06b52baSBryan Drewery signal(SIGALRM, SIG_IGN); 163b06b52baSBryan Drewery if (setitimer(ITIMER_REAL, &itv, NULL) == -1) 164b06b52baSBryan Drewery err(EX_OSERR, "setitimer"); 165b06b52baSBryan Drewery } 1660e5e4167SJilles Tjoelker while (nleft > 0) { 167b06b52baSBryan Drewery n = kevent(kq, NULL, 0, e, nleft + tflag, NULL); 1680e5e4167SJilles Tjoelker if (n == -1) 169532b3f47SPawel Jakub Dawidek err(EX_OSERR, "kevent"); 1700e5e4167SJilles Tjoelker for (i = 0; i < n; i++) { 171b06b52baSBryan Drewery if (e[i].filter == EVFILT_SIGNAL) { 172b06b52baSBryan Drewery if (verbose) 173b06b52baSBryan Drewery printf("timeout\n"); 174532b3f47SPawel Jakub Dawidek exit(124); 175b06b52baSBryan Drewery } 176b06b52baSBryan Drewery if (verbose) { 1770e5e4167SJilles Tjoelker status = e[i].data; 1780e5e4167SJilles Tjoelker if (WIFEXITED(status)) 1790e5e4167SJilles Tjoelker printf("%ld: exited with status %d.\n", 1800e5e4167SJilles Tjoelker (long)e[i].ident, 1810e5e4167SJilles Tjoelker WEXITSTATUS(status)); 1820e5e4167SJilles Tjoelker else if (WIFSIGNALED(status)) 1830e5e4167SJilles Tjoelker printf("%ld: killed by signal %d.\n", 1840e5e4167SJilles Tjoelker (long)e[i].ident, 1850e5e4167SJilles Tjoelker WTERMSIG(status)); 1860e5e4167SJilles Tjoelker else 1870e5e4167SJilles Tjoelker printf("%ld: terminated.\n", 1880e5e4167SJilles Tjoelker (long)e[i].ident); 1890e5e4167SJilles Tjoelker } 190b06b52baSBryan Drewery --nleft; 191b06b52baSBryan Drewery } 1920e5e4167SJilles Tjoelker } 1930e5e4167SJilles Tjoelker 194f3de285bSXin LI exit(EX_OK); 1950e5e4167SJilles Tjoelker } 196