summaryrefslogtreecommitdiff
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.xz
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]) ;
}