diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2016-11-07 22:12:18 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2016-11-07 22:12:18 +0100 |
commit | 62b717b75ebff3ab00aae2fee0087a8106208a39 (patch) | |
tree | 7b9db59e2ae168cd7105821bfbf89871422a726f | |
parent | 4e1c8b4f6a5765e1c2b5033f6fd2196408874233 (diff) | |
download | busybox-w32-62b717b75ebff3ab00aae2fee0087a8106208a39.tar.gz busybox-w32-62b717b75ebff3ab00aae2fee0087a8106208a39.tar.bz2 busybox-w32-62b717b75ebff3ab00aae2fee0087a8106208a39.zip |
hush: implement "wait %jobspec"
function old new delta
parse_jobspec - 83 +83
job_exited_or_stopped - 79 +79
builtin_wait 236 302 +66
wait_for_child_or_signal 199 228 +29
checkjobs 142 158 +16
builtin_jobs 59 68 +9
process_wait_result 453 408 -45
builtin_fg_bg 272 203 -69
------------------------------------------------------------------------------
(add/remove: 2/0 grow/shrink: 4/2 up/down: 282/-114) Total: 168 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 117 |
1 files changed, 88 insertions, 29 deletions
diff --git a/shell/hush.c b/shell/hush.c index 3b87d283c..b842d6eec 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -45,7 +45,6 @@ | |||
45 | * tilde expansion | 45 | * tilde expansion |
46 | * aliases | 46 | * aliases |
47 | * kill %jobspec | 47 | * kill %jobspec |
48 | * wait %jobspec | ||
49 | * follow IFS rules more precisely, including update semantics | 48 | * follow IFS rules more precisely, including update semantics |
50 | * builtins mandated by standards we don't support: | 49 | * builtins mandated by standards we don't support: |
51 | * [un]alias, command, fc, getopts, newgrp, readonly, times | 50 | * [un]alias, command, fc, getopts, newgrp, readonly, times |
@@ -7065,6 +7064,27 @@ static void delete_finished_bg_job(struct pipe *pi) | |||
7065 | } | 7064 | } |
7066 | #endif /* JOB */ | 7065 | #endif /* JOB */ |
7067 | 7066 | ||
7067 | static int job_exited_or_stopped(struct pipe *pi) | ||
7068 | { | ||
7069 | int rcode, i; | ||
7070 | |||
7071 | if (pi->alive_cmds != pi->stopped_cmds) | ||
7072 | return -1; | ||
7073 | |||
7074 | /* All processes in fg pipe have exited or stopped */ | ||
7075 | rcode = 0; | ||
7076 | i = pi->num_cmds; | ||
7077 | while (--i >= 0) { | ||
7078 | rcode = pi->cmds[i].cmd_exitcode; | ||
7079 | /* usually last process gives overall exitstatus, | ||
7080 | * but with "set -o pipefail", last *failed* process does */ | ||
7081 | if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0) | ||
7082 | break; | ||
7083 | } | ||
7084 | IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) | ||
7085 | return rcode; | ||
7086 | } | ||
7087 | |||
7068 | static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | 7088 | static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) |
7069 | { | 7089 | { |
7070 | #if ENABLE_HUSH_JOB | 7090 | #if ENABLE_HUSH_JOB |
@@ -7088,7 +7108,10 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
7088 | /* Were we asked to wait for a fg pipe? */ | 7108 | /* Were we asked to wait for a fg pipe? */ |
7089 | if (fg_pipe) { | 7109 | if (fg_pipe) { |
7090 | i = fg_pipe->num_cmds; | 7110 | i = fg_pipe->num_cmds; |
7111 | |||
7091 | while (--i >= 0) { | 7112 | while (--i >= 0) { |
7113 | int rcode; | ||
7114 | |||
7092 | debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid); | 7115 | debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid); |
7093 | if (fg_pipe->cmds[i].pid != childpid) | 7116 | if (fg_pipe->cmds[i].pid != childpid) |
7094 | continue; | 7117 | continue; |
@@ -7117,18 +7140,8 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
7117 | } | 7140 | } |
7118 | debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n", | 7141 | debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n", |
7119 | fg_pipe->alive_cmds, fg_pipe->stopped_cmds); | 7142 | fg_pipe->alive_cmds, fg_pipe->stopped_cmds); |
7120 | if (fg_pipe->alive_cmds == fg_pipe->stopped_cmds) { | 7143 | rcode = job_exited_or_stopped(fg_pipe); |
7121 | /* All processes in fg pipe have exited or stopped */ | 7144 | if (rcode >= 0) { |
7122 | int rcode = 0; | ||
7123 | i = fg_pipe->num_cmds; | ||
7124 | while (--i >= 0) { | ||
7125 | rcode = fg_pipe->cmds[i].cmd_exitcode; | ||
7126 | /* usually last process gives overall exitstatus, | ||
7127 | * but with "set -o pipefail", last *failed* process does */ | ||
7128 | if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0) | ||
7129 | break; | ||
7130 | } | ||
7131 | IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) | ||
7132 | /* Note: *non-interactive* bash does not continue if all processes in fg pipe | 7145 | /* Note: *non-interactive* bash does not continue if all processes in fg pipe |
7133 | * are stopped. Testcase: "cat | cat" in a script (not on command line!) | 7146 | * are stopped. Testcase: "cat | cat" in a script (not on command line!) |
7134 | * and "killall -STOP cat" */ | 7147 | * and "killall -STOP cat" */ |
@@ -7185,9 +7198,18 @@ static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) | |||
7185 | 7198 | ||
7186 | /* Check to see if any processes have exited -- if they have, | 7199 | /* Check to see if any processes have exited -- if they have, |
7187 | * figure out why and see if a job has completed. | 7200 | * figure out why and see if a job has completed. |
7188 | * Alternatively (fg_pipe == NULL, waitfor_pid != 0), | 7201 | * |
7189 | * wait for a specific pid to complete, return exitcode+1 | 7202 | * If non-NULL fg_pipe: wait for its completion or stop. |
7190 | * (this allows to distinguish zero as "no children exited" result). | 7203 | * Return its exitcode or zero if stopped. |
7204 | * | ||
7205 | * Alternatively (fg_pipe == NULL, waitfor_pid != 0): | ||
7206 | * waitpid(WNOHANG), if waitfor_pid exits or stops, return exitcode+1, | ||
7207 | * else return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for) | ||
7208 | * or 0 if no children changed status. | ||
7209 | * | ||
7210 | * Alternatively (fg_pipe == NULL, waitfor_pid == 0), | ||
7211 | * return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for) | ||
7212 | * or 0 if no children changed status. | ||
7191 | */ | 7213 | */ |
7192 | static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) | 7214 | static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) |
7193 | { | 7215 | { |
@@ -7256,9 +7278,13 @@ static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) | |||
7256 | break; | 7278 | break; |
7257 | } | 7279 | } |
7258 | if (childpid == waitfor_pid) { | 7280 | if (childpid == waitfor_pid) { |
7281 | debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status); | ||
7259 | rcode = WEXITSTATUS(status); | 7282 | rcode = WEXITSTATUS(status); |
7260 | if (WIFSIGNALED(status)) | 7283 | if (WIFSIGNALED(status)) |
7261 | rcode = 128 + WTERMSIG(status); | 7284 | rcode = 128 + WTERMSIG(status); |
7285 | if (WIFSTOPPED(status)) | ||
7286 | /* bash: "cmd & wait $!" and cmd stops: $? = 128 + stopsig */ | ||
7287 | rcode = 128 + WSTOPSIG(status); | ||
7262 | rcode++; | 7288 | rcode++; |
7263 | break; /* "wait PID" called us, give it exitcode+1 */ | 7289 | break; /* "wait PID" called us, give it exitcode+1 */ |
7264 | } | 7290 | } |
@@ -9329,6 +9355,7 @@ static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM) | |||
9329 | struct pipe *job; | 9355 | struct pipe *job; |
9330 | const char *status_string; | 9356 | const char *status_string; |
9331 | 9357 | ||
9358 | checkjobs(NULL, 0 /*(no pid to wait for)*/); | ||
9332 | for (job = G.job_list; job; job = job->next) { | 9359 | for (job = G.job_list; job; job = job->next) { |
9333 | if (job->alive_cmds == job->stopped_cmds) | 9360 | if (job->alive_cmds == job->stopped_cmds) |
9334 | status_string = "Stopped"; | 9361 | status_string = "Stopped"; |
@@ -9481,12 +9508,15 @@ static int FAST_FUNC builtin_umask(char **argv) | |||
9481 | } | 9508 | } |
9482 | 9509 | ||
9483 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ | 9510 | /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */ |
9484 | static int wait_for_child_or_signal(pid_t waitfor_pid) | 9511 | #if !ENABLE_HUSH_JOB |
9512 | # define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid) | ||
9513 | #endif | ||
9514 | static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid) | ||
9485 | { | 9515 | { |
9486 | int ret = 0; | 9516 | int ret = 0; |
9487 | for (;;) { | 9517 | for (;;) { |
9488 | int sig; | 9518 | int sig; |
9489 | sigset_t oldset, allsigs; | 9519 | sigset_t oldset; |
9490 | 9520 | ||
9491 | /* waitpid is not interruptible by SA_RESTARTed | 9521 | /* waitpid is not interruptible by SA_RESTARTed |
9492 | * signals which we use. Thus, this ugly dance: | 9522 | * signals which we use. Thus, this ugly dance: |
@@ -9495,10 +9525,10 @@ static int wait_for_child_or_signal(pid_t waitfor_pid) | |||
9495 | /* Make sure possible SIGCHLD is stored in kernel's | 9525 | /* Make sure possible SIGCHLD is stored in kernel's |
9496 | * pending signal mask before we call waitpid. | 9526 | * pending signal mask before we call waitpid. |
9497 | * Or else we may race with SIGCHLD, lose it, | 9527 | * Or else we may race with SIGCHLD, lose it, |
9498 | * and get stuck in sigwaitinfo... | 9528 | * and get stuck in sigsuspend... |
9499 | */ | 9529 | */ |
9500 | sigfillset(&allsigs); | 9530 | sigfillset(&oldset); /* block all signals, remember old set */ |
9501 | sigprocmask(SIG_SETMASK, &allsigs, &oldset); | 9531 | sigprocmask(SIG_SETMASK, &oldset, &oldset); |
9502 | 9532 | ||
9503 | if (!sigisemptyset(&G.pending_set)) { | 9533 | if (!sigisemptyset(&G.pending_set)) { |
9504 | /* Crap! we raced with some signal! */ | 9534 | /* Crap! we raced with some signal! */ |
@@ -9507,19 +9537,31 @@ static int wait_for_child_or_signal(pid_t waitfor_pid) | |||
9507 | } | 9537 | } |
9508 | 9538 | ||
9509 | /*errno = 0; - checkjobs does this */ | 9539 | /*errno = 0; - checkjobs does this */ |
9540 | /* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */ | ||
9510 | ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */ | 9541 | ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */ |
9542 | debug_printf_exec("checkjobs:%d\n", ret); | ||
9543 | #if ENABLE_HUSH_JOB | ||
9544 | if (waitfor_pipe) { | ||
9545 | int rcode = job_exited_or_stopped(waitfor_pipe); | ||
9546 | debug_printf_exec("job_exited_or_stopped:%d\n", rcode); | ||
9547 | if (rcode >= 0) { | ||
9548 | ret = rcode; | ||
9549 | sigprocmask(SIG_SETMASK, &oldset, NULL); | ||
9550 | break; | ||
9551 | } | ||
9552 | } | ||
9553 | #endif | ||
9511 | /* if ECHILD, there are no children (ret is -1 or 0) */ | 9554 | /* if ECHILD, there are no children (ret is -1 or 0) */ |
9512 | /* if ret == 0, no children changed state */ | 9555 | /* if ret == 0, no children changed state */ |
9513 | /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */ | 9556 | /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */ |
9514 | if (errno == ECHILD || ret--) { | 9557 | if (errno == ECHILD || ret) { |
9515 | if (ret < 0) /* if ECHILD, may need to fix */ | 9558 | ret--; |
9559 | if (ret < 0) /* if ECHILD, may need to fix "ret" */ | ||
9516 | ret = 0; | 9560 | ret = 0; |
9517 | sigprocmask(SIG_SETMASK, &oldset, NULL); | 9561 | sigprocmask(SIG_SETMASK, &oldset, NULL); |
9518 | break; | 9562 | break; |
9519 | } | 9563 | } |
9520 | |||
9521 | /* Wait for SIGCHLD or any other signal */ | 9564 | /* Wait for SIGCHLD or any other signal */ |
9522 | //sig = sigwaitinfo(&allsigs, NULL); | ||
9523 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ | 9565 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ |
9524 | /* Note: sigsuspend invokes signal handler */ | 9566 | /* Note: sigsuspend invokes signal handler */ |
9525 | sigsuspend(&oldset); | 9567 | sigsuspend(&oldset); |
@@ -9544,6 +9586,7 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9544 | { | 9586 | { |
9545 | int ret; | 9587 | int ret; |
9546 | int status; | 9588 | int status; |
9589 | struct pipe *wait_pipe = NULL; | ||
9547 | 9590 | ||
9548 | argv = skip_dash_dash(argv); | 9591 | argv = skip_dash_dash(argv); |
9549 | if (argv[0] == NULL) { | 9592 | if (argv[0] == NULL) { |
@@ -9563,18 +9606,27 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9563 | * ^C <-- after ~4 sec from keyboard | 9606 | * ^C <-- after ~4 sec from keyboard |
9564 | * $ | 9607 | * $ |
9565 | */ | 9608 | */ |
9566 | return wait_for_child_or_signal(0 /*(no pid to wait for)*/); | 9609 | return wait_for_child_or_signal(NULL, 0 /*(no job and no pid to wait for)*/); |
9567 | } | 9610 | } |
9568 | 9611 | ||
9569 | /* TODO: support "wait %jobspec" */ | ||
9570 | do { | 9612 | do { |
9571 | pid_t pid = bb_strtou(*argv, NULL, 10); | 9613 | pid_t pid = bb_strtou(*argv, NULL, 10); |
9572 | if (errno || pid <= 0) { | 9614 | if (errno || pid <= 0) { |
9615 | #if ENABLE_HUSH_JOB | ||
9616 | if (argv[0][0] == '%') { | ||
9617 | wait_pipe = parse_jobspec(*argv); | ||
9618 | if (wait_pipe) { | ||
9619 | pid = - wait_pipe->pgrp; | ||
9620 | goto do_wait; | ||
9621 | } | ||
9622 | } | ||
9623 | #endif | ||
9573 | /* mimic bash message */ | 9624 | /* mimic bash message */ |
9574 | bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); | 9625 | bb_error_msg("wait: '%s': not a pid or valid job spec", *argv); |
9575 | ret = EXIT_FAILURE; | 9626 | ret = EXIT_FAILURE; |
9576 | continue; /* bash checks all argv[] */ | 9627 | continue; /* bash checks all argv[] */ |
9577 | } | 9628 | } |
9629 | IF_HUSH_JOB(do_wait:) | ||
9578 | /* Do we have such child? */ | 9630 | /* Do we have such child? */ |
9579 | ret = waitpid(pid, &status, WNOHANG); | 9631 | ret = waitpid(pid, &status, WNOHANG); |
9580 | if (ret < 0) { | 9632 | if (ret < 0) { |
@@ -9599,13 +9651,20 @@ static int FAST_FUNC builtin_wait(char **argv) | |||
9599 | } | 9651 | } |
9600 | if (ret == 0) { | 9652 | if (ret == 0) { |
9601 | /* Yes, and it still runs */ | 9653 | /* Yes, and it still runs */ |
9602 | ret = wait_for_child_or_signal(pid); | 9654 | ret = wait_for_child_or_signal(wait_pipe, wait_pipe ? 0 : pid); |
9603 | } else { | 9655 | } else { |
9604 | /* Yes, and it just exited */ | 9656 | /* Yes, and it just exited */ |
9605 | process_wait_result(NULL, pid, status); | 9657 | process_wait_result(NULL, ret, status); |
9606 | ret = WEXITSTATUS(status); | 9658 | ret = WEXITSTATUS(status); |
9607 | if (WIFSIGNALED(status)) | 9659 | if (WIFSIGNALED(status)) |
9608 | ret = 128 + WTERMSIG(status); | 9660 | ret = 128 + WTERMSIG(status); |
9661 | #if ENABLE_HUSH_JOB | ||
9662 | if (wait_pipe) { | ||
9663 | ret = job_exited_or_stopped(wait_pipe); | ||
9664 | if (ret < 0) | ||
9665 | goto do_wait; | ||
9666 | } | ||
9667 | #endif | ||
9609 | } | 9668 | } |
9610 | } while (*++argv); | 9669 | } while (*++argv); |
9611 | 9670 | ||