timespec_get: New module.
[gnulib.git] / lib / fatal-signal.c
blob15aa5229824aeef2e877b91fbec4f62230784c55
1 /* Emergency actions in case of a fatal signal.
2 Copyright (C) 2003-2004, 2006-2021 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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.
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.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
19 #include <config.h>
21 /* Specification. */
22 #include "fatal-signal.h"
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <signal.h>
27 #include <unistd.h>
29 #include "glthread/lock.h"
30 #include "thread-optim.h"
31 #include "sig-handler.h"
32 #include "xalloc.h"
34 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
36 /* ========================================================================= */
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. */
53 static int fatal_signals[] =
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 /* Native Windows signals. */
77 #ifdef SIGBREAK
78 SIGBREAK,
79 #endif
83 #define num_fatal_signals (SIZEOF (fatal_signals) - 1)
85 /* Eliminate signals whose signal handler is SIG_IGN. */
87 static void
88 init_fatal_signals (void)
90 /* This function is multithread-safe even without synchronization, because
91 if two threads execute it simultaneously, the fatal_signals[] array will
92 not change any more after the first of the threads has completed this
93 function. */
94 static bool fatal_signals_initialized = false;
95 if (!fatal_signals_initialized)
97 size_t i;
99 for (i = 0; i < num_fatal_signals; i++)
101 struct sigaction action;
103 if (sigaction (fatal_signals[i], NULL, &action) >= 0
104 && get_handler (&action) == SIG_IGN)
105 fatal_signals[i] = -1;
108 fatal_signals_initialized = true;
113 /* ========================================================================= */
116 typedef _GL_ASYNC_SAFE void (*action_t) (int sig);
118 /* Type of an entry in the actions array.
119 The 'action' field is accessed from within the fatal_signal_handler(),
120 therefore we mark it as 'volatile'. */
121 typedef struct
123 volatile action_t action;
125 actions_entry_t;
127 /* The registered cleanup actions. */
128 static actions_entry_t static_actions[32];
129 static actions_entry_t * volatile actions = static_actions;
130 static sig_atomic_t volatile actions_count = 0;
131 static size_t actions_allocated = SIZEOF (static_actions);
134 /* The saved signal handlers.
135 Size 32 would not be sufficient: On HP-UX, SIGXCPU = 33, SIGXFSZ = 34. */
136 static struct sigaction saved_sigactions[64];
139 /* Uninstall the handlers. */
140 static _GL_ASYNC_SAFE void
141 uninstall_handlers (void)
143 size_t i;
145 for (i = 0; i < num_fatal_signals; i++)
146 if (fatal_signals[i] >= 0)
148 int sig = fatal_signals[i];
149 if (saved_sigactions[sig].sa_handler == SIG_IGN)
150 saved_sigactions[sig].sa_handler = SIG_DFL;
151 sigaction (sig, &saved_sigactions[sig], NULL);
156 /* The signal handler. It gets called asynchronously. */
157 static _GL_ASYNC_SAFE void
158 fatal_signal_handler (int sig)
160 for (;;)
162 /* Get the last registered cleanup action, in a reentrant way. */
163 action_t action;
164 size_t n = actions_count;
165 if (n == 0)
166 break;
167 n--;
168 actions_count = n;
169 action = actions[n].action;
170 /* Execute the action. */
171 action (sig);
174 /* Now execute the signal's default action.
175 If the signal being delivered was blocked, the re-raised signal would be
176 delivered when this handler returns. But the way we install this handler,
177 no signal is blocked, and the re-raised signal is delivered already
178 during raise(). */
179 uninstall_handlers ();
180 raise (sig);
184 /* Install the handlers. */
185 static void
186 install_handlers (void)
188 size_t i;
189 struct sigaction action;
191 action.sa_handler = &fatal_signal_handler;
192 /* If we get a fatal signal while executing fatal_signal_handler, enter
193 fatal_signal_handler recursively, since it is reentrant. Hence no
194 SA_RESETHAND. */
195 action.sa_flags = SA_NODEFER;
196 sigemptyset (&action.sa_mask);
197 for (i = 0; i < num_fatal_signals; i++)
198 if (fatal_signals[i] >= 0)
200 int sig = fatal_signals[i];
202 if (!(sig < sizeof (saved_sigactions) / sizeof (saved_sigactions[0])))
203 abort ();
204 sigaction (sig, &action, &saved_sigactions[sig]);
209 /* Lock that makes at_fatal_signal multi-thread safe. */
210 gl_lock_define_initialized (static, at_fatal_signal_lock)
212 /* Register a cleanup function to be executed when a catchable fatal signal
213 occurs. */
214 void
215 at_fatal_signal (action_t action)
217 bool mt = gl_multithreaded ();
219 if (mt) gl_lock_lock (at_fatal_signal_lock);
221 static bool cleanup_initialized = false;
222 if (!cleanup_initialized)
224 init_fatal_signals ();
225 install_handlers ();
226 cleanup_initialized = true;
229 if (actions_count == actions_allocated)
231 /* Extend the actions array. Note that we cannot use xrealloc(),
232 because then the cleanup() function could access an already
233 deallocated array. */
234 actions_entry_t *old_actions = actions;
235 size_t old_actions_allocated = actions_allocated;
236 size_t new_actions_allocated = 2 * actions_allocated;
237 actions_entry_t *new_actions =
238 XNMALLOC (new_actions_allocated, actions_entry_t);
239 size_t k;
241 /* Don't use memcpy() here, because memcpy takes non-volatile arguments
242 and is therefore not guaranteed to complete all memory stores before
243 the next statement. */
244 for (k = 0; k < old_actions_allocated; k++)
245 new_actions[k] = old_actions[k];
246 actions = new_actions;
247 actions_allocated = new_actions_allocated;
248 /* Now we can free the old actions array. */
249 /* No, we can't do that. If fatal_signal_handler is running in a
250 different thread and has already fetched the actions pointer (getting
251 old_actions) but not yet accessed its n-th element, that thread may
252 crash when accessing an element of the already freed old_actions
253 array. */
254 #if 0
255 if (old_actions != static_actions)
256 free (old_actions);
257 #endif
259 /* The two uses of 'volatile' in the types above (and ISO C 99 section
260 5.1.2.3.(5)) ensure that we increment the actions_count only after
261 the new action has been written to the memory location
262 actions[actions_count]. */
263 actions[actions_count].action = action;
264 actions_count++;
266 if (mt) gl_lock_unlock (at_fatal_signal_lock);
270 /* ========================================================================= */
273 static sigset_t fatal_signal_set;
275 static void
276 do_init_fatal_signal_set (void)
278 size_t i;
280 init_fatal_signals ();
282 sigemptyset (&fatal_signal_set);
283 for (i = 0; i < num_fatal_signals; i++)
284 if (fatal_signals[i] >= 0)
285 sigaddset (&fatal_signal_set, fatal_signals[i]);
288 /* Ensure that do_init_fatal_signal_set is called once only. */
289 gl_once_define(static, fatal_signal_set_once)
291 static void
292 init_fatal_signal_set (void)
294 gl_once (fatal_signal_set_once, do_init_fatal_signal_set);
297 /* Lock and counter that allow block_fatal_signals/unblock_fatal_signals pairs
298 to occur in different threads and even overlap in time. */
299 gl_lock_define_initialized (static, fatal_signals_block_lock)
300 static unsigned int fatal_signals_block_counter = 0;
302 /* Temporarily delay the catchable fatal signals. */
303 void
304 block_fatal_signals (void)
306 bool mt = gl_multithreaded ();
308 if (mt) gl_lock_lock (fatal_signals_block_lock);
310 if (fatal_signals_block_counter++ == 0)
312 init_fatal_signal_set ();
313 sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
316 if (mt) gl_lock_unlock (fatal_signals_block_lock);
319 /* Stop delaying the catchable fatal signals. */
320 void
321 unblock_fatal_signals (void)
323 bool mt = gl_multithreaded ();
325 if (mt) gl_lock_lock (fatal_signals_block_lock);
327 if (fatal_signals_block_counter == 0)
328 /* There are more calls to unblock_fatal_signals() than to
329 block_fatal_signals(). */
330 abort ();
331 if (--fatal_signals_block_counter == 0)
333 init_fatal_signal_set ();
334 sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
337 if (mt) gl_lock_unlock (fatal_signals_block_lock);
341 unsigned int
342 get_fatal_signals (int signals[64])
344 init_fatal_signal_set ();
347 int *p = signals;
348 size_t i;
350 for (i = 0; i < num_fatal_signals; i++)
351 if (fatal_signals[i] >= 0)
352 *p++ = fatal_signals[i];
353 return p - signals;
357 const sigset_t *
358 get_fatal_signal_set (void)
360 init_fatal_signal_set ();
361 return &fatal_signal_set;