diff options
Diffstat (limited to 'shell/ash.c')
-rw-r--r-- | shell/ash.c | 145 |
1 files changed, 70 insertions, 75 deletions
diff --git a/shell/ash.c b/shell/ash.c index 1f57c2d7e..b8f75af05 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -240,17 +240,17 @@ | |||
240 | #define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT | 240 | #define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT |
241 | /* BASH_TEST2: [[ EXPR ]] | 241 | /* BASH_TEST2: [[ EXPR ]] |
242 | * Status of [[ support: | 242 | * Status of [[ support: |
243 | * We replace && and || with -a and -o | 243 | * && and || work as they should |
244 | * = is glob match operator, not equality operator: STR = GLOB | ||
245 | * == same as = | ||
246 | * =~ is regex match operator: STR =~ REGEX | ||
244 | * TODO: | 247 | * TODO: |
245 | * singleword+noglob expansion: | 248 | * singleword+noglob expansion: |
246 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? | 249 | * v='a b'; [[ $v = 'a b' ]]; echo 0:$? |
247 | * [[ /bin/n* ]]; echo 0:$? | 250 | * [[ /bin/n* ]]; echo 0:$? |
248 | * -a/-o are not AND/OR ops! (they are just strings) | ||
249 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) | 251 | * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc) |
250 | * = is glob match operator, not equality operator: STR = GLOB | 252 | * ( ) < > should not have special meaning (IOW: should not require quoting) |
251 | * (in GLOB, quoting is significant on char-by-char basis: a*cd"*") | 253 | * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*" |
252 | * == same as = | ||
253 | * add =~ regex match operator: STR =~ REGEX | ||
254 | */ | 254 | */ |
255 | #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) | 255 | #define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST) |
256 | #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT | 256 | #define BASH_SOURCE ENABLE_ASH_BASH_COMPAT |
@@ -3173,7 +3173,7 @@ updatepwd(const char *dir) | |||
3173 | lim++; | 3173 | lim++; |
3174 | } | 3174 | } |
3175 | } | 3175 | } |
3176 | p = strtok(cdcomppath, "/"); | 3176 | p = strtok_r(cdcomppath, "/", &cdcomppath); |
3177 | while (p) { | 3177 | while (p) { |
3178 | switch (*p) { | 3178 | switch (*p) { |
3179 | case '.': | 3179 | case '.': |
@@ -3192,7 +3192,7 @@ updatepwd(const char *dir) | |||
3192 | new = stack_putstr(p, new); | 3192 | new = stack_putstr(p, new); |
3193 | USTPUTC('/', new); | 3193 | USTPUTC('/', new); |
3194 | } | 3194 | } |
3195 | p = strtok(NULL, "/"); | 3195 | p = strtok_r(NULL, "/", &cdcomppath); |
3196 | } | 3196 | } |
3197 | if (new > lim) | 3197 | if (new > lim) |
3198 | STUNPUTC(new); | 3198 | STUNPUTC(new); |
@@ -4780,61 +4780,54 @@ waitpid_child(int *status, int wait_flags) | |||
4780 | return pid; | 4780 | return pid; |
4781 | } | 4781 | } |
4782 | #define waitpid(p, s, f) waitpid_child(s, f) | 4782 | #define waitpid(p, s, f) waitpid_child(s, f) |
4783 | #define wait_block_or_sig(s) waitpid_child(s, 0) | 4783 | #endif |
4784 | 4784 | ||
4785 | #else | 4785 | #define DOWAIT_NONBLOCK 0 |
4786 | #define DOWAIT_BLOCK 1 | ||
4787 | #define DOWAIT_BLOCK_OR_SIG 2 | ||
4788 | #if BASH_WAIT_N | ||
4789 | # define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ | ||
4790 | #endif | ||
4786 | 4791 | ||
4787 | static int | 4792 | static int |
4788 | wait_block_or_sig(int *status) | 4793 | waitproc(int block, int *status) |
4789 | { | 4794 | { |
4790 | int pid; | 4795 | #if !ENABLE_PLATFORM_MINGW32 |
4796 | sigset_t oldmask; | ||
4797 | int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; | ||
4798 | int err; | ||
4791 | 4799 | ||
4792 | do { | 4800 | #if JOBS |
4793 | sigset_t mask; | 4801 | if (doing_jobctl) |
4802 | flags |= WUNTRACED; | ||
4803 | #endif | ||
4794 | 4804 | ||
4795 | /* Poll all children for changes in their state */ | 4805 | do { |
4796 | got_sigchld = 0; | 4806 | got_sigchld = 0; |
4797 | /* if job control is active, accept stopped processes too */ | 4807 | do |
4798 | pid = waitpid(-1, status, doing_jobctl ? (WNOHANG|WUNTRACED) : WNOHANG); | 4808 | err = waitpid(-1, status, flags); |
4799 | if (pid != 0) | 4809 | while (err < 0 && errno == EINTR); |
4800 | break; /* Error (e.g. EINTR, ECHILD) or pid */ | ||
4801 | 4810 | ||
4802 | /* Children exist, but none are ready. Sleep until interesting signal */ | 4811 | if (err || (err = -!block)) |
4803 | #if 1 | 4812 | break; |
4804 | sigfillset(&mask); | 4813 | |
4805 | sigprocmask2(SIG_SETMASK, &mask); /* mask is updated */ | 4814 | sigfillset(&oldmask); |
4806 | while (!got_sigchld && !pending_sig) { | 4815 | sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */ |
4807 | sigsuspend(&mask); | ||
4808 | /* ^^^ add "sigdelset(&mask, SIGCHLD);" before sigsuspend | ||
4809 | * to make sure SIGCHLD is not masked off? | ||
4810 | * It was reported that this: | ||
4811 | * fn() { : | return; } | ||
4812 | * shopt -s lastpipe | ||
4813 | * fn | ||
4814 | * exec ash SCRIPT | ||
4815 | * under bash 4.4.23 runs SCRIPT with SIGCHLD masked, | ||
4816 | * making "wait" commands in SCRIPT block forever. | ||
4817 | */ | ||
4818 | } | ||
4819 | sigprocmask(SIG_SETMASK, &mask, NULL); | ||
4820 | #else /* unsafe: a signal can set pending_sig after check, but before pause() */ | ||
4821 | while (!got_sigchld && !pending_sig) | 4816 | while (!got_sigchld && !pending_sig) |
4822 | pause(); | 4817 | sigsuspend(&oldmask); |
4823 | #endif | 4818 | sigprocmask(SIG_SETMASK, &oldmask, NULL); |
4819 | //simpler, but unsafe: a signal can set pending_sig after check, but before pause(): | ||
4820 | //while (!got_sigchld && !pending_sig) | ||
4821 | // pause(); | ||
4824 | 4822 | ||
4825 | /* If it was SIGCHLD, poll children again */ | ||
4826 | } while (got_sigchld); | 4823 | } while (got_sigchld); |
4827 | 4824 | ||
4828 | return pid; | 4825 | return err; |
4829 | } | 4826 | #else |
4830 | #endif | 4827 | int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; |
4831 | 4828 | return waitpid(-1, status, flags); | |
4832 | #define DOWAIT_NONBLOCK 0 | ||
4833 | #define DOWAIT_BLOCK 1 | ||
4834 | #define DOWAIT_BLOCK_OR_SIG 2 | ||
4835 | #if BASH_WAIT_N | ||
4836 | # define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */ | ||
4837 | #endif | 4829 | #endif |
4830 | } | ||
4838 | 4831 | ||
4839 | static int | 4832 | static int |
4840 | waitone(int block, struct job *job) | 4833 | waitone(int block, struct job *job) |
@@ -4842,7 +4835,7 @@ waitone(int block, struct job *job) | |||
4842 | int pid; | 4835 | int pid; |
4843 | int status; | 4836 | int status; |
4844 | struct job *jp; | 4837 | struct job *jp; |
4845 | struct job *thisjob; | 4838 | struct job *thisjob = NULL; |
4846 | #if BASH_WAIT_N | 4839 | #if BASH_WAIT_N |
4847 | bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS); | 4840 | bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS); |
4848 | block = (block & ~DOWAIT_JOBSTATUS); | 4841 | block = (block & ~DOWAIT_JOBSTATUS); |
@@ -4869,21 +4862,8 @@ waitone(int block, struct job *job) | |||
4869 | * SIG_DFL handler does not wake sigsuspend(). | 4862 | * SIG_DFL handler does not wake sigsuspend(). |
4870 | */ | 4863 | */ |
4871 | INT_OFF; | 4864 | INT_OFF; |
4872 | if (block == DOWAIT_BLOCK_OR_SIG) { | 4865 | pid = waitproc(block, &status); |
4873 | pid = wait_block_or_sig(&status); | 4866 | TRACE(("wait returns pid %d, status=%d\n", pid, status)); |
4874 | } else { | ||
4875 | int wait_flags = 0; | ||
4876 | if (block == DOWAIT_NONBLOCK) | ||
4877 | wait_flags = WNOHANG; | ||
4878 | /* if job control is active, accept stopped processes too */ | ||
4879 | if (doing_jobctl) | ||
4880 | wait_flags |= WUNTRACED; | ||
4881 | /* NB: _not_ safe_waitpid, we need to detect EINTR */ | ||
4882 | pid = waitpid(-1, &status, wait_flags); | ||
4883 | } | ||
4884 | TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", | ||
4885 | pid, status, errno, strerror(errno))); | ||
4886 | thisjob = NULL; | ||
4887 | if (pid <= 0) | 4867 | if (pid <= 0) |
4888 | goto out; | 4868 | goto out; |
4889 | 4869 | ||
@@ -4971,21 +4951,35 @@ static int | |||
4971 | dowait(int block, struct job *jp) | 4951 | dowait(int block, struct job *jp) |
4972 | { | 4952 | { |
4973 | #if !ENABLE_PLATFORM_MINGW32 | 4953 | #if !ENABLE_PLATFORM_MINGW32 |
4974 | int pid = block == DOWAIT_NONBLOCK ? got_sigchld : 1; | 4954 | smallint gotchld = *(volatile smallint *)&got_sigchld; |
4955 | int rpid; | ||
4956 | int pid; | ||
4957 | |||
4958 | if (jp && jp->state != JOBRUNNING) | ||
4959 | block = DOWAIT_NONBLOCK; | ||
4960 | |||
4961 | if (block == DOWAIT_NONBLOCK && !gotchld) | ||
4962 | return 1; | ||
4963 | |||
4964 | rpid = 1; | ||
4975 | 4965 | ||
4976 | while (jp ? jp->state == JOBRUNNING : pid > 0) { | 4966 | do { |
4977 | if (!jp) | ||
4978 | got_sigchld = 0; | ||
4979 | pid = waitone(block, jp); | 4967 | pid = waitone(block, jp); |
4980 | } | 4968 | rpid &= !!pid; |
4969 | |||
4970 | if (!pid || (jp && jp->state != JOBRUNNING)) | ||
4971 | block = DOWAIT_NONBLOCK; | ||
4972 | } while (pid >= 0); | ||
4973 | |||
4974 | return rpid; | ||
4981 | #else | 4975 | #else |
4982 | int pid = 1; | 4976 | int pid = 1; |
4983 | 4977 | ||
4984 | while (jp ? jp->state == JOBRUNNING : pid > 0) | 4978 | while (jp ? jp->state == JOBRUNNING : pid > 0) |
4985 | pid = waitone(block, jp); | 4979 | pid = waitone(block, jp); |
4986 | #endif | ||
4987 | 4980 | ||
4988 | return pid; | 4981 | return pid; |
4982 | #endif | ||
4989 | } | 4983 | } |
4990 | 4984 | ||
4991 | #if JOBS | 4985 | #if JOBS |
@@ -5232,7 +5226,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
5232 | job = getjob(*argv, 0); | 5226 | job = getjob(*argv, 0); |
5233 | } | 5227 | } |
5234 | /* loop until process terminated or stopped */ | 5228 | /* loop until process terminated or stopped */ |
5235 | dowait(DOWAIT_BLOCK_OR_SIG, NULL); | 5229 | dowait(DOWAIT_BLOCK_OR_SIG, job); |
5236 | if (pending_sig) | 5230 | if (pending_sig) |
5237 | goto sigout; | 5231 | goto sigout; |
5238 | job->waited = 1; | 5232 | job->waited = 1; |
@@ -12622,7 +12616,8 @@ simplecmd(void) | |||
12622 | tokpushback = 1; | 12616 | tokpushback = 1; |
12623 | goto out; | 12617 | goto out; |
12624 | } | 12618 | } |
12625 | wordtext = (char *) (t == TAND ? "-a" : "-o"); | 12619 | /* pass "&&" or "||" to [[ ]] as literal args */ |
12620 | wordtext = (char *) (t == TAND ? "&&" : "||"); | ||
12626 | #endif | 12621 | #endif |
12627 | case TWORD: | 12622 | case TWORD: |
12628 | n = stzalloc(sizeof(struct narg)); | 12623 | n = stzalloc(sizeof(struct narg)); |
@@ -13600,7 +13595,7 @@ parsebackq: { | |||
13600 | goto done; | 13595 | goto done; |
13601 | 13596 | ||
13602 | case '\\': | 13597 | case '\\': |
13603 | pc = pgetc(); /* or pgetc_eatbnl()? why (example)? */ | 13598 | pc = pgetc(); /* not pgetc_eatbnl! */ |
13604 | if (pc != '\\' && pc != '`' && pc != '$' | 13599 | if (pc != '\\' && pc != '`' && pc != '$' |
13605 | && (!synstack->dblquote || pc != '"') | 13600 | && (!synstack->dblquote || pc != '"') |
13606 | ) { | 13601 | ) { |