aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-10-27 14:25:52 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-10-27 14:25:52 +0000
commitbe54d6bc60e611bdc6cc4f962328ba9c7f2ef840 (patch)
tree93113dd691c86722605ceb0296269382231cffb8
parent7ff85c53f17cbd6e3696ece978910f33f74901fe (diff)
downloadbusybox-w32-be54d6bc60e611bdc6cc4f962328ba9c7f2ef840.tar.gz
busybox-w32-be54d6bc60e611bdc6cc4f962328ba9c7f2ef840.tar.bz2
busybox-w32-be54d6bc60e611bdc6cc4f962328ba9c7f2ef840.zip
ash: fix "while kill -0 $child; do true; done" looping forever.
-rw-r--r--shell/ash.c66
1 files changed, 20 insertions, 46 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 81ac563fb..92aa5ecc0 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -3762,48 +3762,6 @@ sprint_status(char *s, int status, int sigonly)
3762 return col; 3762 return col;
3763} 3763}
3764 3764
3765/*
3766 * Do a wait system call. If job control is compiled in, we accept
3767 * stopped processes. If block is zero, we return a value of zero
3768 * rather than blocking.
3769 *
3770 * System V doesn't have a non-blocking wait system call. It does
3771 * have a SIGCLD signal that is sent to a process when one of it's
3772 * children dies. The obvious way to use SIGCLD would be to install
3773 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
3774 * was received, and have waitproc bump another counter when it got
3775 * the status of a process. Waitproc would then know that a wait
3776 * system call would not block if the two counters were different.
3777 * This approach doesn't work because if a process has children that
3778 * have not been waited for, System V will send it a SIGCLD when it
3779 * installs a signal handler for SIGCLD. What this means is that when
3780 * a child exits, the shell will be sent SIGCLD signals continuously
3781 * until is runs out of stack space, unless it does a wait call before
3782 * restoring the signal handler. The code below takes advantage of
3783 * this (mis)feature by installing a signal handler for SIGCLD and
3784 * then checking to see whether it was called. If there are any
3785 * children to be waited for, it will be.
3786 *
3787 * If neither SYSV nor BSD is defined, we don't implement nonblocking
3788 * waits at all. In this case, the user will not be informed when
3789 * a background process until the next time she runs a real program
3790 * (as opposed to running a builtin command or just typing return),
3791 * and the jobs command may give out of date information.
3792 */
3793static int
3794waitproc(int wait_flags, int *status)
3795{
3796#if JOBS
3797 if (doing_jobctl)
3798 wait_flags |= WUNTRACED;
3799#endif
3800 /* NB: _not_ safe_waitpid, we need to detect EINTR */
3801 return waitpid(-1, status, wait_flags);
3802}
3803
3804/*
3805 * Wait for a process to terminate.
3806 */
3807static int 3765static int
3808dowait(int wait_flags, struct job *job) 3766dowait(int wait_flags, struct job *job)
3809{ 3767{
@@ -3813,9 +3771,15 @@ dowait(int wait_flags, struct job *job)
3813 struct job *thisjob; 3771 struct job *thisjob;
3814 int state; 3772 int state;
3815 3773
3816 TRACE(("dowait(%d) called\n", wait_flags)); 3774 TRACE(("dowait(0x%x) called\n", wait_flags));
3817 pid = waitproc(wait_flags, &status); 3775
3818 TRACE(("wait returns pid=%d, status=%d\n", pid, status)); 3776 /* Do a wait system call. If job control is compiled in, we accept
3777 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
3778 * NB: _not_ safe_waitpid, we need to detect EINTR */
3779 pid = waitpid(-1, &status,
3780 (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags));
3781 TRACE(("wait returns pid=%d, status=0x%x\n", pid, status));
3782
3819 if (pid <= 0) { 3783 if (pid <= 0) {
3820 /* If we were doing blocking wait and (probably) got EINTR, 3784 /* If we were doing blocking wait and (probably) got EINTR,
3821 * check for pending sigs received while waiting. 3785 * check for pending sigs received while waiting.
@@ -8923,7 +8887,10 @@ evalcommand(union node *cmd, int flags)
8923 /* Execute the command. */ 8887 /* Execute the command. */
8924 switch (cmdentry.cmdtype) { 8888 switch (cmdentry.cmdtype) {
8925 default: 8889 default:
8890
8926#if ENABLE_FEATURE_SH_NOFORK 8891#if ENABLE_FEATURE_SH_NOFORK
8892/* Hmmm... shouldn't it happen somewhere in forkshell() instead?
8893 * Why "fork off a child process if necessary" doesn't apply to NOFORK? */
8927 { 8894 {
8928 /* find_command() encodes applet_no as (-2 - applet_no) */ 8895 /* find_command() encodes applet_no as (-2 - applet_no) */
8929 int applet_no = (- cmdentry.u.index - 2); 8896 int applet_no = (- cmdentry.u.index - 2);
@@ -8935,7 +8902,6 @@ evalcommand(union node *cmd, int flags)
8935 } 8902 }
8936 } 8903 }
8937#endif 8904#endif
8938
8939 /* Fork off a child process if necessary. */ 8905 /* Fork off a child process if necessary. */
8940 if (!(flags & EV_EXIT) || trap[0]) { 8906 if (!(flags & EV_EXIT) || trap[0]) {
8941 INT_OFF; 8907 INT_OFF;
@@ -8963,6 +8929,12 @@ evalcommand(union node *cmd, int flags)
8963 } 8929 }
8964 listsetvar(list, i); 8930 listsetvar(list, i);
8965 } 8931 }
8932 /* Tight loop with builtins only:
8933 * "while kill -0 $child; do true; done"
8934 * will never exit even if $child died, unless we do this
8935 * to reap the zombie and make kill detect that it's gone: */
8936 dowait(DOWAIT_NONBLOCK, NULL);
8937
8966 if (evalbltin(cmdentry.u.cmd, argc, argv)) { 8938 if (evalbltin(cmdentry.u.cmd, argc, argv)) {
8967 int exit_status; 8939 int exit_status;
8968 int i = exception; 8940 int i = exception;
@@ -8984,6 +8956,8 @@ evalcommand(union node *cmd, int flags)
8984 8956
8985 case CMDFUNCTION: 8957 case CMDFUNCTION:
8986 listsetvar(varlist.list, 0); 8958 listsetvar(varlist.list, 0);
8959 /* See above for the rationale */
8960 dowait(DOWAIT_NONBLOCK, NULL);
8987 if (evalfun(cmdentry.u.func, argc, argv, flags)) 8961 if (evalfun(cmdentry.u.func, argc, argv, flags))
8988 goto raise; 8962 goto raise;
8989 break; 8963 break;