10f66f451Sopenharmony_ci/* timeout.c - Run command line with a timeout
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2013 Rob Landley <rob@landley.net>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * No standard
60f66f451Sopenharmony_ci
70f66f451Sopenharmony_ciUSE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125)))
80f66f451Sopenharmony_ci
90f66f451Sopenharmony_ciconfig TIMEOUT
100f66f451Sopenharmony_ci  bool "timeout"
110f66f451Sopenharmony_ci  default y
120f66f451Sopenharmony_ci  help
130f66f451Sopenharmony_ci    usage: timeout [-k DURATION] [-s SIGNAL] DURATION COMMAND...
140f66f451Sopenharmony_ci
150f66f451Sopenharmony_ci    Run command line as a child process, sending child a signal if the
160f66f451Sopenharmony_ci    command doesn't exit soon enough.
170f66f451Sopenharmony_ci
180f66f451Sopenharmony_ci    DURATION can be a decimal fraction. An optional suffix can be "m"
190f66f451Sopenharmony_ci    (minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
200f66f451Sopenharmony_ci
210f66f451Sopenharmony_ci    -s	Send specified signal (default TERM)
220f66f451Sopenharmony_ci    -k	Send KILL signal if child still running this long after first signal
230f66f451Sopenharmony_ci    -v	Verbose
240f66f451Sopenharmony_ci    --foreground       Don't create new process group
250f66f451Sopenharmony_ci    --preserve-status  Exit with the child's exit status
260f66f451Sopenharmony_ci*/
270f66f451Sopenharmony_ci
280f66f451Sopenharmony_ci#define FOR_timeout
290f66f451Sopenharmony_ci#include "toys.h"
300f66f451Sopenharmony_ci
310f66f451Sopenharmony_ciGLOBALS(
320f66f451Sopenharmony_ci  char *s, *k;
330f66f451Sopenharmony_ci
340f66f451Sopenharmony_ci  int nextsig;
350f66f451Sopenharmony_ci  pid_t pid;
360f66f451Sopenharmony_ci  struct timeval ktv;
370f66f451Sopenharmony_ci  struct itimerval itv;
380f66f451Sopenharmony_ci)
390f66f451Sopenharmony_ci
400f66f451Sopenharmony_cistatic void handler(int i)
410f66f451Sopenharmony_ci{
420f66f451Sopenharmony_ci  if (FLAG(v))
430f66f451Sopenharmony_ci    fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig);
440f66f451Sopenharmony_ci
450f66f451Sopenharmony_ci  toys.exitval = (TT.nextsig==9) ? 137 : 124;
460f66f451Sopenharmony_ci  kill(TT.pid, TT.nextsig);
470f66f451Sopenharmony_ci  if (TT.k) {
480f66f451Sopenharmony_ci    TT.k = 0;
490f66f451Sopenharmony_ci    TT.nextsig = SIGKILL;
500f66f451Sopenharmony_ci    xsignal(SIGALRM, handler);
510f66f451Sopenharmony_ci    TT.itv.it_value = TT.ktv;
520f66f451Sopenharmony_ci    setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
530f66f451Sopenharmony_ci  }
540f66f451Sopenharmony_ci}
550f66f451Sopenharmony_ci
560f66f451Sopenharmony_ci// timeval inexplicably makes up a new type for microseconds, despite timespec's
570f66f451Sopenharmony_ci// nanoseconds field (needing to store 1000* the range) using "long". Bravo.
580f66f451Sopenharmony_civoid xparsetimeval(char *s, struct timeval *tv)
590f66f451Sopenharmony_ci{
600f66f451Sopenharmony_ci  long ll;
610f66f451Sopenharmony_ci
620f66f451Sopenharmony_ci  tv->tv_sec = xparsetime(s, 6, &ll);
630f66f451Sopenharmony_ci  tv->tv_usec = ll;
640f66f451Sopenharmony_ci}
650f66f451Sopenharmony_ci
660f66f451Sopenharmony_civoid timeout_main(void)
670f66f451Sopenharmony_ci{
680f66f451Sopenharmony_ci  // Use same ARGFAIL value for any remaining parsing errors
690f66f451Sopenharmony_ci  toys.exitval = 125;
700f66f451Sopenharmony_ci  xparsetimeval(*toys.optargs, &TT.itv.it_value);
710f66f451Sopenharmony_ci  if (TT.k) xparsetimeval(TT.k, &TT.ktv);
720f66f451Sopenharmony_ci
730f66f451Sopenharmony_ci  TT.nextsig = SIGTERM;
740f66f451Sopenharmony_ci  if (TT.s && -1 == (TT.nextsig = sig_to_num(TT.s)))
750f66f451Sopenharmony_ci    error_exit("bad -s: '%s'", TT.s);
760f66f451Sopenharmony_ci
770f66f451Sopenharmony_ci  if (!FLAG(foreground)) setpgid(0, 0);
780f66f451Sopenharmony_ci
790f66f451Sopenharmony_ci  toys.exitval = 0;
800f66f451Sopenharmony_ci  if (!(TT.pid = XVFORK())) xexec(toys.optargs+1);
810f66f451Sopenharmony_ci  else {
820f66f451Sopenharmony_ci    int status;
830f66f451Sopenharmony_ci
840f66f451Sopenharmony_ci    xsignal(SIGALRM, handler);
850f66f451Sopenharmony_ci    setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
860f66f451Sopenharmony_ci
870f66f451Sopenharmony_ci    status = xwaitpid(TT.pid);
880f66f451Sopenharmony_ci    if (FLAG(preserve_status) || !toys.exitval) toys.exitval = status;
890f66f451Sopenharmony_ci  }
900f66f451Sopenharmony_ci}
91