summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2022-04-20 23:43:43 +0000
committerLaurent Bercot <ska@appnovation.com>2022-04-20 23:43:43 +0000
commit9aa0c8c2f34b42e671d6418bd2cc95c79d3b7d54 (patch)
tree859747338ad4309f58410ca743ac7186657e1d0f
parenta0664e349a7c0526ea64a9e160ae808475d7d3b5 (diff)
downloadexecline-9aa0c8c2f34b42e671d6418bd2cc95c79d3b7d54.tar.xz
Prepare for 2.9.0.0; rewrite "wait"
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r--COPYING2
-rw-r--r--INSTALL2
-rw-r--r--NEWS8
-rw-r--r--doc/index.html4
-rw-r--r--doc/upgrade.html11
-rw-r--r--doc/wait.html20
-rw-r--r--package/info2
-rw-r--r--src/execline/wait.c159
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>&nbsp;: 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>&nbsp;: wait for <em>one</em> of the listed
+<em>pids</em> &mdash; 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>&nbsp;: 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) ;
+ }
}