hush: fix subshell.tests failure on NOMMU
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
f3ec0fb1b4
commit
e89a241b9e
104
shell/hush.c
104
shell/hush.c
@ -520,6 +520,7 @@ struct globals {
|
||||
smalluint last_exitcode;
|
||||
/* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
|
||||
smalluint global_args_malloced;
|
||||
smalluint inherited_set_is_saved;
|
||||
/* how many non-NULL argv's we have. NB: $# + 1 */
|
||||
int global_argc;
|
||||
char **global_argv;
|
||||
@ -1050,7 +1051,8 @@ static void restore_G_args(save_arg_t *sv, char **argv)
|
||||
*
|
||||
* Trap handlers will execute even within trap handlers. (right?)
|
||||
*
|
||||
* User trap handlers are forgotten when subshell ("(cmd)") is entered.
|
||||
* User trap handlers are forgotten when subshell ("(cmd)") is entered,
|
||||
* except for handlers set to '' (empty string).
|
||||
*
|
||||
* If job control is off, backgrounded commands ("cmd &")
|
||||
* have SIGINT, SIGQUIT set to SIG_IGN.
|
||||
@ -1106,7 +1108,7 @@ static void restore_G_args(save_arg_t *sv, char **argv)
|
||||
* after [v]fork, if we plan to be a shell:
|
||||
* unblock signals with special interactive handling
|
||||
* (child shell is not interactive),
|
||||
* unset all traps (note: regardless of child shell's type - {}, (), etc)
|
||||
* unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
|
||||
* after [v]fork, if we plan to exec:
|
||||
* POSIX says fork clears pending signal mask in child - no need to clear it.
|
||||
* Restore blocked signal set to one inherited by shell just prior to exec.
|
||||
@ -1118,7 +1120,7 @@ static void restore_G_args(save_arg_t *sv, char **argv)
|
||||
* Note 2 (compat):
|
||||
* Standard says "When a subshell is entered, traps that are not being ignored
|
||||
* are set to the default actions". bash interprets it so that traps which
|
||||
* are set to "" (ignore) are NOT reset to defaults. We do the same.
|
||||
* are set to '' (ignore) are NOT reset to defaults. We do the same.
|
||||
*/
|
||||
enum {
|
||||
SPECIAL_INTERACTIVE_SIGS = 0
|
||||
@ -2925,6 +2927,7 @@ static void re_execute_shell(char ***to_free, const char *s,
|
||||
# endif
|
||||
char **argv, **pp;
|
||||
unsigned cnt;
|
||||
unsigned long long empty_trap_mask;
|
||||
|
||||
if (!g_argv0) { /* heredoc */
|
||||
argv = heredoc_argv;
|
||||
@ -2941,12 +2944,22 @@ static void re_execute_shell(char ***to_free, const char *s,
|
||||
if (pp) while (*pp++)
|
||||
cnt++;
|
||||
|
||||
sprintf(param_buf, "-$%x:%x:%x:%x:%x" IF_HUSH_LOOPS(":%x")
|
||||
empty_trap_mask = 0;
|
||||
if (G.traps) {
|
||||
int sig;
|
||||
for (sig = 1; sig < NSIG; sig++) {
|
||||
if (G.traps[sig] && !G.traps[sig][0])
|
||||
empty_trap_mask |= 1LL << sig;
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(param_buf, "-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x")
|
||||
, (unsigned) G.root_pid
|
||||
, (unsigned) G.root_ppid
|
||||
, (unsigned) G.last_bg_pid
|
||||
, (unsigned) G.last_exitcode
|
||||
, cnt
|
||||
, empty_trap_mask
|
||||
IF_HUSH_LOOPS(, G.depth_of_loop)
|
||||
);
|
||||
/* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...> <funcs...>
|
||||
@ -3002,7 +3015,9 @@ static void re_execute_shell(char ***to_free, const char *s,
|
||||
* _inside_ group (just before echo 1), it works.
|
||||
*
|
||||
* I conclude it means we don't need to pass active traps here.
|
||||
* exec syscall below resets them to SIG_DFL for us.
|
||||
* Even if we would use signal handlers instead of signal masking
|
||||
* in order to implement trap handling,
|
||||
* exec syscall below resets signals to SIG_DFL for us.
|
||||
*/
|
||||
*pp++ = (char *) "-c";
|
||||
*pp++ = (char *) s;
|
||||
@ -5447,7 +5462,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
|
||||
pid_t pid;
|
||||
int channel[2];
|
||||
# if !BB_MMU
|
||||
char **to_free;
|
||||
char **to_free = NULL;
|
||||
# endif
|
||||
|
||||
xpipe(channel);
|
||||
@ -6677,10 +6692,17 @@ static void parse_and_run_file(FILE *f)
|
||||
}
|
||||
|
||||
/* Called a few times only (or even once if "sh -c") */
|
||||
static void block_signals(int second_time)
|
||||
static void init_sigmasks(void)
|
||||
{
|
||||
unsigned sig;
|
||||
unsigned mask;
|
||||
sigset_t old_blocked_set;
|
||||
|
||||
if (!G.inherited_set_is_saved) {
|
||||
sigprocmask(SIG_SETMASK, NULL, &G.blocked_set);
|
||||
G.inherited_set = G.blocked_set;
|
||||
}
|
||||
old_blocked_set = G.blocked_set;
|
||||
|
||||
mask = (1 << SIGQUIT);
|
||||
if (G_interactive_fd) {
|
||||
@ -6690,8 +6712,6 @@ static void block_signals(int second_time)
|
||||
}
|
||||
G.non_DFL_mask = mask;
|
||||
|
||||
if (!second_time)
|
||||
sigprocmask(SIG_SETMASK, NULL, &G.blocked_set);
|
||||
sig = 0;
|
||||
while (mask) {
|
||||
if (mask & 1)
|
||||
@ -6701,18 +6721,21 @@ static void block_signals(int second_time)
|
||||
}
|
||||
sigdelset(&G.blocked_set, SIGCHLD);
|
||||
|
||||
sigprocmask(SIG_SETMASK, &G.blocked_set,
|
||||
second_time ? NULL : &G.inherited_set);
|
||||
if (memcmp(&old_blocked_set, &G.blocked_set, sizeof(old_blocked_set)) != 0)
|
||||
sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
|
||||
|
||||
/* POSIX allows shell to re-enable SIGCHLD
|
||||
* even if it was SIG_IGN on entry */
|
||||
#if ENABLE_HUSH_FAST
|
||||
G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
|
||||
if (!second_time)
|
||||
if (!G.inherited_set_is_saved)
|
||||
signal(SIGCHLD, SIGCHLD_handler);
|
||||
#else
|
||||
if (!second_time)
|
||||
if (!G.inherited_set_is_saved)
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
#endif
|
||||
|
||||
G.inherited_set_is_saved = 1;
|
||||
}
|
||||
|
||||
#if ENABLE_HUSH_JOB
|
||||
@ -6774,7 +6797,6 @@ int hush_main(int argc, char **argv)
|
||||
.flg_export = 1,
|
||||
.flg_read_only = 1,
|
||||
};
|
||||
int signal_mask_is_inited = 0;
|
||||
int opt;
|
||||
unsigned builtin_argc;
|
||||
char **e;
|
||||
@ -6865,10 +6887,9 @@ int hush_main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Shell is non-interactive at first. We need to call
|
||||
* block_signals(0) if we are going to execute "sh <script>",
|
||||
* init_sigmasks() if we are going to execute "sh <script>",
|
||||
* "sh -c <cmds>" or login shell's /etc/profile and friends.
|
||||
* If we later decide that we are interactive, we run block_signals(0)
|
||||
* (or re-run block_signals(1) if we ran block_signals(0) before)
|
||||
* If we later decide that we are interactive, we run init_sigmasks()
|
||||
* in order to intercept (more) signals.
|
||||
*/
|
||||
|
||||
@ -6908,7 +6929,7 @@ int hush_main(int argc, char **argv)
|
||||
/* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
|
||||
const struct built_in_command *x;
|
||||
|
||||
block_signals(0); /* 0: called 1st time */
|
||||
init_sigmasks();
|
||||
x = find_builtin(optarg);
|
||||
if (x) { /* paranoia */
|
||||
G.global_argc -= builtin_argc; /* skip [BARGV...] "" */
|
||||
@ -6924,7 +6945,7 @@ int hush_main(int argc, char **argv)
|
||||
G.global_argv[0] = argv[0];
|
||||
G.global_argc--;
|
||||
} /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
|
||||
block_signals(0); /* 0: called 1st time */
|
||||
init_sigmasks();
|
||||
parse_and_run_string(optarg);
|
||||
goto final_return;
|
||||
case 'i':
|
||||
@ -6940,7 +6961,9 @@ int hush_main(int argc, char **argv)
|
||||
case '<': /* "big heredoc" support */
|
||||
full_write(STDOUT_FILENO, optarg, strlen(optarg));
|
||||
_exit(0);
|
||||
case '$':
|
||||
case '$': {
|
||||
unsigned long long empty_trap_mask;
|
||||
|
||||
G.root_pid = bb_strtou(optarg, &optarg, 16);
|
||||
optarg++;
|
||||
G.root_ppid = bb_strtou(optarg, &optarg, 16);
|
||||
@ -6950,11 +6973,26 @@ int hush_main(int argc, char **argv)
|
||||
G.last_exitcode = bb_strtou(optarg, &optarg, 16);
|
||||
optarg++;
|
||||
builtin_argc = bb_strtou(optarg, &optarg, 16);
|
||||
optarg++;
|
||||
empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
|
||||
if (empty_trap_mask != 0) {
|
||||
int sig;
|
||||
init_sigmasks();
|
||||
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(""); */
|
||||
sigaddset(&G.blocked_set, sig);
|
||||
}
|
||||
}
|
||||
sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
|
||||
}
|
||||
# if ENABLE_HUSH_LOOPS
|
||||
optarg++;
|
||||
G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
|
||||
# endif
|
||||
break;
|
||||
}
|
||||
case 'R':
|
||||
case 'V':
|
||||
set_local_var(xstrdup(optarg), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ opt == 'R');
|
||||
@ -6997,8 +7035,7 @@ int hush_main(int argc, char **argv)
|
||||
input = fopen_for_read("/etc/profile");
|
||||
if (input != NULL) {
|
||||
close_on_exec_on(fileno(input));
|
||||
block_signals(0); /* 0: called 1st time */
|
||||
signal_mask_is_inited = 1;
|
||||
init_sigmasks();
|
||||
parse_and_run_file(input);
|
||||
fclose(input);
|
||||
}
|
||||
@ -7023,8 +7060,7 @@ int hush_main(int argc, char **argv)
|
||||
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 */
|
||||
init_sigmasks();
|
||||
parse_and_run_file(input);
|
||||
#if ENABLE_FEATURE_CLEAN_UP
|
||||
fclose(input);
|
||||
@ -7033,7 +7069,7 @@ int hush_main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Up to here, shell was non-interactive. Now it may become one.
|
||||
* NB: don't forget to (re)run block_signals(0/1) as needed.
|
||||
* NB: don't forget to (re)run init_sigmasks() as needed.
|
||||
*/
|
||||
|
||||
/* A shell is interactive if the '-i' flag was given,
|
||||
@ -7086,7 +7122,7 @@ int hush_main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* Block some signals */
|
||||
block_signals(signal_mask_is_inited);
|
||||
init_sigmasks();
|
||||
|
||||
if (G_saved_tty_pgrp) {
|
||||
/* Set other signals to restore saved_tty_pgrp */
|
||||
@ -7100,9 +7136,9 @@ int hush_main(int argc, char **argv)
|
||||
/* -1 is special - makes xfuncs longjmp, not exit
|
||||
* (we reset die_sleep = 0 whereever we [v]fork) */
|
||||
enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
|
||||
} else if (!signal_mask_is_inited) {
|
||||
block_signals(0); /* 0: called 1st time */
|
||||
} /* else: block_signals(0) was done before */
|
||||
} else {
|
||||
init_sigmasks();
|
||||
}
|
||||
#elif ENABLE_HUSH_INTERACTIVE
|
||||
/* No job control compiled in, only prompt/line editing */
|
||||
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
|
||||
@ -7117,15 +7153,11 @@ int hush_main(int argc, char **argv)
|
||||
}
|
||||
if (G_interactive_fd) {
|
||||
close_on_exec_on(G_interactive_fd);
|
||||
block_signals(signal_mask_is_inited);
|
||||
} else if (!signal_mask_is_inited) {
|
||||
block_signals(0);
|
||||
}
|
||||
init_sigmasks();
|
||||
#else
|
||||
/* We have interactiveness code disabled */
|
||||
if (!signal_mask_is_inited) {
|
||||
block_signals(0);
|
||||
}
|
||||
init_sigmasks();
|
||||
#endif
|
||||
/* bash:
|
||||
* if interactive but not a login shell, sources ~/.bashrc
|
||||
@ -7471,7 +7503,7 @@ static int FAST_FUNC builtin_trap(char **argv)
|
||||
free(G.traps[sig]);
|
||||
G.traps[sig] = xstrdup(new_cmd);
|
||||
|
||||
debug_printf("trap: setting SIG%s (%i) to '%s'",
|
||||
debug_printf("trap: setting SIG%s (%i) to '%s'\n",
|
||||
get_signame(sig), sig, G.traps[sig]);
|
||||
|
||||
/* There is no signal for 0 (EXIT) */
|
||||
|
@ -1,6 +1,21 @@
|
||||
trap -- '' HUP
|
||||
trap -- '' QUIT
|
||||
trap -- '' SYS
|
||||
Ok
|
||||
trap -- '' HUP
|
||||
trap -- '' QUIT
|
||||
trap -- '' SYS
|
||||
Ok
|
||||
trap -- '' HUP
|
||||
trap -- '' QUIT
|
||||
trap -- '' SYS
|
||||
Ok
|
||||
trap -- '' HUP
|
||||
trap -- '' QUIT
|
||||
trap -- '' SYS
|
||||
Ok
|
||||
trap -- '' HUP
|
||||
trap -- '' QUIT
|
||||
trap -- '' SYS
|
||||
TERM
|
||||
Done
|
||||
|
@ -12,9 +12,9 @@ trap 'bad: caught WINCH' WINCH
|
||||
trap 'bad: caught TERM' TERM
|
||||
|
||||
# using bash, because we don't have $PPID (yet)
|
||||
(bash -c 'kill -HUP $PPID'; echo Ok)
|
||||
(bash -c 'kill -QUIT $PPID'; echo Ok)
|
||||
(bash -c 'kill -SYS $PPID'; echo Ok)
|
||||
(bash -c 'kill -WINCH $PPID'; echo Ok)
|
||||
(bash -c 'kill -TERM $PPID'; echo Bad: TERM is not reset)
|
||||
(trap; bash -c 'kill -HUP $PPID'; echo Ok)
|
||||
(trap; bash -c 'kill -QUIT $PPID'; echo Ok)
|
||||
(trap; bash -c 'kill -SYS $PPID'; echo Ok)
|
||||
(trap; bash -c 'kill -WINCH $PPID'; echo Ok)
|
||||
(trap; bash -c 'kill -TERM $PPID'; echo Bad: TERM is not reset)
|
||||
echo Done
|
||||
|
Loading…
Reference in New Issue
Block a user