Kea 2.6.2
d2_process.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2024 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>
11#include <config/command_mgr.h>
12#include <d2/d2_controller.h>
13#include <d2/d2_process.h>
14#include <d2srv/d2_cfg_mgr.h>
15#include <d2srv/d2_log.h>
16#include <d2srv/d2_stats.h>
17#include <d2srv/d2_tsig_key.h>
18#include <hooks/hooks.h>
19#include <hooks/hooks_manager.h>
20
21using namespace isc::asiolink;
22using namespace isc::config;
23using namespace isc::hooks;
24using namespace isc::process;
25
26namespace {
27
29struct D2ProcessHooks {
30 int hooks_index_d2_srv_configured_;
31
33 D2ProcessHooks() {
34 hooks_index_d2_srv_configured_ = HooksManager::registerHook("d2_srv_configured");
35 }
36
37};
38
39// Declare a Hooks object. As this is outside any function or method, it
40// will be instantiated (and the constructor run) when the module is loaded.
41// As a result, the hook indexes will be defined before any method in this
42// module is called.
43D2ProcessHooks Hooks;
44
45}
46
47namespace isc {
48namespace d2 {
49
50// Setting to 80% for now. This is an arbitrary choice and should probably
51// be configurable.
52const unsigned int D2Process::QUEUE_RESTART_PERCENT = 80;
53
54D2Process::D2Process(const char* name, const asiolink::IOServicePtr& io_service)
55 : DProcessBase(name, io_service, DCfgMgrBasePtr(new D2CfgMgr())),
56 reconf_queue_flag_(false), shutdown_type_(SD_NORMAL) {
57
58 // Instantiate queue manager. Note that queue manager does not start
59 // listening at this point. That can only occur after configuration has
60 // been received. This means that until we receive the configuration,
61 // D2 will neither receive nor process NameChangeRequests.
62 // Pass in IOService for NCR IO event processing.
63 queue_mgr_.reset(new D2QueueMgr(getIOService()));
64
65 // Instantiate update manager.
66 // Pass in both queue manager and configuration manager.
67 // Pass in IOService for DNS update transaction IO event processing.
69 update_mgr_.reset(new D2UpdateMgr(queue_mgr_, tmp, getIOService()));
70
71 // Initialize stats manager.
73};
74
75void
77 // CommandMgr uses IO service to run asynchronous socket operations.
79};
80
81void
84 D2ControllerPtr controller =
85 boost::dynamic_pointer_cast<D2Controller>(D2Controller::instance());
86 try {
87 // Now logging was initialized so commands can be registered.
88 controller->registerCommands();
89
90 // Loop forever until we are allowed to shutdown.
91 while (!canShutdown()) {
92 // Check on the state of the request queue. Take any
93 // actions necessary regarding it.
95
96 // Give update manager a time slice to queue new jobs and
97 // process finished ones.
98 update_mgr_->sweep();
99
100 // Wait on IO event(s) - block until one or more of the following
101 // has occurred:
102 // a. NCR message has been received
103 // b. Transaction IO has completed
104 // c. Interval timer expired
105 // d. Control channel event
106 // e. Something stopped IO service (runIO returns 0)
107 if (runIO() == 0) {
108 // Pretty sure this amounts to an unexpected stop and we
109 // should bail out now. Normal shutdowns do not utilize
110 // stopping the IOService.
112 "Primary IO service stopped unexpectedly");
113 }
114 }
115 } catch (const std::exception& ex) {
116 LOG_FATAL(d2_logger, DHCP_DDNS_FAILED).arg(ex.what());
117 controller->deregisterCommands();
119 "Process run method failed: " << ex.what());
120 }
121
125
126 controller->deregisterCommands();
127
129
130};
131
132size_t
134 // Handle events registered by hooks using external IOService objects.
136 // We want to block until at least one handler is called.
137 // Poll runs all that are ready. If none are ready it returns immediately
138 // with a count of zero.
139 size_t cnt = getIOService()->poll();
140 if (!cnt) {
141 // Poll ran no handlers either none are ready or the service has been
142 // stopped. Either way, call runOne to wait for a IO event. If the
143 // service is stopped it will return immediately with a cnt of zero.
144 cnt = getIOService()->runOne();
145 }
146 return (cnt);
147}
148
149bool
151 bool all_clear = false;
152
153 // If we have been told to shutdown, find out if we are ready to do so.
154 if (shouldShutdown()) {
155 switch (shutdown_type_) {
156 case SD_NORMAL:
157 // For a normal shutdown we need to stop the queue manager but
158 // wait until we have finished all the transactions in progress.
159 all_clear = (((queue_mgr_->getMgrState() != D2QueueMgr::RUNNING) &&
160 (queue_mgr_->getMgrState() != D2QueueMgr::STOPPING))
161 && (update_mgr_->getTransactionCount() == 0));
162 break;
163
164 case SD_DRAIN_FIRST:
165 // For a drain first shutdown we need to stop the queue manager but
166 // process all of the requests in the receive queue first.
167 all_clear = (((queue_mgr_->getMgrState() != D2QueueMgr::RUNNING) &&
168 (queue_mgr_->getMgrState() != D2QueueMgr::STOPPING))
169 && (queue_mgr_->getQueueSize() == 0)
170 && (update_mgr_->getTransactionCount() == 0));
171 break;
172
173 case SD_NOW:
174 // Get out right now, no niceties.
175 all_clear = true;
176 break;
177
178 default:
179 // shutdown_type_ is an enum and should only be one of the above.
180 // if its getting through to this, something is whacked.
181 break;
182 }
183
184 if (all_clear) {
187 .arg(getShutdownTypeStr(shutdown_type_));
188 }
189 }
190
191 return (all_clear);
192}
193
198 .arg(args ? args->str() : "(no arguments)");
199
200 // Default shutdown type is normal.
201 std::string type_str(getShutdownTypeStr(SD_NORMAL));
202 shutdown_type_ = SD_NORMAL;
203
204 if (args) {
205 if ((args->getType() == isc::data::Element::map) &&
206 args->contains("type")) {
207 type_str = args->get("type")->stringValue();
208
209 if (type_str == getShutdownTypeStr(SD_NORMAL)) {
210 shutdown_type_ = SD_NORMAL;
211 } else if (type_str == getShutdownTypeStr(SD_DRAIN_FIRST)) {
212 shutdown_type_ = SD_DRAIN_FIRST;
213 } else if (type_str == getShutdownTypeStr(SD_NOW)) {
214 shutdown_type_ = SD_NOW;
215 } else {
216 setShutdownFlag(false);
218 "Invalid Shutdown type: " +
219 type_str));
220 }
221 }
222 }
223
224 // Set the base class's shutdown flag.
225 setShutdownFlag(true);
227 "Shutdown initiated, type is: " +
228 type_str));
229}
230
234 .arg(check_only ? "check" : "update")
235 .arg(getD2CfgMgr()->redactConfig(config_set)->str());
236
238 answer = getCfgMgr()->simpleParseConfig(config_set, check_only,
239 std::bind(&D2Process::reconfigureCommandChannel, this));
240 if (check_only) {
241 return (answer);
242 }
243
244 int rcode = 0;
246 comment = isc::config::parseAnswer(rcode, answer);
247
248 if (rcode) {
249 // Non-zero means we got an invalid configuration, take no further
250 // action. In integrated mode, this will send a failed response back
251 // to the configuration backend.
252 reconf_queue_flag_ = false;
253 return (answer);
254 }
255
256 // Set the reconf_queue_flag to indicate that we need to reconfigure
257 // the queue manager. Reconfiguring the queue manager may be asynchronous
258 // and require one or more events to occur, therefore we set a flag
259 // indicating it needs to be done but we cannot do it here. It must
260 // be done over time, while events are being processed. Remember that
261 // the method we are in now is invoked as part of the configuration event
262 // callback. This means you can't wait for events here, you are already
263 // in one.
267 reconf_queue_flag_ = true;
268
269 // This hook point notifies hooks libraries that the configuration of the
270 // D2 server has completed. It provides the hook library with the pointer
271 // to the common IO service object, new server configuration in the JSON
272 // format and with the pointer to the configuration storage where the
273 // parsed configuration is stored.
274 std::string error("");
275 if (HooksManager::calloutsPresent(Hooks.hooks_index_d2_srv_configured_)) {
277
278 callout_handle->setArgument("io_context", getIOService());
279 callout_handle->setArgument("json_config", config_set);
280 callout_handle->setArgument("server_config",
281 getD2CfgMgr()->getD2CfgContext());
282 callout_handle->setArgument("error", error);
283
284 HooksManager::callCallouts(Hooks.hooks_index_d2_srv_configured_,
285 *callout_handle);
286
287 // The config can be rejected by a hook.
288 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
289 callout_handle->getArgument("error", error);
291 .arg(error);
292 reconf_queue_flag_ = false;
294 return (answer);
295 }
296 }
297
299 try {
300 // Handle events registered by hooks using external IOService objects.
302 } catch (const std::exception& ex) {
303 std::ostringstream err;
304 err << "Error initializing hooks: "
305 << ex.what();
307 }
308
309 // If we are here, configuration was valid, at least it parsed correctly
310 // and therefore contained no invalid values.
311 // Return the success answer from above.
312 return (answer);
313}
314
315void
317 switch (queue_mgr_->getMgrState()){
319 if (reconf_queue_flag_ || shouldShutdown()) {
324 try {
327 .arg(reconf_queue_flag_ ? "reconfiguration" : "shutdown");
328 queue_mgr_->stopListening();
329 } catch (const isc::Exception& ex) {
330 // It is very unlikely that we would experience an error
331 // here, but theoretically possible.
333 .arg(ex.what());
334 }
335 }
336 break;
337
342 size_t threshold = (((queue_mgr_->getMaxQueueSize()
343 * QUEUE_RESTART_PERCENT)) / 100);
344 if (queue_mgr_->getQueueSize() <= threshold) {
346 .arg(threshold).arg(queue_mgr_->getMaxQueueSize());
347 try {
348 queue_mgr_->startListening();
349 } catch (const isc::Exception& ex) {
351 .arg(ex.what());
352 }
353 }
354
355 break;
356 }
357
366 if (!shouldShutdown()) {
369 }
370 break;
371
377 break;
378
379 default:
380 // If the reconfigure flag is set, then we are in a state now where
381 // we can do the reconfigure. In other words, we aren't RUNNING or
382 // STOPPING.
383 if (reconf_queue_flag_) {
387 }
388 break;
389 }
390}
391
392void
394 // Set reconfigure flag to false. We are only here because we have
395 // a valid configuration to work with so if we fail below, it will be
396 // an operational issue, such as a busy IP address. That will leave
397 // queue manager in INITTED state, which is fine.
398 // What we don't want is to continually attempt to reconfigure so set
399 // the flag false now.
403 reconf_queue_flag_ = false;
404 try {
405 // Wipe out the current listener.
406 queue_mgr_->removeListener();
407
408 // Get the configuration parameters that affect Queue Manager.
409 const D2ParamsPtr& d2_params = getD2CfgMgr()->getD2Params();
410
413 std::string ip_address = d2_params->getIpAddress().toText();
414 if (ip_address == "0.0.0.0" || ip_address == "::") {
416 } else if (ip_address != "127.0.0.1" && ip_address != "::1") {
418 }
419
420 // Instantiate the listener.
421 if (d2_params->getNcrProtocol() == dhcp_ddns::NCR_UDP) {
422 queue_mgr_->initUDPListener(d2_params->getIpAddress(),
423 d2_params->getPort(),
424 d2_params->getNcrFormat(), true);
425 } else {
427 // We should never get this far but if we do deal with it.
428 isc_throw(DProcessBaseError, "Unsupported NCR listener protocol:"
429 << dhcp_ddns::ncrProtocolToString(d2_params->
430 getNcrProtocol()));
431 }
432
433 // Now start it. This assumes that starting is a synchronous,
434 // blocking call that executes quickly.
437 queue_mgr_->startListening();
438 } catch (const isc::Exception& ex) {
439 // Queue manager failed to initialize and therefore not listening.
440 // This is most likely due to an unavailable IP address or port,
441 // which is a configuration issue.
443 }
444}
445
447 queue_mgr_->stopListening();
448 getIOService()->stopAndPoll();
449 queue_mgr_->removeListener();
450}
451
454 // The base class gives a base class pointer to our configuration manager.
455 // Since we are D2, and we need D2 specific extensions, we need a pointer
456 // to D2CfgMgr for some things.
457 return (boost::dynamic_pointer_cast<D2CfgMgr>(getCfgMgr()));
458}
459
461 const char* str;
462 switch (type) {
463 case SD_NORMAL:
464 str = "normal";
465 break;
466 case SD_DRAIN_FIRST:
467 str = "drain_first";
468 break;
469 case SD_NOW:
470 str = "now";
471 break;
472 default:
473 str = "invalid";
474 break;
475 }
476
477 return (str);
478}
479
480void
482 // Get new socket configuration.
483 isc::data::ConstElementPtr sock_cfg = getD2CfgMgr()->getControlSocketInfo();
484
485 // Determine if the socket configuration has changed. It has if
486 // both old and new configuration is specified but respective
487 // data elements aren't equal.
488 bool sock_changed = (sock_cfg && current_control_socket_ &&
489 !sock_cfg->equals(*current_control_socket_));
490
491 // If the previous or new socket configuration doesn't exist or
492 // the new configuration differs from the old configuration we
493 // close the existing socket and open a new socket as appropriate.
494 // Note that closing an existing socket means the client will not
495 // receive the configuration result.
496 if (!sock_cfg || !current_control_socket_ || sock_changed) {
497 // Close the existing socket.
498 if (current_control_socket_) {
500 current_control_socket_.reset();
501 }
502
503 // Open the new socket.
504 if (sock_cfg) {
506 }
507 }
508
509 // Commit the new socket configuration.
510 current_control_socket_ = sock_cfg;
511}
512
513} // namespace isc::d2
514} // namespace isc
it forwards queries to a single upstream resolver and passes the answers back to the client It is constructed with the address of the forward server Queries are initiated with the question to ask the forward a buffer into which to write the answer
Definition asiodns.dox:60
CtrlAgentHooks Hooks
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
void closeCommandSocket()
Shuts down any open control sockets.
static CommandMgr & instance()
CommandMgr is a singleton class.
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the command manager.
void openCommandSocket(const isc::data::ConstElementPtr &socket_info)
Opens control socket with parameters specified in socket_info.
DHCP-DDNS Configuration Manager.
Definition d2_cfg_mgr.h:161
static process::DControllerBasePtr & instance()
Static singleton instance method.
D2Process(const char *name, const asiolink::IOServicePtr &io_service)
Constructor.
Definition d2_process.cc:54
static const unsigned int QUEUE_RESTART_PERCENT
Defines the point at which to resume receiving requests.
Definition d2_process.h:48
virtual bool canShutdown() const
Indicates whether or not the process can perform a shutdown.
virtual void checkQueueStatus()
Monitors current queue manager state, takes action accordingly.
virtual ~D2Process()
Destructor.
virtual void run()
Implements the process's event loop.
Definition d2_process.cc:82
virtual void init()
Called after instantiation to perform initialization unique to D2.
Definition d2_process.cc:76
D2CfgMgrPtr getD2CfgMgr()
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 reconfigureCommandChannel()
(Re-)Configure the command channel.
virtual void reconfigureQueueMgr()
Initializes then starts the queue manager.
ShutdownType
Defines the shutdown types supported by D2Process.
Definition d2_process.h:36
virtual isc::data::ConstElementPtr shutdown(isc::data::ConstElementPtr args)
Initiates the D2Process shutdown process.
static const char * getShutdownTypeStr(const ShutdownType &type)
Returns a text label for the given shutdown type.
virtual size_t runIO()
Allows IO processing to run until at least callback is invoked.
D2QueueMgr creates and manages a queue of DNS update requests.
static void init()
Initialize D2 statistics.
Definition d2_stats.cc:46
D2UpdateMgr creates and manages update transactions.
@ NEXT_STEP_DROP
drop the packet
static int registerHook(const std::string &name)
Register Hook.
static bool calloutsPresent(int index)
Are callouts present?
static boost::shared_ptr< CalloutHandle > createCalloutHandle()
Return callout handle.
static void callCallouts(int index, CalloutHandle &handle)
Calls the callouts for a given hook.
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
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_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
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Parses a standard config/command level answer and returns arguments or text status code.
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.
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
boost::shared_ptr< D2CfgMgr > D2CfgMgrPtr
Defines a shared pointer to D2CfgMgr.
Definition d2_cfg_mgr.h:334
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECOVERING
Definition d2_messages.h:59
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOP_ERROR
Definition d2_messages.h:67
const isc::log::MessageID DHCP_DDNS_FAILED
Definition d2_messages.h:22
const isc::log::MessageID DHCP_DDNS_LISTENING_ON_ALL_INTERFACES
Definition d2_messages.h:49
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_START_ERROR
Definition d2_messages.h:64
const isc::log::MessageID DHCP_DDNS_SHUTDOWN_COMMAND
Definition d2_messages.h:87
const isc::log::MessageID DHCP_DDNS_CONFIGURE
Definition d2_messages.h:17
const isc::log::MessageID DHCP_DDNS_CLEARED_FOR_SHUTDOWN
Definition d2_messages.h:15
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECONFIGURING
Definition d2_messages.h:58
const isc::log::MessageID DHCP_DDNS_RUN_EXIT
Definition d2_messages.h:86
const isc::log::MessageID DHCP_DDNS_CONFIGURED_CALLOUT_DROP
Definition d2_messages.h:18
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUME_ERROR
Definition d2_messages.h:61
const isc::log::MessageID DHCP_DDNS_STARTED
Definition d2_messages.h:88
boost::shared_ptr< D2Controller > D2ControllerPtr
Pointer to a process controller.
isc::log::Logger d2_logger("dhcpddns")
Defines the logger used within D2.
Definition d2_log.h:18
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUMING
Definition d2_messages.h:62
boost::shared_ptr< D2Params > D2ParamsPtr
Defines a pointer for D2Params instances.
Definition d2_config.h:255
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPING
Definition d2_messages.h:66
const isc::log::MessageID DHCP_DDNS_NOT_ON_LOOPBACK
Definition d2_messages.h:50
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
std::string ncrProtocolToString(NameChangeProtocol protocol)
Function which converts NameChangeProtocol enums to text labels.
Definition ncr_io.cc:36
boost::shared_ptr< CalloutHandle > CalloutHandlePtr
A shared pointer to a CalloutHandle object.
const int DBGLVL_TRACE_BASIC
Trace basic operations.
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
ConstElementPtr redactConfig(ConstElementPtr const &element, list< string > const &json_path)
Redact a configuration.
Defines the logger used by the top-level component of kea-lfc.