LCOV - code coverage report
Current view: top level - eventdispatcher - signal.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 79 1.3 %
Date: 2019-08-10 01:48:51 Functions: 2 11 18.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12