diff options
Diffstat (limited to 'toys/other/timeout.c')
-rw-r--r-- | toys/other/timeout.c | 104 |
1 files changed, 64 insertions, 40 deletions
diff --git a/toys/other/timeout.c b/toys/other/timeout.c index e93a806c..ed4956a4 100644 --- a/toys/other/timeout.c +++ b/toys/other/timeout.c @@ -4,13 +4,13 @@ * * No standard -USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125))) +USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):i", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125))) config TIMEOUT bool "timeout" default y help - usage: timeout [-k DURATION] [-s SIGNAL] DURATION COMMAND... + usage: timeout [-i] [-k DURATION] [-s SIGNAL] DURATION COMMAND... Run command line as a child process, sending child a signal if the command doesn't exit soon enough. @@ -18,8 +18,9 @@ config TIMEOUT DURATION can be a decimal fraction. An optional suffix can be "m" (minutes), "h" (hours), "d" (days), or "s" (seconds, the default). - -s Send specified signal (default TERM) + -i Only kill for inactivity (restart timeout when command produces output) -k Send KILL signal if child still running this long after first signal + -s Send specified signal (default TERM) -v Verbose --foreground Don't create new process group --preserve-status Exit with the child's exit status @@ -31,54 +32,77 @@ config TIMEOUT GLOBALS( char *s, *k; - int nextsig; - pid_t pid; - struct timespec kts; - struct itimerspec its; - timer_t timer; + struct pollfd pfd; + sigjmp_buf sj; + int fds[2], pid; ) -static void handler(int i) +static void handler(int sig) { - if (FLAG(v)) - fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig); - - toys.exitval = (TT.nextsig==9) ? 137 : 124; - kill(TT.pid, TT.nextsig); - if (TT.k) { - TT.k = 0; - TT.nextsig = SIGKILL; - xsignal(SIGALRM, handler); - TT.its.it_value = TT.kts; - if (timer_settime(TT.timer, 0, &TT.its, 0)) perror_exit("timer_settime"); - } + siglongjmp(TT.sj, 1); +} + +static long nantomil(struct timespec *ts) +{ + return ts->tv_sec*1000+ts->tv_nsec/1000000; +} + +static void callback(char *argv[]) +{ + xsignal(SIGCHLD, SIG_DFL); + if (!FLAG(foreground)) setpgid(0, 0); } void timeout_main(void) { - struct sigevent se = { .sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGALRM }; + int ii, ms, nextsig = SIGTERM; + struct timespec tts, kts; // Use same ARGFAIL value for any remaining parsing errors toys.exitval = 125; - xparsetimespec(*toys.optargs, &TT.its.it_value); - if (TT.k) xparsetimespec(TT.k, &TT.kts); - - TT.nextsig = SIGTERM; - if (TT.s && -1 == (TT.nextsig = sig_to_num(TT.s))) - error_exit("bad -s: '%s'", TT.s); - - if (!FLAG(foreground)) setpgid(0, 0); + xparsetimespec(*toys.optargs, &tts); + if (TT.k) xparsetimespec(TT.k, &kts); + if (TT.s && -1==(nextsig = sig_to_num(TT.s))) error_exit("bad -s: '%s'",TT.s); toys.exitval = 0; - if (!(TT.pid = XVFORK())) xexec(toys.optargs+1); - else { - int status; - - xsignal(SIGALRM, handler); - if (timer_create(CLOCK_MONOTONIC, &se, &TT.timer)) perror_exit("timer"); - if (timer_settime(TT.timer, 0, &TT.its, 0)) perror_exit("timer_settime"); - - status = xwaitpid(TT.pid); - if (FLAG(preserve_status) || !toys.exitval) toys.exitval = status; + TT.pfd.events = POLLIN; + TT.fds[1] = -1; + if (sigsetjmp(TT.sj, 1)) goto done; + xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP); + + TT.pid = xpopen_setup(toys.optargs+1, FLAG(i) ? TT.fds : 0, callback); + xsignal(SIGTTIN, SIG_IGN); + xsignal(SIGTTOU, SIG_IGN); + xsignal(SIGTSTP, SIG_IGN); + if (!FLAG(i)) xpipe(TT.fds); + TT.pfd.fd = TT.fds[1]; + ms = nantomil(&tts); + for (;;) { + if (1 != xpoll(&TT.pfd, 1, ms)) { + if (FLAG(v)) + perror_msg("sending signal %s to command %s", num_to_sig(nextsig), + toys.optargs[1]); + toys.exitval = (nextsig==9) ? 137 : 124; + kill(FLAG(foreground) ? TT.pid : -TT.pid, nextsig); + if (!TT.k || nextsig==SIGKILL) break; + nextsig = SIGKILL; + ms = nantomil(&kts); + + continue; + } + if (TT.pfd.revents&POLLIN) { + errno = 0; + if (1>(ii = read(TT.fds[1], toybuf, sizeof(toybuf)))) { + if (errno==EINTR) continue; + break; + } + writeall(1, toybuf, ii); + } + if (TT.pfd.revents&POLLHUP) break; } +done: + xsignal(SIGCHLD, SIG_DFL); + ii = xpclose_both(TT.pid, TT.fds); + + if (FLAG(preserve_status) || !toys.exitval) toys.exitval = ii; } |