From 4d1c5149a09722cd6dcf718812a347db60110706 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Tue, 26 Mar 2019 18:34:06 +0100 Subject: hush: add "wait -n" bashism Signed-off-by: Denys Vlasenko --- shell/hush.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 920a85299..7c907686e 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -8763,7 +8763,11 @@ static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) /* fg_pipe exited or stopped */ break; } - if (childpid == waitfor_pid) { + if (childpid == waitfor_pid /* "wait PID" */ +#if ENABLE_HUSH_BASH_COMPAT + || -1 == waitfor_pid /* "wait -n" (wait for any one child) */ +#endif + ) { debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status); rcode = WEXITSTATUS(status); if (WIFSIGNALED(status)) @@ -11471,6 +11475,12 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid ret--; if (ret < 0) /* if ECHILD, may need to fix "ret" */ ret = 0; +#if ENABLE_HUSH_BASH_COMPAT + if (waitfor_pid == -1 && errno == ECHILD) { + /* exitcode of "wait -n" with no children is 127, not 0 */ + ret = 127; + } +#endif sigprocmask(SIG_SETMASK, &oldset, NULL); break; } @@ -11499,6 +11509,12 @@ static int FAST_FUNC builtin_wait(char **argv) int status; argv = skip_dash_dash(argv); +#if ENABLE_HUSH_BASH_COMPAT + if (argv[0] && !argv[1] && strcmp(argv[0], "-n") == 0) { + /* wait -n */ + return wait_for_child_or_signal(NULL, -1 /*(no job, wait for one child)*/); + } +#endif if (argv[0] == NULL) { /* Don't care about wait results */ /* Note 1: must wait until there are no more children */ -- cgit v1.2.3-55-g6feb 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') 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-55-g6feb From e6f51ac6975963e538a28fe2060dd4511143449b Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 27 Mar 2019 18:34:10 +0100 Subject: hush: fix "wait -n" to wait for a _job_, not a _process_ function old new delta checkjobs 163 183 +20 process_wait_result 449 463 +14 leave_var_nest_level 98 107 +9 enter_var_nest_level 32 38 +6 set_vars_and_save_old 147 150 +3 builtin_local 53 56 +3 builtin_wait 322 323 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 7/0 up/down: 56/0) Total: 56 bytes Signed-off-by: Denys Vlasenko --- shell/hush.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) (limited to 'shell') diff --git a/shell/hush.c b/shell/hush.c index 7c907686e..fa9afa38e 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -923,7 +923,7 @@ struct globals { # define G_flag_return_in_progress 0 #endif smallint exiting; /* used to prevent EXIT trap recursion */ - /* These support $?, $#, and $1 */ + /* These support $? */ smalluint last_exitcode; smalluint expand_exitcode; smalluint last_bg_pid_exitcode; @@ -933,6 +933,9 @@ struct globals { # define G_global_args_malloced (G.global_args_malloced) #else # define G_global_args_malloced 0 +#endif +#if ENABLE_HUSH_BASH_COMPAT + int dead_job_exitcode; /* for "wait -n" */ #endif /* how many non-NULL argv's we have. NB: $# + 1 */ int global_argc; @@ -8657,6 +8660,9 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) pi->cmds[i].pid = 0; pi->alive_cmds--; if (!pi->alive_cmds) { +#if ENABLE_HUSH_BASH_COMPAT + G.dead_job_exitcode = job_exited_or_stopped(pi); +#endif if (G_interactive_fd) { printf(JOB_STATUS_FORMAT, pi->jobid, "Done", pi->cmdtext); @@ -8763,11 +8769,7 @@ static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) /* fg_pipe exited or stopped */ break; } - if (childpid == waitfor_pid /* "wait PID" */ -#if ENABLE_HUSH_BASH_COMPAT - || -1 == waitfor_pid /* "wait -n" (wait for any one child) */ -#endif - ) { + if (childpid == waitfor_pid) { /* "wait PID" */ debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status); rcode = WEXITSTATUS(status); if (WIFSIGNALED(status)) @@ -8778,6 +8780,15 @@ static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) rcode++; break; /* "wait PID" called us, give it exitcode+1 */ } +#if ENABLE_HUSH_BASH_COMPAT + if (-1 == waitfor_pid /* "wait -n" (wait for any one job) */ + && G.dead_job_exitcode >= 0 /* some job did finish */ + ) { + debug_printf_exec("waitfor_pid:-1\n"); + rcode = G.dead_job_exitcode + 1; + break; + } +#endif /* This wasn't one of our processes, or */ /* fg_pipe still has running processes, do waitpid again */ } /* while (waitpid succeeds)... */ @@ -11510,9 +11521,11 @@ static int FAST_FUNC builtin_wait(char **argv) argv = skip_dash_dash(argv); #if ENABLE_HUSH_BASH_COMPAT - if (argv[0] && !argv[1] && strcmp(argv[0], "-n") == 0) { + if (argv[0] && strcmp(argv[0], "-n") == 0) { /* wait -n */ - return wait_for_child_or_signal(NULL, -1 /*(no job, wait for one child)*/); + /* (bash accepts "wait -n PID" too and ignores PID) */ + G.dead_job_exitcode = -1; + return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/); } #endif if (argv[0] == NULL) { @@ -11532,7 +11545,7 @@ static int FAST_FUNC builtin_wait(char **argv) * ^C <-- after ~4 sec from keyboard * $ */ - return wait_for_child_or_signal(NULL, 0 /*(no job and no pid to wait for)*/); + return wait_for_child_or_signal(NULL, 0 /*no job and no pid to wait for*/); } do { -- cgit v1.2.3-55-g6feb