From 60eca880ee450f822fc32cf5bd9c32ab04b6956a Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Mon, 12 Oct 2026 00:58:40 +0000 Subject: Add dollarsharp, rewrite wait Entirely untested, will test now --- NEWS | 3 + doc/dollarsharp.html | 46 +++++++ doc/index.html | 12 ++ doc/upgrade.html | 4 + doc/wait.html | 49 +++++--- package/deps.mak | 5 +- package/modes | 1 + src/execline/deps-exe/dollarsharp | 1 + src/execline/dollarsharp.c | 23 ++++ src/execline/wait.c | 257 +++++++++++++++++++------------------- 10 files changed, 252 insertions(+), 149 deletions(-) create mode 100644 doc/dollarsharp.html create mode 100644 src/execline/deps-exe/dollarsharp create mode 100644 src/execline/dollarsharp.c diff --git a/NEWS b/NEWS index a69510f..a30deb9 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,9 @@ In 2.10.0.0 - execline-umask and posix-cd removed. - loopwhilex -n option removed. - loopwhilex -x "" now loops forever. + - wait has been rewritten, its options and behaviour have changed a bit. +In particular, it does not reap unlisted zombies anymore (that was the +point of the rewrite). In 2.9.9.2 diff --git a/doc/dollarsharp.html b/doc/dollarsharp.html new file mode 100644 index 0000000..4643775 --- /dev/null +++ b/doc/dollarsharp.html @@ -0,0 +1,46 @@ + + + + + + + execline: the dollarsharp command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The dollarsharp program

+ +

+dollarsharp prints the number of arguments it is called with. +

+ +

Interface

+ +
+     dollarsharp [ args ]
+
+ +

(Miscellaneous) @@ -252,5 +253,16 @@ the execline documentation as a skaware mailing-list. +

Support this work

+ +

+ If you like this skarnet.org project, you can donate to the author via Liberapay, +by clicking the following button: + + +

+ diff --git a/doc/upgrade.html b/doc/upgrade.html index 31de4b9..de3fbf4 100644 --- a/doc/upgrade.html +++ b/doc/upgrade.html @@ -27,6 +27,10 @@ been removed, due to POSIX relaxing requirements on intrinsic utilities.
  • The cd binary is now what was previously execline-cd.
  • The umask binary is now what was previously posix-umask, (without locale support, not that it had much to start with)
  • +
  • loopwhilex -x "" now loops forever. The -n +option has been removed.
  • +
  • wait has slightly different options. When given a +non-empty list of pids, it only waits for these pids and doesn't touch the others.
  • in 2.9.9.2

    diff --git a/doc/wait.html b/doc/wait.html index 44197a0..d38b06a 100644 --- a/doc/wait.html +++ b/doc/wait.html @@ -30,7 +30,7 @@

    -     wait [ -I | -i ] [ -a | -o ] [ -r | -t timeout ] { [ pids... ] } prog...
    +     wait [ -a | -o ] [ -u ] [ -t timeout ] { [ pids... ] } prog...
     

    Options

    + +

    Environment variables

    + +

    + If the -u option has been given, wait execs into prog... +with the ! and ? environment variables unset. Same thing if an +error occurred before wait could successfully reap a zombie. +

    + +

    + Otherwise, the ! environment variable is set to the pid of a reapt zombie, +and the ? environment variable is set to its exit code. Here is how wait +decides what pid to report: +

    + +

    Notes

    diff --git a/package/deps.mak b/package/deps.mak index d03a1d8..b37a626 100644 --- a/package/deps.mak +++ b/package/deps.mak @@ -8,6 +8,7 @@ src/execline/case.o src/execline/case.lo: src/execline/case.c src/include/execli src/execline/cd.o src/execline/cd.lo: src/execline/cd.c src/execline/define.o src/execline/define.lo: src/execline/define.c src/include-local/exlsn.h src/execline/dollarat.o src/execline/dollarat.lo: src/execline/dollarat.c +src/execline/dollarsharp.o src/execline/dollarsharp.lo: src/execline/dollarsharp.c src/execline/elgetopt.o src/execline/elgetopt.lo: src/execline/elgetopt.c src/include/execline/execline.h src/execline/elgetpositionals.o src/execline/elgetpositionals.lo: src/execline/elgetpositionals.c src/include-local/exlsn.h src/execline/elglob.o src/execline/elglob.lo: src/execline/elglob.c src/include-local/exlsn.h @@ -49,7 +50,7 @@ src/execline/trap.o src/execline/trap.lo: src/execline/trap.c src/include/execli src/execline/tryexec.o src/execline/tryexec.lo: src/execline/tryexec.c src/include/execline/execline.h src/execline/umask.o src/execline/umask.lo: src/execline/umask.c src/execline/unexport.o src/execline/unexport.lo: src/execline/unexport.c -src/execline/wait.o src/execline/wait.lo: src/execline/wait.c src/include/execline/config.h src/include/execline/execline.h +src/execline/wait.o src/execline/wait.lo: src/execline/wait.c src/include/execline/execline.h src/execline/withstdinas.o src/execline/withstdinas.lo: src/execline/withstdinas.c src/include/execline/execline.h src/libexecline/el_execsequence.o src/libexecline/el_execsequence.lo: src/libexecline/el_execsequence.c src/include/execline/execline.h src/libexecline/el_forx_pidinfo.o src/libexecline/el_forx_pidinfo.lo: src/libexecline/el_forx_pidinfo.c src/include/execline/execline.h @@ -93,6 +94,8 @@ define: EXTRA_LIBS := define: src/execline/define.o ${LIBEXECLINE} -lskarnet dollarat: EXTRA_LIBS := dollarat: src/execline/dollarat.o -lskarnet +dollarsharp: EXTRA_LIBS := +dollarsharp: src/execline/dollarsharp.o -lskarnet elgetopt: EXTRA_LIBS := elgetopt: src/execline/elgetopt.o ${LIBEXECLINE} -lskarnet elgetpositionals: EXTRA_LIBS := diff --git a/package/modes b/package/modes index 025602e..6e7c23f 100644 --- a/package/modes +++ b/package/modes @@ -4,6 +4,7 @@ case 0755 cd 0755 define 0755 dollarat 0755 +dollarsharp 0755 elgetopt 0755 elgetpositionals 0755 elglob 0755 diff --git a/src/execline/deps-exe/dollarsharp b/src/execline/deps-exe/dollarsharp new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/dollarsharp @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/dollarsharp.c b/src/execline/dollarsharp.c new file mode 100644 index 0000000..b382061 --- /dev/null +++ b/src/execline/dollarsharp.c @@ -0,0 +1,23 @@ +/* ISC license. */ + +#include + +#include +#include +#include +#include + +#define USAGE "dollarsharp [ args ]" + +int main (int argc, char const *const *argv) +{ + size_t len ; + char fmt[UINT_FMT] ; + PROG = "dollarsharp" ; + if (argc-- <= 0) strerr_dieusage(100, USAGE) ; + len = uint_fmt(fmt, argc) ; + fmt[len++] = '\n' ; + if (buffer_putflush(buffer_1small, fmt, len) == -1) + strerr_diefu1sys(111, "write to stdout") ; + _exit(0) ; +} diff --git a/src/execline/wait.c b/src/execline/wait.c index d320d54..2715344 100644 --- a/src/execline/wait.c +++ b/src/execline/wait.c @@ -1,10 +1,10 @@ /* ISC license. */ #include - #include #include +#include #include #include #include @@ -12,167 +12,167 @@ #include #include -#include #include -#define USAGE "wait [ -I | -i ] [ -a | -o ] [ -r | -t timeout ] { pids... } [ prog... ]" +#define USAGE "wait [ -u ] [ -a | -o ] [ -t timeout ] { [ pids... ] } [ prog... ]" #define dieusage() strerr_dieusage(100, USAGE) -typedef int ac_func (pid_t *, unsigned int *, int *) ; -typedef ac_func *ac_func_ref ; +typedef pid_t wait_ac_func (void *, int *) ; +typedef wait_ac_func *wait_ac_func_ref ; -static int wait_all (void) +enum wait_golb_e { - errno = 0 ; - while (wait_nointr(0) > 0) ; - if (errno != ECHILD) strerr_diefu1sys(111, "wait") ; - return 0 ; -} + WAIT_GOLB_JUSTONE = 0x01, + WAIT_GOLB_NOEXPORT = 0x02, +} ; -static int wait_from_list (pid_t *pids, unsigned int n) +enum wait_gola_e { - int wstat = -1 ; - if (!waitn_posix(pids, n, &wstat) && errno != ECHILD) - strerr_diefu1sys(111, "wait") ; - return wstat == -1 ? -1 : wait_estatus(wstat) ; -} + WAIT_GOLA_TIMEOUT, + WAIT_GOLA_N +} ; -static int wait_one (pid_t *pid) +struct wait_pidlist_s { - int wstat = 0 ; - *pid = wait(&wstat) ; - if (*pid == -1) - { - if (errno != ECHILD) strerr_diefu1sys(111, "wait") ; - else return -1 ; - } - return wait_estatus(wstat) ; -} + pid_t *pids ; + size_t n ; + pid_t last ; + int lastwstat ; +} ; -static int wait_one_from_list (pid_t *pids, unsigned int n, pid_t *pid) +static inline pid_t wait_hang_nolist_all (int *wstat) { + pid_t pid = -1 ; + int w ; for (;;) { - int wstat ; - pid_t r = wait(&wstat) ; - unsigned int i = 0 ; - if (r == -1) - { - 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) ; - } + pid_t r = wait_nointr(&w) ; + if (r == -1) break ; + pid = r ; } + if (errno != ECHILD) strerr_diefusys(111, "wait") ; + if (pid > 0) *wstat = w ; + return pid ; } -static int wait_one_nohang (pid_t *pids, unsigned int *n, int *wstat) +static pid_t wait_nohang_nolist_one (void *data, int *wstat) { pid_t r = wait_nohang(wstat) ; - if (r == -1 && errno != ECHILD) strerr_diefu1sys(111, "wait") ; - (void)pids ; - (void)n ; + if (r == -1 && errno != ECHILD) + strerr_diefusys(111, "wait_nohang") ; + (void)data ; return r ; } -static int wait_one_from_list_nohang (pid_t *pids, unsigned int *n, int *wstat) +static pid_t wait_nohang_nolist_all (void *data, int *wstat) { + pid_t *last = data ; for (;;) { pid_t r = wait_nohang(wstat) ; - unsigned int i = 0 ; - if (r <= 0) + if (r == -1) break ; + else if (!r) return 0 ; + else *last = r ; + } + if (errno != ECHILD) strerr_diefusys(111, "wait_nohang") ; + return *last ; +} + +static pid_t wait_nohang_list_one (void *data, int *wstat) +{ + struct wait_pidlist_s *p = data ; + while (p->n) + { + size_t i ; + int r = waitpids_nohang(p->pids, p->n, wstat, &i) ; + if (!r) return 0 ; + else if (r == -1) { - if (r == -1 && errno != ECHILD) strerr_diefu1sys(111, "wait") ; - else return r ; + char fmt[PID_FMT] ; + fmt[pid_fmt(fmt, p->pids[i])] = 0 ; + if (errno != ECHILD) strerr_diefusys(111, "wait for pid", fmt) ; + strerr_warnwusys("wait for pid ", fmt) ; + p->pids[i] = p->pids[--p->n] ; } - for (; i < *n ; i++) if (r == pids[i]) break ; - if (i < *n) + else return p->pids[i] ; + } + return -1 ; +} + +static pid_t wait_nohang_list_all (void *data, int *wstat) +{ + struct wait_pidlist_s *p = data ; + while (p->n) + { + size_t i ; + int w ; + int r = waitpids_nohang(p->pids, p->n, &w, &i) ; + if (!r) return 0 ; + else if (r == -1) + { + char fmt[PID_FMT] ; + fmt[pid_fmt(fmt, p->pids[i])] = 0 ; + if (errno != ECHILD) strerr_diefusys(111, "wait for pid ", fmt) ; + else strerr_warnwusys("wait for pid ", fmt) ; + } + if (p->pids[i] == p->last) { - pids[i] = pids[--*n] ; - return r ; + if (r == -1) p->last = 0 ; + else p->lastwstat = w ; } + p->pids[i] = p->pids[--p->n] ; } + *wstat = p->lastwstat ; + return p->last ; } -static inline void empty_selfpipe (void) +static inline void wait_empty_selfpipe (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.") ; + default: strerr_dief(101, "internal inconsistency. Please submit a bug-report.") ; } } -static int wait_with_timeout (pid_t *pids, unsigned int n, pid_t *returned, ac_func_ref f, tain *tto, int justone, int strict) +static pid_t wait_with_timeout (wait_ac_func_ref f, void *data, int *wstat, tain *tto) { + pid_t pid ; iopause_fd x = { .fd = selfpipe_init(), .events = IOPAUSE_READ } ; - pid_t special = pids[n-1] ; - int e = special ? -1 : 0 ; - if (x.fd == -1) strerr_diefu1sys(111, "create selfpipe") ; - if (!selfpipe_trap(SIGCHLD)) strerr_diefu1sys(111, "trap SIGCHLD") ; + if (x.fd == -1) strerr_diefusys(111, "create selfpipe") ; + if (!selfpipe_trap(SIGCHLD)) strerr_diefusys(111, "trap SIGCHLD") ; tain_now_set_stopwatch_g() ; tain_add_g(tto, tto) ; + for (;;) { int r ; - for (;;) - { - int wstat ; - pid_t pid = (*f)(pids, &n, &wstat) ; - if (!pid) break ; - if (pid < 0) goto endloop ; - if (justone || pid == special) - { - *returned = pid ; - e = wait_estatus(wstat) ; - } - if (justone || !n) goto endloop ; - } + pid = (*f)(data, wstat) ; + if (pid) break ; r = iopause_g(&x, 1, tto) ; - if (r == -1) strerr_diefu1sys(111, "iopause") ; + if (r == -1) strerr_diefusys(111, "iopause") ; else if (!r) { - if (!strict) { e = -1 ; break ; } - errno = ETIMEDOUT ; - strerr_diefu1sys(99, "wait") ; + pid = -1 ; + break ; } - else empty_selfpipe() ; + else wait_empty_selfpipe() ; } - endloop: + selfpipe_finish() ; - return e ; + return pid ; } -enum wait_golb_e -{ - WAIT_GOLB_INSIST = 0x01, - WAIT_GOLB_JUSTONE = 0x02, - WAIT_GOLB_REAP = 0x04, -} ; - -enum wait_gola_e -{ - WAIT_GOLA_TIMEOUT, - WAIT_GOLA_N -} ; - int main (int argc, char const **argv) { static gol_bool const rgolb[] = { - { .so = 'I', .lo = "no-insist", .clear = WAIT_GOLB_INSIST, .set = 0 }, - { .so = 'i', .lo = "insist", .clear = 0, .set = WAIT_GOLB_INSIST }, { .so = 'a', .lo = "all", .clear = WAIT_GOLB_JUSTONE, .set = 0 }, { .so = 'o', .lo = "one", .clear = 0, .set = WAIT_GOLB_JUSTONE }, - { .so = 'r', .lo = "reap", .clear = 0, .set = WAIT_GOLB_REAP }, + { .so = 0, .lo = "export", .clear = WAIT_GOLB_NOEXPORT, .set = 0 }, + { .so = 'u', .lo = "no-export", .clear = 0, .set = WAIT_GOLB_NOEXPORT }, } ; static gol_arg const rgola[] = { @@ -180,17 +180,18 @@ int main (int argc, char const **argv) } ; tain tto = TAIN_INFINITE_RELATIVE ; int argc1 ; - int hasblock = 1 ; - int e ; + int posixcompat = 0 ; + pid_t pid ; + int wstat ; uint64_t wgolb = 0 ; char const *wgola[WAIT_GOLA_N] = { 0 } ; - unsigned int golc ; - pid_t pid = -1 ; PROG = "wait" ; - golc = GOL_main(argc, argv, rgolb, rgola, &wgolb, wgola) ; - argc -= golc ; argv += golc ; - if (wgolb & WAIT_GOLB_REAP) wgola[WAIT_GOLA_TIMEOUT] = "0" ; + { + unsigned int golc = GOL_main(argc, argv, rgolb, rgola, &wgolb, wgola) ; + argc -= golc ; argv += golc ; + } + if (wgola[WAIT_GOLA_TIMEOUT]) { unsigned int t = 0 ; @@ -201,41 +202,37 @@ int main (int argc, char const **argv) argc1 = el_semicolon_nostrict(argv) ; if (argc1 > argc) { - hasblock = 0 ; + posixcompat = 1 ; argc1 = argc ; } + 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], pids + i)) strerr_dieusage(100, USAGE) ; - } - else pids[0] = 0 ; - - e = wgola[WAIT_GOLA_TIMEOUT] ? /* wait -t30000 whatever */ - wait_with_timeout(pids, n, &pid, argc1 ? &wait_one_from_list_nohang : &wait_one_nohang, &tto, !!(wgolb & WAIT_GOLB_JUSTONE), !!(wgolb & WAIT_GOLB_INSIST)) : - wgolb & WAIT_GOLB_JUSTONE ? - argc1 ? - wait_one_from_list(pids, n, &pid) : /* wait -o -- 2 3 4 / wait -o -- { 2 3 4 } */ - wait_one(&pid) : /* wait -o / wait -o { } */ - argc1 ? - wait_from_list(pids, n) : /* wait 2 3 4 / wait { 2 3 4 }*/ - wait_all() ; /* wait / wait { } */ + pid_t pids[argc1] ; + struct wait_pidlist_s info = { .pids = pids, .n = argc1 } ; + for (unsigned int i = 0 ; i < argc1 ; i++) + if (!pid0_scan(argv[i], pids + i)) + strerr_dief(100, "pids must be numerical") ; + info.last = pids[argc1 - 1] ; + pid = wait_with_timeout(wgolb & WAIT_GOLB_JUSTONE ? &wait_nohang_list_one : &wait_nohang_list_all, &info, &wstat, &tto) ; } - if (!hasblock) _exit(e >= 0 ? e : 127) ; - if (!(wgolb & WAIT_GOLB_JUSTONE)) xexec0(argv + argc1 + 1) ; - if (e == -1) xmexec0_n(argv + argc1 + 1, "?\0!", 4, 2) ; + else if (wgola[WAIT_GOLA_TIMEOUT]) + { + pid_t saved = -1 ; + pid = wait_with_timeout(wgolb & WAIT_GOLB_JUSTONE ? &wait_nohang_nolist_one : &wait_nohang_nolist_all, &saved, &wstat, &tto) ; + } + else pid = wgolb & WAIT_GOLB_JUSTONE ? wait_nointr(&wstat) : wait_hang_nolist_all(&wstat) ; + + if (posixcompat) _exit(pid == -1 ? 1 : argc1 ? wait_estatus(wstat) : 0) ; + if (pid > 0 && !(wgolb & WAIT_GOLB_NOEXPORT)) { char fmt[4 + UINT_FMT + PID_FMT] = "?=" ; size_t m = 2 ; - m += uint_fmt(fmt + m, (unsigned int)e) ; fmt[m++] = 0 ; + m += uint_fmt(fmt + m, wait_estatus(wstat)) ; fmt[m++] = 0 ; fmt[m++] = '!' ; fmt[m++] = '=' ; m += pid_fmt(fmt + m, pid) ; fmt[m++] = 0 ; xmexec0_n(argv + argc1 + 1, fmt, m, 2) ; } + else xmexec0_n(argv + argc1 + 1, "?\0!", 4, 2) ; } -- cgit v1.3.1