X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=shell%2Fhush.c;h=1187cbe8fb60b7e5a9acd6af06a68c091c94e038;hb=12450dbeef1afee5ed27f8f3a2395edbdc7a9355;hp=85a45dd6bf1aa93bb860b96b5534a78aea5624b4;hpb=2f7894b1bb60ab12b518fac5ea5928f3ec01de28;p=oweals%2Fbusybox.git diff --git a/shell/hush.c b/shell/hush.c index 85a45dd6b..1187cbe8f 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -86,6 +86,11 @@ #endif #include "math.h" #include "match.h" +#if ENABLE_HUSH_RANDOM_SUPPORT +# include "random.h" +#else +# define CLEAR_RANDOM_T(rnd) ((void)0) +#endif #ifndef PIPE_BUF # define PIPE_BUF 4096 /* amount of buffering in a pipe */ #endif @@ -449,9 +454,9 @@ struct function { char *name; struct command *parent_cmd; struct pipe *body; -#if !BB_MMU +# if !BB_MMU char *body_as_string; -#endif +# endif }; #endif @@ -484,7 +489,11 @@ struct globals { line_input_t *line_input_state; #endif pid_t root_pid; + pid_t root_ppid; pid_t last_bg_pid; +#if ENABLE_HUSH_RANDOM_SUPPORT + random_t random_gen; +#endif #if ENABLE_HUSH_JOB int run_list_level; int last_jobid; @@ -547,7 +556,7 @@ struct globals { unsigned long memleak_value; int debug_indent; #endif - char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2]; + char user_input_buf[ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 2]; }; #define G (*ptr_to_globals) /* Not #defining name to G.name - this quickly gets unwieldy @@ -578,6 +587,9 @@ static int builtin_local(char **argv) FAST_FUNC; #if HUSH_DEBUG static int builtin_memleak(char **argv) FAST_FUNC; #endif +#if ENABLE_PRINTF +static int builtin_printf(char **argv) FAST_FUNC; +#endif static int builtin_pwd(char **argv) FAST_FUNC; static int builtin_read(char **argv) FAST_FUNC; static int builtin_set(char **argv) FAST_FUNC; @@ -665,6 +677,9 @@ static const struct built_in_command bltins1[] = { static const struct built_in_command bltins2[] = { BLTIN("[" , builtin_test , NULL), BLTIN("echo" , builtin_echo , NULL), +#if ENABLE_PRINTF + BLTIN("printf" , builtin_printf , NULL), +#endif BLTIN("pwd" , builtin_pwd , NULL), BLTIN("test" , builtin_test , NULL), }; @@ -899,7 +914,7 @@ static int is_well_formed_var_name(const char *s, char terminator) /* Replace each \x with x in place, return ptr past NUL. */ static char *unbackslash(char *src) { - char *dst = src; + char *dst = src = strchrnul(src, '\\'); while (1) { if (*src == '\\') src++; @@ -1111,6 +1126,11 @@ static void restore_G_args(save_arg_t *sv, char **argv) * Note: as a result, we do not use signal handlers much. The only uses * are to count SIGCHLDs * and to restore tty pgrp on signal-induced exit. + * + * Note 2 (compat): + * Standard says "When a subshell is entered, traps that are not being ignored + * are set to the default actions". bash interprets it so that traps which + * are set to "" (ignore) are NOT reset to defaults. We do the same. */ enum { SPECIAL_INTERACTIVE_SIGS = 0 @@ -1302,6 +1322,14 @@ static const char *get_local_var_value(const char *name) struct variable **pp = get_ptr_to_local_var(name); if (pp) return strchr((*pp)->varstr, '=') + 1; + if (strcmp(name, "PPID") == 0) + return utoa(G.root_ppid); + // bash compat: UID? EUID? +#if ENABLE_HUSH_RANDOM_SUPPORT + if (strcmp(name, "RANDOM") == 0) { + return utoa(next_random(&G.random_gen)); + } +#endif return NULL; } @@ -1638,7 +1666,7 @@ static void get_user_input(struct in_str *i) G.flag_SIGINT = 0; /* buglet: SIGINT will not make new prompt to appear _at once_, * only after . (^C will work) */ - r = read_line_input(prompt_str, G.user_input_buf, BUFSIZ-1, G.line_input_state); + r = read_line_input(prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, G.line_input_state); /* catch *SIGINT* etc (^C is handled by read_line_input) */ check_and_run_traps(0); } while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */ @@ -2146,7 +2174,7 @@ static char *expand_pseudo_dquoted(const char *str) * to be filled). This routine is extremely tricky: has to deal with * variables/parameters with whitespace, $* and $@, and constructs like * 'echo -$*-'. If you play here, you must run testsuite afterwards! */ -static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) +static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) { /* or_mask is either 0 (normal case) or 0x80 - * expansion of right-hand side of assignment == 1-element expand. @@ -2157,7 +2185,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) ored_ch = 0; - debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg); + debug_printf_expand("expand_vars_to_list: arg:'%s' or_mask:%x\n", arg, or_mask); debug_print_list("expand_vars_to_list", output, n); n = o_save_ptr(output, n); debug_print_list("expand_vars_to_list[0]", output, n); @@ -2597,43 +2625,51 @@ static void reset_traps_to_defaults(void) { /* This function is always called in a child shell * after fork (not vfork, NOMMU doesn't use this function). - * Child shells are not interactive. - * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling. - * Testcase: (while :; do :; done) + ^Z should background. - * Same goes for SIGTERM, SIGHUP, SIGINT. */ unsigned sig; unsigned mask; + /* Child shells are not interactive. + * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling. + * Testcase: (while :; do :; done) + ^Z should background. + * Same goes for SIGTERM, SIGHUP, SIGINT. + */ if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) - return; + return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ - /* Stupid. It can be done with *single* &= op, but we can't use - * the fact that G.blocked_set is implemented as a bitmask... */ + /* Switching off SPECIAL_INTERACTIVE_SIGS. + * Stupid. It can be done with *single* &= op, but we can't use + * the fact that G.blocked_set is implemented as a bitmask + * in libc... */ mask = (SPECIAL_INTERACTIVE_SIGS >> 1); sig = 1; while (1) { - if (mask & 1) - sigdelset(&G.blocked_set, sig); + if (mask & 1) { + /* Careful. Only if no trap or trap is not "" */ + if (!G.traps || !G.traps[sig] || G.traps[sig][0]) + sigdelset(&G.blocked_set, sig); + } mask >>= 1; if (!mask) break; sig++; } - + /* Our homegrown sig mask is saner to work with :) */ G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; + + /* Resetting all traps to default except empty ones */ mask = G.non_DFL_mask; if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { - if (!G.traps[sig]) + if (!G.traps[sig] || !G.traps[sig][0]) continue; free(G.traps[sig]); G.traps[sig] = NULL; /* There is no signal for 0 (EXIT) */ if (sig == 0) continue; - /* There was a trap handler, we are removing it. + /* There was a trap handler, we just removed it. * But if sig still has non-DFL handling, - * we should not unblock it. */ + * we should not unblock the sig. */ if (mask & 1) continue; sigdelset(&G.blocked_set, sig); @@ -2653,9 +2689,9 @@ static void re_execute_shell(char ***to_free, const char *s, char param_buf[sizeof("-$%x:%x:%x:%x:%x") + sizeof(unsigned) * 2]; char *heredoc_argv[4]; struct variable *cur; -#if ENABLE_HUSH_FUNCTIONS +# if ENABLE_HUSH_FUNCTIONS struct function *funcp; -#endif +# endif char **argv, **pp; unsigned cnt; @@ -2674,8 +2710,9 @@ static void re_execute_shell(char ***to_free, const char *s, if (pp) while (*pp++) cnt++; - sprintf(param_buf, "-$%x:%x:%x:%x" IF_HUSH_LOOPS(":%x") + sprintf(param_buf, "-$%x:%x:%x:%x:%x" IF_HUSH_LOOPS(":%x") , (unsigned) G.root_pid + , (unsigned) G.root_ppid , (unsigned) G.last_bg_pid , (unsigned) G.last_exitcode , cnt @@ -2689,10 +2726,10 @@ static void re_execute_shell(char ***to_free, const char *s, if (!cur->flg_export || cur->flg_read_only) cnt += 2; } -#if ENABLE_HUSH_FUNCTIONS +# if ENABLE_HUSH_FUNCTIONS for (funcp = G.top_func; funcp; funcp = funcp->next) cnt += 3; -#endif +# endif pp = g_argv; while (*pp++) cnt++; @@ -2710,13 +2747,13 @@ static void re_execute_shell(char ***to_free, const char *s, *pp++ = cur->varstr; } } -#if ENABLE_HUSH_FUNCTIONS +# if ENABLE_HUSH_FUNCTIONS for (funcp = G.top_func; funcp; funcp = funcp->next) { *pp++ = (char *) "-F"; *pp++ = funcp->name; *pp++ = funcp->body_as_string; } -#endif +# endif /* We can pass activated traps here. Say, -Tnn:trap_string * * However, POSIX says that subshells reset signals with traps @@ -3205,9 +3242,9 @@ static int run_function(const struct function *funcp, char **argv) /* "we are in function, ok to use return" */ sv_flg = G.flag_return_in_progress; G.flag_return_in_progress = -1; -#if ENABLE_HUSH_LOCAL +# if ENABLE_HUSH_LOCAL G.func_nest_level++; -#endif +# endif /* On MMU, funcp->body is always non-NULL */ # if !BB_MMU @@ -3221,7 +3258,7 @@ static int run_function(const struct function *funcp, char **argv) rc = run_list(funcp->body); } -#if ENABLE_HUSH_LOCAL +# if ENABLE_HUSH_LOCAL { struct variable *var; struct variable **var_pp; @@ -3244,7 +3281,7 @@ static int run_function(const struct function *funcp, char **argv) } G.func_nest_level--; } -#endif +# endif G.flag_return_in_progress = sv_flg; restore_G_args(&sv, argv); @@ -3254,13 +3291,13 @@ static int run_function(const struct function *funcp, char **argv) #endif /* ENABLE_HUSH_FUNCTIONS */ -# if BB_MMU +#if BB_MMU #define exec_builtin(to_free, x, argv) \ exec_builtin(x, argv) -# else +#else #define exec_builtin(to_free, x, argv) \ exec_builtin(to_free, argv) -# endif +#endif static void exec_builtin(char ***to_free, const struct built_in_command *x, char **argv) NORETURN; @@ -3268,11 +3305,11 @@ static void exec_builtin(char ***to_free, const struct built_in_command *x, char **argv) { -# if BB_MMU +#if BB_MMU int rcode = x->function(argv); fflush(NULL); _exit(rcode); -# else +#else /* On NOMMU, we must never block! * Example: { sleep 99 | read line; } & echo Ok */ @@ -3281,10 +3318,20 @@ static void exec_builtin(char ***to_free, G.global_argv[0], G.global_argv + 1, argv); -# endif +#endif } +static void execvp_or_die(char **argv) NORETURN; +static void execvp_or_die(char **argv) +{ + debug_printf_exec("execing '%s'\n", argv[0]); + sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); + execvp(argv[0], argv); + bb_perror_msg("can't execute '%s'", argv[0]); + _exit(127); /* bash compat */ +} + #if BB_MMU #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ pseudo_exec_argv(argv, assignment_cnt, argv_expanded) @@ -3300,7 +3347,7 @@ static void exec_builtin(char ***to_free, static void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) NORETURN; -static void pseudo_exec_argv(nommu_save_t *nommu_save, +static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, char **argv, int assignment_cnt, char **argv_expanded) { @@ -3384,11 +3431,7 @@ static void pseudo_exec_argv(nommu_save_t *nommu_save, #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU skip: #endif - debug_printf_exec("execing '%s'\n", argv[0]); - sigprocmask(SIG_SETMASK, &G.inherited_set, NULL); - execvp(argv[0], argv); - bb_perror_msg("can't execute '%s'", argv[0]); - _exit(EXIT_FAILURE); + execvp_or_die(argv); } /* Called after [v]fork() in run_pipe @@ -3625,9 +3668,9 @@ static int checkjobs(struct pipe* fg_pipe) /* Note: is WIFSIGNALED, WEXITSTATUS = sig + 128 */ rcode = WEXITSTATUS(status); IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) - /* bash prints killing signal's name for *last* + /* bash prints killer signal's name for *last* * process in pipe (prints just newline for SIGINT). - * Mimic this. Example: "sleep 5" + ^\ + * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) */ if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); @@ -3737,7 +3780,7 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) * backgrounded: cmd & { list } & * subshell: ( list ) [&] */ -static int run_pipe(struct pipe *pi) +static NOINLINE int run_pipe(struct pipe *pi) { static const char *const null_ptr = NULL; int i; @@ -3858,6 +3901,12 @@ static int run_pipe(struct pipe *pi) argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); } + /* if someone gives us an empty string: `cmd with empty output` */ + if (!argv_expanded[0]) { + debug_leave(); + return 0; + } + x = find_builtin(argv_expanded[0]); #if ENABLE_HUSH_FUNCTIONS funcp = NULL; @@ -3969,6 +4018,7 @@ static int run_pipe(struct pipe *pi) if (!command->pid) { /* child */ #if ENABLE_HUSH_JOB disable_restore_tty_pgrp_on_exit(); + CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ /* Every child adds itself to new process group * with pgid == pid_of_first_child_in_pipe */ @@ -4071,30 +4121,30 @@ static void debug_print_tree(struct pipe *pi, int lvl) }; static const char *RES[] = { [RES_NONE ] = "NONE" , -#if ENABLE_HUSH_IF +# if ENABLE_HUSH_IF [RES_IF ] = "IF" , [RES_THEN ] = "THEN" , [RES_ELIF ] = "ELIF" , [RES_ELSE ] = "ELSE" , [RES_FI ] = "FI" , -#endif -#if ENABLE_HUSH_LOOPS +# endif +# if ENABLE_HUSH_LOOPS [RES_FOR ] = "FOR" , [RES_WHILE] = "WHILE", [RES_UNTIL] = "UNTIL", [RES_DO ] = "DO" , [RES_DONE ] = "DONE" , -#endif -#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE +# endif +# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE [RES_IN ] = "IN" , -#endif -#if ENABLE_HUSH_CASE +# endif +# if ENABLE_HUSH_CASE [RES_CASE ] = "CASE" , [RES_CASE_IN ] = "CASE_IN" , [RES_MATCH] = "MATCH", [RES_CASE_BODY] = "CASE_BODY", [RES_ESAC ] = "ESAC" , -#endif +# endif [RES_XXXX ] = "XXXX" , [RES_SNTX ] = "SNTX" , }; @@ -4102,9 +4152,9 @@ static void debug_print_tree(struct pipe *pi, int lvl) "{}", "()", "[noglob]", -#if ENABLE_HUSH_FUNCTIONS +# if ENABLE_HUSH_FUNCTIONS "func()", -#endif +# endif }; int pin, prn; @@ -4140,7 +4190,7 @@ static void debug_print_tree(struct pipe *pi, int lvl) pin++; } } -#endif +#endif /* debug_print_tree */ /* NB: called by pseudo_exec, and therefore must not modify any * global data until exec/_exit (we can be a child after vfork!) */ @@ -4614,25 +4664,25 @@ struct reserved_combo { }; enum { FLAG_END = (1 << RES_NONE ), -#if ENABLE_HUSH_IF +# if ENABLE_HUSH_IF FLAG_IF = (1 << RES_IF ), FLAG_THEN = (1 << RES_THEN ), FLAG_ELIF = (1 << RES_ELIF ), FLAG_ELSE = (1 << RES_ELSE ), FLAG_FI = (1 << RES_FI ), -#endif -#if ENABLE_HUSH_LOOPS +# endif +# if ENABLE_HUSH_LOOPS FLAG_FOR = (1 << RES_FOR ), FLAG_WHILE = (1 << RES_WHILE), FLAG_UNTIL = (1 << RES_UNTIL), FLAG_DO = (1 << RES_DO ), FLAG_DONE = (1 << RES_DONE ), FLAG_IN = (1 << RES_IN ), -#endif -#if ENABLE_HUSH_CASE +# endif +# if ENABLE_HUSH_CASE FLAG_MATCH = (1 << RES_MATCH), FLAG_ESAC = (1 << RES_ESAC ), -#endif +# endif FLAG_START = (1 << RES_XXXX ), }; @@ -4644,26 +4694,26 @@ static const struct reserved_combo* match_reserved_word(o_string *word) * FLAG_START means the word must start a new compound list. */ static const struct reserved_combo reserved_list[] = { -#if ENABLE_HUSH_IF +# if ENABLE_HUSH_IF { "!", RES_NONE, NOT_ASSIGNMENT , 0 }, { "if", RES_IF, WORD_IS_KEYWORD, FLAG_THEN | FLAG_START }, { "then", RES_THEN, WORD_IS_KEYWORD, FLAG_ELIF | FLAG_ELSE | FLAG_FI }, { "elif", RES_ELIF, WORD_IS_KEYWORD, FLAG_THEN }, { "else", RES_ELSE, WORD_IS_KEYWORD, FLAG_FI }, { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END }, -#endif -#if ENABLE_HUSH_LOOPS +# endif +# if ENABLE_HUSH_LOOPS { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START }, { "while", RES_WHILE, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, { "until", RES_UNTIL, WORD_IS_KEYWORD, FLAG_DO | FLAG_START }, { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO }, { "do", RES_DO, WORD_IS_KEYWORD, FLAG_DONE }, { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END }, -#endif -#if ENABLE_HUSH_CASE +# endif +# if ENABLE_HUSH_CASE { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START }, { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END }, -#endif +# endif }; const struct reserved_combo *r; @@ -4677,11 +4727,11 @@ static const struct reserved_combo* match_reserved_word(o_string *word) */ static int reserved_word(o_string *word, struct parse_context *ctx) { -#if ENABLE_HUSH_CASE +# if ENABLE_HUSH_CASE static const struct reserved_combo reserved_match = { "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC }; -#endif +# endif const struct reserved_combo *r; if (word->o_quoted) @@ -4691,12 +4741,12 @@ static int reserved_word(o_string *word, struct parse_context *ctx) return 0; debug_printf("found reserved word %s, res %d\n", r->literal, r->res); -#if ENABLE_HUSH_CASE +# if ENABLE_HUSH_CASE if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) { /* "case word IN ..." - IN part starts first MATCH part */ r = &reserved_match; } else -#endif +# endif if (r->flag == 0) { /* '!' */ if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */ syntax_error("! ! command"); @@ -4737,19 +4787,19 @@ static int reserved_word(o_string *word, struct parse_context *ctx) old = ctx->stack; old->command->group = ctx->list_head; old->command->cmd_type = CMD_NORMAL; -#if !BB_MMU +# if !BB_MMU o_addstr(&old->as_string, ctx->as_string.data); o_free_unsafe(&ctx->as_string); old->command->group_as_string = xstrdup(old->as_string.data); debug_printf_parse("pop, remembering as:'%s'\n", old->command->group_as_string); -#endif +# endif *ctx = *old; /* physical copy */ free(old); } return 1; } -#endif +#endif /* HAS_KEYWORDS */ /* Word is complete, look at it and update parsing context. * Normal return is 0. Syntax errors return 1. @@ -5156,9 +5206,9 @@ static FILE *generate_stream_from_string(const char *s) { FILE *pf; int pid, channel[2]; -#if !BB_MMU +# if !BB_MMU char **to_free; -#endif +# endif xpipe(channel); pid = BB_MMU ? fork() : vfork(); @@ -5176,6 +5226,7 @@ static FILE *generate_stream_from_string(const char *s) + (1 << SIGTTIN) + (1 << SIGTTOU) , SIG_IGN); + CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */ close(channel[0]); /* NB: close _first_, then move fd! */ xmove_fd(channel[1], 1); /* Prevent it from trying to handle ctrl-z etc */ @@ -5221,11 +5272,11 @@ static FILE *generate_stream_from_string(const char *s) builtin_trap((char**)argv); exit(0); /* not _exit() - we need to fflush */ } -#if BB_MMU +# if BB_MMU reset_traps_to_defaults(); parse_and_run_string(s); _exit(G.last_exitcode); -#else +# else /* We re-execute after vfork on NOMMU. This makes this script safe: * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG * huge=`cat BIG` # was blocking here forever @@ -5236,18 +5287,18 @@ static FILE *generate_stream_from_string(const char *s) G.global_argv[0], G.global_argv + 1, NULL); -#endif +# endif } /* parent */ -#if ENABLE_HUSH_FAST +# if ENABLE_HUSH_FAST G.count_SIGCHLD++; //bb_error_msg("[%d] fork in generate_stream_from_string: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD); -#endif +# endif enable_restore_tty_pgrp_on_exit(); -#if !BB_MMU +# if !BB_MMU free(to_free); -#endif +# endif close(channel[1]); pf = fdopen(channel[0], "r"); return pf; @@ -5291,7 +5342,7 @@ static int process_command_subs(o_string *dest, const char *s) debug_printf("closed FILE from child. return 0\n"); return 0; } -#endif +#endif /* ENABLE_HUSH_TICK */ static int parse_group(o_string *dest, struct parse_context *ctx, struct in_str *input, int ch) @@ -5659,7 +5710,7 @@ static int handle_dollar(o_string *as_string, o_addchr(dest, SPECIAL_VAR_SYMBOL); break; } -#if (ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK) +#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK case '(': { # if !BB_MMU int pos; @@ -5757,7 +5808,7 @@ static int parse_stream_dquoted(o_string *as_string, if (ch != '\n') { next = i_peek(input); } - debug_printf_parse(": ch=%c (%d) escape=%d\n", + debug_printf_parse("\" ch=%c (%d) escape=%d\n", ch, ch, dest->o_escape); if (ch == '\\') { if (next == EOF) { @@ -5837,6 +5888,11 @@ static struct pipe *parse_stream(char **pstring, end_trigger ? end_trigger : 'X'); debug_enter(); + /* If very first arg is "" or '', dest.data may end up NULL. + * Preventing this: */ + o_addchr(&dest, '\0'); + dest.length = 0; + G.ifs = get_local_var_value("IFS"); if (G.ifs == NULL) G.ifs = " \t\n"; @@ -6119,12 +6175,13 @@ static struct pipe *parse_stream(char **pstring, /* Example: echo Hello \2>file * we need to know that word 2 is quoted */ dest.o_quoted = 1; - } else { + } #if !BB_MMU + else { /* It's "\". Remove trailing '\' from ctx.as_string */ ctx.as_string.data[--ctx.as_string.length] = '\0'; -#endif } +#endif break; case '$': if (handle_dollar(&ctx.as_string, &dest, input) != 0) { @@ -6497,7 +6554,7 @@ int hush_main(int argc, char **argv) * MACHTYPE=i386-pc-linux-gnu * OSTYPE=linux-gnu * HOSTNAME= - * PPID= + * PPID= - we also do it elsewhere * EUID= * UID= * GROUPS=() @@ -6574,8 +6631,10 @@ int hush_main(int argc, char **argv) * Note: this form never happens: * sh ... -c 'builtin' [BARGV...] "" */ - if (!G.root_pid) + if (!G.root_pid) { G.root_pid = getpid(); + G.root_ppid = getppid(); + } G.global_argv = argv + optind; G.global_argc = argc - optind; if (builtin_argc) { @@ -6617,6 +6676,8 @@ int hush_main(int argc, char **argv) case '$': G.root_pid = bb_strtou(optarg, &optarg, 16); optarg++; + G.root_ppid = bb_strtou(optarg, &optarg, 16); + optarg++; G.last_bg_pid = bb_strtou(optarg, &optarg, 16); optarg++; G.last_exitcode = bb_strtou(optarg, &optarg, 16); @@ -6657,8 +6718,10 @@ int hush_main(int argc, char **argv) } } /* option parsing loop */ - if (!G.root_pid) + if (!G.root_pid) { G.root_pid = getpid(); + G.root_ppid = getppid(); + } /* If we are login shell... */ if (argv[0] && argv[0][0] == '-') { @@ -6803,10 +6866,13 @@ int hush_main(int argc, char **argv) */ if (!ENABLE_FEATURE_SH_EXTRA_QUIET && G_interactive_fd) { - printf("\n\n%s hush - the humble shell\n", bb_banner); - if (ENABLE_HUSH_HELP) - puts("Enter 'help' for a list of built-in commands."); - puts(""); + /* note: ash and hush share this string */ + printf("\n\n%s %s\n" + IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n") + "\n", + bb_banner, + "hush - the humble shell" + ); } parse_and_run_file(stdin); @@ -6855,26 +6921,33 @@ static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM) return 0; } -static int FAST_FUNC builtin_test(char **argv) +static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv)) { int argc = 0; while (*argv) { argc++; argv++; } - return test_main(argc, argv - argc); + return applet_main_func(argc, argv - argc); +} + +static int FAST_FUNC builtin_test(char **argv) +{ + return run_applet_main(argv, test_main); } static int FAST_FUNC builtin_echo(char **argv) { - int argc = 0; - while (*argv) { - argc++; - argv++; - } - return echo_main(argc, argv - argc); + return run_applet_main(argv, echo_main); } +#if ENABLE_PRINTF +static int FAST_FUNC builtin_printf(char **argv) +{ + return run_applet_main(argv, printf_main); +} +#endif + static int FAST_FUNC builtin_eval(char **argv) { int rcode = EXIT_SUCCESS; @@ -6920,14 +6993,17 @@ static int FAST_FUNC builtin_exec(char **argv) { if (*++argv == NULL) return EXIT_SUCCESS; /* bash does this */ - { -#if !BB_MMU - nommu_save_t dummy; -#endif - /* TODO: if exec fails, bash does NOT exit! We do... */ - pseudo_exec_argv(&dummy, argv, 0, NULL); - /* never returns */ - } + + /* Careful: we can end up here after [v]fork. Do not restore + * tty pgrp then, only top-level shell process does that */ + if (G_saved_tty_pgrp && getpid() == G.root_pid) + tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp); + + /* TODO: if exec fails, bash does NOT exit! We do. + * We'll need to undo sigprocmask (it's inside execvp_or_die) + * and tcsetpgrp, and this is inherently racy. + */ + execvp_or_die(argv); } static int FAST_FUNC builtin_exit(char **argv) @@ -7098,8 +7174,11 @@ static int FAST_FUNC builtin_trap(char **argv) if (G.traps[i]) { printf("trap -- "); print_escaped(G.traps[i]); - /* bash compat: it says SIGxxx, not just xxx */ - printf(" %s%s\n", i == 0 ? "" : "SIG", get_signame(i)); + /* note: bash adds "SIG", but only if invoked + * as "bash". If called as "sh", or if set -o posix, + * then it prints short signal names. + * We are printing short names: */ + printf(" %s\n", get_signame(i)); } } /*fflush(stdout); - done after each builtin anyway */ @@ -7310,10 +7389,10 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) void *p; unsigned long l; -#ifdef M_TRIM_THRESHOLD +# ifdef M_TRIM_THRESHOLD /* Optional. Reduces probability of false positives */ malloc_trim(0); -#endif +# endif /* Crude attempt to find where "free memory" starts, * sans fragmentation. */ p = malloc(240);