diff options
author | Rob Landley <rob@landley.net> | 2013-07-17 17:22:46 -0500 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2013-07-17 17:22:46 -0500 |
commit | 72756670274dac9562b869761c50c59ed57b7295 (patch) | |
tree | 71fee36e35134086c91adfced91da837fa4002b8 | |
parent | d390493d76c4cda76c1c6d21897b0f246857e588 (diff) | |
download | toybox-72756670274dac9562b869761c50c59ed57b7295.tar.gz |
Add timeout, factoring out common code from sleep.
-rw-r--r-- | lib/lib.h | 4 | ||||
-rw-r--r-- | lib/xwrap.c | 37 | ||||
-rw-r--r-- | toys/other/timeout.c | 74 | ||||
-rw-r--r-- | toys/posix/sleep.c | 29 |
4 files changed, 122 insertions, 22 deletions
@@ -85,7 +85,7 @@ struct dirtree *dirtree_read(char *path, int (*callback)(struct dirtree *node)); void show_help(void); -// xfuncs.c +// xwrap.c void xstrncpy(char *dest, char *src, size_t size); void xexit(void) noreturn; void *xmalloc(size_t size); @@ -98,6 +98,7 @@ void xprintf(char *format, ...); void xputs(char *s); void xputc(char c); void xflush(void); +void xexec_optargs(int skip); void xexec(char **argv); void xaccess(char *path, int flags); void xunlink(char *path); @@ -120,6 +121,7 @@ void xchdir(char *path); void xmkpath(char *path, int mode); void xsetuid(uid_t uid); char *xreadlink(char *name); +long xparsetime(char *arg, long units, long *fraction); // lib.c void verror_msg(char *msg, int err, va_list va); diff --git a/lib/xwrap.c b/lib/xwrap.c index b0029eef..a0f64fd2 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -112,6 +112,17 @@ void xflush(void) if (fflush(stdout)) perror_exit("write");; } +// Call xexec with a chunk of optargs, starting at skip. (You can't just +// call xexec() directly because toy_init() frees optargs.) +void xexec_optargs(int skip) +{ + char **s = toys.optargs; + + toys.optargs = 0; + xexec(s+skip); +} + + // Die unless we can exec argv[] (or run builtin command). Note that anything // with a path isn't a builtin, so /bin/sh won't match the builtin sh. void xexec(char **argv) @@ -468,3 +479,29 @@ void xsendfile(int in, int out) xwrite(out, buf, len); } } + +// parse fractional seconds with optional s/m/h/d suffix +long xparsetime(char *arg, long units, long *fraction) +{ + double d; + long l; + + if (CFG_TOYBOX_FLOAT) d = strtod(arg, &arg); + else l = strtoul(arg, &arg, 10); + + // Parse suffix + if (*arg) { + int ismhd[]={1,60,3600,86400}, i = stridx("smhd", *arg); + + if (i == -1) error_exit("Unknown suffix '%c'", *arg); + if (CFG_TOYBOX_FLOAT) d *= ismhd[i]; + else l *= ismhd[i]; + } + + if (CFG_TOYBOX_FLOAT) { + l = (long)d; + if (fraction) *fraction = units*(d-l); + } else if (fraction) *fraction = 0; + + return l; +} diff --git a/toys/other/timeout.c b/toys/other/timeout.c new file mode 100644 index 00000000..4b8a87df --- /dev/null +++ b/toys/other/timeout.c @@ -0,0 +1,74 @@ +/* timeout.c - Run command line with a timeout + * + * Copyright 2013 Rob Landley <rob@landley.net> + * + * No standard + +USE_TIMEOUT(NEWTOY(timeout, "<2^k:s: ", TOYFLAG_BIN)) + +config TIMEOUT + bool "timeout" + default y + depends on TOYBOX_FLOAT + help + usage: timeout [-k LENGTH] [-s SIGNAL] LENGTH 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" + (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. +*/ + +#define FOR_timeout +#include "toys.h" + +GLOBALS( + char *s_signal; + char *k_timeout; + + int nextsig; + pid_t pid; + struct timeval ktv; + struct itimerval itv; +) + +static void handler(int i) +{ + kill(TT.pid, TT.nextsig); + + if (TT.k_timeout) { + TT.k_timeout = 0; + TT.nextsig = SIGKILL; + signal(SIGALRM, handler); + TT.itv.it_value = TT.ktv; + setitimer(ITIMER_REAL, &TT.itv, (void *)&toybuf); + } +} + +void timeout_main(void) +{ + // Parse early to get any errors out of the way. + TT.itv.it_value.tv_sec = xparsetime(*toys.optargs, 1000000, &TT.itv.it_value.tv_usec); + + if (TT.k_timeout) + TT.ktv.tv_sec = xparsetime(TT.k_timeout, 1000000, &TT.ktv.tv_usec); + TT.nextsig = SIGTERM; + if (TT.s_signal && -1 == (TT.nextsig = sig_to_num(TT.s_signal))) + error_exit("bad -s: '%s'", TT.s_signal); + + if (!(TT.pid = fork())) xexec_optargs(1); + else { + int status; + + signal(SIGALRM, handler); + setitimer(ITIMER_REAL, &TT.itv, (void *)&toybuf); + while (-1 == waitpid(TT.pid, &status, 0) && errno == EINTR); + if (WIFEXITED(status)) toys.exitval = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) toys.exitval = WTERMSIG(status); + else toys.exitval = 1; + } +} diff --git a/toys/posix/sleep.c b/toys/posix/sleep.c index a83f1e56..c7b8bbf1 100644 --- a/toys/posix/sleep.c +++ b/toys/posix/sleep.c @@ -11,39 +11,26 @@ config SLEEP bool "sleep" default y help - usage: sleep SECONDS + usage: sleep LENGTH + + 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 - The delay can be a decimal fraction. An optional suffix can be "m" - (minutes), "h" (hours), "d" (days), or "s" (seconds, the default). + Length can be a decimal fraction. */ #include "toys.h" void sleep_main(void) { + struct timespec tv; - if (!CFG_TOYBOX_FLOAT) toys.exitval = sleep(atol(*toys.optargs)); - else { - char *arg; - double d = strtod(*toys.optargs, &arg); - struct timespec tv; - - // Parse suffix - if (*arg) { - int ismhd[]={1,60,3600,86400}; - char *smhd = "smhd", *c = strchr(smhd, *arg); - if (!c) error_exit("Unknown suffix '%c'", *arg); - d *= ismhd[c-smhd]; - } - - tv.tv_nsec=1000000000*(d-(tv.tv_sec = (unsigned long)d)); - toys.exitval = !!nanosleep(&tv, NULL); - } + tv.tv_sec = xparsetime(*toys.optargs, 1000000000, &tv.tv_nsec); + toys.exitval = !!nanosleep(&tv, NULL); } |