Line data Source code
1 : // Copyright (c) 2012-2021 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 0 : signal_handler::pointer_t signal_handler::create_instance(
310 : signal_mask_t terminal
311 : , signal_mask_t ignored
312 : , id_t callback_id
313 : , int callback_sig
314 : , callback_t callback)
315 : {
316 0 : cppthread::guard g(*cppthread::g_system_mutex);
317 :
318 0 : if(g_signal_handler != nullptr)
319 : {
320 0 : throw std::runtime_error("signal_handler::create_instance() must be called once before signal_handler::get_instance() ever gets called.");
321 : }
322 :
323 0 : pointer_t handler(get_instance());
324 :
325 0 : handler->add_terminal_signals(terminal);
326 0 : handler->add_ignored_signals(ignored);
327 :
328 0 : if(callback_sig != 0)
329 : {
330 0 : handler->add_callback(callback_id, callback_sig, callback);
331 : }
332 :
333 0 : return handler;
334 : }
335 :
336 :
337 : /** \brief Returns the signal handler instance.
338 : *
339 : * This function creates an instance of the signal handler and returns
340 : * the pointer. The very first time, though, you probably want to call
341 : * the create_instance() function so as to automatically initialize the
342 : * class. You can also reprogram your own initialization.
343 : * This function can be called any number of times.
344 : *
345 : * \warning
346 : * If you have threads, make sure to call this function at least once
347 : * before you create a thread since it is not otherwise thread safe.
348 : * Actually, the whole class is not considered thread safe so you should
349 : * create and initialize it.
350 : *
351 : * \return A pointer to the signal handler.
352 : */
353 0 : signal_handler::pointer_t signal_handler::get_instance()
354 : {
355 0 : cppthread::guard g(*cppthread::g_system_mutex);
356 :
357 0 : if(g_signal_handler == nullptr)
358 : {
359 0 : g_signal_handler.reset(new signal_handler());
360 : }
361 0 : return g_signal_handler;
362 : }
363 :
364 :
365 : /** \brief Add a callback to the signal handler.
366 : *
367 : * This function adds a callback to the signal_handler object. Callbacks
368 : * get called whenever the specified \p sig is received.
369 : *
370 : * You can add any number of callbacks per signal.
371 : *
372 : * The \p id parameter is a number you define. It is useful really only
373 : * if you add the same callback multiple times with different identifiers
374 : * and in case you want to be able to call the remove_callback() function.
375 : * You can always use `0` in all other cases.
376 : *
377 : * If you set the \p sig parameter to 0, then it will match all the
378 : * signals received. In other words, that callback will be called whatever
379 : * the received signal is (i.e. _match any_).
380 : *
381 : * \param[in] id The callback identifier.
382 : * \param[in] sig The signal (i.e. SIGPIPE) to assign a callback to.
383 : * \param[in] callback The user callback to call on \p sig signal.
384 : */
385 0 : void signal_handler::add_callback(callback_id_t id, int sig, callback_t callback)
386 : {
387 0 : if(static_cast<std::size_t>(sig) >= sizeof(f_signal_actions) / sizeof(f_signal_actions[0]))
388 : {
389 : throw std::runtime_error(
390 : "signal_handler::add_callback() called with invalid signal number "
391 0 : + std::to_string(sig));
392 : }
393 :
394 0 : if(callback == nullptr)
395 : {
396 0 : throw std::runtime_error("signal_handler::add_callback() called with nullptr as the callback.");
397 : }
398 :
399 0 : cppthread::guard g(f_mutex);
400 :
401 0 : f_callbacks.push_back(signal_callback_t{id, sig, callback});
402 0 : }
403 :
404 :
405 : /** \brief Remove a user callback.
406 : *
407 : * This function searches for the specified callback using its identifier
408 : * and removes it from the list of callbacks of the signal_handler object.
409 : *
410 : * If more than one callback is assigned the same identifier, then all
411 : * those callbacks are removed at once.
412 : *
413 : * \note
414 : * To be able to remove your callback, you must keep a reference to it.
415 : * So if you use std::bind() to call add_callback(), you need to keep
416 : * a reference to that std::bind().
417 : *
418 : * \param[in] id The callback identifier.
419 : */
420 0 : void signal_handler::remove_callback(id_t id)
421 : {
422 0 : cppthread::guard g(f_mutex);
423 :
424 0 : for(auto it(f_callbacks.begin()); it != f_callbacks.end();)
425 : {
426 0 : if(it->f_id == id)
427 : {
428 0 : it = f_callbacks.erase(it);
429 : }
430 : else
431 : {
432 0 : ++it;
433 : }
434 : }
435 0 : }
436 :
437 :
438 : /** \brief Set signals that generate a stack trace.
439 : *
440 : * Whenever a signal happens, this class can automatically log a stack
441 : * trace of location of the event. By default the mask is set to
442 : * DEFAULT_SHOW_STACK.
443 : *
444 : * You may add signals to the list by doing:
445 : *
446 : * \code
447 : * set_show_stack(get_show_stack() | ed::signal_handler::SIGNAL_TRAP);
448 : * \endcode
449 : *
450 : * \param[in] sigs The mask of signals which will generate a stack trace.
451 : */
452 0 : void signal_handler::set_show_stack(signal_mask_t sigs)
453 : {
454 0 : cppthread::guard g(f_mutex);
455 :
456 0 : g_show_stack = sigs;
457 0 : }
458 :
459 :
460 : /** \brief Get list of signals that generate a stack trace.
461 : *
462 : * This function retrieves the current list of signal that request the
463 : * class to generate a stack trace when they happen.
464 : *
465 : * It can be used with the set_show_stack() function in order to add or
466 : * remove some signals from that list.
467 : *
468 : * \return The mask of signals that generate a stack trace.
469 : */
470 0 : signal_handler::signal_mask_t signal_handler::get_show_stack() const
471 : {
472 0 : cppthread::guard g(f_mutex);
473 :
474 0 : return g_show_stack;
475 : }
476 :
477 :
478 : /** \brief Add signals that terminate the process.
479 : *
480 : * Any signal that you consider terminal should be added using this function.
481 : * Whenever that signal is raised by the system, the process_signal() function
482 : * gets called. If the corresponding bit is set in the show stack mask, then
483 : * the function first sends the stack trace to the logs, then it terminates
484 : * the process with a log specifying which signal terminated the process.
485 : *
486 : * \note
487 : * Some signals can't be caught (i.e. SIGKILL). It is useless to add those
488 : * to this list.
489 : *
490 : * \param[in] sigs The mask of signals that are expected to terminate your
491 : * process.
492 : */
493 0 : void signal_handler::add_terminal_signals(signal_mask_t sigs)
494 : {
495 0 : cppthread::guard g(f_mutex);
496 :
497 0 : for(size_t i = 0; i < sizeof(f_signal_actions) / sizeof(f_signal_actions[0]); ++i)
498 : {
499 0 : if((sigs & (1L << i)) != 0 && f_signal_actions[i] == nullptr)
500 : {
501 0 : sigaction_t action = sigaction_t();
502 0 : action.sa_sigaction = signal_handler_func;
503 0 : action.sa_flags = SA_SIGINFO;
504 :
505 0 : f_signal_actions[i] = std::make_shared<sigaction_t>();
506 0 : sigaction(i, &action, f_signal_actions[i].get());
507 : }
508 : }
509 0 : }
510 :
511 :
512 : /** \brief The class allows you to ignore some signals.
513 : *
514 : * This function allows you to add a list of signals you want to ignore.
515 : * For example, it is often that you want to ignore SIGPIPE signals when
516 : * you deal with sockets, otherwise, reading or writing to a closed socket
517 : * generates that signal instead of just returns a -1.
518 : *
519 : * \note
520 : * Trying to ignore signals such as SIGSEGV and SIGBUS is not a good idea.
521 : *
522 : * \param[in] sigs The mask of signals you want to ignore.
523 : */
524 0 : void signal_handler::add_ignored_signals(signal_mask_t sigs)
525 : {
526 0 : cppthread::guard g(f_mutex);
527 :
528 0 : for(size_t i = 0; i < sizeof(f_signal_actions) / sizeof(f_signal_actions[0]); ++i)
529 : {
530 0 : if((sigs & (1L << i)) != 0 && f_signal_actions[i] == nullptr)
531 : {
532 0 : sigaction_t action = sigaction_t();
533 0 : action.sa_handler = SIG_IGN;
534 0 : action.sa_sigaction = signal_handler_func;
535 :
536 0 : f_signal_actions[i] = std::make_shared<sigaction_t>();
537 0 : sigaction(i, &action, f_signal_actions[i].get());
538 : }
539 : }
540 0 : }
541 :
542 :
543 : /** \brief Remove a terminal or ignored signal.
544 : *
545 : * This function removes the callback for the specified signals. The function
546 : * has no effect is you did not first add the signal with one of the
547 : * add_terminal_signals() or add_ignored_signals() functions.
548 : *
549 : * \param[in] sigs The mask of signals to remove from the list of signals
550 : * we manage through the signal handler.
551 : */
552 0 : void signal_handler::remove_signals(signal_mask_t sigs)
553 : {
554 0 : cppthread::guard g(f_mutex);
555 :
556 0 : for(size_t i = 0; i < sizeof(f_signal_actions) / sizeof(f_signal_actions[0]); ++i)
557 : {
558 0 : if((sigs & (1L << i)) != 0 && f_signal_actions[i] != nullptr)
559 : {
560 0 : sigaction(i, f_signal_actions[i].get(), nullptr);
561 0 : f_signal_actions[i].reset();
562 : }
563 : }
564 0 : }
565 :
566 :
567 : /** \brief Remove all the signals.
568 : *
569 : * Remove all the signals at once.
570 : *
571 : * This function is primarily used when the signal_handler is deleted to
572 : * restore the state to normal. It should be the very last thing you want
573 : * to do. You are welcome to call this function at any time, of course,
574 : * with the consequence that none of the signals will now be handled by
575 : * this handler.
576 : *
577 : * This is equivalent to:
578 : *
579 : * \code
580 : * remove_signals(ed::signal_handler::ALL_SIGNALS);
581 : * \endcode
582 : */
583 0 : void signal_handler::remove_all_signals()
584 : {
585 0 : remove_signals(ALL_SIGNALS);
586 0 : }
587 :
588 :
589 : /** \brief Get the name of the signal.
590 : *
591 : * This function converts the signal \p sig to a name one can use to print
592 : * the name in a log or a console.
593 : *
594 : * \param[in] sig The signal number.
595 : *
596 : * \return The name of the specified signal or nullptr if \p sig is invalid.
597 : */
598 0 : char const * signal_handler::get_signal_name(int sig)
599 : {
600 0 : if(static_cast<std::size_t>(sig) >= sizeof(g_signal_names) / sizeof(g_signal_names[0]))
601 : {
602 0 : return nullptr;
603 : }
604 0 : return g_signal_names[sig];
605 : }
606 :
607 :
608 : /** \brief This is out handler.
609 : *
610 : * This function is the handler that gets called whenever a signal is
611 : * raised.
612 : *
613 : * \param[in] sig The signal that generated this handler.
614 : * \param[in] info Information about the signal handler.
615 : * \param[in] context The context from when the interrupt was generated.
616 : */
617 0 : void signal_handler::signal_handler_func(int sig, siginfo_t * info, void * context)
618 : {
619 : // if we are called, g_signal_handler can't be nullptr
620 : //
621 0 : g_signal_handler->process_signal(sig, info, reinterpret_cast<ucontext_t *>(context));
622 0 : }
623 :
624 :
625 : /** \brief Callback to process a signal we just received.
626 : *
627 : * This function is the one called whenever a signal is received by your
628 : * process. It includes the signal number (\p sig), the signal information
629 : * as defined by the kernel (\p info) and the user context when the signal
630 : * happened (\p ucontext).
631 : *
632 : * By default, the function prints out the stack trace if requested for that
633 : * signal and then print a log message about the signal that generated
634 : * this call. Finally, it calls `std::terminate()` to terminate the process.
635 : *
636 : * However, you can add callbacks to capture the signals in your own handler.
637 : * When doing so, your callback can return true, meaning that you handled the
638 : * signal and you do not want the default process to take over. See the
639 : * add_callback() for additional details.
640 : *
641 : * \note
642 : * An `exit(1)` could be very problematic, so would raising an exception in
643 : * a thread at an impromptus moment (especially in a signal handler). So here
644 : * we use `std::terminate()`.
645 : *
646 : * \param[in] sig The signal being processed.
647 : */
648 0 : void signal_handler::process_signal(int sig, siginfo_t * info, ucontext_t * ucontext)
649 : {
650 0 : callback_list_t callbacks;
651 0 : bool show_stack(false);
652 :
653 : // here we lock as little as possible
654 : {
655 0 : cppthread::guard g(f_mutex);
656 0 : callbacks = f_callbacks;
657 0 : show_stack = (g_show_stack & (1UL << sig)) != 0;
658 : }
659 :
660 0 : bool handled(false);
661 0 : for(auto it(callbacks.begin()); it != callbacks.end(); ++it)
662 : {
663 0 : if(it->f_sig == sig)
664 : {
665 0 : if((it->f_callback)(it->f_id, sig, info, ucontext))
666 : {
667 0 : handled = true;
668 : }
669 : }
670 : }
671 0 : if(handled)
672 : {
673 : // user said it was handled, leave it to that...
674 : //
675 0 : return;
676 : }
677 :
678 0 : if(show_stack)
679 : {
680 0 : auto const trace(libexcept::collect_stack_trace());
681 0 : for(auto const & stack_line : trace)
682 : {
683 0 : SNAP_LOG_ERROR
684 0 : << "signal_handler(): backtrace="
685 : << stack_line
686 : << SNAP_LOG_SEND;
687 : }
688 : }
689 :
690 0 : char const * n(get_signal_name(sig));
691 0 : std::string signame;
692 0 : if(n == nullptr)
693 : {
694 0 : signame = "UNKNOWN";
695 : }
696 : else
697 : {
698 0 : signame = n;
699 : }
700 :
701 0 : SNAP_LOG_FATAL
702 0 : << "Fatal signal caught: "
703 : << signame
704 : << SNAP_LOG_SEND;
705 :
706 : // Abort
707 : //
708 0 : std::terminate();
709 : snap::NOT_REACHED();
710 : }
711 :
712 :
713 :
714 :
715 6 : } // namespace ed
716 : // vim: ts=4 sw=4 et
|