Kea 3.1.1
ca_process.cc
Go to the documentation of this file.
1// Copyright (C) 2016-2025 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
10#include <agent/ca_process.h>
11#include <agent/ca_controller.h>
13#include <agent/ca_log.h>
14#include <asiolink/io_address.h>
15#include <asiolink/io_error.h>
17#include <config/timeouts.h>
18#include <util/filesystem.h>
19#include <boost/pointer_cast.hpp>
20
21using namespace isc::asiolink;
22using namespace isc::config;
23using namespace isc::data;
24using namespace isc::http;
25using namespace isc::process;
26using namespace isc::util::file;
27
28namespace isc {
29namespace agent {
30
32 const asiolink::IOServicePtr& io_service)
33 : DProcessBase(name, io_service, DCfgMgrBasePtr(new CtrlAgentCfgMgr())) {
34}
35
38
39void
42
43void
46
48
51 }
52
53 try {
54 // Register commands.
55 CtrlAgentControllerPtr controller =
56 boost::dynamic_pointer_cast<CtrlAgentController>(
58 controller->registerCommands();
59
60 // Let's process incoming data or expiring timers in a loop until
61 // shutdown condition is detected.
62 while (!shouldShutdown()) {
63 runIO();
64 }
65 // Done so removing all listeners.
68 } catch (const std::exception& ex) {
70 try {
72 } catch (...) {
73 // Ignore double errors
74 }
76 "Process run method failed: " << ex.what());
77 }
78
79 try {
80 // Deregister commands.
81 CtrlAgentControllerPtr controller =
82 boost::dynamic_pointer_cast<CtrlAgentController>(
84 controller->deregisterCommands();
85 } catch (const std::exception&) {
86 // What to do? Simply ignore...
87 }
88
90}
91
92size_t
93CtrlAgentProcess::runIO() {
94 // Handle events registered by hooks using external IOService objects.
96 size_t cnt = getIOService()->poll();
97 if (!cnt) {
98 cnt = getIOService()->runOne();
99 }
100 return (cnt);
101}
102
105 setShutdownFlag(true);
107 "Control Agent is shutting down"));
108}
109
112 bool check_only) {
113 // System reconfiguration often poses an interesting issue whereby the
114 // configuration parsing is successful, but an attempt to use a new
115 // configuration is not. This will leave us in the inconsistent state
116 // when the configuration is in fact only partially applied and the
117 // system's ability to operate is impaired. The use of C++ lambda is
118 // a way to resolve this problem by injecting the code to the
119 // simpleParseConfig which performs an attempt to open new instance
120 // of the listener (if required). The lambda code will throw an
121 // exception if it fails and cause the simpleParseConfig to rollback
122 // configuration changes and report an error.
123 ConstElementPtr answer = getCfgMgr()->simpleParseConfig(config_set,
124 check_only,
125 [this]() {
126 ConfigPtr base_ctx = getCfgMgr()->getContext();
128 ctx = boost::dynamic_pointer_cast<CtrlAgentCfgContext>(base_ctx);
129
130 if (!ctx) {
131 isc_throw(Unexpected, "Internal logic error: bad context type");
132 }
133
135 IOAddress server_address("::");
136 try {
137 server_address = IOAddress(ctx->getHttpHost());
138
139 } catch (const IOError& e) {
140 isc_throw(BadValue, "Failed to convert " << ctx->getHttpHost()
141 << " to IP address:" << e.what());
142 }
143
144 uint16_t server_port = ctx->getHttpPort();
145
146 // Search for the specific connection and reuse the existing one if found.
147 auto it = sockets_.find(std::make_pair(server_address, server_port));
148 if (it != sockets_.end()) {
149 auto listener = it->second->listener_;
150 if (listener) {
151 // Reconfig keeping the same address and port.
152 if (listener->getTlsContext()) {
153 if (ctx->getTrustAnchor().empty()) {
154 // Can not switch from HTTPS to HTTP
156 .arg(server_address.toText())
157 .arg(server_port);
159 "Can not switch from HTTPS to HTTP sockets using the same address and port.");
160 } else {
161 // Apply TLS settings each time.
162 TlsContextPtr tls_context;
163 TlsContext::configure(tls_context,
165 ctx->getTrustAnchor(),
166 ctx->getCertFile(),
167 ctx->getKeyFile(),
168 ctx->getCertRequired());
169 // Overwrite the authentication setup and the http headers in the response creator config.
170 it->second->config_->setAuthConfig(ctx->getAuthConfig());
171 it->second->config_->setHttpHeaders(ctx->getHttpHeaders());
172 listener->setTlsContext(tls_context);
174 .arg(server_address.toText())
175 .arg(server_port);
176 }
177 } else {
178 if (!ctx->getTrustAnchor().empty()) {
179 // Can not switch from HTTP to HTTPS
181 .arg(server_address.toText())
182 .arg(server_port);
184 "Can not switch from HTTP to HTTPS sockets using the same address and port.");
185 } else {
186 // Overwrite the authentication setup and the http headers in the response creator config.
187 it->second->config_->setAuthConfig(ctx->getAuthConfig());
188 it->second->config_->setHttpHeaders(ctx->getHttpHeaders());
190 .arg(server_address.toText())
191 .arg(server_port);
192 }
193 }
194 }
195 // If the connection can be reused, mark it as usable.
196 it->second->usable_ = true;
197 } else {
198
199 // Connection not found so it needs to be created.
200 // When TLS is enabled configure it.
201 bool use_https = false;
202 TlsContextPtr tls_context;
203 if (!ctx->getCertFile().empty()) {
204 TlsContext::configure(tls_context,
206 ctx->getTrustAnchor(),
207 ctx->getCertFile(),
208 ctx->getKeyFile(),
209 ctx->getCertRequired());
210 use_https = true;
211 }
212
213 // Create response creator factory first. It will be used to
214 // generate response creators. Each response creator will be
215 // used to generate answer to specific request.
217
218 // Create HTTP listener. It will open up a TCP socket and be
219 // prepared to accept incoming connection.
220 HttpListenerPtr http_listener
222 server_address,
223 server_port,
224 tls_context,
225 rcf,
228
229 // Instruct the HTTP listener to actually open socket, install
230 // callback and start listening.
231 http_listener->start();
232
233 HttpSocketInfoPtr socket_info(new HttpSocketInfo());
234 socket_info->config_ = ctx;
235 socket_info->listener_ = http_listener;
236
237 sockets_[std::make_pair(server_address, server_port)] = socket_info;
238
239 // Ok, seems we're good to go.
240 if (use_https) {
242 .arg(server_address.toText())
243 .arg(server_port);
244 } else {
246 .arg(server_address.toText())
247 .arg(server_port);
248 }
249 }
250
251 auto copy = sockets_;
252 for (auto const& data : copy) {
253 if (data.second->usable_) {
254 // If the connection can be used (just created) or reused, keep it
255 // in the list and clear the flag. It will be marked again on next
256 // configuration event if needed.
257 data.second->usable_ = false;
258 } else {
259 // If the connection can not be reused, stop it and remove it from the list.
260 data.second->listener_->stop();
261 auto it2 = sockets_.find(std::make_pair(data.second->config_->getHttpHost(),
262 data.second->config_->getHttpPort()));
263 if (it2 != sockets_.end()) {
264 sockets_.erase(it2);
265 }
266 }
267 }
268 });
269
270 int rcode = 0;
271 config::parseAnswer(rcode, answer);
272
274 try {
275 // Handle events registered by hooks using external IOService objects.
277 } catch (const std::exception& ex) {
278 std::ostringstream err;
279 err << "Error initializing hooks: "
280 << ex.what();
282 }
283
284 return (answer);
285}
286
287void
289 for (auto const& data : sockets_) {
290 data.second->listener_->stop();
291 }
292 sockets_.clear();
293 // We have stopped listeners but there may be some pending handlers
294 // related to these listeners. Need to invoke these handlers.
295 try {
296 getIOService()->poll();
297 } catch (...) {
298 }
299}
300
303 return (boost::dynamic_pointer_cast<CtrlAgentCfgMgr>(getCfgMgr()));
304}
305
308 // Return the most recent listener or null.
309 if (info) {
310 auto it = sockets_.find(std::make_pair(info->config_->getHttpHost(), info->config_->getHttpPort()));
311 if (it != sockets_.end()) {
312 return (it->second->listener_);
313 }
314 } else if (sockets_.size()) {
315 return (sockets_.begin()->second->listener_);
316 }
317 return (ConstHttpListenerPtr());
318}
319
320bool
322 // If there are is a listener, we're listening.
323 return (static_cast<bool>(getHttpListener()));
324}
325
326} // namespace isc::agent
327} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown when an unexpected error condition occurs.
Ctrl Agent Configuration Manager.
Definition ca_cfg_mgr.h:271
static process::DControllerBasePtr & instance()
Static singleton instance method.
bool isListening() const
Checks if the process is listening to the HTTP requests.
isc::http::ConstHttpListenerPtr getHttpListener(HttpSocketInfoPtr info=HttpSocketInfoPtr()) const
Returns a const pointer to the HTTP listener.
virtual isc::data::ConstElementPtr shutdown(isc::data::ConstElementPtr args)
Initiates the process's shutdown process.
virtual ~CtrlAgentProcess()
Destructor.
Definition ca_process.cc:36
CtrlAgentProcess(const char *name, const asiolink::IOServicePtr &io_service)
Constructor.
Definition ca_process.cc:31
virtual void init()
Initialize the Control Agent process.
Definition ca_process.cc:40
CtrlAgentCfgMgrPtr getCtrlAgentCfgMgr()
Returns a pointer to the configuration manager.
virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr config_set, bool check_only=false)
Processes the given configuration.
void closeCommandSockets()
Close http control sockets.
virtual void run()
Implements the process's event loop.
Definition ca_process.cc:44
HTTP response creator factory for Control Agent.
HTTP listener.
Definition listener.h:52
Exception thrown if the process encountered an operational error.
Definition d_process.h:24
void setShutdownFlag(bool value)
Sets the process shut down flag to the given value.
Definition d_process.h:162
DProcessBase(const char *app_name, asiolink::IOServicePtr io_service, DCfgMgrBasePtr cfg_mgr)
Constructor.
Definition d_process.h:87
void stopIOService()
Convenience method for stopping IOservice processing.
Definition d_process.h:184
bool shouldShutdown() const
Checks if the process has been instructed to shut down.
Definition d_process.h:155
asiolink::IOServicePtr & getIOService()
Fetches the controller's IOService.
Definition d_process.h:176
DCfgMgrBasePtr & getCfgMgr()
Fetches the process's configuration manager.
Definition d_process.h:191
static bool shouldEnforceSecurity()
Indicates security checks should be enforced.
This file contains several functions and constants that are used for handling commands and responses ...
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
#define LOG_FATAL(LOGGER, MESSAGE)
Macro to conveniently test fatal output and log it.
Definition macros.h:38
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
const isc::log::MessageID CTRL_AGENT_HTTPS_SERVICE_UPDATED
Definition ca_messages.h:21
boost::shared_ptr< CtrlAgentCfgContext > CtrlAgentCfgContextPtr
Pointer to a configuration context.
Definition ca_cfg_mgr.h:24
const isc::log::MessageID CTRL_AGENT_HTTP_SERVICE_REUSE_FAILED
Definition ca_messages.h:22
const isc::log::MessageID CTRL_AGENT_HTTP_SERVICE_UPDATED
Definition ca_messages.h:24
const isc::log::MessageID CTRL_AGENT_HTTP_SERVICE_STARTED
Definition ca_messages.h:23
const isc::log::MessageID CTRL_AGENT_STARTED
Definition ca_messages.h:28
isc::log::Logger agent_logger("ctrl-agent")
Control Agent logger.
Definition ca_log.h:18
boost::shared_ptr< CtrlAgentCfgMgr > CtrlAgentCfgMgrPtr
Defines a shared pointer to CtrlAgentCfgMgr.
Definition ca_cfg_mgr.h:329
boost::shared_ptr< CtrlAgentController > CtrlAgentControllerPtr
boost::shared_ptr< HttpSocketInfo > HttpSocketInfoPtr
Pointer to a HttpSocketInfo object.
Definition ca_process.h:39
const isc::log::MessageID CTRL_AGENT_HTTPS_SERVICE_STARTED
Definition ca_messages.h:20
const isc::log::MessageID CTRL_AGENT_RUN_EXIT
Definition ca_messages.h:26
const isc::log::MessageID CTRL_AGENT_SECURITY_CHECKS_DISABLED
Definition ca_messages.h:27
const isc::log::MessageID CTRL_AGENT_HTTPS_SERVICE_REUSE_FAILED
Definition ca_messages.h:19
const isc::log::MessageID CTRL_AGENT_IS_DEPRECATED
Definition ca_messages.h:25
const isc::log::MessageID CTRL_AGENT_FAILED
Definition ca_messages.h:18
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Parses a standard config/command level answer and returns arguments or text status code.
constexpr long TIMEOUT_AGENT_IDLE_CONNECTION_TIMEOUT
Timeout for the idle connection to be closed.
Definition timeouts.h:24
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
ConstElementPtr createAnswer(const int status_code, const std::string &text, const ConstElementPtr &arg)
Creates a standard config/command level answer message.
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
constexpr long TIMEOUT_AGENT_RECEIVE_COMMAND
Timeout for the Control Agent to receive command over the RESTful interface.
Definition timeouts.h:21
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition data.cc:1420
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
boost::shared_ptr< const HttpListener > ConstHttpListenerPtr
Pointer to the const HttpListener.
Definition listener.h:159
boost::shared_ptr< HttpListener > HttpListenerPtr
Pointer to the HttpListener.
Definition listener.h:156
boost::shared_ptr< HttpResponseCreatorFactory > HttpResponseCreatorFactoryPtr
Pointer to the HttpResponseCreatorFactory.
const int DBGLVL_START_SHUT
This is given a value of 0 as that is the level selected if debugging is enabled without giving a lev...
boost::shared_ptr< DCfgMgrBase > DCfgMgrBasePtr
Defines a shared pointer to DCfgMgrBase.
Definition d_cfg_mgr.h:247
boost::shared_ptr< ConfigBase > ConfigPtr
Non-const pointer to the ConfigBase.
Defines the logger used by the top-level component of kea-lfc.
Structure used to store HTTP/HTTPS connection data.
Definition ca_process.h:20
Idle connection timeout.
Definition listener.h:67
HTTP request timeout value.
Definition listener.h:56