1/* Emergency actions in case of a fatal signal. 2 Copyright (C) 2003-2004, 2006-2007 Free Software Foundation, Inc. 3 Written by Bruno Haible <bruno@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 "xalloc.h" 30 31#define SIZEOF(a) (sizeof(a) / sizeof(a[0])) 32 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 51static 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 /* Woe32 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 85static void 86init_fatal_signals (void) 87{ 88 static bool fatal_signals_initialized = false; 89 if (!fatal_signals_initialized) 90 { 91#if HAVE_SIGACTION 92 size_t i; 93 94 for (i = 0; i < num_fatal_signals; i++) 95 { 96 struct sigaction action; 97 98 if (sigaction (fatal_signals[i], NULL, &action) >= 0 99 && action.sa_handler == SIG_IGN) 100 fatal_signals[i] = -1; 101 } 102#endif 103 104 fatal_signals_initialized = true; 105 } 106} 107 108 109/* ========================================================================= */ 110 111 112typedef void (*action_t) (void); 113 114/* Type of an entry in the actions array. 115 The 'action' field is accessed from within the fatal_signal_handler(), 116 therefore we mark it as 'volatile'. */ 117typedef struct 118{ 119 volatile action_t action; 120} 121actions_entry_t; 122 123/* The registered cleanup actions. */ 124static actions_entry_t static_actions[32]; 125static actions_entry_t * volatile actions = static_actions; 126static sig_atomic_t volatile actions_count = 0; 127static size_t actions_allocated = SIZEOF (static_actions); 128 129 130/* Uninstall the handlers. */ 131static inline void 132uninstall_handlers () 133{ 134 size_t i; 135 136 for (i = 0; i < num_fatal_signals; i++) 137 if (fatal_signals[i] >= 0) 138 signal (fatal_signals[i], SIG_DFL); 139} 140 141 142/* The signal handler. It gets called asynchronously. */ 143static void 144fatal_signal_handler (int sig) 145{ 146 for (;;) 147 { 148 /* Get the last registered cleanup action, in a reentrant way. */ 149 action_t action; 150 size_t n = actions_count; 151 if (n == 0) 152 break; 153 n--; 154 actions_count = n; 155 action = actions[n].action; 156 /* Execute the action. */ 157 action (); 158 } 159 160 /* Now execute the signal's default action. 161 If signal() blocks the signal being delivered for the duration of the 162 signal handler's execution, the re-raised signal is delivered when this 163 handler returns; otherwise it is delivered already during raise(). */ 164 uninstall_handlers (); 165#if HAVE_RAISE 166 raise (sig); 167#else 168 kill (getpid (), sig); 169#endif 170} 171 172 173/* Install the handlers. */ 174static inline void 175install_handlers () 176{ 177 size_t i; 178 179 for (i = 0; i < num_fatal_signals; i++) 180 if (fatal_signals[i] >= 0) 181 signal (fatal_signals[i], &fatal_signal_handler); 182} 183 184 185/* Register a cleanup function to be executed when a catchable fatal signal 186 occurs. */ 187void 188at_fatal_signal (action_t action) 189{ 190 static bool cleanup_initialized = false; 191 if (!cleanup_initialized) 192 { 193 init_fatal_signals (); 194 install_handlers (); 195 cleanup_initialized = true; 196 } 197 198 if (actions_count == actions_allocated) 199 { 200 /* Extend the actions array. Note that we cannot use xrealloc(), 201 because then the cleanup() function could access an already 202 deallocated array. */ 203 actions_entry_t *old_actions = actions; 204 size_t old_actions_allocated = actions_allocated; 205 size_t new_actions_allocated = 2 * actions_allocated; 206 actions_entry_t *new_actions = 207 XNMALLOC (new_actions_allocated, actions_entry_t); 208 size_t k; 209 210 /* Don't use memcpy() here, because memcpy takes non-volatile arguments 211 and is therefore not guaranteed to complete all memory stores before 212 the next statement. */ 213 for (k = 0; k < old_actions_allocated; k++) 214 new_actions[k] = old_actions[k]; 215 actions = new_actions; 216 actions_allocated = new_actions_allocated; 217 /* Now we can free the old actions array. */ 218 if (old_actions != static_actions) 219 free (old_actions); 220 } 221 /* The two uses of 'volatile' in the types above (and ISO C 99 section 222 5.1.2.3.(5)) ensure that we increment the actions_count only after 223 the new action has been written to the memory location 224 actions[actions_count]. */ 225 actions[actions_count].action = action; 226 actions_count++; 227} 228 229 230/* ========================================================================= */ 231 232 233static sigset_t fatal_signal_set; 234 235static void 236init_fatal_signal_set () 237{ 238 static bool fatal_signal_set_initialized = false; 239 if (!fatal_signal_set_initialized) 240 { 241 size_t i; 242 243 init_fatal_signals (); 244 245 sigemptyset (&fatal_signal_set); 246 for (i = 0; i < num_fatal_signals; i++) 247 if (fatal_signals[i] >= 0) 248 sigaddset (&fatal_signal_set, fatal_signals[i]); 249 250 fatal_signal_set_initialized = true; 251 } 252} 253 254/* Temporarily delay the catchable fatal signals. */ 255void 256block_fatal_signals () 257{ 258 init_fatal_signal_set (); 259 sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL); 260} 261 262/* Stop delaying the catchable fatal signals. */ 263void 264unblock_fatal_signals () 265{ 266 init_fatal_signal_set (); 267 sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL); 268} 269