toybox/toys/other/timeout.c
<<
>>
Prefs
   1/* timeout.c - Run command line with a timeout
   2 *
   3 * Copyright 2013 Rob Landley <rob@landley.net>
   4 *
   5 * No standard
   6
   7USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
   8
   9config TIMEOUT
  10  bool "timeout"
  11  default y
  12  help
  13    usage: timeout [-k DURATION] [-s SIGNAL] DURATION COMMAND...
  14
  15    Run command line as a child process, sending child a signal if the
  16    command doesn't exit soon enough.
  17
  18    DURATION can be a decimal fraction. An optional suffix can be "m"
  19    (minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
  20
  21    -s  Send specified signal (default TERM)
  22    -k  Send KILL signal if child still running this long after first signal
  23    -v  Verbose
  24    --foreground       Don't create new process group
  25    --preserve-status  Exit with the child's exit status
  26*/
  27
  28#define FOR_timeout
  29#include "toys.h"
  30
  31GLOBALS(
  32  char *s, *k;
  33
  34  int nextsig;
  35  pid_t pid;
  36  struct timeval ktv;
  37  struct itimerval itv;
  38)
  39
  40static void handler(int i)
  41{
  42  if (FLAG(v))
  43    fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig);
  44
  45  kill(TT.pid, TT.nextsig);
  46
  47  if (TT.k) {
  48    TT.k = 0;
  49    TT.nextsig = SIGKILL;
  50    xsignal(SIGALRM, handler);
  51    TT.itv.it_value = TT.ktv;
  52    setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
  53  }
  54}
  55
  56// timeval inexplicably makes up a new type for microseconds, despite timespec's
  57// nanoseconds field (needing to store 1000* the range) using "long". Bravo.
  58void xparsetimeval(char *s, struct timeval *tv)
  59{
  60  long ll;
  61
  62  tv->tv_sec = xparsetime(s, 6, &ll);
  63  tv->tv_usec = ll;
  64}
  65
  66void timeout_main(void)
  67{
  68  toys.exitval = 125;
  69
  70  // Parse early to get any errors out of the way.
  71  xparsetimeval(*toys.optargs, &TT.itv.it_value);
  72  if (TT.k) xparsetimeval(TT.k, &TT.ktv);
  73
  74  TT.nextsig = SIGTERM;
  75  if (TT.s && -1 == (TT.nextsig = sig_to_num(TT.s)))
  76    error_exit("bad -s: '%s'", TT.s);
  77
  78  if (!FLAG(foreground)) setpgid(0, 0);
  79
  80  if (!(TT.pid = XVFORK())) xexec(toys.optargs+1);
  81  else {
  82    int status;
  83
  84    xsignal(SIGALRM, handler);
  85    setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
  86
  87    while (-1 == waitpid(TT.pid, &status, 0) && errno == EINTR);
  88    if (WIFEXITED(status)) toys.exitval = WEXITSTATUS(status);
  89    else if (WTERMSIG(status)==SIGKILL) toys.exitval = 137;
  90    else toys.exitval = FLAG(preserve_status) ? 128+WTERMSIG(status) : 124;
  91  }
  92}
  93