diff options
-rw-r--r-- | shell/hush.c | 128 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/redir1.right | 10 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/redir1.tests | 32 |
3 files changed, 121 insertions, 49 deletions
diff --git a/shell/hush.c b/shell/hush.c index 0ac42c90b..61db928b2 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -593,7 +593,6 @@ static const struct built_in_command bltins[] = { }; -/* Normal */ static void maybe_die(const char *notice, const char *msg) { /* Was using fancy stuff: @@ -615,6 +614,7 @@ static void maybe_die(const char *notice, const char *msg) #define syntax(msg) _syntax(msg, __LINE__) #endif + static int glob_needed(const char *s) { while (*s) { @@ -816,7 +816,7 @@ static void free_strings(char **strings) * sigset_t blocked_set: current blocked signal set * * "trap - SIGxxx": - * clear bit in blocked_set unless it is also in non_DFL + * clear bit in blocked_set unless it is also in non_DFL_mask * "trap 'cmd' SIGxxx": * set bit in blocked_set (even if 'cmd' is '') * after [v]fork, if we plan to be a shell: @@ -2292,24 +2292,33 @@ static void pseudo_exec(nommu_save_t *nommu_save, struct command *command, char **argv_expanded) { - if (command->argv) - pseudo_exec_argv(nommu_save, command->argv, command->assignment_cnt, argv_expanded); + if (command->argv) { + pseudo_exec_argv(nommu_save, command->argv, + command->assignment_cnt, argv_expanded); + } if (command->group) { -#if !BB_MMU - bb_error_msg_and_die("nested lists are not supported on NOMMU"); -#else + /* Cases when we are here: + * ( list ) + * { list } & + * ... | ( list ) | ... + * ... | { list } | ... + */ +#if BB_MMU int rcode; debug_printf_exec("pseudo_exec: run_list\n"); rcode = run_list(command->group); /* OK to leak memory by not calling free_pipe_list, * since this process is about to exit */ _exit(rcode); +#else +//TODO: re-exec "hush -c command->group_as_a_string" + bb_error_msg_and_die("nested lists are not supported on NOMMU"); #endif } /* Can happen. See what bash does with ">foo" by itself. */ - debug_printf("trying to pseudo_exec null command\n"); + debug_printf("pseudo_exec'ed null command\n"); _exit(EXIT_SUCCESS); } @@ -2563,61 +2572,72 @@ static int checkjobs_and_fg_shell(struct pipe* fg_pipe) /* run_pipe() starts all the jobs, but doesn't wait for anything * to finish. See checkjobs(). * - * return code is normally -1, when the caller has to wait for children + * Return code is normally -1, when the caller has to wait for children * to finish to determine the exit status of the pipe. If the pipe * is a simple builtin command, however, the action is done by the * time run_pipe returns, and the exit code is provided as the * return value. * - * The input of the pipe is always stdin, the output is always - * stdout. The outpipe[] mechanism in BusyBox-0.48 lash is bogus, - * because it tries to avoid running the command substitution in - * subshell, when that is in fact necessary. The subshell process - * now has its stdout directed to the input of the appropriate pipe, - * so this routine is noticeably simpler. - * * Returns -1 only if started some children. IOW: we have to * mask out retvals of builtins etc with 0xff! + * + * The only case when we do not need to [v]fork is when the pipe + * is single, non-backgrounded, non-subshell command. Examples: + * cmd ; ... { list } ; ... + * cmd && ... { list } && ... + * cmd || ... { list } || ... + * If it is, then we can run cmd as a builtin, NOFORK [do we do this?], + * or (if SH_STANDALONE) an applet, and we can run the { list } + * with run_list(). Otherwise, we fork and exec cmd. + * + * Cases when we must fork: + * non-single: cmd | cmd + * backgrounded: cmd & { list } & + * subshell: ( list ) [&] */ static int run_pipe(struct pipe *pi) { + static const char *const null_ptr = NULL; int i; int nextin; int pipefds[2]; /* pipefds[0] is for reading */ struct command *command; char **argv_expanded; char **argv; - const struct built_in_command *x; char *p; /* it is not always needed, but we aim to smaller code */ int squirrel[] = { -1, -1, -1 }; int rcode; - const int single_and_fg = (pi->num_cmds == 1 && pi->followup != PIPE_BG); - debug_printf_exec("run_pipe start: single_and_fg=%d\n", single_and_fg); + debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds); -#if ENABLE_HUSH_JOB - pi->pgrp = -1; -#endif - pi->alive_cmds = 1; + USE_HUSH_JOB(pi->pgrp = -1;) pi->stopped_cmds = 0; - - /* Check if this is a simple builtin (not part of a pipe). - * Builtins within pipes have to fork anyway, and are handled in - * pseudo_exec. "echo foo | read bar" doesn't work on bash, either. - */ command = &(pi->cmds[0]); + argv_expanded = NULL; -#if ENABLE_HUSH_FUNCTIONS - if (single_and_fg && command->group && command->grp_type == GRP_FUNCTION) { - /* We "execute" function definition */ - bb_error_msg("here we ought to remember function definition, and go on"); - return EXIT_SUCCESS; + if (pi->num_cmds != 1 + || pi->followup == PIPE_BG + || command->grp_type == GRP_SUBSHELL + ) { + goto must_fork; } -#endif - if (single_and_fg && command->group && command->grp_type == GRP_NORMAL) { - debug_printf("non-subshell grouping\n"); + pi->alive_cmds = 1; + + debug_printf_exec(": group:%p argv:'%s'\n", + command->group, command->argv ? command->argv[0] : "NONE"); + + if (command->group) { +#if ENABLE_HUSH_FUNCTIONS + if (command->grp_type == GRP_FUNCTION) { + /* func () { list } */ + bb_error_msg("here we ought to remember function definition, and go on"); + return EXIT_SUCCESS; + } +#endif + /* { list } */ + debug_printf("non-subshell group\n"); setup_redirects(command, squirrel); debug_printf_exec(": run_list\n"); rcode = run_list(command->group) & 0xff; @@ -2627,26 +2647,33 @@ static int run_pipe(struct pipe *pi) return rcode; } - argv = command->argv; - argv_expanded = NULL; - - if (single_and_fg && argv != NULL) { + argv = command->argv ? command->argv : (char **) &null_ptr; + { + const struct built_in_command *x; char **new_env = NULL; char **old_env = NULL; - i = command->assignment_cnt; - if (i != 0 && argv[i] == NULL) { - /* assignments, but no command: set local environment */ - for (i = 0; argv[i] != NULL; i++) { - debug_printf("local environment set: %s\n", argv[i]); - p = expand_string_to_string(argv[i]); + if (argv[command->assignment_cnt] == NULL) { + /* Assignments, but no command */ + /* Ensure redirects take effect. Try "a=t >file" */ + setup_redirects(command, squirrel); + restore_redirects(squirrel); + /* Set shell variables */ + while (*argv) { + p = expand_string_to_string(*argv); + debug_printf_exec("set shell var:'%s'->'%s'\n", + *argv, p); set_local_var(p, 0); + argv++; } - return EXIT_SUCCESS; /* don't worry about errors in set_local_var() yet */ + /* Do we need to flag set_local_var() errors? + * "assignment to readonly var" and "putenv error" + */ + return EXIT_SUCCESS; } /* Expand the rest into (possibly) many strings each */ - argv_expanded = expand_strvec_to_strvec(argv + i); + argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt); for (x = bltins; x != &bltins[ARRAY_SIZE(bltins)]; x++) { if (strcmp(argv_expanded[0], x->cmd) != 0) @@ -2694,8 +2721,10 @@ static int run_pipe(struct pipe *pi) goto clean_up_and_ret; } #endif + /* It is neither builtin nor applet. We must fork. */ } + must_fork: /* NB: argv_expanded may already be created, and that * might include `cmd` runs! Do not rerun it! We *must* * use argv_expanded if it's non-NULL */ @@ -2715,8 +2744,9 @@ static int run_pipe(struct pipe *pi) if (command->argv) { debug_printf_exec(": pipe member '%s' '%s'...\n", command->argv[0], command->argv[1]); - } else + } else { debug_printf_exec(": pipe member with no argv\n"); + } /* pipes are inserted between pairs of commands */ pipefds[0] = 0; diff --git a/shell/hush_test/hush-misc/redir1.right b/shell/hush_test/hush-misc/redir1.right new file mode 100644 index 000000000..ac90b4a0a --- /dev/null +++ b/shell/hush_test/hush-misc/redir1.right @@ -0,0 +1,10 @@ +Test 1: var:ok +File created:ok +Test 2: var:ok +File created:ok +Test 3: var:ok +File created:ok +Test 4: var:ok +File created:ok +Test 5: var:ok +File created:ok diff --git a/shell/hush_test/hush-misc/redir1.tests b/shell/hush_test/hush-misc/redir1.tests new file mode 100755 index 000000000..5f6c20612 --- /dev/null +++ b/shell/hush_test/hush-misc/redir1.tests @@ -0,0 +1,32 @@ +rm shell_test_$$ 2>/dev/null +var=bad +var=ok >shell_test_$$ +echo "Test 1: var:$var" +test -f shell_test_$$ && echo "File created:ok" + +rm shell_test_$$ 2>/dev/null +var=ok +true | var=bad >shell_test_$$ +echo "Test 2: var:$var" +test -f shell_test_$$ && echo "File created:ok" + +rm shell_test_$$ 2>/dev/null +var=bad +{ var=ok >shell_test_$$; } +echo "Test 3: var:$var" +test -f shell_test_$$ && echo "File created:ok" + +rm shell_test_$$ 2>/dev/null +var=ok +{ var=bad >shell_test_$$; } & +usleep 100000 +echo "Test 4: var:$var" +test -f shell_test_$$ && echo "File created:ok" + +rm shell_test_$$ 2>/dev/null +var=ok +( var=bad >shell_test_$$ ) +echo "Test 5: var:$var" +test -f shell_test_$$ && echo "File created:ok" + +rm shell_test_$$ 2>/dev/null |