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 | XNMALLOC (new_actions_allocated, 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 | }
|
---|