hush: replace signal handling machinery
With new version of signal handling, read builtin should be less buggy wrt signals. function old new delta install_sighandlers - 121 +121 switch_off_special_sigs - 84 +84 pick_sighandler - 58 +58 install_special_sighandlers - 47 +47 builtin_wait 284 319 +35 record_pending_signo - 21 +21 execvp_or_die 43 48 +5 file_get 290 288 -2 run_list 1004 998 -6 static.zero_timespec 8 - -8 sigprocmask_set 14 - -14 sigwaitinfo 23 - -23 record_signal 23 - -23 __GI_sigwaitinfo 23 - -23 sigtimedwait 25 - -25 builtin_trap 417 392 -25 __GI_sigtimedwait 25 - -25 hush_main 1003 965 -38 check_and_run_traps 263 217 -46 __rt_sigtimedwait 52 - -52 reset_traps_to_defaults 213 126 -87 init_sigmasks 198 - -198 builtin_read 536 197 -339 ------------------------------------------------------------------------------ (add/remove: 5/10 grow/shrink: 2/7 up/down: 371/-934) Total: -563 bytes text data bss dec hex filename 903075 936 17736 921747 e1093 busybox_old 902547 936 17736 921219 e0e83 busybox_unstripped Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
10c0131a8a
commit
9d6cbafe72
512
shell/hush.c
512
shell/hush.c
@ -106,6 +106,10 @@
|
|||||||
# define PIPE_BUF 4096 /* amount of buffering in a pipe */
|
# define PIPE_BUF 4096 /* amount of buffering in a pipe */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Not every libc has sighandler_t. Fix it */
|
||||||
|
typedef void (*hush_sighandler_t)(int);
|
||||||
|
#define sighandler_t hush_sighandler_t
|
||||||
|
|
||||||
//config:config HUSH
|
//config:config HUSH
|
||||||
//config: bool "hush"
|
//config: bool "hush"
|
||||||
//config: default y
|
//config: default y
|
||||||
@ -764,7 +768,6 @@ 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;
|
||||||
@ -794,21 +797,20 @@ struct globals {
|
|||||||
#endif
|
#endif
|
||||||
/* Which signals have non-DFL handler (even with no traps set)?
|
/* Which signals have non-DFL handler (even with no traps set)?
|
||||||
* Set at the start to:
|
* Set at the start to:
|
||||||
* (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOB_SIGS)
|
* (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
|
||||||
* SPECIAL_INTERACTIVE_SIGS are cleared after fork.
|
* SPECIAL_INTERACTIVE_SIGS are cleared after fork.
|
||||||
|
* The rest is cleared right before execv syscalls.
|
||||||
* Other than these two times, never modified.
|
* Other than these two times, never modified.
|
||||||
*/
|
*/
|
||||||
unsigned special_sig_mask;
|
unsigned special_sig_mask;
|
||||||
|
#if ENABLE_HUSH_JOB
|
||||||
|
unsigned fatal_sig_mask;
|
||||||
|
#define G_fatal_sig_mask G.fatal_sig_mask
|
||||||
|
#else
|
||||||
|
#define G_fatal_sig_mask 0
|
||||||
|
#endif
|
||||||
char **traps; /* char *traps[NSIG] */
|
char **traps; /* char *traps[NSIG] */
|
||||||
/* Signal mask on the entry to the (top-level) shell. Never modified. */
|
sigset_t pending_set;
|
||||||
sigset_t inherited_set;
|
|
||||||
/* Starts equal to inherited_set,
|
|
||||||
* but shell-special signals are added and SIGCHLD is removed.
|
|
||||||
* When a trap is set/cleared, signal is added to/removed from it:
|
|
||||||
*/
|
|
||||||
sigset_t blocked_set;
|
|
||||||
/* Used by read() */
|
|
||||||
sigset_t detected_set;
|
|
||||||
#if HUSH_DEBUG
|
#if HUSH_DEBUG
|
||||||
unsigned long memleak_value;
|
unsigned long memleak_value;
|
||||||
int debug_indent;
|
int debug_indent;
|
||||||
@ -1337,8 +1339,8 @@ static void restore_G_args(save_arg_t *sv, char **argv)
|
|||||||
* (What happens to signals which are IGN on shell start?)
|
* (What happens to signals which are IGN on shell start?)
|
||||||
* (What happens with signal mask on shell start?)
|
* (What happens with signal mask on shell start?)
|
||||||
*
|
*
|
||||||
* Implementation in hush
|
* Old implementation
|
||||||
* ======================
|
* ==================
|
||||||
* We use in-kernel pending signal mask to determine which signals were sent.
|
* We use in-kernel pending signal mask to determine which signals were sent.
|
||||||
* We block all signals which we don't want to take action immediately,
|
* We block all signals which we don't want to take action immediately,
|
||||||
* i.e. we block all signals which need to have special handling as described
|
* i.e. we block all signals which need to have special handling as described
|
||||||
@ -1369,6 +1371,49 @@ static void restore_G_args(save_arg_t *sv, char **argv)
|
|||||||
* 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.
|
||||||
|
*
|
||||||
|
* Problem: the above approach makes it unwieldy to catch signals while
|
||||||
|
* we are in read builtin, of while we read commands from stdin:
|
||||||
|
* masked signals are not visible!
|
||||||
|
*
|
||||||
|
* New implementation
|
||||||
|
* ==================
|
||||||
|
* We record each signal we are interested in by installing signal handler
|
||||||
|
* for them - a bit like emulating kernel pending signal mask in userspace.
|
||||||
|
* We are interested in: signals which need to have special handling
|
||||||
|
* as described above, and all signals which have traps set.
|
||||||
|
* Signals are rocorded in pending_set.
|
||||||
|
* After each pipe execution, we extract any pending signals
|
||||||
|
* and act on them.
|
||||||
|
*
|
||||||
|
* unsigned special_sig_mask: a mask of shell-special signals.
|
||||||
|
* unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp.
|
||||||
|
* char *traps[sig] if trap for sig is set (even if it's '').
|
||||||
|
* sigset_t pending_set: set of sigs we received.
|
||||||
|
*
|
||||||
|
* "trap - SIGxxx":
|
||||||
|
* if sig is in special_sig_mask, set handler back to:
|
||||||
|
* record_pending_signo, or to IGN if it's a tty stop signal
|
||||||
|
* if sig is in fatal_sig_mask, set handler back to sigexit.
|
||||||
|
* else: set handler back to SIG_DFL
|
||||||
|
* "trap 'cmd' SIGxxx":
|
||||||
|
* set handler to record_pending_signo.
|
||||||
|
* "trap '' SIGxxx":
|
||||||
|
* set handler to SIG_IGN.
|
||||||
|
* after [v]fork, if we plan to be a shell:
|
||||||
|
* set signals with special interactive handling to SIG_DFL
|
||||||
|
* (because child shell is not interactive),
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* To make wait builtin interruptible, we handle SIGCHLD as special signal,
|
||||||
|
* otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it.
|
||||||
|
*
|
||||||
|
* Note (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.
|
||||||
*/
|
*/
|
||||||
enum {
|
enum {
|
||||||
SPECIAL_INTERACTIVE_SIGS = 0
|
SPECIAL_INTERACTIVE_SIGS = 0
|
||||||
@ -1376,26 +1421,25 @@ enum {
|
|||||||
| (1 << SIGINT)
|
| (1 << SIGINT)
|
||||||
| (1 << SIGHUP)
|
| (1 << SIGHUP)
|
||||||
,
|
,
|
||||||
SPECIAL_JOB_SIGS = 0
|
SPECIAL_JOBSTOP_SIGS = 0
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
| (1 << SIGTTIN)
|
| (1 << SIGTTIN)
|
||||||
| (1 << SIGTTOU)
|
| (1 << SIGTTOU)
|
||||||
| (1 << SIGTSTP)
|
| (1 << SIGTSTP)
|
||||||
#endif
|
#endif
|
||||||
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sigprocmask_set(sigset_t *set)
|
static void record_pending_signo(int sig)
|
||||||
{
|
{
|
||||||
sigprocmask(SIG_SETMASK, set, NULL);
|
sigaddset(&G.pending_set, sig);
|
||||||
}
|
|
||||||
|
|
||||||
#if ENABLE_HUSH_FAST
|
#if ENABLE_HUSH_FAST
|
||||||
static void SIGCHLD_handler(int sig UNUSED_PARAM)
|
if (sig == SIGCHLD) {
|
||||||
{
|
G.count_SIGCHLD++;
|
||||||
G.count_SIGCHLD++;
|
|
||||||
//bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
|
//bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
|
|
||||||
@ -1433,6 +1477,31 @@ static void sigexit(int sig)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static sighandler_t pick_sighandler(unsigned sig)
|
||||||
|
{
|
||||||
|
sighandler_t handler = SIG_DFL;
|
||||||
|
if (sig < sizeof(unsigned)*8) {
|
||||||
|
unsigned sigmask = (1 << sig);
|
||||||
|
|
||||||
|
#if ENABLE_HUSH_JOB
|
||||||
|
/* sig is fatal? */
|
||||||
|
if (G_fatal_sig_mask & sigmask)
|
||||||
|
handler = sigexit;
|
||||||
|
#endif
|
||||||
|
/* sig has special handling? */
|
||||||
|
else if (G.special_sig_mask & sigmask)
|
||||||
|
handler = record_pending_signo;
|
||||||
|
/* TTIN/TTOU/TSTS can't be set to record_pending_signo
|
||||||
|
* in order to ignore them: they will be raised
|
||||||
|
* in an endless loop then when we try to do some
|
||||||
|
* terminal ioctls! We do nave to _ignore_ these.
|
||||||
|
*/
|
||||||
|
if (SPECIAL_JOBSTOP_SIGS & sigmask)
|
||||||
|
handler = SIG_IGN;
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
/* Restores tty foreground process group, and exits. */
|
/* Restores tty foreground process group, and exits. */
|
||||||
static void hush_exit(int exitcode) NORETURN;
|
static void hush_exit(int exitcode) NORETURN;
|
||||||
static void hush_exit(int exitcode)
|
static void hush_exit(int exitcode)
|
||||||
@ -1478,39 +1547,30 @@ static void hush_exit(int exitcode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int check_and_run_traps(int sig)
|
//TODO: return a mask of ALL handled sigs?
|
||||||
|
static int check_and_run_traps(void)
|
||||||
{
|
{
|
||||||
/* I want it in rodata, not in bss.
|
|
||||||
* gcc 4.2.1 puts it in rodata only if it has { 0, 0 }
|
|
||||||
* initializer. But other compilers may still use bss.
|
|
||||||
* TODO: find more portable solution.
|
|
||||||
*/
|
|
||||||
static const struct timespec zero_timespec = { 0, 0 };
|
|
||||||
smalluint save_rcode;
|
|
||||||
int last_sig = 0;
|
int last_sig = 0;
|
||||||
|
|
||||||
if (sig)
|
|
||||||
goto got_sig;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
if (!sigisemptyset(&G.detected_set)) {
|
int sig;
|
||||||
sig = 0;
|
|
||||||
do {
|
|
||||||
sig++;
|
|
||||||
if (sigismember(&G.detected_set, sig)) {
|
|
||||||
sigdelset(&G.detected_set, sig);
|
|
||||||
goto got_sig;
|
|
||||||
}
|
|
||||||
} while (sig < NSIG);
|
|
||||||
}
|
|
||||||
|
|
||||||
sig = sigtimedwait(&G.blocked_set, NULL, &zero_timespec);
|
if (sigisemptyset(&G.pending_set))
|
||||||
if (sig <= 0)
|
|
||||||
break;
|
break;
|
||||||
|
sig = 0;
|
||||||
|
do {
|
||||||
|
sig++;
|
||||||
|
if (sigismember(&G.pending_set, sig)) {
|
||||||
|
sigdelset(&G.pending_set, sig);
|
||||||
|
goto got_sig;
|
||||||
|
}
|
||||||
|
} while (sig < NSIG);
|
||||||
|
break;
|
||||||
got_sig:
|
got_sig:
|
||||||
if (G.traps && G.traps[sig]) {
|
if (G.traps && 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;
|
||||||
char *argv[3];
|
char *argv[3];
|
||||||
/* argv[0] is unused */
|
/* argv[0] is unused */
|
||||||
argv[1] = G.traps[sig];
|
argv[1] = G.traps[sig];
|
||||||
@ -1524,12 +1584,6 @@ static int check_and_run_traps(int sig)
|
|||||||
}
|
}
|
||||||
/* not a trap: special action */
|
/* not a trap: special action */
|
||||||
switch (sig) {
|
switch (sig) {
|
||||||
#if ENABLE_HUSH_FAST
|
|
||||||
case SIGCHLD:
|
|
||||||
G.count_SIGCHLD++;
|
|
||||||
//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
case SIGINT:
|
case SIGINT:
|
||||||
/* Builtin was ^C'ed, make it look prettier: */
|
/* Builtin was ^C'ed, make it look prettier: */
|
||||||
bb_putchar('\n');
|
bb_putchar('\n');
|
||||||
@ -1550,12 +1604,22 @@ static int check_and_run_traps(int sig)
|
|||||||
}
|
}
|
||||||
sigexit(SIGHUP);
|
sigexit(SIGHUP);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if ENABLE_HUSH_FAST
|
||||||
|
case SIGCHLD:
|
||||||
|
G.count_SIGCHLD++;
|
||||||
|
//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
|
||||||
|
/* Note:
|
||||||
|
* We dont do 'last_sig = sig' here -> NOT returning this sig.
|
||||||
|
* This simplifies wait builtin a bit.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
default: /* ignored: */
|
default: /* ignored: */
|
||||||
/* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
|
/* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
|
||||||
/* note:
|
/* Note:
|
||||||
* we dont do 'last_sig = sig' here -> NOT returning this sig.
|
* We dont do 'last_sig = sig' here -> NOT returning this sig.
|
||||||
* example: wait is not interrupted by TERM
|
* Example: wait is not interrupted by TERM
|
||||||
* in interactive shell, because TERM is ignored.
|
* in interactive shell, because TERM is ignored.
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
@ -1948,7 +2012,7 @@ static void get_user_input(struct in_str *i)
|
|||||||
* only after <Enter>. (^C will work) */
|
* only after <Enter>. (^C will work) */
|
||||||
r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1);
|
r = read_line_input(G.line_input_state, prompt_str, G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, /*timeout*/ -1);
|
||||||
/* catch *SIGINT* etc (^C is handled by read_line_input) */
|
/* catch *SIGINT* etc (^C is handled by read_line_input) */
|
||||||
check_and_run_traps(0);
|
check_and_run_traps();
|
||||||
} while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
|
} while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
|
||||||
i->eof_flag = (r < 0);
|
i->eof_flag = (r < 0);
|
||||||
if (i->eof_flag) { /* EOF/error detected */
|
if (i->eof_flag) { /* EOF/error detected */
|
||||||
@ -1964,7 +2028,7 @@ static void get_user_input(struct in_str *i)
|
|||||||
* $ <[enter], repeatedly...>
|
* $ <[enter], repeatedly...>
|
||||||
* Without check_and_run_traps, handler never runs.
|
* Without check_and_run_traps, handler never runs.
|
||||||
*/
|
*/
|
||||||
check_and_run_traps(0);
|
check_and_run_traps();
|
||||||
fputs(prompt_str, stdout);
|
fputs(prompt_str, stdout);
|
||||||
}
|
}
|
||||||
fflush_all();
|
fflush_all();
|
||||||
@ -5368,6 +5432,25 @@ void re_execute_shell(char ***to_free, const char *s,
|
|||||||
char *g_argv0, char **g_argv,
|
char *g_argv0, char **g_argv,
|
||||||
char **builtin_argv) NORETURN;
|
char **builtin_argv) NORETURN;
|
||||||
|
|
||||||
|
static void switch_off_special_sigs(unsigned mask)
|
||||||
|
{
|
||||||
|
unsigned sig = 0;
|
||||||
|
while ((mask >>= 1) != 0) {
|
||||||
|
sig++;
|
||||||
|
if (!(mask & 1))
|
||||||
|
continue;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
/* We are here only if no trap or trap was not '' */
|
||||||
|
signal(sig, SIG_DFL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void reset_traps_to_defaults(void)
|
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
|
||||||
@ -5381,44 +5464,35 @@ static void reset_traps_to_defaults(void)
|
|||||||
* Testcase: (while :; do :; done) + ^Z should background.
|
* Testcase: (while :; do :; done) + ^Z should background.
|
||||||
* Same goes for SIGTERM, SIGHUP, SIGINT.
|
* Same goes for SIGTERM, SIGHUP, SIGINT.
|
||||||
*/
|
*/
|
||||||
if (!G.traps && !(G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS))
|
mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
|
||||||
return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */
|
if (!G.traps && !mask)
|
||||||
|
return; /* already no traps and no special sigs */
|
||||||
|
|
||||||
/* Switching off SPECIAL_INTERACTIVE_SIGS.
|
/* Switch off special sigs */
|
||||||
* Stupid. It can be done with *single* &= op, but we can't use
|
switch_off_special_sigs(mask);
|
||||||
* the fact that G.blocked_set is implemented as a bitmask
|
#if ENABLE_HUSH_JOB
|
||||||
* in libc... */
|
G_fatal_sig_mask = 0;
|
||||||
mask = SPECIAL_INTERACTIVE_SIGS;
|
#endif
|
||||||
sig = 0;
|
|
||||||
while ((mask >>= 1) != 0) {
|
|
||||||
sig++;
|
|
||||||
if (mask & 1) {
|
|
||||||
/* Careful. Only if no trap or trap is not "" */
|
|
||||||
if (!G.traps || !G.traps[sig] || G.traps[sig][0])
|
|
||||||
sigdelset(&G.blocked_set, sig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Our homegrown sig mask is saner to work with :) */
|
|
||||||
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 */
|
||||||
|
|
||||||
/* Resetting all traps to default except empty ones */
|
if (!G.traps)
|
||||||
mask = G.special_sig_mask;
|
return;
|
||||||
if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) {
|
|
||||||
if (!G.traps[sig] || !G.traps[sig][0])
|
/* Reset all sigs to default except ones with empty traps */
|
||||||
continue;
|
for (sig = 0; sig < NSIG; sig++) {
|
||||||
|
if (!G.traps[sig])
|
||||||
|
continue; /* no trap: nothing to do */
|
||||||
|
if (!G.traps[sig][0])
|
||||||
|
continue; /* empty trap: has to remain SIG_IGN */
|
||||||
|
/* 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 0 (EXIT) */
|
/* There is no signal for trap 0 (EXIT) */
|
||||||
if (sig == 0)
|
if (sig == 0)
|
||||||
continue;
|
continue;
|
||||||
/* There was a trap handler, we just removed it.
|
signal(sig, pick_sighandler(sig));
|
||||||
* But if sig still has non-DFL handling,
|
|
||||||
* we should not unblock the sig. */
|
|
||||||
if (mask & 1)
|
|
||||||
continue;
|
|
||||||
sigdelset(&G.blocked_set, sig);
|
|
||||||
}
|
}
|
||||||
sigprocmask_set(&G.blocked_set);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else /* !BB_MMU */
|
#else /* !BB_MMU */
|
||||||
@ -5463,6 +5537,7 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5548,7 +5623,7 @@ static void re_execute_shell(char ***to_free, const char *s,
|
|||||||
|
|
||||||
do_exec:
|
do_exec:
|
||||||
debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
|
debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
|
||||||
sigprocmask_set(&G.inherited_set);
|
switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
|
||||||
execve(bb_busybox_exec_path, argv, pp);
|
execve(bb_busybox_exec_path, argv, pp);
|
||||||
/* Fallback. Useful for init=/bin/hush usage etc */
|
/* Fallback. Useful for init=/bin/hush usage etc */
|
||||||
if (argv[0][0] == '/')
|
if (argv[0][0] == '/')
|
||||||
@ -6202,7 +6277,7 @@ static void execvp_or_die(char **argv) NORETURN;
|
|||||||
static void execvp_or_die(char **argv)
|
static void execvp_or_die(char **argv)
|
||||||
{
|
{
|
||||||
debug_printf_exec("execing '%s'\n", argv[0]);
|
debug_printf_exec("execing '%s'\n", argv[0]);
|
||||||
sigprocmask_set(&G.inherited_set);
|
switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
|
||||||
execvp(argv[0], argv);
|
execvp(argv[0], argv);
|
||||||
bb_perror_msg("can't execute '%s'", argv[0]);
|
bb_perror_msg("can't execute '%s'", argv[0]);
|
||||||
_exit(127); /* bash compat */
|
_exit(127); /* bash compat */
|
||||||
@ -6334,7 +6409,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
|
|||||||
# endif
|
# endif
|
||||||
/* Re-exec ourselves */
|
/* Re-exec ourselves */
|
||||||
debug_printf_exec("re-execing applet '%s'\n", argv[0]);
|
debug_printf_exec("re-execing applet '%s'\n", argv[0]);
|
||||||
sigprocmask_set(&G.inherited_set);
|
switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
|
||||||
execv(bb_busybox_exec_path, argv);
|
execv(bb_busybox_exec_path, argv);
|
||||||
/* If they called chroot or otherwise made the binary no longer
|
/* If they called chroot or otherwise made the binary no longer
|
||||||
* executable, fall through */
|
* executable, fall through */
|
||||||
@ -7033,9 +7108,6 @@ static NOINLINE int run_pipe(struct pipe *pi)
|
|||||||
if (setup_redirects(command, NULL))
|
if (setup_redirects(command, NULL))
|
||||||
_exit(1);
|
_exit(1);
|
||||||
|
|
||||||
/* Restore default handlers just prior to exec */
|
|
||||||
/*signal(SIGCHLD, SIG_DFL); - so far we don't have any handlers */
|
|
||||||
|
|
||||||
/* Stores to nommu_save list of env vars putenv'ed
|
/* Stores to nommu_save list of env vars putenv'ed
|
||||||
* (NOMMU, on MMU we don't need that) */
|
* (NOMMU, on MMU we don't need that) */
|
||||||
/* cast away volatility... */
|
/* cast away volatility... */
|
||||||
@ -7313,7 +7385,7 @@ static int run_list(struct pipe *pi)
|
|||||||
* and we don't need to wait for anything. */
|
* and we don't need to wait for anything. */
|
||||||
G.last_exitcode = rcode;
|
G.last_exitcode = rcode;
|
||||||
debug_printf_exec(": builtin/func exitcode %d\n", rcode);
|
debug_printf_exec(": builtin/func exitcode %d\n", rcode);
|
||||||
check_and_run_traps(0);
|
check_and_run_traps();
|
||||||
#if ENABLE_HUSH_LOOPS
|
#if ENABLE_HUSH_LOOPS
|
||||||
/* Was it "break" or "continue"? */
|
/* Was it "break" or "continue"? */
|
||||||
if (G.flag_break_continue) {
|
if (G.flag_break_continue) {
|
||||||
@ -7345,7 +7417,7 @@ static int run_list(struct pipe *pi)
|
|||||||
/* even bash 3.2 doesn't do that well with nested bg:
|
/* even bash 3.2 doesn't do that well with nested bg:
|
||||||
* try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
|
* try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
|
||||||
* I'm NOT treating inner &'s as jobs */
|
* I'm NOT treating inner &'s as jobs */
|
||||||
check_and_run_traps(0);
|
check_and_run_traps();
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
if (G.run_list_level == 1)
|
if (G.run_list_level == 1)
|
||||||
insert_bg_job(pi);
|
insert_bg_job(pi);
|
||||||
@ -7360,13 +7432,13 @@ static int run_list(struct pipe *pi)
|
|||||||
/* Waits for completion, then fg's main shell */
|
/* Waits for completion, then fg's main shell */
|
||||||
rcode = checkjobs_and_fg_shell(pi);
|
rcode = checkjobs_and_fg_shell(pi);
|
||||||
debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
|
debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
|
||||||
check_and_run_traps(0);
|
check_and_run_traps();
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{ /* This one just waits for completion */
|
{ /* This one just waits for completion */
|
||||||
rcode = checkjobs(pi);
|
rcode = checkjobs(pi);
|
||||||
debug_printf_exec(": checkjobs exitcode %d\n", rcode);
|
debug_printf_exec(": checkjobs exitcode %d\n", rcode);
|
||||||
check_and_run_traps(0);
|
check_and_run_traps();
|
||||||
}
|
}
|
||||||
G.last_exitcode = rcode;
|
G.last_exitcode = rcode;
|
||||||
}
|
}
|
||||||
@ -7437,58 +7509,61 @@ static int run_and_free_list(struct pipe *pi)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Called a few times only (or even once if "sh -c") */
|
static void install_sighandlers(unsigned mask)
|
||||||
static void init_sigmasks(void)
|
{
|
||||||
|
sighandler_t old_handler;
|
||||||
|
unsigned sig = 0;
|
||||||
|
while ((mask >>= 1) != 0) {
|
||||||
|
sig++;
|
||||||
|
if (!(mask & 1))
|
||||||
|
continue;
|
||||||
|
old_handler = signal(sig, pick_sighandler(sig));
|
||||||
|
/* POSIX allows shell to re-enable SIGCHLD
|
||||||
|
* even if it was SIG_IGN on entry.
|
||||||
|
* Therefore we skip IGN check for it:
|
||||||
|
*/
|
||||||
|
if (sig == SIGCHLD)
|
||||||
|
continue;
|
||||||
|
if (old_handler == SIG_IGN) {
|
||||||
|
/* oops... restore back to IGN, and record this fact */
|
||||||
|
signal(sig, old_handler);
|
||||||
|
if (!G.traps)
|
||||||
|
G.traps = xzalloc(sizeof(G.traps[0]) * NSIG);
|
||||||
|
free(G.traps[sig]);
|
||||||
|
G.traps[sig] = xzalloc(1); /* == xstrdup(""); */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called a few times only (or even once if "sh -c") */
|
||||||
|
static void install_special_sighandlers(void)
|
||||||
{
|
{
|
||||||
unsigned sig;
|
|
||||||
unsigned mask;
|
unsigned mask;
|
||||||
|
|
||||||
/* POSIX allows shell to re-enable SIGCHLD
|
if (G.special_sig_mask != 0)
|
||||||
* even if it was SIG_IGN on entry */
|
return;
|
||||||
if (!G.inherited_set_is_saved) {
|
|
||||||
#if ENABLE_HUSH_FAST
|
|
||||||
signal(SIGCHLD, SIGCHLD_handler);
|
|
||||||
#else
|
|
||||||
signal(SIGCHLD, SIG_DFL);
|
|
||||||
#endif
|
|
||||||
sigprocmask(SIG_SETMASK, NULL, &G.blocked_set);
|
|
||||||
G.inherited_set = G.blocked_set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Which signals are shell-special? */
|
/* Which signals are shell-special? */
|
||||||
mask = (1 << SIGQUIT);
|
mask = (1 << SIGQUIT) | (1 << SIGCHLD);
|
||||||
if (G_interactive_fd) {
|
if (G_interactive_fd) {
|
||||||
mask |= SPECIAL_INTERACTIVE_SIGS;
|
mask |= SPECIAL_INTERACTIVE_SIGS;
|
||||||
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_JOB_SIGS;
|
mask |= SPECIAL_JOBSTOP_SIGS;
|
||||||
}
|
}
|
||||||
G.special_sig_mask = mask;
|
G.special_sig_mask = mask;
|
||||||
|
|
||||||
/* Block them. And unblock SIGCHLD */
|
install_sighandlers(mask);
|
||||||
sig = 0;
|
|
||||||
while ((mask >>= 1) != 0) {
|
|
||||||
sig++;
|
|
||||||
if (mask & 1)
|
|
||||||
sigaddset(&G.blocked_set, sig);
|
|
||||||
}
|
|
||||||
sigdelset(&G.blocked_set, SIGCHLD);
|
|
||||||
|
|
||||||
if (memcmp(&G.inherited_set, &G.blocked_set, sizeof(G.inherited_set)) != 0)
|
|
||||||
sigprocmask_set(&G.blocked_set);
|
|
||||||
|
|
||||||
G.inherited_set_is_saved = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_HUSH_JOB
|
#if ENABLE_HUSH_JOB
|
||||||
/* helper */
|
/* helper */
|
||||||
/* Set handlers to restore tty pgrp and exit */
|
/* Set handlers to restore tty pgrp and exit */
|
||||||
static void set_fatal_handlers_to_sigexit(void)
|
static void install_fatal_sighandlers(void)
|
||||||
{
|
{
|
||||||
void (*handler)(int);
|
unsigned mask;
|
||||||
unsigned fatal_sigs, sig;
|
|
||||||
|
|
||||||
/* We will restore tty pgrp on these signals */
|
/* We will restore tty pgrp on these signals */
|
||||||
fatal_sigs = 0
|
mask = 0
|
||||||
+ (1 << SIGILL ) * HUSH_DEBUG
|
+ (1 << SIGILL ) * HUSH_DEBUG
|
||||||
+ (1 << SIGFPE ) * HUSH_DEBUG
|
+ (1 << SIGFPE ) * HUSH_DEBUG
|
||||||
+ (1 << SIGBUS ) * HUSH_DEBUG
|
+ (1 << SIGBUS ) * HUSH_DEBUG
|
||||||
@ -7505,22 +7580,13 @@ static void set_fatal_handlers_to_sigexit(void)
|
|||||||
/*+ (1 << SIGTERM)*/
|
/*+ (1 << SIGTERM)*/
|
||||||
/*+ (1 << SIGINT )*/
|
/*+ (1 << SIGINT )*/
|
||||||
;
|
;
|
||||||
|
/* special_sig_mask'ed signals are set to record_pending_signo
|
||||||
/* special_sig_mask'ed signals are, well, masked,
|
|
||||||
* no need to set handler for them.
|
* no need to set handler for them.
|
||||||
*/
|
*/
|
||||||
fatal_sigs &= ~G.special_sig_mask;
|
/*mask &= ~G.special_sig_mask; - they never overlap */
|
||||||
|
G_fatal_sig_mask = mask;
|
||||||
|
|
||||||
/* For each sig in fatal_sigs... */
|
install_sighandlers(mask);
|
||||||
sig = 0;
|
|
||||||
while ((fatal_sigs >>= 1) != 0) {
|
|
||||||
sig++;
|
|
||||||
if (!(fatal_sigs & 1))
|
|
||||||
continue;
|
|
||||||
handler = signal(sig, sigexit);
|
|
||||||
if (handler == SIG_IGN) /* oops... restore back to IGN! */
|
|
||||||
signal(sig, handler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -7682,10 +7748,11 @@ 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
|
||||||
* init_sigmasks() if we are going to execute "sh <script>",
|
* install_special_sighandlers() 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 init_sigmasks()
|
* 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 */
|
||||||
@ -7724,7 +7791,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;
|
||||||
|
|
||||||
init_sigmasks();
|
install_special_sighandlers();
|
||||||
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...] "" */
|
||||||
@ -7741,7 +7808,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 */
|
||||||
init_sigmasks();
|
install_special_sighandlers();
|
||||||
parse_and_run_string(optarg);
|
parse_and_run_string(optarg);
|
||||||
goto final_return;
|
goto final_return;
|
||||||
case 'i':
|
case 'i':
|
||||||
@ -7773,15 +7840,15 @@ int hush_main(int argc, char **argv)
|
|||||||
empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
|
empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
|
||||||
if (empty_trap_mask != 0) {
|
if (empty_trap_mask != 0) {
|
||||||
int sig;
|
int sig;
|
||||||
init_sigmasks();
|
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(""); */
|
||||||
sigaddset(&G.blocked_set, sig);
|
signal(sig, SIG_IGN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sigprocmask_set(&G.blocked_set);
|
|
||||||
}
|
}
|
||||||
# if ENABLE_HUSH_LOOPS
|
# if ENABLE_HUSH_LOOPS
|
||||||
optarg++;
|
optarg++;
|
||||||
@ -7831,7 +7898,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));
|
||||||
init_sigmasks();
|
install_special_sighandlers();
|
||||||
parse_and_run_file(input);
|
parse_and_run_file(input);
|
||||||
fclose(input);
|
fclose(input);
|
||||||
}
|
}
|
||||||
@ -7856,7 +7923,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));
|
||||||
init_sigmasks();
|
install_special_sighandlers();
|
||||||
parse_and_run_file(input);
|
parse_and_run_file(input);
|
||||||
#if ENABLE_FEATURE_CLEAN_UP
|
#if ENABLE_FEATURE_CLEAN_UP
|
||||||
fclose(input);
|
fclose(input);
|
||||||
@ -7865,7 +7932,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 init_sigmasks() as needed.
|
* NB: don't forget to (re)run install_special_sighandlers() as needed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* A shell is interactive if the '-i' flag was given,
|
/* A shell is interactive if the '-i' flag was given,
|
||||||
@ -7918,11 +7985,11 @@ int hush_main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Block some signals */
|
/* Block some signals */
|
||||||
init_sigmasks();
|
install_special_sighandlers();
|
||||||
|
|
||||||
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 */
|
||||||
set_fatal_handlers_to_sigexit();
|
install_fatal_sighandlers();
|
||||||
/* Put ourselves in our own process group
|
/* Put ourselves in our own process group
|
||||||
* (bash, too, does this only if ctty is available) */
|
* (bash, too, does this only if ctty is available) */
|
||||||
bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
|
bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
|
||||||
@ -7933,7 +8000,7 @@ int hush_main(int argc, char **argv)
|
|||||||
* (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 {
|
} else {
|
||||||
init_sigmasks();
|
install_special_sighandlers();
|
||||||
}
|
}
|
||||||
#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 */
|
||||||
@ -7950,10 +8017,10 @@ 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);
|
||||||
}
|
}
|
||||||
init_sigmasks();
|
install_special_sighandlers();
|
||||||
#else
|
#else
|
||||||
/* We have interactiveness code disabled */
|
/* We have interactiveness code disabled */
|
||||||
init_sigmasks();
|
install_special_sighandlers();
|
||||||
#endif
|
#endif
|
||||||
/* bash:
|
/* bash:
|
||||||
* if interactive but not a login shell, sources ~/.bashrc
|
* if interactive but not a login shell, sources ~/.bashrc
|
||||||
@ -8087,7 +8154,7 @@ static int FAST_FUNC builtin_exec(char **argv)
|
|||||||
tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
|
tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
|
||||||
|
|
||||||
/* TODO: if exec fails, bash does NOT exit! We do.
|
/* TODO: if exec fails, bash does NOT exit! We do.
|
||||||
* We'll need to undo sigprocmask (it's inside execvp_or_die)
|
* We'll need to undo trap cleanup (it's inside execvp_or_die)
|
||||||
* and tcsetpgrp, and this is inherently racy.
|
* and tcsetpgrp, and this is inherently racy.
|
||||||
*/
|
*/
|
||||||
execvp_or_die(argv);
|
execvp_or_die(argv);
|
||||||
@ -8284,6 +8351,8 @@ static int FAST_FUNC builtin_trap(char **argv)
|
|||||||
process_sig_list:
|
process_sig_list:
|
||||||
ret = EXIT_SUCCESS;
|
ret = EXIT_SUCCESS;
|
||||||
while (*argv) {
|
while (*argv) {
|
||||||
|
sighandler_t handler;
|
||||||
|
|
||||||
sig = get_signum(*argv++);
|
sig = get_signum(*argv++);
|
||||||
if (sig < 0 || sig >= NSIG) {
|
if (sig < 0 || sig >= NSIG) {
|
||||||
ret = EXIT_FAILURE;
|
ret = EXIT_FAILURE;
|
||||||
@ -8302,18 +8371,13 @@ static int FAST_FUNC builtin_trap(char **argv)
|
|||||||
if (sig == 0)
|
if (sig == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (new_cmd) {
|
if (new_cmd)
|
||||||
sigaddset(&G.blocked_set, sig);
|
handler = (new_cmd[0] ? record_pending_signo : SIG_IGN);
|
||||||
} else {
|
else
|
||||||
/* There was a trap handler, we are removing it
|
/* We are removing trap handler */
|
||||||
* (if sig has non-DFL handling,
|
handler = pick_sighandler(sig);
|
||||||
* we don't need to do anything) */
|
signal(sig, handler);
|
||||||
if (sig < sizeof(G.special_sig_mask)*8 && (G.special_sig_mask & (1 << sig)))
|
|
||||||
continue;
|
|
||||||
sigdelset(&G.blocked_set, sig);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sigprocmask_set(&G.blocked_set);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8535,11 +8599,6 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
|
|||||||
* if it has non-empty trap:
|
* if it has non-empty trap:
|
||||||
* - executes trap and returns to read;
|
* - executes trap and returns to read;
|
||||||
*/
|
*/
|
||||||
/* helper */
|
|
||||||
static void record_signal(int sig)
|
|
||||||
{
|
|
||||||
sigaddset(&G.detected_set, sig);
|
|
||||||
}
|
|
||||||
static int FAST_FUNC builtin_read(char **argv)
|
static int FAST_FUNC builtin_read(char **argv)
|
||||||
{
|
{
|
||||||
const char *r;
|
const char *r;
|
||||||
@ -8549,7 +8608,6 @@ static int FAST_FUNC builtin_read(char **argv)
|
|||||||
char *opt_u = NULL;
|
char *opt_u = NULL;
|
||||||
const char *ifs;
|
const char *ifs;
|
||||||
int read_flags;
|
int read_flags;
|
||||||
sigset_t saved_blkd_set;
|
|
||||||
|
|
||||||
/* "!": do not abort on errors.
|
/* "!": do not abort on errors.
|
||||||
* Option string must start with "sr" to match BUILTIN_READ_xxx
|
* Option string must start with "sr" to match BUILTIN_READ_xxx
|
||||||
@ -8561,41 +8619,6 @@ static int FAST_FUNC builtin_read(char **argv)
|
|||||||
ifs = get_local_var_value("IFS"); /* can be NULL */
|
ifs = get_local_var_value("IFS"); /* can be NULL */
|
||||||
|
|
||||||
again:
|
again:
|
||||||
/* We need to temporarily unblock and record signals around read */
|
|
||||||
|
|
||||||
saved_blkd_set = G.blocked_set;
|
|
||||||
{
|
|
||||||
unsigned sig;
|
|
||||||
struct sigaction sa, old_sa;
|
|
||||||
|
|
||||||
memset(&sa, 0, sizeof(sa));
|
|
||||||
sigfillset(&sa.sa_mask);
|
|
||||||
sa.sa_flags = SA_RESTART;
|
|
||||||
sa.sa_handler = record_signal;
|
|
||||||
|
|
||||||
sig = 0;
|
|
||||||
do {
|
|
||||||
sig++;
|
|
||||||
if (sigismember(&G.blocked_set, sig)) {
|
|
||||||
char *sig_trap = (G.traps && G.traps[sig]) ? G.traps[sig] : NULL;
|
|
||||||
/* If has a nonempty trap... */
|
|
||||||
if ((sig_trap && sig_trap[0])
|
|
||||||
/* ...or has no trap and is SIGINT or SIGHUP */
|
|
||||||
|| (!sig_trap && (sig == SIGINT || sig == SIGHUP))
|
|
||||||
) {
|
|
||||||
sigaction(sig, &sa, &old_sa);
|
|
||||||
if (old_sa.sa_handler == SIG_IGN) /* oops... restore back to IGN! */
|
|
||||||
sigaction_set(sig, &old_sa);
|
|
||||||
else
|
|
||||||
sigdelset(&G.blocked_set, sig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (sig < NSIG-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (memcmp(&saved_blkd_set, &G.blocked_set, sizeof(saved_blkd_set)) != 0)
|
|
||||||
sigprocmask_set(&G.blocked_set);
|
|
||||||
|
|
||||||
r = shell_builtin_read(set_local_var_from_halves,
|
r = shell_builtin_read(set_local_var_from_halves,
|
||||||
argv,
|
argv,
|
||||||
ifs,
|
ifs,
|
||||||
@ -8606,13 +8629,8 @@ static int FAST_FUNC builtin_read(char **argv)
|
|||||||
opt_u
|
opt_u
|
||||||
);
|
);
|
||||||
|
|
||||||
if (memcmp(&saved_blkd_set, &G.blocked_set, sizeof(saved_blkd_set)) != 0) {
|
|
||||||
G.blocked_set = saved_blkd_set;
|
|
||||||
sigprocmask_set(&G.blocked_set);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uintptr_t)r == 1 && errno == EINTR) {
|
if ((uintptr_t)r == 1 && errno == EINTR) {
|
||||||
unsigned sig = check_and_run_traps(0);
|
unsigned sig = check_and_run_traps();
|
||||||
if (sig && sig != SIGINT)
|
if (sig && sig != SIGINT)
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
@ -8849,7 +8867,7 @@ static int FAST_FUNC builtin_unset(char **argv)
|
|||||||
static int FAST_FUNC builtin_wait(char **argv)
|
static int FAST_FUNC builtin_wait(char **argv)
|
||||||
{
|
{
|
||||||
int ret = EXIT_SUCCESS;
|
int ret = EXIT_SUCCESS;
|
||||||
int status, sig;
|
int status;
|
||||||
|
|
||||||
argv = skip_dash_dash(argv);
|
argv = skip_dash_dash(argv);
|
||||||
if (argv[0] == NULL) {
|
if (argv[0] == NULL) {
|
||||||
@ -8869,25 +8887,53 @@ static int FAST_FUNC builtin_wait(char **argv)
|
|||||||
* ^C <-- after ~4 sec from keyboard
|
* ^C <-- after ~4 sec from keyboard
|
||||||
* $
|
* $
|
||||||
*/
|
*/
|
||||||
sigaddset(&G.blocked_set, SIGCHLD);
|
|
||||||
sigprocmask_set(&G.blocked_set);
|
|
||||||
while (1) {
|
while (1) {
|
||||||
checkjobs(NULL);
|
int sig;
|
||||||
if (errno == ECHILD)
|
sigset_t oldset, allsigs;
|
||||||
break;
|
|
||||||
/* Wait for SIGCHLD or any other signal of interest */
|
/* waitpid is not interruptible by SA_RESTARTed
|
||||||
/* sigtimedwait with infinite timeout: */
|
* signals which we use. Thus, this ugly dance:
|
||||||
sig = sigwaitinfo(&G.blocked_set, NULL);
|
*/
|
||||||
if (sig > 0) {
|
|
||||||
sig = check_and_run_traps(sig);
|
/* Make sure possible SIGCHLD is stored in kernel's
|
||||||
if (sig && sig != SIGCHLD) { /* see note 2 */
|
* pending signal mask before we call waitpid.
|
||||||
ret = 128 + sig;
|
* Or else we may race with SIGCHLD, lose it,
|
||||||
break;
|
* and get stuck in sigwaitinfo...
|
||||||
}
|
*/
|
||||||
|
sigfillset(&allsigs);
|
||||||
|
sigprocmask(SIG_SETMASK, &allsigs, &oldset);
|
||||||
|
|
||||||
|
if (!sigisemptyset(&G.pending_set)) {
|
||||||
|
/* Crap! we raced with some signal! */
|
||||||
|
// sig = 0;
|
||||||
|
goto restore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkjobs(NULL); /* waitpid(WNOHANG) inside */
|
||||||
|
if (errno == ECHILD) {
|
||||||
|
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for SIGCHLD or any other signal */
|
||||||
|
//sig = sigwaitinfo(&allsigs, NULL);
|
||||||
|
/* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
|
||||||
|
/* Note: sigsuspend invokes signal handler */
|
||||||
|
sigsuspend(&oldset);
|
||||||
|
restore:
|
||||||
|
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||||
|
|
||||||
|
/* So, did we get a signal? */
|
||||||
|
//if (sig > 0)
|
||||||
|
// raise(sig); /* run handler */
|
||||||
|
sig = check_and_run_traps();
|
||||||
|
if (sig /*&& sig != SIGCHLD - always true */) {
|
||||||
|
/* see note 2 */
|
||||||
|
ret = 128 + sig;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
|
||||||
}
|
}
|
||||||
sigdelset(&G.blocked_set, SIGCHLD);
|
|
||||||
sigprocmask_set(&G.blocked_set);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user