diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-05 19:13:39 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2009-04-05 19:13:39 +0000 |
commit | f9375285719035dbf2f7003d582c22447ed579f0 (patch) | |
tree | bfc4fcab7ae9f73bebe1e89d34aca33af332f57f | |
parent | 46f9b6db80e4fd0a1c50d435bc387ad28d12abb2 (diff) | |
download | busybox-w32-f9375285719035dbf2f7003d582c22447ed579f0.tar.gz busybox-w32-f9375285719035dbf2f7003d582c22447ed579f0.tar.bz2 busybox-w32-f9375285719035dbf2f7003d582c22447ed579f0.zip |
hush: audit and fix "interactive shell" setup code.
function old new delta
block_signals - 139 +139
maybe_set_to_sigexit - 47 +47
run_list 2018 2030 +12
expand_variables 2155 2165 +10
maybe_set_sighandler 47 - -47
hush_main 992 918 -74
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 2/1 up/down: 208/-121) Total: 87 bytes
-rw-r--r-- | shell/hush.c | 323 |
1 files changed, 175 insertions, 148 deletions
diff --git a/shell/hush.c b/shell/hush.c index eba7a86df..a3f80d512 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -850,8 +850,6 @@ static void free_strings(char **strings) | |||
850 | * Note: as a result, we do not use signal handlers much. The only uses | 850 | * Note: as a result, we do not use signal handlers much. The only uses |
851 | * are to count SIGCHLDs [disabled - bug somewhere, + bloat] | 851 | * are to count SIGCHLDs [disabled - bug somewhere, + bloat] |
852 | * and to restore tty pgrp on signal-induced exit. | 852 | * and to restore tty pgrp on signal-induced exit. |
853 | * | ||
854 | * TODO: check/fix wait builtin to be interruptible. | ||
855 | */ | 853 | */ |
856 | 854 | ||
857 | //static void SIGCHLD_handler(int sig UNUSED_PARAM) | 855 | //static void SIGCHLD_handler(int sig UNUSED_PARAM) |
@@ -859,36 +857,6 @@ static void free_strings(char **strings) | |||
859 | // G.count_SIGCHLD++; | 857 | // G.count_SIGCHLD++; |
860 | //} | 858 | //} |
861 | 859 | ||
862 | /* called once at shell init */ | ||
863 | static void init_signal_mask(void) | ||
864 | { | ||
865 | unsigned sig; | ||
866 | unsigned mask = (1 << SIGQUIT); | ||
867 | if (G_interactive_fd) { | ||
868 | mask = 0 | ||
869 | | (1 << SIGQUIT) | ||
870 | | (1 << SIGTERM) | ||
871 | | (1 << SIGHUP) | ||
872 | #if ENABLE_HUSH_JOB | ||
873 | | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP) | ||
874 | #endif | ||
875 | | (1 << SIGINT) | ||
876 | ; | ||
877 | } | ||
878 | G.non_DFL_mask = mask; | ||
879 | |||
880 | sigprocmask(SIG_SETMASK, NULL, &G.blocked_set); | ||
881 | sig = 0; | ||
882 | while (mask) { | ||
883 | if (mask & 1) | ||
884 | sigaddset(&G.blocked_set, sig); | ||
885 | mask >>= 1; | ||
886 | sig++; | ||
887 | } | ||
888 | sigdelset(&G.blocked_set, SIGCHLD); | ||
889 | sigprocmask(SIG_SETMASK, &G.blocked_set, &G.inherited_set); | ||
890 | } | ||
891 | |||
892 | static int check_and_run_traps(int sig) | 860 | static int check_and_run_traps(int sig) |
893 | { | 861 | { |
894 | static const struct timespec zero_timespec = { 0, 0 }; | 862 | static const struct timespec zero_timespec = { 0, 0 }; |
@@ -934,7 +902,6 @@ static int check_and_run_traps(int sig) | |||
934 | } | 902 | } |
935 | 903 | ||
936 | #if ENABLE_HUSH_JOB | 904 | #if ENABLE_HUSH_JOB |
937 | |||
938 | /* Restores tty foreground process group, and exits. | 905 | /* Restores tty foreground process group, and exits. |
939 | * May be called as signal handler for fatal signal | 906 | * May be called as signal handler for fatal signal |
940 | * (will faithfully resend signal to itself, producing correct exit state) | 907 | * (will faithfully resend signal to itself, producing correct exit state) |
@@ -957,48 +924,6 @@ static void sigexit(int sig) | |||
957 | 924 | ||
958 | kill_myself_with_sig(sig); /* does not return */ | 925 | kill_myself_with_sig(sig); /* does not return */ |
959 | } | 926 | } |
960 | |||
961 | /* helper */ | ||
962 | static void maybe_set_sighandler(int sig) | ||
963 | { | ||
964 | void (*handler)(int); | ||
965 | /* non_DFL_mask'ed signals are, well, masked, | ||
966 | * no need to set handler for them. | ||
967 | */ | ||
968 | if (!((G.non_DFL_mask >> sig) & 1)) { | ||
969 | handler = signal(sig, sigexit); | ||
970 | if (handler == SIG_IGN) /* oops... restore back to IGN! */ | ||
971 | signal(sig, handler); | ||
972 | } | ||
973 | } | ||
974 | /* Used only to set handler to restore pgrp on exit */ | ||
975 | static void set_fatal_signals_to_sigexit(void) | ||
976 | { | ||
977 | if (HUSH_DEBUG) { | ||
978 | maybe_set_sighandler(SIGILL ); | ||
979 | maybe_set_sighandler(SIGFPE ); | ||
980 | maybe_set_sighandler(SIGBUS ); | ||
981 | maybe_set_sighandler(SIGSEGV); | ||
982 | maybe_set_sighandler(SIGTRAP); | ||
983 | } /* else: hush is perfect. what SEGV? */ | ||
984 | |||
985 | maybe_set_sighandler(SIGABRT); | ||
986 | |||
987 | /* bash 3.2 seems to handle these just like 'fatal' ones */ | ||
988 | maybe_set_sighandler(SIGPIPE); | ||
989 | maybe_set_sighandler(SIGALRM); | ||
990 | maybe_set_sighandler(SIGHUP ); | ||
991 | |||
992 | /* if we aren't interactive... but in this case | ||
993 | * we never want to restore pgrp on exit, and this fn is not called */ | ||
994 | /*maybe_set_sighandler(SIGTERM);*/ | ||
995 | /*maybe_set_sighandler(SIGINT );*/ | ||
996 | } | ||
997 | |||
998 | #else /* !JOB */ | ||
999 | |||
1000 | #define set_fatal_signals_to_sigexit(handler) ((void)0) | ||
1001 | |||
1002 | #endif | 927 | #endif |
1003 | 928 | ||
1004 | /* Restores tty foreground process group, and exits. */ | 929 | /* Restores tty foreground process group, and exits. */ |
@@ -1007,6 +932,7 @@ static void hush_exit(int exitcode) | |||
1007 | { | 932 | { |
1008 | if (G.traps && G.traps[0] && G.traps[0][0]) { | 933 | if (G.traps && G.traps[0] && G.traps[0][0]) { |
1009 | char *argv[] = { NULL, xstrdup(G.traps[0]), NULL }; | 934 | char *argv[] = { NULL, xstrdup(G.traps[0]), NULL }; |
935 | //TODO: do we need to prevent recursion? | ||
1010 | builtin_eval(argv); | 936 | builtin_eval(argv); |
1011 | free(argv[1]); | 937 | free(argv[1]); |
1012 | } | 938 | } |
@@ -2896,7 +2822,7 @@ static int run_pipe(struct pipe *pi) | |||
2896 | command->pid = BB_MMU ? fork() : vfork(); | 2822 | command->pid = BB_MMU ? fork() : vfork(); |
2897 | if (!command->pid) { /* child */ | 2823 | if (!command->pid) { /* child */ |
2898 | #if ENABLE_HUSH_JOB | 2824 | #if ENABLE_HUSH_JOB |
2899 | die_sleep = 0; /* let nofork's xfuncs die */ | 2825 | die_sleep = 0; /* do not restore tty pgrp on xfunc death */ |
2900 | 2826 | ||
2901 | /* Every child adds itself to new process group | 2827 | /* Every child adds itself to new process group |
2902 | * with pgid == pid_of_first_child_in_pipe */ | 2828 | * with pgid == pid_of_first_child_in_pipe */ |
@@ -2930,7 +2856,10 @@ static int run_pipe(struct pipe *pi) | |||
2930 | /* pseudo_exec() does not return */ | 2856 | /* pseudo_exec() does not return */ |
2931 | } | 2857 | } |
2932 | 2858 | ||
2933 | /* parent */ | 2859 | /* parent or error */ |
2860 | #if ENABLE_HUSH_JOB | ||
2861 | die_sleep = -1; /* restore tty pgrp on xfunc death */ | ||
2862 | #endif | ||
2934 | #if !BB_MMU | 2863 | #if !BB_MMU |
2935 | /* Clean up after vforked child */ | 2864 | /* Clean up after vforked child */ |
2936 | clean_up_after_re_execute(); | 2865 | clean_up_after_re_execute(); |
@@ -3900,6 +3829,9 @@ static FILE *generate_stream_from_string(const char *s) | |||
3900 | bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork"); | 3829 | bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork"); |
3901 | 3830 | ||
3902 | if (pid == 0) { /* child */ | 3831 | if (pid == 0) { /* child */ |
3832 | #if ENABLE_HUSH_JOB | ||
3833 | die_sleep = 0; /* do not restore tty pgrp on xfunc death */ | ||
3834 | #endif | ||
3903 | /* Process substitution is not considered to be usual | 3835 | /* Process substitution is not considered to be usual |
3904 | * 'command execution'. | 3836 | * 'command execution'. |
3905 | * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. | 3837 | * SUSv3 says ctrl-Z should be ignored, ctrl-C should not. |
@@ -3909,8 +3841,6 @@ static FILE *generate_stream_from_string(const char *s) | |||
3909 | + (1 << SIGTTIN) | 3841 | + (1 << SIGTTIN) |
3910 | + (1 << SIGTTOU) | 3842 | + (1 << SIGTTOU) |
3911 | , SIG_IGN); | 3843 | , SIG_IGN); |
3912 | if (ENABLE_HUSH_JOB) | ||
3913 | die_sleep = 0; /* let nofork's xfuncs die */ | ||
3914 | close(channel[0]); /* NB: close _first_, then move fd! */ | 3844 | close(channel[0]); /* NB: close _first_, then move fd! */ |
3915 | xmove_fd(channel[1], 1); | 3845 | xmove_fd(channel[1], 1); |
3916 | /* Prevent it from trying to handle ctrl-z etc */ | 3846 | /* Prevent it from trying to handle ctrl-z etc */ |
@@ -3920,8 +3850,8 @@ static FILE *generate_stream_from_string(const char *s) | |||
3920 | _exit(G.last_return_code); | 3850 | _exit(G.last_return_code); |
3921 | #else | 3851 | #else |
3922 | /* We re-execute after vfork on NOMMU. This makes this script safe: | 3852 | /* We re-execute after vfork on NOMMU. This makes this script safe: |
3923 | * yes "0123456789012345678901234567890" | dd bs=32 count=64k >TESTFILE | 3853 | * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG |
3924 | * huge=`cat TESTFILE` # was blocking here forever | 3854 | * huge=`cat BIG` # was blocking here forever |
3925 | * echo OK | 3855 | * echo OK |
3926 | */ | 3856 | */ |
3927 | re_execute_shell(s); | 3857 | re_execute_shell(s); |
@@ -3929,6 +3859,9 @@ static FILE *generate_stream_from_string(const char *s) | |||
3929 | } | 3859 | } |
3930 | 3860 | ||
3931 | /* parent */ | 3861 | /* parent */ |
3862 | #if ENABLE_HUSH_JOB | ||
3863 | die_sleep = -1; /* restore tty pgrp on xfunc death */ | ||
3864 | #endif | ||
3932 | clean_up_after_re_execute(); | 3865 | clean_up_after_re_execute(); |
3933 | close(channel[1]); | 3866 | close(channel[1]); |
3934 | pf = fdopen(channel[0], "r"); | 3867 | pf = fdopen(channel[0], "r"); |
@@ -4945,31 +4878,81 @@ static void parse_and_run_file(FILE *f) | |||
4945 | parse_and_run_stream(&input, ';'); | 4878 | parse_and_run_stream(&input, ';'); |
4946 | } | 4879 | } |
4947 | 4880 | ||
4948 | #if ENABLE_HUSH_JOB | 4881 | /* Called a few times only (or even once if "sh -c") */ |
4949 | /* Make sure we have a controlling tty. If we get started under a job | 4882 | static void block_signals(int second_time) |
4950 | * aware app (like bash for example), make sure we are now in charge so | ||
4951 | * we don't fight over who gets the foreground */ | ||
4952 | static void setup_job_control(void) | ||
4953 | { | 4883 | { |
4954 | pid_t shell_pgrp; | 4884 | unsigned sig; |
4885 | unsigned mask; | ||
4955 | 4886 | ||
4956 | shell_pgrp = getpgrp(); | 4887 | mask = (1 << SIGQUIT); |
4888 | if (G_interactive_fd) { | ||
4889 | mask = 0 | ||
4890 | | (1 << SIGQUIT) | ||
4891 | | (1 << SIGTERM) | ||
4892 | | (1 << SIGHUP) | ||
4893 | #if ENABLE_HUSH_JOB | ||
4894 | | (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP) | ||
4895 | #endif | ||
4896 | | (1 << SIGINT) | ||
4897 | ; | ||
4898 | } | ||
4899 | G.non_DFL_mask = mask; | ||
4957 | 4900 | ||
4958 | /* If we were ran as 'hush &', | 4901 | if (!second_time) |
4959 | * sleep until we are in the foreground. */ | 4902 | sigprocmask(SIG_SETMASK, NULL, &G.blocked_set); |
4960 | while (tcgetpgrp(G_interactive_fd) != shell_pgrp) { | 4903 | sig = 0; |
4961 | /* Send TTIN to ourself (should stop us) */ | 4904 | while (mask) { |
4962 | kill(- shell_pgrp, SIGTTIN); | 4905 | if (mask & 1) |
4963 | shell_pgrp = getpgrp(); | 4906 | sigaddset(&G.blocked_set, sig); |
4907 | mask >>= 1; | ||
4908 | sig++; | ||
4964 | } | 4909 | } |
4910 | sigdelset(&G.blocked_set, SIGCHLD); | ||
4965 | 4911 | ||
4966 | /* We _must_ restore tty pgrp on fatal signals */ | 4912 | sigprocmask(SIG_SETMASK, &G.blocked_set, |
4967 | set_fatal_signals_to_sigexit(); | 4913 | second_time ? NULL : &G.inherited_set); |
4914 | /* POSIX allows shell to re-enable SIGCHLD | ||
4915 | * even if it was SIG_IGN on entry */ | ||
4916 | // G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | ||
4917 | if (!second_time) | ||
4918 | signal(SIGCHLD, SIG_DFL); // SIGCHLD_handler); | ||
4919 | } | ||
4968 | 4920 | ||
4969 | /* Put ourselves in our own process group. */ | 4921 | #if ENABLE_HUSH_JOB |
4970 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ | 4922 | /* helper */ |
4971 | /* Grab control of the terminal. */ | 4923 | static void maybe_set_to_sigexit(int sig) |
4972 | tcsetpgrp(G_interactive_fd, getpid()); | 4924 | { |
4925 | void (*handler)(int); | ||
4926 | /* non_DFL_mask'ed signals are, well, masked, | ||
4927 | * no need to set handler for them. | ||
4928 | */ | ||
4929 | if (!((G.non_DFL_mask >> sig) & 1)) { | ||
4930 | handler = signal(sig, sigexit); | ||
4931 | if (handler == SIG_IGN) /* oops... restore back to IGN! */ | ||
4932 | signal(sig, handler); | ||
4933 | } | ||
4934 | } | ||
4935 | /* Set handlers to restore tty pgrm and exit */ | ||
4936 | static void set_fatal_handlers(void) | ||
4937 | { | ||
4938 | /* We _must_ restore tty pgrp on fatal signals */ | ||
4939 | if (HUSH_DEBUG) { | ||
4940 | maybe_set_to_sigexit(SIGILL ); | ||
4941 | maybe_set_to_sigexit(SIGFPE ); | ||
4942 | maybe_set_to_sigexit(SIGBUS ); | ||
4943 | maybe_set_to_sigexit(SIGSEGV); | ||
4944 | maybe_set_to_sigexit(SIGTRAP); | ||
4945 | } /* else: hush is perfect. what SEGV? */ | ||
4946 | maybe_set_to_sigexit(SIGABRT); | ||
4947 | /* bash 3.2 seems to handle these just like 'fatal' ones */ | ||
4948 | maybe_set_to_sigexit(SIGPIPE); | ||
4949 | maybe_set_to_sigexit(SIGALRM); | ||
4950 | maybe_set_to_sigexit(SIGHUP ); | ||
4951 | /* if we are interactive, SIGTERM and SIGINT are masked. | ||
4952 | * if we aren't interactive... but in this case | ||
4953 | * we never want to restore pgrp on exit, and this fn is not called */ | ||
4954 | /*maybe_set_to_sigexit(SIGTERM);*/ | ||
4955 | /*maybe_set_to_sigexit(SIGINT );*/ | ||
4973 | } | 4956 | } |
4974 | #endif | 4957 | #endif |
4975 | 4958 | ||
@@ -4994,7 +4977,7 @@ int hush_main(int argc, char **argv) | |||
4994 | .flg_export = 1, | 4977 | .flg_export = 1, |
4995 | .flg_read_only = 1, | 4978 | .flg_read_only = 1, |
4996 | }; | 4979 | }; |
4997 | 4980 | int signal_mask_is_inited = 0; | |
4998 | int opt; | 4981 | int opt; |
4999 | char **e; | 4982 | char **e; |
5000 | struct variable *cur_var; | 4983 | struct variable *cur_var; |
@@ -5060,6 +5043,7 @@ int hush_main(int argc, char **argv) | |||
5060 | optind--; | 5043 | optind--; |
5061 | } /* else -c 'script' PAR0 PAR1: $0 is PAR0 */ | 5044 | } /* else -c 'script' PAR0 PAR1: $0 is PAR0 */ |
5062 | G.global_argc = argc - optind; | 5045 | G.global_argc = argc - optind; |
5046 | block_signals(0); /* 0: called 1st time */ | ||
5063 | parse_and_run_string(optarg); | 5047 | parse_and_run_string(optarg); |
5064 | goto final_return; | 5048 | goto final_return; |
5065 | case 'i': | 5049 | case 'i': |
@@ -5104,10 +5088,12 @@ int hush_main(int argc, char **argv) | |||
5104 | bb_show_usage(); | 5088 | bb_show_usage(); |
5105 | #endif | 5089 | #endif |
5106 | } | 5090 | } |
5107 | } | 5091 | } /* option parsing loop */ |
5108 | 5092 | ||
5109 | if (!G.root_pid) | 5093 | if (!G.root_pid) |
5110 | G.root_pid = getpid(); | 5094 | G.root_pid = getpid(); |
5095 | |||
5096 | /* If we are login shell... */ | ||
5111 | if (argv[0] && argv[0][0] == '-') { | 5097 | if (argv[0] && argv[0][0] == '-') { |
5112 | FILE *input; | 5098 | FILE *input; |
5113 | /* XXX what should argv be while sourcing /etc/profile? */ | 5099 | /* XXX what should argv be while sourcing /etc/profile? */ |
@@ -5115,26 +5101,57 @@ int hush_main(int argc, char **argv) | |||
5115 | input = fopen_for_read("/etc/profile"); | 5101 | input = fopen_for_read("/etc/profile"); |
5116 | if (input != NULL) { | 5102 | if (input != NULL) { |
5117 | close_on_exec_on(fileno(input)); | 5103 | close_on_exec_on(fileno(input)); |
5104 | block_signals(0); /* 0: called 1st time */ | ||
5105 | signal_mask_is_inited = 1; | ||
5118 | parse_and_run_file(input); | 5106 | parse_and_run_file(input); |
5119 | fclose(input); | 5107 | fclose(input); |
5120 | } | 5108 | } |
5109 | /* bash: after sourcing /etc/profile, | ||
5110 | * tries to source (in the given order): | ||
5111 | * ~/.bash_profile, ~/.bash_login, ~/.profile, | ||
5112 | * stopping of first found. --noprofile turns this off. | ||
5113 | * bash also sources ~/.bash_logout on exit. | ||
5114 | * If called as sh, skips .bash_XXX files. | ||
5115 | */ | ||
5121 | } | 5116 | } |
5122 | 5117 | ||
5123 | #if ENABLE_HUSH_JOB | 5118 | if (argv[optind]) { |
5119 | FILE *input; | ||
5120 | /* | ||
5121 | * Non-interactive "bash <script>" sources $BASH_ENV here | ||
5122 | * (without scanning $PATH). | ||
5123 | * If called as sh, does the same but with $ENV. | ||
5124 | */ | ||
5125 | debug_printf("running script '%s'\n", argv[optind]); | ||
5126 | G.global_argv = argv + optind; | ||
5127 | G.global_argc = argc - optind; | ||
5128 | input = xfopen_for_read(argv[optind]); | ||
5129 | close_on_exec_on(fileno(input)); | ||
5130 | if (!signal_mask_is_inited) | ||
5131 | block_signals(0); /* 0: called 1st time */ | ||
5132 | parse_and_run_file(input); | ||
5133 | #if ENABLE_FEATURE_CLEAN_UP | ||
5134 | fclose(input); | ||
5135 | #endif | ||
5136 | goto final_return; | ||
5137 | } | ||
5138 | |||
5139 | /* Up to here, shell was non-interactive. Now it may become one. */ | ||
5140 | |||
5124 | /* A shell is interactive if the '-i' flag was given, or if all of | 5141 | /* A shell is interactive if the '-i' flag was given, or if all of |
5125 | * the following conditions are met: | 5142 | * the following conditions are met: |
5126 | * no -c command | 5143 | * no -c command |
5127 | * no arguments remaining or the -s flag given | 5144 | * no arguments remaining or the -s flag given |
5128 | * standard input is a terminal | 5145 | * standard input is a terminal |
5129 | * standard output is a terminal | 5146 | * standard output is a terminal |
5130 | * Refer to Posix.2, the description of the 'sh' utility. */ | 5147 | * Refer to Posix.2, the description of the 'sh' utility. |
5131 | if (argv[optind] == NULL | 5148 | */ |
5132 | && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) | 5149 | #if ENABLE_HUSH_JOB |
5133 | ) { | 5150 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { |
5134 | G.saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); | 5151 | G.saved_tty_pgrp = tcgetpgrp(STDIN_FILENO); |
5135 | debug_printf("saved_tty_pgrp=%d\n", G.saved_tty_pgrp); | 5152 | debug_printf("saved_tty_pgrp:%d\n", G.saved_tty_pgrp); |
5136 | if (G.saved_tty_pgrp >= 0) { | 5153 | if (G.saved_tty_pgrp >= 0) { |
5137 | /* try to dup to high fd#, >= 255 */ | 5154 | /* try to dup stdin to high fd#, >= 255 */ |
5138 | G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); | 5155 | G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); |
5139 | if (G_interactive_fd < 0) { | 5156 | if (G_interactive_fd < 0) { |
5140 | /* try to dup to any fd */ | 5157 | /* try to dup to any fd */ |
@@ -5147,12 +5164,35 @@ int hush_main(int argc, char **argv) | |||
5147 | // to (inadvertently) close/redirect it | 5164 | // to (inadvertently) close/redirect it |
5148 | } | 5165 | } |
5149 | } | 5166 | } |
5150 | init_signal_mask(); /* note: ensures SIGCHLD is not masked */ | 5167 | debug_printf("interactive_fd:%d\n", G_interactive_fd); |
5151 | debug_printf("interactive_fd=%d\n", G_interactive_fd); | ||
5152 | if (G_interactive_fd) { | 5168 | if (G_interactive_fd) { |
5153 | fcntl(G_interactive_fd, F_SETFD, FD_CLOEXEC); | 5169 | pid_t shell_pgrp; |
5154 | /* Looks like they want an interactive shell */ | 5170 | |
5155 | setup_job_control(); | 5171 | /* We are indeed interactive shell, and we will perform |
5172 | * job control. Setting up for that. */ | ||
5173 | |||
5174 | close_on_exec_on(G_interactive_fd); | ||
5175 | /* If we were run as 'hush &', sleep until we are | ||
5176 | * in the foreground (tty pgrp == our pgrp). | ||
5177 | * If we get started under a job aware app (like bash), | ||
5178 | * make sure we are now in charge so we don't fight over | ||
5179 | * who gets the foreground */ | ||
5180 | while (1) { | ||
5181 | shell_pgrp = getpgrp(); | ||
5182 | G.saved_tty_pgrp = tcgetpgrp(G_interactive_fd); | ||
5183 | if (G.saved_tty_pgrp == shell_pgrp) | ||
5184 | break; | ||
5185 | /* send TTIN to ourself (should stop us) */ | ||
5186 | kill(- shell_pgrp, SIGTTIN); | ||
5187 | } | ||
5188 | /* Block some signals */ | ||
5189 | block_signals(signal_mask_is_inited); | ||
5190 | /* Set other signals to restore saved_tty_pgrp */ | ||
5191 | set_fatal_handlers(); | ||
5192 | /* Put ourselves in our own process group */ | ||
5193 | bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */ | ||
5194 | /* Grab control of the terminal */ | ||
5195 | tcsetpgrp(G_interactive_fd, getpid()); | ||
5156 | /* -1 is special - makes xfuncs longjmp, not exit | 5196 | /* -1 is special - makes xfuncs longjmp, not exit |
5157 | * (we reset die_sleep = 0 whereever we [v]fork) */ | 5197 | * (we reset die_sleep = 0 whereever we [v]fork) */ |
5158 | die_sleep = -1; | 5198 | die_sleep = -1; |
@@ -5160,12 +5200,12 @@ int hush_main(int argc, char **argv) | |||
5160 | /* xfunc has failed! die die die */ | 5200 | /* xfunc has failed! die die die */ |
5161 | hush_exit(xfunc_error_retval); | 5201 | hush_exit(xfunc_error_retval); |
5162 | } | 5202 | } |
5163 | } | 5203 | } else if (!signal_mask_is_inited) { |
5204 | block_signals(0); /* 0: called 1st time */ | ||
5205 | } /* else: block_signals(0) was done before */ | ||
5164 | #elif ENABLE_HUSH_INTERACTIVE | 5206 | #elif ENABLE_HUSH_INTERACTIVE |
5165 | /* no job control compiled, only prompt/line editing */ | 5207 | /* No job control compiled in, only prompt/line editing */ |
5166 | if (argv[optind] == NULL | 5208 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) { |
5167 | && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) | ||
5168 | ) { | ||
5169 | G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); | 5209 | G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255); |
5170 | if (G_interactive_fd < 0) { | 5210 | if (G_interactive_fd < 0) { |
5171 | /* try to dup to any fd */ | 5211 | /* try to dup to any fd */ |
@@ -5174,45 +5214,32 @@ int hush_main(int argc, char **argv) | |||
5174 | /* give up */ | 5214 | /* give up */ |
5175 | G_interactive_fd = 0; | 5215 | G_interactive_fd = 0; |
5176 | } | 5216 | } |
5177 | if (G_interactive_fd) { | ||
5178 | fcntl(G_interactive_fd, F_SETFD, FD_CLOEXEC); | ||
5179 | } | ||
5180 | } | 5217 | } |
5181 | init_signal_mask(); /* note: ensures SIGCHLD is not masked */ | 5218 | if (G_interactive_fd) { |
5219 | close_on_exec_on(G_interactive_fd); | ||
5220 | block_signals(signal_mask_is_inited); | ||
5221 | } else if (!signal_mask_is_inited) { | ||
5222 | block_signals(0); | ||
5223 | } | ||
5182 | #else | 5224 | #else |
5183 | //TODO: we didn't do it for -c or /etc/profile! Shouldn't we? | 5225 | /* We have interactiveness code disabled */ |
5184 | init_signal_mask(); | 5226 | if (!signal_mask_is_inited) { |
5227 | block_signals(0); | ||
5228 | } | ||
5185 | #endif | 5229 | #endif |
5186 | /* POSIX allows shell to re-enable SIGCHLD | 5230 | /* bash: |
5187 | * even if it was SIG_IGN on entry */ | 5231 | * if interactive but not a login shell, sources ~/.bashrc |
5188 | //TODO: we didn't do it for -c or /etc/profile! Shouldn't we? | 5232 | * (--norc turns this off, --rcfile <file> overrides) |
5189 | // G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */ | 5233 | */ |
5190 | signal(SIGCHLD, SIG_DFL); // SIGCHLD_handler); | ||
5191 | 5234 | ||
5192 | #if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_SH_EXTRA_QUIET | 5235 | if (!ENABLE_FEATURE_SH_EXTRA_QUIET && G_interactive_fd) { |
5193 | if (G_interactive_fd) { | ||
5194 | printf("\n\n%s hush - the humble shell v"HUSH_VER_STR"\n", bb_banner); | 5236 | printf("\n\n%s hush - the humble shell v"HUSH_VER_STR"\n", bb_banner); |
5195 | printf("Enter 'help' for a list of built-in commands.\n\n"); | 5237 | printf("Enter 'help' for a list of built-in commands.\n\n"); |
5196 | } | 5238 | } |
5197 | #endif | ||
5198 | 5239 | ||
5199 | if (argv[optind] == NULL) { | 5240 | parse_and_run_file(stdin); |
5200 | parse_and_run_file(stdin); | ||
5201 | } else { | ||
5202 | FILE *input; | ||
5203 | debug_printf("\nrunning script '%s'\n", argv[optind]); | ||
5204 | G.global_argv = argv + optind; | ||
5205 | G.global_argc = argc - optind; | ||
5206 | input = xfopen_for_read(argv[optind]); | ||
5207 | fcntl(fileno(input), F_SETFD, FD_CLOEXEC); | ||
5208 | parse_and_run_file(input); | ||
5209 | #if ENABLE_FEATURE_CLEAN_UP | ||
5210 | fclose(input); | ||
5211 | #endif | ||
5212 | } | ||
5213 | 5241 | ||
5214 | final_return: | 5242 | final_return: |
5215 | |||
5216 | #if ENABLE_FEATURE_CLEAN_UP | 5243 | #if ENABLE_FEATURE_CLEAN_UP |
5217 | if (G.cwd != bb_msg_unknown) | 5244 | if (G.cwd != bb_msg_unknown) |
5218 | free((char*)G.cwd); | 5245 | free((char*)G.cwd); |