kea_controller.cc 6.97 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// 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 <dhcp4/json_config_parser.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
20
#include <dhcpsrv/cfgmgr.h>
21 22 23 24 25 26 27 28
#include <exceptions/exceptions.h>

#include <string>

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

29 30 31 32 33 34 35 36 37 38 39 40
namespace {

/// @brief Configure DHCPv4 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 'Dhcp4' section of
/// the JSON configuration file.
///
/// @param file_name Configuration file location.
void configure(const std::string& file_name) {
41 42 43
    // This is a configuration backend implementation that reads the
    // configuration from a JSON file.

44 45 46 47 48
    // We are starting the configuration process so we should remove any
    // staging configuration that has been created during previous
    // configuration attempts.
    CfgMgr::instance().rollback();

49 50
    isc::data::ConstElementPtr json;
    isc::data::ConstElementPtr dhcp4;
51
    isc::data::ConstElementPtr logger;
52 53 54 55 56 57
    isc::data::ConstElementPtr result;

    // Basic sanity check: file name must not be empty.
    try {
        if (file_name.empty()) {
            // Basic sanity check: file name must not be empty.
58 59
            isc_throw(isc::BadValue, "JSON configuration file not specified."
                      " Please use -c command line option.");
60 61 62
        }

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

68 69 70
        // Let's configure logging before applying the configuration,
        // so we can log things during configuration process.

Tomek Mrugalski's avatar
Tomek Mrugalski committed
71 72 73
        // If there's no logging element, we'll just pass NULL pointer,
        // which will be handled by configureLogger().
        Daemon::configureLogger(json->get("Logging"),
74
                                CfgMgr::instance().getStaging(),
Tomek Mrugalski's avatar
Tomek Mrugalski committed
75
                                ControlledDhcpv4Srv::getInstance()->getVerbose());
76

77 78 79
        // Get Dhcp4 component from the config
        dhcp4 = json->get("Dhcp4");
        if (!dhcp4) {
80 81
            isc_throw(isc::BadValue, "no mandatory 'Dhcp4' entry in"
                      " the configuration");
82 83 84
        }

        // Use parsed JSON structures to configure the server
85
        result = ControlledDhcpv4Srv::processCommand("config-reload", dhcp4);
86 87 88 89 90 91 92
        if (!result) {
            // Undetermined status of the configuration. This should never
            // happen, but as the configureDhcp4Server returns a pointer, it is
            // theoretically possible that it will return NULL.
            isc_throw(isc::BadValue, "undefined result of "
                      "processCommand(\"config-reload\", dhcp4)");
        }
93

94 95 96 97 98 99 100 101 102 103
        // 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);
        }
104

105
    }  catch (const std::exception& ex) {
106
        LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_LOAD_FAIL)
107 108 109
            .arg(file_name).arg(ex.what());
        isc_throw(isc::BadValue, "configuration error using file '"
                  << file_name << "': " << ex.what());
110
    }
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
}

/// @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);
155 156 157 158

    // We don't need to call openActiveSockets() or startD2() as these
    // methods are called in processConfig() which is called by
    // processCommand("reload-config", ...)
159 160 161 162

    // 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.
163
    signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM));
164 165 166
    // Set the pointer to the handler function.
    signal_handler_ = signalHandler;

167 168 169 170 171 172 173 174 175
}

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

/// This is a logger initialization for JSON file backend.
/// For now, it's just setting log messages to be printed on stdout.
/// @todo: Implement this properly (see #3427)
Tomek Mrugalski's avatar
Tomek Mrugalski committed
176
void Daemon::loggerInit(const char* logger_name, bool verbose) {
177

Tomek Mrugalski's avatar
Tomek Mrugalski committed
178
    setenv("KEA_LOCKFILE_DIR_FROM_BUILD", "/tmp", 1);
179
    setenv("KEA_LOGGER_ROOT", logger_name, 0);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
180 181 182
    setenv("KEA_LOGGER_SEVERITY", (verbose ? "DEBUG":"INFO"), 0);
    setenv("KEA_LOGGER_DBGLEVEL", "99", 0);
    setenv("KEA_LOGGER_DESTINATION",  "stdout", 0);
183 184 185 186 187
    isc::log::initLogger();
}

};
};