summary refs log tree commit diff
diff options
context:
space:
mode:
-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) ;
+  }
 }