From 6b03865e9e7c8edbc0fd54a809410fb71b741de2 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Tue, 5 Jul 2022 06:31:08 -0500 Subject: [PATCH] Add timeout -i and update tests. --- tests/timeout.test | 9 ++++- toys/other/timeout.c | 88 ++++++++++++++++++++++++++------------------ 2 files changed, 60 insertions(+), 37 deletions(-) mode change 100644 => 100755 tests/timeout.test diff --git a/tests/timeout.test b/tests/timeout.test old mode 100644 new mode 100755 index 18f93790..386441a6 --- a/tests/timeout.test +++ b/tests/timeout.test @@ -22,7 +22,7 @@ testcmd "--preserve-status killed" '--preserve-status -s 9 .1 sleep 100 ; echo $ # There's another special case where if the subprocess catches our timeout # signal and exits, we need to report that as a timeout (unless overridden). cat > loop.sh <&1 | egrep -o 'TERM|sleep'" \ + 'TERM\nsleep\n' '' '' + diff --git a/toys/other/timeout.c b/toys/other/timeout.c index e93a806c..899bca61 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,69 @@ config TIMEOUT GLOBALS( char *s, *k; - int nextsig; - pid_t pid; - struct timespec kts; - struct itimerspec its; - timer_t timer; + struct pollfd pfd; ) -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"); - } + close(TT.pfd.fd); + TT.pfd.fd = -1; +} + +static long nantomil(struct timespec *ts) +{ + return ts->tv_sec*1000+ts->tv_nsec/1000000; } void timeout_main(void) { - struct sigevent se = { .sigev_notify = SIGEV_SIGNAL, .sigev_signo = SIGALRM }; + int fds[] = {0, -1}, ii, ms, nextsig, pid; + 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); + xparsetimespec(*toys.optargs, &tts); + if (TT.k) xparsetimespec(TT.k, &kts); - TT.nextsig = SIGTERM; - if (TT.s && -1 == (TT.nextsig = sig_to_num(TT.s))) + nextsig = SIGTERM; + if (TT.s && -1 == (nextsig = sig_to_num(TT.s))) error_exit("bad -s: '%s'", TT.s); if (!FLAG(foreground)) setpgid(0, 0); 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; + pid = xpopen_both(toys.optargs+1, FLAG(i) ? fds : 0); + if (!FLAG(i)) pipe(fds); + TT.pfd.events = POLLIN; + TT.pfd.fd = fds[1]; + ms = nantomil(&tts); + xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP); + for (;;) { + if (1 != xpoll(&TT.pfd, 1, ms)) { + if (-1==TT.pfd.fd) break; + 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(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(fds[1], toybuf, sizeof(toybuf)))) { + if (errno==EINTR) continue; + break; + } + writeall(1, toybuf, ii); + } + if (TT.pfd.revents&POLLHUP) break; } + xsignal(SIGCHLD, SIG_DFL); + ii = xpclose_both(pid, fds); + + if (FLAG(preserve_status) || !toys.exitval) toys.exitval = ii; } -- 2.39.2