diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-10-27 14:25:52 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-10-27 14:25:52 +0000 |
commit | be54d6bc60e611bdc6cc4f962328ba9c7f2ef840 (patch) | |
tree | 93113dd691c86722605ceb0296269382231cffb8 | |
parent | 7ff85c53f17cbd6e3696ece978910f33f74901fe (diff) | |
download | busybox-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.c | 66 |
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 | */ | ||
3793 | static int | ||
3794 | waitproc(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 | */ | ||
3807 | static int | 3765 | static int |
3808 | dowait(int wait_flags, struct job *job) | 3766 | dowait(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; |