|
@@ -1,319 +0,0 @@
|
|
|
-// Copyright (C) 2014, 2015 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 <d2/bundy_d2_controller.h>
|
|
|
-#include <d2/d2_process.h>
|
|
|
-#include <d2/spec_config.h>
|
|
|
-
|
|
|
-#include <sstream>
|
|
|
-
|
|
|
-namespace isc {
|
|
|
-namespace d2 {
|
|
|
-
|
|
|
-/// @brief Defines the application name, this is passed into base class
|
|
|
-/// it may be used to locate configuration data and appears in log statement.
|
|
|
-const char* D2Controller::d2_app_name_ = "DHCP-DDNS";
|
|
|
-
|
|
|
-/// @brief Defines the executable name. This is passed into the base class
|
|
|
-const char* D2Controller::d2_bin_name_ = "kea-dhcp-ddns";
|
|
|
-
|
|
|
-DControllerBasePtr&
|
|
|
-D2Controller::instance() {
|
|
|
- // If the instance hasn't been created yet, create it. Note this method
|
|
|
- // must use the base class singleton instance methods. The base class
|
|
|
- // must have access to the singleton in order to use it within BUNDY
|
|
|
- // static function callbacks.
|
|
|
- if (!getController()) {
|
|
|
- DControllerBasePtr controller_ptr(new D2Controller());
|
|
|
- setController(controller_ptr);
|
|
|
- }
|
|
|
-
|
|
|
- return (getController());
|
|
|
-}
|
|
|
-
|
|
|
-DProcessBase* D2Controller::createProcess() {
|
|
|
- // Instantiate and return an instance of the D2 application process. Note
|
|
|
- // that the process is passed the controller's io_service.
|
|
|
- return (new D2Process(getAppName().c_str(), getIOService()));
|
|
|
-}
|
|
|
-
|
|
|
-D2Controller::D2Controller()
|
|
|
- : DControllerBase(d2_app_name_, d2_bin_name_) {
|
|
|
- // set the spec file either from the environment or
|
|
|
- // use the production value.
|
|
|
- if (getenv("KEA_FROM_BUILD")) {
|
|
|
- setSpecFileName(std::string(getenv("KEA_FROM_BUILD")) +
|
|
|
- "/src/bin/d2/dhcp-ddns.spec");
|
|
|
- } else {
|
|
|
- setSpecFileName(D2_SPECFILE_LOCATION);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-D2Controller::~D2Controller() {
|
|
|
-}
|
|
|
-
|
|
|
-void
|
|
|
-D2Controller::launch(int argc, char* argv[], const bool test_mode) {
|
|
|
- // Step 1 is to parse the command line arguments.
|
|
|
- try {
|
|
|
- parseArgs(argc, argv);
|
|
|
- } catch (const InvalidUsage& ex) {
|
|
|
- usage(ex.what());
|
|
|
- throw; // rethrow it
|
|
|
- }
|
|
|
-
|
|
|
- // Do not initialize logger here if we are running unit tests. It would
|
|
|
- // replace an instance of unit test specific logger.
|
|
|
- if (!test_mode) {
|
|
|
- // Now that we know what the mode flags are, we can init logging.
|
|
|
- // If standalone is enabled, do not buffer initial log messages
|
|
|
- isc::log::initLogger(getBinName(),
|
|
|
- (isVerbose() ? isc::log::DEBUG : isc::log::INFO),
|
|
|
- isc::log::MAX_DEBUG_LEVEL, NULL, true);
|
|
|
- }
|
|
|
-
|
|
|
- LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_STARTING)
|
|
|
- .arg(getAppName()).arg(getpid());
|
|
|
- try {
|
|
|
- // Step 2 is to create and initialize the application process object.
|
|
|
- initProcess();
|
|
|
- } catch (const std::exception& ex) {
|
|
|
- LOG_FATAL(dctl_logger, DCTL_INIT_PROCESS_FAIL)
|
|
|
- .arg(getAppName()).arg(ex.what());
|
|
|
- isc_throw (ProcessInitError,
|
|
|
- "Application Process initialization failed: " << ex.what());
|
|
|
- }
|
|
|
-
|
|
|
- // Next we connect to Bundy.
|
|
|
- try {
|
|
|
- establishSession();
|
|
|
- } catch (const std::exception& ex) {
|
|
|
- LOG_FATAL(dctl_logger, DCTL_SESSION_FAIL).arg(ex.what());
|
|
|
- isc_throw (SessionStartError,
|
|
|
- "Session start up failed: " << ex.what());
|
|
|
- }
|
|
|
-
|
|
|
- // Everything is clear for launch, so start the application's
|
|
|
- // event loop.
|
|
|
- try {
|
|
|
- runProcess();
|
|
|
- } catch (const std::exception& ex) {
|
|
|
- LOG_FATAL(dctl_logger, DCTL_PROCESS_FAILED)
|
|
|
- .arg(getAppName()).arg(ex.what());
|
|
|
- isc_throw (ProcessRunError,
|
|
|
- "Application process event loop failed: " << ex.what());
|
|
|
- }
|
|
|
-
|
|
|
- // Disconnect from Bundy.
|
|
|
- try {
|
|
|
- disconnectSession();
|
|
|
- } catch (const std::exception& ex) {
|
|
|
- LOG_ERROR(dctl_logger, DCTL_DISCONNECT_FAIL)
|
|
|
- .arg(getAppName()).arg(ex.what());
|
|
|
- isc_throw (SessionEndError, "Session end failed: " << ex.what());
|
|
|
- }
|
|
|
-
|
|
|
- // All done, so bail out.
|
|
|
- LOG_INFO(dctl_logger, DCTL_STOPPING).arg(getAppName());
|
|
|
-}
|
|
|
-
|
|
|
-void
|
|
|
-D2Controller::parseArgs(int argc, char* argv[])
|
|
|
-{
|
|
|
- // Iterate over the given command line options. If its a stock option
|
|
|
- // ("s" or "v") handle it here. If its a valid custom option, then
|
|
|
- // invoke customOption.
|
|
|
- int ch;
|
|
|
- opterr = 0;
|
|
|
- optind = 1;
|
|
|
- std::string opts(":v" + getCustomOpts());
|
|
|
- while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
|
|
|
- switch (ch) {
|
|
|
- case 'v':
|
|
|
- // Enables verbose logging.
|
|
|
- setVerbose(true);
|
|
|
- break;
|
|
|
-
|
|
|
- case '?': {
|
|
|
- // We hit an invalid option.
|
|
|
- isc_throw(InvalidUsage, "unsupported option: ["
|
|
|
- << static_cast<char>(optopt) << "] "
|
|
|
- << (!optarg ? "" : optarg));
|
|
|
-
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- default:
|
|
|
- // We hit a valid custom option
|
|
|
- if (!customOption(ch, optarg)) {
|
|
|
- // This would be a programmatic error.
|
|
|
- isc_throw(InvalidUsage, " Option listed but implemented?: ["
|
|
|
- << static_cast<char>(ch) << "] "
|
|
|
- << (!optarg ? "" : optarg));
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // There was too much information on the command line.
|
|
|
- if (argc > optind) {
|
|
|
- isc_throw(InvalidUsage, "extraneous command line information");
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void
|
|
|
-D2Controller::establishSession() {
|
|
|
- LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CCSESSION_STARTING)
|
|
|
- .arg(getAppName()).arg(getSpecFileName());
|
|
|
-
|
|
|
- // Create the Bundy command control session with the our IOService.
|
|
|
- cc_session_ = SessionPtr(new isc::cc::Session(
|
|
|
- getIOService()->get_io_service()));
|
|
|
-
|
|
|
- // Create the Bundy config session with the stub configuration handler.
|
|
|
- // This handler is internally invoked by the constructor and on success
|
|
|
- // the constructor updates the current session with the configuration that
|
|
|
- // had been committed in the previous session. If we do not install
|
|
|
- // the dummy handler, the previous configuration would be lost.
|
|
|
- config_session_ = ModuleCCSessionPtr(new isc::config::ModuleCCSession(
|
|
|
- getSpecFileName(), *cc_session_,
|
|
|
- dummyConfigHandler,
|
|
|
- commandHandler,
|
|
|
- false));
|
|
|
- // Enable configuration even processing.
|
|
|
- config_session_->start();
|
|
|
-
|
|
|
- // We initially create ModuleCCSession() with a dummy configHandler, as
|
|
|
- // the session module is too eager to send partial configuration.
|
|
|
- // Replace the dummy config handler with the real handler.
|
|
|
- config_session_->setConfigHandler(configHandler);
|
|
|
-
|
|
|
- // Call the real configHandler with the full configuration retrieved
|
|
|
- // from the config session.
|
|
|
- isc::data::ConstElementPtr answer = configHandler(
|
|
|
- config_session_->getFullConfig());
|
|
|
-
|
|
|
- // Parse the answer returned from the configHandler. Log the error but
|
|
|
- // keep running. This provides an opportunity for the user to correct
|
|
|
- // the configuration dynamically.
|
|
|
- int ret = 0;
|
|
|
- isc::data::ConstElementPtr comment = isc::config::parseAnswer(ret, answer);
|
|
|
- if (ret) {
|
|
|
- LOG_ERROR(dctl_logger, DCTL_CONFIG_LOAD_FAIL)
|
|
|
- .arg(getAppName()).arg(comment->str());
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void D2Controller::disconnectSession() {
|
|
|
- LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CCSESSION_ENDING)
|
|
|
- .arg(getAppName());
|
|
|
-
|
|
|
- // Destroy the Bundy config session.
|
|
|
- if (config_session_) {
|
|
|
- config_session_.reset();
|
|
|
- }
|
|
|
-
|
|
|
- // Destroy the Bundy command and control session.
|
|
|
- if (cc_session_) {
|
|
|
- cc_session_->disconnect();
|
|
|
- cc_session_.reset();
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-isc::data::ConstElementPtr
|
|
|
-D2Controller::dummyConfigHandler(isc::data::ConstElementPtr) {
|
|
|
- LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CONFIG_STUB)
|
|
|
- .arg(getController()->getAppName());
|
|
|
- return (isc::config::createAnswer(0, "Configuration accepted."));
|
|
|
-}
|
|
|
-
|
|
|
-isc::data::ConstElementPtr
|
|
|
-D2Controller::configHandler(isc::data::ConstElementPtr new_config) {
|
|
|
-
|
|
|
- LOG_DEBUG(dctl_logger, DBGLVL_COMMAND, DCTL_CONFIG_UPDATE)
|
|
|
- .arg(getController()->getAppName()).arg(new_config->str());
|
|
|
-
|
|
|
- // Invoke the instance method on the controller singleton.
|
|
|
- return (getController()->updateConfig(new_config));
|
|
|
-}
|
|
|
-
|
|
|
-// Static callback which invokes non-static handler on singleton
|
|
|
-isc::data::ConstElementPtr
|
|
|
-D2Controller::commandHandler(const std::string& command,
|
|
|
- isc::data::ConstElementPtr args) {
|
|
|
-
|
|
|
- LOG_DEBUG(dctl_logger, DBGLVL_COMMAND, DCTL_COMMAND_RECEIVED)
|
|
|
- .arg(getController()->getAppName()).arg(command)
|
|
|
- .arg(args ? args->str() : "(no args)");
|
|
|
-
|
|
|
- // Invoke the instance method on the controller singleton.
|
|
|
- return (getController()->executeCommand(command, args));
|
|
|
-}
|
|
|
-
|
|
|
-isc::data::ConstElementPtr
|
|
|
-D2Controller::updateConfig(isc::data::ConstElementPtr new_config) {
|
|
|
- if (!config_session_) {
|
|
|
- // That should never happen as we install config_handler
|
|
|
- // after we instantiate the server.
|
|
|
- isc::data::ConstElementPtr answer =
|
|
|
- isc::config::createAnswer(1, "Configuration rejected,"
|
|
|
- " Session has not started.");
|
|
|
- return (answer);
|
|
|
- }
|
|
|
-
|
|
|
- // Let's get the existing configuration.
|
|
|
- isc::data::ConstElementPtr full_config = config_session_->getFullConfig();
|
|
|
-
|
|
|
- // The configuration passed to this handler function is partial.
|
|
|
- // In other words, it just includes the values being modified.
|
|
|
- // In the same time, there may be dependencies between various
|
|
|
- // configuration parsers. For example: the option value can
|
|
|
- // be set if the definition of this option is set. If someone removes
|
|
|
- // an existing option definition then the partial configuration that
|
|
|
- // removes that definition is triggered while a relevant option value
|
|
|
- // may remain configured. This eventually results in the
|
|
|
- // configuration being in the inconsistent state.
|
|
|
- // In order to work around this problem we need to merge the new
|
|
|
- // configuration with the existing (full) configuration.
|
|
|
-
|
|
|
- // Let's create a new object that will hold the merged configuration.
|
|
|
- boost::shared_ptr<isc::data::MapElement>
|
|
|
- merged_config(new isc::data::MapElement());
|
|
|
-
|
|
|
- // Merge an existing and new configuration.
|
|
|
- merged_config->setValue(full_config->mapValue());
|
|
|
- isc::data::merge(merged_config, new_config);
|
|
|
-
|
|
|
- // Send the merged configuration to the application.
|
|
|
- return (getProcess()->configure(merged_config));
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-void
|
|
|
-D2Controller::usage(const std::string & text)
|
|
|
-{
|
|
|
- if (text != "") {
|
|
|
- std::cerr << "Usage error: " << text << std::endl;
|
|
|
- }
|
|
|
-
|
|
|
- std::cerr << "Usage: " << getBinName() << std::endl;
|
|
|
- std::cerr << " -v: verbose output" << std::endl;
|
|
|
- std::cerr << getUsageText() << std::endl;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-}; // namespace isc::d2
|
|
|
-}; // namespace isc
|