aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2016-10-28 21:57:31 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2016-10-28 21:59:09 +0200
commit7e6753609d102b68a625072fb1660065246a54e2 (patch)
treecd18eb18be9402da2bc1daf6ac9b53acba94dcbf
parent8f7b0248adca9a88351fd7f3dd208775242f3fe6 (diff)
downloadbusybox-w32-7e6753609d102b68a625072fb1660065246a54e2.tar.gz
busybox-w32-7e6753609d102b68a625072fb1660065246a54e2.tar.bz2
busybox-w32-7e6753609d102b68a625072fb1660065246a54e2.zip
hush: fix "wait PID"
It was not properly interruptible, and did not update job status (the exited processes were still thought of as running). function old new delta process_wait_result - 453 +453 wait_for_child_or_signal - 199 +199 run_list 996 1002 +6 checkjobs_and_fg_shell 41 43 +2 builtin_wait 328 215 -113 checkjobs 516 142 -374 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 2/2 up/down: 660/-487) Total: 173 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--shell/hush.c404
-rw-r--r--shell/hush_test/hush-misc/wait1.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait1.tests3
-rw-r--r--shell/hush_test/hush-misc/wait2.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait2.tests4
-rw-r--r--shell/hush_test/hush-misc/wait3.right2
-rwxr-xr-xshell/hush_test/hush-misc/wait3.tests3
7 files changed, 246 insertions, 174 deletions
diff --git a/shell/hush.c b/shell/hush.c
index c80429d5c..1f8a3e607 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -44,6 +44,8 @@
44 * special variables (done: PWD, PPID, RANDOM) 44 * special variables (done: PWD, PPID, RANDOM)
45 * tilde expansion 45 * tilde expansion
46 * aliases 46 * aliases
47 * kill %jobspec
48 * wait %jobspec
47 * follow IFS rules more precisely, including update semantics 49 * follow IFS rules more precisely, including update semantics
48 * builtins mandated by standards we don't support: 50 * builtins mandated by standards we don't support:
49 * [un]alias, command, fc, getopts, newgrp, readonly, times 51 * [un]alias, command, fc, getopts, newgrp, readonly, times
@@ -7041,16 +7043,134 @@ static void delete_finished_bg_job(struct pipe *pi)
7041} 7043}
7042#endif /* JOB */ 7044#endif /* JOB */
7043 7045
7044/* Check to see if any processes have exited -- if they 7046static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
7045 * have, figure out why and see if a job has completed */
7046static int checkjobs(struct pipe *fg_pipe)
7047{ 7047{
7048 int attributes;
7049 int status;
7050#if ENABLE_HUSH_JOB 7048#if ENABLE_HUSH_JOB
7051 struct pipe *pi; 7049 struct pipe *pi;
7052#endif 7050#endif
7053 pid_t childpid; 7051 int i, dead;
7052
7053 dead = WIFEXITED(status) || WIFSIGNALED(status);
7054
7055#if DEBUG_JOBS
7056 if (WIFSTOPPED(status))
7057 debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
7058 childpid, WSTOPSIG(status), WEXITSTATUS(status));
7059 if (WIFSIGNALED(status))
7060 debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
7061 childpid, WTERMSIG(status), WEXITSTATUS(status));
7062 if (WIFEXITED(status))
7063 debug_printf_jobs("pid %d exited, exitcode %d\n",
7064 childpid, WEXITSTATUS(status));
7065#endif
7066 /* Were we asked to wait for a fg pipe? */
7067 if (fg_pipe) {
7068 i = fg_pipe->num_cmds;
7069 while (--i >= 0) {
7070 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
7071 if (fg_pipe->cmds[i].pid != childpid)
7072 continue;
7073 if (dead) {
7074 int ex;
7075 fg_pipe->cmds[i].pid = 0;
7076 fg_pipe->alive_cmds--;
7077 ex = WEXITSTATUS(status);
7078 /* bash prints killer signal's name for *last*
7079 * process in pipe (prints just newline for SIGINT/SIGPIPE).
7080 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
7081 */
7082 if (WIFSIGNALED(status)) {
7083 int sig = WTERMSIG(status);
7084 if (i == fg_pipe->num_cmds-1)
7085 /* TODO: use strsignal() instead for bash compat? but that's bloat... */
7086 puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
7087 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
7088 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
7089 * Maybe we need to use sig | 128? */
7090 ex = sig + 128;
7091 }
7092 fg_pipe->cmds[i].cmd_exitcode = ex;
7093 } else {
7094 fg_pipe->stopped_cmds++;
7095 }
7096 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
7097 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
7098 if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
7099 /* All processes in fg pipe have exited or stopped */
7100 int rcode = 0;
7101 i = fg_pipe->num_cmds;
7102 while (--i >= 0) {
7103 rcode = fg_pipe->cmds[i].cmd_exitcode;
7104 /* usually last process gives overall exitstatus,
7105 * but with "set -o pipefail", last *failed* process does */
7106 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
7107 break;
7108 }
7109 IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
7110/* Note: *non-interactive* bash does not continue if all processes in fg pipe
7111 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
7112 * and "killall -STOP cat" */
7113 if (G_interactive_fd) {
7114#if ENABLE_HUSH_JOB
7115 if (fg_pipe->alive_cmds != 0)
7116 insert_bg_job(fg_pipe);
7117#endif
7118 return rcode;
7119 }
7120 if (fg_pipe->alive_cmds == 0)
7121 return rcode;
7122 }
7123 /* There are still running processes in the fg_pipe */
7124 return -1;
7125 }
7126 /* It wasnt in fg_pipe, look for process in bg pipes */
7127 }
7128
7129#if ENABLE_HUSH_JOB
7130 /* We were asked to wait for bg or orphaned children */
7131 /* No need to remember exitcode in this case */
7132 for (pi = G.job_list; pi; pi = pi->next) {
7133 for (i = 0; i < pi->num_cmds; i++) {
7134 if (pi->cmds[i].pid == childpid)
7135 goto found_pi_and_prognum;
7136 }
7137 }
7138 /* Happens when shell is used as init process (init=/bin/sh) */
7139 debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
7140 return -1; /* this wasn't a process from fg_pipe */
7141
7142 found_pi_and_prognum:
7143 if (dead) {
7144 /* child exited */
7145 pi->cmds[i].pid = 0;
7146 pi->cmds[i].cmd_exitcode = WEXITSTATUS(status);
7147 if (WIFSIGNALED(status))
7148 pi->cmds[i].cmd_exitcode = 128 + WTERMSIG(status);
7149 pi->alive_cmds--;
7150 if (!pi->alive_cmds) {
7151 if (G_interactive_fd)
7152 printf(JOB_STATUS_FORMAT, pi->jobid,
7153 "Done", pi->cmdtext);
7154 delete_finished_bg_job(pi);
7155 }
7156 } else {
7157 /* child stopped */
7158 pi->stopped_cmds++;
7159 }
7160#endif
7161 return -1; /* this wasn't a process from fg_pipe */
7162}
7163
7164/* Check to see if any processes have exited -- if they have,
7165 * figure out why and see if a job has completed.
7166 * Alternatively (fg_pipe == NULL, waitfor_pid != 0),
7167 * wait for a specific pid to complete, return exitcode+1
7168 * (this allows to distinguish zero as "no children exited" result).
7169 */
7170static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid)
7171{
7172 int attributes;
7173 int status;
7054 int rcode = 0; 7174 int rcode = 0;
7055 7175
7056 debug_printf_jobs("checkjobs %p\n", fg_pipe); 7176 debug_printf_jobs("checkjobs %p\n", fg_pipe);
@@ -7087,12 +7207,10 @@ static int checkjobs(struct pipe *fg_pipe)
7087 * 1 <========== bg pipe is not fully done, but exitcode is already known! 7207 * 1 <========== bg pipe is not fully done, but exitcode is already known!
7088 * [hush 1.14.0: yes we do it right] 7208 * [hush 1.14.0: yes we do it right]
7089 */ 7209 */
7090 wait_more:
7091 while (1) { 7210 while (1) {
7092 int i; 7211 pid_t childpid;
7093 int dead;
7094
7095#if ENABLE_HUSH_FAST 7212#if ENABLE_HUSH_FAST
7213 int i;
7096 i = G.count_SIGCHLD; 7214 i = G.count_SIGCHLD;
7097#endif 7215#endif
7098 childpid = waitpid(-1, &status, attributes); 7216 childpid = waitpid(-1, &status, attributes);
@@ -7106,112 +7224,24 @@ static int checkjobs(struct pipe *fg_pipe)
7106//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); 7224//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
7107 } 7225 }
7108#endif 7226#endif
7227 /* ECHILD (no children), or 0 (no change in children status) */
7228 rcode = childpid;
7109 break; 7229 break;
7110 } 7230 }
7111 dead = WIFEXITED(status) || WIFSIGNALED(status); 7231 rcode = process_wait_result(fg_pipe, childpid, status);
7112 7232 if (rcode >= 0) {
7113#if DEBUG_JOBS 7233 /* fg_pipe exited or stopped */
7114 if (WIFSTOPPED(status)) 7234 break;
7115 debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
7116 childpid, WSTOPSIG(status), WEXITSTATUS(status));
7117 if (WIFSIGNALED(status))
7118 debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
7119 childpid, WTERMSIG(status), WEXITSTATUS(status));
7120 if (WIFEXITED(status))
7121 debug_printf_jobs("pid %d exited, exitcode %d\n",
7122 childpid, WEXITSTATUS(status));
7123#endif
7124 /* Were we asked to wait for fg pipe? */
7125 if (fg_pipe) {
7126 i = fg_pipe->num_cmds;
7127 while (--i >= 0) {
7128 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
7129 if (fg_pipe->cmds[i].pid != childpid)
7130 continue;
7131 if (dead) {
7132 int ex;
7133 fg_pipe->cmds[i].pid = 0;
7134 fg_pipe->alive_cmds--;
7135 ex = WEXITSTATUS(status);
7136 /* bash prints killer signal's name for *last*
7137 * process in pipe (prints just newline for SIGINT/SIGPIPE).
7138 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
7139 */
7140 if (WIFSIGNALED(status)) {
7141 int sig = WTERMSIG(status);
7142 if (i == fg_pipe->num_cmds-1)
7143 /* TODO: use strsignal() instead for bash compat? but that's bloat... */
7144 puts(sig == SIGINT || sig == SIGPIPE ? "" : get_signame(sig));
7145 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
7146 /* TODO: MIPS has 128 sigs (1..128), what if sig==128 here?
7147 * Maybe we need to use sig | 128? */
7148 ex = sig + 128;
7149 }
7150 fg_pipe->cmds[i].cmd_exitcode = ex;
7151 } else {
7152 fg_pipe->stopped_cmds++;
7153 }
7154 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
7155 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
7156 if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) {
7157 /* All processes in fg pipe have exited or stopped */
7158 i = fg_pipe->num_cmds;
7159 while (--i >= 0) {
7160 rcode = fg_pipe->cmds[i].cmd_exitcode;
7161 /* usually last process gives overall exitstatus,
7162 * but with "set -o pipefail", last *failed* process does */
7163 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
7164 break;
7165 }
7166 IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;)
7167/* Note: *non-interactive* bash does not continue if all processes in fg pipe
7168 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
7169 * and "killall -STOP cat" */
7170 if (G_interactive_fd) {
7171#if ENABLE_HUSH_JOB
7172 if (fg_pipe->alive_cmds != 0)
7173 insert_bg_job(fg_pipe);
7174#endif
7175 return rcode;
7176 }
7177 if (fg_pipe->alive_cmds == 0)
7178 return rcode;
7179 }
7180 /* There are still running processes in the fg pipe */
7181 goto wait_more; /* do waitpid again */
7182 }
7183 /* it wasnt fg_pipe, look for process in bg pipes */
7184 }
7185
7186#if ENABLE_HUSH_JOB
7187 /* We asked to wait for bg or orphaned children */
7188 /* No need to remember exitcode in this case */
7189 for (pi = G.job_list; pi; pi = pi->next) {
7190 for (i = 0; i < pi->num_cmds; i++) {
7191 if (pi->cmds[i].pid == childpid)
7192 goto found_pi_and_prognum;
7193 }
7194 } 7235 }
7195 /* Happens when shell is used as init process (init=/bin/sh) */ 7236 if (childpid == waitfor_pid) {
7196 debug_printf("checkjobs: pid %d was not in our list!\n", childpid); 7237 rcode = WEXITSTATUS(status);
7197 continue; /* do waitpid again */ 7238 if (WIFSIGNALED(status))
7198 7239 rcode = 128 + WTERMSIG(status);
7199 found_pi_and_prognum: 7240 rcode++;
7200 if (dead) { 7241 break; /* "wait PID" called us, give it exitcode+1 */
7201 /* child exited */
7202 pi->cmds[i].pid = 0;
7203 pi->alive_cmds--;
7204 if (!pi->alive_cmds) {
7205 if (G_interactive_fd)
7206 printf(JOB_STATUS_FORMAT, pi->jobid,
7207 "Done", pi->cmdtext);
7208 delete_finished_bg_job(pi);
7209 }
7210 } else {
7211 /* child stopped */
7212 pi->stopped_cmds++;
7213 } 7242 }
7214#endif 7243 /* This wasn't one of our processes, or */
7244 /* fg_pipe still has running processes, do waitpid again */
7215 } /* while (waitpid succeeds)... */ 7245 } /* while (waitpid succeeds)... */
7216 7246
7217 return rcode; 7247 return rcode;
@@ -7221,7 +7251,7 @@ static int checkjobs(struct pipe *fg_pipe)
7221static int checkjobs_and_fg_shell(struct pipe *fg_pipe) 7251static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
7222{ 7252{
7223 pid_t p; 7253 pid_t p;
7224 int rcode = checkjobs(fg_pipe); 7254 int rcode = checkjobs(fg_pipe, 0 /*(no pid to wait for)*/);
7225 if (G_saved_tty_pgrp) { 7255 if (G_saved_tty_pgrp) {
7226 /* Job finished, move the shell to the foreground */ 7256 /* Job finished, move the shell to the foreground */
7227 p = getpgrp(); /* our process group id */ 7257 p = getpgrp(); /* our process group id */
@@ -7893,7 +7923,7 @@ static int run_list(struct pipe *pi)
7893 /* else: e.g. "continue 2" should *break* once, *then* continue */ 7923 /* else: e.g. "continue 2" should *break* once, *then* continue */
7894 } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */ 7924 } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
7895 if (G.depth_break_continue != 0 || fbc == BC_BREAK) { 7925 if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
7896 checkjobs(NULL); 7926 checkjobs(NULL, 0 /*(no pid to wait for)*/);
7897 break; 7927 break;
7898 } 7928 }
7899 /* "continue": simulate end of loop */ 7929 /* "continue": simulate end of loop */
@@ -7902,7 +7932,7 @@ static int run_list(struct pipe *pi)
7902 } 7932 }
7903#endif 7933#endif
7904 if (G_flag_return_in_progress == 1) { 7934 if (G_flag_return_in_progress == 1) {
7905 checkjobs(NULL); 7935 checkjobs(NULL, 0 /*(no pid to wait for)*/);
7906 break; 7936 break;
7907 } 7937 }
7908 } else if (pi->followup == PIPE_BG) { 7938 } else if (pi->followup == PIPE_BG) {
@@ -7929,7 +7959,7 @@ static int run_list(struct pipe *pi)
7929 } else 7959 } else
7930#endif 7960#endif
7931 { /* This one just waits for completion */ 7961 { /* This one just waits for completion */
7932 rcode = checkjobs(pi); 7962 rcode = checkjobs(pi, 0 /*(no pid to wait for)*/);
7933 debug_printf_exec(": checkjobs exitcode %d\n", rcode); 7963 debug_printf_exec(": checkjobs exitcode %d\n", rcode);
7934 check_and_run_traps(); 7964 check_and_run_traps();
7935 } 7965 }
@@ -7943,7 +7973,7 @@ static int run_list(struct pipe *pi)
7943 cond_code = rcode; 7973 cond_code = rcode;
7944#endif 7974#endif
7945 check_jobs_and_continue: 7975 check_jobs_and_continue:
7946 checkjobs(NULL); 7976 checkjobs(NULL, 0 /*(no pid to wait for)*/);
7947 dont_check_jobs_but_continue: ; 7977 dont_check_jobs_but_continue: ;
7948#if ENABLE_HUSH_LOOPS 7978#if ENABLE_HUSH_LOOPS
7949 /* Beware of "while false; true; do ..."! */ 7979 /* Beware of "while false; true; do ..."! */
@@ -9408,9 +9438,68 @@ static int FAST_FUNC builtin_umask(char **argv)
9408} 9438}
9409 9439
9410/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ 9440/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
9441static int wait_for_child_or_signal(pid_t waitfor_pid)
9442{
9443 int ret = 0;
9444 for (;;) {
9445 int sig;
9446 sigset_t oldset, allsigs;
9447
9448 /* waitpid is not interruptible by SA_RESTARTed
9449 * signals which we use. Thus, this ugly dance:
9450 */
9451
9452 /* Make sure possible SIGCHLD is stored in kernel's
9453 * pending signal mask before we call waitpid.
9454 * Or else we may race with SIGCHLD, lose it,
9455 * and get stuck in sigwaitinfo...
9456 */
9457 sigfillset(&allsigs);
9458 sigprocmask(SIG_SETMASK, &allsigs, &oldset);
9459
9460 if (!sigisemptyset(&G.pending_set)) {
9461 /* Crap! we raced with some signal! */
9462 // sig = 0;
9463 goto restore;
9464 }
9465
9466 /*errno = 0; - checkjobs does this */
9467 ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */
9468 /* if ECHILD, there are no children (ret is -1 or 0) */
9469 /* if ret == 0, no children changed state */
9470 /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */
9471 if (errno == ECHILD || ret--) {
9472 if (ret < 0) /* if ECHILD, may need to fix */
9473 ret = 0;
9474 sigprocmask(SIG_SETMASK, &oldset, NULL);
9475 break;
9476 }
9477
9478 /* Wait for SIGCHLD or any other signal */
9479 //sig = sigwaitinfo(&allsigs, NULL);
9480 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
9481 /* Note: sigsuspend invokes signal handler */
9482 sigsuspend(&oldset);
9483 restore:
9484 sigprocmask(SIG_SETMASK, &oldset, NULL);
9485
9486 /* So, did we get a signal? */
9487 //if (sig > 0)
9488 // raise(sig); /* run handler */
9489 sig = check_and_run_traps();
9490 if (sig /*&& sig != SIGCHLD - always true */) {
9491 /* see note 2 */
9492 ret = 128 + sig;
9493 break;
9494 }
9495 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
9496 }
9497 return ret;
9498}
9499
9411static int FAST_FUNC builtin_wait(char **argv) 9500static int FAST_FUNC builtin_wait(char **argv)
9412{ 9501{
9413 int ret = EXIT_SUCCESS; 9502 int ret;
9414 int status; 9503 int status;
9415 9504
9416 argv = skip_dash_dash(argv); 9505 argv = skip_dash_dash(argv);
@@ -9431,74 +9520,41 @@ static int FAST_FUNC builtin_wait(char **argv)
9431 * ^C <-- after ~4 sec from keyboard 9520 * ^C <-- after ~4 sec from keyboard
9432 * $ 9521 * $
9433 */ 9522 */
9434 while (1) { 9523 return wait_for_child_or_signal(0 /*(no pid to wait for)*/);
9435 int sig;
9436 sigset_t oldset, allsigs;
9437
9438 /* waitpid is not interruptible by SA_RESTARTed
9439 * signals which we use. Thus, this ugly dance:
9440 */
9441
9442 /* Make sure possible SIGCHLD is stored in kernel's
9443 * pending signal mask before we call waitpid.
9444 * Or else we may race with SIGCHLD, lose it,
9445 * and get stuck in sigwaitinfo...
9446 */
9447 sigfillset(&allsigs);
9448 sigprocmask(SIG_SETMASK, &allsigs, &oldset);
9449
9450 if (!sigisemptyset(&G.pending_set)) {
9451 /* Crap! we raced with some signal! */
9452 // sig = 0;
9453 goto restore;
9454 }
9455
9456 checkjobs(NULL); /* waitpid(WNOHANG) inside */
9457 if (errno == ECHILD) {
9458 sigprocmask(SIG_SETMASK, &oldset, NULL);
9459 break;
9460 }
9461
9462 /* Wait for SIGCHLD or any other signal */
9463 //sig = sigwaitinfo(&allsigs, NULL);
9464 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
9465 /* Note: sigsuspend invokes signal handler */
9466 sigsuspend(&oldset);
9467 restore:
9468 sigprocmask(SIG_SETMASK, &oldset, NULL);
9469
9470 /* So, did we get a signal? */
9471 //if (sig > 0)
9472 // raise(sig); /* run handler */
9473 sig = check_and_run_traps();
9474 if (sig /*&& sig != SIGCHLD - always true */) {
9475 /* see note 2 */
9476 ret = 128 + sig;
9477 break;
9478 }
9479 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
9480 }
9481 return ret;
9482 } 9524 }
9483 9525
9484 /* This is probably buggy wrt interruptible-ness */ 9526 /* TODO: support "wait %jobspec" */
9485 while (*argv) { 9527 do {
9486 pid_t pid = bb_strtou(*argv, NULL, 10); 9528 pid_t pid = bb_strtou(*argv, NULL, 10);
9487 if (errno) { 9529 if (errno || pid <= 0) {
9488 /* mimic bash message */ 9530 /* mimic bash message */
9489 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); 9531 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
9490 return EXIT_FAILURE; 9532 return EXIT_FAILURE;
9491 } 9533 }
9492 if (waitpid(pid, &status, 0) == pid) { 9534 /* Do we have such child? */
9535 ret = waitpid(pid, &status, WNOHANG);
9536 if (ret < 0) {
9537 /* No */
9538 if (errno == ECHILD) {
9539 /* Example: "wait 1". mimic bash message */
9540 bb_error_msg("wait: pid %d is not a child of this shell", (int)pid);
9541 } else {
9542 /* ??? */
9543 bb_perror_msg("wait %s", *argv);
9544 }
9545 ret = 127;
9546 } else if (ret == 0) {
9547 /* Yes, and it still runs */
9548 ret = wait_for_child_or_signal(pid);
9549 } else {
9550 /* Yes, and it just exited */
9551 process_wait_result(NULL, pid, status);
9493 ret = WEXITSTATUS(status); 9552 ret = WEXITSTATUS(status);
9494 if (WIFSIGNALED(status)) 9553 if (WIFSIGNALED(status))
9495 ret = 128 + WTERMSIG(status); 9554 ret = 128 + WTERMSIG(status);
9496 } else {
9497 bb_perror_msg("wait %s", *argv);
9498 ret = 127;
9499 } 9555 }
9500 argv++; 9556 argv++;
9501 } 9557 } while (*argv);
9502 9558
9503 return ret; 9559 return ret;
9504} 9560}
diff --git a/shell/hush_test/hush-misc/wait1.right b/shell/hush_test/hush-misc/wait1.right
new file mode 100644
index 000000000..20f514da0
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait1.right
@@ -0,0 +1,2 @@
10
2[1] Running sleep 2
diff --git a/shell/hush_test/hush-misc/wait1.tests b/shell/hush_test/hush-misc/wait1.tests
new file mode 100755
index 000000000..f9cf6d48c
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait1.tests
@@ -0,0 +1,3 @@
1sleep 2 & sleep 1 & wait $!
2echo $?
3jobs
diff --git a/shell/hush_test/hush-misc/wait2.right b/shell/hush_test/hush-misc/wait2.right
new file mode 100644
index 000000000..f018c2c98
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait2.right
@@ -0,0 +1,2 @@
10
2[1] Running sleep 3
diff --git a/shell/hush_test/hush-misc/wait2.tests b/shell/hush_test/hush-misc/wait2.tests
new file mode 100755
index 000000000..be20f95a5
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait2.tests
@@ -0,0 +1,4 @@
1sleep 3 & sleep 2 & sleep 1
2wait $!
3echo $?
4jobs
diff --git a/shell/hush_test/hush-misc/wait3.right b/shell/hush_test/hush-misc/wait3.right
new file mode 100644
index 000000000..5437b1d73
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait3.right
@@ -0,0 +1,2 @@
13
2[1] Running sleep 2
diff --git a/shell/hush_test/hush-misc/wait3.tests b/shell/hush_test/hush-misc/wait3.tests
new file mode 100755
index 000000000..ac541c3fc
--- /dev/null
+++ b/shell/hush_test/hush-misc/wait3.tests
@@ -0,0 +1,3 @@
1sleep 2 & (sleep 1;exit 3) & wait $!
2echo $?
3jobs