From 7a85c60e7e370d0bc171bd10043c1eb7c20ab913 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 8 Jan 2017 17:40:18 +0100 Subject: [PATCH] hush: make read and trap builtins optional Signed-off-by: Denys Vlasenko --- shell/hush.c | 189 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 71 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index 9aafb4d25..e6e8c1baf 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -49,7 +49,6 @@ * [un]alias, command, fc, getopts, newgrp, readonly, times * make complex ${var%...} constructs support optional * make here documents optional - * make trap, read, ulimit builtins optional * * Bash compat TODO: * redirection of stdout+stderr: &> and >& @@ -110,41 +109,6 @@ //config: help //config: Enable {abc,def} extension. //config: -//config:config HUSH_HELP -//config: bool "help builtin" -//config: default y -//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH -//config: help -//config: Enable help builtin in hush. Code size + ~1 kbyte. -//config: -//config:config HUSH_PRINTF -//config: bool "printf builtin" -//config: default y -//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH -//config: help -//config: Enable printf builtin in hush. -//config: -//config:config HUSH_KILL -//config: bool "kill builtin (for kill %jobspec)" -//config: default y -//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH -//config: help -//config: Enable kill builtin in hush. -//config: -//config:config HUSH_WAIT -//config: bool "wait builtin" -//config: default y -//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH -//config: help -//config: Enable wait builtin in hush. -//config: -//config:config HUSH_TYPE -//config: bool "type builtin" -//config: default y -//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH -//config: help -//config: Enable type builtin in hush. -//config: //config:config HUSH_INTERACTIVE //config: bool "Interactive mode" //config: default y @@ -209,7 +173,7 @@ //config: Enable support for shell functions in hush. +800 bytes. //config: //config:config HUSH_LOCAL -//config: bool "Support local builtin" +//config: bool "local builtin" //config: default y //config: depends on HUSH_FUNCTIONS //config: help @@ -238,6 +202,62 @@ //config: This instructs hush to print commands before execution. //config: Adds ~300 bytes. //config: +//config:config HUSH_HELP +//config: bool "help builtin" +//config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH +//config: help +//config: Enable help builtin in hush. Code size + ~1 kbyte. +//config: +//config:config HUSH_PRINTF +//config: bool "printf builtin" +//config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH +//config: help +//config: Enable printf builtin in hush. +//config: +//config:config HUSH_KILL +//config: bool "kill builtin (for kill %jobspec)" +//config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH +//config: help +//config: Enable kill builtin in hush. +//config: +//config:config HUSH_WAIT +//config: bool "wait builtin" +//config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH +//config: help +//config: Enable wait builtin in hush. +//config: +//config:config HUSH_TRAP +//config: bool "trap builtin" +//config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH +//config: help +//config: Enable trap builtin in hush. +//config: +//config:config HUSH_ULIMIT +//config: bool "ulimit builtin" +//config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH +//config: help +//config: Enable ulimit builtin in hush. +//config: +//config:config HUSH_TYPE +//config: bool "type builtin" +//config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH +//config: help +//config: Enable type builtin in hush. +//config: +//config:config HUSH_READ +//config: bool "read builtin" +//config: default y +//config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH +//config: help +//config: Enable read builtin in hush. +//config: //config:config MSH //config: bool "msh (deprecated: aliased to hush)" //config: default n @@ -842,7 +862,12 @@ struct globals { #else # define G_fatal_sig_mask 0 #endif +#if ENABLE_HUSH_TRAP char **traps; /* char *traps[NSIG] */ +# define G_traps G.traps +#else +# define G_traps ((char**)NULL) +#endif sigset_t pending_set; #if HUSH_DEBUG unsigned long memleak_value; @@ -892,12 +917,16 @@ static int builtin_memleak(char **argv) FAST_FUNC; static int builtin_printf(char **argv) FAST_FUNC; #endif static int builtin_pwd(char **argv) FAST_FUNC; +#if ENABLE_HUSH_READ static int builtin_read(char **argv) FAST_FUNC; +#endif static int builtin_set(char **argv) FAST_FUNC; static int builtin_shift(char **argv) FAST_FUNC; static int builtin_source(char **argv) FAST_FUNC; static int builtin_test(char **argv) FAST_FUNC; +#if ENABLE_HUSH_TRAP static int builtin_trap(char **argv) FAST_FUNC; +#endif #if ENABLE_HUSH_TYPE static int builtin_type(char **argv) FAST_FUNC; #endif @@ -973,7 +1002,9 @@ static const struct built_in_command bltins1[] = { #if HUSH_DEBUG BLTIN("memleak" , builtin_memleak , NULL), #endif +#if ENABLE_HUSH_READ BLTIN("read" , builtin_read , "Input into variable"), +#endif #if ENABLE_HUSH_FUNCTIONS BLTIN("return" , builtin_return , "Return from a function"), #endif @@ -982,12 +1013,16 @@ static const struct built_in_command bltins1[] = { #if ENABLE_HUSH_BASH_COMPAT BLTIN("source" , builtin_source , "Run commands in a file"), #endif +#if ENABLE_HUSH_TRAP BLTIN("trap" , builtin_trap , "Trap signals"), +#endif BLTIN("true" , builtin_true , NULL), #if ENABLE_HUSH_TYPE BLTIN("type" , builtin_type , "Show command type"), #endif +#if ENABLE_HUSH_ULIMIT BLTIN("ulimit" , shell_builtin_ulimit , "Control resource limits"), +#endif BLTIN("umask" , builtin_umask , "Set file creation mask"), BLTIN("unset" , builtin_unset , "Unset variables"), #if ENABLE_HUSH_WAIT @@ -1712,13 +1747,13 @@ static void hush_exit(int exitcode) #endif 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]) { char *argv[3]; /* argv[0] is unused */ - argv[1] = G.traps[0]; + argv[1] = G_traps[0]; argv[2] = NULL; G.exiting = 1; /* prevent EXIT trap recursion */ - /* Note: G.traps[0] is not cleared! + /* Note: G_traps[0] is not cleared! * "trap" will still show it, if executed * in the handler */ builtin_eval(argv); @@ -1769,14 +1804,14 @@ static int check_and_run_traps(void) } while (sig < NSIG); break; got_sig: - if (G.traps && G.traps[sig]) { + if (G_traps && G_traps[sig]) { debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]); - if (G.traps[sig][0]) { + if (G_traps[sig][0]) { /* We have user-defined handler */ smalluint save_rcode; char *argv[3]; /* argv[0] is unused */ - argv[1] = G.traps[sig]; + argv[1] = G_traps[sig]; argv[2] = NULL; save_rcode = G.last_exitcode; builtin_eval(argv); @@ -5924,13 +5959,15 @@ static void switch_off_special_sigs(unsigned mask) sig++; if (!(mask & 1)) continue; - if (G.traps) { - if (G.traps[sig] && !G.traps[sig][0]) +#if ENABLE_HUSH_TRAP + if (G_traps) { + if (G_traps[sig] && !G_traps[sig][0]) /* trap is '', has to remain SIG_IGN */ continue; - free(G.traps[sig]); - G.traps[sig] = NULL; + free(G_traps[sig]); + G_traps[sig] = NULL; } +#endif /* We are here only if no trap or trap was not '' */ install_sighandler(sig, SIG_DFL); } @@ -5947,7 +5984,7 @@ static void reset_traps_to_defaults(void) /* This function is always called in a child shell * after fork (not vfork, NOMMU doesn't use this function). */ - unsigned sig; + IF_HUSH_TRAP(unsigned sig;) unsigned mask; /* Child shells are not interactive. @@ -5956,35 +5993,37 @@ static void reset_traps_to_defaults(void) * Same goes for SIGTERM, SIGHUP, SIGINT. */ mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask; - if (!G.traps && !mask) + if (!G_traps && !mask) return; /* already no traps and no special sigs */ /* Switch off special sigs */ switch_off_special_sigs(mask); -#if ENABLE_HUSH_JOB +# if ENABLE_HUSH_JOB G_fatal_sig_mask = 0; -#endif +# endif G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS; /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS * remain set in G.special_sig_mask */ - if (!G.traps) +# if ENABLE_HUSH_TRAP + if (!G_traps) return; /* Reset all sigs to default except ones with empty traps */ for (sig = 0; sig < NSIG; sig++) { - if (!G.traps[sig]) + if (!G_traps[sig]) continue; /* no trap: nothing to do */ - if (!G.traps[sig][0]) + if (!G_traps[sig][0]) continue; /* empty trap: has to remain SIG_IGN */ /* sig has non-empty trap, reset it: */ - free(G.traps[sig]); - G.traps[sig] = NULL; + free(G_traps[sig]); + G_traps[sig] = NULL; /* There is no signal for trap 0 (EXIT) */ if (sig == 0) continue; install_sighandler(sig, pick_sighandler(sig)); } +# endif } #else /* !BB_MMU */ @@ -6024,10 +6063,10 @@ static void re_execute_shell(char ***to_free, const char *s, cnt++; empty_trap_mask = 0; - if (G.traps) { + if (G_traps) { int 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; } } @@ -6220,6 +6259,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) xmove_fd(channel[1], 1); /* Prevent it from trying to handle ctrl-z etc */ IF_HUSH_JOB(G.run_list_level = 1;) +# if ENABLE_HUSH_TRAP /* Awful hack for `trap` or $(trap). * * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html @@ -6263,6 +6303,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p) fflush_all(); /* important */ _exit(0); } +# endif # if BB_MMU reset_traps_to_defaults(); parse_and_run_string(s); @@ -8160,10 +8201,12 @@ static void install_sighandlers(unsigned mask) if (old_handler == SIG_IGN) { /* oops... restore back to IGN, and record this fact */ install_sighandler(sig, old_handler); - if (!G.traps) - G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); - free(G.traps[sig]); - G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ +#if ENABLE_HUSH_TRAP + if (!G_traps) + G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); + free(G_traps[sig]); + G_traps[sig] = xzalloc(1); /* == xstrdup(""); */ +#endif } } } @@ -8464,10 +8507,10 @@ int hush_main(int argc, char **argv) if (empty_trap_mask != 0) { int sig; 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++) { if (empty_trap_mask & (1LL << sig)) { - G.traps[sig] = xzalloc(1); /* == xstrdup(""); */ + G_traps[sig] = xzalloc(1); /* == xstrdup(""); */ install_sighandler(sig, SIG_IGN); } } @@ -9114,6 +9157,7 @@ static int FAST_FUNC builtin_shift(char **argv) return EXIT_FAILURE; } +#if ENABLE_HUSH_READ /* Interruptibility of read builtin in bash * (tested on bash-4.2.8 by sending signals (not by ^C)): * @@ -9178,23 +9222,25 @@ static int FAST_FUNC builtin_read(char **argv) return (uintptr_t)r; } +#endif +#if ENABLE_HUSH_TRAP static int FAST_FUNC builtin_trap(char **argv) { int sig; char *new_cmd; - if (!G.traps) - G.traps = xzalloc(sizeof(G.traps[0]) * NSIG); + if (!G_traps) + G_traps = xzalloc(sizeof(G_traps[0]) * NSIG); argv++; if (!*argv) { int i; /* No args: print all trapped */ for (i = 0; i < NSIG; ++i) { - if (G.traps[i]) { + if (G_traps[i]) { printf("trap -- "); - print_escaped(G.traps[i]); + print_escaped(G_traps[i]); /* note: bash adds "SIG", but only if invoked * as "bash". If called as "sh", or if set -o posix, * then it prints short signal names. @@ -9224,11 +9270,11 @@ static int FAST_FUNC builtin_trap(char **argv) continue; } - free(G.traps[sig]); - G.traps[sig] = xstrdup(new_cmd); + free(G_traps[sig]); + G_traps[sig] = xstrdup(new_cmd); debug_printf("trap: setting SIG%s (%i) to '%s'\n", - get_signame(sig), sig, G.traps[sig]); + get_signame(sig), sig, G_traps[sig]); /* There is no signal for 0 (EXIT) */ if (sig == 0) @@ -9268,6 +9314,7 @@ static int FAST_FUNC builtin_trap(char **argv) argv++; goto process_sig_list; } +#endif #if ENABLE_HUSH_TYPE /* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */ -- 2.25.1