| /* sig.c - interface for shell signal handlers and signal initialization. */ |
| |
| /* Copyright (C) 1994-2013 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 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. |
| |
| You should have received a copy of the GNU General Public License |
| along with Bash. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "config.h" |
| |
| #include "bashtypes.h" |
| |
| #if defined (HAVE_UNISTD_H) |
| # ifdef _MINIX |
| # include <sys/types.h> |
| # endif |
| # include <unistd.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <signal.h> |
| |
| #include "bashintl.h" |
| |
| #include "shell.h" |
| #if defined (JOB_CONTROL) |
| #include "jobs.h" |
| #endif /* JOB_CONTROL */ |
| #include "siglist.h" |
| #include "sig.h" |
| #include "trap.h" |
| |
| #include "builtins/common.h" |
| #include "builtins/builtext.h" |
| |
| #if defined (READLINE) |
| # include "bashline.h" |
| # include <readline/readline.h> |
| #endif |
| |
| #if defined (HISTORY) |
| # include "bashhist.h" |
| #endif |
| |
| extern int last_command_exit_value; |
| extern int last_command_exit_signal; |
| extern int return_catch_flag; |
| extern int loop_level, continuing, breaking, funcnest; |
| extern int executing_list; |
| extern int comsub_ignore_return; |
| extern int parse_and_execute_level, shell_initialized; |
| #if defined (HISTORY) |
| extern int history_lines_this_session; |
| #endif |
| extern int no_line_editing; |
| extern int wait_signal_received; |
| extern sh_builtin_func_t *this_shell_builtin; |
| |
| extern void initialize_siglist (); |
| |
| /* Non-zero after SIGINT. */ |
| 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. */ |
| procenv_t top_level; |
| |
| #if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS) |
| /* The signal masks that this shell runs with. */ |
| sigset_t top_level_mask; |
| #endif /* JOB_CONTROL */ |
| |
| /* When non-zero, we throw_to_top_level (). */ |
| int interrupt_immediately = 0; |
| |
| /* When non-zero, we call the terminating signal handler immediately. */ |
| int terminate_immediately = 0; |
| |
| #if defined (SIGWINCH) |
| static SigHandler *old_winch = (SigHandler *)SIG_DFL; |
| #endif |
| |
| static void initialize_shell_signals __P((void)); |
| |
| void |
| initialize_signals (reinit) |
| int reinit; |
| { |
| 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 |
| caught. The orig_handler member is present so children can reset |
| these signals back to their original handlers. */ |
| struct termsig { |
| int signum; |
| SigHandler *orig_handler; |
| int orig_flags; |
| }; |
| |
| #define NULL_HANDLER (SigHandler *)SIG_DFL |
| |
| /* The list of signals that would terminate the shell if not caught. |
| We catch them, but just so that we can write the history file, |
| and so forth. */ |
| static struct termsig terminating_signals[] = { |
| #ifdef SIGHUP |
| { SIGHUP, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGINT |
| { SIGINT, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGILL |
| { SIGILL, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGTRAP |
| { SIGTRAP, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGIOT |
| { SIGIOT, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGDANGER |
| { SIGDANGER, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGEMT |
| { SIGEMT, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGFPE |
| { SIGFPE, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGBUS |
| { SIGBUS, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGSEGV |
| { SIGSEGV, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGSYS |
| { SIGSYS, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGPIPE |
| { SIGPIPE, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGALRM |
| { SIGALRM, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGTERM |
| { SIGTERM, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGXCPU |
| { SIGXCPU, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGXFSZ |
| { SIGXFSZ, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGVTALRM |
| { SIGVTALRM, NULL_HANDLER, 0 }, |
| #endif |
| |
| #if 0 |
| #ifdef SIGPROF |
| { SIGPROF, NULL_HANDLER, 0 }, |
| #endif |
| #endif |
| |
| #ifdef SIGLOST |
| { SIGLOST, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGUSR1 |
| { SIGUSR1, NULL_HANDLER, 0 }, |
| #endif |
| |
| #ifdef SIGUSR2 |
| { SIGUSR2, NULL_HANDLER, 0 }, |
| #endif |
| }; |
| |
| #define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig)) |
| |
| #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. 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) |
| act.sa_handler = termsig_sighandler; |
| act.sa_flags = 0; |
| sigemptyset (&act.sa_mask); |
| sigemptyset (&oact.sa_mask); |
| for (i = 0; i < TERMSIGS_LENGTH; i++) |
| 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); |
| 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. */ |
| /* XXX - should we do this for interactive shells, too? */ |
| if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN) |
| { |
| sigaction (XSIG (i), &oact, &act); |
| 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++) |
| { |
| /* 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. */ |
| /* XXX - should we do this for interactive shells, too? */ |
| if (interactive_shell == 0 && XHANDLER (i) == SIG_IGN) |
| { |
| 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. */ |
| set_signal_handler (SIGQUIT, SIG_IGN); |
| |
| if (interactive) |
| { |
| set_signal_handler (SIGINT, sigint_sighandler); |
| get_original_signal (SIGTERM); |
| if (signal_is_hard_ignored (SIGTERM) == 0) |
| set_signal_handler (SIGTERM, sigterm_sighandler); |
| set_sigwinch_handler (); |
| } |
| } |
| |
| 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++) |
| { |
| /* Skip a signal if it's trapped or handled specially, because the |
| trap code will restore the correct value. */ |
| if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i))) |
| continue; |
| |
| act.sa_handler = XHANDLER (i); |
| act.sa_flags = XSAFLAGS (i); |
| sigaction (XSIG (i), &act, (struct sigaction *) NULL); |
| } |
| #else /* !HAVE_POSIX_SIGNALS */ |
| for (i = 0; i < TERMSIGS_LENGTH; i++) |
| { |
| if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i))) |
| continue; |
| |
| 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 = 0; |
| } |
| |
| /* What to do when we've been interrupted, and it is safe to handle it. */ |
| void |
| throw_to_top_level () |
| { |
| int print_newline = 0; |
| |
| if (interrupt_state) |
| { |
| if (last_command_exit_value < 128) |
| last_command_exit_value = 128 + SIGINT; |
| print_newline = 1; |
| DELINTERRUPT; |
| } |
| |
| 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 (); |
| |
| /* Clean up string parser environment. */ |
| while (parse_and_execute_level) |
| parse_and_execute_cleanup (); |
| |
| #if defined (JOB_CONTROL) |
| give_terminal_to (shell_pgrp, 0); |
| #endif /* JOB_CONTROL */ |
| |
| #if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS) |
| /* 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 |
| |
| reset_parser (); |
| |
| #if defined (READLINE) |
| if (interactive) |
| bashline_reset (); |
| #endif /* READLINE */ |
| |
| #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 = 0; |
| |
| if (interactive && print_newline) |
| { |
| fflush (stdout); |
| fprintf (stderr, "\n"); |
| fflush (stderr); |
| } |
| |
| /* An interrupted `wait' command in a script does not exit the script. */ |
| if (interactive || (interactive_shell && !shell_initialized) || |
| (print_newline && signal_is_trapped (SIGINT))) |
| jump_to_top_level (DISCARD); |
| else |
| jump_to_top_level (EXITPROG); |
| } |
| |
| /* This is just here to isolate the longjmp calls. */ |
| void |
| jump_to_top_level (value) |
| int value; |
| { |
| longjmp (top_level, value); |
| } |
| |
| sighandler |
| 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 (interactive_shell && interactive && no_line_editing == 0) |
| 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 (); |
| |
| #if defined (HISTORY) |
| /* 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 defined (JOB_CONTROL) |
| if (sig == SIGHUP && (interactive || (subshell_environment & (SUBSHELL_COMSUB|SUBSHELL_PROCSUB)))) |
| hangup_all_jobs (); |
| end_job_control (); |
| #endif /* JOB_CONTROL */ |
| |
| #if defined (PROCESS_SUBSTITUTION) |
| unlink_fifo_list (); |
| #endif /* PROCESS_SUBSTITUTION */ |
| |
| /* Reset execution context */ |
| loop_level = continuing = breaking = funcnest = 0; |
| executing_list = comsub_ignore_return = return_catch_flag = 0; |
| |
| run_exit_trap (); /* XXX - run exit trap possibly in signal context? */ |
| set_signal_handler (sig, SIG_DFL); |
| kill (getpid (), sig); |
| } |
| |
| /* What we really do when SIGINT occurs. */ |
| sighandler |
| sigint_sighandler (sig) |
| int sig; |
| { |
| #if defined (MUST_REINSTALL_SIGHANDLERS) |
| signal (sig, sigint_sighandler); |
| #endif |
| |
| /* interrupt_state needs to be set for the stack of interrupts to work |
| right. Should it be set unconditionally? */ |
| if (interrupt_state == 0) |
| ADDINTERRUPT; |
| |
| /* We will get here in interactive shells with job control active; allow |
| an interactive wait to be interrupted. */ |
| if (this_shell_builtin && this_shell_builtin == wait_builtin) |
| { |
| 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) |
| |
| /* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */ |
| sigprocmask (operation, newset, oldset) |
| int operation, *newset, *oldset; |
| { |
| int old, new; |
| |
| if (newset) |
| new = *newset; |
| else |
| new = 0; |
| |
| switch (operation) |
| { |
| case SIG_BLOCK: |
| old = sigblock (new); |
| break; |
| |
| case SIG_SETMASK: |
| old = sigsetmask (new); |
| break; |
| |
| default: |
| internal_error (_("sigprocmask: %d: invalid operation"), operation); |
| } |
| |
| if (oldset) |
| *oldset = old; |
| } |
| |
| #else |
| |
| #if !defined (SA_INTERRUPT) |
| # define SA_INTERRUPT 0 |
| #endif |
| |
| #if !defined (SA_RESTART) |
| # define SA_RESTART 0 |
| #endif |
| |
| SigHandler * |
| set_signal_handler (sig, handler) |
| int sig; |
| SigHandler *handler; |
| { |
| struct sigaction act, oact; |
| |
| act.sa_handler = handler; |
| act.sa_flags = 0; |
| |
| /* 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); |
| if (sigaction (sig, &act, &oact) == 0) |
| return (oact.sa_handler); |
| else |
| return (SIG_DFL); |
| } |
| #endif /* HAVE_POSIX_SIGNALS */ |