diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-09 13:43:11 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-09 13:43:11 +0000 |
commit | efea9d2819165b414b81a47c8227ae007bd5382a (patch) | |
tree | fb8dbfb1877b5fafc082acb9ffef4b30273fb3ed | |
parent | f81e8dbc74186f34faa5ae2b0fe8bdc3f18114ae (diff) | |
download | busybox-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.c | 76 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/exit1.right | 1 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/exit1.tests | 4 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/redir3.right | 14 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/redir3.tests | 9 |
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) | |||
1005 | static void hush_exit(int exitcode) NORETURN; | 1006 | static void hush_exit(int exitcode) NORETURN; |
1006 | static void hush_exit(int exitcode) | 1007 | static 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. */ |
2351 | static int setup_redirects(struct command *prog, int squirrel[]) | 2356 | static 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 @@ | |||
1 | trap "echo Not shown" EXIT | ||
2 | (exit) # must be silent | ||
3 | trap "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 @@ | |||
1 | hush: can't open '/does/not/exist': No such file or directory | ||
2 | One:1 | ||
3 | hush: can't open '/cant/be/created': No such file or directory | ||
4 | One:1 | ||
5 | Ok | ||
6 | hush: can't open '/cant/be/created': No such file or directory | ||
7 | Zero:0 | ||
8 | hush: can't open '/cant/be/created': No such file or directory | ||
9 | One:1 | ||
10 | hush: can't open '/cant/be/created': No such file or directory | ||
11 | One:1 | ||
12 | hush: can't open '/cant/be/created': No such file or directory | ||
13 | Zero:0 | ||
14 | Done | ||
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 @@ | |||
1 | echo Error >/does/not/exist; echo One:$? | ||
2 | t=BAD | ||
3 | t=Ok >>/cant/be/created; echo One:$? | ||
4 | echo $t | ||
5 | ! >/cant/be/created; echo Zero:$? | ||
6 | exec >/cant/be/created; echo One:$? | ||
7 | exec /bin/true >/cant/be/created; echo One:$? | ||
8 | ! exec /bin/true >/cant/be/created; echo Zero:$? | ||
9 | echo Done | ||