From: Tomek Date: Wed, 19 Jul 2017 14:57:31 +0000 (+0200) Subject: [client] Stub files for a client added. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bdb31e79600802f24fceef50f6730200dfa97102;p=thirdparty%2Fkea.git [client] Stub files for a client added. --- diff --git a/src/bin/client/Makefile.am b/src/bin/client/Makefile.am index 48c0d8d1ce..246d740fae 100644 --- a/src/bin/client/Makefile.am +++ b/src/bin/client/Makefile.am @@ -9,7 +9,7 @@ if USE_STATIC_LINK AM_LDFLAGS = -static endif -CLEANFILES = +CLEANFILES = man_MANS = kea-client.8 DISTCLEANFILES = $(man_MANS) @@ -37,15 +37,22 @@ noinst_LTLIBRARIES = libclient.la libclnt_la_SOURCES = libclnt_la_SOURCES += main.cc libclnt_la_SOURCES += client_interface.h client_interface.cc +libclnt_la_SOURCES += executor.cc executor.h +libclnt_la_SOURCES += clnt_process.cc clnt_process.h +libclnt_la_SOURCES += clnt_cfg_mgr.cc clnt_cfg_mgr.h +libclnt_la_SOURCES += clnt_config.cc clnt_config.h sbin_PROGRAMS = kea-client +kea_client_SOURCES = main.cc + kea_client_LDADD = libclnt.la kea_client_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la kea_client_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la kea_client_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la kea_client_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +kea_client_LDADD += $(top_builddir)/src/lib/process/libkea-process.la kea_client_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la kea_client_LDADD += $(BOOST_LIBS) @@ -70,7 +77,6 @@ kea_client_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) kea_clntdir = $(pkgdatadir) -kea_clnt_DATA = dhcp6.spec if GENERATE_PARSER diff --git a/src/bin/client/clnt_cfg_mgr.cc b/src/bin/client/clnt_cfg_mgr.cc new file mode 100644 index 0000000000..afadddb3ec --- /dev/null +++ b/src/bin/client/clnt_cfg_mgr.cc @@ -0,0 +1,47 @@ +// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Paublic +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include + +using namespace isc::data; + +namespace isc { +namespace client { + +ClntCfgMgr::ClntCfgMgr() + :DCfgMgrBase(process::DCfgContextBasePtr(new ClntConfig())) { +} + +std::string +ClntCfgMgr::getConfigSummary(const uint32_t selection) { + return ("not-implemented"); +} + +isc::data::ConstElementPtr +ClntCfgMgr::parse(isc::data::ConstElementPtr config, bool check_only) { + return (ConstElementPtr(new MapElement())); +} + +isc::dhcp::ParserPtr +ClntCfgMgr::createConfigParser(const std::string&, + const isc::data::Element::Position& pos) { + return (isc::dhcp::ParserPtr()); +} + +process::DCfgContextBasePtr +ClntCfgMgr::createNewContext() { + return (process::DCfgContextBasePtr(new ClntConfig())); +} + +void ClntCfgMgr::ensureCurrentAllocated() { + +} + +}; +}; + diff --git a/src/bin/client/clnt_cfg_mgr.h b/src/bin/client/clnt_cfg_mgr.h new file mode 100644 index 0000000000..d6c9f7a557 --- /dev/null +++ b/src/bin/client/clnt_cfg_mgr.h @@ -0,0 +1,82 @@ +// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CLNT_CFG_MGR_H +#define CLNT_CFG_MGR_H + +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace client { + +class ClntCfgMgr : public process::DCfgMgrBase { +public: + + /// @brief Constructor. + ClntCfgMgr(); + + /// @brief Destructor + virtual ~ClntCfgMgr() { } + + /// @brief Convenience method that returns the Control Agent configuration + /// context. + /// + /// @return returns a pointer to the configuration context. + ClntConfigPtr getCtrlAgentCfgContext() { + return (boost::dynamic_pointer_cast(getContext())); + } + + /// @brief Returns configuration summary in the textual format. + /// + /// @param selection Bitfield which describes the parts of the configuration + /// to be returned. This parameter is ignored for the Control Agent. + /// + /// @return Summary of the configuration in the textual format. + virtual std::string getConfigSummary(const uint32_t selection); + +protected: + + virtual void ensureCurrentAllocated(); + + /// @brief Parses configuration of the Control Agent. + /// + /// @param config Pointer to a configuration specified for the agent. + /// @param check_only Boolean flag indicating if this method should + /// only verify correctness of the provided conifiguration. + /// @return Pointer to a result of configuration parsing. + virtual isc::data::ConstElementPtr + parse(isc::data::ConstElementPtr config, bool check_only); + + /// @brief This is no longer used. + /// + /// @throw NotImplemented + /// @return nothing, always throws + virtual isc::dhcp::ParserPtr + createConfigParser(const std::string&, + const isc::data::Element::Position& pos); + + /// @brief Creates a new, blank CtrlAgentCfgContext context. + /// + /// + /// This method is used at the beginning of configuration process to + /// create a fresh, empty copy of a CtrlAgentCfgContext. This new context + /// will be populated during the configuration process and will replace the + /// existing context provided the configuration process completes without + /// error. + /// + /// @return Returns a DCfgContextBasePtr to the new context instance. + virtual process::DCfgContextBasePtr createNewContext(); +}; + +}; +}; + +#endif diff --git a/src/bin/client/clnt_config.cc b/src/bin/client/clnt_config.cc new file mode 100644 index 0000000000..14e169d76c --- /dev/null +++ b/src/bin/client/clnt_config.cc @@ -0,0 +1,29 @@ +// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include + +using namespace isc::data; + +namespace isc { +namespace client { + +ClntConfig::ClntConfig() { + +} + +isc::data::ElementPtr ClntConfig::toElement() const { + ElementPtr map(new MapElement()); + return (map); +} + +std::string ClntConfig::getConfigSummary(const uint32_t selection) const { + return ("not-impl"); +} + +}; +}; diff --git a/src/bin/client/clnt_config.h b/src/bin/client/clnt_config.h new file mode 100644 index 0000000000..97872d29e9 --- /dev/null +++ b/src/bin/client/clnt_config.h @@ -0,0 +1,35 @@ +// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CLNT_CONFIG_H +#define CLNT_CONFIG_H + +#include + +namespace isc { +namespace client { + +class ClntConfig; +typedef boost::shared_ptr ClntConfigPtr; + +class ClntConfig : public process::DCfgContextBase { +public: + ClntConfig(); + + virtual process::BaseConfigPtr clone() const { + return (process::BaseConfigPtr(new ClntConfig(*this))); + } + + virtual isc::data::ElementPtr toElement() const; + + virtual std::string getConfigSummary(const uint32_t selection) const; +}; + + +}; +}; + +#endif diff --git a/src/bin/client/clnt_process.cc b/src/bin/client/clnt_process.cc new file mode 100644 index 0000000000..7dba35262f --- /dev/null +++ b/src/bin/client/clnt_process.cc @@ -0,0 +1,46 @@ +// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include + +using namespace isc::client; +using namespace isc::config; + +ClntProcess::ClntProcess(const char* name, + const asiolink::IOServicePtr& io_service) + :DProcessBase(name, io_service, process::DCfgMgrBasePtr(new ClntCfgMgr())) { + +} + +void ClntProcess::init() { + +} + +void ClntProcess::run() { + std::cout << "Running...." << std::endl; + while (true) { + std::cout << "."; + sleep(1); + } +} + +isc::data::ConstElementPtr +ClntProcess::configure(isc::data::ConstElementPtr config_set, + bool check_only) { + return (createAnswer(3, "not implemented")); +} + +isc::data::ConstElementPtr +ClntProcess::shutdown(isc::data::ConstElementPtr args) { + return (createAnswer(3, "not implemented")); +} + +ClntProcess::~ClntProcess() { +} + diff --git a/src/bin/client/clnt_process.h b/src/bin/client/clnt_process.h new file mode 100644 index 0000000000..2ee57590c9 --- /dev/null +++ b/src/bin/client/clnt_process.h @@ -0,0 +1,51 @@ +// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CLNT_PROCESS_H +#define CLNT_PROCESS_H + +#include + +namespace isc { +namespace client { + + +class ClntProcess : public process::DProcessBase { + + public: + ClntProcess(const char* nane, const asiolink::IOServicePtr& io_service); + + virtual void init(); + + virtual void run(); + + virtual isc::data::ConstElementPtr + configure(isc::data::ConstElementPtr config_set, + bool check_only = false); + + /// @brief Initiates the process's shutdown process. + /// + /// This is last step in the shutdown event callback chain, that is + /// intended to notify the process it is to begin its shutdown process. + /// + /// @param args an Element set of shutdown arguments (if any) that are + /// supported by the process derivation. + /// + /// @return an Element that contains the results of argument processing, + /// consisting of an integer status value (0 means successful, + /// non-zero means failure), and a string explanation of the outcome. + /// + /// @throw DProcessBaseError if an operational error is encountered. + virtual isc::data::ConstElementPtr + shutdown(isc::data::ConstElementPtr args); + + virtual ~ClntProcess(); +}; + +}; +}; + +#endif diff --git a/src/bin/client/d_controller.cc b/src/bin/client/d_controller.cc deleted file mode 100644 index 6f31a83fe8..0000000000 --- a/src/bin/client/d_controller.cc +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_MYSQL -#include -#endif -#ifdef HAVE_PGSQL -#include -#endif -#ifdef HAVE_CQL -#include -#endif -#include - -#include -#include - -using namespace isc::data; -using namespace isc::config; - -namespace isc { -namespace process { - -DControllerBasePtr DControllerBase::controller_; - -// Note that the constructor instantiates the controller's primary IOService. -DControllerBase::DControllerBase(const char* app_name, const char* bin_name) - : app_name_(app_name), bin_name_(bin_name), - verbose_(false), check_only_(false), spec_file_name_(""), - io_service_(new isc::asiolink::IOService()), - io_signal_queue_() { -} - -void -DControllerBase::setController(const DControllerBasePtr& controller) { - if (controller_) { - // This shouldn't happen, but let's make sure it can't be done. - // It represents a programmatic error. - isc_throw (DControllerBaseError, - "Multiple controller instances attempted."); - } - - controller_ = controller; -} - -ConstElementPtr -DControllerBase::parseFile(const std::string&) { - ConstElementPtr elements; - return (elements); -} - -void -DControllerBase::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()); - // rethrow it with an empty message - isc_throw(InvalidUsage, ""); - } - - setProcName(bin_name_); - - if (isCheckOnly()) { - checkConfigOnly(); - return; - } - - // It is important that we set a default logger name because this name - // will be used when the user doesn't provide the logging configuration - // in the Kea configuration file. - isc::dhcp::CfgMgr::instance().setDefaultLoggerName(bin_name_); - - // Logger's default configuration depends on whether we are in the - // verbose mode or not. CfgMgr manages the logger configuration so - // the verbose mode is set for CfgMgr. - isc::dhcp::CfgMgr::instance().setVerbose(verbose_); - - // 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. - Daemon::loggerInit(bin_name_.c_str(), verbose_); - } - - try { - createPIDFile(); - } catch (const dhcp::DaemonPIDExists& ex) { - LOG_FATAL(dctl_logger, DCTL_ALREADY_RUNNING) - .arg(bin_name_).arg(ex.what()); - isc_throw (LaunchError, "Launch Failed: " << ex.what()); - } catch (const std::exception& ex) { - LOG_FATAL(dctl_logger, DCTL_PID_FILE_ERROR) - .arg(app_name_).arg(ex.what()); - isc_throw (LaunchError, "Launch failed: " << ex.what()); - } - - // Log the starting of the service. - LOG_INFO(dctl_logger, DCTL_STARTING) - .arg(app_name_).arg(getpid()).arg(VERSION); - 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(app_name_).arg(ex.what()); - isc_throw (ProcessInitError, - "Application Process initialization failed: " << ex.what()); - } - - LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, DCTL_STANDALONE) - .arg(app_name_); - - // Step 3 is to load configuration from file. - int rcode; - ConstElementPtr comment = parseAnswer(rcode, configFromFile()); - if (rcode != 0) { - LOG_FATAL(dctl_logger, DCTL_CONFIG_FILE_LOAD_FAIL) - .arg(app_name_).arg(comment->stringValue()); - isc_throw (ProcessInitError, "Could Not load configuration file: " - << comment->stringValue()); - } - - // Everything is clear for launch, so start the application's - // event loop. - try { - // Now that we have a proces, we can set up signal handling. - initSignalHandling(); - runProcess(); - } catch (const std::exception& ex) { - LOG_FATAL(dctl_logger, DCTL_PROCESS_FAILED) - .arg(app_name_).arg(ex.what()); - isc_throw (ProcessRunError, - "Application process event loop failed: " << ex.what()); - } - - // All done, so bail out. - LOG_INFO(dctl_logger, DCTL_SHUTDOWN) - .arg(app_name_).arg(getpid()).arg(VERSION); -} - -void -DControllerBase::checkConfigOnly() { - try { - // We need to initialize logging, in case any error - // messages are to be printed. - // This is just a test, so we don't care about lockfile. - setenv("KEA_LOCKFILE_DIR", "none", 0); - isc::dhcp::CfgMgr::instance().setDefaultLoggerName(bin_name_); - isc::dhcp::CfgMgr::instance().setVerbose(verbose_); - Daemon::loggerInit(bin_name_.c_str(), verbose_); - - // Check the syntax first. - std::string config_file = getConfigFile(); - if (config_file.empty()) { - // Basic sanity check: file name must not be empty. - isc_throw(InvalidUsage, "JSON configuration file not specified"); - } - ConstElementPtr whole_config = parseFile(config_file); - if (!whole_config) { - // No fallback to fromJSONFile - isc_throw(InvalidUsage, "No configuration found"); - } - if (verbose_) { - std::cerr << "Syntax check OK" << std::endl; - } - - // Check the logic next. - ConstElementPtr module_config; - module_config = whole_config->get(getAppName()); - if (!module_config) { - isc_throw(InvalidUsage, "Config file " << config_file << - " does not include '" << getAppName() << "' entry"); - } - - // Get an application process object. - initProcess(); - - ConstElementPtr answer = checkConfig(module_config); - int rcode = 0; - answer = parseAnswer(rcode, answer); - if (rcode != 0) { - isc_throw(InvalidUsage, "Error encountered: " - << answer->stringValue()); - } - } catch (const VersionMessage&) { - throw; - } catch (const InvalidUsage&) { - throw; - } catch (const std::exception& ex) { - isc_throw(InvalidUsage, "Syntax check failed with: " << ex.what()); - } - return; -} - -void -DControllerBase::parseArgs(int argc, char* argv[]) -{ - // Iterate over the given command line options. If its a stock option - // ("c" or "d") handle it here. If its a valid custom option, then - // invoke customOption. - int ch; - opterr = 0; - optind = 1; - std::string opts("dvVWc:t:" + getCustomOpts()); - while ((ch = getopt(argc, argv, opts.c_str())) != -1) { - switch (ch) { - case 'd': - // Enables verbose logging. - verbose_ = true; - break; - - case 'v': - // gather Kea version and throw so main() can catch and return - // rather than calling exit() here which disrupts gtest. - isc_throw(VersionMessage, getVersion(false)); - break; - - case 'V': - // gather Kea version and throw so main() can catch and return - // rather than calling exit() here which disrupts gtest. - isc_throw(VersionMessage, getVersion(true)); - break; - - case 'W': - // gather Kea config report and throw so main() can catch and - // return rather than calling exit() here which disrupts gtest. - isc_throw(VersionMessage, isc::detail::getConfigReport()); - break; - - case 'c': - case 't': - // config file name - if (optarg == NULL) { - isc_throw(InvalidUsage, "configuration file name missing"); - } - - setConfigFile(optarg); - - if (ch == 't') { - check_only_ = true; - } - break; - - case '?': { - // We hit an invalid option. - isc_throw(InvalidUsage, "unsupported option: [" - << static_cast(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(ch) << "] " - << (!optarg ? "" : optarg)); - } - break; - } - } - - // There was too much information on the command line. - if (argc > optind) { - isc_throw(InvalidUsage, "extraneous command line information"); - } -} - -bool -DControllerBase::customOption(int /* option */, char* /*optarg*/) -{ - // Default implementation returns false. - return (false); -} - -void -DControllerBase::initProcess() { - LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, DCTL_INIT_PROCESS) - .arg(app_name_); - - // Invoke virtual method to instantiate the application process. - try { - process_.reset(createProcess()); - } catch (const std::exception& ex) { - isc_throw(DControllerBaseError, std::string("createProcess failed: ") - + ex.what()); - } - - // This is pretty unlikely, but will test for it just to be safe.. - if (!process_) { - isc_throw(DControllerBaseError, "createProcess returned NULL"); - } - - // Invoke application's init method (Note this call should throw - // DProcessBaseError if it fails). - process_->init(); -} - -ConstElementPtr -DControllerBase::configFromFile() { - // Rollback any previous staging configuration. For D2, only a - // logger configuration is used here. - isc::dhcp::CfgMgr::instance().rollback(); - // Will hold configuration. - ConstElementPtr module_config; - // Will receive configuration result. - ConstElementPtr answer; - try { - std::string config_file = getConfigFile(); - if (config_file.empty()) { - // Basic sanity check: file name must not be empty. - isc_throw(BadValue, "JSON configuration file not specified. Please " - "use -c command line option."); - } - - // If parseFile returns an empty pointer, then pass the file onto the - // original JSON parser. - ConstElementPtr whole_config = parseFile(config_file); - if (!whole_config) { - // Read contents of the file and parse it as JSON - whole_config = Element::fromJSONFile(config_file, true); - } - - // Let's configure logging before applying the configuration, - // so we can log things during configuration process. - - // Temporary storage for logging configuration - isc::dhcp::SrvConfigPtr storage = - isc::dhcp::CfgMgr::instance().getStagingCfg(); - - // Get 'Logging' element from the config and use it to set up - // logging. If there's no such element, we'll just pass NULL. - Daemon::configureLogger(whole_config->get("Logging"), storage); - - // Extract derivation-specific portion of the configuration. - module_config = whole_config->get(getAppName()); - if (!module_config) { - isc_throw(BadValue, "Config file " << config_file << - " does not include '" << - getAppName() << "' entry."); - } - - answer = updateConfig(module_config); - int rcode = 0; - parseAnswer(rcode, answer); - if (!rcode) { - // Configuration successful, so apply the logging configuration - // to log4cplus. - isc::dhcp::CfgMgr::instance().getStagingCfg()->applyLoggingCfg(); - isc::dhcp::CfgMgr::instance().commit(); - } - - } catch (const std::exception& ex) { - // Rollback logging configuration. - isc::dhcp::CfgMgr::instance().rollback(); - // build an error result - ConstElementPtr error = createAnswer(COMMAND_ERROR, - std::string("Configuration parsing failed: ") + ex.what()); - return (error); - } - - return (answer); -} - - -void -DControllerBase::runProcess() { - LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, DCTL_RUN_PROCESS) - .arg(app_name_); - if (!process_) { - // This should not be possible. - isc_throw(DControllerBaseError, "Process not initialized"); - } - - // Invoke the application process's run method. This may throw - // DProcessBaseError - process_->run(); -} - -// Instance method for handling new config -ConstElementPtr -DControllerBase::updateConfig(ConstElementPtr new_config) { - return (process_->configure(new_config, false)); -} - -// Instance method for checking new config -ConstElementPtr -DControllerBase::checkConfig(ConstElementPtr new_config) { - return (process_->configure(new_config, true)); -} - -ConstElementPtr -DControllerBase::configGetHandler(const std::string&, - ConstElementPtr /*args*/) { - ConstElementPtr config = process_->getCfgMgr()->getContext()->toElement(); - - return (createAnswer(COMMAND_SUCCESS, config)); -} - -ConstElementPtr -DControllerBase::configWriteHandler(const std::string&, - ConstElementPtr args) { - std::string filename; - - if (args) { - if (args->getType() != Element::map) { - return (createAnswer(COMMAND_ERROR, "Argument must be a map")); - } - ConstElementPtr filename_param = args->get("filename"); - if (filename_param) { - if (filename_param->getType() != Element::string) { - return (createAnswer(COMMAND_ERROR, - "passed parameter 'filename' " - "is not a string")); - } - filename = filename_param->stringValue(); - } - } - - if (filename.empty()) { - // filename parameter was not specified, so let's use - // whatever we remember - filename = getConfigFile(); - if (filename.empty()) { - return (createAnswer(COMMAND_ERROR, - "Unable to determine filename." - "Please specify filename explicitly.")); - } - } - - - // Ok, it's time to write the file. - size_t size = 0; - ElementPtr cfg = process_->getCfgMgr()->getContext()->toElement(); - - // Logging storage is messed up in CA. During its configuration (see - // DControllerBase::configFromFile() it calls Daemon::configureLogger() - // that stores the logging info in isc::dhcp::CfgMgr::getStagingCfg(). - // This is later moved to getCurrentCfg() when the configuration is - // commited. All control-agent specific configuration is stored in - // a structure accessible by process_->getCfgMgr()->getContext(). Note - // logging information is not stored there. - // - // As a result, we need to extract the CA configuration from one - // place and logging from another. - ConstElementPtr loginfo = isc::dhcp::CfgMgr::instance().getCurrentCfg()->toElement(); - if (loginfo) { - // If there was a config stored in dhcp::CfgMgr, try to get Logging info from it. - loginfo = loginfo->get("Logging"); - } - if (loginfo) { - // If there is some logging information, add it to our config. - cfg->set("Logging", loginfo); - } - - try { - size = writeConfigFile(filename, cfg); - } catch (const isc::Exception& ex) { - return (createAnswer(COMMAND_ERROR, - std::string("Error during write-config:") - + ex.what())); - } - if (size == 0) { - return (createAnswer(COMMAND_ERROR, - "Error writing configuration to " + filename)); - } - - // Ok, it's time to return the successful response. - ElementPtr params = Element::createMap(); - params->set("size", Element::create(static_cast(size))); - params->set("filename", Element::create(filename)); - - return (createAnswer(CONTROL_RESULT_SUCCESS, "Configuration written to " - + filename + " successful", params)); -} - -ConstElementPtr -DControllerBase::configTestHandler(const std::string&, ConstElementPtr args) { - const int status_code = COMMAND_ERROR; // 1 indicates an error - ConstElementPtr module_config; - std::string app_name = getAppName(); - std::string message; - - // Command arguments are expected to be: - // { "Module": { ... }, "Logging": { ... } } - // The Logging component is technically optional. If it's not supplied - // logging will revert to default logging. - if (!args) { - message = "Missing mandatory 'arguments' parameter."; - } else { - module_config = args->get(app_name); - if (!module_config) { - message = "Missing mandatory '" + app_name + "' parameter."; - } else if (module_config->getType() != Element::map) { - message = "'" + app_name + "' parameter expected to be a map."; - } - } - - if (!message.empty()) { - // Something is amiss with arguments, return a failure response. - ConstElementPtr result = isc::config::createAnswer(status_code, - message); - return (result); - } - - // We are starting the configuration process so we should remove any - // staging configuration that has been created during previous - // configuration attempts. - isc::dhcp::CfgMgr::instance().rollback(); - - // Now we check the server proper. - return (checkConfig(module_config)); -} - -ConstElementPtr -DControllerBase::versionGetHandler(const std::string&, ConstElementPtr) { - ConstElementPtr answer; - - // For version-get put the extended version in arguments - ElementPtr extended = Element::create(getVersion(true)); - ElementPtr arguments = Element::createMap(); - arguments->set("extended", extended); - answer = createAnswer(COMMAND_SUCCESS, getVersion(false), arguments); - return (answer); -} - -ConstElementPtr -DControllerBase::buildReportHandler(const std::string&, ConstElementPtr) { - return (createAnswer(COMMAND_SUCCESS, isc::detail::getConfigReport())); -} - -ConstElementPtr -DControllerBase::shutdownHandler(const std::string&, ConstElementPtr args) { - // Shutdown is universal. If its not that, then try it as - // a custom command supported by the derivation. If that - // doesn't pan out either, than send to it the application - // as it may be supported there. - return (shutdownProcess(args)); -} - -ConstElementPtr -DControllerBase::shutdownProcess(ConstElementPtr args) { - if (process_) { - return (process_->shutdown(args)); - } - - // Not really a failure, but this condition is worth noting. In reality - // it should be pretty hard to cause this. - LOG_WARN(dctl_logger, DCTL_NOT_RUNNING).arg(app_name_); - return (createAnswer(COMMAND_SUCCESS, "Process has not been initialized")); -} - -void -DControllerBase::initSignalHandling() { - /// @todo block everything we don't handle - - // Create our signal queue. - io_signal_queue_.reset(new IOSignalQueue(io_service_)); - - // Install the on-receipt handler - util::SignalSet::setOnReceiptHandler(boost::bind(&DControllerBase:: - osSignalHandler, - this, _1)); - // Register for the signals we wish to handle. - signal_set_.reset(new util::SignalSet(SIGHUP,SIGINT,SIGTERM)); -} - -bool -DControllerBase::osSignalHandler(int signum) { - // Create a IOSignal to propagate the signal to IOService. - io_signal_queue_->pushSignal(signum, boost::bind(&DControllerBase:: - ioSignalHandler, - this, _1)); - return (true); -} - -void -DControllerBase::ioSignalHandler(IOSignalId sequence_id) { - // Pop the signal instance off the queue. This should make us - // the only one holding it, so when we leave it should be freed. - // (note that popSignal will throw if signal is not found, which - // in turn will caught, logged, and swallowed by IOSignal callback - // invocation code.) - IOSignalPtr io_signal = io_signal_queue_->popSignal(sequence_id); - - // Now call virtual signal processing method. - processSignal(io_signal->getSignum()); -} - -void -DControllerBase::processSignal(int signum) { - switch (signum) { - case SIGHUP: - { - LOG_INFO(dctl_logger, DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD) - .arg(signum).arg(getConfigFile()); - int rcode; - ConstElementPtr comment = parseAnswer(rcode, configFromFile()); - if (rcode != 0) { - LOG_ERROR(dctl_logger, DCTL_CFG_FILE_RELOAD_ERROR) - .arg(comment->stringValue()); - } - - break; - } - - case SIGINT: - case SIGTERM: - { - LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, - DCTL_SHUTDOWN_SIGNAL_RECVD).arg(signum); - ElementPtr arg_set; - shutdownHandler(SHUT_DOWN_COMMAND, arg_set); - break; - } - - default: - LOG_WARN(dctl_logger, DCTL_UNSUPPORTED_SIGNAL).arg(signum); - break; - } -} - -void -DControllerBase::usage(const std::string & text) -{ - if (text != "") { - std::cerr << "Usage error: " << text << std::endl; - } - - std::cerr << "Usage: " << bin_name_ << std::endl - << " -v: print version number and exit" << std::endl - << " -V: print extended version information and exit" - << std::endl - << " -W: display the configuration report and exit" - << std::endl - << " -d: optional, verbose output " << std::endl - << " -c : mandatory," - << " specify name of configuration file" << std::endl - << " -t : check the" - << " configuration file and exit" << std::endl; - - // add any derivation specific usage - std::cerr << getUsageText() << std::endl; -} - -DControllerBase::~DControllerBase() { -} - -// Refer to config_report so it will be embedded in the binary -const char* const* d2_config_report = isc::detail::config_report; - -std::string -DControllerBase::getVersion(bool extended) { - std::stringstream tmp; - - tmp << VERSION; - if (extended) { - tmp << std::endl << EXTENDED_VERSION << std::endl; - tmp << "linked with:" << std::endl; - tmp << isc::log::Logger::getVersion() << std::endl; - tmp << getVersionAddendum(); - } - - return (tmp.str()); -} - -}; // namespace isc::process - -}; // namespace isc diff --git a/src/bin/client/d_controller.h b/src/bin/client/d_controller.h deleted file mode 100644 index 63d00c87be..0000000000 --- a/src/bin/client/d_controller.h +++ /dev/null @@ -1,643 +0,0 @@ -// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef D_CONTROLLER_H -#define D_CONTROLLER_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -namespace isc { -namespace process { - -/// @brief Exception thrown when the command line is invalid. -/// Can be used to transmit negative messages too. -class InvalidUsage : public isc::Exception { -public: - InvalidUsage(const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) { }; -}; - -/// @brief Exception used to convey version info upwards. -/// Since command line argument parsing is done as part of -/// DControllerBase::launch(), it uses this exception to propagate -/// version information up to main(), when command line argument -/// -v, -V or -W is given. Can be used to transmit positive messages too. -class VersionMessage : public isc::Exception { -public: - VersionMessage(const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) { }; -}; - -/// @brief Exception thrown when the controller launch fails. -class LaunchError: public isc::Exception { -public: - LaunchError (const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) { }; -}; - -/// @brief Exception thrown when the application process fails. -class ProcessInitError: public isc::Exception { -public: - ProcessInitError (const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) { }; -}; - -/// @brief Exception thrown when the application process encounters an -/// operation in its event loop (i.e. run method). -class ProcessRunError: public isc::Exception { -public: - ProcessRunError (const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) { }; -}; - -/// @brief Exception thrown when the controller encounters an operational error. -class DControllerBaseError : public isc::Exception { -public: - DControllerBaseError (const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) { }; -}; - - -/// @brief Defines a shared pointer to DControllerBase. -class DControllerBase; -typedef boost::shared_ptr DControllerBasePtr; - -/// @brief Application Controller -/// -/// DControllerBase is an abstract singleton which provides the framework and -/// services for managing an application process that implements the -/// DProcessBase interface. It runs the process like a stand-alone, command -/// line driven executable, which must be supplied a configuration file at -/// startup. It coordinates command line argument parsing, process -/// instantiation and initialization, and runtime control through external -/// command and configuration event handling. -/// It creates the IOService instance which is used for runtime control -/// events and passes the IOService into the application process at process -/// creation. -/// It provides the callback handlers for command and configuration events -/// which could be triggered by an external source. Such sources are intended -/// to be registered with and monitored by the controller's IOService such that -/// the appropriate handler can be invoked. -/// -/// DControllerBase provides dynamic configuration file reloading upon receipt -/// of SIGHUP, and graceful shutdown upon receipt of either SIGINT or SIGTERM. -/// -/// NOTE: Derivations must supply their own static singleton instance method(s) -/// for creating and fetching the instance. The base class declares the instance -/// member in order for it to be available for static callback functions. -class DControllerBase : public dhcp::Daemon { -public: - /// @brief Constructor - /// - /// @param app_name is display name of the application under control. This - /// name appears in log statements. - /// @param bin_name is the name of the application executable. - DControllerBase(const char* app_name, const char* bin_name); - - /// @brief Destructor - virtual ~DControllerBase(); - - /// @brief returns Kea version on stdout and exit. - /// redeclaration/redefinition. @ref isc::dhcp::Daemon::getVersion() - std::string getVersion(bool extended); - - /// @brief Acts as the primary entry point into the controller execution - /// and provides the outermost application control logic: - /// - /// 1. parse command line arguments - /// 2. instantiate and initialize the application process - /// 3. load the configuration file - /// 4. initialize signal handling - /// 5. start and wait on the application process event loop - /// 6. exit to the caller - /// - /// It is intended to be called from main() and be given the command line - /// arguments. - /// - /// This function can be run in "test mode". It prevents initialization - /// of module logger. This is used in unit tests which initialize logger - /// in their main function. Such a logger uses environmental variables to - /// control severity, verbosity etc. - /// - /// @param argc is the number of command line arguments supplied - /// @param argv is the array of string (char *) command line arguments - /// @param test_mode is a bool value which indicates if - /// @c DControllerBase::launch should be run in the test mode (if true). - /// This parameter doesn't have default value to force test implementers to - /// enable test mode explicitly. - /// - /// @throw throws one of the following exceptions: - /// InvalidUsage - Indicates invalid command line. - /// ProcessInitError - Failed to create and initialize application - /// process object. - /// ProcessRunError - A fatal error occurred while in the application - /// process event loop. - virtual void launch(int argc, char* argv[], const bool test_mode); - - /// @brief Instance method invoked by the configuration event handler and - /// which processes the actual configuration update. Provides behavioral - /// path for both integrated and stand-alone modes. The current - /// implementation will merge the configuration update into the existing - /// configuration and then invoke the application process' configure method. - /// - /// @param new_config is the new configuration - /// - /// @return returns an Element that contains the results of configuration - /// update composed of an integer status value (0 means successful, - /// non-zero means failure), and a string explanation of the outcome. - virtual isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr - new_config); - - /// @brief Instance method invoked by the configuration event handler and - /// which processes the actual configuration check. Provides behavioral - /// path for both integrated and stand-alone modes. The current - /// implementation will merge the configuration update into the existing - /// configuration and then invoke the application process' configure method - /// with a final rollback. - /// - /// @param new_config is the new configuration - /// - /// @return returns an Element that contains the results of configuration - /// update composed of an integer status value (0 means successful, - /// non-zero means failure), and a string explanation of the outcome. - virtual isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr - new_config); - - /// @brief Reconfigures the process from a configuration file - /// - /// By default the file is assumed to be a JSON text file whose contents - /// include at least: - /// - /// @code - /// { "": {} - /// - /// # Logging element is optional - /// ,"Logging": {(input); // just tu shut up the unused parameter warning - isc::data::ConstElementPtr elements; - return (elements); - } - - /// @brief Instantiates the application process and then initializes it. - /// This is the second step taken during launch, following successful - /// command line parsing. It is used to invoke the derivation-specific - /// implementation of createProcess, following by an invoking of the - /// newly instantiated process's init method. - /// - /// @throw throws DControllerBaseError or indirectly DProcessBaseError - /// if there is a failure creating or initializing the application process. - void initProcess(); - - /// @brief Invokes the application process's event loop,(DBaseProcess::run). - /// It is called during launch only after successfully completing the - /// requested setup: command line parsing, application initialization, - /// and session establishment (if not stand-alone). - /// The process event loop is expected to only return upon application - /// shutdown either in response to the shutdown command or due to an - /// unrecoverable error. - /// - // @throw throws DControllerBaseError or indirectly DProcessBaseError - void runProcess(); - - /// @brief Initiates shutdown procedure. This method is invoked - /// by executeCommand in response to the shutdown command. It will invoke - /// the application process's shutdown method which causes the process to - /// to begin its shutdown process. - /// - /// Note, it is assumed that the process of shutting down is neither - /// instantaneous nor synchronous. This method does not "block" waiting - /// until the process has halted. Rather it is used to convey the - /// need to shutdown. A successful return indicates that the shutdown - /// has successfully commenced, but does not indicate that the process - /// has actually exited. - /// - /// @return returns an Element that contains the results of shutdown - /// command composed of an integer status value (0 means successful, - /// non-zero means failure), and a string explanation of the outcome. - /// - /// @param args is a set of derivation-specific arguments (if any) - /// for the shutdown command. - isc::data::ConstElementPtr shutdownProcess(isc::data::ConstElementPtr args); - - /// @brief Initializes signal handling - /// - /// This method configures the controller to catch and handle signals. - /// It instantiates an IOSignalQueue, registers @c osSignalHandler() as - /// the SignalSet "on-receipt" handler, and lastly instantiates a SignalSet - /// which listens for SIGHUP, SIGINT, and SIGTERM. - void initSignalHandling(); - - /// @brief Handler for processing OS-level signals - /// - /// This method is installed as the SignalSet "on-receipt" handler. Upon - /// invocation, it uses the controller's IOSignalQueue to schedule an - /// IOSignal with for the given signal value. - /// - /// @param signum OS signal value (e.g. SIGINT, SIGUSR1 ...) to received - /// - /// @return SignalSet "on-receipt" handlers are required to return a - /// boolean indicating if the OS signal has been processed (true) or if it - /// should be saved for deferred processing (false). Currently this - /// method processes all received signals, so it always returns true. - bool osSignalHandler(int signum); - - /// @brief Handler for processing IOSignals - /// - /// This method is supplied as the callback when IOSignals are scheduled. - /// It fetches the IOSignal for the given sequence_id and then invokes - /// the virtual method, @c processSignal() passing it the signal value - /// obtained from the IOSignal. This allows derivations to supply a - /// custom signal processing method, while ensuring IOSignalQueue - /// integrity. - /// - /// @param sequence_id id of the IOSignal instance "received" - void ioSignalHandler(IOSignalId sequence_id); - - /// @brief Fetches the current process - /// - /// @return a pointer to the current process instance. - DProcessBasePtr getProcess() { - return (process_); - } - - /// @brief Prints the program usage text to std error. - /// - /// @param text is a string message which will preceded the usage text. - /// This is intended to be used for specific usage violation messages. - void usage(const std::string& text); - - /// @brief Fetches text containing additional version specifics - /// - /// This method is provided so derivations can append any additional - /// desired information such as library dependencies to the extended - /// version text returned when DControllerBase::getVersion(true) is - /// invoked. - /// @return a string containing additonal version info - virtual std::string getVersionAddendum() { return (""); } - -private: - /// @brief Name of the service under control. - /// This name is used as the configuration module name and appears in log - /// statements. - std::string app_name_; - - /// @brief Name of the service executable. - /// By convention this matches the executable name. It is also used to - /// establish the logger name. - std::string bin_name_; - - /// @brief Indicates if the verbose logging mode is enabled. - bool verbose_; - - /// @brief Indicates if the check only mode for the configuration - /// is enabled (usually specified by the command line -t argument). - bool check_only_; - - /// @brief The absolute file name of the JSON spec file. - std::string spec_file_name_; - - /// @brief Pointer to the instance of the process. - /// - /// This is required for config and command handlers to gain access to - /// the process - DProcessBasePtr process_; - - /// @brief Shared pointer to an IOService object, used for ASIO operations. - asiolink::IOServicePtr io_service_; - - /// @brief Queue for propagating caught signals to the IOService. - IOSignalQueuePtr io_signal_queue_; - - /// @brief Singleton instance value. - static DControllerBasePtr controller_; - -// DControllerTest is named a friend class to facilitate unit testing while -// leaving the intended member scopes intact. -friend class DControllerTest; -}; - -}; // namespace isc::process -}; // namespace isc - -#endif diff --git a/src/bin/client/executor.cc b/src/bin/client/executor.cc new file mode 100644 index 0000000000..359b224fae --- /dev/null +++ b/src/bin/client/executor.cc @@ -0,0 +1,47 @@ +// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include + +using namespace isc::process; + +namespace isc { +namespace client { + +const char* Executor::name_("kea-client"); + +Executor::Executor() + : DControllerBase(name_, name_) { +} + +DControllerBasePtr& +Executor::instance() { + // If the instance hasn't been created yet, create it. Note this method + // must use the base class singleton instance methods. + if (!getController()) { + DControllerBasePtr controller_ptr(new Executor()); + setController(controller_ptr); + } + + return (getController()); +} + +DProcessBase* Executor::createProcess() { + // Instantiate and return an instance of the D2 application process. Note + // that the process is passed the controller's io_service. + return (new ClntProcess(getAppName().c_str(), getIOService())); +} + + +isc::data::ConstElementPtr +Executor::parseFile(const std::string& file_name) { + return (config::createAnswer(3, "sorry, not implemented")); +} + +}; +}; diff --git a/src/bin/client/executor.h b/src/bin/client/executor.h new file mode 100644 index 0000000000..2207e30604 --- /dev/null +++ b/src/bin/client/executor.h @@ -0,0 +1,76 @@ +// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef CLNT_EXECUTOR_H +#define CLNT_EXECUTOR_H + + +// One of the client requirements is to keep its footprint small. We can't use +// the DControllerBase until the mess with its dependencies is sorted out. +// See all the crap in libkea_process_la_LIBADD in src/lib/process/Makefile.am +// and make your own opinion. +#include + +namespace isc { +namespace client { + +/// @brief Process Controller for the client process +/// +/// This class is the client specific derivation of DControllerBase. It +/// creates and manages an instance of the client application process, +/// D2Process. +/// +/// @todo Currently, this class provides only the minimum required specialized +/// behavior to run the client service. It may very well expand as the +/// service implementation evolves. Some thought was given to making +/// DControllerBase a templated class but the labor savings versus the +/// potential number of virtual methods which may be overridden didn't seem +/// worth the clutter at this point. +class Executor : public process::DControllerBase { +public: + /// @brief Static singleton instance method. This method returns the + /// base class singleton instance member. It instantiates the singleton + /// and sets the base class instance member upon first invocation. + /// + /// @return returns the pointer reference to the singleton instance. + static process::DControllerBasePtr& instance(); + + /// @brief Destructor. + virtual ~Executor() {} + + static const char* name_; + +private: + /// @brief Creates an instance of the DHCP-DDNS specific application + /// process. This method is invoked during the process initialization + /// step of the controller launch. + /// + /// @return returns a DProcessBase* to the application process created. + /// Note the caller is responsible for destructing the process. This + /// is handled by the base class, which wraps this pointer with a smart + /// pointer. + virtual process::DProcessBase* createProcess(); + + ///@brief Parse a given file into Elements + /// + /// Uses bison parsing to parse a JSON configuration file into an + /// a element map. + /// + /// @param file_name pathname of the file to parse + /// + /// @return pointer to the map of elements created + /// @throw BadValue if the file is empty + virtual isc::data::ConstElementPtr parseFile(const std::string& file_name); + + /// @brief Constructor is declared private to maintain the integrity of + /// the singleton instance. + Executor(); +}; + +}; // namespace isc::d2 +}; // namespace isc + +#endif diff --git a/src/bin/client/main.cc b/src/bin/client/main.cc index d724bad66f..296fb1edbd 100644 --- a/src/bin/client/main.cc +++ b/src/bin/client/main.cc @@ -5,7 +5,8 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include -#include +#include +#include #include #include @@ -25,8 +26,12 @@ int main(int argc, char* argv[]) { // Launch the controller passing in command line arguments. // Exit program with the controller's return code. try { + + //ClntConfig cfg; + //std::cout << cfg.toElement()->str() << std::endl; + // Instantiate/fetch the client application controller singleton. - DControllerBasePtr& controller = ClntController::instance(); + DControllerBasePtr& controller = Executor::instance(); // 'false' value disables test mode. controller->launch(argc, argv, false);