summaryrefslogtreecommitdiff
path: root/sig.c
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>1996-12-23 17:02:34 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:46:49 +0000
commitccc6cda312fea9f0468ee65b8f368e9653e1380b (patch)
treeb059878adcfd876c4acb8030deda1eeb918c7e75 /sig.c
parent726f63884db0132f01745f1fb4465e6621088ccf (diff)
downloadbash-ccc6cda312fea9f0468ee65b8f368e9653e1380b.tar.gz
Imported from ../bash-2.0.tar.gz.
Diffstat (limited to 'sig.c')
-rw-r--r--sig.c482
1 files changed, 482 insertions, 0 deletions
diff --git a/sig.c b/sig.c
new file mode 100644
index 00000000..d0243f79
--- /dev/null
+++ b/sig.c
@@ -0,0 +1,482 @@
+/* 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 <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <signal.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"
+
+#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 */