172756670274dac9562b869761c50c59ed57b7295Rob Landley/* timeout.c - Run command line with a timeout
272756670274dac9562b869761c50c59ed57b7295Rob Landley *
372756670274dac9562b869761c50c59ed57b7295Rob Landley * Copyright 2013 Rob Landley <rob@landley.net>
472756670274dac9562b869761c50c59ed57b7295Rob Landley *
572756670274dac9562b869761c50c59ed57b7295Rob Landley * No standard
672756670274dac9562b869761c50c59ed57b7295Rob Landley
78a3c0edadbf915b9d2f318412587f6107a8e6d42Rob LandleyUSE_TIMEOUT(NEWTOY(timeout, "<2^vk:s: ", TOYFLAG_BIN))
872756670274dac9562b869761c50c59ed57b7295Rob Landley
972756670274dac9562b869761c50c59ed57b7295Rob Landleyconfig TIMEOUT
1072756670274dac9562b869761c50c59ed57b7295Rob Landley  bool "timeout"
1172756670274dac9562b869761c50c59ed57b7295Rob Landley  default y
1272756670274dac9562b869761c50c59ed57b7295Rob Landley  depends on TOYBOX_FLOAT
1372756670274dac9562b869761c50c59ed57b7295Rob Landley  help
1472756670274dac9562b869761c50c59ed57b7295Rob Landley    usage: timeout [-k LENGTH] [-s SIGNAL] LENGTH COMMAND...
1572756670274dac9562b869761c50c59ed57b7295Rob Landley
1672756670274dac9562b869761c50c59ed57b7295Rob Landley    Run command line as a child process, sending child a signal if the
1772756670274dac9562b869761c50c59ed57b7295Rob Landley    command doesn't exit soon enough.
1872756670274dac9562b869761c50c59ed57b7295Rob Landley
1972756670274dac9562b869761c50c59ed57b7295Rob Landley    Length can be a decimal fraction. An optional suffix can be "m"
2072756670274dac9562b869761c50c59ed57b7295Rob Landley    (minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
2172756670274dac9562b869761c50c59ed57b7295Rob Landley
2272756670274dac9562b869761c50c59ed57b7295Rob Landley    -s	Send specified signal (default TERM)
2372756670274dac9562b869761c50c59ed57b7295Rob Landley    -k	Send KILL signal if child still running this long after first signal.
248a3c0edadbf915b9d2f318412587f6107a8e6d42Rob Landley    -v	Verbose
2572756670274dac9562b869761c50c59ed57b7295Rob Landley*/
2672756670274dac9562b869761c50c59ed57b7295Rob Landley
2772756670274dac9562b869761c50c59ed57b7295Rob Landley#define FOR_timeout
2872756670274dac9562b869761c50c59ed57b7295Rob Landley#include "toys.h"
2972756670274dac9562b869761c50c59ed57b7295Rob Landley
3072756670274dac9562b869761c50c59ed57b7295Rob LandleyGLOBALS(
3172756670274dac9562b869761c50c59ed57b7295Rob Landley  char *s_signal;
3272756670274dac9562b869761c50c59ed57b7295Rob Landley  char *k_timeout;
3372756670274dac9562b869761c50c59ed57b7295Rob Landley
3472756670274dac9562b869761c50c59ed57b7295Rob Landley  int nextsig;
3572756670274dac9562b869761c50c59ed57b7295Rob Landley  pid_t pid;
3672756670274dac9562b869761c50c59ed57b7295Rob Landley  struct timeval ktv;
3772756670274dac9562b869761c50c59ed57b7295Rob Landley  struct itimerval itv;
3872756670274dac9562b869761c50c59ed57b7295Rob Landley)
3972756670274dac9562b869761c50c59ed57b7295Rob Landley
4072756670274dac9562b869761c50c59ed57b7295Rob Landleystatic void handler(int i)
4172756670274dac9562b869761c50c59ed57b7295Rob Landley{
428a3c0edadbf915b9d2f318412587f6107a8e6d42Rob Landley  fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig);
4372756670274dac9562b869761c50c59ed57b7295Rob Landley  kill(TT.pid, TT.nextsig);
4472756670274dac9562b869761c50c59ed57b7295Rob Landley
4572756670274dac9562b869761c50c59ed57b7295Rob Landley  if (TT.k_timeout) {
4672756670274dac9562b869761c50c59ed57b7295Rob Landley    TT.k_timeout = 0;
4772756670274dac9562b869761c50c59ed57b7295Rob Landley    TT.nextsig = SIGKILL;
48c776bde13bb2767db2943bc3b02df737a465c035Rob Landley    xsignal(SIGALRM, handler);
4972756670274dac9562b869761c50c59ed57b7295Rob Landley    TT.itv.it_value = TT.ktv;
508a3c0edadbf915b9d2f318412587f6107a8e6d42Rob Landley    setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
5172756670274dac9562b869761c50c59ed57b7295Rob Landley  }
5272756670274dac9562b869761c50c59ed57b7295Rob Landley}
5372756670274dac9562b869761c50c59ed57b7295Rob Landley
5472756670274dac9562b869761c50c59ed57b7295Rob Landleyvoid timeout_main(void)
5572756670274dac9562b869761c50c59ed57b7295Rob Landley{
5672756670274dac9562b869761c50c59ed57b7295Rob Landley  // Parse early to get any errors out of the way.
5772756670274dac9562b869761c50c59ed57b7295Rob Landley  TT.itv.it_value.tv_sec = xparsetime(*toys.optargs, 1000000, &TT.itv.it_value.tv_usec);
5872756670274dac9562b869761c50c59ed57b7295Rob Landley
5972756670274dac9562b869761c50c59ed57b7295Rob Landley  if (TT.k_timeout)
6072756670274dac9562b869761c50c59ed57b7295Rob Landley    TT.ktv.tv_sec = xparsetime(TT.k_timeout, 1000000, &TT.ktv.tv_usec);
6172756670274dac9562b869761c50c59ed57b7295Rob Landley  TT.nextsig = SIGTERM;
6272756670274dac9562b869761c50c59ed57b7295Rob Landley  if (TT.s_signal && -1 == (TT.nextsig = sig_to_num(TT.s_signal)))
6372756670274dac9562b869761c50c59ed57b7295Rob Landley    error_exit("bad -s: '%s'", TT.s_signal);
6472756670274dac9562b869761c50c59ed57b7295Rob Landley
65c0045207a7cd3bc11aace920d895c69b027c16afRob Landley  if (!(TT.pid = xfork())) xexec(toys.optargs+1);
6672756670274dac9562b869761c50c59ed57b7295Rob Landley  else {
6772756670274dac9562b869761c50c59ed57b7295Rob Landley    int status;
6872756670274dac9562b869761c50c59ed57b7295Rob Landley
69c776bde13bb2767db2943bc3b02df737a465c035Rob Landley    xsignal(SIGALRM, handler);
708a3c0edadbf915b9d2f318412587f6107a8e6d42Rob Landley    setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
7172756670274dac9562b869761c50c59ed57b7295Rob Landley    while (-1 == waitpid(TT.pid, &status, 0) && errno == EINTR);
72d8872c43b48eae5501998a4e5a84337017d8fbe6Rob Landley    toys.exitval = WIFEXITED(status)
73d8872c43b48eae5501998a4e5a84337017d8fbe6Rob Landley      ? WEXITSTATUS(status) : WTERMSIG(status) + 127;
7472756670274dac9562b869761c50c59ed57b7295Rob Landley  }
7572756670274dac9562b869761c50c59ed57b7295Rob Landley}
76