--- /dev/null
+// 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 <cc/data.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
+#include <dhcp4/dhcp4_log.h>
+#include <hooks/hooks_manager.h>
+#include <dhcp4/json_config_parser.h>
+
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+ControlledDhcpv4Srv* ControlledDhcpv4Srv::server_ = NULL;
+
+ConstElementPtr
+ControlledDhcpv4Srv::commandShutdownHandler(const string&, ConstElementPtr) {
+ if (ControlledDhcpv4Srv::server_) {
+ ControlledDhcpv4Srv::server_->shutdown();
+ } else {
+ LOG_WARN(dhcp4_logger, DHCP4_NOT_RUNNING);
+ ConstElementPtr answer = isc::config::createAnswer(1,
+ "Shutdown failure.");
+ return (answer);
+ }
+ ConstElementPtr answer = isc::config::createAnswer(0, "Shutting down.");
+ return (answer);
+}
+
+ConstElementPtr
+ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
+
+ /// @todo delete any stored CalloutHandles referring to the old libraries
+ /// Get list of currently loaded libraries and reload them.
+ vector<string> loaded = HooksManager::getLibraryNames();
+ bool status = HooksManager::loadLibraries(loaded);
+ if (!status) {
+ LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL);
+ ConstElementPtr answer = isc::config::createAnswer(1,
+ "Failed to reload hooks libraries.");
+ return (answer);
+ }
+ ConstElementPtr answer = isc::config::createAnswer(0,
+ "Hooks libraries successfully reloaded.");
+ return (answer);
+}
+
+ConstElementPtr
+ControlledDhcpv4Srv::commandConfigReloadHandler(const string&,
+ ConstElementPtr args) {
+ return (processConfig(args));
+}
+
+ConstElementPtr
+ControlledDhcpv4Srv::processCommand(const string& command,
+ ConstElementPtr args) {
+ LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_COMMAND_RECEIVED)
+ .arg(command).arg(args->str());
+
+ ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
+
+ if (!srv) {
+ ConstElementPtr no_srv = isc::config::createAnswer(1,
+ "Server object not initialized, can't process command '" +
+ command + "'.");
+ return (no_srv);
+ }
+
+ try {
+ if (command == "shutdown") {
+ return (srv->commandShutdownHandler(command, args));
+
+ } else if (command == "libreload") {
+ return (srv->commandLibReloadHandler(command, args));
+
+ } else if (command == "config-reload") {
+ return (srv->commandConfigReloadHandler(command, args));
+
+ }
+ ConstElementPtr answer = isc::config::createAnswer(1,
+ "Unrecognized command:" + command);
+ return (answer);
+ } catch (const Exception& ex) {
+ return (isc::config::createAnswer(1, "Error while processing command '"
+ + command + "':" + ex.what()));
+ }
+}
+
+isc::data::ConstElementPtr
+ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
+ ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
+
+ if (!srv) {
+ ConstElementPtr no_srv = isc::config::createAnswer(1,
+ "Server object not initialized, can't process config.");
+ return (no_srv);
+ }
+
+ ConstElementPtr answer = configureDhcp4Server(*srv, config);
+
+ // Check that configuration was successful. If not, do not reopen sockets
+ // and don't bother with DDNS stuff.
+ try {
+ int rcode = 0;
+ isc::config::parseAnswer(rcode, answer);
+ if (rcode != 0) {
+ return (answer);
+ }
+ } catch (const std::exception& ex) {
+ return (isc::config::createAnswer(1, "Failed to process configuration:"
+ + string(ex.what())));
+ }
+
+ // Server will start DDNS communications if its enabled.
+ try {
+ srv->startD2();
+ } catch (const std::exception& ex) {
+ std::ostringstream err;
+ err << "error starting DHCP_DDNS client "
+ " after server reconfiguration: " << ex.what();
+ return (isc::config::createAnswer(1, err.str()));
+ }
+
+ // Configuration may change active interfaces. Therefore, we have to reopen
+ // sockets according to new configuration. This operation is not exception
+ // safe and we really don't want to emit exceptions to the callback caller.
+ // Instead, catch an exception and create appropriate answer.
+ try {
+ srv->openActiveSockets(srv->getPort(), server_->useBroadcast());
+ } catch (std::exception& ex) {
+ std::ostringstream err;
+ err << "failed to open sockets after server reconfiguration: " << ex.what();
+ answer = isc::config::createAnswer(1, err.str());
+ }
+ return (answer);
+}
+
+ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t port /*= DHCP4_SERVER_PORT*/)
+ :Dhcpv4Srv(port) {
+ if (server_) {
+ isc_throw(InvalidOperation,
+ "There is another Dhcpv4Srv instance already.");
+ }
+ server_ = this; // remember this instance for use in callback
+}
+
+void ControlledDhcpv4Srv::shutdown() {
+ io_service_.stop(); // Stop ASIO transmissions
+ Dhcpv4Srv::shutdown(); // Initiate DHCPv4 shutdown procedure.
+}
+
+ControlledDhcpv4Srv::~ControlledDhcpv4Srv() {
+ cleanup();
+
+ server_ = NULL; // forget this instance. There should be no callback anymore
+ // at this stage anyway.
+}
+
+void ControlledDhcpv4Srv::sessionReader(void) {
+ // Process one asio event. If there are more events, iface_mgr will call
+ // this callback more than once.
+ if (server_) {
+ server_->io_service_.run_one();
+ }
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace