diff options
-rw-r--r-- | COPYING | 2 | ||||
-rw-r--r-- | INSTALL | 2 | ||||
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | doc/index.html | 4 | ||||
-rw-r--r-- | doc/upgrade.html | 11 | ||||
-rw-r--r-- | doc/wait.html | 20 | ||||
-rw-r--r-- | package/info | 2 | ||||
-rw-r--r-- | src/execline/wait.c | 159 |
8 files changed, 143 insertions, 65 deletions
diff --git a/COPYING b/COPYING index 9e95f25..0dfd4b1 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (c) 2011-2021 Laurent Bercot <ska-skaware@skarnet.org> +Copyright (c) 2011-2022 Laurent Bercot <ska-skaware@skarnet.org> Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/INSTALL b/INSTALL index 63c6b44..c49cfa2 100644 --- a/INSTALL +++ b/INSTALL @@ -6,7 +6,7 @@ Build Instructions - A POSIX-compliant C development environment - GNU make version 3.81 or later - - skalibs version 2.11.2.0 or later: https://skarnet.org/software/skalibs/ + - skalibs version 2.12.0.0 or later: https://skarnet.org/software/skalibs/ - Optional: nsss version 0.2.0.1 or later: https://skarnet.org/software/nsss/ This software will run on any operating system that implements diff --git a/NEWS b/NEWS index 91b1b18..84894aa 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,13 @@ Changelog for execline. +In 2.9.0.0 +---------- + + - Bugfixes. + - New -a/-o options to wait (-o waits for one process only) + - wait now exits 99 on timeout + + In 2.8.3.0 ---------- diff --git a/doc/index.html b/doc/index.html index 9a01f17..69fa015 100644 --- a/doc/index.html +++ b/doc/index.html @@ -51,7 +51,7 @@ shell's syntax, and has no security issues. <li> A POSIX-compliant system with a standard C development environment </li> <li> GNU make, version 3.81 or later. </li> <li> <a href="//skarnet.org/software/skalibs/">skalibs</a> version -2.11.2.0 or later. It's a build-time requirement. It's also a run-time +2.12.0.0 or later. It's a build-time requirement. It's also a run-time requirement if you link against the shared version of the skalibs library. </li> </ul> @@ -77,7 +77,7 @@ want nsswitch-like functionality: <h3> Download </h3> <ul> - <li> The current released version of execline is <a href="execline-2.8.3.0.tar.gz">2.8.3.0</a>. </li> + <li> The current released version of execline is <a href="execline-2.9.0.0.tar.gz">2.9.0.0</a>. </li> <li> Alternatively, you can checkout a copy of the <a href="//git.skarnet.org/cgi-bin/cgit.cgi/execline/">execline git repository</a>: diff --git a/doc/upgrade.html b/doc/upgrade.html index 534ad57..705f03a 100644 --- a/doc/upgrade.html +++ b/doc/upgrade.html @@ -18,6 +18,17 @@ <h1> What has changed in execline </h1> +<h2> in 2.9.0.0 </h2> + +<ul> + <li> <a href="//skarnet.org/software/skalibs/">skalibs</a> +dependency bumped to 2.12.0.0. </li> + <li> New options to <a href="wait.html">wait</a>: <tt>-o</tt> +to wait for one of the listed processes, and <tt>-a</tt> to get the +default behaviour. </li> + <li> <a href="wait.html">wait</a> now exits 99 on timeout. </li> +</ul> + <h2> in 2.8.3.0 </h2> <ul> diff --git a/doc/wait.html b/doc/wait.html index a092859..a66cefc 100644 --- a/doc/wait.html +++ b/doc/wait.html @@ -29,7 +29,7 @@ </p> <pre> - wait [ -I | -i ] [ -r | -t <em>timeout</em> ] { [ <em>pids...</em> ] } <em>prog...</em> + wait [ -I | -i ] [ -a | -o ] [ -r | -t <em>timeout</em> ] { [ <em>pids...</em> ] } <em>prog...</em> </pre> <ul> @@ -57,7 +57,18 @@ milliseconds, they will not be reaped. </li> 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> +will print an error message and exit 99. </li> + <li> <tt>-o</tt> : wait for <em>one</em> of the listed +<em>pids</em> — exec into <em>prog</em> as soon as one of the +listed children dies. (If no pid is listed, wait for one child to die.) +The <tt>!</tt> environment variable will be set to the +pid that died, and the <tt>?</tt> environment variable will contain an +<a href="exitcodes.html">approximation</a> of its exit code. If no +listed child has died before <tt>wait</tt> has to exec (either because +it timed out or it has no suitable children left), the <tt>?</tt> and +<tt>!</tt> environment variables are unset. </li> +<li> <tt>-a</tt> : wait for <em>all</em> of the listed <em>pids</em>. +Do not touch the <tt>!</tt> or <tt>?</tt> variables. This is the default. </li> </ul> <h2> Notes </h2> @@ -65,8 +76,9 @@ will print an error message and exit 1. </li> <ul> <li> For <a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/wait.html">POSIX compatibility</a>, <tt>wait</tt> also works when it cannot find a block. -In that case, all its command line is interpreted as <em>pids...</em> -arguments and it does not execute into a program. Instead, it exits +In that case, all the options are still supported and have the same +effect, but the rest of the command line is interpreted as <em>pids...</em> +arguments and <tt>wait</tt> does not execute into a program; instead, it exits with a conforming exit code. </li> </ul> diff --git a/package/info b/package/info index d7cff08..a6caa64 100644 --- a/package/info +++ b/package/info @@ -1,4 +1,4 @@ package=execline -version=2.8.3.0 +version=2.9.0.0 category=admin package_macro_name=EXECLINE diff --git a/src/execline/wait.c b/src/execline/wait.c index ada47a1..1b7516e 100644 --- a/src/execline/wait.c +++ b/src/execline/wait.c @@ -19,60 +19,78 @@ #include <execline/execline.h> -#define USAGE "wait [ -I | -i ] [ -r | -t timeout ] { pids... }" +#define USAGE "wait [ -I | -i ] [ -a | -o ] [ -r | -t timeout ] { pids... }" #define dieusage() strerr_dieusage(100, USAGE) typedef int ac_func (pid_t *, unsigned int *, int *) ; typedef ac_func *ac_func_ref ; -static inline int waitall (void) +static int wait_all (void) +{ + errno = 0 ; + while (wait_nointr(0) > 0) ; + if (errno != ECHILD) strerr_diefu1sys(111, "wait") ; + return 0 ; +} + +static int wait_from_list (pid_t *pids, unsigned int n) +{ + int wstat = -1 ; + if (!waitn_posix(pids, n, &wstat) && errno != ECHILD) + strerr_diefu1sys(111, "wait") ; + return wstat == -1 ? -1 : wait_estatus(wstat) ; +} + +static int wait_one (pid_t *pid, int nohang) { int wstat = 0 ; - pid_t r = 1 ; - while (r > 0) r = wait(&wstat) ; + pid_t r = waitpid_nointr(-1, &wstat, nohang ? WNOHANG : 0) ; if (r < 0) { if (errno != ECHILD) strerr_diefu1sys(111, "wait") ; - else return 127 ; + else return -1 ; } + *pid = r ; return wait_estatus(wstat) ; } -static int waitany (pid_t *dummytab, unsigned int *dummyn, int *res) +static int wait_one_from_list (pid_t *pids, unsigned int n, pid_t *pid, int nohang) { - int wstat ; - pid_t r = 1 ; - while (r > 0) r = wait_nohang(&wstat) ; - if (!r) return (*res = wait_estatus(wstat), 1) ; - if (errno != ECHILD) strerr_diefu1sys(111, "wait") ; - *res = 127 ; - (void)dummytab ; - (void)dummyn ; - return 0 ; -} - -static int waitintab (pid_t *tab, unsigned int *n, int *res) -{ - unsigned int i = 0 ; - for (; i < *n ; i++) + for (;;) { int wstat ; - pid_t r = waitpid(tab[i], &wstat, WNOHANG) ; - if (r) + pid_t r = waitpid_nointr(-1, &wstat, nohang ? WNOHANG : 0) ; + unsigned int i = 0 ; + if (r < 0) { - if (r < 0) - { - if (errno == ECHILD) *res = 127 ; - else strerr_diefu1sys(111, "waitpid") ; - } - else *res = wait_estatus(wstat) ; - tab[i--] = tab[--(*n)] ; + if (errno != ECHILD) strerr_diefu1sys(111, "wait") ; + else return -1 ; + } + for (; i < n ; i++) if (r == pids[i]) break ; + if (i < n) + { + *pid = r ; + pids[i] = pids[n-1] ; + return wait_estatus(wstat) ; } } - return !!*n ; } -static inline void handle_signals (void) +static int wait_one_nohang (pid_t *pids, unsigned int *n, pid_t *pid) +{ + (void)pids ; + (void)n ; + return wait_one(pid, 1) ; +} + +static int wait_one_from_list_nohang (pid_t *pids, unsigned int *n, pid_t *pid) +{ + int r = wait_one_from_list(pids, *n, pid, 1) ; + if (r) (*n)-- ; + return r ; +} + +static inline void empty_selfpipe (void) { for (;;) switch (selfpipe_read()) { @@ -83,29 +101,37 @@ static inline void handle_signals (void) } } -static inline int mainloop (tain *deadline, int insist, ac_func_ref f, pid_t *tab, unsigned int *n) +static int wait_with_timeout (pid_t *pids, unsigned int n, pid_t *pid, ac_func_ref f, tain *tto, int justone, int strict) { - iopause_fd x = { .events = IOPAUSE_READ } ; - int res = 0 ; - x.fd = selfpipe_init() ; + iopause_fd x = { .fd = selfpipe_init(), .events = IOPAUSE_READ } ; + pid_t special = pids[n-1] ; + int e = special ? -1 : 0 ; if (x.fd < 0) strerr_diefu1sys(111, "create selfpipe") ; if (!selfpipe_trap(SIGCHLD)) strerr_diefu1sys(111, "trap SIGCHLD") ; tain_now_set_stopwatch_g() ; - tain_add_g(deadline, deadline) ; - while ((*f)(tab, n, &res)) + tain_add_g(tto, tto) ; + for (;;) { - int r = iopause_g(&x, 1, deadline) ; + int r ; + for (;;) + { + r = (*f)(pids, &n, pid) ; + if (!r) break ; + if (r < 0) { selfpipe_finish() ; return e ; } + if (justone) { selfpipe_finish() ; return r ; } + if (*pid == special) e = r ; + } + + r = iopause_g(&x, 1, tto) ; if (r < 0) strerr_diefu1sys(111, "iopause") ; else if (!r) { - if (!insist) break ; + if (!strict) { selfpipe_finish() ; return -1 ; } errno = ETIMEDOUT ; - strerr_diefu1sys(1, "wait") ; + strerr_diefu1sys(99, "wait") ; } - else handle_signals() ; + else empty_selfpipe() ; } - selfpipe_finish() ; - return res ; } int main (int argc, char const **argv) @@ -114,8 +140,10 @@ int main (int argc, char const **argv) int argc1 ; int hastimeout = 0 ; int insist = 0 ; - int r ; + int justone = 0 ; int hasblock ; + int e ; + pid_t pid ; PROG = "wait" ; #ifdef EXECLINE_PEDANTIC_POSIX setlocale(LC_ALL, "") ; /* but of course, dear POSIX */ @@ -125,12 +153,14 @@ int main (int argc, char const **argv) unsigned int t = 0 ; for (;;) { - int opt = subgetopt_r(argc, argv, "iIrt:", &l) ; + int opt = subgetopt_r(argc, argv, "iIaort:", &l) ; if (opt == -1) break ; switch (opt) { case 'i' : insist = 1 ; break ; case 'I' : insist = 0 ; break ; + case 'a' : justone = 0 ; break ; + case 'o' : justone = 1 ; break ; case 'r' : t = 0 ; hastimeout = 1 ; break ; case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; hastimeout = 1 ; break ; default : dieusage() ; @@ -147,21 +177,38 @@ int main (int argc, char const **argv) argc1 = argc ; } else hasblock = 1 ; - if (!argc1 && !hastimeout) r = waitall() ; - else + { - ac_func_ref f = argc1 ? &waitintab : &waitany ; - unsigned int n = argc1 ? (unsigned int)argc1 : 1 ; - pid_t tab[n] ; - if (argc1) + unsigned int n = argc1 ? argc1 : 1 ; + pid_t pids[n] ; + if (argc1) { unsigned int i = 0 ; for (; i < n ; i++) - if (!pid0_scan(argv[i], tab+i)) strerr_dieusage(100, USAGE) ; + if (!pid0_scan(argv[i], pids + i)) strerr_dieusage(100, USAGE) ; } - r = mainloop(&tto, insist, f, tab, &n) ; + else pids[0] = 0 ; + + e = hastimeout ? /* wait -t30000 whatever */ + wait_with_timeout(pids, n, &pid, argc1 ? &wait_one_from_list_nohang : &wait_one_nohang, &tto, justone, insist) : + justone ? + argc1 ? + wait_one_from_list(pids, n, &pid, 0) : /* wait -o -- 2 3 4 / wait -o -- { 2 3 4 } */ + wait_one(&pid, 0) : /* wait -o / wait -o { } */ + argc1 ? + wait_from_list(pids, n) : /* wait 2 3 4 / wait { 2 3 4 }*/ + wait_all() ; /* wait / wait { } */ } + if (!hasblock) return e >= 0 ? e : 127 ; + if (!justone) xexec0(argv + argc1 + 1) ; + if (e < 0) xmexec_n(argv + argc1 + 1, "?\0!", 4, 2) ; - if (!hasblock) return r ; - xexec0(argv + argc1 + 1) ; + { + char fmt[4 + UINT_FMT + PID_FMT] = "?=" ; + size_t m = 2 ; + m += uint_fmt(fmt + m, (unsigned int)e) ; fmt[m++] = 0 ; + fmt[m++] = '!' ; fmt[m++] = '=' ; + m += pid_fmt(fmt + m, pid) ; fmt[m++] = 0 ; + xmexec_n(argv + argc1 + 1, fmt, m, 2) ; + } } |