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

Generated by: LCOV version 1.13