Parcourir la source

[3405] Use SignalSet class to handle signals in DHCPv6 server.

Marcin Siodelski il y a 11 ans
Parent
commit
e8f1f73d0b

+ 1 - 0
src/bin/dhcp6/Makefile.am

@@ -77,6 +77,7 @@ b10_dhcp6_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+b10_dhcp6_LDADD += $(top_builddir)/src/lib/util/io/libkea-util-io.la
 b10_dhcp6_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 
 b10_dhcp6dir = $(pkgdatadir)

+ 12 - 0
src/bin/dhcp6/dhcp6_srv.cc

@@ -255,6 +255,18 @@ bool Dhcpv6Srv::run() {
             LOG_ERROR(dhcp6_logger, DHCP6_PACKET_RECEIVE_FAIL).arg(e.what());
         }
 
+        // Handle next signal received by the process. It must be called after
+        // an attempt to receive a packet to properly handle server shut down.
+        // The SIGTERM or SIGINT will be received prior to, or during execution
+        // of select() (select is invoked by recivePacket()). When that happens,
+        // select will be interrupted. The signal handler will be invoked
+        // immediately after select(). The handler will set the shutdown flag
+        // and cause the process to terminate before the next select() function
+        // is called. If the function was called before receivePacket the
+        // process could wait up to the duration of timeout of select() to
+        // terminate.
+        handleSignal();
+
         // Timeout may be reached or signal received, which breaks select()
         // with no packet received
         if (!query) {

+ 3 - 3
src/bin/dhcp6/kea_controller.cc

@@ -160,9 +160,9 @@ ControlledDhcpv6Srv::init(const std::string& file_name) {
     // Set signal handlers. When the SIGHUP is received by the process
     // the server reconfiguration will be triggered. When SIGTERM or
     // SIGINT will be received, the server will start shutting down.
-    signal(SIGHUP, signalHandler);
-    signal(SIGTERM, signalHandler);
-    signal(SIGINT, signalHandler);
+    signal_set_.reset(new isc::util::io::SignalSet(SIGINT, SIGHUP, SIGTERM));
+    // Set the pointer to the handler function.
+    signal_handler_ = signalHandler;
 }
 
 void ControlledDhcpv6Srv::cleanup() {

+ 1 - 0
src/bin/dhcp6/tests/Makefile.am

@@ -135,6 +135,7 @@ dhcp6_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
 dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+dhcp6_unittests_LDADD += $(top_builddir)/src/lib/util/io/libkea-util-io.la
 endif
 
 noinst_PROGRAMS = $(TESTS)

+ 1 - 0
src/lib/dhcpsrv/Makefile.am

@@ -83,6 +83,7 @@ libkea_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.
 libkea_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 libkea_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/log/libkea-log.la
 libkea_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/util/libkea-util.la
+libkea_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/util/io/libkea-util-io.la
 libkea_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/cc/libkea-cc.la
 libkea_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/hooks/libkea-hooks.la
 

+ 11 - 2
src/lib/dhcpsrv/daemon.cc

@@ -15,6 +15,7 @@
 #include <config.h>
 #include <dhcpsrv/daemon.h>
 #include <exceptions/exceptions.h>
+#include <boost/bind.hpp>
 #include <errno.h>
 
 /// @brief provides default implementation for basic daemon operations
@@ -27,7 +28,11 @@ namespace dhcp {
 // This is an initial config file location.
 std::string Daemon::config_file_ = "";
 
-Daemon::Daemon() {
+Daemon::Daemon()
+    : signal_set_(), signal_handler_() {
+}
+
+Daemon::~Daemon() {
 }
 
 void Daemon::init(const std::string& config_file) {
@@ -42,8 +47,12 @@ void Daemon::shutdown() {
 
 }
 
-Daemon::~Daemon() {
+void Daemon::handleSignal() {
+    if (signal_set_ && signal_handler_) {
+        signal_set_->handleNext(boost::bind(signal_handler_, _1));
+    }
 }
 
+
 };
 };

+ 43 - 8
src/lib/dhcpsrv/daemon.h

@@ -13,8 +13,9 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <config.h>
-#include <string>
+#include <util/io/signal_set.h>
 #include <boost/noncopyable.hpp>
+#include <string>
 
 
 namespace isc {
@@ -44,6 +45,11 @@ namespace dhcp {
 /// By default, the configuration file location is empty and its actual value
 /// is assigned to the static object in @c Daemon::init function.
 ///
+/// Classes derived from @c Daemon may install custom signal handlers using
+/// @c isc::util::io::SignalSet class. This base class provides a declaration
+/// of the @c SignalSet object that should be initialized in the derived
+/// classes to install the custom exception handlers.
+///
 /// @note Only one instance of this class is instantiated as it encompasses
 ///       the whole operation of the server.  Nothing, however, enforces the
 ///       singleton status of the object.
@@ -52,9 +58,16 @@ class Daemon : public boost::noncopyable {
 public:
     /// @brief Default constructor
     ///
-    /// Currently it does nothing.
+    /// Initializes the object installing custom signal handlers for the
+    /// process to NULL.
     Daemon();
 
+    /// @brief Desctructor
+    ///
+    /// Having virtual destructor ensures that all derived classes will have
+    /// virtual destructor as well.
+    virtual ~Daemon();
+
     /// @brief Initializes the server.
     ///
     /// Depending on the configuration backend, it establishes msgq session,
@@ -87,12 +100,6 @@ public:
     /// @brief Initiates shutdown procedure for the whole DHCPv6 server.
     virtual void shutdown();
 
-    /// @brief Desctructor
-    ///
-    /// Having virtual destructor ensures that all derived classes will have
-    /// virtual destructor as well.
-    virtual ~Daemon();
-
     /// @brief Returns config file name.
     static std::string getConfigFile() {
         return (config_file_);
@@ -107,6 +114,34 @@ public:
     /// @param verbose verbose mode (true usually enables DEBUG messages)
     static void loggerInit(const char* log_name, bool verbose);
 
+protected:
+
+    /// @brief Invokes handler for the next received signal.
+    ///
+    /// This function provides a default implementation for the function
+    /// handling next signal received by the process. It checks if a pointer
+    /// to @c isc::util::io::SignalSet object and the signal handler function
+    /// have been set. If they have been set, the signal handler is invoked for
+    /// the the next signal registered in the @c SignalSet object.
+    ///
+    /// This function should be received in the main loop of the process.
+    virtual void handleSignal();
+
+    /// @brief A pointer to the object installing custom signal handlers.
+    ///
+    /// This pointer needs to be initialized to point to the @c SignalSet
+    /// object in the derived classes which need to handle signals received
+    /// by the process.
+    isc::util::io::SignalSetPtr signal_set_;
+
+    /// @brief Pointer to the common signal handler invoked by the handleSignal
+    /// function.
+    ///
+    /// This pointer needs to be initialized to point to the signal handler
+    /// function for signals being handled by the process. If signal handler
+    /// it not initialized, the signals will not be handled.
+    isc::util::io::SignalHandler signal_handler_;
+
 private:
 
     /// @brief Config file name or empty if config file not used.

+ 5 - 0
src/lib/util/io/signal_set.h

@@ -18,6 +18,7 @@
 #include <exceptions/exceptions.h>
 #include <boost/function.hpp>
 #include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
 #include <set>
 #include <signal.h>
 
@@ -31,6 +32,10 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+class SignalSet;
+
+typedef boost::shared_ptr<SignalSet> SignalSetPtr;
+
 typedef boost::function<void(int signum)> SignalHandler;
 
 class SignalSet : public boost::noncopyable {

+ 11 - 8
src/lib/util/io/tests/signal_set_unittest.cc

@@ -26,9 +26,9 @@ using namespace isc::util::io;
 class SignalSetTest : public ::testing::Test {
 public:
 
-    SignalSetTest()
-        : handler_calls_(0),
-          signum_ (-1) {
+    SignalSetTest() {
+        handler_calls_ = 0;
+        signum_ = -1;
     }
 
     ~SignalSetTest() {
@@ -38,21 +38,23 @@ public:
     }
 
     void handleNext() {
-        signal_set_->handleNext(boost::bind(&SignalSetTest::testHandler,
-                                            this, _1));
+        signal_set_->handleNext(boost::bind(&SignalSetTest::testHandler, _1));
     }
 
-    void testHandler(int signum) {
+    static void testHandler(int signum) {
         signum_ = signum;
         ++handler_calls_;
     }
 
-    int handler_calls_;
-    int signum_;
+    static int handler_calls_;
+    static int signum_;
     boost::shared_ptr<SignalSet> signal_set_;
 
 };
 
+int SignalSetTest::handler_calls_ = 0;
+int SignalSetTest::signum_ = -1;
+
 TEST_F(SignalSetTest, twoSignals) {
     // Register handlers for two signals.
     signal_set_.reset(new SignalSet(SIGHUP, SIGINT));
@@ -90,4 +92,5 @@ TEST_F(SignalSetTest, twoSignals) {
     EXPECT_NO_THROW(signal_set_->remove(SIGINT));
 }
 
+
 } // end of anonymous namespace