diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2020-02-18 14:28:30 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2020-02-18 14:28:30 +0100 |
commit | d81af7216b3305a1aac211dc847dd1c191f3b307 (patch) | |
tree | 3ff0488dfe7ee89a7a5472c2f75e9c8cecc99554 | |
parent | 22c75924daa41b7ea097796afd4baafa2fc99d05 (diff) | |
download | busybox-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>
-rw-r--r-- | shell/ash.c | 26 |
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); |