diff options
-rw-r--r-- | shell/hush.c | 98 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/syntax_err.right | 2 |
2 files changed, 42 insertions, 58 deletions
diff --git a/shell/hush.c b/shell/hush.c index c8bcf9b59..6734c9212 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -90,14 +90,6 @@ | |||
90 | #define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__ | 90 | #define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__ |
91 | #endif | 91 | #endif |
92 | 92 | ||
93 | #if !BB_MMU && ENABLE_HUSH_TICK | ||
94 | //#undef ENABLE_HUSH_TICK | ||
95 | //#define ENABLE_HUSH_TICK 0 | ||
96 | #warning On NOMMU, hush command substitution is dangerous. | ||
97 | #warning Dont use it for commands which produce lots of output. | ||
98 | #warning For more info see shell/hush.c, generate_stream_from_list(). | ||
99 | #endif | ||
100 | |||
101 | #if !ENABLE_HUSH_INTERACTIVE | 93 | #if !ENABLE_HUSH_INTERACTIVE |
102 | #undef ENABLE_FEATURE_EDITING | 94 | #undef ENABLE_FEATURE_EDITING |
103 | #define ENABLE_FEATURE_EDITING 0 | 95 | #define ENABLE_FEATURE_EDITING 0 |
@@ -1649,7 +1641,7 @@ static char **o_finalize_list(o_string *o, int n) | |||
1649 | 1641 | ||
1650 | /* Expansion can recurse */ | 1642 | /* Expansion can recurse */ |
1651 | #if ENABLE_HUSH_TICK | 1643 | #if ENABLE_HUSH_TICK |
1652 | static int process_command_subs(o_string *dest, struct in_str *input); | 1644 | static int process_command_subs(o_string *dest, const char *s); |
1653 | #endif | 1645 | #endif |
1654 | static char *expand_string_to_string(const char *str); | 1646 | static char *expand_string_to_string(const char *str); |
1655 | static int parse_stream_dquoted(o_string *dest, struct in_str *input, int dquote_end); | 1647 | static int parse_stream_dquoted(o_string *dest, struct in_str *input, int dquote_end); |
@@ -1798,18 +1790,15 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
1798 | ored_ch = 0x80; | 1790 | ored_ch = 0x80; |
1799 | break; | 1791 | break; |
1800 | #if ENABLE_HUSH_TICK | 1792 | #if ENABLE_HUSH_TICK |
1801 | case '`': { /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ | 1793 | case '`': /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */ |
1802 | struct in_str input; | ||
1803 | *p = '\0'; | 1794 | *p = '\0'; |
1804 | arg++; | 1795 | arg++; |
1805 | //TODO: can we just stuff it into "output" directly? | 1796 | //TODO: can we just stuff it into "output" directly? |
1806 | debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); | 1797 | debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch); |
1807 | setup_string_in_str(&input, arg); | 1798 | process_command_subs(&subst_result, arg); |
1808 | process_command_subs(&subst_result, &input); | ||
1809 | debug_printf_subst("SUBST RES '%s'\n", subst_result.data); | 1799 | debug_printf_subst("SUBST RES '%s'\n", subst_result.data); |
1810 | val = subst_result.data; | 1800 | val = subst_result.data; |
1811 | goto store_val; | 1801 | goto store_val; |
1812 | } | ||
1813 | #endif | 1802 | #endif |
1814 | #if ENABLE_SH_MATH_SUPPORT | 1803 | #if ENABLE_SH_MATH_SUPPORT |
1815 | case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ | 1804 | case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ |
@@ -3711,35 +3700,20 @@ static int redirect_opt_num(o_string *o) | |||
3711 | } | 3700 | } |
3712 | 3701 | ||
3713 | static struct pipe *parse_stream(struct in_str *input, int end_trigger); | 3702 | static struct pipe *parse_stream(struct in_str *input, int end_trigger); |
3703 | static void parse_and_run_string(const char *s); | ||
3714 | 3704 | ||
3715 | #if ENABLE_HUSH_TICK | 3705 | #if ENABLE_HUSH_TICK |
3716 | static FILE *generate_stream_from_list(struct pipe *head) | 3706 | static FILE *generate_stream_from_string(const char *s) |
3717 | { | 3707 | { |
3718 | FILE *pf; | 3708 | FILE *pf; |
3719 | int pid, channel[2]; | 3709 | int pid, channel[2]; |
3720 | 3710 | ||
3721 | xpipe(channel); | 3711 | xpipe(channel); |
3722 | /* *** NOMMU WARNING *** */ | ||
3723 | /* By using vfork here, we suspend parent till child exits or execs. | ||
3724 | * If child will not do it before it fills the pipe, it can block forever | ||
3725 | * in write(STDOUT_FILENO), and parent (shell) will be also stuck. | ||
3726 | * Try this script: | ||
3727 | * yes "0123456789012345678901234567890" | dd bs=32 count=64k >TESTFILE | ||
3728 | * huge=`cat TESTFILE` # will block here forever | ||
3729 | * echo OK | ||
3730 | */ | ||
3731 | pid = BB_MMU ? fork() : vfork(); | 3712 | pid = BB_MMU ? fork() : vfork(); |
3732 | if (pid < 0) | 3713 | if (pid < 0) |
3733 | bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork"); | 3714 | bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork"); |
3715 | |||
3734 | if (pid == 0) { /* child */ | 3716 | if (pid == 0) { /* child */ |
3735 | if (ENABLE_HUSH_JOB) | ||
3736 | die_sleep = 0; /* let nofork's xfuncs die */ | ||
3737 | close(channel[0]); /* NB: close _first_, then move fd! */ | ||
3738 | xmove_fd(channel[1], 1); | ||
3739 | /* Prevent it from trying to handle ctrl-z etc */ | ||
3740 | #if ENABLE_HUSH_JOB | ||
3741 | G.run_list_level = 1; | ||
3742 | #endif | ||
3743 | /* Process substitution is not considered to be usual | 3717 | /* Process substitution is not considered to be usual |
3744 | * 'command execution'. | 3718 | * 'command execution'. |
3745 | * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. | 3719 | * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. |
@@ -3749,40 +3723,47 @@ static FILE *generate_stream_from_list(struct pipe *head) | |||
3749 | + (1 << SIGTTIN) | 3723 | + (1 << SIGTTIN) |
3750 | + (1 << SIGTTOU) | 3724 | + (1 << SIGTTOU) |
3751 | , SIG_IGN); | 3725 | , SIG_IGN); |
3752 | 3726 | if (ENABLE_HUSH_JOB) | |
3753 | /* Note: freeing 'head' here would break NOMMU. */ | 3727 | die_sleep = 0; /* let nofork's xfuncs die */ |
3754 | _exit(run_list(head)); | 3728 | close(channel[0]); /* NB: close _first_, then move fd! */ |
3729 | xmove_fd(channel[1], 1); | ||
3730 | /* Prevent it from trying to handle ctrl-z etc */ | ||
3731 | USE_HUSH_JOB(G.run_list_level = 1;) | ||
3732 | #if BB_MMU | ||
3733 | parse_and_run_string(s); | ||
3734 | _exit(G.last_return_code); | ||
3735 | #else | ||
3736 | /* We re-execute after vfork on NOMMU. This makes this script safe: | ||
3737 | * yes "0123456789012345678901234567890" | dd bs=32 count=64k >TESTFILE | ||
3738 | * huge=`cat TESTFILE` # was blocking here forever | ||
3739 | * echo OK | ||
3740 | */ | ||
3741 | //TODO: pass non-exported variables, traps, and functions | ||
3742 | execl(CONFIG_BUSYBOX_EXEC_PATH, "hush", "-c", s, NULL); | ||
3743 | _exit(127); | ||
3744 | #endif | ||
3755 | } | 3745 | } |
3746 | |||
3747 | /* parent */ | ||
3756 | close(channel[1]); | 3748 | close(channel[1]); |
3757 | pf = fdopen(channel[0], "r"); | 3749 | pf = fdopen(channel[0], "r"); |
3758 | return pf; | 3750 | return pf; |
3759 | /* 'head' is freed by the caller */ | ||
3760 | } | 3751 | } |
3761 | 3752 | ||
3762 | /* Return code is exit status of the process that is run. */ | 3753 | /* Return code is exit status of the process that is run. */ |
3763 | static int process_command_subs(o_string *dest, | 3754 | static int process_command_subs(o_string *dest, const char *s) |
3764 | struct in_str *input) | ||
3765 | { | 3755 | { |
3766 | int retcode, ch, eol_cnt; | 3756 | FILE *pf; |
3767 | struct pipe *pipe_list; | ||
3768 | FILE *p; | ||
3769 | struct in_str pipe_str; | 3757 | struct in_str pipe_str; |
3758 | int ch, eol_cnt; | ||
3770 | 3759 | ||
3771 | /* Recursion to generate command */ | 3760 | pf = generate_stream_from_string(s); |
3772 | pipe_list = parse_stream(input, '\0'); | 3761 | if (pf == NULL) |
3773 | if (pipe_list == NULL) | ||
3774 | return 0; /* EOF: empty `cmd`: ``, ` ` etc */ | ||
3775 | if (pipe_list == ERR_PTR) | ||
3776 | return 1; /* parse error. can this really happen? */ | ||
3777 | |||
3778 | p = generate_stream_from_list(pipe_list); | ||
3779 | free_pipe_list(pipe_list, /* indent: */ 0); | ||
3780 | if (p == NULL) | ||
3781 | return 1; | 3762 | return 1; |
3782 | close_on_exec_on(fileno(p)); | 3763 | close_on_exec_on(fileno(pf)); |
3783 | 3764 | ||
3784 | /* Now send results of command back into original context */ | 3765 | /* Now send results of command back into original context */ |
3785 | setup_file_in_str(&pipe_str, p); | 3766 | setup_file_in_str(&pipe_str, pf); |
3786 | eol_cnt = 0; | 3767 | eol_cnt = 0; |
3787 | while ((ch = i_getch(&pipe_str)) != EOF) { | 3768 | while ((ch = i_getch(&pipe_str)) != EOF) { |
3788 | if (ch == '\n') { | 3769 | if (ch == '\n') { |
@@ -3799,12 +3780,13 @@ static int process_command_subs(o_string *dest, | |||
3799 | debug_printf("done reading from pipe, pclose()ing\n"); | 3780 | debug_printf("done reading from pipe, pclose()ing\n"); |
3800 | /* Note: we got EOF, and we just close the read end of the pipe. | 3781 | /* Note: we got EOF, and we just close the read end of the pipe. |
3801 | * We do not wait for the `cmd` child to terminate. bash and ash do. | 3782 | * We do not wait for the `cmd` child to terminate. bash and ash do. |
3802 | * Try this: | 3783 | * Try these: |
3803 | * echo `echo Hi; exec 1>&-; sleep 2` | 3784 | * echo `echo Hi; exec 1>&-; sleep 2` - bash waits 2 sec |
3785 | * `false`; echo $? - bash outputs "1" | ||
3804 | */ | 3786 | */ |
3805 | retcode = fclose(p); | 3787 | fclose(pf); |
3806 | debug_printf("closed FILE from child, retcode=%d\n", retcode); | 3788 | debug_printf("closed FILE from child. return 0\n"); |
3807 | return retcode; | 3789 | return 0; |
3808 | } | 3790 | } |
3809 | #endif | 3791 | #endif |
3810 | 3792 | ||
diff --git a/shell/hush_test/hush-misc/syntax_err.right b/shell/hush_test/hush-misc/syntax_err.right index 08a270c31..680e7967d 100644 --- a/shell/hush_test/hush-misc/syntax_err.right +++ b/shell/hush_test/hush-misc/syntax_err.right | |||
@@ -1,2 +1,4 @@ | |||
1 | shown | 1 | shown |
2 | hush: syntax error: unterminated ' | 2 | hush: syntax error: unterminated ' |
3 | test | ||
4 | not shown | ||