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