aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2020-02-18 14:28:30 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2020-02-18 14:28:30 +0100
commitd81af7216b3305a1aac211dc847dd1c191f3b307 (patch)
tree3ff0488dfe7ee89a7a5472c2f75e9c8cecc99554 /shell
parent22c75924daa41b7ea097796afd4baafa2fc99d05 (diff)
downloadbusybox-d81af7216b3305a1aac211dc847dd1c191f3b307.tar.gz
ash: eval: Reap zombies after built-in commands and functions
Upstream commit: Date: Mon, 26 Mar 2018 23:55:50 +0800 eval: Reap zombies after built-in commands and functions Currently dash does not reap dead children after built-in commands or functions. This means that if you construct a loop consisting of solely built-in commands and functions, then zombies can hang around indefinitely. This patch fixes this by reaping when necessary after each built-in command and function. Reported-by: Denys Vlasenko <vda.linux@googlemail.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r--shell/ash.c26
1 files changed, 10 insertions, 16 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 389db3cd0..8047cf98f 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -5355,10 +5355,10 @@ waitforjob(struct job *jp)
{
int st;
- TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+ TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
INT_OFF;
- while (jp->state == JOBRUNNING) {
+ while ((jp && jp->state == JOBRUNNING) || got_sigchld) {
/* In non-interactive shells, we _can_ get
* a keyboard signal here and be EINTRed,
* but we just loop back, waiting for command to complete.
@@ -5393,6 +5393,8 @@ waitforjob(struct job *jp)
}
INT_ON;
+ if (!jp)
+ return exitstatus;
st = getstatus(jp);
#if JOBS
if (jp->jobctl) {
@@ -10311,6 +10313,8 @@ evalcommand(union node *cmd, int flags)
goto out;
}
+ jp = NULL;
+
/* Execute the command. */
switch (cmdentry.cmdtype) {
default: {
@@ -10365,7 +10369,6 @@ evalcommand(union node *cmd, int flags)
jp = makejob(/*cmd,*/ 1);
if (forkshell(jp, cmd, FORK_FG) != 0) {
/* parent */
- status = waitforjob(jp);
INT_ON;
TRACE(("forked child exited with %d\n", status));
break;
@@ -10384,33 +10387,24 @@ evalcommand(union node *cmd, int flags)
if (cmd_is_exec && argc > 1)
listsetvar(varlist.list, VEXPORT);
}
-
- /* Tight loop with builtins only:
- * "while kill -0 $child; do true; done"
- * will never exit even if $child died, unless we do this
- * to reap the zombie and make kill detect that it's gone: */
- dowait(DOWAIT_NONBLOCK, NULL);
-
if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
if (exception_type == EXERROR && spclbltin <= 0) {
FORCE_INT_ON;
- goto readstatus;
+ break;
}
raise:
longjmp(exception_handler->loc, 1);
}
- goto readstatus;
+ break;
case CMDFUNCTION:
- /* See above for the rationale */
- dowait(DOWAIT_NONBLOCK, NULL);
if (evalfun(cmdentry.u.func, argc, argv, flags))
goto raise;
- readstatus:
- status = exitstatus;
break;
} /* switch */
+ status = waitforjob(jp);
+
out:
if (cmd->ncmd.redirect)
popredir(/*drop:*/ cmd_is_exec);