Home | History | Annotate | Line # | Download | only in gnulib-lib
      1 /* Emergency actions in case of a fatal signal.
      2    Copyright (C) 2003-2004, 2006 Free Software Foundation, Inc.
      3    Written by Bruno Haible <bruno (at) clisp.org>, 2003.
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 2, or (at your option)
      8    any later version.
      9 
     10    This program is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     18 
     19 
     20 #include <config.h>
     21 
     22 /* Specification.  */
     23 #include "fatal-signal.h"
     24 
     25 #include <stdbool.h>
     26 #include <stdlib.h>
     27 #include <signal.h>
     28 #include <unistd.h>
     29 
     30 #include "sigprocmask.h"
     31 #include "xalloc.h"
     32 
     33 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
     34 
     35 
     36 /* ========================================================================= */
     37 
     38 
     39 /* The list of fatal signals.
     40    These are those signals whose default action is to terminate the process
     41    without a core dump, except
     42      SIGKILL - because it cannot be caught,
     43      SIGALRM SIGUSR1 SIGUSR2 SIGPOLL SIGIO SIGLOST - because applications
     44        often use them for their own purpose,
     45      SIGPROF SIGVTALRM - because they are used for profiling,
     46      SIGSTKFLT - because it is more similar to SIGFPE, SIGSEGV, SIGBUS,
     47      SIGSYS - because it is more similar to SIGABRT, SIGSEGV,
     48      SIGPWR - because it of too special use,
     49      SIGRTMIN...SIGRTMAX - because they are reserved for application use.
     50    plus
     51      SIGXCPU, SIGXFSZ - because they are quite similar to SIGTERM.  */
     52 
     53 static int fatal_signals[] =
     54   {
     55     /* ISO C 99 signals.  */
     56 #ifdef SIGINT
     57     SIGINT,
     58 #endif
     59 #ifdef SIGTERM
     60     SIGTERM,
     61 #endif
     62     /* POSIX:2001 signals.  */
     63 #ifdef SIGHUP
     64     SIGHUP,
     65 #endif
     66 #ifdef SIGPIPE
     67     SIGPIPE,
     68 #endif
     69     /* BSD signals.  */
     70 #ifdef SIGXCPU
     71     SIGXCPU,
     72 #endif
     73 #ifdef SIGXFSZ
     74     SIGXFSZ,
     75 #endif
     76     /* Woe32 signals.  */
     77 #ifdef SIGBREAK
     78     SIGBREAK,
     79 #endif
     80     0
     81   };
     82 
     83 #define num_fatal_signals (SIZEOF (fatal_signals) - 1)
     84 
     85 /* Eliminate signals whose signal handler is SIG_IGN.  */
     86 
     87 static void
     88 init_fatal_signals (void)
     89 {
     90   static bool fatal_signals_initialized = false;
     91   if (!fatal_signals_initialized)
     92     {
     93 #if HAVE_SIGACTION
     94       size_t i;
     95 
     96       for (i = 0; i < num_fatal_signals; i++)
     97 	{
     98 	  struct sigaction action;
     99 
    100 	  if (sigaction (fatal_signals[i], NULL, &action) >= 0
    101 	      && action.sa_handler == SIG_IGN)
    102 	    fatal_signals[i] = -1;
    103 	}
    104 #endif
    105 
    106       fatal_signals_initialized = true;
    107     }
    108 }
    109 
    110 
    111 /* ========================================================================= */
    112 
    113 
    114 typedef void (*action_t) (void);
    115 
    116 /* Type of an entry in the actions array.
    117    The 'action' field is accessed from within the fatal_signal_handler(),
    118    therefore we mark it as 'volatile'.  */
    119 typedef struct
    120 {
    121   volatile action_t action;
    122 }
    123 actions_entry_t;
    124 
    125 /* The registered cleanup actions.  */
    126 static actions_entry_t static_actions[32];
    127 static actions_entry_t * volatile actions = static_actions;
    128 static sig_atomic_t volatile actions_count = 0;
    129 static size_t actions_allocated = SIZEOF (static_actions);
    130 
    131 
    132 /* Uninstall the handlers.  */
    133 static inline void
    134 uninstall_handlers ()
    135 {
    136   size_t i;
    137 
    138   for (i = 0; i < num_fatal_signals; i++)
    139     if (fatal_signals[i] >= 0)
    140       signal (fatal_signals[i], SIG_DFL);
    141 }
    142 
    143 
    144 /* The signal handler.  It gets called asynchronously.  */
    145 static void
    146 fatal_signal_handler (int sig)
    147 {
    148   for (;;)
    149     {
    150       /* Get the last registered cleanup action, in a reentrant way.  */
    151       action_t action;
    152       size_t n = actions_count;
    153       if (n == 0)
    154 	break;
    155       n--;
    156       actions_count = n;
    157       action = actions[n].action;
    158       /* Execute the action.  */
    159       action ();
    160     }
    161 
    162   /* Now execute the signal's default action.
    163      If signal() blocks the signal being delivered for the duration of the
    164      signal handler's execution, the re-raised signal is delivered when this
    165      handler returns; otherwise it is delivered already during raise().  */
    166   uninstall_handlers ();
    167 #if HAVE_RAISE
    168   raise (sig);
    169 #else
    170   kill (getpid (), sig);
    171 #endif
    172 }
    173 
    174 
    175 /* Install the handlers.  */
    176 static inline void
    177 install_handlers ()
    178 {
    179   size_t i;
    180 
    181   for (i = 0; i < num_fatal_signals; i++)
    182     if (fatal_signals[i] >= 0)
    183       signal (fatal_signals[i], &fatal_signal_handler);
    184 }
    185 
    186 
    187 /* Register a cleanup function to be executed when a catchable fatal signal
    188    occurs.  */
    189 void
    190 at_fatal_signal (action_t action)
    191 {
    192   static bool cleanup_initialized = false;
    193   if (!cleanup_initialized)
    194     {
    195       init_fatal_signals ();
    196       install_handlers ();
    197       cleanup_initialized = true;
    198     }
    199 
    200   if (actions_count == actions_allocated)
    201     {
    202       /* Extend the actions array.  Note that we cannot use xrealloc(),
    203 	 because then the cleanup() function could access an already
    204 	 deallocated array.  */
    205       actions_entry_t *old_actions = actions;
    206       size_t old_actions_allocated = actions_allocated;
    207       size_t new_actions_allocated = 2 * actions_allocated;
    208       actions_entry_t *new_actions =
    209 	xmalloc (new_actions_allocated * sizeof (actions_entry_t));
    210       size_t k;
    211 
    212       /* Don't use memcpy() here, because memcpy takes non-volatile arguments
    213 	 and is therefore not guaranteed to complete all memory stores before
    214 	 the next statement.  */
    215       for (k = 0; k < old_actions_allocated; k++)
    216 	new_actions[k] = old_actions[k];
    217       actions = new_actions;
    218       actions_allocated = new_actions_allocated;
    219       /* Now we can free the old actions array.  */
    220       if (old_actions != static_actions)
    221 	free (old_actions);
    222     }
    223   /* The two uses of 'volatile' in the types above (and ISO C 99 section
    224      5.1.2.3.(5)) ensure that we increment the actions_count only after
    225      the new action has been written to the memory location
    226      actions[actions_count].  */
    227   actions[actions_count].action = action;
    228   actions_count++;
    229 }
    230 
    231 
    232 /* ========================================================================= */
    233 
    234 
    235 static sigset_t fatal_signal_set;
    236 
    237 static void
    238 init_fatal_signal_set ()
    239 {
    240   static bool fatal_signal_set_initialized = false;
    241   if (!fatal_signal_set_initialized)
    242     {
    243       size_t i;
    244 
    245       init_fatal_signals ();
    246 
    247       sigemptyset (&fatal_signal_set);
    248       for (i = 0; i < num_fatal_signals; i++)
    249 	if (fatal_signals[i] >= 0)
    250 	  sigaddset (&fatal_signal_set, fatal_signals[i]);
    251 
    252       fatal_signal_set_initialized = true;
    253     }
    254 }
    255 
    256 /* Temporarily delay the catchable fatal signals.  */
    257 void
    258 block_fatal_signals ()
    259 {
    260   init_fatal_signal_set ();
    261   sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
    262 }
    263 
    264 /* Stop delaying the catchable fatal signals.  */
    265 void
    266 unblock_fatal_signals ()
    267 {
    268   init_fatal_signal_set ();
    269   sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
    270 }
    271