AC_SUBST(PYCOVERAGE_RUN)
AC_SUBST(USE_PYCOVERAGE)
+
+
+
enable_gtest="no"
GTEST_INCLUDES=
AC_PATH_PROGS(AWK, gawk awk)
AC_SUBST(AWK)
+
+# Kea configuration backend section
+# Currently there are 2 backends available: BIND10 and JSON
+# It is possible that we may extend this to accept additional backends.
+AC_ARG_WITH(kea-config,
+ AC_HELP_STRING([--with-kea-config],
+ [Selects configuration backend; currently available options are: BIND10 (default) or JSON]),
+ [CONFIG_BACKEND="$withval"],
+ [CONFIG_BACKEND=BIND10])
+
+AM_CONDITIONAL(CONFIG_BACKEND_BIND10, test "x$CONFIG_BACKEND" = "xBIND10")
+AM_CONDITIONAL(CONFIG_BACKEND_JSON, test "x$CONFIG_BACKEND" = "xJSON")
+
+if test "x$CONFIG_BACKEND" = "xBIND10"; then
+ AC_DEFINE(CONFIG_BACKEND_BIND10, 1, [Define to 1 if Kea config was set to BIND10])
+fi
+
+if test "x$CONFIG_BACKEND" = "xJSON"; then
+ AC_DEFINE(CONFIG_BACKEND_JSON, 1, [Define to 1 if Kea config was set to JSON])
+fi
+
+# Let's sanity check if the specified backend value is allowed
+if test "x$CONFIG_BACKEND" != "xBIND10" && test "x$CONFIG_BACKEND" != "xJSON"; then
+ AC_MSG_ERROR("Invalid configuration backend specified: $CONFIG_BACKEND. The only supported are: BIND10 JSON")
+fi
+
AC_ARG_ENABLE(generate_docs, [AC_HELP_STRING([--enable-generate-docs],
[regenerate documentation using Docbook [default=no]])],
enable_generate_docs=$enableval, enable_generate_docs=no)
SQLITE_VERSION: ${SQLITE_VERSION}
SQLITE_CFLAGS: ${SQLITE_CFLAGS}
SQLITE_LIBS: ${SQLITE_LIBS}
+
+Kea config backend:
+ CONFIG_BACKEND: ${CONFIG_BACKEND}
+
END
# Avoid confusion on DNS/DHCP and only mention MySQL if it
pkglibexec_PROGRAMS = b10-dhcp6
b10_dhcp6_SOURCES = main.cc
-b10_dhcp6_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
-b10_dhcp6_SOURCES += config_parser.cc config_parser.h
b10_dhcp6_SOURCES += dhcp6_log.cc dhcp6_log.h
b10_dhcp6_SOURCES += dhcp6_srv.cc dhcp6_srv.h
+b10_dhcp6_SOURCES += ctrl_dhcp6_srv.h
+
+if CONFIG_BACKEND_BIND10
+b10_dhcp6_SOURCES += json_config_parser.cc json_config_parser.h ctrl_bind10_dhcp6_srv.cc
+endif
+
+if CONFIG_BACKEND_JSON
+b10_dhcp6_SOURCES += json_config_parser.cc json_config_parser.h ctrl_json_dhcp6_srv.cc
+endif
nodist_b10_dhcp6_SOURCES = dhcp6_messages.h dhcp6_messages.cc
EXTRA_DIST += dhcp6_messages.mes
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/dhcp_config_parser.h>
#include <dhcpsrv/cfgmgr.h>
-#include <dhcp6/config_parser.h>
+#include <dhcp6/json_config_parser.h>
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/spec_config.h>
}
}
-void ControlledDhcpv6Srv::establishSession() {
+
+bool
+ControlledDhcpv6Srv::init(const std::string& /* config_file*/) {
+ // This is BIND10 configuration backed. It established control session
+ // that is used to connect to BIND10 framework.
+ //
+ // Creates session that will be used to receive commands and updated
+ // configuration from cfgmgr (or indirectly from user via bindctl).
string specfile;
if (getenv("B10_FROM_BUILD")) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_CCSESSION_STARTED)
.arg(ctrl_socket);
IfaceMgr::instance().addExternalSocket(ctrl_socket, sessionReader);
+
+ return (true);
}
-void ControlledDhcpv6Srv::disconnectSession() {
+void ControlledDhcpv6Srv::cleanup() {
if (config_session_) {
delete config_session_;
config_session_ = NULL;
}
ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
- disconnectSession();
+ cleanup();
server_ = NULL; // forget this instance. There should be no callback anymore
// at this stage anyway.
}
}
+void
+Daemon::loggerInit(const char* log_name, bool verbose, bool stand_alone) {
+ isc::log::initLogger(log_name,
+ (verbose ? isc::log::DEBUG : isc::log::INFO),
+ isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
+}
+
};
};
/// @brief Destructor.
~ControlledDhcpv6Srv();
- /// @brief Establishes msgq session.
+ /// @brief Initializes the server.
///
- /// Creates session that will be used to receive commands and updated
- /// configuration from cfgmgr (or indirectly from user via bindctl).
- void establishSession();
+ /// Depending on the configuration backend, it establishes msgq session,
+ /// reads the JSON file from disk or may perform any other setup
+ /// operation. For specific details, see actual implementation in
+ /// ctrl_*_dhcp6_srv.cc
+ ///
+ /// @return true if initialization was successful, false if it failed
+ bool init(const std::string& config_file);
/// @brief Terminates existing msgq session.
///
/// may be received.
///
/// It is ok to call this method when session is disconnected already.
- void disconnectSession();
+ void cleanup();
/// @brief Initiates shutdown procedure for the whole DHCPv6 server.
void shutdown();
--- /dev/null
+// Copyright (C) 2012-2013 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 <asiolink/asiolink.h>
+#include <dhcp/iface_mgr.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 <dhcp6/spec_config.h>
+#include <exceptions/exceptions.h>
+#include <util/buffer.h>
+
+#include <cassert>
+#include <iostream>
+#include <string>
+#include <vector>
+
+using namespace isc::asiolink;
+using namespace isc::cc;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::log;
+using namespace isc::util;
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+
+// Need to provide dummy reader. JSON-file backend does not use any control
+// readers for now. Eventually, we may consider having a socket (named socket?)
+// that other processes (like IPAM) could write to, triggering specific actions.
+// For now, it's a no-op method.
+void ControlledDhcpv6Srv::sessionReader(void) {
+}
+
+bool
+ControlledDhcpv6Srv::init(const std::string& file_name) {
+ // This is a configuration backend implementation that reads the
+ // configuration from a JSON file.
+
+ isc::data::ConstElementPtr json;
+ isc::data::ConstElementPtr result;
+
+ // Basic sanity check: file name must not be empty.
+ if (file_name.empty()) {
+ isc_throw(BadValue, "JSON configuration file not specified");
+ }
+
+ try {
+ // Read contents of the file
+ string config = readFile(file_name);
+
+ // Try to parse it as JSON
+ json = Element::fromJSON(config);
+
+ // Use parsed JSON structures to configure the server
+ result = configureDhcp6Server(*this, json);
+
+ } catch (const std::exception& ex) {
+ LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(ex.what());
+ isc_throw(BadValue, "Unable to process JSON configuration file:"
+ + file_name);
+ }
+
+ 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.
+ LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL)
+ .arg("Configuration failed: Undefined result of configureDhcp6Server"
+ "() function after attempting to read " + file_name);
+ return (false);
+ }
+
+ // Now check is the returned result is successful (rcode=0) or not
+ ConstElementPtr comment; /// see @ref isc::config::parseAnswer
+ int rcode;
+ comment = parseAnswer(rcode, result);
+ if (rcode != 0) {
+ string reason = "";
+ if (comment) {
+ reason = string(" (") + comment->stringValue() + string(")");
+ }
+ LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_LOAD_FAIL).arg(reason);
+ return (false);
+ }
+
+ // Configuration may disable or enable interfaces so we have to
+ // reopen sockets according to new configuration.
+ openActiveSockets(getPort());
+
+ // Server will start DDNS communications if its enabled.
+ this->startD2();
+
+ return (true);
+}
+
+void ControlledDhcpv6Srv::cleanup() {
+ // Nothing to do here. No need to disconnect from anything.
+}
+
+ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t port)
+ : Dhcpv6Srv(port), cc_session_(NULL), config_session_(NULL) {
+}
+
+void ControlledDhcpv6Srv::shutdown() {
+
+ // Stop ASIO transmissions. Even though we didn't use it for
+ // configuration reading, there may be on-going transmissions
+ // with D2.
+ io_service_.stop();
+
+ Dhcpv6Srv::shutdown(); // Initiate DHCPv6 shutdown procedure.
+}
+
+ControlledDhcpv6Srv::~ControlledDhcpv6Srv() {
+ cleanup();
+}
+
+};
+};
The IPv6 DHCP server has encountered a fatal error and is terminating.
The reason for the failure is included in the message.
-% DHCP6_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone
-The server has failed to establish communication with the rest of BIND
-10 and is running in stand-alone mode. (This behavior will change once
-the IPv6 DHCP server is properly integrated with the rest of BIND 10.)
+% DHCP6_INIT_FAIL failed to initialize Kea server: %1
+The server has failed to establish communication with the rest of BIND 10,
+failed to read JSON configuration file or excountered any other critical
+issue that prevents it from starting up properly. Attached error message
+provides more details about the issue.
% DHCP6_SHUTDOWN server shutdown
The IPv6 DHCP server has terminated normally.
#include <dhcpsrv/d2_client_mgr.h>
#include <dhcpsrv/subnet.h>
#include <hooks/callout_handle.h>
-
-#include <boost/noncopyable.hpp>
+#include <dhcpsrv/daemon.h>
#include <iostream>
#include <queue>
/// that is going to be used as server-identifier, receives incoming
/// packets, processes them, manages leases assignment and generates
/// appropriate responses.
-///
-/// @note Only one instance of this class is instantiated as it encompasses
-/// the whole operation of the server. Nothing, however, enforces the
-/// singleton status of the object.
-class Dhcpv6Srv : public boost::noncopyable {
+class Dhcpv6Srv : public Daemon {
public:
/// @brief defines if certain option may, must or must not appear
#include <cc/data.h>
#include <config/ccsession.h>
#include <dhcp/libdhcp++.h>
-#include <dhcp6/config_parser.h>
+#include <dhcp6/json_config_parser.h>
#include <dhcp6/dhcp6_log.h>
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcp6/dhcp6_log.h>
#include <log/logger_support.h>
#include <log/logger_manager.h>
+#include <exceptions/exceptions.h>
#include <boost/lexical_cast.hpp>
namespace {
const char* const DHCP6_NAME = "b10-dhcp6";
+const char* const DHCP6_LOGGER_NAME = "bind10";
+
void
usage() {
- cerr << "Usage: " << DHCP6_NAME << " [-v] [-s] [-p number]" << endl;
+ cerr << "Usage: " << DHCP6_NAME << " [-v] [-s] [-p port_number] [-c cfgfile]" << endl;
cerr << " -v: verbose output" << endl;
cerr << " -s: stand-alone mode (don't connect to BIND10)" << endl;
cerr << " -p number: specify non-standard port number 1-65535 "
<< "(useful for testing only)" << endl;
+ cerr << " -c file: specify configuration file" << endl;
exit(EXIT_FAILURE);
}
} // end of anonymous namespace
bool stand_alone = false; // Should be connect to BIND10 msgq?
bool verbose_mode = false; // Should server be verbose?
- while ((ch = getopt(argc, argv, "vsp:")) != -1) {
+ // The standard config file
+ std::string config_file("");
+
+ while ((ch = getopt(argc, argv, "vsp:c:")) != -1) {
switch (ch) {
case 'v':
verbose_mode = true;
break;
- case 's':
+ case 's': // stand-alone
stand_alone = true;
break;
- case 'p':
+ case 'p': // port number
try {
port_number = boost::lexical_cast<int>(optarg);
} catch (const boost::bad_lexical_cast &) {
}
break;
+ case 'c': // config file
+ config_file = optarg;
+ break;
+
default:
usage();
}
usage();
}
- // Initialize logging. If verbose, we'll use maximum verbosity.
- // If standalone is enabled, do not buffer initial log messages
- isc::log::initLogger(DHCP6_NAME,
- (verbose_mode ? isc::log::DEBUG : isc::log::INFO),
- isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone);
- LOG_INFO(dhcp6_logger, DHCP6_STARTING);
- LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
- .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
- .arg(stand_alone ? "yes" : "no" );
int ret = EXIT_SUCCESS;
+ bool status = true;
try {
+ // Initialize logging. If verbose, we'll use maximum verbosity.
+ // If standalone is enabled, do not buffer initial log messages
+ Daemon::loggerInit(DHCP6_LOGGER_NAME, verbose_mode, stand_alone);
+
+ LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_START_INFO)
+ .arg(getpid()).arg(port_number).arg(verbose_mode ? "yes" : "no")
+ .arg(stand_alone ? "yes" : "no" );
+
+ LOG_INFO(dhcp6_logger, DHCP6_STARTING);
+
ControlledDhcpv6Srv server(port_number);
+
if (!stand_alone) {
try {
- server.establishSession();
+ // Initialize the server, i.e. establish control session
+ // if BIND10 backend is used or read a configuration file
+ //
+ status = server.init(config_file);
} catch (const std::exception& ex) {
- LOG_ERROR(dhcp6_logger, DHCP6_SESSION_FAIL).arg(ex.what());
- // Let's continue. It is useful to have the ability to run
- // DHCP server in stand-alone mode, e.g. for testing
- // We do need to make sure logging is no longer buffered
- // since then it would not print until dhcp6 is stopped
+ LOG_ERROR(dhcp6_logger, DHCP6_INIT_FAIL).arg(ex.what());
+
+ // We should not continue if were told to configure (either read
+ // config file or establish BIND10 control session).
isc::log::LoggerManager log_manager;
log_manager.process();
+
+ cerr << "Failed to initialize server: " << ex.what() << endl;
+ return (EXIT_FAILURE);
}
} else {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_STANDALONE);
}
+ if (!status) {
+ isc_throw(isc::Unexpected, "Failed to initialize configuration backend.");
+ }
+
server.run();
LOG_INFO(dhcp6_logger, DHCP6_SHUTDOWN);
} catch (const std::exception& ex) {
LOG_FATAL(dhcp6_logger, DHCP6_SERVER_FAILED).arg(ex.what());
+ cerr << "Fatal error during start up: " << ex.what() << endl;
ret = EXIT_FAILURE;
}
dhcp6_unittests_SOURCES += fqdn_unittest.cc
dhcp6_unittests_SOURCES += hooks_unittest.cc
dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h
-dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
-dhcp6_unittests_SOURCES += config_parser_unittest.cc
dhcp6_unittests_SOURCES += d2_unittest.cc d2_unittest.h
dhcp6_unittests_SOURCES += marker_file.cc
dhcp6_unittests_SOURCES += ../dhcp6_srv.h ../dhcp6_srv.cc
dhcp6_unittests_SOURCES += ../dhcp6_log.h ../dhcp6_log.cc
-dhcp6_unittests_SOURCES += ../ctrl_dhcp6_srv.cc
-dhcp6_unittests_SOURCES += ../config_parser.cc ../config_parser.h
dhcp6_unittests_SOURCES += wireshark.cc
dhcp6_unittests_SOURCES += dhcp6_client.cc dhcp6_client.h
dhcp6_unittests_SOURCES += rebind_unittest.cc
+if CONFIG_BACKEND_BIND10
+dhcp6_unittests_SOURCES += ../json_config_parser.cc ../json_config_parser.h ../ctrl_bind10_dhcp6_srv.cc
+dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
+dhcp6_unittests_SOURCES += config_parser_unittest.cc
+endif
+
+if CONFIG_BACKEND_JSON
+dhcp6_unittests_SOURCES += ../json_config_parser.cc ../json_config_parser.h ../ctrl_json_dhcp6_srv.cc
+dhcp6_unittests_SOURCES += config_parser_unittest.cc
+endif
+
+
nodist_dhcp6_unittests_SOURCES = ../dhcp6_messages.h ../dhcp6_messages.cc
nodist_dhcp6_unittests_SOURCES += marker_file.h test_libraries.h
#include <dhcp/iface_mgr.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_int.h>
-#include <dhcp6/config_parser.h>
+#include <dhcp6/json_config_parser.h>
#include <dhcp6/dhcp6_srv.h>
#include <dhcpsrv/addr_utilities.h>
#include <dhcpsrv/cfgmgr.h>
ElementPtr json = Element::fromJSON(config);
+ ofstream out("config.json");
+ out << config;
+ out.close();
+
+
do {
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
ASSERT_TRUE(x);
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcp/iface_mgr.h>
-#include <dhcp6/config_parser.h>
+#include <dhcp6/json_config_parser.h>
#include <dhcp6/tests/d2_unittest.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcp/option_string.h>
#include <dhcp/option_vendor.h>
#include <dhcp/iface_mgr.h>
-#include <dhcp6/config_parser.h>
+#include <dhcp6/json_config_parser.h>
#include <dhcp/dhcp6.h>
#include <dhcp/docsis3_option_defs.h>
#include <dhcp/tests/iface_mgr_test_config.h>
ASSERT_NO_THROW( {
// Skip opening any sockets
- srv.reset(new Dhcpv6Srv(0));
+ srv.reset(new NakedDhcpv6Srv(0));
});
srv.reset();
ASSERT_NO_THROW({
// open an unpriviledged port
- srv.reset(new Dhcpv6Srv(DHCP6_SERVER_PORT + 10000));
+ srv.reset(new NakedDhcpv6Srv(DHCP6_SERVER_PORT + 10000));
});
}
#include <gtest/gtest.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
-#include <dhcp6/config_parser.h>
+#include <dhcp6/json_config_parser.h>
using namespace isc::data;
using namespace isc::dhcp;
#include <asiolink/io_address.h>
#include <dhcp/dhcp6.h>
#include <dhcp/duid.h>
-#include <dhcp6/config_parser.h>
+#include <dhcp6/json_config_parser.h>
#include <dhcp/dhcp6.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease_mgr.h>
#include <asiolink/io_address.h>
#include <cc/data.h>
#include <dhcp/tests/iface_mgr_test_config.h>
-#include <dhcp6/config_parser.h>
+#include <dhcp6/json_config_parser.h>
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/dhcp6_client.h>
dhcp_data_dir = @localstatedir@/@PACKAGE@
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib -DDHCP_DATA_DIR="\"$(dhcp_data_dir)\""
+AM_CPPFLAGS += -DTOP_BUILDDIR="\"$(top_builddir)\""
AM_CPPFLAGS += $(BOOST_INCLUDES)
if HAVE_MYSQL
AM_CPPFLAGS += $(MYSQL_CPPFLAGS)
libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
libkea_dhcpsrv_la_SOURCES += d2_client_cfg.cc d2_client_cfg.h
libkea_dhcpsrv_la_SOURCES += d2_client_mgr.cc d2_client_mgr.h
+libkea_dhcpsrv_la_SOURCES += daemon.cc daemon.h
libkea_dhcpsrv_la_SOURCES += dbaccess_parser.cc dbaccess_parser.h
libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
libkea_dhcpsrv_la_SOURCES += lease_mgr.cc lease_mgr.h
libkea_dhcpsrv_la_SOURCES += lease_mgr_factory.cc lease_mgr_factory.h
libkea_dhcpsrv_la_SOURCES += memfile_lease_mgr.cc memfile_lease_mgr.h
+
if HAVE_MYSQL
libkea_dhcpsrv_la_SOURCES += mysql_lease_mgr.cc mysql_lease_mgr.h
endif
--- /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 <log/logger_level.h>
+#include <log/logger_name.h>
+#include <log/logger_manager.h>
+#include <log/logger_specification.h>
+#include <log/logger_support.h>
+#include <log/output_option.h>
+#include <dhcpsrv/daemon.h>
+#include <exceptions/exceptions.h>
+#include <fstream>
+#include <errno.h>
+
+/// @brief provides default implementation for basic daemon operations
+///
+/// This file provides stub implementations that are expected to be redefined
+/// in derived classes (e.g. ControlledDhcpv6Srv)
+namespace isc {
+namespace dhcp {
+
+Daemon::Daemon() {
+}
+
+bool Daemon::init(const std::string&) {
+ return false;
+}
+
+void Daemon::cleanup() {
+
+}
+
+void Daemon::shutdown() {
+
+}
+
+Daemon::~Daemon() {
+}
+
+std::string Daemon::readFile(const std::string& file_name) {
+
+ // This is the fastest method to read a file according to:
+ // http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html
+
+ std::ifstream infile(file_name.c_str(), std::ios::in | std::ios::binary);
+ if (infile.is_open())
+ {
+ std::string contents;
+ infile.seekg(0, std::ios::end);
+ contents.resize(infile.tellg());
+ infile.seekg(0, std::ios::beg);
+ infile.read(&contents[0], contents.size());
+ infile.close();
+ return(contents);
+ }
+ isc_throw(InvalidOperation, "Failed to read file " << file_name << ",error:"
+ << errno);
+}
+
+void Daemon::loggerInit(const char* log_name, bool verbose, bool ) {
+ // This method configures logger. For now it is very simple.
+ // We'll make it more robust once we add support for JSON-based logging
+ // configuration.
+
+ using namespace isc::log;
+
+ Severity severity = b10LoggerSeverity(verbose ? isc::log::DEBUG : isc::log::INFO);
+
+ // Set a directory for creating lockfiles when running tests
+ // @todo: Find out why this is needed. Without this, the logger doesn't
+ // work.
+ setenv("B10_LOCKFILE_DIR_FROM_BUILD", TOP_BUILDDIR, 1);
+
+ // Initialize logging
+ initLogger(log_name, severity, isc::log::MAX_DEBUG_LEVEL, NULL);
+
+ // Now configure logger output to stdout.
+ /// @todo: Make this configurable as part of #3427.
+ LoggerSpecification spec(log_name, severity,
+ b10LoggerDbglevel(isc::log::MAX_DEBUG_LEVEL));
+ OutputOption option;
+ option.destination = OutputOption::DEST_CONSOLE;
+ option.stream = OutputOption::STR_STDOUT;
+
+ spec.addOutputOption(option);
+ LoggerManager manager;
+ manager.process(spec);
+}
+
+};
+};
--- /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 <string>
+#include <boost/noncopyable.hpp>
+
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Base class for all services
+///
+/// This is the base class that all daemons (DHCPv4, DHCPv6, D2 and possibly
+/// others) are derived from. It provides a standard interface for starting up,
+/// reconfiguring, shutting down and several other operations. It also covers
+/// some common operations.
+///
+/// This class is not expected to be instantiated directly, but rather daemon
+/// implementations should derive from it.
+///
+/// Methods are not pure virtual, as we need to instantiate basic daemons (e.g.
+/// Dhcpv6Srv) in tests, without going through the hassles of implemeting stub
+/// methods.
+///
+/// @note Only one instance of this class is instantiated as it encompasses
+/// the whole operation of the server. Nothing, however, enforces the
+/// singleton status of the object.
+class Daemon : public boost::noncopyable {
+
+public:
+ /// @brief Default constructor
+ ///
+ /// Currently it does nothing.
+ Daemon();
+
+ /// @brief Initializes the server.
+ ///
+ /// Depending on the configuration backend, it establishes msgq session,
+ /// or reads the
+ /// Creates session that will be used to receive commands and updated
+ /// configuration from cfgmgr (or indirectly from user via bindctl).
+ /// @return true if initialization was successful, false if it failed
+ virtual bool init(const std::string& config_file);
+
+ /// @brief Performs final deconfiguration.
+ ///
+ /// Performs configuration backend specific final clean-up. This is called
+ /// shortly before the daemon terminates. Depending on backend, it may
+ /// terminat existing msgq session, close LDAP connection or similar.
+ ///
+ /// The daemon is not expected to receive any further commands or
+ /// configuration updates as it is in final stages of shutdown.
+ virtual void cleanup();
+
+ /// @brief Initiates shutdown procedure for the whole DHCPv6 server.
+ virtual void shutdown();
+
+ /// @brief Desctructor
+ ///
+ /// Having virtual destructor ensures that all derived classes will have
+ /// virtual destructor as well.
+ virtual ~Daemon();
+
+ /// Initializez logger
+ ///
+ /// This method initializes logger. I
+ static void loggerInit(const char* log_name, bool verbose, bool stand_alone);
+
+ protected:
+
+ /// @brief reads file and return its content as a string
+ ///
+ /// This is an utility method that is expected to be used by several daemons.
+ /// It reads contents of a text file and return it as a string.
+ ///
+ /// @param file_name name of the file to read
+ /// @return content of the file
+ std::string readFile(const std::string& file_name);
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc
+libdhcpsrv_unittests_SOURCES += daemon_unittest.cc
libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += lease_file_io.cc lease_file_io.h
libdhcpsrv_unittests_SOURCES += lease_unittest.cc
--- /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 <exceptions/exceptions.h>
+#include <dhcpsrv/daemon.h>
+#include <gtest/gtest.h>
+#include <fstream>
+
+using namespace isc;
+using namespace isc::dhcp;
+
+/// Temporary file name used in some tests
+const char* TEMP_FILE="temp-file.json";
+
+namespace {
+
+/// @brief Test-friendly version of the daemon
+///
+/// This class exposes internal Daemon class methods.
+class NakedDaemon: public Daemon {
+public:
+
+ using Daemon::readFile;
+};
+
+TEST(Daemon, readFile) {
+ NakedDaemon x;
+
+ const char* content = "Horse doesn't eat cucumber salad";
+
+ // Write sample content to disk
+ unlink(TEMP_FILE);
+ std::ofstream write_me(TEMP_FILE);
+ EXPECT_TRUE(write_me.is_open());
+ write_me << content;
+ write_me.close();
+
+ // Check that the read content is correct
+ EXPECT_EQ(std::string(content), x.readFile(TEMP_FILE));
+
+ unlink(TEMP_FILE);
+}
+
+TEST(Daemon, readFileError) {
+ NakedDaemon x;
+
+ // Check that the read content is correct
+ EXPECT_THROW(x.readFile("no-such-file.txt"), isc::InvalidOperation);
+
+ unlink(TEMP_FILE);
+}
+
+};