/src/openvswitch/lib/fatal-signal.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at: |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | #include <config.h> |
17 | | #include "backtrace.h" |
18 | | #include "fatal-signal.h" |
19 | | #include <errno.h> |
20 | | #include <signal.h> |
21 | | #include <stdbool.h> |
22 | | #include <stdio.h> |
23 | | #include <stdint.h> |
24 | | #include <stdlib.h> |
25 | | #include <string.h> |
26 | | #include <unistd.h> |
27 | | #include "ovs-thread.h" |
28 | | #include "openvswitch/poll-loop.h" |
29 | | #include "openvswitch/shash.h" |
30 | | #include "sset.h" |
31 | | #include "signals.h" |
32 | | #include "socket-util.h" |
33 | | #include "util.h" |
34 | | #include "openvswitch/vlog.h" |
35 | | |
36 | | #include "openvswitch/type-props.h" |
37 | | |
38 | | #if defined(HAVE_UNWIND) || defined(HAVE_BACKTRACE) |
39 | | #include "daemon-private.h" |
40 | | #endif |
41 | | |
42 | | #ifdef HAVE_BACKTRACE |
43 | | #include <execinfo.h> |
44 | | #endif |
45 | | |
46 | | #ifndef SIG_ATOMIC_MAX |
47 | | #define SIG_ATOMIC_MAX TYPE_MAXIMUM(sig_atomic_t) |
48 | | #endif |
49 | | |
50 | | VLOG_DEFINE_THIS_MODULE(fatal_signal); |
51 | | |
52 | | /* Signals to catch. */ |
53 | | static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM, |
54 | | SIGSEGV }; |
55 | | |
56 | | /* Hooks to call upon catching a signal */ |
57 | | struct hook { |
58 | | void (*hook_cb)(void *aux); |
59 | | void (*cancel_cb)(void *aux); |
60 | | void *aux; |
61 | | bool run_at_exit; |
62 | | }; |
63 | 0 | #define MAX_HOOKS 32 |
64 | | static struct hook hooks[MAX_HOOKS]; |
65 | | static size_t n_hooks; |
66 | | |
67 | | static int signal_fds[2]; |
68 | | static volatile sig_atomic_t stored_sig_nr = SIG_ATOMIC_MAX; |
69 | | |
70 | | static struct ovs_mutex mutex; |
71 | | |
72 | | static void call_hooks(int sig_nr) |
73 | | OVS_REQUIRES(mutex); |
74 | | |
75 | | /* Sets up a pipe or event handle that will be used to wake up the current |
76 | | * process after signal is received, so it can be processed outside of the |
77 | | * signal handler context in fatal_signal_run(). */ |
78 | | static void |
79 | | fatal_signal_create_wakeup_events(void) |
80 | 0 | { |
81 | 0 | xpipe_nonblocking(signal_fds); |
82 | 0 | } |
83 | | |
84 | | static void |
85 | | fatal_signal_destroy_wakeup_events(void) |
86 | 0 | { |
87 | 0 | close(signal_fds[0]); |
88 | 0 | signal_fds[0] = -1; |
89 | 0 | close(signal_fds[1]); |
90 | 0 | signal_fds[1] = -1; |
91 | 0 | } |
92 | | |
93 | | |
94 | | /* Initializes the fatal signal handling module. Calling this function is |
95 | | * optional, because calling any other function in the module will also |
96 | | * initialize it. However, in a multithreaded program, the module must be |
97 | | * initialized while the process is still single-threaded. */ |
98 | | void |
99 | | fatal_signal_init(void) |
100 | 0 | { |
101 | 0 | static bool inited = false; |
102 | |
|
103 | 0 | if (!inited) { |
104 | 0 | size_t i; |
105 | |
|
106 | 0 | assert_single_threaded(); |
107 | 0 | inited = true; |
108 | |
|
109 | 0 | ovs_mutex_init_recursive(&mutex); |
110 | | |
111 | | /* The dummy backtrace is needed. |
112 | | * See comment for send_backtrace_to_monitor(). */ |
113 | 0 | struct backtrace dummy_bt; |
114 | |
|
115 | 0 | backtrace_capture(&dummy_bt); |
116 | |
|
117 | 0 | fatal_signal_create_wakeup_events(); |
118 | |
|
119 | 0 | for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { |
120 | 0 | int sig_nr = fatal_signals[i]; |
121 | 0 | struct sigaction old_sa; |
122 | |
|
123 | 0 | xsigaction(sig_nr, NULL, &old_sa); |
124 | 0 | if (old_sa.sa_handler == SIG_DFL |
125 | 0 | && signal(sig_nr, fatal_signal_handler) == SIG_ERR) { |
126 | 0 | VLOG_FATAL("signal failed (%s)", ovs_strerror(errno)); |
127 | 0 | } |
128 | 0 | } |
129 | 0 | atexit(fatal_signal_atexit_handler); |
130 | 0 | } |
131 | 0 | } |
132 | | |
133 | | /* Registers 'hook_cb' to be called from inside poll_block() following a fatal |
134 | | * signal. 'hook_cb' does not need to be async-signal-safe. In a |
135 | | * multithreaded program 'hook_cb' might be called from any thread, with |
136 | | * threads other than the one running 'hook_cb' in unknown states. |
137 | | * |
138 | | * If 'run_at_exit' is true, 'hook_cb' is also called during normal process |
139 | | * termination, e.g. when exit() is called or when main() returns. |
140 | | * |
141 | | * If the current process forks, fatal_signal_fork() may be called to clear the |
142 | | * parent process's fatal signal hooks, so that 'hook_cb' is only called when |
143 | | * the child terminates, not when the parent does. When fatal_signal_fork() is |
144 | | * called, it calls the 'cancel_cb' function if it is nonnull, passing 'aux', |
145 | | * to notify that the hook has been canceled. This allows the hook to free |
146 | | * memory, etc. */ |
147 | | void |
148 | | fatal_signal_add_hook(void (*hook_cb)(void *aux), void (*cancel_cb)(void *aux), |
149 | | void *aux, bool run_at_exit) |
150 | 0 | { |
151 | 0 | fatal_signal_init(); |
152 | |
|
153 | 0 | ovs_mutex_lock(&mutex); |
154 | 0 | ovs_assert(n_hooks < MAX_HOOKS); |
155 | 0 | hooks[n_hooks].hook_cb = hook_cb; |
156 | 0 | hooks[n_hooks].cancel_cb = cancel_cb; |
157 | 0 | hooks[n_hooks].aux = aux; |
158 | 0 | hooks[n_hooks].run_at_exit = run_at_exit; |
159 | 0 | n_hooks++; |
160 | 0 | ovs_mutex_unlock(&mutex); |
161 | 0 | } |
162 | | |
163 | | #ifdef HAVE_UNWIND |
164 | | /* Convert unsigned long long to string. This is needed because |
165 | | * using snprintf() is not async signal safe. */ |
166 | | static inline int |
167 | | llong_to_hex_str(unsigned long long value, char *str) |
168 | | { |
169 | | int i = 0, res; |
170 | | |
171 | | if (value / 16 > 0) { |
172 | | i = llong_to_hex_str(value / 16, str); |
173 | | } |
174 | | |
175 | | res = value % 16; |
176 | | str[i] = "0123456789abcdef"[res]; |
177 | | |
178 | | return i + 1; |
179 | | } |
180 | | |
181 | | /* Send the backtrace buffer to monitor thread. |
182 | | * |
183 | | * Note that this runs in the signal handling context, any system |
184 | | * library functions used here must be async-signal-safe. |
185 | | */ |
186 | | static inline void |
187 | | send_backtrace_to_monitor(void) |
188 | | { |
189 | | /* volatile added to prevent a "clobbered" error on ppc64le with gcc */ |
190 | | volatile int dep; |
191 | | struct unw_backtrace unw_bt[UNW_MAX_DEPTH]; |
192 | | unw_cursor_t cursor; |
193 | | unw_context_t uc; |
194 | | |
195 | | if (daemonize_fd == -1) { |
196 | | return; |
197 | | } |
198 | | |
199 | | dep = 0; |
200 | | unw_getcontext(&uc); |
201 | | unw_init_local(&cursor, &uc); |
202 | | |
203 | | while (dep < UNW_MAX_DEPTH && unw_step(&cursor)) { |
204 | | memset(unw_bt[dep].func, 0, UNW_MAX_FUNCN); |
205 | | unw_get_reg(&cursor, UNW_REG_IP, &unw_bt[dep].ip); |
206 | | unw_get_proc_name(&cursor, unw_bt[dep].func, UNW_MAX_FUNCN, |
207 | | &unw_bt[dep].offset); |
208 | | dep++; |
209 | | } |
210 | | |
211 | | if (monitor) { |
212 | | ovs_ignore(write(daemonize_fd, unw_bt, |
213 | | dep * sizeof(struct unw_backtrace))); |
214 | | } else { |
215 | | /* Since there is no monitor daemon running, write backtrace |
216 | | * in current process. |
217 | | */ |
218 | | char ip_str[16], offset_str[6]; |
219 | | char line[64], fn_name[UNW_MAX_FUNCN]; |
220 | | |
221 | | vlog_direct_write_to_log_file_unsafe(BACKTRACE_DUMP_MSG); |
222 | | |
223 | | for (int i = 0; i < dep; i++) { |
224 | | memset(line, 0, sizeof line); |
225 | | memset(fn_name, 0, sizeof fn_name); |
226 | | memset(offset_str, 0, sizeof offset_str); |
227 | | memset(ip_str, ' ', sizeof ip_str); |
228 | | ip_str[sizeof(ip_str) - 1] = 0; |
229 | | |
230 | | llong_to_hex_str(unw_bt[i].ip, ip_str); |
231 | | llong_to_hex_str(unw_bt[i].offset, offset_str); |
232 | | |
233 | | strcat(line, "0x"); |
234 | | strcat(line, ip_str); |
235 | | strcat(line, "<"); |
236 | | memcpy(fn_name, unw_bt[i].func, UNW_MAX_FUNCN - 1); |
237 | | strcat(line, fn_name); |
238 | | strcat(line, "+0x"); |
239 | | strcat(line, offset_str); |
240 | | strcat(line, ">\n"); |
241 | | vlog_direct_write_to_log_file_unsafe(line); |
242 | | } |
243 | | } |
244 | | } |
245 | | #elif HAVE_BACKTRACE |
246 | | /* Send the backtrace to monitor thread. |
247 | | * |
248 | | * Note that this runs in the signal handling context, any system |
249 | | * library functions used here must be async-signal-safe. |
250 | | * backtrace() is only signal safe if the "libgcc" or equivalent was loaded |
251 | | * before the signal handler. In order to keep it safe the fatal_signal_init() |
252 | | * should always call backtrace_capture which will ensure that "libgcc" or |
253 | | * equivlent is loaded. |
254 | | */ |
255 | | static inline void |
256 | | send_backtrace_to_monitor(void) |
257 | 0 | { |
258 | 0 | struct backtrace bt; |
259 | |
|
260 | 0 | backtrace_capture(&bt); |
261 | |
|
262 | 0 | if (monitor && daemonize_fd > -1) { |
263 | 0 | ovs_ignore(write(daemonize_fd, &bt, sizeof bt)); |
264 | 0 | } else { |
265 | 0 | int log_fd = vlog_get_log_file_fd_unsafe(); |
266 | |
|
267 | 0 | if (log_fd < 0) { |
268 | 0 | return; |
269 | 0 | } |
270 | | |
271 | 0 | vlog_direct_write_to_log_file_unsafe(BACKTRACE_DUMP_MSG); |
272 | 0 | backtrace_symbols_fd(bt.frames, bt.n_frames, log_fd); |
273 | 0 | } |
274 | 0 | } |
275 | | #else |
276 | | static inline void |
277 | | send_backtrace_to_monitor(void) { |
278 | | /* Nothing. */ |
279 | | } |
280 | | #endif |
281 | | |
282 | | /* Handles fatal signal number 'sig_nr'. |
283 | | * |
284 | | * Ordinarily this is the actual signal handler. When other code needs to |
285 | | * handle one of our signals, however, it can register for that signal and, if |
286 | | * and when necessary, call this function to do fatal signal processing for it |
287 | | * and terminate the process. Currently only timeval.c does this, for SIGALRM. |
288 | | * (It is not important whether the other code sets up its signal handler |
289 | | * before or after this file, because this file will only set up a signal |
290 | | * handler in the case where the signal has its default handling.) */ |
291 | | void |
292 | | fatal_signal_handler(int sig_nr) |
293 | 0 | { |
294 | 0 | if (sig_nr == SIGSEGV) { |
295 | 0 | signal(sig_nr, SIG_DFL); /* Set it back immediately. */ |
296 | 0 | send_backtrace_to_monitor(); |
297 | 0 | raise(sig_nr); |
298 | 0 | } |
299 | 0 | ovs_ignore(write(signal_fds[1], "", 1)); |
300 | 0 | stored_sig_nr = sig_nr; |
301 | 0 | } |
302 | | |
303 | | /* Check whether a fatal signal has occurred and, if so, call the fatal signal |
304 | | * hooks and exit. |
305 | | * |
306 | | * This function is called automatically by poll_block(), but specialized |
307 | | * programs that may not always call poll_block() on a regular basis should |
308 | | * also call it periodically. (Therefore, any function with "block" in its |
309 | | * name should call fatal_signal_run() each time it is called, either directly |
310 | | * or through poll_block(), because such functions can only used by specialized |
311 | | * programs that can afford to block outside their main loop around |
312 | | * poll_block().) |
313 | | */ |
314 | | void |
315 | | fatal_signal_run(void) |
316 | 0 | { |
317 | 0 | sig_atomic_t sig_nr; |
318 | |
|
319 | 0 | fatal_signal_init(); |
320 | |
|
321 | 0 | sig_nr = stored_sig_nr; |
322 | 0 | if (sig_nr != SIG_ATOMIC_MAX) { |
323 | 0 | char namebuf[SIGNAL_NAME_BUFSIZE]; |
324 | |
|
325 | 0 | ovs_mutex_lock(&mutex); |
326 | |
|
327 | 0 | VLOG_WARN("terminating with signal %d (%s)", |
328 | 0 | (int)sig_nr, signal_name(sig_nr, namebuf, sizeof namebuf)); |
329 | 0 | call_hooks(sig_nr); |
330 | 0 | fflush(stderr); |
331 | | |
332 | | /* Re-raise the signal with the default handling so that the program |
333 | | * termination status reflects that we were killed by this signal */ |
334 | 0 | signal(sig_nr, SIG_DFL); |
335 | 0 | raise(sig_nr); |
336 | |
|
337 | 0 | ovs_mutex_unlock(&mutex); |
338 | 0 | OVS_NOT_REACHED(); |
339 | 0 | } |
340 | 0 | } |
341 | | |
342 | | void |
343 | | fatal_signal_wait(void) |
344 | 0 | { |
345 | 0 | fatal_signal_init(); |
346 | 0 | poll_fd_wait(signal_fds[0], POLLIN); |
347 | 0 | } |
348 | | |
349 | | void |
350 | | fatal_ignore_sigpipe(void) |
351 | 0 | { |
352 | 0 | signal(SIGPIPE, SIG_IGN); |
353 | 0 | } |
354 | | |
355 | | void |
356 | | fatal_signal_atexit_handler(void) |
357 | 0 | { |
358 | 0 | ovs_mutex_lock(&mutex); |
359 | 0 | call_hooks(0); |
360 | 0 | ovs_mutex_unlock(&mutex); |
361 | 0 | } |
362 | | |
363 | | static void |
364 | | call_hooks(int sig_nr) |
365 | | OVS_REQUIRES(mutex) |
366 | 0 | { |
367 | 0 | static volatile sig_atomic_t recurse = 0; |
368 | 0 | if (!recurse) { |
369 | 0 | size_t i; |
370 | |
|
371 | 0 | recurse = 1; |
372 | |
|
373 | 0 | for (i = 0; i < n_hooks; i++) { |
374 | 0 | struct hook *h = &hooks[i]; |
375 | 0 | if (sig_nr || h->run_at_exit) { |
376 | 0 | h->hook_cb(h->aux); |
377 | 0 | } |
378 | 0 | } |
379 | 0 | } |
380 | 0 | } |
381 | | |
382 | | |
383 | | /* Files to delete on exit. */ |
384 | | static struct sset files = SSET_INITIALIZER(&files); |
385 | | |
386 | | /* Has a hook function been registered with fatal_signal_add_hook() (and not |
387 | | * cleared by fatal_signal_fork())? */ |
388 | | static bool added_hook; |
389 | | |
390 | | static void unlink_files(void *aux); |
391 | | static void cancel_files(void *aux); |
392 | | static void do_unlink_files(void); |
393 | | |
394 | | /* Registers 'file' to be unlinked when the program terminates via exit() or a |
395 | | * fatal signal. */ |
396 | | void |
397 | | fatal_signal_add_file_to_unlink(const char *file) |
398 | 0 | { |
399 | 0 | fatal_signal_init(); |
400 | |
|
401 | 0 | ovs_mutex_lock(&mutex); |
402 | 0 | if (!added_hook) { |
403 | 0 | added_hook = true; |
404 | 0 | fatal_signal_add_hook(unlink_files, cancel_files, NULL, true); |
405 | 0 | } |
406 | |
|
407 | 0 | sset_add(&files, file); |
408 | 0 | ovs_mutex_unlock(&mutex); |
409 | 0 | } |
410 | | |
411 | | /* Unregisters 'file' from being unlinked when the program terminates via |
412 | | * exit() or a fatal signal. */ |
413 | | void |
414 | | fatal_signal_remove_file_to_unlink(const char *file) |
415 | 0 | { |
416 | 0 | fatal_signal_init(); |
417 | |
|
418 | 0 | ovs_mutex_lock(&mutex); |
419 | 0 | sset_find_and_delete(&files, file); |
420 | 0 | ovs_mutex_unlock(&mutex); |
421 | 0 | } |
422 | | |
423 | | /* Like fatal_signal_remove_file_to_unlink(), but also unlinks 'file'. |
424 | | * Returns 0 if successful, otherwise a positive errno value. */ |
425 | | int |
426 | | fatal_signal_unlink_file_now(const char *file) |
427 | 0 | { |
428 | 0 | int error; |
429 | |
|
430 | 0 | fatal_signal_init(); |
431 | |
|
432 | 0 | ovs_mutex_lock(&mutex); |
433 | |
|
434 | 0 | error = unlink(file) ? errno : 0; |
435 | 0 | if (error) { |
436 | 0 | VLOG_WARN("could not unlink \"%s\" (%s)", file, ovs_strerror(error)); |
437 | 0 | } |
438 | |
|
439 | 0 | fatal_signal_remove_file_to_unlink(file); |
440 | |
|
441 | 0 | ovs_mutex_unlock(&mutex); |
442 | |
|
443 | 0 | return error; |
444 | 0 | } |
445 | | |
446 | | static void |
447 | | unlink_files(void *aux OVS_UNUSED) |
448 | 0 | { |
449 | 0 | do_unlink_files(); |
450 | 0 | } |
451 | | |
452 | | static void |
453 | | cancel_files(void *aux OVS_UNUSED) |
454 | 0 | { |
455 | 0 | sset_clear(&files); |
456 | 0 | added_hook = false; |
457 | 0 | } |
458 | | |
459 | | static void |
460 | | do_unlink_files(void) |
461 | 0 | { |
462 | 0 | const char *file; |
463 | |
|
464 | 0 | SSET_FOR_EACH (file, &files) { |
465 | 0 | unlink(file); |
466 | 0 | } |
467 | 0 | } |
468 | | |
469 | | /* Clears all of the fatal signal hooks without executing them. If any of the |
470 | | * hooks passed a 'cancel_cb' function to fatal_signal_add_hook(), then those |
471 | | * functions will be called, allowing them to free resources, etc. |
472 | | * |
473 | | * Also re-creates wake-up events, so signals in one of the processes do not |
474 | | * wake up the other one. |
475 | | * |
476 | | * Following a fork, one of the resulting processes can call this function to |
477 | | * allow it to terminate without calling the hooks registered before calling |
478 | | * this function. New hooks registered after calling this function will take |
479 | | * effect normally. */ |
480 | | void |
481 | | fatal_signal_fork(void) |
482 | 0 | { |
483 | 0 | size_t i; |
484 | |
|
485 | 0 | assert_single_threaded(); |
486 | |
|
487 | 0 | fatal_signal_destroy_wakeup_events(); |
488 | 0 | fatal_signal_create_wakeup_events(); |
489 | |
|
490 | 0 | for (i = 0; i < n_hooks; i++) { |
491 | 0 | struct hook *h = &hooks[i]; |
492 | 0 | if (h->cancel_cb) { |
493 | 0 | h->cancel_cb(h->aux); |
494 | 0 | } |
495 | 0 | } |
496 | 0 | n_hooks = 0; |
497 | | |
498 | | /* Raise any signals that we have already received with the default |
499 | | * handler. */ |
500 | 0 | if (stored_sig_nr != SIG_ATOMIC_MAX) { |
501 | 0 | raise(stored_sig_nr); |
502 | 0 | } |
503 | 0 | } |
504 | | |
505 | | /* Blocks all fatal signals and returns previous signal mask into |
506 | | * 'prev_mask'. */ |
507 | | void |
508 | | fatal_signal_block(sigset_t *prev_mask) |
509 | 0 | { |
510 | 0 | int i; |
511 | 0 | sigset_t block_mask; |
512 | |
|
513 | 0 | sigemptyset(&block_mask); |
514 | 0 | for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { |
515 | 0 | int sig_nr = fatal_signals[i]; |
516 | 0 | sigaddset(&block_mask, sig_nr); |
517 | 0 | } |
518 | | xpthread_sigmask(SIG_BLOCK, &block_mask, prev_mask); |
519 | 0 | } |