Browse Source

[3405] Install signal handlers in DHCPv4 server.

Marcin Siodelski 11 years ago
parent
commit
db9b5842fc
3 changed files with 100 additions and 20 deletions
  1. 8 0
      src/bin/dhcp4/dhcp4_messages.mes
  2. 12 0
      src/bin/dhcp4/dhcp4_srv.cc
  3. 80 20
      src/bin/dhcp4/kea_controller.cc

+ 8 - 0
src/bin/dhcp4/dhcp4_messages.mes

@@ -97,6 +97,14 @@ This error message is logged when the attempt to compute DHCID for a specified
 lease has failed. The lease details and reason for failure is logged in the
 lease has failed. The lease details and reason for failure is logged in the
 message.
 message.
 
 
+% DHCP4_DYNAMIC_RECONFIGURATION initate server reconfiguration using file: %1, after receiving SIGHUP signal
+This is the info message logged when the DHCPv4 server starts reconfiguration
+as a result of receiving SIGHUP signal.
+
+% DHCP4_DYNAMIC_RECONFIGURATION_FAIL dynamic server reconfiguration failed with file: %1
+This is an error message logged when the dynamic reconfiguration of the
+DHCP server failed.
+
 % DHCP4_EMPTY_HOSTNAME received empty hostname from the client, skipping processing of this option
 % DHCP4_EMPTY_HOSTNAME received empty hostname from the client, skipping processing of this option
 This debug message is issued when the server received an empty Hostname option
 This debug message is issued when the server received an empty Hostname option
 from a client. Server does not process empty Hostname options and therefore
 from a client. Server does not process empty Hostname options and therefore

+ 12 - 0
src/bin/dhcp4/dhcp4_srv.cc

@@ -175,6 +175,18 @@ Dhcpv4Srv::run() {
             LOG_ERROR(dhcp4_logger, DHCP4_PACKET_RECEIVE_FAIL).arg(e.what());
             LOG_ERROR(dhcp4_logger, DHCP4_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()
         // Timeout may be reached or signal received, which breaks select()
         // with no reception ocurred
         // with no reception ocurred
         if (!query) {
         if (!query) {

+ 80 - 20
src/bin/dhcp4/kea_controller.cc

@@ -25,11 +25,18 @@ using namespace isc::asiolink;
 using namespace isc::dhcp;
 using namespace isc::dhcp;
 using namespace std;
 using namespace std;
 
 
-namespace isc {
+namespace {
-namespace dhcp {
+
-
+/// @brief Configure DHCPv4 server using the configuration file specified.
-void
+///
-ControlledDhcpv4Srv::init(const std::string& file_name) {
+/// This function is used to both configure the DHCP server on its startup
+/// and dynamically reconfigure the server when SIGHUP signal is received.
+///
+/// It fetches DHCPv6 server's configuration from the 'Dhcp4' section of
+/// the JSON configuration file.
+///
+/// @param file_name Configuration file location.
+void configure(const std::string& file_name) {
     // This is a configuration backend implementation that reads the
     // This is a configuration backend implementation that reads the
     // configuration from a JSON file.
     // configuration from a JSON file.
 
 
@@ -41,8 +48,8 @@ ControlledDhcpv4Srv::init(const std::string& file_name) {
     try {
     try {
         if (file_name.empty()) {
         if (file_name.empty()) {
             // Basic sanity check: file name must not be empty.
             // Basic sanity check: file name must not be empty.
-            isc_throw(BadValue, "JSON configuration file not specified. Please "
+            isc_throw(isc::BadValue, "JSON configuration file not specified."
-                      "use -c command line option.");
+                      " Please use -c command line option.");
         }
         }
 
 
         // Read contents of the file and parse it as JSON
         // Read contents of the file and parse it as JSON
@@ -51,8 +58,8 @@ ControlledDhcpv4Srv::init(const std::string& file_name) {
         if (!json) {
         if (!json) {
             LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
             LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
                 .arg("Config file " + file_name + " missing or empty.");
                 .arg("Config file " + file_name + " missing or empty.");
-            isc_throw(BadValue, "Unable to process JSON configuration file:"
+            isc_throw(isc::BadValue, "Unable to process JSON configuration"
-                      + file_name);
+                      " file: " << file_name);
         }
         }
 
 
         // Get Dhcp4 component from the config
         // Get Dhcp4 component from the config
@@ -60,29 +67,30 @@ ControlledDhcpv4Srv::init(const std::string& file_name) {
 
 
         if (!dhcp4) {
         if (!dhcp4) {
             LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
             LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
-                .arg("Config file " + file_name + " does not include 'Dhcp4' entry.");
+                .arg("Config file " + file_name + " does not include 'Dhcp4'"
-            isc_throw(BadValue, "Unable to process JSON configuration file:"
+                     " entry.");
-                      + file_name);
+            isc_throw(isc::BadValue, "Unable to process JSON configuration"
+                      " file: " << file_name);
         }
         }
 
 
         // Use parsed JSON structures to configure the server
         // Use parsed JSON structures to configure the server
-        result = processCommand("config-reload", dhcp4);
+        result = ControlledDhcpv4Srv::processCommand("config-reload", dhcp4);
 
 
     }  catch (const std::exception& ex) {
     }  catch (const std::exception& ex) {
         LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
         LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(ex.what());
-        isc_throw(BadValue, "Unable to process JSON configuration file:"
+        isc_throw(isc::BadValue, "Unable to process JSON configuration file: "
-                  + file_name);
+                  << file_name);
     }
     }
 
 
     if (!result) {
     if (!result) {
         // Undetermined status of the configuration. This should never happen,
         // Undetermined status of the configuration. This should never happen,
-        // but as the configureDhcp4Server returns a pointer, it is theoretically
+        // but as the configureDhcp4Server returns a pointer, it is
-        // possible that it will return NULL.
+        // theoretically possible that it will return NULL.
         LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
         LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
             .arg("Configuration failed: Undefined result of processCommand("
             .arg("Configuration failed: Undefined result of processCommand("
                  "config-reload, " + file_name + ")");
                  "config-reload, " + file_name + ")");
-        isc_throw(BadValue, "Configuration failed: Undefined result of "
+        isc_throw(isc::BadValue, "Configuration failed: Undefined result of "
-                  "processCommand('config-reload', " + file_name + ")");
+                  "processCommand('config-reload', " << file_name << ")");
     }
     }
 
 
     // Now check is the returned result is successful (rcode=0) or not
     // Now check is the returned result is successful (rcode=0) or not
@@ -95,12 +103,64 @@ ControlledDhcpv4Srv::init(const std::string& file_name) {
             reason = string(" (") + comment->stringValue() + string(")");
             reason = string(" (") + comment->stringValue() + string(")");
         }
         }
         LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(reason);
         LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL).arg(reason);
-        isc_throw(BadValue, "Failed to apply configuration:" << reason);
+        isc_throw(isc::BadValue, "Failed to apply configuration: " << reason);
     }
     }
+}
+
+/// @brief Signals handler for DHCPv4 server.
+///
+/// This signal handler handles the following signals received by the DHCPv4
+/// server process:
+/// - SIGHUP - triggers server's dynamic reconfiguration.
+/// - SIGTERM - triggers server's shut down.
+/// - SIGINT - triggers server's shut down.
+///
+/// @param signo Signal number received.
+void signalHandler(int signo) {
+    // SIGHUP signals a request to reconfigure the server.
+    if (signo == SIGHUP) {
+        // Get configuration file name.
+        std::string file = ControlledDhcpv4Srv::getInstance()->getConfigFile();
+        try {
+            LOG_INFO(dhcp4_logger, DHCP4_DYNAMIC_RECONFIGURATION).arg(file);
+            configure(file);
+        } catch (const std::exception& ex) {
+            // Log the unsuccessful reconfiguration. The reason for failure
+            // should be already logged. Don't rethrow an exception so as
+            // the server keeps working.
+            LOG_ERROR(dhcp4_logger, DHCP4_DYNAMIC_RECONFIGURATION_FAIL)
+                .arg(file);
+        }
+    } else if ((signo == SIGTERM) || (signo == SIGINT)) {
+        isc::data::ElementPtr params(new isc::data::MapElement());
+        ControlledDhcpv4Srv::processCommand("shutdown", params);
+    }
+}
+
+}
+
+namespace isc {
+namespace dhcp {
+
+void
+ControlledDhcpv4Srv::init(const std::string& file_name) {
+    // Call parent class's init to initialize file name.
+    Daemon::init(file_name);
+
+    // Configure the server using JSON file.
+    configure(file_name);
 
 
     // We don't need to call openActiveSockets() or startD2() as these
     // We don't need to call openActiveSockets() or startD2() as these
     // methods are called in processConfig() which is called by
     // methods are called in processConfig() which is called by
     // processCommand("reload-config", ...)
     // processCommand("reload-config", ...)
+
+    // 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_set_.reset(new isc::util::io::SignalSet(SIGINT, SIGHUP, SIGTERM));
+    // Set the pointer to the handler function.
+    signal_handler_ = signalHandler;
+
 }
 }
 
 
 void ControlledDhcpv4Srv::cleanup() {
 void ControlledDhcpv4Srv::cleanup() {