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