LCOV - code coverage report
Current view: top level - eventdispatcher - signal.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 38 84 45.2 %
Date: 2021-09-19 09:06:58 Functions: 8 12 66.7 %
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 class.
      22             :  *
      23             :  * The Signal class listens for Unix signals to happen. This wakes us
      24             :  * up when the signal happens.
      25             :  */
      26             : 
      27             : // self
      28             : //
      29             : #include    "eventdispatcher/signal.h"
      30             : 
      31             : #include    "eventdispatcher/exception.h"
      32             : 
      33             : 
      34             : // snaplogger lib
      35             : //
      36             : #include    <snaplogger/message.h>
      37             : 
      38             : 
      39             : // cppthread lib
      40             : //
      41             : #include    <cppthread/thread.h>
      42             : 
      43             : 
      44             : // C++ lib
      45             : //
      46             : #include    <iostream>
      47             : 
      48             : 
      49             : // C lib
      50             : //
      51             : #include    <string.h>
      52             : #include    <signal.h>
      53             : 
      54             : 
      55             : // last include
      56             : //
      57             : #include    <snapdev/poison.h>
      58             : 
      59             : 
      60             : 
      61             : 
      62             : namespace ed
      63             : {
      64             : 
      65             : 
      66             : namespace
      67             : {
      68             : 
      69             : 
      70             : 
      71             : /** \brief The array of signals handled by signal objects.
      72             :  *
      73             :  * This map holds a list of signal handlers. You cannot register
      74             :  * the same signal more than once so this map is used to make
      75             :  * sure that each signal is unique.
      76             :  *
      77             :  * \todo
      78             :  * We may actually want to use a sigset_t object and just set
      79             :  * bits and remove 
      80             :  *
      81             :  * \note
      82             :  * The pointer to the signal object is a bare pointer
      83             :  * for in part because we cannot use a smart pointer in
      84             :  * a constructor where we add the signal to this map. Also
      85             :  * at this time that pointer does not get used so it could
      86             :  * as well have been a boolean.
      87             :  *
      88             :  * \bug
      89             :  * Having a global list this means signal objects can't safely
      90             :  * be created before main() gets called.
      91             :  */
      92             : sigset_t                            g_signal_handlers = sigset_t();
      93             : 
      94             : 
      95             : 
      96             : }
      97             : // no name namespace
      98             : 
      99             : 
     100             : 
     101             : 
     102             : /** \brief Initializes the signal object.
     103             :  *
     104             :  * This function initializes the signal object with the specified
     105             :  * \p posix_signal which represents a POSIX signal such as SIGHUP,
     106             :  * SIGTERM, SIGUSR1, SIGUSR2, etc.
     107             :  *
     108             :  * The signal automatically gets masked out. This allows us to
     109             :  * unmask the signal only when we are ready to call ppoll() and
     110             :  * thus not have the signal break any of our normal user code.
     111             :  *
     112             :  * The ppoll() function unblocks all the signals that you listen
     113             :  * to (i.e. for each signal object you created.) The run()
     114             :  * loop ends up calling your process_signal() callback function.
     115             :  *
     116             :  * Note that the signal callback is called from the normal user
     117             :  * environment and not directly from the POSIX signal handler.
     118             :  * This means you can call any function from your callback.
     119             :  *
     120             :  * \note
     121             :  * IMPORTANT: Remember that POSIX signals stop your code at a 'breakable'
     122             :  * point which in many circumstances can create many problems unless
     123             :  * you make sure to mask signals while doing work. For example, you
     124             :  * could end up with a read() returning an error when the file you
     125             :  * are reading has absolutely no error but a dude decided to signal
     126             :  * you with a 'kill -HUP 123'...
     127             :  *
     128             :  * \code
     129             :  *      {
     130             :  *          // use an RAII masking mechanism
     131             :  *          mask_posix_signal mask();
     132             :  *
     133             :  *          // do your work (i.e. read/write/etc.)
     134             :  *          ...
     135             :  *      }
     136             :  * \endcode
     137             :  *
     138             :  * \par
     139             :  * The best way in our processes will be to block all signals except
     140             :  * while poll() is called (using ppoll() for the feat.)
     141             :  *
     142             :  * \note
     143             :  * By default the constructor masks the specified \p posix_signal and
     144             :  * it does not restore the signal on destruction. If you want the
     145             :  * signal to be unmasked on destruction (say to restore the default
     146             :  * functioning of the SIGINT signal,) then make sure to call the
     147             :  * unblock_signal() function right after you create your connection.
     148             :  *
     149             :  * \warning
     150             :  * The signal gets masked by this constructor. If you want to make
     151             :  * sure that most of your code does not get affected by said signal,
     152             :  * make sure to create your signal object early on or mask those
     153             :  * signals beforehand. Otherwise the signal could happen before it
     154             :  * gets masked. Initialization of your process may not require
     155             :  * protection anyway.
     156             :  *
     157             :  * \bug
     158             :  * You should not use signal() and setup a handler for the same signal.
     159             :  * It will not play nice to have both types of signal handlers. That
     160             :  * being said, with my current testing (as of Ubuntu 16.04), it seems
     161             :  * to work just fine..
     162             :  *
     163             :  * \bug
     164             :  * At the moment you can't create a signal() object if you already
     165             :  * started a thread. This is because the thread could end up being
     166             :  * the one accepting the signal and when that happens, it would most
     167             :  * certainly crash (i.e. the `sigprocmask()` only protects the current
     168             :  * thread and its spawns, not existing threads). Since you may not be
     169             :  * in control of other threads, this is really not easy to handle. One
     170             :  * possibility, though, would be to offer a "low level" function which
     171             :  * you can call near the beginning of your process (i.e. in main())
     172             :  * and call that function to "pre-block" the signals you're interested
     173             :  * in.
     174             :  *
     175             :  * \exception event_dispatcher_initialization_error
     176             :  * Create multiple signal() with the same posix_signal parameter
     177             :  * is not supported and this exception is raised whenever you attempt
     178             :  * to do that. Remember that you can have at most one communicator
     179             :  * object (hence the singleton.)
     180             :  *
     181             :  * \exception event_dispatcher_runtime_error
     182             :  * The signalfd() function is expected to create a "socket" (file
     183             :  * descriptor) listening for incoming signals. If it fails, this
     184             :  * exception is raised (which is very similar to other socket
     185             :  * based connections which throw whenever a connection cannot
     186             :  * be achieved.)
     187             :  *
     188             :  * \param[in] posix_signal  The signal to be managed by this signal.
     189             :  */
     190           1 : signal::signal(int posix_signal)
     191           1 :     : f_signal(posix_signal)
     192             : {
     193           1 :     int const r(sigismember(&g_signal_handlers, f_signal));
     194           1 :     if(r != 0)
     195             :     {
     196           0 :         if(r == 1)
     197             :         {
     198             :             // this could be fixed, but probably not worth the trouble...
     199             :             //
     200           0 :             throw event_dispatcher_initialization_error("the same signal cannot be created more than once in your entire process.");
     201             :         }
     202             : 
     203             :         // f_signal is not considered valid by this OS
     204             :         //
     205           0 :         throw event_dispatcher_initialization_error("posix_signal (f_signal) is not a valid/recognized signal number.");
     206             :     }
     207             : 
     208           2 :     cppthread::process_ids_t const pids(cppthread::get_thread_ids());
     209           1 :     if(pids.size() != 1)
     210             :     {
     211           0 :         std::string const msg("an ed::signal object must be created before any threads or the signals will kill your process.");
     212           0 :         std::cerr << msg << std::endl;
     213           0 :         throw event_dispatcher_initialization_error(msg);
     214             :     }
     215             : 
     216             :     // create a mask for that signal
     217             :     //
     218           1 :     sigset_t set;
     219           1 :     sigemptyset(&set);
     220           1 :     sigaddset(&set, f_signal); // ignore returned error, we already know f_signal is valid
     221             : 
     222             :     // first we block the signal
     223             :     //
     224           1 :     if(sigprocmask(SIG_BLOCK, &set, nullptr) != 0)
     225             :     {
     226           0 :         throw event_dispatcher_runtime_error("sigprocmask() failed to block signal.");
     227             :     }
     228             : 
     229             :     // second we create a "socket" for the signal (really it is a file
     230             :     // descriptor manager by the kernel)
     231             :     //
     232           1 :     f_socket = signalfd(-1, &set, SFD_NONBLOCK | SFD_CLOEXEC);
     233           1 :     if(f_socket == -1)
     234             :     {
     235           0 :         sigprocmask(SIG_UNBLOCK, &set, nullptr);
     236             : 
     237           0 :         int const e(errno);
     238           0 :         std::string err("signalfd() failed to create a signal listener for signal ");
     239           0 :         err += std::to_string(f_signal);
     240           0 :         err += " (errno: ";
     241           0 :         err += std::to_string(e);
     242           0 :         err += " -- ";
     243           0 :         err += strerror(e);
     244           0 :         err += ").";
     245           0 :         SNAP_LOG_ERROR << err << SNAP_LOG_SEND;
     246           0 :         throw event_dispatcher_runtime_error(err);
     247             :     }
     248             : 
     249             :     // mark this signal as in use
     250             :     //
     251           1 :     sigaddset(&g_signal_handlers, f_signal); // ignore error, we already know f_signal is valid
     252           1 : }
     253             : 
     254             : 
     255             : /** \brief Restore the signal as it was before you created a signal.
     256             :  *
     257             :  * The destructor is expected to restore the signal to what it was
     258             :  * before you create this signal. Of course, if you created
     259             :  * other signal handlers in between, it will not work right since
     260             :  * this function will destroy your handler pointer.
     261             :  *
     262             :  * To do it right, it has to be done in order (i.e. set handler 1, set
     263             :  * handler 2, set handler 3, remove handler 3, remove handler 2, remove
     264             :  * handler 1.) We do not guarantee anything at this level!
     265             :  */
     266           2 : signal::~signal()
     267             : {
     268           1 :     close();
     269           1 : }
     270             : 
     271             : 
     272             : /** \brief Tell that this connection is listening on a Unix signal.
     273             :  *
     274             :  * The signal implements the signal listening feature. We use
     275             :  * a simple flag in the virtual table to avoid a more expansive
     276             :  * dynamic_cast<>() is a loop that goes over all the connections
     277             :  * you have defined.
     278             :  *
     279             :  * \return The base implementation returns false.
     280             :  */
     281          24 : bool signal::is_signal() const
     282             : {
     283          24 :     return true;
     284             : }
     285             : 
     286             : 
     287             : /** \brief Retrieve the "socket" of the signal object.
     288             :  *
     289             :  * Signal objects have a socket (file descriptor) assigned to them
     290             :  * using the signalfd() function.
     291             :  *
     292             :  * \note
     293             :  * You should not override this function since there is no other
     294             :  * value it can return.
     295             :  *
     296             :  * \return The signal socket to listen on with poll().
     297             :  */
     298          40 : int signal::get_socket() const
     299             : {
     300          40 :     return f_socket;
     301             : }
     302             : 
     303             : 
     304             : /** \brief Retrieve the PID of the child process that just emitted SIGCHLD.
     305             :  *
     306             :  * This function returns the process identifier (pid_t) of the child that
     307             :  * just sent us a SIGCHLD Unix signal.
     308             :  *
     309             :  * \exception event_dispatcher_runtime_error
     310             :  * This exception is raised if the function gets called before any signal
     311             :  * ever occurred.
     312             :  *
     313             :  * \return The process identifier (pid_t) of the child that died.
     314             :  */
     315           0 : pid_t signal::get_child_pid() const
     316             : {
     317           0 :     if(f_signal_info.ssi_signo == 0)
     318             :     {
     319           0 :         throw event_dispatcher_runtime_error("signal::get_child_pid() called before any signal ever occurred.");
     320             :     }
     321             : 
     322           0 :     return f_signal_info.ssi_pid;
     323             : }
     324             : 
     325             : 
     326             : /** \brief Get a copy of the current signal.
     327             :  *
     328             :  * Whenever we read a signal, the data is saved in the internal f_signal_info
     329             :  * structure. You can access that structure and its content using this
     330             :  * function.
     331             :  *
     332             :  * The structure is return as read-only. You should not modify it. If you
     333             :  * need to do so, make a copy.
     334             :  *
     335             :  * The structure remains valid until your process_signal() function returns.
     336             :  * If you need the info after, make sure to make a copy.
     337             :  *
     338             :  * \return A direct pointer to the signal info in the object.
     339             :  */
     340           0 : signalfd_siginfo const * signal::get_signal_info() const
     341             : {
     342           0 :     return &f_signal_info;
     343             : }
     344             : 
     345             : 
     346             : /** \brief Processes this signal.
     347             :  *
     348             :  * This function reads the signal "socket" for all the signal received
     349             :  * so far.
     350             :  *
     351             :  * For each instance found in the signal queue, the process_signal() gets
     352             :  * called.
     353             :  */
     354          14 : void signal::process()
     355             : {
     356             :     // loop any number of times as required
     357             :     // (or can we receive a maximum of 1 such signal at a time?)
     358             :     //
     359          21 :     while(f_socket != -1)
     360             :     {
     361          14 :         int const r(read(f_socket, &f_signal_info, sizeof(f_signal_info)));
     362          14 :         if(r == sizeof(f_signal_info))
     363             :         {
     364           7 :             process_signal();
     365             :         }
     366             :         else
     367             :         {
     368           7 :             if(r == -1)
     369             :             {
     370             :                 // if EAGAIN then we are done as expected, any other error
     371             :                 // is logged
     372             :                 //
     373           7 :                 if(errno != EAGAIN)
     374             :                 {
     375           0 :                     int const e(errno);
     376           0 :                     SNAP_LOG_ERROR
     377           0 :                         << "an error occurred while reading from the signalfd() file descriptor. (errno: "
     378             :                         << e
     379             :                         << " -- "
     380           0 :                         << strerror(e)
     381             :                         << ")."
     382             :                         << SNAP_LOG_SEND;
     383             :                 }
     384             :             }
     385             :             else
     386             :             {
     387             :                 // what to do? what to do?
     388           0 :                 SNAP_LOG_ERROR
     389           0 :                     << "reading from the signalfd() file descriptor did not return the expected size. (got "
     390             :                     << r
     391           0 :                     << ", expected "
     392           0 :                     << sizeof(f_signal_info)
     393             :                     << ")"
     394             :                     << SNAP_LOG_SEND;
     395             :             }
     396           7 :             break;
     397             :         }
     398             :     }
     399           7 : }
     400             : 
     401             : 
     402             : /** \brief Close the signal file descriptor.
     403             :  *
     404             :  * This function closes the file descriptor and, if you called the
     405             :  * unblock_signal_on_destruction() function, it also restores the
     406             :  * signal (unblocks it.)
     407             :  *
     408             :  * After this call, the connection is pretty much useless (although
     409             :  * you could still use it as a timer.) You cannot reopen the signal
     410             :  * file descriptor once closed. Instead, you have to create a new
     411             :  * connection.
     412             :  */
     413           1 : void signal::close()
     414             : {
     415           1 :     if(f_socket != -1)
     416             :     {
     417           1 :         ::close(f_socket);
     418           1 :         f_socket = -1;
     419             : 
     420           1 :         sigdelset(&g_signal_handlers, f_signal);     // ignore error, we already know f_signal is valid
     421             : 
     422           1 :         if(f_unblock)
     423             :         {
     424             :             // also unblock the signal
     425             :             //
     426           0 :             sigset_t set;
     427           0 :             sigemptyset(&set);
     428           0 :             sigaddset(&set, f_signal); // ignore error, we already know f_signal is valid
     429           0 :             if(sigprocmask(SIG_UNBLOCK, &set, nullptr) != 0)
     430             :             {
     431             :                 // we cannot throw in a destructor and in most cases this
     432             :                 // happens in the destructor...
     433             :                 //throw event_dispatcher_runtime_error("sigprocmask() failed to block signal.");
     434             : 
     435           0 :                 int const e(errno);
     436           0 :                 SNAP_LOG_FATAL
     437           0 :                     << "an error occurred while unblocking signal "
     438             :                     << f_signal
     439           0 :                     << " with sigprocmask(). (errno: "
     440             :                     << e
     441             :                     << " -- "
     442           0 :                     << strerror(e)
     443             :                     << SNAP_LOG_SEND;
     444           0 :                 std::cerr << "signal::close(): sigprocmask() failed to unblock signal." << std::endl;
     445             : 
     446           0 :                 std::terminate();
     447             :             }
     448             :         }
     449             :     }
     450           1 : }
     451             : 
     452             : 
     453             : /** \brief Unmask a signal that was part of a connection.
     454             :  *
     455             :  * If you remove a signal connection, you may want to restore
     456             :  * the mask functionality. By default the signal gets masked but
     457             :  * it does not get unmasked.
     458             :  *
     459             :  * By calling this function just after creation, the signal gets restored
     460             :  * (unblocked) whenever the signal object gets destroyed.
     461             :  */
     462           0 : void signal::unblock_signal_on_destruction()
     463             : {
     464           0 :     f_unblock = true;
     465           0 : }
     466             : 
     467             : 
     468             : 
     469           6 : } // namespace ed
     470             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13