hush: make read and trap builtins optional

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-01-08 17:40:18 +01:00
parent 1125d7d680
commit 7a85c60e7e

View file

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