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-w32-d81af7216b3305a1aac211dc847dd1c191f3b307.tar.gz busybox-w32-d81af7216b3305a1aac211dc847dd1c191f3b307.tar.bz2 busybox-w32-d81af7216b3305a1aac211dc847dd1c191f3b307.zip |
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) | |||
5355 | { | 5355 | { |
5356 | int st; | 5356 | int st; |
5357 | 5357 | ||
5358 | TRACE(("waitforjob(%%%d) called\n", jobno(jp))); | 5358 | TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0)); |
5359 | 5359 | ||
5360 | INT_OFF; | 5360 | INT_OFF; |
5361 | while (jp->state == JOBRUNNING) { | 5361 | while ((jp && jp->state == JOBRUNNING) || got_sigchld) { |
5362 | /* In non-interactive shells, we _can_ get | 5362 | /* In non-interactive shells, we _can_ get |
5363 | * a keyboard signal here and be EINTRed, | 5363 | * a keyboard signal here and be EINTRed, |
5364 | * but we just loop back, waiting for command to complete. | 5364 | * but we just loop back, waiting for command to complete. |
@@ -5393,6 +5393,8 @@ waitforjob(struct job *jp) | |||
5393 | } | 5393 | } |
5394 | INT_ON; | 5394 | INT_ON; |
5395 | 5395 | ||
5396 | if (!jp) | ||
5397 | return exitstatus; | ||
5396 | st = getstatus(jp); | 5398 | st = getstatus(jp); |
5397 | #if JOBS | 5399 | #if JOBS |
5398 | if (jp->jobctl) { | 5400 | if (jp->jobctl) { |
@@ -10311,6 +10313,8 @@ evalcommand(union node *cmd, int flags) | |||
10311 | goto out; | 10313 | goto out; |
10312 | } | 10314 | } |
10313 | 10315 | ||
10316 | jp = NULL; | ||
10317 | |||
10314 | /* Execute the command. */ | 10318 | /* Execute the command. */ |
10315 | switch (cmdentry.cmdtype) { | 10319 | switch (cmdentry.cmdtype) { |
10316 | default: { | 10320 | default: { |
@@ -10365,7 +10369,6 @@ evalcommand(union node *cmd, int flags) | |||
10365 | jp = makejob(/*cmd,*/ 1); | 10369 | jp = makejob(/*cmd,*/ 1); |
10366 | if (forkshell(jp, cmd, FORK_FG) != 0) { | 10370 | if (forkshell(jp, cmd, FORK_FG) != 0) { |
10367 | /* parent */ | 10371 | /* parent */ |
10368 | status = waitforjob(jp); | ||
10369 | INT_ON; | 10372 | INT_ON; |
10370 | TRACE(("forked child exited with %d\n", status)); | 10373 | TRACE(("forked child exited with %d\n", status)); |
10371 | break; | 10374 | break; |
@@ -10384,33 +10387,24 @@ evalcommand(union node *cmd, int flags) | |||
10384 | if (cmd_is_exec && argc > 1) | 10387 | if (cmd_is_exec && argc > 1) |
10385 | listsetvar(varlist.list, VEXPORT); | 10388 | listsetvar(varlist.list, VEXPORT); |
10386 | } | 10389 | } |
10387 | |||
10388 | /* Tight loop with builtins only: | ||
10389 | * "while kill -0 $child; do true; done" | ||
10390 | * will never exit even if $child died, unless we do this | ||
10391 | * to reap the zombie and make kill detect that it's gone: */ | ||
10392 | dowait(DOWAIT_NONBLOCK, NULL); | ||
10393 | |||
10394 | if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { | 10390 | if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { |
10395 | if (exception_type == EXERROR && spclbltin <= 0) { | 10391 | if (exception_type == EXERROR && spclbltin <= 0) { |
10396 | FORCE_INT_ON; | 10392 | FORCE_INT_ON; |
10397 | goto readstatus; | 10393 | break; |
10398 | } | 10394 | } |
10399 | raise: | 10395 | raise: |
10400 | longjmp(exception_handler->loc, 1); | 10396 | longjmp(exception_handler->loc, 1); |
10401 | } | 10397 | } |
10402 | goto readstatus; | 10398 | break; |
10403 | 10399 | ||
10404 | case CMDFUNCTION: | 10400 | case CMDFUNCTION: |
10405 | /* See above for the rationale */ | ||
10406 | dowait(DOWAIT_NONBLOCK, NULL); | ||
10407 | if (evalfun(cmdentry.u.func, argc, argv, flags)) | 10401 | if (evalfun(cmdentry.u.func, argc, argv, flags)) |
10408 | goto raise; | 10402 | goto raise; |
10409 | readstatus: | ||
10410 | status = exitstatus; | ||
10411 | break; | 10403 | break; |
10412 | } /* switch */ | 10404 | } /* switch */ |
10413 | 10405 | ||
10406 | status = waitforjob(jp); | ||
10407 | |||
10414 | out: | 10408 | out: |
10415 | if (cmd->ncmd.redirect) | 10409 | if (cmd->ncmd.redirect) |
10416 | popredir(/*drop:*/ cmd_is_exec); | 10410 | popredir(/*drop:*/ cmd_is_exec); |