diff options
| author | Ron Yorston <rmy@pobox.com> | 2020-02-20 12:28:31 +0000 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2020-02-20 12:28:31 +0000 |
| commit | eee533be9b75c2f5f92f37d7516e12cf9e4992be (patch) | |
| tree | 90dbb287476e63907711acc6060df5f9d6cf6053 /shell | |
| parent | 412c2cab62dcae5509404b48d83aa10717d3b68e (diff) | |
| parent | 23bc562a0556d1c0ddad4252fa8d46c863b5fa88 (diff) | |
| download | busybox-w32-eee533be9b75c2f5f92f37d7516e12cf9e4992be.tar.gz busybox-w32-eee533be9b75c2f5f92f37d7516e12cf9e4992be.tar.bz2 busybox-w32-eee533be9b75c2f5f92f37d7516e12cf9e4992be.zip | |
Merge branch 'busybox' into merge
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash.c | 177 | ||||
| -rw-r--r-- | shell/hush.c | 20 |
2 files changed, 106 insertions, 91 deletions
diff --git a/shell/ash.c b/shell/ash.c index 4c143b8e9..39b14d5a6 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
| @@ -4181,8 +4181,6 @@ static struct job *jobtab; //5 | |||
| 4181 | static unsigned njobs; //4 | 4181 | static unsigned njobs; //4 |
| 4182 | /* current job */ | 4182 | /* current job */ |
| 4183 | static struct job *curjob; //lots | 4183 | static struct job *curjob; //lots |
| 4184 | /* number of presumed living untracked jobs */ | ||
| 4185 | static int jobless; //4 | ||
| 4186 | 4184 | ||
| 4187 | #if 0 | 4185 | #if 0 |
| 4188 | /* Bash has a feature: it restores termios after a successful wait for | 4186 | /* Bash has a feature: it restores termios after a successful wait for |
| @@ -4796,8 +4794,19 @@ wait_block_or_sig(int *status) | |||
| 4796 | #if 1 | 4794 | #if 1 |
| 4797 | sigfillset(&mask); | 4795 | sigfillset(&mask); |
| 4798 | sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */ | 4796 | sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */ |
| 4799 | while (!got_sigchld && !pending_sig) | 4797 | while (!got_sigchld && !pending_sig) { |
| 4800 | sigsuspend(&mask); | 4798 | sigsuspend(&mask); |
| 4799 | /* ^^^ add "sigdelset(&mask, SIGCHLD);" before sigsuspend | ||
| 4800 | * to make sure SIGCHLD is not masked off? | ||
| 4801 | * It was reported that this: | ||
| 4802 | * fn() { : | return; } | ||
| 4803 | * shopt -s lastpipe | ||
| 4804 | * fn | ||
| 4805 | * exec ash SCRIPT | ||
| 4806 | * under bash 4.4.23 runs SCRIPT with SIGCHLD masked, | ||
| 4807 | * making "wait" commands in SCRIPT block forever. | ||
| 4808 | */ | ||
| 4809 | } | ||
| 4801 | sigprocmask(SIG_SETMASK, &mask, NULL); | 4810 | sigprocmask(SIG_SETMASK, &mask, NULL); |
| 4802 | #else /* unsafe: a signal can set pending_sig after check, but before pause() */ | 4811 | #else /* unsafe: a signal can set pending_sig after check, but before pause() */ |
| 4803 | while (!got_sigchld && !pending_sig) | 4812 | while (!got_sigchld && !pending_sig) |
| @@ -4819,7 +4828,7 @@ wait_block_or_sig(int *status) | |||
| 4819 | #endif | 4828 | #endif |
| 4820 | 4829 | ||
| 4821 | static int | 4830 | static int |
| 4822 | dowait(int block, struct job *job) | 4831 | waitone(int block, struct job *job) |
| 4823 | { | 4832 | { |
| 4824 | int pid; | 4833 | int pid; |
| 4825 | int status; | 4834 | int status; |
| @@ -4925,10 +4934,6 @@ dowait(int block, struct job *job) | |||
| 4925 | goto out; | 4934 | goto out; |
| 4926 | } | 4935 | } |
| 4927 | /* The process wasn't found in job list */ | 4936 | /* The process wasn't found in job list */ |
| 4928 | #if JOBS | ||
| 4929 | if (!WIFSTOPPED(status)) | ||
| 4930 | jobless--; | ||
| 4931 | #endif | ||
| 4932 | out: | 4937 | out: |
| 4933 | INT_ON; | 4938 | INT_ON; |
| 4934 | 4939 | ||
| @@ -4953,6 +4958,20 @@ dowait(int block, struct job *job) | |||
| 4953 | return pid; | 4958 | return pid; |
| 4954 | } | 4959 | } |
| 4955 | 4960 | ||
| 4961 | static int | ||
| 4962 | dowait(int block, struct job *jp) | ||
| 4963 | { | ||
| 4964 | int pid = block == DOWAIT_NONBLOCK ? got_sigchld : 1; | ||
| 4965 | |||
| 4966 | while (jp ? jp->state == JOBRUNNING : pid > 0) { | ||
| 4967 | if (!jp) | ||
| 4968 | got_sigchld = 0; | ||
| 4969 | pid = waitone(block, jp); | ||
| 4970 | } | ||
| 4971 | |||
| 4972 | return pid; | ||
| 4973 | } | ||
| 4974 | |||
| 4956 | #if JOBS | 4975 | #if JOBS |
| 4957 | static void | 4976 | static void |
| 4958 | showjob(struct job *jp, int mode) | 4977 | showjob(struct job *jp, int mode) |
| @@ -5041,8 +5060,7 @@ showjobs(int mode) | |||
| 5041 | TRACE(("showjobs(0x%x) called\n", mode)); | 5060 | TRACE(("showjobs(0x%x) called\n", mode)); |
| 5042 | 5061 | ||
| 5043 | /* Handle all finished jobs */ | 5062 | /* Handle all finished jobs */ |
| 5044 | while (dowait(DOWAIT_NONBLOCK, NULL) > 0) | 5063 | dowait(DOWAIT_NONBLOCK, NULL); |
| 5045 | continue; | ||
| 5046 | 5064 | ||
| 5047 | for (jp = curjob; jp; jp = jp->prev_job) { | 5065 | for (jp = curjob; jp; jp = jp->prev_job) { |
| 5048 | if (!(mode & SHOW_CHANGED) || jp->changed) { | 5066 | if (!(mode & SHOW_CHANGED) || jp->changed) { |
| @@ -5159,10 +5177,10 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
| 5159 | #else | 5177 | #else |
| 5160 | dowait(DOWAIT_BLOCK_OR_SIG, NULL); | 5178 | dowait(DOWAIT_BLOCK_OR_SIG, NULL); |
| 5161 | #endif | 5179 | #endif |
| 5162 | /* if child sends us a signal *and immediately exits*, | 5180 | /* if child sends us a signal *and immediately exits*, |
| 5163 | * dowait() returns pid > 0. Check this case, | 5181 | * dowait() returns pid > 0. Check this case, |
| 5164 | * not "if (dowait() < 0)"! | 5182 | * not "if (dowait() < 0)"! |
| 5165 | */ | 5183 | */ |
| 5166 | if (pending_sig) | 5184 | if (pending_sig) |
| 5167 | goto sigout; | 5185 | goto sigout; |
| 5168 | #if BASH_WAIT_N | 5186 | #if BASH_WAIT_N |
| @@ -5198,11 +5216,9 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
| 5198 | job = getjob(*argv, 0); | 5216 | job = getjob(*argv, 0); |
| 5199 | } | 5217 | } |
| 5200 | /* loop until process terminated or stopped */ | 5218 | /* loop until process terminated or stopped */ |
| 5201 | while (job->state == JOBRUNNING) { | 5219 | dowait(DOWAIT_BLOCK_OR_SIG, NULL); |
| 5202 | dowait(DOWAIT_BLOCK_OR_SIG, NULL); | 5220 | if (pending_sig) |
| 5203 | if (pending_sig) | 5221 | goto sigout; |
| 5204 | goto sigout; | ||
| 5205 | } | ||
| 5206 | job->waited = 1; | 5222 | job->waited = 1; |
| 5207 | retval = getstatus(job); | 5223 | retval = getstatus(job); |
| 5208 | repeat: ; | 5224 | repeat: ; |
| @@ -5755,7 +5771,6 @@ forkchild(struct job *jp, union node *n, int mode) | |||
| 5755 | #endif | 5771 | #endif |
| 5756 | for (jp = curjob; jp; jp = jp->prev_job) | 5772 | for (jp = curjob; jp; jp = jp->prev_job) |
| 5757 | freejob(jp); | 5773 | freejob(jp); |
| 5758 | jobless = 0; | ||
| 5759 | } | 5774 | } |
| 5760 | #endif | 5775 | #endif |
| 5761 | 5776 | ||
| @@ -5774,13 +5789,8 @@ forkparent(struct job *jp, union node *n, int mode, HANDLE proc) | |||
| 5774 | pid_t pid = GetProcessId(proc); | 5789 | pid_t pid = GetProcessId(proc); |
| 5775 | #endif | 5790 | #endif |
| 5776 | TRACE(("In parent shell: child = %d\n", pid)); | 5791 | TRACE(("In parent shell: child = %d\n", pid)); |
| 5777 | if (!jp && !ENABLE_PLATFORM_MINGW32) { /* FIXME not quite understand this */ | 5792 | if (!jp) /* jp is NULL when called by openhere() for heredoc support */ |
| 5778 | /* jp is NULL when called by openhere() for heredoc support */ | ||
| 5779 | while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) | ||
| 5780 | continue; | ||
| 5781 | jobless++; | ||
| 5782 | return; | 5793 | return; |
| 5783 | } | ||
| 5784 | #if JOBS | 5794 | #if JOBS |
| 5785 | if (mode != FORK_NOJOB && jp->jobctl) { | 5795 | if (mode != FORK_NOJOB && jp->jobctl) { |
| 5786 | int pgrp; | 5796 | int pgrp; |
| @@ -5862,43 +5872,41 @@ waitforjob(struct job *jp) | |||
| 5862 | { | 5872 | { |
| 5863 | int st; | 5873 | int st; |
| 5864 | 5874 | ||
| 5865 | TRACE(("waitforjob(%%%d) called\n", jobno(jp))); | 5875 | TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0)); |
| 5866 | 5876 | ||
| 5867 | INT_OFF; | 5877 | /* In non-interactive shells, we _can_ get |
| 5868 | while (jp->state == JOBRUNNING) { | 5878 | * a keyboard signal here and be EINTRed, but we just loop |
| 5869 | /* In non-interactive shells, we _can_ get | 5879 | * inside dowait(), waiting for command to complete. |
| 5870 | * a keyboard signal here and be EINTRed, | 5880 | * |
| 5871 | * but we just loop back, waiting for command to complete. | 5881 | * man bash: |
| 5872 | * | 5882 | * "If bash is waiting for a command to complete and receives |
| 5873 | * man bash: | 5883 | * a signal for which a trap has been set, the trap |
| 5874 | * "If bash is waiting for a command to complete and receives | 5884 | * will not be executed until the command completes." |
| 5875 | * a signal for which a trap has been set, the trap | 5885 | * |
| 5876 | * will not be executed until the command completes." | 5886 | * Reality is that even if trap is not set, bash |
| 5877 | * | 5887 | * will not act on the signal until command completes. |
| 5878 | * Reality is that even if trap is not set, bash | 5888 | * Try this. sleep5intoff.c: |
| 5879 | * will not act on the signal until command completes. | 5889 | * #include <signal.h> |
| 5880 | * Try this. sleep5intoff.c: | 5890 | * #include <unistd.h> |
| 5881 | * #include <signal.h> | 5891 | * int main() { |
| 5882 | * #include <unistd.h> | 5892 | * sigset_t set; |
| 5883 | * int main() { | 5893 | * sigemptyset(&set); |
| 5884 | * sigset_t set; | 5894 | * sigaddset(&set, SIGINT); |
| 5885 | * sigemptyset(&set); | 5895 | * sigaddset(&set, SIGQUIT); |
| 5886 | * sigaddset(&set, SIGINT); | 5896 | * sigprocmask(SIG_BLOCK, &set, NULL); |
| 5887 | * sigaddset(&set, SIGQUIT); | 5897 | * sleep(5); |
| 5888 | * sigprocmask(SIG_BLOCK, &set, NULL); | 5898 | * return 0; |
| 5889 | * sleep(5); | 5899 | * } |
| 5890 | * return 0; | 5900 | * $ bash -c './sleep5intoff; echo hi' |
| 5891 | * } | 5901 | * ^C^C^C^C <--- pressing ^C once a second |
| 5892 | * $ bash -c './sleep5intoff; echo hi' | 5902 | * $ _ |
| 5893 | * ^C^C^C^C <--- pressing ^C once a second | 5903 | * $ bash -c './sleep5intoff; echo hi' |
| 5894 | * $ _ | 5904 | * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT) |
| 5895 | * $ bash -c './sleep5intoff; echo hi' | 5905 | * $ _ |
| 5896 | * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT) | 5906 | */ |
| 5897 | * $ _ | 5907 | dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp); |
| 5898 | */ | 5908 | if (!jp) |
| 5899 | dowait(DOWAIT_BLOCK, jp); | 5909 | return exitstatus; |
| 5900 | } | ||
| 5901 | INT_ON; | ||
| 5902 | 5910 | ||
| 5903 | st = getstatus(jp); | 5911 | st = getstatus(jp); |
| 5904 | #if JOBS | 5912 | #if JOBS |
| @@ -10991,6 +10999,8 @@ evalcommand(union node *cmd, int flags) | |||
| 10991 | goto out; | 10999 | goto out; |
| 10992 | } | 11000 | } |
| 10993 | 11001 | ||
| 11002 | jp = NULL; | ||
| 11003 | |||
| 10994 | /* Execute the command. */ | 11004 | /* Execute the command. */ |
| 10995 | switch (cmdentry.cmdtype) { | 11005 | switch (cmdentry.cmdtype) { |
| 10996 | default: { | 11006 | default: { |
| @@ -11052,8 +11062,6 @@ evalcommand(union node *cmd, int flags) | |||
| 11052 | fs.varlist = varlist.list; | 11062 | fs.varlist = varlist.list; |
| 11053 | jp = makejob(/*cmd,*/ 1); | 11063 | jp = makejob(/*cmd,*/ 1); |
| 11054 | spawn_forkshell(&fs, jp, cmd, FORK_FG); | 11064 | spawn_forkshell(&fs, jp, cmd, FORK_FG); |
| 11055 | status = waitforjob(jp); | ||
| 11056 | INT_ON; | ||
| 11057 | TRACE(("forked child exited with %d\n", status)); | 11065 | TRACE(("forked child exited with %d\n", status)); |
| 11058 | break; | 11066 | break; |
| 11059 | } | 11067 | } |
| @@ -11072,8 +11080,6 @@ evalcommand(union node *cmd, int flags) | |||
| 11072 | jp = makejob(/*cmd,*/ 1); | 11080 | jp = makejob(/*cmd,*/ 1); |
| 11073 | if (forkshell(jp, cmd, FORK_FG) != 0) { | 11081 | if (forkshell(jp, cmd, FORK_FG) != 0) { |
| 11074 | /* parent */ | 11082 | /* parent */ |
| 11075 | status = waitforjob(jp); | ||
| 11076 | INT_ON; | ||
| 11077 | TRACE(("forked child exited with %d\n", status)); | 11083 | TRACE(("forked child exited with %d\n", status)); |
| 11078 | break; | 11084 | break; |
| 11079 | } | 11085 | } |
| @@ -11092,33 +11098,23 @@ evalcommand(union node *cmd, int flags) | |||
| 11092 | if (cmd_is_exec && argc > 1) | 11098 | if (cmd_is_exec && argc > 1) |
| 11093 | listsetvar(varlist.list, VEXPORT); | 11099 | listsetvar(varlist.list, VEXPORT); |
| 11094 | } | 11100 | } |
| 11095 | 11101 | if (evalbltin(cmdentry.u.cmd, argc, argv, flags) | |
| 11096 | /* Tight loop with builtins only: | 11102 | && !(exception_type == EXERROR && spclbltin <= 0) |
| 11097 | * "while kill -0 $child; do true; done" | 11103 | ) { |
| 11098 | * will never exit even if $child died, unless we do this | ||
| 11099 | * to reap the zombie and make kill detect that it's gone: */ | ||
| 11100 | dowait(DOWAIT_NONBLOCK, NULL); | ||
| 11101 | |||
| 11102 | if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { | ||
| 11103 | if (exception_type == EXERROR && spclbltin <= 0) { | ||
| 11104 | FORCE_INT_ON; | ||
| 11105 | goto readstatus; | ||
| 11106 | } | ||
| 11107 | raise: | 11104 | raise: |
| 11108 | longjmp(exception_handler->loc, 1); | 11105 | longjmp(exception_handler->loc, 1); |
| 11109 | } | 11106 | } |
| 11110 | goto readstatus; | 11107 | break; |
| 11111 | 11108 | ||
| 11112 | case CMDFUNCTION: | 11109 | case CMDFUNCTION: |
| 11113 | /* See above for the rationale */ | ||
| 11114 | dowait(DOWAIT_NONBLOCK, NULL); | ||
| 11115 | if (evalfun(cmdentry.u.func, argc, argv, flags)) | 11110 | if (evalfun(cmdentry.u.func, argc, argv, flags)) |
| 11116 | goto raise; | 11111 | goto raise; |
| 11117 | readstatus: | ||
| 11118 | status = exitstatus; | ||
| 11119 | break; | 11112 | break; |
| 11120 | } /* switch */ | 11113 | } /* switch */ |
| 11121 | 11114 | ||
| 11115 | status = waitforjob(jp); | ||
| 11116 | FORCE_INT_ON; | ||
| 11117 | |||
| 11122 | out: | 11118 | out: |
| 11123 | if (cmd->ncmd.redirect) | 11119 | if (cmd->ncmd.redirect) |
| 11124 | popredir(/*drop:*/ cmd_is_exec); | 11120 | popredir(/*drop:*/ cmd_is_exec); |
| @@ -14956,11 +14952,6 @@ init(void) | |||
| 14956 | #if !ENABLE_PLATFORM_MINGW32 | 14952 | #if !ENABLE_PLATFORM_MINGW32 |
| 14957 | sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ | 14953 | sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ |
| 14958 | setsignal(SIGCHLD); | 14954 | setsignal(SIGCHLD); |
| 14959 | |||
| 14960 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. | ||
| 14961 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" | ||
| 14962 | */ | ||
| 14963 | signal(SIGHUP, SIG_DFL); | ||
| 14964 | #endif | 14955 | #endif |
| 14965 | 14956 | ||
| 14966 | { | 14957 | { |
| @@ -15448,6 +15439,16 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
| 15448 | } | 15439 | } |
| 15449 | #endif | 15440 | #endif |
| 15450 | state4: /* XXX ??? - why isn't this before the "if" statement */ | 15441 | state4: /* XXX ??? - why isn't this before the "if" statement */ |
| 15442 | |||
| 15443 | /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry. | ||
| 15444 | * Try: | ||
| 15445 | * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect | ||
| 15446 | * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed | ||
| 15447 | */ | ||
| 15448 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 15449 | signal(SIGHUP, SIG_DFL); | ||
| 15450 | #endif | ||
| 15451 | |||
| 15451 | cmdloop(1); | 15452 | cmdloop(1); |
| 15452 | } | 15453 | } |
| 15453 | #if PROFILE | 15454 | #if PROFILE |
diff --git a/shell/hush.c b/shell/hush.c index 6e44d4e11..bced388bf 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
| @@ -9775,10 +9775,14 @@ static void install_sighandlers(unsigned mask) | |||
| 9775 | */ | 9775 | */ |
| 9776 | if (sig == SIGCHLD) | 9776 | if (sig == SIGCHLD) |
| 9777 | continue; | 9777 | continue; |
| 9778 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. | 9778 | /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry. |
| 9779 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" | 9779 | * Try: |
| 9780 | * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect | ||
| 9781 | * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed | ||
| 9780 | */ | 9782 | */ |
| 9781 | //if (sig == SIGHUP) continue; - TODO? | 9783 | if (sig == SIGHUP && G_interactive_fd) |
| 9784 | continue; | ||
| 9785 | /* Unless one of the above signals, is it SIG_IGN? */ | ||
| 9782 | if (old_handler == SIG_IGN) { | 9786 | if (old_handler == SIG_IGN) { |
| 9783 | /* oops... restore back to IGN, and record this fact */ | 9787 | /* oops... restore back to IGN, and record this fact */ |
| 9784 | install_sighandler(sig, old_handler); | 9788 | install_sighandler(sig, old_handler); |
| @@ -11554,6 +11558,16 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
| 11554 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ | 11558 | /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */ |
| 11555 | /* Note: sigsuspend invokes signal handler */ | 11559 | /* Note: sigsuspend invokes signal handler */ |
| 11556 | sigsuspend(&oldset); | 11560 | sigsuspend(&oldset); |
| 11561 | /* ^^^ add "sigdelset(&oldset, SIGCHLD)" before sigsuspend | ||
| 11562 | * to make sure SIGCHLD is not masked off? | ||
| 11563 | * It was reported that this: | ||
| 11564 | * fn() { : | return; } | ||
| 11565 | * shopt -s lastpipe | ||
| 11566 | * fn | ||
| 11567 | * exec hush SCRIPT | ||
| 11568 | * under bash 4.4.23 runs SCRIPT with SIGCHLD masked, | ||
| 11569 | * making "wait" commands in SCRIPT block forever. | ||
| 11570 | */ | ||
| 11557 | restore: | 11571 | restore: |
| 11558 | sigprocmask(SIG_SETMASK, &oldset, NULL); | 11572 | sigprocmask(SIG_SETMASK, &oldset, NULL); |
| 11559 | check_sig: | 11573 | check_sig: |
