Home | History | Annotate | Download | only in lib
      1 /* Emergency actions in case of a fatal signal.
      2    Copyright (C) 2003-2004, 2006-2012 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 3 of the License, or
      8    (at your option) 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, see <http://www.gnu.org/licenses/>.  */
     17 
     18 
     19 #include <config.h>
     20 
     21 /* Specification.  */
     22 #include "fatal-signal.h"
     23 
     24 #include <stdbool.h>
     25 #include <stdlib.h>
     26 #include <signal.h>
     27 #include <unistd.h>
     28 
     29 #include "sig-handler.h"
     30 #include "xalloc.h"
     31 
     32 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
     33 
     34 /* ========================================================================= */
     35 
     36 
     37 /* The list of fatal signals.
     38    These are those signals whose default action is to terminate the process
     39    without a core dump, except
     40      SIGKILL - because it cannot be caught,
     41      SIGALRM SIGUSR1 SIGUSR2 SIGPOLL SIGIO SIGLOST - because applications
     42        often use them for their own purpose,
     43      SIGPROF SIGVTALRM - because they are used for profiling,
     44      SIGSTKFLT - because it is more similar to SIGFPE, SIGSEGV, SIGBUS,
     45      SIGSYS - because it is more similar to SIGABRT, SIGSEGV,
     46      SIGPWR - because it of too special use,
     47      SIGRTMIN...SIGRTMAX - because they are reserved for application use.
     48    plus
     49      SIGXCPU, SIGXFSZ - because they are quite similar to SIGTERM.  */
     50 
     51 static int fatal_signals[] =
     52   {
     53     /* ISO C 99 signals.  */
     54 #ifdef SIGINT
     55     SIGINT,
     56 #endif
     57 #ifdef SIGTERM
     58     SIGTERM,
     59 #endif
     60     /* POSIX:2001 signals.  */
     61 #ifdef SIGHUP
     62     SIGHUP,
     63 #endif
     64 #ifdef SIGPIPE
     65     SIGPIPE,
     66 #endif
     67     /* BSD signals.  */
     68 #ifdef SIGXCPU
     69     SIGXCPU,
     70 #endif
     71 #ifdef SIGXFSZ
     72     SIGXFSZ,
     73 #endif
     74     /* Native Windows signals.  */
     75 #ifdef SIGBREAK
     76     SIGBREAK,
     77 #endif
     78     0
     79   };
     80 
     81 #define num_fatal_signals (SIZEOF (fatal_signals) - 1)
     82 
     83 /* Eliminate signals whose signal handler is SIG_IGN.  */
     84 
     85 static void
     86 init_fatal_signals (void)
     87 {
     88   static bool fatal_signals_initialized = false;
     89   if (!fatal_signals_initialized)
     90     {
     91       size_t i;
     92 
     93       for (i = 0; i < num_fatal_signals; i++)
     94         {
     95           struct sigaction action;
     96 
     97           if (sigaction (fatal_signals[i], NULL, &action) >= 0
     98               && get_handler (&action) == SIG_IGN)
     99             fatal_signals[i] = -1;
    100         }
    101 
    102       fatal_signals_initialized = true;
    103     }
    104 }
    105 
    106 
    107 /* ========================================================================= */
    108 
    109 
    110 typedef void (*action_t) (void);
    111 
    112 /* Type of an entry in the actions array.
    113    The 'action' field is accessed from within the fatal_signal_handler(),
    114    therefore we mark it as 'volatile'.  */
    115 typedef struct
    116 {
    117   volatile action_t action;
    118 }
    119 actions_entry_t;
    120 
    121 /* The registered cleanup actions.  */
    122 static actions_entry_t static_actions[32];
    123 static actions_entry_t * volatile actions = static_actions;
    124 static sig_atomic_t volatile actions_count = 0;
    125 static size_t actions_allocated = SIZEOF (static_actions);
    126 
    127 
    128 /* The saved signal handlers.
    129    Size 32 would not be sufficient: On HP-UX, SIGXCPU = 33, SIGXFSZ = 34.  */
    130 static struct sigaction saved_sigactions[64];
    131 
    132 
    133 /* Uninstall the handlers.  */
    134 static void
    135 uninstall_handlers (void)
    136 {
    137   size_t i;
    138 
    139   for (i = 0; i < num_fatal_signals; i++)
    140     if (fatal_signals[i] >= 0)
    141       {
    142         int sig = fatal_signals[i];
    143         if (saved_sigactions[sig].sa_handler == SIG_IGN)
    144           saved_sigactions[sig].sa_handler = SIG_DFL;
    145         sigaction (sig, &saved_sigactions[sig], NULL);
    146       }
    147 }
    148 
    149 
    150 /* The signal handler.  It gets called asynchronously.  */
    151 static void
    152 fatal_signal_handler (int sig)
    153 {
    154   for (;;)
    155     {
    156       /* Get the last registered cleanup action, in a reentrant way.  */
    157       action_t action;
    158       size_t n = actions_count;
    159       if (n == 0)
    160         break;
    161       n--;
    162       actions_count = n;
    163       action = actions[n].action;
    164       /* Execute the action.  */
    165       action ();
    166     }
    167 
    168   /* Now execute the signal's default action.
    169      If the signal being delivered was blocked, the re-raised signal would be
    170      delivered when this handler returns.  But the way we install this handler,
    171      no signal is blocked, and the re-raised signal is delivered already
    172      during raise().  */
    173   uninstall_handlers ();
    174   raise (sig);
    175 }
    176 
    177 
    178 /* Install the handlers.  */
    179 static void
    180 install_handlers (void)
    181 {
    182   size_t i;
    183   struct sigaction action;
    184 
    185   action.sa_handler = &fatal_signal_handler;
    186   /* If we get a fatal signal while executing fatal_signal_handler, enter
    187      fatal_signal_handler recursively, since it is reentrant.  Hence no
    188      SA_RESETHAND.  */
    189   action.sa_flags = SA_NODEFER;
    190   sigemptyset (&action.sa_mask);
    191   for (i = 0; i < num_fatal_signals; i++)
    192     if (fatal_signals[i] >= 0)
    193       {
    194         int sig = fatal_signals[i];
    195 
    196         if (!(sig < sizeof (saved_sigactions) / sizeof (saved_sigactions[0])))
    197           abort ();
    198         sigaction (sig, &action, &saved_sigactions[sig]);
    199       }
    200 }
    201 
    202 
    203 /* Register a cleanup function to be executed when a catchable fatal signal
    204    occurs.  */
    205 void
    206 at_fatal_signal (action_t action)
    207 {
    208   static bool cleanup_initialized = false;
    209   if (!cleanup_initialized)
    210     {
    211       init_fatal_signals ();
    212       install_handlers ();
    213       cleanup_initialized = true;
    214     }
    215 
    216   if (actions_count == actions_allocated)
    217     {
    218       /* Extend the actions array.  Note that we cannot use xrealloc(),
    219          because then the cleanup() function could access an already
    220          deallocated array.  */
    221       actions_entry_t *old_actions = actions;
    222       size_t old_actions_allocated = actions_allocated;
    223       size_t new_actions_allocated = 2 * actions_allocated;
    224       actions_entry_t *new_actions =
    225         XNMALLOC (new_actions_allocated, actions_entry_t);
    226       size_t k;
    227 
    228       /* Don't use memcpy() here, because memcpy takes non-volatile arguments
    229          and is therefore not guaranteed to complete all memory stores before
    230          the next statement.  */
    231       for (k = 0; k < old_actions_allocated; k++)
    232         new_actions[k] = old_actions[k];
    233       actions = new_actions;
    234       actions_allocated = new_actions_allocated;
    235       /* Now we can free the old actions array.  */
    236       if (old_actions != static_actions)
    237         free (old_actions);
    238     }
    239   /* The two uses of 'volatile' in the types above (and ISO C 99 section
    240      5.1.2.3.(5)) ensure that we increment the actions_count only after
    241      the new action has been written to the memory location
    242      actions[actions_count].  */
    243   actions[actions_count].action = action;
    244   actions_count++;
    245 }
    246 
    247 
    248 /* ========================================================================= */
    249 
    250 
    251 static sigset_t fatal_signal_set;
    252 
    253 static void
    254 init_fatal_signal_set (void)
    255 {
    256   static bool fatal_signal_set_initialized = false;
    257   if (!fatal_signal_set_initialized)
    258     {
    259       size_t i;
    260 
    261       init_fatal_signals ();
    262 
    263       sigemptyset (&fatal_signal_set);
    264       for (i = 0; i < num_fatal_signals; i++)
    265         if (fatal_signals[i] >= 0)
    266           sigaddset (&fatal_signal_set, fatal_signals[i]);
    267 
    268       fatal_signal_set_initialized = true;
    269     }
    270 }
    271 
    272 /* Temporarily delay the catchable fatal signals.  */
    273 void
    274 block_fatal_signals (void)
    275 {
    276   init_fatal_signal_set ();
    277   sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
    278 }
    279 
    280 /* Stop delaying the catchable fatal signals.  */
    281 void
    282 unblock_fatal_signals (void)
    283 {
    284   init_fatal_signal_set ();
    285   sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
    286 }
    287