changeset 952:ce0519f6457c

Add timeout, factoring out common code from sleep.
author Rob Landley <rob@landley.net>
date Wed, 17 Jul 2013 17:22:46 -0500
parents 62d59b8aea34
children 13916d161ec0
files lib/lib.h lib/xwrap.c toys/other/timeout.c toys/posix/sleep.c
diffstat 4 files changed, 122 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/lib/lib.h	Tue Jul 16 00:04:56 2013 -0500
+++ b/lib/lib.h	Wed Jul 17 17:22:46 2013 -0500
@@ -85,7 +85,7 @@
 
 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 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 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);
--- a/lib/xwrap.c	Tue Jul 16 00:04:56 2013 -0500
+++ b/lib/xwrap.c	Wed Jul 17 17:22:46 2013 -0500
@@ -112,6 +112,17 @@
   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 @@
     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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/other/timeout.c	Wed Jul 17 17:22:46 2013 -0500
@@ -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;
+  }
+}
--- a/toys/posix/sleep.c	Tue Jul 16 00:04:56 2013 -0500
+++ b/toys/posix/sleep.c	Wed Jul 17 17:22:46 2013 -0500
@@ -11,39 +11,26 @@
   bool "sleep"
   default y
   help
-    usage: sleep SECONDS
+    usage: sleep LENGTH
 
-    Wait before exiting.
+    Wait before exiting. An optional suffix can be "m" (minutes), "h" (hours),
+    "d" (days), or "s" (seconds, the default).
+
 
 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)
 {
-
-  if (!CFG_TOYBOX_FLOAT) toys.exitval = sleep(atol(*toys.optargs));
-  else {
-    char *arg;
-    double d = strtod(*toys.optargs, &arg);
-    struct timespec tv;
+  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);
 }