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: |