Line data Source code
1 : // Copyright (c) 2012-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : /** \file
21 : * \brief Implementation of the Signal Handler class.
22 : *
23 : * The Signal Handler class is used to make sure that we get a log entry in
24 : * case a terminal signal happens. We can also use this class to ignore
25 : * certain signals and get callbacks called. In many cases, our services want
26 : * to do that with many signals and that code is pretty much always the same.
27 : *
28 : * The best is to add the signal_handler to your main() function like
29 : * so:
30 : *
31 : * \code
32 : * #include <eventdispatcher/signal_handler.h>
33 : *
34 : * int main(int argc, char * argv[])
35 : * {
36 : * ed::signal_handler::create_instance();
37 : * ...
38 : * }
39 : * \endcode
40 : *
41 : * This is sufficient to get all the events of interest captured and
42 : * reported with a stack trace in your logs (the eventdispatcher makes
43 : * use of the snaplogger for that purpose). Note that the function returns
44 : * a pointer to the signal_handler object, so you can save that pointer
45 : * and make tweaks immediately after (see examples of tweaks below).
46 : *
47 : * Note that it is possible to call ed::signal_handler::get_instance()
48 : * and never call the ed::signal_handler::create_instance() function.
49 : * However, the create function will setup defaults in the handler which
50 : * makes it easy to start with the expected state.
51 : *
52 : * In sub-functions, you may tweak the setup by doing various calls such as:
53 : *
54 : * \code
55 : * ed::signal_handler::get_instance()->add_terminal_signals(ed::signal_handler::SIGNAL_CHILD);
56 : * \endcode
57 : *
58 : * \note
59 : * You can also add that terminal signal to the mask of the create_handler()
60 : * function. For example, you may want to add SIGNAL_TERMINATE (SIGTERM),
61 : * SIGNAL_INTERRUPT (Ctrl-C) and SIGNAL_QUIT to the list of terminal signals.
62 : * At the same time, those are expected termination signals but if you have
63 : * a TCP controller connection and can send a QUIT message, then that message
64 : * should be used and receiving those additional signals could be viewed as
65 : * an unexpected event. For that reason, we have the EXTENDED_SIGNAL_TERMINAL
66 : * which includes those additional three signals.
67 : *
68 : * Now if one of your process children dies, you will die too.
69 : *
70 : * Also, if you have an object that deals with pipes or sockets and you do
71 : * not want to receive the SIGPIPE, you can do:
72 : *
73 : * \code
74 : * ed::signal_handler::get_instance()->add_ignored_signals(ed::signal_handler::SIGNAL_PIPE);
75 : * \endcode
76 : *
77 : * \note
78 : * You can also add that ignored signal to the mask of the create_handler()
79 : * function.
80 : *
81 : * Finally, you may be interested to capture a signal such as the SIGUSR1
82 : * signal. You do that by first adding the signal as a terminal signal and
83 : * then by adding a callback which will return true (i.e. signal handled).
84 : *
85 : * \note
86 : * You may want to consider using a signal connection object instead
87 : * of a callback for such a flag. You can wait on those signals with a
88 : * poll() and you avoid the EINTR errors which are so difficult to deal
89 : * with in a very large piece of software (see eventdispatcher/signal.h).
90 : *
91 : * \code
92 : * bool handle_usr1(
93 : * callback_id_t callback_id
94 : * , int callback_sig
95 : * , siginfo_t const * info
96 : * , ucontext_t const * ucontext)
97 : * {
98 : * ...handle USR1 signal...
99 : * return true;
100 : * }
101 : *
102 : * ...
103 : * signal_handler::pointer_t sh(ed::signal_handler::get_instance());
104 : * sh->add_terminal_signals(ed::signal_handler::SIGNAL_USR1);
105 : * sh->add_callback(1, ed::signal_handler::SIGNAL_USR1, &handle_usr1);
106 : * ...
107 : * \endcode
108 : *
109 : * \note
110 : * This class is thread safe. It will lock its own mutex before running
111 : * functions dealing with this class parameters. However, this may also
112 : * results in a deadlock whenever a signal occurs.
113 : */
114 :
115 : // self
116 : //
117 : #include "eventdispatcher/signal_handler.h"
118 :
119 : #include "eventdispatcher/exception.h"
120 :
121 :
122 : // snaplogger lib
123 : //
124 : #include <snaplogger/message.h>
125 :
126 :
127 : // cppthread lib
128 : //
129 : #include <cppthread/mutex.h>
130 : #include <cppthread/guard.h>
131 :
132 :
133 : // snapdev lib
134 : //
135 : #include <snapdev/not_reached.h>
136 : #include <snapdev/not_used.h>
137 :
138 :
139 : // C++ lib
140 : //
141 : #include <iostream>
142 :
143 :
144 : // C lib
145 : //
146 : #include <string.h>
147 : #include <signal.h>
148 :
149 :
150 : // last include
151 : //
152 : #include <snapdev/poison.h>
153 :
154 :
155 :
156 :
157 : namespace ed
158 : {
159 :
160 :
161 : namespace
162 : {
163 :
164 :
165 : /** \brief Signal number to signal name table.
166 : *
167 : * This table is used to transform a signal number in a name.
168 : *
169 : * \sa signal_handler::get_signal_name()
170 : */
171 : #pragma GCC diagnostic push
172 : #pragma GCC diagnostic ignored "-Wpedantic"
173 : constexpr char const * g_signal_names[64] =
174 : {
175 : [0] = nullptr,
176 : [SIGHUP] = "SIGHUP",
177 : [SIGINT] = "SIGINT",
178 : [SIGQUIT] = "SIGQUIT",
179 : [SIGILL] = "SIGILL",
180 : [SIGTRAP] = "SIGTRAP",
181 : [SIGABRT] = "SIGABRT",
182 : [SIGBUS] = "SIGBUS",
183 : [SIGFPE] = "SIGFPE",
184 : [SIGKILL] = "SIGKILL",
185 : [SIGUSR1] = "SIGUSR1",
186 : [SIGSEGV] = "SIGSEGV",
187 : [SIGUSR2] = "SIGUSR2",
188 : [SIGPIPE] = "SIGPIPE",
189 : [SIGALRM] = "SIGALRM",
190 : [SIGTERM] = "SIGTERM",
191 : [SIGSTKFLT] = "SIGSTKFLT",
192 : [SIGCHLD] = "SIGCHLD",
193 : [SIGCONT] = "SIGCONT",
194 : [SIGSTOP] = "SIGSTOP",
195 : [SIGTSTP] = "SIGTSTP",
196 : [SIGTTIN] = "SIGTTIN",
197 : [SIGTTOU] = "SIGTTOU",
198 : [SIGURG] = "SIGURG",
199 : [SIGXCPU] = "SIGXCPU",
200 : [SIGXFSZ] = "SIGXFSZ",
201 : [SIGVTALRM] = "SIGVTALRM",
202 : [SIGPROF] = "SIGPROF",
203 : [SIGWINCH] = "SIGWINCH",
204 : [SIGPOLL] = "SIGPOLL",
205 : [SIGPWR] = "SIGPWR",
206 : [SIGSYS] = "SIGSYS",
207 : };
208 : #pragma GCC diagnostic pop
209 :
210 :
211 : /** \brief Set of signals for which we want to log a stack trace.
212 : *
213 : * Just knowing where a signal occurred is often a bit limited. Knowing
214 : * the call stack for 10 to 20 items is much more helpful. However, for
215 : * some signals, it's generally totally useless so we use a mask to know
216 : * which signals to log the stack trace for.
217 : */
218 : signal_handler::signal_mask_t g_show_stack = signal_handler::DEFAULT_SHOW_STACK;
219 :
220 :
221 : /** \brief The allocated signal_handler instance.
222 : *
223 : * The get_instance() allocates this handler.
224 : *
225 : * \warning
226 : * If you want to call the create_instance() function, then it has to be
227 : * called before get_instance().
228 : */
229 2 : signal_handler::pointer_t g_signal_handler = signal_handler::pointer_t();
230 :
231 :
232 :
233 : }
234 : // no name namespace
235 :
236 :
237 :
238 :
239 : /** \brief Initialize the signal handler class.
240 : *
241 : * This function sets all the signal action structures to a nullptr
242 : * and then it sets up the \p terminal and \p ignored signals as
243 : * defined by the corresponding masks.
244 : *
245 : * You add to the set of signals that are terminal and ignored
246 : * later with the add_terminal_signals() and the add_ignored_signals().
247 : *
248 : * You can remove from the set of signals that are terminal or ignored
249 : * by calling the remove_signals() function.
250 : *
251 : * This function is private. It gets called by the get_instance() function.
252 : * You may also want to use the create_instance() function the first time
253 : * you create an instance.
254 : */
255 0 : signal_handler::signal_handler()
256 : {
257 0 : }
258 :
259 :
260 : /** \brief Restore the signals.
261 : *
262 : * The signal handler destructor restores all the signals that it changed.
263 : *
264 : * \note
265 : * At this point, the destructor is never called since we use an instance
266 : * and we do not give a way to destroy it. Unloading the library would have
267 : * that effect, but that generally doesn't happen.
268 : */
269 0 : signal_handler::~signal_handler()
270 : {
271 0 : remove_all_signals();
272 0 : g_signal_handler = nullptr;
273 0 : }
274 :
275 :
276 : /** \brief Handy function used to create the signal handler instance.
277 : *
278 : * In many cases, you want to create the signal handler and then setup
279 : * the terminal signal, the ignored signals, and a callback. This function
280 : * does all of that for you in one go.
281 : *
282 : * \code
283 : * int main(int argc, char * argv[])
284 : * {
285 : * ed::signal_handler::create_instance(
286 : * ed::signal_mask_t terminal = DEFAULT_SIGNAL_TERMINAL
287 : * , ed::signal_mask_t ignored = DEFAULT_SIGNAL_IGNORE
288 : * , SIGFPE
289 : * , handle_floating_point_errors);
290 : * ...
291 : * }
292 : * \endcode
293 : *
294 : * This function automatically calls the add_terminal_signals()
295 : * with the specified \p terminal parameter.
296 : *
297 : * It then calls the add_ignored_signals with the \p ignored parameter.
298 : *
299 : * If the \p sig parameter is set to a non-zero value then the
300 : * add_callback() gets called. In that case, the \p callback must
301 : * be properly defined.
302 : *
303 : * \param[in] terminal Mask with the set of terminal signals.
304 : * \param[in] ignored Mask with set of signals to be ignored.
305 : * \param[in] callback_id An identifier to attach the callback with.
306 : * \param[in] callback_sig Signal for which you want a callback.
307 : * \param[in] callback The callback function to call.
308 : *
309 : * \return The pointer to the signal handler.
310 : */
311 0 : signal_handler::pointer_t signal_handler::create_instance(
312 : signal_mask_t terminal
313 : , signal_mask_t ignored
314 : , id_t callback_id
315 : , int callback_sig
316 : , callback_t callback)
317 : {
318 0 : cppthread::guard g(*cppthread::g_system_mutex);
319 :
320 0 : if(g_signal_handler != nullptr)
321 : {
322 0 : throw invalid_callback("signal_handler::create_instance() must be called once before signal_handler::get_instance() ever gets called.");
323 : }
324 :
325 0 : pointer_t handler(get_instance());
326 :
327 0 : handler->add_terminal_signals(terminal);
328 0 : handler->add_ignored_signals(ignored);
329 :
330 0 : if(callback_sig > 0)
331 : {
332 0 : handler->add_callback(callback_id, callback_sig, callback);
333 : }
334 :
335 0 : return handler;
336 : }
337 :
338 :
339 : /** \brief Returns the signal handler instance.
340 : *
341 : * This function creates an instance of the signal handler and returns
342 : * the pointer. The very first time, though, you probably want to call
343 : * the create_instance() function so as to automatically initialize the
344 : * class. You can also reprogram your own initialization.
345 : * This function can be called any number of times.
346 : *
347 : * \warning
348 : * If you have threads, make sure to call this function at least once
349 : * before you create a thread since it is not otherwise thread safe.
350 : * Actually, the whole class is not considered thread safe so you should
351 : * create and initialize it.
352 : *
353 : * \return A pointer to the signal handler.
354 : */
355 0 : signal_handler::pointer_t signal_handler::get_instance()
356 : {
357 0 : cppthread::guard g(*cppthread::g_system_mutex);
358 :
359 0 : if(g_signal_handler == nullptr)
360 : {
361 0 : g_signal_handler.reset(new signal_handler());
362 : }
363 0 : return g_signal_handler;
364 : }
365 :
366 :
367 : /** \brief Add a callback to the signal handler.
368 : *
369 : * This function adds a callback to the signal_handler object. Callbacks
370 : * get called whenever the specified \p sig is received.
371 : *
372 : * You can add any number of callbacks per signal.
373 : *
374 : * The \p id parameter is a number you define. It is useful really only
375 : * if you add the same callback multiple times with different identifiers
376 : * and in case you want to be able to call the remove_callback() function.
377 : * You can always use `0` in all other cases.
378 : *
379 : * If you set the \p sig parameter to 0, then it will match all the
380 : * signals received. In other words, that callback will be called whatever
381 : * the received signal is (i.e. _match any_).
382 : *
383 : * \param[in] id The callback identifier.
384 : * \param[in] sig The signal (i.e. SIGPIPE) to assign a callback to.
385 : * \param[in] callback The user callback to call on \p sig signal.
386 : */
387 0 : void signal_handler::add_callback(callback_id_t id, int sig, callback_t callback)
388 : {
389 0 : if(static_cast<std::size_t>(sig) >= sizeof(f_signal_actions) / sizeof(f_signal_actions[0]))
390 : {
391 : throw invalid_signal(
392 : "signal_handler::add_callback() called with invalid signal number "
393 0 : + std::to_string(sig));
394 : }
395 :
396 0 : if(callback == nullptr)
397 : {
398 0 : throw invalid_callback("signal_handler::add_callback() called with nullptr as the callback.");
399 : }
400 :
401 0 : cppthread::guard g(f_mutex);
402 :
403 0 : f_callbacks.push_back(signal_callback_t{id, sig, callback});
404 0 : }
405 :
406 :
407 : /** \brief Remove a user callback.
408 : *
409 : * This function searches for the specified callback using its identifier
410 : * and removes it from the list of callbacks of the signal_handler object.
411 : *
412 : * If more than one callback is assigned the same identifier, then all
413 : * those callbacks are removed at once.
414 : *
415 : * \note
416 : * To be able to remove your callback, you must keep a reference to it.
417 : * So if you use std::bind() to call add_callback(), you need to keep
418 : * a reference to that std::bind().
419 : *
420 : * \param[in] id The callback identifier.
421 : */
422 0 : void signal_handler::remove_callback(id_t id)
423 : {
424 0 : cppthread::guard g(f_mutex);
425 :
426 0 : for(auto it(f_callbacks.begin()); it != f_callbacks.end();)
427 : {
428 0 : if(it->f_id == id)
429 : {
430 0 : it = f_callbacks.erase(it);
431 : }
432 : else
433 : {
434 0 : ++it;
435 : }
436 : }
437 0 : }
438 :
439 :
440 : /** \brief Set signals that generate a stack trace.
441 : *
442 : * Whenever a signal happens, this class can automatically log a stack
443 : * trace of location of the event. By default the mask is set to
444 : * DEFAULT_SHOW_STACK.
445 : *
446 : * You may add signals to the list by doing:
447 : *
448 : * \code
449 : * set_show_stack(get_show_stack() | ed::signal_handler::SIGNAL_TRAP);
450 : * \endcode
451 : *
452 : * \param[in] sigs The mask of signals which will generate a stack trace.
453 : */
454 0 : void signal_handler::set_show_stack(signal_mask_t sigs)
455 : {
456 0 : cppthread::guard g(f_mutex);
457 :
458 0 : g_show_stack = sigs;
459 0 : }
460 :
461 :
462 : /** \brief Get list of signals that generate a stack trace.
463 : *
464 : * This function retrieves the current list of signal that request the
465 : * class to generate a stack trace when they happen.
466 : *
467 : * It can be used with the set_show_stack() function in order to add or
468 : * remove some signals from that list.
469 : *
470 : * \return The mask of signals that generate a stack trace.
471 : */
472 0 : signal_handler::signal_mask_t signal_handler::get_show_stack() const
473 : {
474 0 : cppthread::guard g(f_mutex);
475 :
476 0 : return g_show_stack;
477 : }
478 :
479 :
480 : /** \brief Add signals that terminate the process.
481 : *
482 : * Any signal that you consider terminal should be added using this function.
483 : * Whenever that signal is raised by the system, the process_signal() function
484 : * gets called. If the corresponding bit is set in the show stack mask, then
485 : * the function first sends the stack trace to the logs, then it terminates
486 : * the process with a log specifying which signal terminated the process.
487 : *
488 : * \note
489 : * Some signals can't be caught (i.e. SIGKILL). It is useless to add those
490 : * to this list.
491 : *
492 : * \param[in] sigs The mask of signals that are expected to terminate your
493 : * process.
494 : */
495 0 : void signal_handler::add_terminal_signals(signal_mask_t sigs)
496 : {
497 0 : cppthread::guard g(f_mutex);
498 :
499 0 : for(size_t i = 0; i < sizeof(f_signal_actions) / sizeof(f_signal_actions[0]); ++i)
500 : {
501 0 : if((sigs & (1L << i)) != 0 && f_signal_actions[i] == nullptr)
502 : {
503 0 : sigaction_t action = sigaction_t();
504 0 : action.sa_sigaction = signal_handler_func;
505 0 : action.sa_flags = SA_SIGINFO;
506 :
507 0 : f_signal_actions[i] = std::make_shared<sigaction_t>();
508 0 : sigaction(i, &action, f_signal_actions[i].get());
509 : }
510 : }
511 0 : }
512 :
513 :
514 : /** \brief The class allows you to ignore some signals.
515 : *
516 : * This function allows you to add a list of signals you want to ignore.
517 : * For example, it is often that you want to ignore SIGPIPE signals when
518 : * you deal with sockets, otherwise, reading or writing to a closed socket
519 : * generates that signal instead of just returns a -1.
520 : *
521 : * \note
522 : * Trying to ignore signals such as SIGSEGV and SIGBUS is not a good idea.
523 : *
524 : * \param[in] sigs The mask of signals you want to ignore.
525 : */
526 0 : void signal_handler::add_ignored_signals(signal_mask_t sigs)
527 : {
528 0 : cppthread::guard g(f_mutex);
529 :
530 0 : for(size_t i = 0; i < sizeof(f_signal_actions) / sizeof(f_signal_actions[0]); ++i)
531 : {
532 0 : if((sigs & (1L << i)) != 0 && f_signal_actions[i] == nullptr)
533 : {
534 0 : sigaction_t action = sigaction_t();
535 0 : action.sa_handler = SIG_IGN;
536 0 : action.sa_sigaction = signal_handler_func;
537 :
538 0 : f_signal_actions[i] = std::make_shared<sigaction_t>();
539 0 : sigaction(i, &action, f_signal_actions[i].get());
540 : }
541 : }
542 0 : }
543 :
544 :
545 : /** \brief Remove a terminal or ignored signal.
546 : *
547 : * This function removes the callback for the specified signals. The function
548 : * has no effect is you did not first add the signal with one of the
549 : * add_terminal_signals() or add_ignored_signals() functions.
550 : *
551 : * \param[in] sigs The mask of signals to remove from the list of signals
552 : * we manage through the signal handler.
553 : */
554 0 : void signal_handler::remove_signals(signal_mask_t sigs)
555 : {
556 0 : cppthread::guard g(f_mutex);
557 :
558 0 : for(size_t i = 0; i < sizeof(f_signal_actions) / sizeof(f_signal_actions[0]); ++i)
559 : {
560 0 : if((sigs & (1L << i)) != 0 && f_signal_actions[i] != nullptr)
561 : {
562 0 : sigaction(i, f_signal_actions[i].get(), nullptr);
563 0 : f_signal_actions[i].reset();
564 : }
565 : }
566 0 : }
567 :
568 :
569 : /** \brief Remove all the signals.
570 : *
571 : * Remove all the signals at once.
572 : *
573 : * This function is primarily used when the signal_handler is deleted to
574 : * restore the state to normal. It should be the very last thing you want
575 : * to do. You are welcome to call this function at any time, of course,
576 : * with the consequence that none of the signals will now be handled by
577 : * this handler.
578 : *
579 : * This is equivalent to:
580 : *
581 : * \code
582 : * remove_signals(ed::signal_handler::ALL_SIGNALS);
583 : * \endcode
584 : */
585 0 : void signal_handler::remove_all_signals()
586 : {
587 0 : remove_signals(ALL_SIGNALS);
588 0 : }
589 :
590 :
591 : /** \brief Get the name of the signal.
592 : *
593 : * This function converts the signal \p sig to a name one can use to print
594 : * the name in a log or a console.
595 : *
596 : * \param[in] sig The signal number.
597 : *
598 : * \return The name of the specified signal or nullptr if \p sig is invalid.
599 : */
600 0 : char const * signal_handler::get_signal_name(int sig)
601 : {
602 0 : if(static_cast<std::size_t>(sig) >= sizeof(g_signal_names) / sizeof(g_signal_names[0]))
603 : {
604 0 : return nullptr;
605 : }
606 0 : return g_signal_names[sig];
607 : }
608 :
609 :
610 : /** \brief This is out handler.
611 : *
612 : * This function is the handler that gets called whenever a signal is
613 : * raised.
614 : *
615 : * \param[in] sig The signal that generated this handler.
616 : * \param[in] info Information about the signal handler.
617 : * \param[in] context The context from when the interrupt was generated.
618 : */
619 0 : void signal_handler::signal_handler_func(
620 : int sig
621 : , siginfo_t * info
622 : , void * context)
623 : {
624 : // if we are called, g_signal_handler can't be nullptr
625 : //
626 0 : g_signal_handler->process_signal(sig, info, reinterpret_cast<ucontext_t *>(context));
627 0 : }
628 :
629 :
630 : /** \brief Callback to process a signal we just received.
631 : *
632 : * This function is the one called whenever a signal is received by your
633 : * process. It includes the signal number (\p sig), the signal information
634 : * as defined by the kernel (\p info) and the user context when the signal
635 : * happened (\p ucontext).
636 : *
637 : * By default, the function prints out the stack trace if requested for that
638 : * signal and then print a log message about the signal that generated
639 : * this call. Finally, it calls `std::terminate()` to terminate the process.
640 : *
641 : * However, you can add callbacks to capture the signals in your own handler.
642 : * When doing so, your callback can return true, meaning that you handled the
643 : * signal and you do not want the default process to take over. See the
644 : * add_callback() for additional details.
645 : *
646 : * \note
647 : * An `exit(1)` could be very problematic, so would raising an exception in
648 : * a thread at an impromptus moment (especially in a signal handler). So here
649 : * we use `std::terminate()`.
650 : *
651 : * \param[in] sig The signal being processed.
652 : * \param[in] info Information about the signal handler.
653 : * \param[in] context The context from when the interrupt was generated.
654 : */
655 0 : void signal_handler::process_signal(
656 : int sig
657 : , siginfo_t * info
658 : , ucontext_t * ucontext)
659 : {
660 0 : callback_list_t callbacks;
661 0 : bool show_stack(false);
662 :
663 : // here we lock as little as possible
664 : {
665 0 : cppthread::guard g(f_mutex);
666 0 : callbacks = f_callbacks;
667 0 : show_stack = (g_show_stack & (1UL << sig)) != 0;
668 : }
669 :
670 0 : bool handled(false);
671 0 : for(auto it(callbacks.begin()); it != callbacks.end(); ++it)
672 : {
673 0 : if(it->f_sig == sig)
674 : {
675 0 : if((it->f_callback)(it->f_id, sig, info, ucontext))
676 : {
677 0 : handled = true;
678 : }
679 : }
680 : }
681 0 : if(handled)
682 : {
683 : // user said it was handled, leave it to that...
684 : //
685 0 : return;
686 : }
687 :
688 0 : if(show_stack)
689 : {
690 0 : auto const trace(libexcept::collect_stack_trace());
691 0 : for(auto const & stack_line : trace)
692 : {
693 0 : SNAP_LOG_ERROR
694 : << "signal_handler(): backtrace="
695 : << stack_line
696 : << SNAP_LOG_SEND;
697 : }
698 : }
699 :
700 0 : char const * n(get_signal_name(sig));
701 0 : std::string signame;
702 0 : if(n == nullptr)
703 : {
704 0 : signame = "UNKNOWN";
705 : }
706 : else
707 : {
708 0 : signame = n;
709 : }
710 :
711 0 : SNAP_LOG_FATAL
712 : << "Fatal signal caught: "
713 : << signame
714 : << SNAP_LOG_SEND;
715 :
716 : // Abort
717 : //
718 0 : std::terminate();
719 : snapdev::NOT_REACHED();
720 : }
721 :
722 :
723 :
724 :
725 6 : } // namespace ed
726 : // vim: ts=4 sw=4 et
|