Annotation of src/usr.bin/timeout/timeout.c, Revision 1.1
1.1 ! job 1: /* $NetBSD: timeout.c,v 1.4 2014/08/05 08:20:02 christos Exp $ */
! 2:
! 3: /*-
! 4: * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org>
! 5: * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
! 6: * All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer
! 13: * in this position and unchanged.
! 14: * 2. Redistributions in binary form must reproduce the above copyright
! 15: * notice, this list of conditions and the following disclaimer in the
! 16: * documentation and/or other materials provided with the distribution.
! 17: *
! 18: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
! 19: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
! 20: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
! 21: * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
! 22: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
! 23: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
! 24: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
! 25: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 26: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
! 27: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 28: */
! 29:
! 30: #include <sys/cdefs.h>
! 31: #include <sys/time.h>
! 32: #include <sys/wait.h>
! 33:
! 34: #include <err.h>
! 35: #include <errno.h>
! 36: #include <getopt.h>
! 37: #include <limits.h>
! 38: #include <signal.h>
! 39: #include <stdbool.h>
! 40: #include <stdio.h>
! 41: #include <stdlib.h>
! 42: #include <string.h>
! 43: #include <sysexits.h>
! 44: #include <unistd.h>
! 45:
! 46: #define EXIT_TIMEOUT 124
! 47:
! 48: static sig_atomic_t sig_chld = 0;
! 49: static sig_atomic_t sig_term = 0;
! 50: static sig_atomic_t sig_alrm = 0;
! 51: static sig_atomic_t sig_ign = 0;
! 52:
! 53: static void __dead
! 54: usage(void)
! 55: {
! 56: fprintf(stderr, "Usage: %s [--signal sig | -s sig] [--preserve-status]"
! 57: " [--kill-after time | -k time] [--foreground] <duration> <command>"
! 58: " <arg ...>\n", getprogname());
! 59:
! 60: exit(EX_USAGE);
! 61: }
! 62:
! 63: static double
! 64: parse_duration(const char *duration)
! 65: {
! 66: double ret;
! 67: char *end;
! 68:
! 69: ret = strtod(duration, &end);
! 70: if (ret == 0 && end == duration)
! 71: errx(EXIT_FAILURE, "invalid duration");
! 72:
! 73: if (end == NULL || *end == '\0')
! 74: return (ret);
! 75:
! 76: if (end != NULL && *(end + 1) != '\0')
! 77: errx(EX_USAGE, "invalid duration");
! 78:
! 79: switch (*end) {
! 80: case 's':
! 81: break;
! 82: case 'm':
! 83: ret *= 60;
! 84: break;
! 85: case 'h':
! 86: ret *= 60 * 60;
! 87: break;
! 88: case 'd':
! 89: ret *= 60 * 60 * 24;
! 90: break;
! 91: default:
! 92: errx(EX_USAGE, "invalid duration");
! 93: }
! 94:
! 95: if (ret < 0 || ret >= 100000000UL)
! 96: errx(EX_USAGE, "invalid duration");
! 97:
! 98: return (ret);
! 99: }
! 100:
! 101: static int
! 102: parse_signal(const char *str)
! 103: {
! 104: char *ep;
! 105: int i;
! 106: long sig;
! 107:
! 108: if (strncasecmp(str, "SIG", 3) == 0) {
! 109: str += 3;
! 110:
! 111: for (i = 1; i < NSIG; i++) {
! 112: if (strcasecmp(str, sys_signame[i]) == 0)
! 113: return (i);
! 114: }
! 115:
! 116: goto err;
! 117: }
! 118:
! 119: errno = 0;
! 120: sig = strtol(str, &ep, 10);
! 121:
! 122: if (str[0] == '\0' || *ep != '\0')
! 123: goto err;
! 124: if (errno == ERANGE && (sig == LONG_MAX || sig == LONG_MIN))
! 125: goto err;
! 126: if (sig >= NSIG || sig < 0)
! 127: goto err;
! 128:
! 129: return (int)sig;
! 130:
! 131: err:
! 132: errx(EX_USAGE, "invalid signal");
! 133: }
! 134:
! 135: static void
! 136: sig_handler(int signo)
! 137: {
! 138: if (sig_ign != 0 && signo == sig_ign) {
! 139: sig_ign = 0;
! 140: return;
! 141: }
! 142:
! 143: switch(signo) {
! 144: case 0:
! 145: case SIGINT:
! 146: case SIGHUP:
! 147: case SIGQUIT:
! 148: case SIGTERM:
! 149: sig_term = signo;
! 150: break;
! 151: case SIGCHLD:
! 152: sig_chld = 1;
! 153: break;
! 154: case SIGALRM:
! 155: sig_alrm = 1;
! 156: break;
! 157: }
! 158: }
! 159:
! 160: static void
! 161: set_interval(double iv)
! 162: {
! 163: struct itimerval tim;
! 164:
! 165: memset(&tim, 0, sizeof(tim));
! 166: tim.it_value.tv_sec = (time_t)iv;
! 167: iv -= (double)tim.it_value.tv_sec;
! 168: tim.it_value.tv_usec = (suseconds_t)(iv * 1000000UL);
! 169:
! 170: if (setitimer(ITIMER_REAL, &tim, NULL) == -1)
! 171: err(EX_OSERR, "setitimer()");
! 172: }
! 173:
! 174: int
! 175: main(int argc, char **argv)
! 176: {
! 177: int ch;
! 178: unsigned long i;
! 179: int foreground, preserve;
! 180: int error, pstat, status;
! 181: int killsig = SIGTERM;
! 182: pid_t pgid, pid, cpid;
! 183: double first_kill;
! 184: double second_kill;
! 185: bool timedout = false;
! 186: bool do_second_kill = false;
! 187: struct sigaction signals;
! 188: int signums[] = {
! 189: -1,
! 190: SIGTERM,
! 191: SIGINT,
! 192: SIGHUP,
! 193: SIGCHLD,
! 194: SIGALRM,
! 195: SIGQUIT,
! 196: };
! 197:
! 198: setprogname(argv[0]);
! 199:
! 200: foreground = preserve = 0;
! 201: second_kill = 0;
! 202: cpid = -1;
! 203: pgid = -1;
! 204:
! 205: const struct option longopts[] = {
! 206: { "preserve-status", no_argument, &preserve, 1 },
! 207: { "foreground", no_argument, &foreground, 1 },
! 208: { "kill-after", required_argument, NULL, 'k'},
! 209: { "signal", required_argument, NULL, 's'},
! 210: { "help", no_argument, NULL, 'h'},
! 211: { NULL, 0, NULL, 0 }
! 212: };
! 213:
! 214: while ((ch = getopt_long(argc, argv, "+k:s:h", longopts, NULL)) != -1) {
! 215: switch (ch) {
! 216: case 'k':
! 217: do_second_kill = true;
! 218: second_kill = parse_duration(optarg);
! 219: break;
! 220: case 's':
! 221: killsig = parse_signal(optarg);
! 222: break;
! 223: case 0:
! 224: break;
! 225: case 'h':
! 226: default:
! 227: usage();
! 228: break;
! 229: }
! 230: }
! 231:
! 232: argc -= optind;
! 233: argv += optind;
! 234:
! 235: if (argc < 2)
! 236: usage();
! 237:
! 238: first_kill = parse_duration(argv[0]);
! 239: argc--;
! 240: argv++;
! 241:
! 242: if (!foreground) {
! 243: pgid = setpgid(0,0);
! 244:
! 245: if (pgid == -1)
! 246: err(EX_OSERR, "setpgid()");
! 247: }
! 248:
! 249: memset(&signals, 0, sizeof(signals));
! 250: sigemptyset(&signals.sa_mask);
! 251:
! 252: if (killsig != SIGKILL && killsig != SIGSTOP)
! 253: signums[0] = killsig;
! 254:
! 255: for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i ++)
! 256: sigaddset(&signals.sa_mask, signums[i]);
! 257:
! 258: signals.sa_handler = sig_handler;
! 259: signals.sa_flags = SA_RESTART;
! 260:
! 261: for (i = 0; i < sizeof(signums) / sizeof(signums[0]); i ++) {
! 262: if (signums[i] != -1 && signums[i] != 0 &&
! 263: sigaction(signums[i], &signals, NULL) == -1)
! 264: err(EX_OSERR, "sigaction()");
! 265: }
! 266:
! 267: signal(SIGTTIN, SIG_IGN);
! 268: signal(SIGTTOU, SIG_IGN);
! 269:
! 270: pid = fork();
! 271: if (pid == -1)
! 272: err(EX_OSERR, "fork()");
! 273: else if (pid == 0) {
! 274: /* child process */
! 275: signal(SIGTTIN, SIG_DFL);
! 276: signal(SIGTTOU, SIG_DFL);
! 277:
! 278: error = execvp(argv[0], argv);
! 279: if (error == -1)
! 280: err(EX_UNAVAILABLE, "exec()");
! 281: }
! 282:
! 283: if (sigprocmask(SIG_BLOCK, &signals.sa_mask, NULL) == -1)
! 284: err(EX_OSERR, "sigprocmask()");
! 285:
! 286: /* parent continues here */
! 287: set_interval(first_kill);
! 288:
! 289: for (;;) {
! 290: sigemptyset(&signals.sa_mask);
! 291: sigsuspend(&signals.sa_mask);
! 292:
! 293: if (sig_chld) {
! 294: sig_chld = 0;
! 295: while (((cpid = wait(&status)) < 0) && errno == EINTR)
! 296: continue;
! 297:
! 298: if (cpid == pid) {
! 299: pstat = status;
! 300: break;
! 301: }
! 302: } else if (sig_alrm) {
! 303: sig_alrm = 0;
! 304:
! 305: timedout = true;
! 306: if (!foreground)
! 307: killpg(pgid, killsig);
! 308: else
! 309: kill(pid, killsig);
! 310:
! 311: if (do_second_kill) {
! 312: set_interval(second_kill);
! 313: second_kill = 0;
! 314: sig_ign = killsig;
! 315: killsig = SIGKILL;
! 316: } else
! 317: break;
! 318:
! 319: } else if (sig_term) {
! 320: if (!foreground)
! 321: killpg(pgid, killsig);
! 322: else
! 323: kill(pid, (int)sig_term);
! 324:
! 325: if (do_second_kill) {
! 326: set_interval(second_kill);
! 327: second_kill = 0;
! 328: sig_ign = killsig;
! 329: killsig = SIGKILL;
! 330: } else
! 331: break;
! 332: }
! 333: }
! 334:
! 335: while (cpid != pid && wait(&pstat) == -1) {
! 336: if (errno != EINTR)
! 337: err(EX_OSERR, "waitpid()");
! 338: }
! 339:
! 340: if (WEXITSTATUS(pstat))
! 341: pstat = WEXITSTATUS(pstat);
! 342: else if(WIFSIGNALED(pstat))
! 343: pstat = 128 + WTERMSIG(pstat);
! 344:
! 345: if (timedout && !preserve)
! 346: pstat = EXIT_TIMEOUT;
! 347:
! 348: return (pstat);
! 349: }