hush: fix "(sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?"
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 7 Jul 2017 20:56:02 +0000 (22:56 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 7 Jul 2017 20:56:02 +0000 (22:56 +0200)
function                                             old     new   delta
process_wait_result                                  414     426     +12
builtin_wait                                         283     291      +8
run_list                                             974     978      +4
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 24/0)               Total: 24 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash_test/ash-misc/wait6.right [new file with mode: 0644]
shell/ash_test/ash-misc/wait6.tests [new file with mode: 0755]
shell/hush.c
shell/hush_test/hush-misc/wait6.right [new file with mode: 0644]
shell/hush_test/hush-misc/wait6.tests [new file with mode: 0755]

diff --git a/shell/ash_test/ash-misc/wait6.right b/shell/ash_test/ash-misc/wait6.right
new file mode 100644 (file)
index 0000000..12decc1
--- /dev/null
@@ -0,0 +1,2 @@
+0
+3
diff --git a/shell/ash_test/ash-misc/wait6.tests b/shell/ash_test/ash-misc/wait6.tests
new file mode 100755 (executable)
index 0000000..c237131
--- /dev/null
@@ -0,0 +1,6 @@
+# In bash, "wait $!" extracts correct exitcode even if bg task has already exited
+# It prints 0, then 3:
+(sleep 0; exit 3) & sleep 1
+echo $?
+wait $!
+echo $?
index 59bddbfff7e759f51f14dfd4797f29c788334ba4..df96e6fde016882122ae2c80f54c63f4a9ada794 100644 (file)
@@ -832,6 +832,7 @@ struct globals {
        smallint exiting; /* used to prevent EXIT trap recursion */
        /* These four support $?, $#, and $1 */
        smalluint last_exitcode;
+       smalluint last_bg_pid_exitcode;
 #if ENABLE_HUSH_SET
        /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
        smalluint global_args_malloced;
@@ -7387,10 +7388,13 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
  found_pi_and_prognum:
        if (dead) {
                /* child exited */
-               pi->cmds[i].pid = 0;
-               pi->cmds[i].cmd_exitcode = WEXITSTATUS(status);
+               int rcode = WEXITSTATUS(status);
                if (WIFSIGNALED(status))
-                       pi->cmds[i].cmd_exitcode = 128 + WTERMSIG(status);
+                       rcode = 128 + WTERMSIG(status);
+               pi->cmds[i].cmd_exitcode = rcode;
+               if (G.last_bg_pid == pi->cmds[i].pid)
+                       G.last_bg_pid_exitcode = rcode;
+               pi->cmds[i].pid = 0;
                pi->alive_cmds--;
                if (!pi->alive_cmds) {
                        if (G_interactive_fd)
@@ -8215,6 +8219,7 @@ static int run_list(struct pipe *pi)
 #endif
                        /* Last command's pid goes to $! */
                        G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
+                       G.last_bg_pid_exitcode = 0;
                        debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
 /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */
                        rcode = EXIT_SUCCESS;
@@ -9909,6 +9914,7 @@ static int FAST_FUNC builtin_wait(char **argv)
                ret = waitpid(pid, &status, WNOHANG);
                if (ret < 0) {
                        /* No */
+                       ret = 127;
                        if (errno == ECHILD) {
                                if (G.last_bg_pid > 0 && pid == G.last_bg_pid) {
                                        /* "wait $!" but last bg task has already exited. Try:
@@ -9916,7 +9922,7 @@ static int FAST_FUNC builtin_wait(char **argv)
                                         * In bash it prints exitcode 0, then 3.
                                         * In dash, it is 127.
                                         */
-                                       /* ret = G.last_bg_pid_exitstatus - FIXME */
+                                       ret = G.last_bg_pid_exitcode;
                                } else {
                                        /* Example: "wait 1". mimic bash message */
                                        bb_error_msg("wait: pid %d is not a child of this shell", (int)pid);
@@ -9925,7 +9931,6 @@ static int FAST_FUNC builtin_wait(char **argv)
                                /* ??? */
                                bb_perror_msg("wait %s", *argv);
                        }
-                       ret = 127;
                        continue; /* bash checks all argv[] */
                }
                if (ret == 0) {
diff --git a/shell/hush_test/hush-misc/wait6.right b/shell/hush_test/hush-misc/wait6.right
new file mode 100644 (file)
index 0000000..12decc1
--- /dev/null
@@ -0,0 +1,2 @@
+0
+3
diff --git a/shell/hush_test/hush-misc/wait6.tests b/shell/hush_test/hush-misc/wait6.tests
new file mode 100755 (executable)
index 0000000..c237131
--- /dev/null
@@ -0,0 +1,6 @@
+# In bash, "wait $!" extracts correct exitcode even if bg task has already exited
+# It prints 0, then 3:
+(sleep 0; exit 3) & sleep 1
+echo $?
+wait $!
+echo $?