aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2013-07-17 17:22:46 -0500
committerRob Landley <rob@landley.net>2013-07-17 17:22:46 -0500
commit72756670274dac9562b869761c50c59ed57b7295 (patch)
tree71fee36e35134086c91adfced91da837fa4002b8
parentd390493d76c4cda76c1c6d21897b0f246857e588 (diff)
downloadtoybox-72756670274dac9562b869761c50c59ed57b7295.tar.gz
Add timeout, factoring out common code from sleep.
-rw-r--r--lib/lib.h4
-rw-r--r--lib/xwrap.c37
-rw-r--r--toys/other/timeout.c74
-rw-r--r--toys/posix/sleep.c29
4 files changed, 122 insertions, 22 deletions
diff --git a/lib/lib.h b/lib/lib.h
index 907d96eb..98f4aadd 100644
--- a/lib/lib.h
+++ b/lib/lib.h
@@ -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);
}