kea_controller.cc 7.07 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 19 20 21 22 23 24
//
// 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/dhcp_config_parser.h>
#include <dhcpsrv/cfgmgr.h>
#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
        // Let's configure logging before applying the configuration,
        // so we can log things during configuration process.
Tomek Mrugalski's avatar
Tomek Mrugalski committed
74 75 76 77 78
        // If there's no logging element, we'll just pass NULL pointer,
        // which will be handled by configureLogger().
        Daemon::configureLogger(json->get("Logging"),
                                CfgMgr::instance().getConfiguration(),
                                ControlledDhcpv6Srv::getInstance()->getVerbose());
79

80 81 82 83
        // Get Dhcp6 component from the config
        dhcp6 = json->get("Dhcp6");

        if (!dhcp6) {
84 85
            isc_throw(isc::BadValue, "no mandatory 'Dhcp6' entry in"
                      " the configuration");
86 87
        }

88
        // Use parsed JSON structures to configure the server
89
        result = ControlledDhcpv6Srv::processCommand("config-reload", dhcp6);
90 91 92 93 94 95 96
        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)");
        }
97

98 99 100 101 102 103 104 105 106 107
        // 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);
        }
108

109
    }  catch (const std::exception& ex) {
110
        LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL)
111 112 113
            .arg(file_name).arg(ex.what());
        isc_throw(isc::BadValue, "configuration error using file '"
                  << file_name << "': " << ex.what());
114 115
    }

116 117 118 119
}

/// @brief Signals handler for DHCPv6 server.
///
120 121 122 123 124
/// 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.
125 126 127
///
/// @param signo Signal number received.
void signalHandler(int signo) {
128
    // SIGHUP signals a request to reconfigure the server.
129
    if (signo == SIGHUP) {
130 131
        // Get configuration file name.
        std::string file = ControlledDhcpv6Srv::getInstance()->getConfigFile();
132
        try {
133 134
            LOG_INFO(dhcp6_logger, DHCP6_DYNAMIC_RECONFIGURATION).arg(file);
            configure(file);
135 136 137 138
        } 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.
139 140
            LOG_ERROR(dhcp6_logger, DHCP6_DYNAMIC_RECONFIGURATION_FAIL)
                .arg(file);
141
        }
142
    } else if ((signo == SIGTERM) || (signo == SIGINT)) {
143
        isc::data::ElementPtr params(new isc::data::MapElement());
144
        ControlledDhcpv6Srv::processCommand("shutdown", params);
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    }
}

}

namespace isc {
namespace dhcp {

void
ControlledDhcpv6Srv::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);
160

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

165 166 167
    // 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.
168
    signal_set_.reset(new isc::util::SignalSet(SIGINT, SIGHUP, SIGTERM));
169 170
    // Set the pointer to the handler function.
    signal_handler_ = signalHandler;
171 172 173 174 175 176
}

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

177 178 179
/// 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
180
void Daemon::loggerInit(const char* logger_name, bool verbose) {
Tomek Mrugalski's avatar
Tomek Mrugalski committed
181

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

190 191
};
};