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
This commit is contained in:
parent
46f9b6db80
commit
f937528571
319
shell/hush.c
319
shell/hush.c
@ -850,8 +850,6 @@ static void free_strings(char **strings)
|
|||||||
* Note: as a result, we do not use signal handlers much. The only uses
|
* Note: as a result, we do not use signal handlers much. The only uses
|
||||||
* are to count SIGCHLDs [disabled - bug somewhere, + bloat]
|
* are to count SIGCHLDs [disabled - bug somewhere, + bloat]
|
||||||
* and to restore tty pgrp on signal-induced exit.
|
* and to restore tty pgrp on signal-induced exit.
|
||||||
*
|
|
||||||
* TODO: check/fix wait builtin to be interruptible.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//static void SIGCHLD_handler(int sig UNUSED_PARAM)
|
//static void SIGCHLD_handler(int sig UNUSED_PARAM)
|
||||||
@ -859,36 +857,6 @@ static void free_strings(char **strings)
|
|||||||
// G.count_SIGCHLD++;
|
// G.count_SIGCHLD++;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
/* called once at shell init */
|
|
||||||
static void init_signal_mask(void)
|
|
||||||
{
|
|
||||||
unsigned sig;
|
|
||||||
unsigned mask = (1 << SIGQUIT);
|
|
||||||
if (G_interactive_fd) {
|
|
||||||
mask = 0
|
|
||||||
| (1 << SIGQUIT)
|
|
||||||
| (1 << SIGTERM)
|
|
||||||
| (1 << SIGHUP)
|
|
||||||
#if ENABLE_HUSH_JOB
|
|
||||||
| (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP)
|
|
||||||
#endif
|
|
||||||
| (1 << SIGINT)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
G.non_DFL_mask = mask;
|
|
||||||
|
|
||||||
sigprocmask(SIG_SETMASK, NULL, &G.blocked_set);
|
|
||||||
sig = 0;
|
|
||||||
while (mask) {
|
|
||||||
if (mask & 1)
|
|
||||||
sigaddset(&G.blocked_set, sig);
|
|
||||||
mask >>= 1;
|
|
||||||
sig++;
|
|
||||||
}
|
|
||||||
sigdelset(&G.blocked_set, SIGCHLD);
|
|
||||||
sigprocmask(SIG_SETMASK, &G.blocked_set, &G.inherited_set);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int check_and_run_traps(int sig)
|
static int check_and_run_traps(int sig)
|
||||||
{
|
{
|
||||||
static const struct timespec zero_timespec = { 0, 0 };
|
static const struct timespec zero_timespec = { 0, 0 };
|
||||||
@ -934,7 +902,6 @@ static int check_and_run_traps(int sig)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
|
|
||||||
/* Restores tty foreground process group, and exits.
|
/* Restores tty foreground process group, and exits.
|
||||||
* May be called as signal handler for fatal signal
|
* May be called as signal handler for fatal signal
|
||||||
* (will faithfully resend signal to itself, producing correct exit state)
|
* (will faithfully resend signal to itself, producing correct exit state)
|
||||||
@ -957,48 +924,6 @@ static void sigexit(int sig)
|
|||||||
|
|
||||||
kill_myself_with_sig(sig); /* does not return */
|
kill_myself_with_sig(sig); /* does not return */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* helper */
|
|
||||||
static void maybe_set_sighandler(int sig)
|
|
||||||
{
|
|
||||||
void (*handler)(int);
|
|
||||||
/* non_DFL_mask'ed signals are, well, masked,
|
|
||||||
* no need to set handler for them.
|
|
||||||
*/
|
|
||||||
if (!((G.non_DFL_mask >> sig) & 1)) {
|
|
||||||
handler = signal(sig, sigexit);
|
|
||||||
if (handler == SIG_IGN) /* oops... restore back to IGN! */
|
|
||||||
signal(sig, handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Used only to set handler to restore pgrp on exit */
|
|
||||||
static void set_fatal_signals_to_sigexit(void)
|
|
||||||
{
|
|
||||||
if (HUSH_DEBUG) {
|
|
||||||
maybe_set_sighandler(SIGILL );
|
|
||||||
maybe_set_sighandler(SIGFPE );
|
|
||||||
maybe_set_sighandler(SIGBUS );
|
|
||||||
maybe_set_sighandler(SIGSEGV);
|
|
||||||
maybe_set_sighandler(SIGTRAP);
|
|
||||||
} /* else: hush is perfect. what SEGV? */
|
|
||||||
|
|
||||||
maybe_set_sighandler(SIGABRT);
|
|
||||||
|
|
||||||
/* bash 3.2 seems to handle these just like 'fatal' ones */
|
|
||||||
maybe_set_sighandler(SIGPIPE);
|
|
||||||
maybe_set_sighandler(SIGALRM);
|
|
||||||
maybe_set_sighandler(SIGHUP );
|
|
||||||
|
|
||||||
/* if we aren't interactive... but in this case
|
|
||||||
* we never want to restore pgrp on exit, and this fn is not called */
|
|
||||||
/*maybe_set_sighandler(SIGTERM);*/
|
|
||||||
/*maybe_set_sighandler(SIGINT );*/
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* !JOB */
|
|
||||||
|
|
||||||
#define set_fatal_signals_to_sigexit(handler) ((void)0)
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Restores tty foreground process group, and exits. */
|
/* Restores tty foreground process group, and exits. */
|
||||||
@ -1007,6 +932,7 @@ static void hush_exit(int exitcode)
|
|||||||
{
|
{
|
||||||
if (G.traps && G.traps[0] && G.traps[0][0]) {
|
if (G.traps && G.traps[0] && G.traps[0][0]) {
|
||||||
char *argv[] = { NULL, xstrdup(G.traps[0]), NULL };
|
char *argv[] = { NULL, xstrdup(G.traps[0]), NULL };
|
||||||
|
//TODO: do we need to prevent recursion?
|
||||||
builtin_eval(argv);
|
builtin_eval(argv);
|
||||||
free(argv[1]);
|
free(argv[1]);
|
||||||
}
|
}
|
||||||
@ -2896,7 +2822,7 @@ static int run_pipe(struct pipe *pi)
|
|||||||
command->pid = BB_MMU ? fork() : vfork();
|
command->pid = BB_MMU ? fork() : vfork();
|
||||||
if (!command->pid) { /* child */
|
if (!command->pid) { /* child */
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
die_sleep = 0; /* let nofork's xfuncs die */
|
die_sleep = 0; /* do not restore tty pgrp on xfunc death */
|
||||||
|
|
||||||
/* Every child adds itself to new process group
|
/* Every child adds itself to new process group
|
||||||
* with pgid == pid_of_first_child_in_pipe */
|
* with pgid == pid_of_first_child_in_pipe */
|
||||||
@ -2930,7 +2856,10 @@ static int run_pipe(struct pipe *pi)
|
|||||||
/* pseudo_exec() does not return */
|
/* pseudo_exec() does not return */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* parent */
|
/* parent or error */
|
||||||
|
#if ENABLE_HUSH_JOB
|
||||||
|
die_sleep = -1; /* restore tty pgrp on xfunc death */
|
||||||
|
#endif
|
||||||
#if !BB_MMU
|
#if !BB_MMU
|
||||||
/* Clean up after vforked child */
|
/* Clean up after vforked child */
|
||||||
clean_up_after_re_execute();
|
clean_up_after_re_execute();
|
||||||
@ -3900,6 +3829,9 @@ static FILE *generate_stream_from_string(const char *s)
|
|||||||
bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
|
bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork");
|
||||||
|
|
||||||
if (pid == 0) { /* child */
|
if (pid == 0) { /* child */
|
||||||
|
#if ENABLE_HUSH_JOB
|
||||||
|
die_sleep = 0; /* do not restore tty pgrp on xfunc death */
|
||||||
|
#endif
|
||||||
/* Process substitution is not considered to be usual
|
/* Process substitution is not considered to be usual
|
||||||
* 'command execution'.
|
* 'command execution'.
|
||||||
* SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
|
* SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
|
||||||
@ -3909,8 +3841,6 @@ static FILE *generate_stream_from_string(const char *s)
|
|||||||
+ (1 << SIGTTIN)
|
+ (1 << SIGTTIN)
|
||||||
+ (1 << SIGTTOU)
|
+ (1 << SIGTTOU)
|
||||||
, SIG_IGN);
|
, SIG_IGN);
|
||||||
if (ENABLE_HUSH_JOB)
|
|
||||||
die_sleep = 0; /* let nofork's xfuncs die */
|
|
||||||
close(channel[0]); /* NB: close _first_, then move fd! */
|
close(channel[0]); /* NB: close _first_, then move fd! */
|
||||||
xmove_fd(channel[1], 1);
|
xmove_fd(channel[1], 1);
|
||||||
/* Prevent it from trying to handle ctrl-z etc */
|
/* Prevent it from trying to handle ctrl-z etc */
|
||||||
@ -3920,8 +3850,8 @@ static FILE *generate_stream_from_string(const char *s)
|
|||||||
_exit(G.last_return_code);
|
_exit(G.last_return_code);
|
||||||
#else
|
#else
|
||||||
/* We re-execute after vfork on NOMMU. This makes this script safe:
|
/* We re-execute after vfork on NOMMU. This makes this script safe:
|
||||||
* yes "0123456789012345678901234567890" | dd bs=32 count=64k >TESTFILE
|
* yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
|
||||||
* huge=`cat TESTFILE` # was blocking here forever
|
* huge=`cat BIG` # was blocking here forever
|
||||||
* echo OK
|
* echo OK
|
||||||
*/
|
*/
|
||||||
re_execute_shell(s);
|
re_execute_shell(s);
|
||||||
@ -3929,6 +3859,9 @@ static FILE *generate_stream_from_string(const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* parent */
|
/* parent */
|
||||||
|
#if ENABLE_HUSH_JOB
|
||||||
|
die_sleep = -1; /* restore tty pgrp on xfunc death */
|
||||||
|
#endif
|
||||||
clean_up_after_re_execute();
|
clean_up_after_re_execute();
|
||||||
close(channel[1]);
|
close(channel[1]);
|
||||||
pf = fdopen(channel[0], "r");
|
pf = fdopen(channel[0], "r");
|
||||||
@ -4945,31 +4878,81 @@ static void parse_and_run_file(FILE *f)
|
|||||||
parse_and_run_stream(&input, ';');
|
parse_and_run_stream(&input, ';');
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_HUSH_JOB
|
/* Called a few times only (or even once if "sh -c") */
|
||||||
/* Make sure we have a controlling tty. If we get started under a job
|
static void block_signals(int second_time)
|
||||||
* aware app (like bash for example), make sure we are now in charge so
|
|
||||||
* we don't fight over who gets the foreground */
|
|
||||||
static void setup_job_control(void)
|
|
||||||
{
|
{
|
||||||
pid_t shell_pgrp;
|
unsigned sig;
|
||||||
|
unsigned mask;
|
||||||
|
|
||||||
shell_pgrp = getpgrp();
|
mask = (1 << SIGQUIT);
|
||||||
|
if (G_interactive_fd) {
|
||||||
|
mask = 0
|
||||||
|
| (1 << SIGQUIT)
|
||||||
|
| (1 << SIGTERM)
|
||||||
|
| (1 << SIGHUP)
|
||||||
|
#if ENABLE_HUSH_JOB
|
||||||
|
| (1 << SIGTTIN) | (1 << SIGTTOU) | (1 << SIGTSTP)
|
||||||
|
#endif
|
||||||
|
| (1 << SIGINT)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
G.non_DFL_mask = mask;
|
||||||
|
|
||||||
/* If we were ran as 'hush &',
|
if (!second_time)
|
||||||
* sleep until we are in the foreground. */
|
sigprocmask(SIG_SETMASK, NULL, &G.blocked_set);
|
||||||
while (tcgetpgrp(G_interactive_fd) != shell_pgrp) {
|
sig = 0;
|
||||||
/* Send TTIN to ourself (should stop us) */
|
while (mask) {
|
||||||
kill(- shell_pgrp, SIGTTIN);
|
if (mask & 1)
|
||||||
shell_pgrp = getpgrp();
|
sigaddset(&G.blocked_set, sig);
|
||||||
|
mask >>= 1;
|
||||||
|
sig++;
|
||||||
|
}
|
||||||
|
sigdelset(&G.blocked_set, SIGCHLD);
|
||||||
|
|
||||||
|
sigprocmask(SIG_SETMASK, &G.blocked_set,
|
||||||
|
second_time ? NULL : &G.inherited_set);
|
||||||
|
/* POSIX allows shell to re-enable SIGCHLD
|
||||||
|
* even if it was SIG_IGN on entry */
|
||||||
|
// G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
|
||||||
|
if (!second_time)
|
||||||
|
signal(SIGCHLD, SIG_DFL); // SIGCHLD_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_HUSH_JOB
|
||||||
|
/* helper */
|
||||||
|
static void maybe_set_to_sigexit(int sig)
|
||||||
|
{
|
||||||
|
void (*handler)(int);
|
||||||
|
/* non_DFL_mask'ed signals are, well, masked,
|
||||||
|
* no need to set handler for them.
|
||||||
|
*/
|
||||||
|
if (!((G.non_DFL_mask >> sig) & 1)) {
|
||||||
|
handler = signal(sig, sigexit);
|
||||||
|
if (handler == SIG_IGN) /* oops... restore back to IGN! */
|
||||||
|
signal(sig, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Set handlers to restore tty pgrm and exit */
|
||||||
|
static void set_fatal_handlers(void)
|
||||||
|
{
|
||||||
/* We _must_ restore tty pgrp on fatal signals */
|
/* We _must_ restore tty pgrp on fatal signals */
|
||||||
set_fatal_signals_to_sigexit();
|
if (HUSH_DEBUG) {
|
||||||
|
maybe_set_to_sigexit(SIGILL );
|
||||||
/* Put ourselves in our own process group. */
|
maybe_set_to_sigexit(SIGFPE );
|
||||||
bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
|
maybe_set_to_sigexit(SIGBUS );
|
||||||
/* Grab control of the terminal. */
|
maybe_set_to_sigexit(SIGSEGV);
|
||||||
tcsetpgrp(G_interactive_fd, getpid());
|
maybe_set_to_sigexit(SIGTRAP);
|
||||||
|
} /* else: hush is perfect. what SEGV? */
|
||||||
|
maybe_set_to_sigexit(SIGABRT);
|
||||||
|
/* bash 3.2 seems to handle these just like 'fatal' ones */
|
||||||
|
maybe_set_to_sigexit(SIGPIPE);
|
||||||
|
maybe_set_to_sigexit(SIGALRM);
|
||||||
|
maybe_set_to_sigexit(SIGHUP );
|
||||||
|
/* if we are interactive, SIGTERM and SIGINT are masked.
|
||||||
|
* if we aren't interactive... but in this case
|
||||||
|
* we never want to restore pgrp on exit, and this fn is not called */
|
||||||
|
/*maybe_set_to_sigexit(SIGTERM);*/
|
||||||
|
/*maybe_set_to_sigexit(SIGINT );*/
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -4994,7 +4977,7 @@ int hush_main(int argc, char **argv)
|
|||||||
.flg_export = 1,
|
.flg_export = 1,
|
||||||
.flg_read_only = 1,
|
.flg_read_only = 1,
|
||||||
};
|
};
|
||||||
|
int signal_mask_is_inited = 0;
|
||||||
int opt;
|
int opt;
|
||||||
char **e;
|
char **e;
|
||||||
struct variable *cur_var;
|
struct variable *cur_var;
|
||||||
@ -5060,6 +5043,7 @@ int hush_main(int argc, char **argv)
|
|||||||
optind--;
|
optind--;
|
||||||
} /* else -c 'script' PAR0 PAR1: $0 is PAR0 */
|
} /* else -c 'script' PAR0 PAR1: $0 is PAR0 */
|
||||||
G.global_argc = argc - optind;
|
G.global_argc = argc - optind;
|
||||||
|
block_signals(0); /* 0: called 1st time */
|
||||||
parse_and_run_string(optarg);
|
parse_and_run_string(optarg);
|
||||||
goto final_return;
|
goto final_return;
|
||||||
case 'i':
|
case 'i':
|
||||||
@ -5104,10 +5088,12 @@ int hush_main(int argc, char **argv)
|
|||||||
bb_show_usage();
|
bb_show_usage();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
} /* option parsing loop */
|
||||||
|
|
||||||
if (!G.root_pid)
|
if (!G.root_pid)
|
||||||
G.root_pid = getpid();
|
G.root_pid = getpid();
|
||||||
|
|
||||||
|
/* If we are login shell... */
|
||||||
if (argv[0] && argv[0][0] == '-') {
|
if (argv[0] && argv[0][0] == '-') {
|
||||||
FILE *input;
|
FILE *input;
|
||||||
/* XXX what should argv be while sourcing /etc/profile? */
|
/* XXX what should argv be while sourcing /etc/profile? */
|
||||||
@ -5115,26 +5101,57 @@ int hush_main(int argc, char **argv)
|
|||||||
input = fopen_for_read("/etc/profile");
|
input = fopen_for_read("/etc/profile");
|
||||||
if (input != NULL) {
|
if (input != NULL) {
|
||||||
close_on_exec_on(fileno(input));
|
close_on_exec_on(fileno(input));
|
||||||
|
block_signals(0); /* 0: called 1st time */
|
||||||
|
signal_mask_is_inited = 1;
|
||||||
parse_and_run_file(input);
|
parse_and_run_file(input);
|
||||||
fclose(input);
|
fclose(input);
|
||||||
}
|
}
|
||||||
|
/* bash: after sourcing /etc/profile,
|
||||||
|
* tries to source (in the given order):
|
||||||
|
* ~/.bash_profile, ~/.bash_login, ~/.profile,
|
||||||
|
* stopping of first found. --noprofile turns this off.
|
||||||
|
* bash also sources ~/.bash_logout on exit.
|
||||||
|
* If called as sh, skips .bash_XXX files.
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_HUSH_JOB
|
if (argv[optind]) {
|
||||||
|
FILE *input;
|
||||||
|
/*
|
||||||
|
* Non-interactive "bash <script>" sources $BASH_ENV here
|
||||||
|
* (without scanning $PATH).
|
||||||
|
* If called as sh, does the same but with $ENV.
|
||||||
|
*/
|
||||||
|
debug_printf("running script '%s'\n", argv[optind]);
|
||||||
|
G.global_argv = argv + optind;
|
||||||
|
G.global_argc = argc - optind;
|
||||||
|
input = xfopen_for_read(argv[optind]);
|
||||||
|
close_on_exec_on(fileno(input));
|
||||||
|
if (!signal_mask_is_inited)
|
||||||
|
block_signals(0); /* 0: called 1st time */
|
||||||
|
parse_and_run_file(input);
|
||||||
|
#if ENABLE_FEATURE_CLEAN_UP
|
||||||
|
fclose(input);
|
||||||
|
#endif
|
||||||
|
goto final_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Up to here, shell was non-interactive. Now it may become one. */
|
||||||
|
|
||||||
/* A shell is interactive if the '-i' flag was given, or if all of
|
/* A shell is interactive if the '-i' flag was given, or if all of
|
||||||
* the following conditions are met:
|
* the following conditions are met:
|
||||||
* no -c command
|
* no -c command
|
||||||
* no arguments remaining or the -s flag given
|
* no arguments remaining or the -s flag given
|
||||||
* standard input is a terminal
|
* standard input is a terminal
|
||||||
* standard output is a terminal
|
* standard output is a terminal
|
||||||
* Refer to Posix.2, the description of the 'sh' utility. */
|
* Refer to Posix.2, the description of the 'sh' utility.
|
||||||
if (argv[optind] == NULL
|
*/
|
||||||
&& isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
|
#if ENABLE_HUSH_JOB
|
||||||
) {
|
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
|
||||||
G.saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
|
G.saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
|
||||||
debug_printf("saved_tty_pgrp=%d\n", G.saved_tty_pgrp);
|
debug_printf("saved_tty_pgrp:%d\n", G.saved_tty_pgrp);
|
||||||
if (G.saved_tty_pgrp >= 0) {
|
if (G.saved_tty_pgrp >= 0) {
|
||||||
/* try to dup to high fd#, >= 255 */
|
/* try to dup stdin to high fd#, >= 255 */
|
||||||
G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
|
G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
|
||||||
if (G_interactive_fd < 0) {
|
if (G_interactive_fd < 0) {
|
||||||
/* try to dup to any fd */
|
/* try to dup to any fd */
|
||||||
@ -5147,12 +5164,35 @@ int hush_main(int argc, char **argv)
|
|||||||
// to (inadvertently) close/redirect it
|
// to (inadvertently) close/redirect it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
init_signal_mask(); /* note: ensures SIGCHLD is not masked */
|
debug_printf("interactive_fd:%d\n", G_interactive_fd);
|
||||||
debug_printf("interactive_fd=%d\n", G_interactive_fd);
|
|
||||||
if (G_interactive_fd) {
|
if (G_interactive_fd) {
|
||||||
fcntl(G_interactive_fd, F_SETFD, FD_CLOEXEC);
|
pid_t shell_pgrp;
|
||||||
/* Looks like they want an interactive shell */
|
|
||||||
setup_job_control();
|
/* We are indeed interactive shell, and we will perform
|
||||||
|
* job control. Setting up for that. */
|
||||||
|
|
||||||
|
close_on_exec_on(G_interactive_fd);
|
||||||
|
/* If we were run as 'hush &', sleep until we are
|
||||||
|
* in the foreground (tty pgrp == our pgrp).
|
||||||
|
* If we get started under a job aware app (like bash),
|
||||||
|
* make sure we are now in charge so we don't fight over
|
||||||
|
* who gets the foreground */
|
||||||
|
while (1) {
|
||||||
|
shell_pgrp = getpgrp();
|
||||||
|
G.saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
|
||||||
|
if (G.saved_tty_pgrp == shell_pgrp)
|
||||||
|
break;
|
||||||
|
/* send TTIN to ourself (should stop us) */
|
||||||
|
kill(- shell_pgrp, SIGTTIN);
|
||||||
|
}
|
||||||
|
/* Block some signals */
|
||||||
|
block_signals(signal_mask_is_inited);
|
||||||
|
/* Set other signals to restore saved_tty_pgrp */
|
||||||
|
set_fatal_handlers();
|
||||||
|
/* Put ourselves in our own process group */
|
||||||
|
bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
|
||||||
|
/* Grab control of the terminal */
|
||||||
|
tcsetpgrp(G_interactive_fd, getpid());
|
||||||
/* -1 is special - makes xfuncs longjmp, not exit
|
/* -1 is special - makes xfuncs longjmp, not exit
|
||||||
* (we reset die_sleep = 0 whereever we [v]fork) */
|
* (we reset die_sleep = 0 whereever we [v]fork) */
|
||||||
die_sleep = -1;
|
die_sleep = -1;
|
||||||
@ -5160,12 +5200,12 @@ int hush_main(int argc, char **argv)
|
|||||||
/* xfunc has failed! die die die */
|
/* xfunc has failed! die die die */
|
||||||
hush_exit(xfunc_error_retval);
|
hush_exit(xfunc_error_retval);
|
||||||
}
|
}
|
||||||
}
|
} else if (!signal_mask_is_inited) {
|
||||||
|
block_signals(0); /* 0: called 1st time */
|
||||||
|
} /* else: block_signals(0) was done before */
|
||||||
#elif ENABLE_HUSH_INTERACTIVE
|
#elif ENABLE_HUSH_INTERACTIVE
|
||||||
/* no job control compiled, only prompt/line editing */
|
/* No job control compiled in, only prompt/line editing */
|
||||||
if (argv[optind] == NULL
|
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
|
||||||
&& isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)
|
|
||||||
) {
|
|
||||||
G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
|
G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
|
||||||
if (G_interactive_fd < 0) {
|
if (G_interactive_fd < 0) {
|
||||||
/* try to dup to any fd */
|
/* try to dup to any fd */
|
||||||
@ -5174,45 +5214,32 @@ int hush_main(int argc, char **argv)
|
|||||||
/* give up */
|
/* give up */
|
||||||
G_interactive_fd = 0;
|
G_interactive_fd = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (G_interactive_fd) {
|
if (G_interactive_fd) {
|
||||||
fcntl(G_interactive_fd, F_SETFD, FD_CLOEXEC);
|
close_on_exec_on(G_interactive_fd);
|
||||||
|
block_signals(signal_mask_is_inited);
|
||||||
|
} else if (!signal_mask_is_inited) {
|
||||||
|
block_signals(0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
init_signal_mask(); /* note: ensures SIGCHLD is not masked */
|
|
||||||
#else
|
#else
|
||||||
//TODO: we didn't do it for -c or /etc/profile! Shouldn't we?
|
/* We have interactiveness code disabled */
|
||||||
init_signal_mask();
|
if (!signal_mask_is_inited) {
|
||||||
|
block_signals(0);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
/* POSIX allows shell to re-enable SIGCHLD
|
/* bash:
|
||||||
* even if it was SIG_IGN on entry */
|
* if interactive but not a login shell, sources ~/.bashrc
|
||||||
//TODO: we didn't do it for -c or /etc/profile! Shouldn't we?
|
* (--norc turns this off, --rcfile <file> overrides)
|
||||||
// G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
|
*/
|
||||||
signal(SIGCHLD, SIG_DFL); // SIGCHLD_handler);
|
|
||||||
|
|
||||||
#if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_SH_EXTRA_QUIET
|
if (!ENABLE_FEATURE_SH_EXTRA_QUIET && G_interactive_fd) {
|
||||||
if (G_interactive_fd) {
|
|
||||||
printf("\n\n%s hush - the humble shell v"HUSH_VER_STR"\n", bb_banner);
|
printf("\n\n%s hush - the humble shell v"HUSH_VER_STR"\n", bb_banner);
|
||||||
printf("Enter 'help' for a list of built-in commands.\n\n");
|
printf("Enter 'help' for a list of built-in commands.\n\n");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (argv[optind] == NULL) {
|
|
||||||
parse_and_run_file(stdin);
|
parse_and_run_file(stdin);
|
||||||
} else {
|
|
||||||
FILE *input;
|
|
||||||
debug_printf("\nrunning script '%s'\n", argv[optind]);
|
|
||||||
G.global_argv = argv + optind;
|
|
||||||
G.global_argc = argc - optind;
|
|
||||||
input = xfopen_for_read(argv[optind]);
|
|
||||||
fcntl(fileno(input), F_SETFD, FD_CLOEXEC);
|
|
||||||
parse_and_run_file(input);
|
|
||||||
#if ENABLE_FEATURE_CLEAN_UP
|
|
||||||
fclose(input);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
final_return:
|
final_return:
|
||||||
|
|
||||||
#if ENABLE_FEATURE_CLEAN_UP
|
#if ENABLE_FEATURE_CLEAN_UP
|
||||||
if (G.cwd != bb_msg_unknown)
|
if (G.cwd != bb_msg_unknown)
|
||||||
free((char*)G.cwd);
|
free((char*)G.cwd);
|
||||||
|
Loading…
Reference in New Issue
Block a user