kea_controller.cc 7.39 KB
Newer Older
1
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <config.h>

#include <asiolink/asiolink.h>
#include <dhcpsrv/cfgmgr.h>
19
#include <dhcpsrv/parsers/dhcp_config_parser.h>
20 21 22 23 24
#include <dhcp6/json_config_parser.h>
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
#include <exceptions/exceptions.h>

25
#include <signal.h>
26

27 28 29 30 31 32
#include <string>

using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace std;

33 34 35 36 37 38 39 40 41 42 43 44
namespace {

/// @brief Configure DHCPv6 server using the configuration file specified.
///
/// 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 'Dhcp6' section of
/// the JSON configuration file.
///
/// @param file_name Configuration file location.
void configure(const std::string& file_name) {
45 46 47
    // This is a configuration backend implementation that reads the
    // configuration from a JSON file.

48 49 50 51 52
    // We are starting the configuration process so we should remove any
    // staging configuration that has been created during previous
    // configuration attempts.
    CfgMgr::instance().rollback();

53
    isc::data::ConstElementPtr json;
54
    isc::data::ConstElementPtr dhcp6;
55
    isc::data::ConstElementPtr logger;
56 57 58 59
    isc::data::ConstElementPtr result;

    // Basic sanity check: file name must not be empty.
    try {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
60 61
        if (file_name.empty()) {
            // Basic sanity check: file name must not be empty.
62
            isc_throw(isc::BadValue, "JSON configuration file not specified. Please "
Tomek Mrugalski's avatar
Tomek Mrugalski committed
63 64 65
                      "use -c command line option.");
        }

66
        // Read contents of the file and parse it as JSON
Tomek Mrugalski's avatar
Tomek Mrugalski committed
67
        json = isc::data::Element::fromJSONFile(file_name, true);
68
        if (!json) {
69
            isc_throw(isc::BadValue, "no configuration found");
70 71
        }

72 73 74 75 76 77 78 79 80 81 82
        // Let's do sanity check before we call json->get() which
        // works only for map.
        if (json->getType() != isc::data::Element::map) {
            isc_throw(isc::BadValue, "Configuration file is expected to be "
		      "a map, i.e., start with { and end with } and contain "
		      "at least an entry called 'Dhcp6' that itself is a map. "
		      << file_name
                      << " is a valid JSON, but its top element is not a map."
		      " Did you forget to add { } around your configuration?");
        }

83 84
        // Let's configure logging before applying the configuration,
        // so we can log things during configuration process.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
85 86 87
        // If there's no logging element, we'll just pass NULL pointer,
        // which will be handled by configureLogger().
        Daemon::configureLogger(json->get("Logging"),
88
                                CfgMgr::instance().getStagingCfg());
89

90 91 92 93
        // Get Dhcp6 component from the config
        dhcp6 = json->get("Dhcp6");

        if (!dhcp6) {
94 95
            isc_throw(isc::BadValue, "no mandatory 'Dhcp6' entry in"
                      " the configuration");
96 97
        }

98
        // Use parsed JSON structures to configure the server
99
        result = ControlledDhcpv6Srv::processCommand("config-reload", dhcp6);
100 101 102 103 104 105 106
        if (!result) {
            // Undetermined status of the configuration. This should never
            // happen, but as the configureDhcp6Server returns a pointer, it is
            // theoretically possible that it will return NULL.
            isc_throw(isc::BadValue, "undefined result of "
                      "processCommand(\"config-reload\", dhcp6)");
        }
107

108 109 110 111 112 113 114 115 116 117
        // Now check is the returned result is successful (rcode=0) or not
        // (see @ref isc::config::parseAnswer).
        int rcode;
        isc::data::ConstElementPtr comment =
            isc::config::parseAnswer(rcode, result);
        if (rcode != 0) {
            string reason = comment ? comment->stringValue() :
                "no details available";
            isc_throw(isc::BadValue, reason);
        }
118

119 120 121 122 123 124
        // If configuration was parsed successfully, apply the new logger
        // configuration to log4cplus. It is done before commit in case
        // something goes wrong.
        CfgMgr::instance().getStagingCfg()->applyLoggingCfg();

        // Use new configuration.
125 126
        CfgMgr::instance().commit();

127
    }  catch (const std::exception& ex) {
128 129 130 131
        // If configuration failed at any stage, we drop the staging
        // configuration and continue to use the previous one.
        CfgMgr::instance().rollback();

132
        LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL)
133 134 135
            .arg(file_name).arg(ex.what());
        isc_throw(isc::BadValue, "configuration error using file '"
                  << file_name << "': " << ex.what());
136 137
    }

138 139 140 141
}

/// @brief Signals handler for DHCPv6 server.
///
142 143 144 145 146
/// This signal handler handles the following signals received by the DHCPv6
/// server process:
/// - SIGHUP - triggers server's dynamic reconfiguration.
/// - SIGTERM - triggers server's shut down.
/// - SIGINT - triggers server's shut down.
147 148 149
///
/// @param signo Signal number received.
void signalHandler(int signo) {
150
    // SIGHUP signals a request to reconfigure the server.
151
    if (signo == SIGHUP) {
152 153
        // Get configuration file name.
        std::string file = ControlledDhcpv6Srv::getInstance()->getConfigFile();
154
        try {
155 156
            LOG_INFO(dhcp6_logger, DHCP6_DYNAMIC_RECONFIGURATION).arg(file);
            configure(file);
157 158 159 160
        } 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.
161 162
            LOG_ERROR(dhcp6_logger, DHCP6_DYNAMIC_RECONFIGURATION_FAIL)
                .arg(file);
163
        }
164
    } else if ((signo == SIGTERM) || (signo == SIGINT)) {
165
        isc::data::ElementPtr params(new isc::data::MapElement());
166
        ControlledDhcpv6Srv::processCommand("shutdown", params);
167 168 169 170 171 172 173 174 175 176 177 178
    }
}

}

namespace isc {
namespace dhcp {

void
ControlledDhcpv6Srv::init(const std::string& file_name) {
    // Configure the server using JSON file.
    configure(file_name);
179

Tomek Mrugalski's avatar
Tomek Mrugalski committed
180 181 182
    // We don't need to call openActiveSockets() or startD2() as these
    // methods are called in processConfig() which is called by
    // processCommand("reload-config", ...)
183

184 185 186
    // 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.
187
    signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM));
188 189
    // Set the pointer to the handler function.
    signal_handler_ = signalHandler;
190 191 192 193 194 195 196 197
}

void ControlledDhcpv6Srv::cleanup() {
    // Nothing to do here. No need to disconnect from anything.
}

};
};