X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=sig.c;h=e5bb73997a89bc125b3c1df637c3bc355a93ab6a;hb=64447609994bfddeef1061948022c074093e9a9f;hp=d0243f79b2fd9976934c71ba6ef3d19a00060a0d;hpb=ccc6cda312fea9f0468ee65b8f368e9653e1380b;p=thirdparty%2Fbash.git diff --git a/sig.c b/sig.c index d0243f79b..e5bb73997 100644 --- a/sig.c +++ b/sig.c @@ -1,34 +1,39 @@ /* sig.c - interface for shell signal handlers and signal initialization. */ -/* Copyright (C) 1994 Free Software Foundation, Inc. +/* Copyright (C) 1994-2015 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. - Bash is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2, or (at your option) any later - version. + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - Bash is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Bash; see the file COPYING. If not, write to the Free Software - Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + You should have received a copy of the GNU General Public License + along with Bash. If not, see . +*/ #include "config.h" #include "bashtypes.h" #if defined (HAVE_UNISTD_H) +# ifdef _MINIX +# include +# endif # include #endif #include #include +#include "bashintl.h" + #include "shell.h" #if defined (JOB_CONTROL) #include "jobs.h" @@ -38,9 +43,11 @@ #include "trap.h" #include "builtins/common.h" +#include "builtins/builtext.h" #if defined (READLINE) # include "bashline.h" +# include #endif #if defined (HISTORY) @@ -48,13 +55,38 @@ #endif extern int last_command_exit_value; +extern int last_command_exit_signal; extern int return_catch_flag; -extern int loop_level, continuing, breaking; +extern int running_trap; +extern int loop_level, continuing, breaking, funcnest; +extern int executing_list; +extern int comsub_ignore_return; extern int parse_and_execute_level, shell_initialized; -extern int interactive, interactive_shell, login_shell, startup_state; +#if defined (HISTORY) +extern int history_lines_this_session; +#endif +extern int no_line_editing; +extern int wait_signal_received; +extern int wait_intr_flag; +extern sh_builtin_func_t *this_shell_builtin; + +extern void initialize_siglist (); + +#if !defined (JOB_CONTROL) +extern void initialize_job_signals __P((void)); +#endif /* Non-zero after SIGINT. */ -int interrupt_state; +volatile sig_atomic_t interrupt_state = 0; + +/* Non-zero after SIGWINCH */ +volatile sig_atomic_t sigwinch_received = 0; + +/* Non-zero after SIGTERM */ +volatile sig_atomic_t sigterm_received = 0; + +/* Set to the value of any terminating signal received. */ +volatile sig_atomic_t terminating_signal = 0; /* The environment at the top-level R-E loop. We use this in the case of error return. */ @@ -68,23 +100,25 @@ sigset_t top_level_mask; /* When non-zero, we throw_to_top_level (). */ int interrupt_immediately = 0; -static void initialize_terminating_signals (); +/* When non-zero, we call the terminating signal handler immediately. */ +int terminate_immediately = 0; -void -initialize_signals () -{ - initialize_terminating_signals (); - initialize_job_signals (); -#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL) - initialize_siglist (); +#if defined (SIGWINCH) +static SigHandler *old_winch = (SigHandler *)SIG_DFL; #endif -} + +static void initialize_shell_signals __P((void)); void -reinitialize_signals () +initialize_signals (reinit) + int reinit; { - initialize_terminating_signals (); + initialize_shell_signals (); initialize_job_signals (); +#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL) + if (reinit == 0) + initialize_siglist (); +#endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */ } /* A structure describing a signal that terminates the shell if not @@ -93,6 +127,7 @@ reinitialize_signals () struct termsig { int signum; SigHandler *orig_handler; + int orig_flags; }; #define NULL_HANDLER (SigHandler *)SIG_DFL @@ -102,87 +137,89 @@ struct termsig { and so forth. */ static struct termsig terminating_signals[] = { #ifdef SIGHUP - SIGHUP, NULL_HANDLER, +{ SIGHUP, NULL_HANDLER, 0 }, #endif #ifdef SIGINT - SIGINT, NULL_HANDLER, +{ SIGINT, NULL_HANDLER, 0 }, #endif #ifdef SIGILL - SIGILL, NULL_HANDLER, +{ SIGILL, NULL_HANDLER, 0 }, #endif #ifdef SIGTRAP - SIGTRAP, NULL_HANDLER, +{ SIGTRAP, NULL_HANDLER, 0 }, #endif #ifdef SIGIOT - SIGIOT, NULL_HANDLER, +{ SIGIOT, NULL_HANDLER, 0 }, #endif #ifdef SIGDANGER - SIGDANGER, NULL_HANDLER, +{ SIGDANGER, NULL_HANDLER, 0 }, #endif #ifdef SIGEMT - SIGEMT, NULL_HANDLER, +{ SIGEMT, NULL_HANDLER, 0 }, #endif #ifdef SIGFPE - SIGFPE, NULL_HANDLER, +{ SIGFPE, NULL_HANDLER, 0 }, #endif #ifdef SIGBUS - SIGBUS, NULL_HANDLER, +{ SIGBUS, NULL_HANDLER, 0 }, #endif #ifdef SIGSEGV - SIGSEGV, NULL_HANDLER, +{ SIGSEGV, NULL_HANDLER, 0 }, #endif #ifdef SIGSYS - SIGSYS, NULL_HANDLER, +{ SIGSYS, NULL_HANDLER, 0 }, #endif #ifdef SIGPIPE - SIGPIPE, NULL_HANDLER, +{ SIGPIPE, NULL_HANDLER, 0 }, #endif #ifdef SIGALRM - SIGALRM, NULL_HANDLER, +{ SIGALRM, NULL_HANDLER, 0 }, #endif #ifdef SIGTERM - SIGTERM, NULL_HANDLER, +{ SIGTERM, NULL_HANDLER, 0 }, #endif #ifdef SIGXCPU - SIGXCPU, NULL_HANDLER, +{ SIGXCPU, NULL_HANDLER, 0 }, #endif #ifdef SIGXFSZ - SIGXFSZ, NULL_HANDLER, +{ SIGXFSZ, NULL_HANDLER, 0 }, #endif #ifdef SIGVTALRM - SIGVTALRM, NULL_HANDLER, +{ SIGVTALRM, NULL_HANDLER, 0 }, #endif +#if 0 #ifdef SIGPROF - SIGPROF, NULL_HANDLER, +{ SIGPROF, NULL_HANDLER, 0 }, +#endif #endif #ifdef SIGLOST - SIGLOST, NULL_HANDLER, +{ SIGLOST, NULL_HANDLER, 0 }, #endif #ifdef SIGUSR1 - SIGUSR1, NULL_HANDLER, +{ SIGUSR1, NULL_HANDLER, 0 }, #endif #ifdef SIGUSR2 - SIGUSR2, NULL_HANDLER, +{ SIGUSR2, NULL_HANDLER, 0 }, #endif }; @@ -190,22 +227,31 @@ static struct termsig terminating_signals[] = { #define XSIG(x) (terminating_signals[x].signum) #define XHANDLER(x) (terminating_signals[x].orig_handler) +#define XSAFLAGS(x) (terminating_signals[x].orig_flags) + +static int termsigs_initialized = 0; /* Initialize signals that will terminate the shell to do some - unwind protection. */ -static void + unwind protection. For non-interactive shells, we only call + this when a trap is defined for EXIT (0) or when trap is run + to display signal dispositions. */ +void initialize_terminating_signals () { register int i; +#if defined (HAVE_POSIX_SIGNALS) + struct sigaction act, oact; +#endif + + if (termsigs_initialized) + return; /* The following code is to avoid an expensive call to set_signal_handler () for each terminating_signals. Fortunately, this is possible in Posix. Unfortunately, we have to call signal () on non-Posix systems for each signal in terminating_signals. */ #if defined (HAVE_POSIX_SIGNALS) - struct sigaction act, oact; - - act.sa_handler = termination_unwind_protect; + act.sa_handler = termsig_sighandler; act.sa_flags = 0; sigemptyset (&act.sa_mask); sigemptyset (&oact.sa_mask); @@ -213,40 +259,70 @@ initialize_terminating_signals () sigaddset (&act.sa_mask, XSIG (i)); for (i = 0; i < TERMSIGS_LENGTH; i++) { + /* If we've already trapped it, don't do anything. */ + if (signal_is_trapped (XSIG (i))) + continue; + sigaction (XSIG (i), &act, &oact); - terminating_signals[i].orig_handler = oact.sa_handler; + XHANDLER(i) = oact.sa_handler; + XSAFLAGS(i) = oact.sa_flags; /* Don't do anything with signals that are ignored at shell entry if the shell is not interactive. */ - if (!interactive_shell && oact.sa_handler == SIG_IGN) - { + /* XXX - should we do this for interactive shells, too? */ + if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN) + { sigaction (XSIG (i), &oact, &act); - set_signal_ignored (XSIG (i)); - } + set_signal_hard_ignored (XSIG (i)); + } +#if defined (SIGPROF) && !defined (_MINIX) + if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN) + sigaction (XSIG (i), &oact, (struct sigaction *)NULL); +#endif /* SIGPROF && !_MINIX */ } #else /* !HAVE_POSIX_SIGNALS */ for (i = 0; i < TERMSIGS_LENGTH; i++) { - terminating_signals[i].orig_handler = - signal (XSIG (i), termination_unwind_protect); + /* If we've already trapped it, don't do anything. */ + if (signal_is_trapped (XSIG (i))) + continue; + + XHANDLER(i) = signal (XSIG (i), termsig_sighandler); + XSAFLAGS(i) = 0; /* Don't do anything with signals that are ignored at shell entry if the shell is not interactive. */ - if (!interactive_shell && terminating_signals[i].orig_handler == SIG_IGN) + /* XXX - should we do this for interactive shells, too? */ + if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN) { - signal (XSIG (i), SIG_IGN); - set_signal_ignored (XSIG (i)); + signal (XSIG (i), SIG_IGN); + set_signal_hard_ignored (XSIG (i)); } +#ifdef SIGPROF + if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN) + signal (XSIG (i), XHANDLER (i)); +#endif } #endif /* !HAVE_POSIX_SIGNALS */ + termsigs_initialized = 1; +} + +static void +initialize_shell_signals () +{ + if (interactive) + initialize_terminating_signals (); + #if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS) /* All shells use the signal mask they inherit, and pass it along to child processes. Children will never block SIGCHLD, though. */ sigemptyset (&top_level_mask); sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask); +# if defined (SIGCHLD) sigdelset (&top_level_mask, SIGCHLD); +# endif #endif /* JOB_CONTROL || HAVE_POSIX_SIGNALS */ /* And, some signals that are specifically ignored by the shell. */ @@ -255,7 +331,10 @@ initialize_terminating_signals () if (interactive) { set_signal_handler (SIGINT, sigint_sighandler); - set_signal_handler (SIGTERM, SIG_IGN); + get_original_signal (SIGTERM); + if (signal_is_hard_ignored (SIGTERM) == 0) + set_signal_handler (SIGTERM, sigterm_sighandler); + set_sigwinch_handler (); } } @@ -263,10 +342,14 @@ void reset_terminating_signals () { register int i; - #if defined (HAVE_POSIX_SIGNALS) struct sigaction act; +#endif + + if (termsigs_initialized == 0) + return; +#if defined (HAVE_POSIX_SIGNALS) act.sa_flags = 0; sigemptyset (&act.sa_mask); for (i = 0; i < TERMSIGS_LENGTH; i++) @@ -277,6 +360,7 @@ reset_terminating_signals () continue; act.sa_handler = XHANDLER (i); + act.sa_flags = XSAFLAGS (i); sigaction (XSIG (i), &act, (struct sigaction *) NULL); } #else /* !HAVE_POSIX_SIGNALS */ @@ -288,10 +372,31 @@ reset_terminating_signals () signal (XSIG (i), XHANDLER (i)); } #endif /* !HAVE_POSIX_SIGNALS */ + + termsigs_initialized = 0; } #undef XSIG #undef XHANDLER +/* Run some of the cleanups that should be performed when we run + jump_to_top_level from a builtin command context. XXX - might want to + also call reset_parser here. */ +void +top_level_cleanup () +{ + /* Clean up string parser environment. */ + while (parse_and_execute_level) + parse_and_execute_cleanup (); + +#if defined (PROCESS_SUBSTITUTION) + unlink_fifo_list (); +#endif /* PROCESS_SUBSTITUTION */ + + run_unwind_protects (); + loop_level = continuing = breaking = funcnest = 0; + executing_list = comsub_ignore_return = return_catch_flag = wait_intr_flag = 0; +} + /* What to do when we've been interrupted, and it is safe to handle it. */ void throw_to_top_level () @@ -300,6 +405,8 @@ throw_to_top_level () if (interrupt_state) { + if (last_command_exit_value < 128) + last_command_exit_value = 128 + SIGINT; print_newline = 1; DELINTERRUPT; } @@ -307,21 +414,28 @@ throw_to_top_level () if (interrupt_state) return; + last_command_exit_signal = (last_command_exit_value > 128) ? + (last_command_exit_value - 128) : 0; last_command_exit_value |= 128; - /* Run any traps set on SIGINT. */ - run_interrupt_trap (); + /* Run any traps set on SIGINT, mostly for interactive shells */ + if (signal_is_trapped (SIGINT)) + run_interrupt_trap (1); - /* Cleanup string parser environment. */ + /* Clean up string parser environment. */ while (parse_and_execute_level) parse_and_execute_cleanup (); + if (running_trap > 0) + run_trap_cleanup (running_trap - 1); + #if defined (JOB_CONTROL) - give_terminal_to (shell_pgrp); + give_terminal_to (shell_pgrp, 0); #endif /* JOB_CONTROL */ #if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS) - /* This should not be necessary on systems using sigsetjmp/siglongjmp. */ + /* This needs to stay because jobs.c:make_child() uses it without resetting + the signal mask. */ sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL); #endif @@ -329,7 +443,7 @@ throw_to_top_level () #if defined (READLINE) if (interactive) - bashline_reinitialize (); + bashline_reset (); #endif /* READLINE */ #if defined (PROCESS_SUBSTITUTION) @@ -337,8 +451,8 @@ throw_to_top_level () #endif /* PROCESS_SUBSTITUTION */ run_unwind_protects (); - loop_level = continuing = breaking = 0; - return_catch_flag = 0; + loop_level = continuing = breaking = funcnest = 0; + executing_list = comsub_ignore_return = return_catch_flag = wait_intr_flag = 0; if (interactive && print_newline) { @@ -360,36 +474,132 @@ void jump_to_top_level (value) int value; { - longjmp (top_level, value); + sh_longjmp (top_level, value); } sighandler -termination_unwind_protect (sig) +termsig_sighandler (sig) int sig; { + /* If we get called twice with the same signal before handling it, + terminate right away. */ + if ( +#ifdef SIGHUP + sig != SIGHUP && +#endif +#ifdef SIGINT + sig != SIGINT && +#endif +#ifdef SIGDANGER + sig != SIGDANGER && +#endif +#ifdef SIGPIPE + sig != SIGPIPE && +#endif +#ifdef SIGALRM + sig != SIGALRM && +#endif +#ifdef SIGTERM + sig != SIGTERM && +#endif +#ifdef SIGXCPU + sig != SIGXCPU && +#endif +#ifdef SIGXFSZ + sig != SIGXFSZ && +#endif +#ifdef SIGVTALRM + sig != SIGVTALRM && +#endif +#ifdef SIGLOST + sig != SIGLOST && +#endif +#ifdef SIGUSR1 + sig != SIGUSR1 && +#endif +#ifdef SIGUSR2 + sig != SIGUSR2 && +#endif + sig == terminating_signal) + terminate_immediately = 1; + + terminating_signal = sig; + + /* XXX - should this also trigger when interrupt_immediately is set? */ + if (terminate_immediately) + { +#if defined (HISTORY) + /* XXX - will inhibit history file being written */ +# if defined (READLINE) + if (interactive_shell == 0 || interactive == 0 || (sig != SIGHUP && sig != SIGTERM) || no_line_editing || (RL_ISSTATE (RL_STATE_READCMD) == 0)) +# endif + history_lines_this_session = 0; +#endif + terminate_immediately = 0; + termsig_handler (sig); + } + +#if defined (READLINE) + /* Set the event hook so readline will call it after the signal handlers + finish executing, so if this interrupted character input we can get + quick response. If readline is active or has modified the terminal we + need to set this no matter what the signal is, though the check for + RL_STATE_TERMPREPPED is possibly redundant. */ + if (RL_ISSTATE (RL_STATE_SIGHANDLER) || RL_ISSTATE (RL_STATE_TERMPREPPED)) + bashline_set_event_hook (); +#endif + + SIGRETURN (0); +} + +void +termsig_handler (sig) + int sig; +{ + static int handling_termsig = 0; + + /* Simple semaphore to keep this function from being executed multiple + times. Since we no longer are running as a signal handler, we don't + block multiple occurrences of the terminating signals while running. */ + if (handling_termsig) + return; + handling_termsig = 1; + terminating_signal = 0; /* keep macro from re-testing true. */ + + /* I don't believe this condition ever tests true. */ if (sig == SIGINT && signal_is_trapped (SIGINT)) - run_interrupt_trap (); + run_interrupt_trap (0); #if defined (HISTORY) - if (interactive_shell && sig != SIGABRT) + /* If we don't do something like this, the history will not be saved when + an interactive shell is running in a terminal window that gets closed + with the `close' button. We can't test for RL_STATE_READCMD because + readline no longer handles SIGTERM synchronously. */ + if (interactive_shell && interactive && (sig == SIGHUP || sig == SIGTERM) && remember_on_history) maybe_save_shell_history (); #endif /* HISTORY */ + if (this_shell_builtin == read_builtin) + read_tty_cleanup (); + #if defined (JOB_CONTROL) - if (interactive && sig == SIGHUP) + if (sig == SIGHUP && (interactive || (subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB)))) hangup_all_jobs (); - end_job_control (); + if ((subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB)) == 0) + end_job_control (); #endif /* JOB_CONTROL */ #if defined (PROCESS_SUBSTITUTION) unlink_fifo_list (); #endif /* PROCESS_SUBSTITUTION */ - run_exit_trap (); + /* Reset execution context */ + loop_level = continuing = breaking = funcnest = 0; + executing_list = comsub_ignore_return = return_catch_flag = wait_intr_flag = 0; + + run_exit_trap (); /* XXX - run exit trap possibly in signal context? */ set_signal_handler (sig, SIG_DFL); kill (getpid (), sig); - - SIGRETURN (0); } /* What we really do when SIGINT occurs. */ @@ -406,19 +616,73 @@ sigint_sighandler (sig) if (interrupt_state == 0) ADDINTERRUPT; + /* We will get here in interactive shells with job control active; allow + an interactive wait to be interrupted. wait_intr_flag is only set during + the execution of the wait builtin and when wait_intr_buf is valid. */ + if (wait_intr_flag) + { + last_command_exit_value = 128 + sig; + wait_signal_received = sig; + SIGRETURN (0); + } + if (interrupt_immediately) { interrupt_immediately = 0; + last_command_exit_value = 128 + sig; throw_to_top_level (); } +#if defined (READLINE) + /* Set the event hook so readline will call it after the signal handlers + finish executing, so if this interrupted character input we can get + quick response. */ + else if (RL_ISSTATE (RL_STATE_SIGHANDLER)) + bashline_set_event_hook (); +#endif SIGRETURN (0); } +#if defined (SIGWINCH) +sighandler +sigwinch_sighandler (sig) + int sig; +{ +#if defined (MUST_REINSTALL_SIGHANDLERS) + set_signal_handler (SIGWINCH, sigwinch_sighandler); +#endif /* MUST_REINSTALL_SIGHANDLERS */ + sigwinch_received = 1; + SIGRETURN (0); +} +#endif /* SIGWINCH */ + +void +set_sigwinch_handler () +{ +#if defined (SIGWINCH) + old_winch = set_signal_handler (SIGWINCH, sigwinch_sighandler); +#endif +} + +void +unset_sigwinch_handler () +{ +#if defined (SIGWINCH) + set_signal_handler (SIGWINCH, old_winch); +#endif +} + +sighandler +sigterm_sighandler (sig) + int sig; +{ + sigterm_received = 1; /* XXX - counter? */ + SIGRETURN (0); +} + /* Signal functions used by the rest of the code. */ #if !defined (HAVE_POSIX_SIGNALS) -#if defined (JOB_CONTROL) /* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */ sigprocmask (operation, newset, oldset) int operation, *newset, *oldset; @@ -437,17 +701,16 @@ sigprocmask (operation, newset, oldset) break; case SIG_SETMASK: - sigsetmask (new); + old = sigsetmask (new); break; default: - internal_error ("Bad code in sig.c: sigprocmask"); + internal_error (_("sigprocmask: %d: invalid operation"), operation); } if (oldset) *oldset = old; } -#endif /* JOB_CONTROL */ #else @@ -468,15 +731,24 @@ set_signal_handler (sig, handler) act.sa_handler = handler; act.sa_flags = 0; -#if 0 - if (sig == SIGALRM) - act.sa_flags |= SA_INTERRUPT; /* XXX */ - else + + /* XXX - bash-4.2 */ + /* We don't want a child death to interrupt interruptible system calls, even + if we take the time to reap children */ +#if defined (SIGCHLD) + if (sig == SIGCHLD) act.sa_flags |= SA_RESTART; /* XXX */ #endif + /* If we're installing a SIGTERM handler for interactive shells, we want + it to be as close to SIG_IGN as possible. */ + if (sig == SIGTERM && handler == sigterm_sighandler) + act.sa_flags |= SA_RESTART; /* XXX */ + sigemptyset (&act.sa_mask); sigemptyset (&oact.sa_mask); - sigaction (sig, &act, &oact); - return (oact.sa_handler); + if (sigaction (sig, &act, &oact) == 0) + return (oact.sa_handler); + else + return (SIG_DFL); } #endif /* HAVE_POSIX_SIGNALS */