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;
|
smalluint last_exitcode;
|
||||||
/* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
|
/* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
|
||||||
smalluint global_args_malloced;
|
smalluint global_args_malloced;
|
||||||
|
smalluint inherited_set_is_saved;
|
||||||
/* how many non-NULL argv's we have. NB: $# + 1 */
|
/* how many non-NULL argv's we have. NB: $# + 1 */
|
||||||
int global_argc;
|
int global_argc;
|
||||||
char **global_argv;
|
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?)
|
* 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 &")
|
* If job control is off, backgrounded commands ("cmd &")
|
||||||
* have SIGINT, SIGQUIT set to SIG_IGN.
|
* 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:
|
* after [v]fork, if we plan to be a shell:
|
||||||
* unblock signals with special interactive handling
|
* unblock signals with special interactive handling
|
||||||
* (child shell is not interactive),
|
* (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:
|
* after [v]fork, if we plan to exec:
|
||||||
* POSIX says fork clears pending signal mask in child - no need to clear it.
|
* 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.
|
* 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):
|
* Note 2 (compat):
|
||||||
* Standard says "When a subshell is entered, traps that are not being ignored
|
* 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 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 {
|
enum {
|
||||||
SPECIAL_INTERACTIVE_SIGS = 0
|
SPECIAL_INTERACTIVE_SIGS = 0
|
||||||
@ -2925,6 +2927,7 @@ static void re_execute_shell(char ***to_free, const char *s,
|
|||||||
# endif
|
# endif
|
||||||
char **argv, **pp;
|
char **argv, **pp;
|
||||||
unsigned cnt;
|
unsigned cnt;
|
||||||
|
unsigned long long empty_trap_mask;
|
||||||
|
|
||||||
if (!g_argv0) { /* heredoc */
|
if (!g_argv0) { /* heredoc */
|
||||||
argv = heredoc_argv;
|
argv = heredoc_argv;
|
||||||
@ -2941,12 +2944,22 @@ static void re_execute_shell(char ***to_free, const char *s,
|
|||||||
if (pp) while (*pp++)
|
if (pp) while (*pp++)
|
||||||
cnt++;
|
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_pid
|
||||||
, (unsigned) G.root_ppid
|
, (unsigned) G.root_ppid
|
||||||
, (unsigned) G.last_bg_pid
|
, (unsigned) G.last_bg_pid
|
||||||
, (unsigned) G.last_exitcode
|
, (unsigned) G.last_exitcode
|
||||||
, cnt
|
, cnt
|
||||||
|
, empty_trap_mask
|
||||||
IF_HUSH_LOOPS(, G.depth_of_loop)
|
IF_HUSH_LOOPS(, G.depth_of_loop)
|
||||||
);
|
);
|
||||||
/* 1:hush 2:-$<pid>:<pid>:<exitcode>:<depth> <vars...> <funcs...>
|
/* 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.
|
* _inside_ group (just before echo 1), it works.
|
||||||
*
|
*
|
||||||
* I conclude it means we don't need to pass active traps here.
|
* 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 *) "-c";
|
||||||
*pp++ = (char *) s;
|
*pp++ = (char *) s;
|
||||||
@ -5447,7 +5462,7 @@ static FILE *generate_stream_from_string(const char *s, pid_t *pid_p)
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
int channel[2];
|
int channel[2];
|
||||||
# if !BB_MMU
|
# if !BB_MMU
|
||||||
char **to_free;
|
char **to_free = NULL;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
xpipe(channel);
|
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") */
|
/* 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 sig;
|
||||||
unsigned mask;
|
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);
|
mask = (1 << SIGQUIT);
|
||||||
if (G_interactive_fd) {
|
if (G_interactive_fd) {
|
||||||
@ -6690,8 +6712,6 @@ static void block_signals(int second_time)
|
|||||||
}
|
}
|
||||||
G.non_DFL_mask = mask;
|
G.non_DFL_mask = mask;
|
||||||
|
|
||||||
if (!second_time)
|
|
||||||
sigprocmask(SIG_SETMASK, NULL, &G.blocked_set);
|
|
||||||
sig = 0;
|
sig = 0;
|
||||||
while (mask) {
|
while (mask) {
|
||||||
if (mask & 1)
|
if (mask & 1)
|
||||||
@ -6701,18 +6721,21 @@ static void block_signals(int second_time)
|
|||||||
}
|
}
|
||||||
sigdelset(&G.blocked_set, SIGCHLD);
|
sigdelset(&G.blocked_set, SIGCHLD);
|
||||||
|
|
||||||
sigprocmask(SIG_SETMASK, &G.blocked_set,
|
if (memcmp(&old_blocked_set, &G.blocked_set, sizeof(old_blocked_set)) != 0)
|
||||||
second_time ? NULL : &G.inherited_set);
|
sigprocmask(SIG_SETMASK, &G.blocked_set, NULL);
|
||||||
|
|
||||||
/* POSIX allows shell to re-enable SIGCHLD
|
/* POSIX allows shell to re-enable SIGCHLD
|
||||||
* even if it was SIG_IGN on entry */
|
* even if it was SIG_IGN on entry */
|
||||||
#if ENABLE_HUSH_FAST
|
#if ENABLE_HUSH_FAST
|
||||||
G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
|
G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
|
||||||
if (!second_time)
|
if (!G.inherited_set_is_saved)
|
||||||
signal(SIGCHLD, SIGCHLD_handler);
|
signal(SIGCHLD, SIGCHLD_handler);
|
||||||
#else
|
#else
|
||||||
if (!second_time)
|
if (!G.inherited_set_is_saved)
|
||||||
signal(SIGCHLD, SIG_DFL);
|
signal(SIGCHLD, SIG_DFL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
G.inherited_set_is_saved = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
@ -6774,7 +6797,6 @@ 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;
|
||||||
unsigned builtin_argc;
|
unsigned builtin_argc;
|
||||||
char **e;
|
char **e;
|
||||||
@ -6865,10 +6887,9 @@ int hush_main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Shell is non-interactive at first. We need to call
|
/* 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.
|
* "sh -c <cmds>" or login shell's /etc/profile and friends.
|
||||||
* If we later decide that we are interactive, we run block_signals(0)
|
* If we later decide that we are interactive, we run init_sigmasks()
|
||||||
* (or re-run block_signals(1) if we ran block_signals(0) before)
|
|
||||||
* in order to intercept (more) signals.
|
* in order to intercept (more) signals.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -6908,7 +6929,7 @@ int hush_main(int argc, char **argv)
|
|||||||
/* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
|
/* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
|
||||||
const struct built_in_command *x;
|
const struct built_in_command *x;
|
||||||
|
|
||||||
block_signals(0); /* 0: called 1st time */
|
init_sigmasks();
|
||||||
x = find_builtin(optarg);
|
x = find_builtin(optarg);
|
||||||
if (x) { /* paranoia */
|
if (x) { /* paranoia */
|
||||||
G.global_argc -= builtin_argc; /* skip [BARGV...] "" */
|
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_argv[0] = argv[0];
|
||||||
G.global_argc--;
|
G.global_argc--;
|
||||||
} /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
|
} /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
|
||||||
block_signals(0); /* 0: called 1st time */
|
init_sigmasks();
|
||||||
parse_and_run_string(optarg);
|
parse_and_run_string(optarg);
|
||||||
goto final_return;
|
goto final_return;
|
||||||
case 'i':
|
case 'i':
|
||||||
@ -6940,7 +6961,9 @@ int hush_main(int argc, char **argv)
|
|||||||
case '<': /* "big heredoc" support */
|
case '<': /* "big heredoc" support */
|
||||||
full_write(STDOUT_FILENO, optarg, strlen(optarg));
|
full_write(STDOUT_FILENO, optarg, strlen(optarg));
|
||||||
_exit(0);
|
_exit(0);
|
||||||
case '$':
|
case '$': {
|
||||||
|
unsigned long long empty_trap_mask;
|
||||||
|
|
||||||
G.root_pid = bb_strtou(optarg, &optarg, 16);
|
G.root_pid = bb_strtou(optarg, &optarg, 16);
|
||||||
optarg++;
|
optarg++;
|
||||||
G.root_ppid = bb_strtou(optarg, &optarg, 16);
|
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);
|
G.last_exitcode = bb_strtou(optarg, &optarg, 16);
|
||||||
optarg++;
|
optarg++;
|
||||||
builtin_argc = bb_strtou(optarg, &optarg, 16);
|
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
|
# if ENABLE_HUSH_LOOPS
|
||||||
optarg++;
|
optarg++;
|
||||||
G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
|
G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
|
||||||
# endif
|
# endif
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 'R':
|
case 'R':
|
||||||
case 'V':
|
case 'V':
|
||||||
set_local_var(xstrdup(optarg), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ opt == 'R');
|
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");
|
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 */
|
init_sigmasks();
|
||||||
signal_mask_is_inited = 1;
|
|
||||||
parse_and_run_file(input);
|
parse_and_run_file(input);
|
||||||
fclose(input);
|
fclose(input);
|
||||||
}
|
}
|
||||||
@ -7023,8 +7060,7 @@ int hush_main(int argc, char **argv)
|
|||||||
G.global_argc = argc - optind;
|
G.global_argc = argc - optind;
|
||||||
input = xfopen_for_read(argv[optind]);
|
input = xfopen_for_read(argv[optind]);
|
||||||
close_on_exec_on(fileno(input));
|
close_on_exec_on(fileno(input));
|
||||||
if (!signal_mask_is_inited)
|
init_sigmasks();
|
||||||
block_signals(0); /* 0: called 1st time */
|
|
||||||
parse_and_run_file(input);
|
parse_and_run_file(input);
|
||||||
#if ENABLE_FEATURE_CLEAN_UP
|
#if ENABLE_FEATURE_CLEAN_UP
|
||||||
fclose(input);
|
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.
|
/* 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,
|
/* 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 some signals */
|
||||||
block_signals(signal_mask_is_inited);
|
init_sigmasks();
|
||||||
|
|
||||||
if (G_saved_tty_pgrp) {
|
if (G_saved_tty_pgrp) {
|
||||||
/* Set other signals to restore 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
|
/* -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) */
|
||||||
enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
|
enable_restore_tty_pgrp_on_exit(); /* sets die_sleep = -1 */
|
||||||
} else if (!signal_mask_is_inited) {
|
} else {
|
||||||
block_signals(0); /* 0: called 1st time */
|
init_sigmasks();
|
||||||
} /* else: block_signals(0) was done before */
|
}
|
||||||
#elif ENABLE_HUSH_INTERACTIVE
|
#elif ENABLE_HUSH_INTERACTIVE
|
||||||
/* No job control compiled in, only prompt/line editing */
|
/* No job control compiled in, only prompt/line editing */
|
||||||
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
|
if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
|
||||||
@ -7117,15 +7153,11 @@ int hush_main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
if (G_interactive_fd) {
|
if (G_interactive_fd) {
|
||||||
close_on_exec_on(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
|
#else
|
||||||
/* We have interactiveness code disabled */
|
/* We have interactiveness code disabled */
|
||||||
if (!signal_mask_is_inited) {
|
init_sigmasks();
|
||||||
block_signals(0);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
/* bash:
|
/* bash:
|
||||||
* if interactive but not a login shell, sources ~/.bashrc
|
* 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]);
|
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'",
|
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) */
|
||||||
|
@ -1,6 +1,21 @@
|
|||||||
|
trap -- '' HUP
|
||||||
|
trap -- '' QUIT
|
||||||
|
trap -- '' SYS
|
||||||
Ok
|
Ok
|
||||||
|
trap -- '' HUP
|
||||||
|
trap -- '' QUIT
|
||||||
|
trap -- '' SYS
|
||||||
Ok
|
Ok
|
||||||
|
trap -- '' HUP
|
||||||
|
trap -- '' QUIT
|
||||||
|
trap -- '' SYS
|
||||||
Ok
|
Ok
|
||||||
|
trap -- '' HUP
|
||||||
|
trap -- '' QUIT
|
||||||
|
trap -- '' SYS
|
||||||
Ok
|
Ok
|
||||||
|
trap -- '' HUP
|
||||||
|
trap -- '' QUIT
|
||||||
|
trap -- '' SYS
|
||||||
TERM
|
TERM
|
||||||
Done
|
Done
|
||||||
|
@ -12,9 +12,9 @@ trap 'bad: caught WINCH' WINCH
|
|||||||
trap 'bad: caught TERM' TERM
|
trap 'bad: caught TERM' TERM
|
||||||
|
|
||||||
# using bash, because we don't have $PPID (yet)
|
# using bash, because we don't have $PPID (yet)
|
||||||
(bash -c 'kill -HUP $PPID'; echo Ok)
|
(trap; bash -c 'kill -HUP $PPID'; echo Ok)
|
||||||
(bash -c 'kill -QUIT $PPID'; echo Ok)
|
(trap; bash -c 'kill -QUIT $PPID'; echo Ok)
|
||||||
(bash -c 'kill -SYS $PPID'; echo Ok)
|
(trap; bash -c 'kill -SYS $PPID'; echo Ok)
|
||||||
(bash -c 'kill -WINCH $PPID'; echo Ok)
|
(trap; bash -c 'kill -WINCH $PPID'; echo Ok)
|
||||||
(bash -c 'kill -TERM $PPID'; echo Bad: TERM is not reset)
|
(trap; bash -c 'kill -TERM $PPID'; echo Bad: TERM is not reset)
|
||||||
echo Done
|
echo Done
|
||||||
|
Loading…
x
Reference in New Issue
Block a user