diff options
-rw-r--r-- | doc/wait.html | 18 | ||||
-rw-r--r-- | src/execline/wait.c | 125 |
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> : 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> : equivalent to <tt>-t 0</tt>. Do not +pause: only reap processes that are already dead when <tt>wait</tt> +is invoked. </li> + <li> <tt>-t</tt> <em>timeout</em> : 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> : 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> : 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]) ; } |