LCOV - code coverage report
Current view: top level - eventdispatcher - signal_handler.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 2 108 1.9 %
Date: 2021-09-19 09:06:58 Functions: 2 17 11.8 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13