summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2016-10-15 20:35:23 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2016-10-15 20:35:23 +0000
commitff4ac0ca4b41deba5e3cb100bee23431c694963f (patch)
treeee3a1f9f0b76a8e6aa8201d93ebda12ae48a9f36
parent86ae96a366603fdf6795692a9f089272bea424aa (diff)
downloadexecline-ff4ac0ca4b41deba5e3cb100bee23431c694963f.tar.gz
execline-ff4ac0ca4b41deba5e3cb100bee23431c694963f.tar.xz
execline-ff4ac0ca4b41deba5e3cb100bee23431c694963f.zip
Implement a timeout in wait
-rw-r--r--doc/wait.html18
-rw-r--r--src/execline/wait.c125
2 files changed, 115 insertions, 28 deletions
diff --git a/doc/wait.html b/doc/wait.html
index 94587a1..913e17c 100644
--- a/doc/wait.html
+++ b/doc/wait.html
@@ -29,7 +29,7 @@
 </p>
 
 <pre>
-     wait [ -r ] { [ <em>pids...</em> ] } <em>prog...</em>
+     wait [ -I | -i ] [ -r | -t <em>timeout</em> ] { [ <em>pids...</em> ] } <em>prog...</em>
 </pre>
 
 <ul>
@@ -45,9 +45,19 @@ empty list, it waits for every child process it has. </li>
 <h2> Options </h2>
 
 <ul>
- <li> <tt>-r</tt>&nbsp;: reap mode. Do not pause until a child has
-exited; only reap all pending zombies. The read block must be empty
-for that option to be effective. </li>
+ <li> <tt>-r</tt>&nbsp;: equivalent to <tt>-t&nbsp;0</tt>. Do not
+pause: only reap processes that are already dead when <tt>wait</tt>
+is invoked. </li>
+ <li> <tt>-t</tt>&nbsp;<em>timeout</em>&nbsp;: wait for a maximum
+of <em>timeout</em> milliseconds. If there still are living processes
+among <em>pids...</em> (or among <tt>wait</tt>'s children if
+<em>pids...</em> is an empty list), after <em>timeout</em>
+milliseconds, they will not be reaped. </li>
+ <li> <tt>-I</tt>&nbsp;: loose. If <tt>wait</tt> times out while
+waiting for children to die, it will still
+exec into <em>prog...</em>. This is the default. </li>
+ <li> <tt>-i</tt>&nbsp;: strict. If <tt>wait</tt> times out, it
+will print an error message and exit 1. </li>
 </ul>
 
 </body>
diff --git a/src/execline/wait.c b/src/execline/wait.c
index 4d550c9..764d503 100644
--- a/src/execline/wait.c
+++ b/src/execline/wait.c
@@ -2,61 +2,138 @@
 
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <skalibs/sgetopt.h>
+#include <errno.h>
+#include <skalibs/uint64.h>
 #include <skalibs/uint.h>
+#include <skalibs/sgetopt.h>
 #include <skalibs/strerr2.h>
+#include <skalibs/tai.h>
 #include <skalibs/djbunix.h>
+#include <skalibs/selfpipe.h>
+#include <skalibs/iopause.h>
 #include <execline/execline.h>
 
-#define USAGE "wait [ -r ] { pids... }"
+#define USAGE "wait [ -I | -i ] [ -r | -t timeout ] { pids... }"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+typedef int actfunc_t (pid_t *, unsigned int *) ;
+typedef actfunc_t *actfunc_t_ref ;
+
+static inline void waitall (void)
+{
+  pid_t r = 1 ;
+  while (r > 0) r = wait(0) ;
+  if (r < 0 && errno != ECHILD) strerr_diefu1sys(111, "wait") ;
+}
+
+static int waitany (pid_t *dummytab, unsigned int *dummyn)
+{
+  pid_t r = 1 ;
+  while (r > 0) r = wait_nohang(0) ;
+  if (!r) return 1 ;
+  if (errno != ECHILD) strerr_diefu1sys(111, "wait") ;
+  (void)dummytab ;
+  (void)dummyn ;
+  return 0 ;
+}
+
+static int waitintab (pid_t *tab, unsigned int *n)
+{
+  unsigned int i = 0 ;
+  for (; i < *n ; i++)
+  {
+    pid_t r = waitpid(tab[i], 0, WNOHANG) ;
+    if (r)
+    {
+      if (r < 0 && errno != ECHILD) strerr_diefu1sys(111, "waitpid") ;
+      tab[i--] = tab[--(*n)] ;
+    }
+  }
+  return !!*n ;
+}
+
+static inline void handle_signals (void)
+{
+  for (;;) switch (selfpipe_read())
+  {
+    case -1 : strerr_diefu1sys(111, "read selfpipe") ;
+    case 0 : return ;
+    case SIGCHLD : break ;
+    default: strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ;
+  }
+}
 
-static unsigned int waitall (void)
+static inline void mainloop (tain_t *deadline, int insist, actfunc_t_ref f, pid_t *tab, unsigned int *n)
 {
-  register unsigned int n = 0 ;
-  int wstat ;
-  while (wait(&wstat) > 0) n++ ;
-  return n ;
+  iopause_fd x = { .events = IOPAUSE_READ } ;
+  x.fd = selfpipe_init() ;
+  if (x.fd < 0) strerr_diefu1sys(111, "create selfpipe") ;
+  if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "trap SIGCHLD") ;
+  tain_now_g() ;
+  tain_add_g(deadline, deadline) ;
+  while ((*f)(tab, n))
+  {
+    register int r = iopause_g(&x, 1, deadline) ;
+    if (r < 0) strerr_diefu1sys(111, "iopause") ;
+    else if (!r)
+    {
+      if (!insist) break ;
+      errno = ETIMEDOUT ;
+      strerr_diefu1sys(1, "wait") ;
+    }
+    else handle_signals() ;
+  }
+  selfpipe_finish() ;
 }
 
 int main (int argc, char const **argv, char const *const *envp)
 {
+  tain_t tto ;
   int argc1 ;
-  int flagreap = 0 ;
+  int hastimeout = 0 ;
+  int insist = 0 ;
   PROG = "wait" ;
   {
     subgetopt_t l = SUBGETOPT_ZERO ;
+    unsigned int t = 0 ;
     for (;;)
     {
-      register int opt = subgetopt_r(argc, argv, "r", &l) ;
+      register int opt = subgetopt_r(argc, argv, "iIrt:", &l) ;
       if (opt == -1) break ;
       switch (opt)
       {
-        case 'r' : flagreap = 1 ; break ;
-        default : strerr_dieusage(100, USAGE) ;
+        case 'i' : insist = 1 ; break ;
+        case 'I' : insist = 0 ; break ;
+        case 'r' : t = 0 ; hastimeout = 1 ; break ;
+        case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; hastimeout = 1 ; break ;    
+        default : dieusage() ;
       }
     }
     argc -= l.ind ; argv += l.ind ;
+    if (hastimeout) tain_from_millisecs(&tto, t) ;
+    else tto = tain_infinite_relative ;
   }
   argc1 = el_semicolon(argv) ;
   if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
-  if (!argc1) flagreap ? wait_reap() : waitall() ;
+  if (!argc1 && !hastimeout) waitall() ;
   else
   {
-    pid_t tab[argc1] ;
-    register unsigned int i = 0 ;
-    for (; i < (unsigned int)argc1 ; i++)
-    {
-      unsigned int pid ;
-      if (!uint0_scan(argv[i], &pid)) strerr_dieusage(100, USAGE) ;
-      tab[i] = pid ;
-    }
-    if (flagreap)
+    actfunc_t_ref f = argc1 ? &waitintab : &waitany ;
+    unsigned int n = argc1 ? (unsigned int)argc1 : 1 ;
+    pid_t tab[n] ;
+    if (argc1)
     {
-      if (waitn_reap(tab, argc1) < 0)
-        strerr_diefu1sys(111, "waitn_reap") ;
+      register unsigned int i = 0 ;
+      for (; i < n ; i++)
+      {
+        uint64_t pid ;
+        if (!uint640_scan(argv[i], &pid)) strerr_dieusage(100, USAGE) ;
+        tab[i] = (pid_t)pid ;
+      }
     }
-    else if (!waitn(tab, argc1)) strerr_diefu1sys(111, "waitn") ;
+    mainloop(&tto, insist, f, tab, &n) ;
   }
+
   pathexec0_run(argv + argc1 + 1, envp) ;
   strerr_dieexec(111, argv[argc1 + 1]) ;
 }