hush: fix login shell's signal handling; add -l option so that it's easier to test

function                                             old     new   delta
hush_main                                            958    1001     +43
install_special_sighandlers                           47      52      +5
packed_usage                                       28752   28741     -11
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 48/-11)             Total: 37 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2011-05-12 02:10:33 +02:00
parent 9d6cbafe72
commit f58f705c58

View File

@ -260,7 +260,7 @@ typedef void (*hush_sighandler_t)(int);
* therefore we don't show them either. * therefore we don't show them either.
*/ */
//usage:#define hush_trivial_usage //usage:#define hush_trivial_usage
//usage: "[-nx] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]" //usage: "[-nxl] [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS]]"
//usage:#define hush_full_usage "\n\n" //usage:#define hush_full_usage "\n\n"
//usage: "Unix shell interpreter" //usage: "Unix shell interpreter"
@ -1493,8 +1493,8 @@ static sighandler_t pick_sighandler(unsigned sig)
handler = record_pending_signo; handler = record_pending_signo;
/* TTIN/TTOU/TSTS can't be set to record_pending_signo /* TTIN/TTOU/TSTS can't be set to record_pending_signo
* in order to ignore them: they will be raised * in order to ignore them: they will be raised
* in an endless loop then when we try to do some * in an endless loop when we try to do some
* terminal ioctls! We do nave to _ignore_ these. * terminal ioctls! We do have to _ignore_ these.
*/ */
if (SPECIAL_JOBSTOP_SIGS & sigmask) if (SPECIAL_JOBSTOP_SIGS & sigmask)
handler = SIG_IGN; handler = SIG_IGN;
@ -1508,9 +1508,6 @@ static void hush_exit(int exitcode)
{ {
fflush_all(); fflush_all();
if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) {
/* Prevent recursion:
* trap "echo Hi; exit" EXIT; exit
*/
char *argv[3]; char *argv[3];
/* argv[0] is unused */ /* argv[0] is unused */
argv[1] = G.traps[0]; argv[1] = G.traps[0];
@ -5474,7 +5471,8 @@ static void reset_traps_to_defaults(void)
G_fatal_sig_mask = 0; G_fatal_sig_mask = 0;
#endif #endif
G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
/* SIGQUIT and maybe SPECIAL_JOBSTOP_SIGS remain set in G.special_sig_mask */ /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
* remain set in G.special_sig_mask */
if (!G.traps) if (!G.traps)
return; return;
@ -5537,7 +5535,6 @@ static void re_execute_shell(char ***to_free, const char *s,
for (sig = 1; sig < NSIG; sig++) { for (sig = 1; sig < NSIG; sig++) {
if (G.traps[sig] && !G.traps[sig][0]) if (G.traps[sig] && !G.traps[sig][0])
empty_trap_mask |= 1LL << sig; empty_trap_mask |= 1LL << sig;
///vda: optimize
} }
} }
@ -7540,9 +7537,6 @@ static void install_special_sighandlers(void)
{ {
unsigned mask; unsigned mask;
if (G.special_sig_mask != 0)
return;
/* Which signals are shell-special? */ /* Which signals are shell-special? */
mask = (1 << SIGQUIT) | (1 << SIGCHLD); mask = (1 << SIGQUIT) | (1 << SIGCHLD);
if (G_interactive_fd) { if (G_interactive_fd) {
@ -7550,9 +7544,12 @@ static void install_special_sighandlers(void)
if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */ if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
mask |= SPECIAL_JOBSTOP_SIGS; mask |= SPECIAL_JOBSTOP_SIGS;
} }
G.special_sig_mask = mask; /* Careful, do not re-install handlers we already installed */
if (G.special_sig_mask != mask) {
install_sighandlers(mask); unsigned diff = mask & ~G.special_sig_mask;
G.special_sig_mask = mask;
install_sighandlers(diff);
}
} }
#if ENABLE_HUSH_JOB #if ENABLE_HUSH_JOB
@ -7573,17 +7570,14 @@ static void install_fatal_sighandlers(void)
/* bash 3.2 seems to handle these just like 'fatal' ones */ /* bash 3.2 seems to handle these just like 'fatal' ones */
+ (1 << SIGPIPE) + (1 << SIGPIPE)
+ (1 << SIGALRM) + (1 << SIGALRM)
/* if we are interactive, SIGHUP, SIGTERM and SIGINT are masked. /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs.
* if we aren't interactive... but in this case * if we aren't interactive... but in this case
* we never want to restore pgrp on exit, and this fn is not called */ * we never want to restore pgrp on exit, and this fn is not called
*/
/*+ (1 << SIGHUP )*/ /*+ (1 << SIGHUP )*/
/*+ (1 << SIGTERM)*/ /*+ (1 << SIGTERM)*/
/*+ (1 << SIGINT )*/ /*+ (1 << SIGINT )*/
; ;
/* special_sig_mask'ed signals are set to record_pending_signo
* no need to set handler for them.
*/
/*mask &= ~G.special_sig_mask; - they never overlap */
G_fatal_sig_mask = mask; G_fatal_sig_mask = mask;
install_sighandlers(mask); install_sighandlers(mask);
@ -7632,6 +7626,10 @@ static int set_mode(int state, char mode, const char *o_opt)
int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int hush_main(int argc, char **argv) int hush_main(int argc, char **argv)
{ {
enum {
OPT_login = (1 << 0),
};
unsigned flags;
int opt; int opt;
unsigned builtin_argc; unsigned builtin_argc;
char **e; char **e;
@ -7735,8 +7733,6 @@ int hush_main(int argc, char **argv)
# endif # endif
#endif #endif
G.global_argc = argc;
G.global_argv = argv;
/* Initialize some more globals to non-zero values */ /* Initialize some more globals to non-zero values */
cmdedit_update_prompt(); cmdedit_update_prompt();
@ -7752,14 +7748,14 @@ int hush_main(int argc, char **argv)
* "sh -c <cmds>" or login shell's /etc/profile and friends. * "sh -c <cmds>" or login shell's /etc/profile and friends.
* If we later decide that we are interactive, we run install_special_sighandlers() * If we later decide that we are interactive, we run install_special_sighandlers()
* in order to intercept (more) signals. * in order to intercept (more) signals.
//FIXME: re-running is currently most likely broken, it's a no-op.
*/ */
/* Parse options */ /* Parse options */
/* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */ /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
builtin_argc = 0; builtin_argc = 0;
while (1) { while (1) {
opt = getopt(argc, argv, "+c:xins" opt = getopt(argc, argv, "+c:xinsl"
#if !BB_MMU #if !BB_MMU
"<:$:R:V:" "<:$:R:V:"
# if ENABLE_HUSH_FUNCTIONS # if ENABLE_HUSH_FUNCTIONS
@ -7820,6 +7816,9 @@ int hush_main(int argc, char **argv)
/* "-s" means "read from stdin", but this is how we always /* "-s" means "read from stdin", but this is how we always
* operate, so simply do nothing here. */ * operate, so simply do nothing here. */
break; break;
case 'l':
flags |= OPT_login;
break;
#if !BB_MMU #if !BB_MMU
case '<': /* "big heredoc" support */ case '<': /* "big heredoc" support */
full_write1_str(optarg); full_write1_str(optarg);
@ -7843,7 +7842,6 @@ int hush_main(int argc, char **argv)
install_special_sighandlers(); install_special_sighandlers();
G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); G.traps = xzalloc(sizeof(G.traps[0]) * NSIG);
for (sig = 1; sig < NSIG; sig++) { for (sig = 1; sig < NSIG; sig++) {
///vda: fixme: more efficient code
if (empty_trap_mask & (1LL << sig)) { if (empty_trap_mask & (1LL << sig)) {
G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ G.traps[sig] = xzalloc(1); /* == xstrdup(""); */
signal(sig, SIG_IGN); signal(sig, SIG_IGN);
@ -7886,13 +7884,18 @@ int hush_main(int argc, char **argv)
} }
} /* option parsing loop */ } /* option parsing loop */
/* Skip options. Try "hush -l": $1 should not be "-l"! */
G.global_argc = argc - (optind - 1);
G.global_argv = argv + (optind - 1);
G.global_argv[0] = argv[0];
if (!G.root_pid) { if (!G.root_pid) {
G.root_pid = getpid(); G.root_pid = getpid();
G.root_ppid = getppid(); G.root_ppid = getppid();
} }
/* If we are login shell... */ /* If we are login shell... */
if (argv[0] && argv[0][0] == '-') { if (flags & OPT_login) {
FILE *input; FILE *input;
debug_printf("sourcing /etc/profile\n"); debug_printf("sourcing /etc/profile\n");
input = fopen_for_read("/etc/profile"); input = fopen_for_read("/etc/profile");
@ -7911,17 +7914,17 @@ int hush_main(int argc, char **argv)
*/ */
} }
if (argv[optind]) { if (G.global_argv[1]) {
FILE *input; FILE *input;
/* /*
* "bash <script>" (which is never interactive (unless -i?)) * "bash <script>" (which is never interactive (unless -i?))
* sources $BASH_ENV here (without scanning $PATH). * sources $BASH_ENV here (without scanning $PATH).
* If called as sh, does the same but with $ENV. * If called as sh, does the same but with $ENV.
*/ */
debug_printf("running script '%s'\n", argv[optind]); G.global_argc--;
G.global_argv = argv + optind; G.global_argv++;
G.global_argc = argc - optind; debug_printf("running script '%s'\n", G.global_argv[0]);
input = xfopen_for_read(argv[optind]); input = xfopen_for_read(G.global_argv[0]);
close_on_exec_on(fileno(input)); close_on_exec_on(fileno(input));
install_special_sighandlers(); install_special_sighandlers();
parse_and_run_file(input); parse_and_run_file(input);
@ -7984,7 +7987,7 @@ int hush_main(int argc, char **argv)
} }
} }
/* Block some signals */ /* Install more signal handlers */
install_special_sighandlers(); install_special_sighandlers();
if (G_saved_tty_pgrp) { if (G_saved_tty_pgrp) {