Kea 2.7.7
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 <boost/pointer_cast.hpp>
19
20using namespace isc::asiolink;
21using namespace isc::config;
22using namespace isc::data;
23using namespace isc::http;
24using namespace isc::process;
25
26
27namespace isc {
28namespace agent {
29
31 const asiolink::IOServicePtr& io_service)
32 : DProcessBase(name, io_service, DCfgMgrBasePtr(new CtrlAgentCfgMgr())) {
33}
34
37
38void
41
42void
45
46 try {
47 // Register commands.
48 CtrlAgentControllerPtr controller =
49 boost::dynamic_pointer_cast<CtrlAgentController>(
51 controller->registerCommands();
52
53 // Let's process incoming data or expiring timers in a loop until
54 // shutdown condition is detected.
55 while (!shouldShutdown()) {
56 runIO();
57 }
58 // Done so removing all listeners.
61 } catch (const std::exception& ex) {
63 try {
65 } catch (...) {
66 // Ignore double errors
67 }
69 "Process run method failed: " << ex.what());
70 }
71
72 try {
73 // Deregister commands.
74 CtrlAgentControllerPtr controller =
75 boost::dynamic_pointer_cast<CtrlAgentController>(
77 controller->deregisterCommands();
78 } catch (const std::exception&) {
79 // What to do? Simply ignore...
80 }
81
83}
84
85size_t
86CtrlAgentProcess::runIO() {
87 // Handle events registered by hooks using external IOService objects.
89 size_t cnt = getIOService()->poll();
90 if (!cnt) {
91 cnt = getIOService()->runOne();
92 }
93 return (cnt);
94}
95
98 setShutdownFlag(true);
100 "Control Agent is shutting down"));
101}
102
105 bool check_only) {
106 // System reconfiguration often poses an interesting issue whereby the
107 // configuration parsing is successful, but an attempt to use a new
108 // configuration is not. This will leave us in the inconsistent state
109 // when the configuration is in fact only partially applied and the
110 // system's ability to operate is impaired. The use of C++ lambda is
111 // a way to resolve this problem by injecting the code to the
112 // simpleParseConfig which performs an attempt to open new instance
113 // of the listener (if required). The lambda code will throw an
114 // exception if it fails and cause the simpleParseConfig to rollback
115 // configuration changes and report an error.
116 ConstElementPtr answer = getCfgMgr()->simpleParseConfig(config_set,
117 check_only,
118 [this]() {
119 ConfigPtr base_ctx = getCfgMgr()->getContext();
121 ctx = boost::dynamic_pointer_cast<CtrlAgentCfgContext>(base_ctx);
122
123 if (!ctx) {
124 isc_throw(Unexpected, "Internal logic error: bad context type");
125 }
126
128 IOAddress server_address("::");
129 try {
130 server_address = IOAddress(ctx->getHttpHost());
131
132 } catch (const IOError& e) {
133 isc_throw(BadValue, "Failed to convert " << ctx->getHttpHost()
134 << " to IP address:" << e.what());
135 }
136
137 uint16_t server_port = ctx->getHttpPort();
138
139 // Search for the specific connection and reuse the existing one if found.
140 auto it = sockets_.find(std::make_pair(server_address, server_port));
141 if (it != sockets_.end()) {
142 auto listener = it->second->listener_;
143 if (listener) {
144 // Reconfig keeping the same address and port.
145 if (listener->getTlsContext()) {
146 if (ctx->getTrustAnchor().empty()) {
147 // Can not switch from HTTPS to HTTP
149 .arg(server_address.toText())
150 .arg(server_port);
152 "Can not switch from HTTPS to HTTP sockets using the same address and port.");
153 } else {
154 // Apply TLS settings each time.
155 TlsContextPtr tls_context;
156 TlsContext::configure(tls_context,
157 TlsRole::SERVER,
158 ctx->getTrustAnchor(),
159 ctx->getCertFile(),
160 ctx->getKeyFile(),
161 ctx->getCertRequired());
162 // Overwrite the authentication setup and the http headers in the response creator config.
163 it->second->config_->setAuthConfig(ctx->getAuthConfig());
164 it->second->config_->setHttpHeaders(ctx->getHttpHeaders());
165 listener->setTlsContext(tls_context);
167 .arg(server_address.toText())
168 .arg(server_port);
169 }
170 } else {
171 if (!ctx->getTrustAnchor().empty()) {
172 // Can not switch from HTTP to HTTPS
174 .arg(server_address.toText())
175 .arg(server_port);
177 "Can not switch from HTTP to HTTPS sockets using the same address and port.");
178 } else {
179 // Overwrite the authentication setup and the http headers in the response creator config.
180 it->second->config_->setAuthConfig(ctx->getAuthConfig());
181 it->second->config_->setHttpHeaders(ctx->getHttpHeaders());
183 .arg(server_address.toText())
184 .arg(server_port);
185 }
186 }
187 }
188 // If the connection can be reused, mark it as usable.
189 it->second->usable_ = true;
190 } else {
191
192 // Connection not found so it needs to be created.
193 // When TLS is enabled configure it.
194 bool use_https = false;
195 TlsContextPtr tls_context;
196 if (!ctx->getCertFile().empty()) {
197 TlsContext::configure(tls_context,
198 TlsRole::SERVER,
199 ctx->getTrustAnchor(),
200 ctx->getCertFile(),
201 ctx->getKeyFile(),
202 ctx->getCertRequired());
203 use_https = true;
204 }
205
206 // Create response creator factory first. It will be used to
207 // generate response creators. Each response creator will be
208 // used to generate answer to specific request.
210
211 // Create HTTP listener. It will open up a TCP socket and be
212 // prepared to accept incoming connection.
213 HttpListenerPtr http_listener
215 server_address,
216 server_port,
217 tls_context,
218 rcf,
221
222 // Instruct the HTTP listener to actually open socket, install
223 // callback and start listening.
224 http_listener->start();
225
226 HttpSocketInfoPtr socket_info(new HttpSocketInfo());
227 socket_info->config_ = ctx;
228 socket_info->listener_ = http_listener;
229
230 sockets_[std::make_pair(server_address, server_port)] = socket_info;
231
232 // Ok, seems we're good to go.
233 if (use_https) {
235 .arg(server_address.toText())
236 .arg(server_port);
237 } else {
239 .arg(server_address.toText())
240 .arg(server_port);
241 }
242 }
243
244 auto copy = sockets_;
245 for (auto const& data : copy) {
246 if (data.second->usable_) {
247 // If the connection can be used (just created) or reused, keep it
248 // in the list and clear the flag. It will be marked again on next
249 // configuration event if needed.
250 data.second->usable_ = false;
251 } else {
252 // If the connection can not be reused, stop it and remove it from the list.
253 data.second->listener_->stop();
254 auto it = sockets_.find(std::make_pair(data.second->config_->getHttpHost(),
255 data.second->config_->getHttpPort()));
256 if (it != sockets_.end()) {
257 sockets_.erase(it);
258 }
259 }
260 }
261 });
262
263 int rcode = 0;
264 config::parseAnswer(rcode, answer);
265
267 try {
268 // Handle events registered by hooks using external IOService objects.
270 } catch (const std::exception& ex) {
271 std::ostringstream err;
272 err << "Error initializing hooks: "
273 << ex.what();
275 }
276
277 return (answer);
278}
279
280void
282 for (auto const& data : sockets_) {
283 data.second->listener_->stop();
284 }
285 sockets_.clear();
286 // We have stopped listeners but there may be some pending handlers
287 // related to these listeners. Need to invoke these handlers.
288 try {
289 getIOService()->poll();
290 } catch (...) {
291 }
292}
293
296 return (boost::dynamic_pointer_cast<CtrlAgentCfgMgr>(getCfgMgr()));
297}
298
301 // Return the most recent listener or null.
302 if (info) {
303 auto it = sockets_.find(std::make_pair(info->config_->getHttpHost(), info->config_->getHttpPort()));
304 if (it != sockets_.end()) {
305 return (it->second->listener_);
306 }
307 } else if (sockets_.size()) {
308 return (sockets_.begin()->second->listener_);
309 }
310 return (ConstHttpListenerPtr());
311}
312
313bool
315 // If there are is a listener, we're listening.
316 return (static_cast<bool>(getHttpListener()));
317}
318
319} // namespace isc::agent
320} // 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.
Definition ca_process.cc:97
virtual ~CtrlAgentProcess()
Destructor.
Definition ca_process.cc:35
CtrlAgentProcess(const char *name, const asiolink::IOServicePtr &io_service)
Constructor.
Definition ca_process.cc:30
virtual void init()
Initialize the Control Agent process.
Definition ca_process.cc:39
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:43
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
Application Process Interface.
Definition d_process.h:75
void setShutdownFlag(bool value)
Sets the process shut down flag to the given value.
Definition d_process.h:162
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
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_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:26
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:25
const isc::log::MessageID CTRL_AGENT_HTTPS_SERVICE_REUSE_FAILED
Definition ca_messages.h:19
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()
Creates a standard config/command level success answer message (i.e.
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