From 966f087ab41e555fb40a158e7feae7603b31d450 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 27 Mar 2019 15:51:42 +0100 Subject: ash: add "wait -n" bashism function old new delta waitcmd 205 288 +83 dowait 405 444 +39 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 122/0) Total: 122 bytes Signed-off-by: Denys Vlasenko --- shell/ash.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) (limited to 'shell/ash.c') diff --git a/shell/ash.c b/shell/ash.c index a4394d756..b1499b037 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -3522,7 +3522,7 @@ struct procstat { struct job { struct procstat ps0; /* status of process */ - struct procstat *ps; /* status or processes when more than one */ + struct procstat *ps; /* status of processes when more than one */ #if JOBS int stopstatus; /* status of a stopped job */ #endif @@ -4235,6 +4235,9 @@ wait_block_or_sig(int *status) #define DOWAIT_NONBLOCK 0 #define DOWAIT_BLOCK 1 #define DOWAIT_BLOCK_OR_SIG 2 +#if ENABLE_ASH_BASH_COMPAT +# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ +#endif static int dowait(int block, struct job *job) @@ -4242,7 +4245,11 @@ dowait(int block, struct job *job) int pid; int status; struct job *jp; - struct job *thisjob = NULL; + struct job *thisjob; +#if ENABLE_ASH_BASH_COMPAT + bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS); + block = (block & ~DOWAIT_JOBSTATUS); +#endif TRACE(("dowait(0x%x) called\n", block)); @@ -4279,10 +4286,10 @@ dowait(int block, struct job *job) } TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", pid, status, errno, strerror(errno))); + thisjob = NULL; if (pid <= 0) goto out; - thisjob = NULL; for (jp = curjob; jp; jp = jp->prev_job) { int jobstate; struct procstat *ps; @@ -4341,6 +4348,13 @@ dowait(int block, struct job *job) out: INT_ON; +#if ENABLE_ASH_BASH_COMPAT + if (want_jobexitstatus) { + pid = -1; + if (thisjob && thisjob->state == JOBDONE) + pid = thisjob->ps[thisjob->nprocs - 1].ps_status; + } +#endif if (thisjob && thisjob == job) { char s[48 + 1]; int len; @@ -4523,15 +4537,24 @@ waitcmd(int argc UNUSED_PARAM, char **argv) struct job *job; int retval; struct job *jp; - +#if ENABLE_ASH_BASH_COMPAT + int status; + char one = nextopt("n"); +#else nextopt(nullstr); +#endif retval = 0; argv = argptr; - if (!*argv) { - /* wait for all jobs */ + if (!argv[0]) { + /* wait for all jobs / one job if -n */ for (;;) { jp = curjob; +#if ENABLE_ASH_BASH_COMPAT + if (one && !jp) + /* exitcode of "wait -n" with nothing to wait for is 127, not 0 */ + retval = 127; +#endif while (1) { if (!jp) /* no running procs */ goto ret; @@ -4547,13 +4570,31 @@ waitcmd(int argc UNUSED_PARAM, char **argv) * with an exit status greater than 128, immediately after which * the trap is executed." */ +#if ENABLE_ASH_BASH_COMPAT + status = dowait(DOWAIT_BLOCK_OR_SIG | DOWAIT_JOBSTATUS, NULL); +#else dowait(DOWAIT_BLOCK_OR_SIG, NULL); +#endif /* if child sends us a signal *and immediately exits*, * dowait() returns pid > 0. Check this case, * not "if (dowait() < 0)"! */ if (pending_sig) goto sigout; +#if ENABLE_ASH_BASH_COMPAT + if (one) { + /* wait -n waits for one _job_, not one _process_. + * date; sleep 3 & sleep 2 | sleep 1 & wait -n; date + * should wait for 2 seconds. Not 1 or 3. + */ + if (status != -1 && !WIFSTOPPED(status)) { + retval = WEXITSTATUS(status); + if (WIFSIGNALED(status)) + retval = WTERMSIG(status) + 128; + goto ret; + } + } +#endif } } -- cgit v1.2.3