Bash-4.0 patchlevel 38
[platform/upstream/bash.git] / sig.c
diff --git a/sig.c b/sig.c
index 9480279..e876a2b 100644 (file)
--- a/sig.c
+++ b/sig.c
@@ -1,22 +1,22 @@
 /* sig.c - interface for shell signal handlers and signal initialization. */
 
-/* Copyright (C) 1994-2005 Free Software Foundation, Inc.
+/* Copyright (C) 1994-2009 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, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+   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"
 
@@ -56,14 +56,19 @@ 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 executing_list;
+extern int comsub_ignore_return;
 extern int parse_and_execute_level, shell_initialized;
 
 /* Non-zero after SIGINT. */
-int interrupt_state;
+volatile int interrupt_state = 0;
 
 /* Non-zero after SIGWINCH */
 volatile int sigwinch_received = 0;
 
+/* Set to the value of any terminating signal received. */
+volatile int 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;
@@ -76,6 +81,9 @@ sigset_t top_level_mask;
 /* 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
@@ -223,7 +231,7 @@ initialize_terminating_signals ()
      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 = termination_unwind_protect;
+  act.sa_handler = termsig_sighandler;
   act.sa_flags = 0;
   sigemptyset (&act.sa_mask);
   sigemptyset (&oact.sa_mask);
@@ -259,7 +267,7 @@ initialize_terminating_signals ()
       if (signal_is_trapped (XSIG (i)))
        continue;
 
-      XHANDLER(i) = signal (XSIG (i), termination_unwind_protect);
+      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. */
@@ -344,6 +352,25 @@ reset_terminating_signals ()
 #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 = 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 ()
@@ -366,7 +393,7 @@ throw_to_top_level ()
   /* Run any traps set on SIGINT. */
   run_interrupt_trap ();
 
-  /* Cleanup string parser environment. */
+  /* Clean up string parser environment. */
   while (parse_and_execute_level)
     parse_and_execute_cleanup ();
 
@@ -383,7 +410,7 @@ throw_to_top_level ()
 
 #if defined (READLINE)
   if (interactive)
-    bashline_reinitialize ();
+    bashline_reset ();
 #endif /* READLINE */
 
 #if defined (PROCESS_SUBSTITUTION)
@@ -392,7 +419,7 @@ throw_to_top_level ()
 
   run_unwind_protects ();
   loop_level = continuing = breaking = 0;
-  return_catch_flag = 0;
+  executing_list = comsub_ignore_return = return_catch_flag = 0;
 
   if (interactive && print_newline)
     {
@@ -418,17 +445,82 @@ jump_to_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)
+    {
+      terminate_immediately = 0;
+      termsig_handler (sig);
+    }
+
+  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)
-  /* This might be unsafe, since it eventually calls functions POSIX says
-     not to call from signal handlers.  If it's a problem, take this code
-     out. */
   if (interactive_shell && sig != SIGABRT)
     maybe_save_shell_history ();
 #endif /* HISTORY */
@@ -443,11 +535,13 @@ termination_unwind_protect (sig)
   unlink_fifo_list ();
 #endif /* PROCESS_SUBSTITUTION */
 
+  /* Reset execution context */
+  loop_level = continuing = breaking = 0;
+  executing_list = comsub_ignore_return = return_catch_flag = 0;
+
   run_exit_trap ();
   set_signal_handler (sig, SIG_DFL);
   kill (getpid (), sig);
-
-  SIGRETURN (0);
 }
 
 /* What we really do when SIGINT occurs. */