timeout: --foreground, --preserve-status, and --signal.

--signal is simply a synonym for the exiting -s.

--foreground disables functionality we didn't yet have: putting the
child into a new process group. I've added the functionality and the
flag to disable it.

--preserve-status also makes it clear that our exit statuses didn't match
the coreutils version. In addition to callers that use --preserve-status
to get away from this madness, I also have callers that check for
specific exit values. This patch implements --preserve-status but also
fixes all the other exit statuses.

(The "125" exit value is broken for toybox in the same way that
`toybox grep --whoops ; echo $?` is. To fix this, we'd need some way to
signal that command-line parsing failures should exit with a different
value than the usual 1 --- 2 for grep, 125 for timeout. I've done as much
as grep manages, and left a TODO.)

Also add timeout tests. I couldn't think of an easy test for
--foreground, so I tested that manually with strace.

Also add some newlines to the `toybox --help` output to make it easier
to find the different sections, and expand the section on durations to
call out that fractions are supported as a matter of policy.

As long as timeout and sleep have text describing the duration syntax,
make them the same. (Personally I'd remove both in favor of the `toybox
--help` output, but as long as they're duplicated, keep them consistent.)

Also remove the SLEEP_FLOAT variant --- xparsetime means that sleep no
longer requires floating point to support sub-second resolution.
This commit is contained in:
Elliott Hughes 2019-03-09 17:41:49 -08:00 committed by Rob Landley
parent ac9eea4afb
commit 502b10c2ab
4 changed files with 60 additions and 22 deletions

View File

@ -28,11 +28,14 @@ config TOYBOX
--help Show command help (only)
--version Show toybox version (only)
The filename "-" means stdin/stdout, "--" stops argument parsing,
and numerical arguments accept a single letter suffix for
The filename "-" means stdin/stdout, and "--" stops argument parsing.
Numerical arguments accept a single letter suffix for
kilo, mega, giga, tera, peta, and exabytes, plus an additional
"d" to indicate decimal 1000's instead of 1024. Durations take
minute, hour, or day suffixes (ala 0.1m = 6s).
"d" to indicate decimal 1000's instead of 1024.
Durations can be decimal fractions and accept minute ("m"), hour ("h"),
or day ("d") suffixes (so 0.1m = 6s).
config TOYBOX_SUID
bool "SUID support"

19
tests/timeout.test Normal file
View File

@ -0,0 +1,19 @@
#!/bin/bash
[ -f testing.sh ] && . testing.sh
#testing "name" "command" "result" "infile" "stdin"
# timeout's exit value is complicated!
testcmd "times out" '.1 sleep 100 ; echo $?' '124\n' '' ''
testcmd "failure" '-s MONKEY .1 sleep 100 2>/dev/null ; echo $?' '125\n' '' ''
testcmd "can't execute" '.1 / 2>/dev/null ; echo $?' '126\n' '' ''
testcmd "can't find" '.1 /does/not/exist 2>/dev/null ; echo $?' '127\n' '' ''
testcmd "custom signal" '-s 3 .1 sleep 100; echo $?' '124\n' '' ''
testcmd "killed" '-s 9 .1 sleep 100; echo $?' '137\n' '' ''
testcmd "TERM" '-s TERM .1 sleep 100; echo $?' '124\n' '' ''
testcmd "exit 0" '.1 true ; echo $?' '0\n' '' ''
testcmd "exit 1" '.1 false ; echo $?' '1\n' '' ''
testcmd "--preserve-status" '--preserve-status .1 sleep 100 ; echo $?' '143\n' '' ''
testcmd "--preserve-status killed" '--preserve-status -s 9 .1 sleep 100 ; echo $?' '137\n' '' ''

View File

@ -4,24 +4,26 @@
*
* No standard
USE_TIMEOUT(NEWTOY(timeout, "<2^vk:s: ", TOYFLAG_USR|TOYFLAG_BIN))
USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):", TOYFLAG_USR|TOYFLAG_BIN))
config TIMEOUT
bool "timeout"
default y
depends on TOYBOX_FLOAT
help
usage: timeout [-k LENGTH] [-s SIGNAL] LENGTH COMMAND...
usage: timeout [-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.
Length can be a decimal fraction. An optional suffix can be "m"
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)
-k Send KILL signal if child still running this long after first signal
-v Verbose
--foreground Don't create new process group
--preserve-status Exit with the child's exit status
*/
#define FOR_timeout
@ -38,10 +40,11 @@ GLOBALS(
static void handler(int i)
{
if (toys.optflags & FLAG_v)
if (FLAG(v))
fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig);
kill(TT.pid, TT.nextsig);
if (TT.k) {
TT.k = 0;
TT.nextsig = SIGKILL;
@ -63,6 +66,11 @@ void xparsetimeval(char *s, struct timeval *tv)
void timeout_main(void)
{
// If timeout fails to parse its arguments, it exits with 125.
// TODO: this and grep both have a bug where built-in error checking like
// "too few arguments" will exit 1 instead of the custom value.
toys.exitval = 125;
// Parse early to get any errors out of the way.
xparsetimeval(*toys.optargs, &TT.itv.it_value);
if (TT.k) xparsetimeval(TT.k, &TT.ktv);
@ -71,10 +79,24 @@ void timeout_main(void)
if (TT.s && -1 == (TT.nextsig = sig_to_num(TT.s)))
error_exit("bad -s: '%s'", TT.s);
if (!(TT.pid = XVFORK())) xexec(toys.optargs+1);
else {
if (!FLAG(foreground)) setpgid(0, 0);
if (!(TT.pid = XVFORK())) {
char **argv = toys.optargs+1;
execvp(argv[0], argv);
perror_msg("failed to run '%s'", argv[0]);
toys.exitval = (errno == ENOENT) ? 127 : 126;
_xexit();
} else {
int status;
xsignal(SIGALRM, handler);
setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf);
toys.exitval = xwaitpid(TT.pid);
while (-1 == waitpid(TT.pid, &status, 0) && errno == EINTR);
if (WIFEXITED(status)) toys.exitval = WEXITSTATUS(status);
else if (WTERMSIG(status)==SIGKILL) toys.exitval = 137;
else toys.exitval = FLAG(preserve_status) ? 128+WTERMSIG(status) : 124;
}
}

View File

@ -11,18 +11,12 @@ config SLEEP
bool "sleep"
default y
help
usage: sleep LENGTH
usage: sleep DURATION
Wait before exiting. An optional suffix can be "m" (minutes), "h" (hours),
"d" (days), or "s" (seconds, the default).
Wait before exiting.
config SLEEP_FLOAT
bool
default y
depends on SLEEP && TOYBOX_FLOAT
help
Length can be a decimal fraction.
DURATION can be a decimal fraction. An optional suffix can be "m"
(minutes), "h" (hours), "d" (days), or "s" (seconds, the default).
*/
#include "toys.h"