/* sig.c - interface for shell signal handlers and signal initialization. */ /* Copyright (C) 1994 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 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. */ #include "config.h" #include "bashtypes.h" #if defined (HAVE_UNISTD_H) # include #endif #include #include #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" #if defined (READLINE) # include "bashline.h" #endif #if defined (HISTORY) # include "bashhist.h" #endif extern int last_command_exit_value; extern int return_catch_flag; extern int loop_level, continuing, breaking; extern int parse_and_execute_level, shell_initialized; extern int interactive, interactive_shell, login_shell, startup_state; /* Non-zero after SIGINT. */ int interrupt_state; /* 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; static void initialize_terminating_signals (); void initialize_signals () { initialize_terminating_signals (); initialize_job_signals (); #if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL) initialize_siglist (); #endif } void reinitialize_signals () { initialize_terminating_signals (); initialize_job_signals (); } /* 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; }; #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, #endif #ifdef SIGINT SIGINT, NULL_HANDLER, #endif #ifdef SIGILL SIGILL, NULL_HANDLER, #endif #ifdef SIGTRAP SIGTRAP, NULL_HANDLER, #endif #ifdef SIGIOT SIGIOT, NULL_HANDLER, #endif #ifdef SIGDANGER SIGDANGER, NULL_HANDLER, #endif #ifdef SIGEMT SIGEMT, NULL_HANDLER, #endif #ifdef SIGFPE SIGFPE, NULL_HANDLER, #endif #ifdef SIGBUS SIGBUS, NULL_HANDLER, #endif #ifdef SIGSEGV SIGSEGV, NULL_HANDLER, #endif #ifdef SIGSYS SIGSYS, NULL_HANDLER, #endif #ifdef SIGPIPE SIGPIPE, NULL_HANDLER, #endif #ifdef SIGALRM SIGALRM, NULL_HANDLER, #endif #ifdef SIGTERM SIGTERM, NULL_HANDLER, #endif #ifdef SIGXCPU SIGXCPU, NULL_HANDLER, #endif #ifdef SIGXFSZ SIGXFSZ, NULL_HANDLER, #endif #ifdef SIGVTALRM SIGVTALRM, NULL_HANDLER, #endif #ifdef SIGPROF SIGPROF, NULL_HANDLER, #endif #ifdef SIGLOST SIGLOST, NULL_HANDLER, #endif #ifdef SIGUSR1 SIGUSR1, NULL_HANDLER, #endif #ifdef SIGUSR2 SIGUSR2, NULL_HANDLER, #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) /* Initialize signals that will terminate the shell to do some unwind protection. */ static void initialize_terminating_signals () { register int i; /* 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_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++) { sigaction (XSIG (i), &act, &oact); terminating_signals[i].orig_handler = oact.sa_handler; /* 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) { sigaction (XSIG (i), &oact, &act); set_signal_ignored (XSIG (i)); } } #else /* !HAVE_POSIX_SIGNALS */ for (i = 0; i < TERMSIGS_LENGTH; i++) { terminating_signals[i].orig_handler = signal (XSIG (i), termination_unwind_protect); /* 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) { signal (XSIG (i), SIG_IGN); set_signal_ignored (XSIG (i)); } } #endif /* !HAVE_POSIX_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); sigdelset (&top_level_mask, SIGCHLD); #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); set_signal_handler (SIGTERM, SIG_IGN); } } void reset_terminating_signals () { register int i; #if defined (HAVE_POSIX_SIGNALS) struct sigaction act; 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); 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 */ } #undef XSIG #undef XHANDLER /* 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) { print_newline = 1; DELINTERRUPT; } if (interrupt_state) return; last_command_exit_value |= 128; /* Run any traps set on SIGINT. */ run_interrupt_trap (); /* Cleanup string parser environment. */ while (parse_and_execute_level) parse_and_execute_cleanup (); #if defined (JOB_CONTROL) give_terminal_to (shell_pgrp); #endif /* JOB_CONTROL */ #if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS) /* This should not be necessary on systems using sigsetjmp/siglongjmp. */ sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL); #endif reset_parser (); #if defined (READLINE) if (interactive) bashline_reinitialize (); #endif /* READLINE */ #if defined (PROCESS_SUBSTITUTION) unlink_fifo_list (); #endif /* PROCESS_SUBSTITUTION */ run_unwind_protects (); loop_level = continuing = breaking = 0; 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 termination_unwind_protect (sig) int sig; { if (sig == SIGINT && signal_is_trapped (SIGINT)) run_interrupt_trap (); #if defined (HISTORY) if (interactive_shell && sig != SIGABRT) maybe_save_shell_history (); #endif /* HISTORY */ #if defined (JOB_CONTROL) if (interactive && sig == SIGHUP) hangup_all_jobs (); end_job_control (); #endif /* JOB_CONTROL */ #if defined (PROCESS_SUBSTITUTION) unlink_fifo_list (); #endif /* PROCESS_SUBSTITUTION */ run_exit_trap (); set_signal_handler (sig, SIG_DFL); kill (getpid (), sig); SIGRETURN (0); } /* 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; if (interrupt_immediately) { interrupt_immediately = 0; throw_to_top_level (); } 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; { int old, new; if (newset) new = *newset; else new = 0; switch (operation) { case SIG_BLOCK: old = sigblock (new); break; case SIG_SETMASK: sigsetmask (new); break; default: internal_error ("Bad code in sig.c: sigprocmask"); } if (oldset) *oldset = old; } #endif /* JOB_CONTROL */ #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; #if 0 if (sig == SIGALRM) act.sa_flags |= SA_INTERRUPT; /* XXX */ else act.sa_flags |= SA_RESTART; /* XXX */ #endif sigemptyset (&act.sa_mask); sigemptyset (&oact.sa_mask); sigaction (sig, &act, &oact); return (oact.sa_handler); } #endif /* HAVE_POSIX_SIGNALS */