ash: fix "while kill -0 $child; do true; done" looping forever.
This commit is contained in:
parent
7ff85c53f1
commit
be54d6bc60
66
shell/ash.c
66
shell/ash.c
@ -3762,48 +3762,6 @@ sprint_status(char *s, int status, int sigonly)
|
|||||||
return col;
|
return col;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Do a wait system call. If job control is compiled in, we accept
|
|
||||||
* stopped processes. If block is zero, we return a value of zero
|
|
||||||
* rather than blocking.
|
|
||||||
*
|
|
||||||
* System V doesn't have a non-blocking wait system call. It does
|
|
||||||
* have a SIGCLD signal that is sent to a process when one of it's
|
|
||||||
* children dies. The obvious way to use SIGCLD would be to install
|
|
||||||
* a handler for SIGCLD which simply bumped a counter when a SIGCLD
|
|
||||||
* was received, and have waitproc bump another counter when it got
|
|
||||||
* the status of a process. Waitproc would then know that a wait
|
|
||||||
* system call would not block if the two counters were different.
|
|
||||||
* This approach doesn't work because if a process has children that
|
|
||||||
* have not been waited for, System V will send it a SIGCLD when it
|
|
||||||
* installs a signal handler for SIGCLD. What this means is that when
|
|
||||||
* a child exits, the shell will be sent SIGCLD signals continuously
|
|
||||||
* until is runs out of stack space, unless it does a wait call before
|
|
||||||
* restoring the signal handler. The code below takes advantage of
|
|
||||||
* this (mis)feature by installing a signal handler for SIGCLD and
|
|
||||||
* then checking to see whether it was called. If there are any
|
|
||||||
* children to be waited for, it will be.
|
|
||||||
*
|
|
||||||
* If neither SYSV nor BSD is defined, we don't implement nonblocking
|
|
||||||
* waits at all. In this case, the user will not be informed when
|
|
||||||
* a background process until the next time she runs a real program
|
|
||||||
* (as opposed to running a builtin command or just typing return),
|
|
||||||
* and the jobs command may give out of date information.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
waitproc(int wait_flags, int *status)
|
|
||||||
{
|
|
||||||
#if JOBS
|
|
||||||
if (doing_jobctl)
|
|
||||||
wait_flags |= WUNTRACED;
|
|
||||||
#endif
|
|
||||||
/* NB: _not_ safe_waitpid, we need to detect EINTR */
|
|
||||||
return waitpid(-1, status, wait_flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wait for a process to terminate.
|
|
||||||
*/
|
|
||||||
static int
|
static int
|
||||||
dowait(int wait_flags, struct job *job)
|
dowait(int wait_flags, struct job *job)
|
||||||
{
|
{
|
||||||
@ -3813,9 +3771,15 @@ dowait(int wait_flags, struct job *job)
|
|||||||
struct job *thisjob;
|
struct job *thisjob;
|
||||||
int state;
|
int state;
|
||||||
|
|
||||||
TRACE(("dowait(%d) called\n", wait_flags));
|
TRACE(("dowait(0x%x) called\n", wait_flags));
|
||||||
pid = waitproc(wait_flags, &status);
|
|
||||||
TRACE(("wait returns pid=%d, status=%d\n", pid, status));
|
/* Do a wait system call. If job control is compiled in, we accept
|
||||||
|
* stopped processes. wait_flags may have WNOHANG, preventing blocking.
|
||||||
|
* NB: _not_ safe_waitpid, we need to detect EINTR */
|
||||||
|
pid = waitpid(-1, &status,
|
||||||
|
(doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
|
||||||
|
TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
|
||||||
|
|
||||||
if (pid <= 0) {
|
if (pid <= 0) {
|
||||||
/* If we were doing blocking wait and (probably) got EINTR,
|
/* If we were doing blocking wait and (probably) got EINTR,
|
||||||
* check for pending sigs received while waiting.
|
* check for pending sigs received while waiting.
|
||||||
@ -8923,7 +8887,10 @@ evalcommand(union node *cmd, int flags)
|
|||||||
/* Execute the command. */
|
/* Execute the command. */
|
||||||
switch (cmdentry.cmdtype) {
|
switch (cmdentry.cmdtype) {
|
||||||
default:
|
default:
|
||||||
|
|
||||||
#if ENABLE_FEATURE_SH_NOFORK
|
#if ENABLE_FEATURE_SH_NOFORK
|
||||||
|
/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
|
||||||
|
* Why "fork off a child process if necessary" doesn't apply to NOFORK? */
|
||||||
{
|
{
|
||||||
/* find_command() encodes applet_no as (-2 - applet_no) */
|
/* find_command() encodes applet_no as (-2 - applet_no) */
|
||||||
int applet_no = (- cmdentry.u.index - 2);
|
int applet_no = (- cmdentry.u.index - 2);
|
||||||
@ -8935,7 +8902,6 @@ evalcommand(union node *cmd, int flags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Fork off a child process if necessary. */
|
/* Fork off a child process if necessary. */
|
||||||
if (!(flags & EV_EXIT) || trap[0]) {
|
if (!(flags & EV_EXIT) || trap[0]) {
|
||||||
INT_OFF;
|
INT_OFF;
|
||||||
@ -8963,6 +8929,12 @@ evalcommand(union node *cmd, int flags)
|
|||||||
}
|
}
|
||||||
listsetvar(list, i);
|
listsetvar(list, i);
|
||||||
}
|
}
|
||||||
|
/* 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)) {
|
if (evalbltin(cmdentry.u.cmd, argc, argv)) {
|
||||||
int exit_status;
|
int exit_status;
|
||||||
int i = exception;
|
int i = exception;
|
||||||
@ -8984,6 +8956,8 @@ evalcommand(union node *cmd, int flags)
|
|||||||
|
|
||||||
case CMDFUNCTION:
|
case CMDFUNCTION:
|
||||||
listsetvar(varlist.list, 0);
|
listsetvar(varlist.list, 0);
|
||||||
|
/* See above for the rationale */
|
||||||
|
dowait(DOWAIT_NONBLOCK, NULL);
|
||||||
if (evalfun(cmdentry.u.func, argc, argv, flags))
|
if (evalfun(cmdentry.u.func, argc, argv, flags))
|
||||||
goto raise;
|
goto raise;
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user