summaryrefslogtreecommitdiffstats
path: root/src/bin/agent/ca_process.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/agent/ca_process.cc')
-rw-r--r--src/bin/agent/ca_process.cc238
1 files changed, 238 insertions, 0 deletions
diff --git a/src/bin/agent/ca_process.cc b/src/bin/agent/ca_process.cc
new file mode 100644
index 0000000..0bc8d68
--- /dev/null
+++ b/src/bin/agent/ca_process.cc
@@ -0,0 +1,238 @@
+// Copyright (C) 2016-2023 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 <config.h>
+#include <asiolink/asio_wrapper.h>
+#include <agent/ca_process.h>
+#include <agent/ca_controller.h>
+#include <agent/ca_response_creator_factory.h>
+#include <agent/ca_log.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
+#include <cc/command_interpreter.h>
+#include <config/timeouts.h>
+#include <boost/pointer_cast.hpp>
+
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::http;
+using namespace isc::process;
+
+
+namespace isc {
+namespace agent {
+
+CtrlAgentProcess::CtrlAgentProcess(const char* name,
+ const asiolink::IOServicePtr& io_service)
+ : DProcessBase(name, io_service, DCfgMgrBasePtr(new CtrlAgentCfgMgr())),
+ http_listeners_() {
+}
+
+CtrlAgentProcess::~CtrlAgentProcess() {
+}
+
+void
+CtrlAgentProcess::init() {
+}
+
+void
+CtrlAgentProcess::run() {
+ LOG_INFO(agent_logger, CTRL_AGENT_STARTED).arg(VERSION);
+
+ try {
+ // Register commands.
+ CtrlAgentControllerPtr controller =
+ boost::dynamic_pointer_cast<CtrlAgentController>(
+ CtrlAgentController::instance());
+ controller->registerCommands();
+
+ // Let's process incoming data or expiring timers in a loop until
+ // shutdown condition is detected.
+ while (!shouldShutdown()) {
+ // Remove unused listeners within the main loop because new listeners
+ // are created in within a callback method. This avoids removal the
+ // listeners within a callback.
+ garbageCollectListeners(1);
+ runIO();
+ }
+ // Done so removing all listeners.
+ garbageCollectListeners(0);
+ stopIOService();
+ } catch (const std::exception& ex) {
+ LOG_FATAL(agent_logger, CTRL_AGENT_FAILED).arg(ex.what());
+ try {
+ stopIOService();
+ } catch (...) {
+ // Ignore double errors
+ }
+ isc_throw(DProcessBaseError,
+ "Process run method failed: " << ex.what());
+ }
+
+ try {
+ // Deregister commands.
+ CtrlAgentControllerPtr controller =
+ boost::dynamic_pointer_cast<CtrlAgentController>(
+ CtrlAgentController::instance());
+ controller->deregisterCommands();
+ } catch (const std::exception&) {
+ // What to do? Simply ignore...
+ }
+
+ LOG_DEBUG(agent_logger, isc::log::DBGLVL_START_SHUT, CTRL_AGENT_RUN_EXIT);
+}
+
+size_t
+CtrlAgentProcess::runIO() {
+ size_t cnt = getIoService()->get_io_service().poll();
+ if (!cnt) {
+ cnt = getIoService()->get_io_service().run_one();
+ }
+ return (cnt);
+}
+
+isc::data::ConstElementPtr
+CtrlAgentProcess::shutdown(isc::data::ConstElementPtr /*args*/) {
+ setShutdownFlag(true);
+ return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
+ "Control Agent is shutting down"));
+}
+
+isc::data::ConstElementPtr
+CtrlAgentProcess::configure(isc::data::ConstElementPtr config_set,
+ bool check_only) {
+ // System reconfiguration often poses an interesting issue whereby the
+ // configuration parsing is successful, but an attempt to use a new
+ // configuration is not. This will leave us in the inconsistent state
+ // when the configuration is in fact only partially applied and the
+ // system's ability to operate is impaired. The use of C++ lambda is
+ // a way to resolve this problem by injecting the code to the
+ // simpleParseConfig which performs an attempt to open new instance
+ // of the listener (if required). The lambda code will throw an
+ // exception if it fails and cause the simpleParseConfig to rollback
+ // configuration changes and report an error.
+ ConstElementPtr answer = getCfgMgr()->simpleParseConfig(config_set,
+ check_only,
+ [this]() {
+ ConfigPtr base_ctx = getCfgMgr()->getContext();
+ CtrlAgentCfgContextPtr
+ ctx = boost::dynamic_pointer_cast<CtrlAgentCfgContext>(base_ctx);
+
+ if (!ctx) {
+ isc_throw(Unexpected, "Internal logic error: bad context type");
+ }
+
+ /// @todo: If the parameter is a hostname, we need to resolve it.
+ IOAddress server_address("::");
+ try {
+ server_address = IOAddress(ctx->getHttpHost());
+
+ } catch (const IOError& e) {
+ isc_throw(BadValue, "Failed to convert " << ctx->getHttpHost()
+ << " to IP address:" << e.what());
+ }
+
+ uint16_t server_port = ctx->getHttpPort();
+ bool use_https = false;
+
+ // Only open a new listener if the configuration has changed.
+ if (http_listeners_.empty() ||
+ (http_listeners_.back()->getLocalAddress() != server_address) ||
+ (http_listeners_.back()->getLocalPort() != server_port)) {
+ // Create a TLS context.
+ TlsContextPtr tls_context;
+ // When TLS is enabled configure it.
+ if (!ctx->getCertFile().empty()) {
+ TlsContext::configure(tls_context,
+ TlsRole::SERVER,
+ ctx->getTrustAnchor(),
+ ctx->getCertFile(),
+ ctx->getKeyFile(),
+ ctx->getCertRequired());
+ use_https = true;
+ }
+
+ // Create response creator factory first. It will be used to
+ // generate response creators. Each response creator will be
+ // used to generate answer to specific request.
+ HttpResponseCreatorFactoryPtr rcf(new CtrlAgentResponseCreatorFactory());
+
+ // Create http listener. It will open up a TCP socket and be
+ // prepared to accept incoming connection.
+ HttpListenerPtr http_listener
+ (new HttpListener(*getIoService(), server_address,
+ server_port, tls_context, rcf,
+ HttpListener::RequestTimeout(TIMEOUT_AGENT_RECEIVE_COMMAND),
+ HttpListener::IdleTimeout(TIMEOUT_AGENT_IDLE_CONNECTION_TIMEOUT)));
+
+ // Instruct the http listener to actually open socket, install
+ // callback and start listening.
+ http_listener->start();
+
+ // The new listener is running so add it to the collection of
+ // active listeners. The next step will be to remove all other
+ // active listeners, but we do it inside the main process loop.
+ http_listeners_.push_back(http_listener);
+ }
+
+ // Ok, seems we're good to go.
+ if (use_https) {
+ LOG_INFO(agent_logger, CTRL_AGENT_HTTPS_SERVICE_STARTED)
+ .arg(server_address.toText()).arg(server_port);
+ } else {
+ LOG_INFO(agent_logger, CTRL_AGENT_HTTP_SERVICE_STARTED)
+ .arg(server_address.toText()).arg(server_port);
+ }
+ });
+
+ int rcode = 0;
+ config::parseAnswer(rcode, answer);
+ return (answer);
+}
+
+void
+CtrlAgentProcess::garbageCollectListeners(size_t leaving) {
+ // We expect only one active listener. If there are more (most likely 2),
+ // it means we have just reconfigured the server and need to shut down all
+ // listeners except the most recently added.
+ if (http_listeners_.size() > leaving) {
+ // Stop no longer used listeners.
+ for (auto l = http_listeners_.begin();
+ l != http_listeners_.end() - leaving;
+ ++l) {
+ (*l)->stop();
+ }
+ // We have stopped listeners but there may be some pending handlers
+ // related to these listeners. Need to invoke these handlers.
+ getIoService()->get_io_service().poll();
+ // Finally, we're ready to remove no longer used listeners.
+ http_listeners_.erase(http_listeners_.begin(),
+ http_listeners_.end() - leaving);
+ }
+}
+
+
+CtrlAgentCfgMgrPtr
+CtrlAgentProcess::getCtrlAgentCfgMgr() {
+ return (boost::dynamic_pointer_cast<CtrlAgentCfgMgr>(getCfgMgr()));
+}
+
+ConstHttpListenerPtr
+CtrlAgentProcess::getHttpListener() const {
+ // Return the most recent listener or null.
+ return (http_listeners_.empty() ? ConstHttpListenerPtr() :
+ http_listeners_.back());
+}
+
+bool
+CtrlAgentProcess::isListening() const {
+ // If there are is a listener, we're listening.
+ return (static_cast<bool>(getHttpListener()));
+}
+
+} // namespace isc::agent
+} // namespace isc