aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2016-11-07 22:12:18 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2016-11-07 22:12:18 +0100
commit62b717b75ebff3ab00aae2fee0087a8106208a39 (patch)
tree7b9db59e2ae168cd7105821bfbf89871422a726f
parent4e1c8b4f6a5765e1c2b5033f6fd2196408874233 (diff)
downloadbusybox-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.c117
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
7067static 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
7068static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status) 7088static 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 */
7192static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid) 7214static 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 */
9484static 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
9514static 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