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@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