aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-09 13:43:11 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-09 13:43:11 +0000
commitefea9d2819165b414b81a47c8227ae007bd5382a (patch)
treefb8dbfb1877b5fafc082acb9ffef4b30273fb3ed
parentf81e8dbc74186f34faa5ae2b0fe8bdc3f18114ae (diff)
downloadbusybox-w32-efea9d2819165b414b81a47c8227ae007bd5382a.tar.gz
busybox-w32-efea9d2819165b414b81a47c8227ae007bd5382a.tar.bz2
busybox-w32-efea9d2819165b414b81a47c8227ae007bd5382a.zip
hush: fix EXIT trap recursion case; check redirection failures
function old new delta run_pipe 1299 1328 +29 hush_exit 90 102 +12 hush_main 1172 1179 +7 run_list 1226 1225 -1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/1 up/down: 48/-1) Total: 47 bytes
-rw-r--r--shell/hush.c76
-rw-r--r--shell/hush_test/hush-misc/exit1.right1
-rwxr-xr-xshell/hush_test/hush-misc/exit1.tests4
-rw-r--r--shell/hush_test/hush-misc/redir3.right14
-rwxr-xr-xshell/hush_test/hush-misc/redir3.tests9
5 files changed, 74 insertions, 30 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 543f1fe67..d791b62d0 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -513,6 +513,7 @@ struct globals {
513 smallint flag_break_continue; 513 smallint flag_break_continue;
514#endif 514#endif
515 smallint fake_mode; 515 smallint fake_mode;
516 smallint exiting; /* used to prevent EXIT trap recursion */
516 /* These four support $?, $#, and $1 */ 517 /* These four support $?, $#, and $1 */
517 smalluint last_exitcode; 518 smalluint last_exitcode;
518 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */ 519 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
@@ -851,7 +852,7 @@ static void free_strings(char **strings)
851 * 852 *
852 * Trap handlers will execute even within trap handlers. (right?) 853 * Trap handlers will execute even within trap handlers. (right?)
853 * 854 *
854 * User trap handlers are forgotten when subshell ("(cmd)") is entered. [TODO] 855 * User trap handlers are forgotten when subshell ("(cmd)") is entered.
855 * 856 *
856 * If job control is off, backgrounded commands ("cmd &") 857 * If job control is off, backgrounded commands ("cmd &")
857 * have SIGINT, SIGQUIT set to SIG_IGN. 858 * have SIGINT, SIGQUIT set to SIG_IGN.
@@ -905,7 +906,7 @@ static void free_strings(char **strings)
905 * set bit in blocked_set (even if 'cmd' is '') 906 * set bit in blocked_set (even if 'cmd' is '')
906 * after [v]fork, if we plan to be a shell: 907 * after [v]fork, if we plan to be a shell:
907 * nothing for {} child shell (say, "true | { true; true; } | true") 908 * nothing for {} child shell (say, "true | { true; true; } | true")
908 * unset all traps if () shell. [TODO] 909 * unset all traps if () shell.
909 * after [v]fork, if we plan to exec: 910 * after [v]fork, if we plan to exec:
910 * POSIX says pending signal mask is cleared in child - no need to clear it. 911 * POSIX says pending signal mask is cleared in child - no need to clear it.
911 * Restore blocked signal set to one inherited by shell just prior to exec. 912 * Restore blocked signal set to one inherited by shell just prior to exec.
@@ -1005,9 +1006,13 @@ static void sigexit(int sig)
1005static void hush_exit(int exitcode) NORETURN; 1006static void hush_exit(int exitcode) NORETURN;
1006static void hush_exit(int exitcode) 1007static void hush_exit(int exitcode)
1007{ 1008{
1008 if (G.traps && G.traps[0] && G.traps[0][0]) { 1009 if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) {
1009 char *argv[] = { NULL, xstrdup(G.traps[0]), NULL }; 1010 /* Prevent recursion:
1010//TODO: do we need to prevent recursion? 1011 * trap "echo Hi; exit" EXIT; exit
1012 */
1013 char *argv[] = { NULL, G.traps[0], NULL };
1014 G.traps[0] = NULL;
1015 G.exiting = 1;
1011 builtin_eval(argv); 1016 builtin_eval(argv);
1012 free(argv[1]); 1017 free(argv[1]);
1013 } 1018 }
@@ -2350,7 +2355,6 @@ static void setup_heredoc(struct redir_struct *redir)
2350 * and stderr if they are redirected. */ 2355 * and stderr if they are redirected. */
2351static int setup_redirects(struct command *prog, int squirrel[]) 2356static int setup_redirects(struct command *prog, int squirrel[])
2352{ 2357{
2353//TODO: no callers ever check return value - ?!
2354 int openfd, mode; 2358 int openfd, mode;
2355 struct redir_struct *redir; 2359 struct redir_struct *redir;
2356 2360
@@ -2952,11 +2956,13 @@ static int run_pipe(struct pipe *pi)
2952#endif 2956#endif
2953 /* { list } */ 2957 /* { list } */
2954 debug_printf("non-subshell group\n"); 2958 debug_printf("non-subshell group\n");
2955 setup_redirects(command, squirrel); 2959 rcode = 1; /* exitcode if redir failed */
2956 debug_printf_exec(": run_list\n"); 2960 if (setup_redirects(command, squirrel) == 0) {
2957 rcode = run_list(command->group) & 0xff; 2961 debug_printf_exec(": run_list\n");
2962 rcode = run_list(command->group) & 0xff;
2963 debug_printf_exec("run_pipe return %d\n", rcode);
2964 }
2958 restore_redirects(squirrel); 2965 restore_redirects(squirrel);
2959 debug_printf_exec("run_pipe return %d\n", rcode);
2960 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) 2966 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
2961 return rcode; 2967 return rcode;
2962 } 2968 }
@@ -2970,7 +2976,7 @@ static int run_pipe(struct pipe *pi)
2970 if (argv[command->assignment_cnt] == NULL) { 2976 if (argv[command->assignment_cnt] == NULL) {
2971 /* Assignments, but no command */ 2977 /* Assignments, but no command */
2972 /* Ensure redirects take effect. Try "a=t >file" */ 2978 /* Ensure redirects take effect. Try "a=t >file" */
2973 setup_redirects(command, squirrel); 2979 rcode = setup_redirects(command, squirrel);
2974 restore_redirects(squirrel); 2980 restore_redirects(squirrel);
2975 /* Set shell variables */ 2981 /* Set shell variables */
2976 while (*argv) { 2982 while (*argv) {
@@ -2983,7 +2989,8 @@ static int run_pipe(struct pipe *pi)
2983 /* Do we need to flag set_local_var() errors? 2989 /* Do we need to flag set_local_var() errors?
2984 * "assignment to readonly var" and "putenv error" 2990 * "assignment to readonly var" and "putenv error"
2985 */ 2991 */
2986 return EXIT_SUCCESS; 2992 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
2993 return rcode;
2987 } 2994 }
2988 2995
2989 /* Expand the rest into (possibly) many strings each */ 2996 /* Expand the rest into (possibly) many strings each */
@@ -2994,8 +3001,7 @@ static int run_pipe(struct pipe *pi)
2994 continue; 3001 continue;
2995 if (x->function == builtin_exec && argv_expanded[1] == NULL) { 3002 if (x->function == builtin_exec && argv_expanded[1] == NULL) {
2996 debug_printf("exec with redirects only\n"); 3003 debug_printf("exec with redirects only\n");
2997 setup_redirects(command, NULL); 3004 rcode = setup_redirects(command, NULL);
2998 rcode = EXIT_SUCCESS;
2999 goto clean_up_and_ret1; 3005 goto clean_up_and_ret1;
3000 } 3006 }
3001 debug_printf("builtin inline %s\n", argv_expanded[0]); 3007 debug_printf("builtin inline %s\n", argv_expanded[0]);
@@ -3003,12 +3009,14 @@ static int run_pipe(struct pipe *pi)
3003 * This is perfect for work that comes after exec(). 3009 * This is perfect for work that comes after exec().
3004 * Is it really safe for inline use? Experimentally, 3010 * Is it really safe for inline use? Experimentally,
3005 * things seem to work with glibc. */ 3011 * things seem to work with glibc. */
3006 setup_redirects(command, squirrel); 3012 rcode = setup_redirects(command, squirrel);
3007 new_env = expand_assignments(argv, command->assignment_cnt); 3013 if (rcode == 0) {
3008 old_env = putenv_all_and_save_old(new_env); 3014 new_env = expand_assignments(argv, command->assignment_cnt);
3009 debug_printf_exec(": builtin '%s' '%s'...\n", 3015 old_env = putenv_all_and_save_old(new_env);
3010 x->cmd, argv_expanded[1]); 3016 debug_printf_exec(": builtin '%s' '%s'...\n",
3011 rcode = x->function(argv_expanded) & 0xff; 3017 x->cmd, argv_expanded[1]);
3018 rcode = x->function(argv_expanded) & 0xff;
3019 }
3012#if ENABLE_FEATURE_SH_STANDALONE 3020#if ENABLE_FEATURE_SH_STANDALONE
3013 clean_up_and_ret: 3021 clean_up_and_ret:
3014#endif 3022#endif
@@ -3027,13 +3035,15 @@ static int run_pipe(struct pipe *pi)
3027#if ENABLE_FEATURE_SH_STANDALONE 3035#if ENABLE_FEATURE_SH_STANDALONE
3028 i = find_applet_by_name(argv_expanded[0]); 3036 i = find_applet_by_name(argv_expanded[0]);
3029 if (i >= 0 && APPLET_IS_NOFORK(i)) { 3037 if (i >= 0 && APPLET_IS_NOFORK(i)) {
3030 setup_redirects(command, squirrel); 3038 rcode = setup_redirects(command, squirrel);
3031 save_nofork_data(&G.nofork_save); 3039 if (rcode == 0) {
3032 new_env = expand_assignments(argv, command->assignment_cnt); 3040 save_nofork_data(&G.nofork_save);
3033 old_env = putenv_all_and_save_old(new_env); 3041 new_env = expand_assignments(argv, command->assignment_cnt);
3034 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", 3042 old_env = putenv_all_and_save_old(new_env);
3043 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
3035 argv_expanded[0], argv_expanded[1]); 3044 argv_expanded[0], argv_expanded[1]);
3036 rcode = run_nofork_applet_prime(&G.nofork_save, i, argv_expanded); 3045 rcode = run_nofork_applet_prime(&G.nofork_save, i, argv_expanded);
3046 }
3037 goto clean_up_and_ret; 3047 goto clean_up_and_ret;
3038 } 3048 }
3039#endif 3049#endif
@@ -3095,7 +3105,8 @@ static int run_pipe(struct pipe *pi)
3095 close(pipefds[0]); /* read end */ 3105 close(pipefds[0]); /* read end */
3096 /* Like bash, explicit redirects override pipes, 3106 /* Like bash, explicit redirects override pipes,
3097 * and the pipe fd is available for dup'ing. */ 3107 * and the pipe fd is available for dup'ing. */
3098 setup_redirects(command, NULL); 3108 if (setup_redirects(command, NULL))
3109 _exit(1);
3099 3110
3100 /* Restore default handlers just prior to exec */ 3111 /* Restore default handlers just prior to exec */
3101 /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */ 3112 /*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */
@@ -3324,9 +3335,9 @@ static int run_list(struct pipe *pi)
3324//// /* ctrl-Z handler will store pid etc in pi */ 3335//// /* ctrl-Z handler will store pid etc in pi */
3325//// G.toplevel_list = pi; 3336//// G.toplevel_list = pi;
3326//// G.ctrl_z_flag = 0; 3337//// G.ctrl_z_flag = 0;
3327////#if ENABLE_FEATURE_SH_STANDALONE 3338#if ENABLE_FEATURE_SH_STANDALONE
3328//// G.nofork_save.saved = 0; /* in case we will run a nofork later */ 3339 G.nofork_save.saved = 0; /* in case we will run a nofork later */
3329////#endif 3340#endif
3330//// signal_SA_RESTART_empty_mask(SIGTSTP, handler_ctrl_z); 3341//// signal_SA_RESTART_empty_mask(SIGTSTP, handler_ctrl_z);
3331//// signal(SIGINT, handler_ctrl_c); 3342//// signal(SIGINT, handler_ctrl_c);
3332 } 3343 }
@@ -5680,6 +5691,8 @@ int hush_main(int argc, char **argv)
5680 enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */ 5691 enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
5681 if (setjmp(die_jmp)) { 5692 if (setjmp(die_jmp)) {
5682 /* xfunc has failed! die die die */ 5693 /* xfunc has failed! die die die */
5694 /* no EXIT traps, this is an escape hatch! */
5695 G.exiting = 1;
5683 hush_exit(xfunc_error_retval); 5696 hush_exit(xfunc_error_retval);
5684 } 5697 }
5685 } else if (!signal_mask_is_inited) { 5698 } else if (!signal_mask_is_inited) {
@@ -5907,6 +5920,9 @@ static int builtin_exit(char **argv)
5907 //puts("exit"); /* bash does it */ 5920 //puts("exit"); /* bash does it */
5908// TODO: warn if we have background jobs: "There are stopped jobs" 5921// TODO: warn if we have background jobs: "There are stopped jobs"
5909// On second consecutive 'exit', exit anyway. 5922// On second consecutive 'exit', exit anyway.
5923// perhaps use G.exiting = -1 as indicator "last cmd was exit"
5924
5925 /* note: EXIT trap is run by hush_exit */
5910 if (*++argv == NULL) 5926 if (*++argv == NULL)
5911 hush_exit(G.last_exitcode); 5927 hush_exit(G.last_exitcode);
5912 /* mimic bash: exit 123abc == exit 255 + error msg */ 5928 /* mimic bash: exit 123abc == exit 255 + error msg */
diff --git a/shell/hush_test/hush-misc/exit1.right b/shell/hush_test/hush-misc/exit1.right
new file mode 100644
index 000000000..dd2cfc279
--- /dev/null
+++ b/shell/hush_test/hush-misc/exit1.right
@@ -0,0 +1 @@
Once
diff --git a/shell/hush_test/hush-misc/exit1.tests b/shell/hush_test/hush-misc/exit1.tests
new file mode 100755
index 000000000..41e0d092d
--- /dev/null
+++ b/shell/hush_test/hush-misc/exit1.tests
@@ -0,0 +1,4 @@
1trap "echo Not shown" EXIT
2(exit) # must be silent
3trap "echo Once; exit" EXIT
4{ exit; }
diff --git a/shell/hush_test/hush-misc/redir3.right b/shell/hush_test/hush-misc/redir3.right
new file mode 100644
index 000000000..3d20bbf68
--- /dev/null
+++ b/shell/hush_test/hush-misc/redir3.right
@@ -0,0 +1,14 @@
1hush: can't open '/does/not/exist': No such file or directory
2One:1
3hush: can't open '/cant/be/created': No such file or directory
4One:1
5Ok
6hush: can't open '/cant/be/created': No such file or directory
7Zero:0
8hush: can't open '/cant/be/created': No such file or directory
9One:1
10hush: can't open '/cant/be/created': No such file or directory
11One:1
12hush: can't open '/cant/be/created': No such file or directory
13Zero:0
14Done
diff --git a/shell/hush_test/hush-misc/redir3.tests b/shell/hush_test/hush-misc/redir3.tests
new file mode 100755
index 000000000..7c28e4324
--- /dev/null
+++ b/shell/hush_test/hush-misc/redir3.tests
@@ -0,0 +1,9 @@
1echo Error >/does/not/exist; echo One:$?
2t=BAD
3t=Ok >>/cant/be/created; echo One:$?
4echo $t
5! >/cant/be/created; echo Zero:$?
6exec >/cant/be/created; echo One:$?
7exec /bin/true >/cant/be/created; echo One:$?
8! exec /bin/true >/cant/be/created; echo Zero:$?
9echo Done