Kea 3.1.1
d2_process.cc
Go to the documentation of this file.
1// Copyright (C) 2013-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>
11#include <config/command_mgr.h>
14#include <d2/d2_controller.h>
15#include <d2/d2_process.h>
16#include <d2srv/d2_cfg_mgr.h>
17#include <d2srv/d2_log.h>
18#include <d2srv/d2_stats.h>
19#include <d2srv/d2_tsig_key.h>
20#include <hooks/hooks.h>
21#include <hooks/hooks_manager.h>
22#include <util/filesystem.h>
23
24using namespace isc::asiolink;
25using namespace isc::config;
26using namespace isc::data;
27using namespace isc::hooks;
28using namespace isc::process;
29using namespace isc::util::file;
30
31namespace {
32
34struct D2ProcessHooks {
35 int hooks_index_d2_srv_configured_;
36
38 D2ProcessHooks() {
39 hooks_index_d2_srv_configured_ = HooksManager::registerHook("d2_srv_configured");
40 }
41
42};
43
44// Declare a Hooks object. As this is outside any function or method, it
45// will be instantiated (and the constructor run) when the module is loaded.
46// As a result, the hook indexes will be defined before any method in this
47// module is called.
48D2ProcessHooks Hooks;
49
50}
51
52namespace isc {
53namespace d2 {
54
55// Setting to 80% for now. This is an arbitrary choice and should probably
56// be configurable.
57const unsigned int D2Process::QUEUE_RESTART_PERCENT = 80;
58
59// IOService run time duration is 100 ms.
60const unsigned int D2Process::IO_SERVICE_RUN_TIME_USECS = (100 * 1000);
61
62D2Process::D2Process(const char* name, const asiolink::IOServicePtr& io_service)
63 : DProcessBase(name, io_service, DCfgMgrBasePtr(new D2CfgMgr())),
64 reconf_queue_flag_(false), shutdown_type_(SD_NORMAL) {
65
66 // Instantiate queue manager. Note that queue manager does not start
67 // listening at this point. That can only occur after configuration has
68 // been received. This means that until we receive the configuration,
69 // D2 will neither receive nor process NameChangeRequests.
70 // Pass in IOService for NCR IO event processing.
71 queue_mgr_.reset(new D2QueueMgr(getIOService()));
72
73 // Instantiate update manager.
74 // Pass in both queue manager and configuration manager.
75 // Pass in IOService for DNS update transaction IO event processing.
77 update_mgr_.reset(new D2UpdateMgr(queue_mgr_, tmp, getIOService()));
78
79 // Initialize stats manager.
81};
82
83void
85 using namespace isc::config;
86 // Command managers use IO service to run asynchronous socket operations.
89
90 // Set the HTTP authentication default realm.
92
93 // D2 server does not use the interface manager.
96};
97
98void
100 LOG_INFO(d2_logger, DHCP_DDNS_STARTED).arg(VERSION);
101
104 }
105
106 D2ControllerPtr controller =
107 boost::dynamic_pointer_cast<D2Controller>(D2Controller::instance());
108 try {
109 // Now logging was initialized so commands can be registered.
110 controller->registerCommands();
111
112 // Loop forever until we are allowed to shutdown.
113 while (!canShutdown()) {
114 // Check on the state of the request queue. Take any
115 // actions necessary regarding it.
117
118 // Give update manager a time slice to queue new jobs and
119 // process finished ones.
120 update_mgr_->sweep();
121
122 // Wait on IO event(s) - block until one or more of the following
123 // has occurred:
124 // a. NCR message has been received
125 // b. Transaction IO has completed
126 // c. Interval timer expired
127 // d. Control channel event
128 // e. Something stopped IO service (runIO returns 0)
129 if (runIO() == 0) {
130 // Pretty sure this amounts to an unexpected stop and we
131 // should bail out now. Normal shutdowns do not utilize
132 // stopping the IOService.
134 "Primary IO service stopped unexpectedly");
135 }
136 }
137 } catch (const std::exception& ex) {
138 LOG_FATAL(d2_logger, DHCP_DDNS_FAILED).arg(ex.what());
139 controller->deregisterCommands();
141 "Process run method failed: " << ex.what());
142 }
143
147
148 controller->deregisterCommands();
149
151
152};
153
154size_t
156 // We want to process all ready handlers on both the managed IOServices
157 // and the main IOservice. If no handlers were executed on any of the
158 // IOServices will wait on the main IOService until at least one handler
159 // executes or we time out.
160 size_t cnt = IOServiceMgr::instance().pollIOServices();
161 cnt += getIOService()->poll();
162 if (!cnt) {
163 // Polling ran no handlers so either none are ready or the service has been
164 // stopped. Either way, call runOneFor() to wait for a IO event on the
165 // main service. If the service is stopped it will return immediately
166 // with a cnt of zero and timed_out set to false.
167 bool timed_out;
168 cnt = getIOService()->runOneFor(IO_SERVICE_RUN_TIME_USECS, timed_out);
169 if (timed_out) {
170 // Return 1 so caller knows the service has not stopped.
171 return (1);
172 }
173 }
174
175 return (cnt);
176}
177
178bool
180 bool all_clear = false;
181
182 // If we have been told to shutdown, find out if we are ready to do so.
183 if (shouldShutdown()) {
184 switch (shutdown_type_) {
185 case SD_NORMAL:
186 // For a normal shutdown we need to stop the queue manager but
187 // wait until we have finished all the transactions in progress.
188 all_clear = (((queue_mgr_->getMgrState() != D2QueueMgr::RUNNING) &&
189 (queue_mgr_->getMgrState() != D2QueueMgr::STOPPING))
190 && (update_mgr_->getTransactionCount() == 0));
191 break;
192
193 case SD_DRAIN_FIRST:
194 // For a drain first shutdown we need to stop the queue manager but
195 // process all of the requests in the receive queue first.
196 all_clear = (((queue_mgr_->getMgrState() != D2QueueMgr::RUNNING) &&
197 (queue_mgr_->getMgrState() != D2QueueMgr::STOPPING))
198 && (queue_mgr_->getQueueSize() == 0)
199 && (update_mgr_->getTransactionCount() == 0));
200 break;
201
202 case SD_NOW:
203 // Get out right now, no niceties.
204 all_clear = true;
205 break;
206
207 default:
208 // shutdown_type_ is an enum and should only be one of the above.
209 // if its getting through to this, something is whacked.
210 break;
211 }
212
213 if (all_clear) {
216 .arg(getShutdownTypeStr(shutdown_type_));
217 }
218 }
219
220 return (all_clear);
221}
222
227 .arg(args ? args->str() : "(no arguments)");
228
229 // Default shutdown type is normal.
230 std::string type_str(getShutdownTypeStr(SD_NORMAL));
231 shutdown_type_ = SD_NORMAL;
232
233 if (args) {
234 if ((args->getType() == isc::data::Element::map) &&
235 args->contains("type")) {
236 type_str = args->get("type")->stringValue();
237
238 if (type_str == getShutdownTypeStr(SD_NORMAL)) {
239 shutdown_type_ = SD_NORMAL;
240 } else if (type_str == getShutdownTypeStr(SD_DRAIN_FIRST)) {
241 shutdown_type_ = SD_DRAIN_FIRST;
242 } else if (type_str == getShutdownTypeStr(SD_NOW)) {
243 shutdown_type_ = SD_NOW;
244 } else {
245 setShutdownFlag(false);
247 "Invalid Shutdown type: " +
248 type_str));
249 }
250 }
251 }
252
253 // Set the base class's shutdown flag.
254 setShutdownFlag(true);
256 "Shutdown initiated, type is: " +
257 type_str));
258}
259
263 .arg(check_only ? "check" : "update")
264 .arg(getD2CfgMgr()->redactConfig(config_set)->str());
265
267 answer = getCfgMgr()->simpleParseConfig(config_set, check_only,
268 std::bind(&D2Process::reconfigureCommandChannel, this));
269 if (check_only) {
270 return (answer);
271 }
272
273 int rcode = 0;
275 comment = isc::config::parseAnswer(rcode, answer);
276
277 if (rcode) {
278 // Non-zero means we got an invalid configuration, take no further
279 // action. In integrated mode, this will send a failed response back
280 // to the configuration backend.
281 reconf_queue_flag_ = false;
282 return (answer);
283 }
284
285 // Set the reconf_queue_flag to indicate that we need to reconfigure
286 // the queue manager. Reconfiguring the queue manager may be asynchronous
287 // and require one or more events to occur, therefore we set a flag
288 // indicating it needs to be done but we cannot do it here. It must
289 // be done over time, while events are being processed. Remember that
290 // the method we are in now is invoked as part of the configuration event
291 // callback. This means you can't wait for events here, you are already
292 // in one.
296 reconf_queue_flag_ = true;
297
298 // This hook point notifies hooks libraries that the configuration of the
299 // D2 server has completed. It provides the hook library with the pointer
300 // to the common IO service object, new server configuration in the JSON
301 // format and with the pointer to the configuration storage where the
302 // parsed configuration is stored.
303 std::string error("");
304 if (HooksManager::calloutsPresent(Hooks.hooks_index_d2_srv_configured_)) {
306
307 callout_handle->setArgument("io_context", getIOService());
308 callout_handle->setArgument("json_config", config_set);
309 callout_handle->setArgument("server_config",
310 getD2CfgMgr()->getD2CfgContext());
311 callout_handle->setArgument("error", error);
312
313 HooksManager::callCallouts(Hooks.hooks_index_d2_srv_configured_,
314 *callout_handle);
315
316 // The config can be rejected by a hook.
317 if (callout_handle->getStatus() == CalloutHandle::NEXT_STEP_DROP) {
318 callout_handle->getArgument("error", error);
320 .arg(error);
321 reconf_queue_flag_ = false;
323 return (answer);
324 }
325 }
326
328 try {
329 // Handle events registered by hooks using external IOService objects.
331 } catch (const std::exception& ex) {
332 std::ostringstream err;
333 err << "Error initializing hooks: "
334 << ex.what();
336 }
337
338 // If we are here, configuration was valid, at least it parsed correctly
339 // and therefore contained no invalid values.
340 // Return the success answer from above.
341 return (answer);
342}
343
344void
346 switch (queue_mgr_->getMgrState()){
348 if (reconf_queue_flag_ || shouldShutdown()) {
353 try {
356 .arg(reconf_queue_flag_ ? "reconfiguration" : "shutdown");
357 queue_mgr_->stopListening();
358 } catch (const isc::Exception& ex) {
359 // It is very unlikely that we would experience an error
360 // here, but theoretically possible.
362 .arg(ex.what());
363 }
364 }
365 break;
366
371 size_t threshold = (((queue_mgr_->getMaxQueueSize()
372 * QUEUE_RESTART_PERCENT)) / 100);
373 if (queue_mgr_->getQueueSize() <= threshold) {
375 .arg(threshold).arg(queue_mgr_->getMaxQueueSize());
376 try {
377 queue_mgr_->startListening();
378 } catch (const isc::Exception& ex) {
380 .arg(ex.what());
381 }
382 }
383
384 break;
385 }
386
395 if (!shouldShutdown()) {
398 }
399 break;
400
406 break;
407
408 default:
409 // If the reconfigure flag is set, then we are in a state now where
410 // we can do the reconfigure. In other words, we aren't RUNNING or
411 // STOPPING.
412 if (reconf_queue_flag_) {
416 }
417 break;
418 }
419}
420
421void
423 // Set reconfigure flag to false. We are only here because we have
424 // a valid configuration to work with so if we fail below, it will be
425 // an operational issue, such as a busy IP address. That will leave
426 // queue manager in INITTED state, which is fine.
427 // What we don't want is to continually attempt to reconfigure so set
428 // the flag false now.
432 reconf_queue_flag_ = false;
433 try {
434 // Wipe out the current listener.
435 queue_mgr_->removeListener();
436
437 // Get the configuration parameters that affect Queue Manager.
438 const D2ParamsPtr& d2_params = getD2CfgMgr()->getD2Params();
439
442 std::string ip_address = d2_params->getIpAddress().toText();
443 if (ip_address == "0.0.0.0" || ip_address == "::") {
445 } else if (ip_address != "127.0.0.1" && ip_address != "::1") {
447 }
448
449 // Instantiate the listener.
450 if (d2_params->getNcrProtocol() == dhcp_ddns::NCR_UDP) {
451 queue_mgr_->initUDPListener(d2_params->getIpAddress(),
452 d2_params->getPort(),
453 d2_params->getNcrFormat(), true);
454 } else {
456 // We should never get this far but if we do deal with it.
457 isc_throw(DProcessBaseError, "Unsupported NCR listener protocol:"
458 << dhcp_ddns::ncrProtocolToString(d2_params->
459 getNcrProtocol()));
460 }
461
462 // Now start it. This assumes that starting is a synchronous,
463 // blocking call that executes quickly.
466 queue_mgr_->startListening();
467 } catch (const isc::Exception& ex) {
468 // Queue manager failed to initialize and therefore not listening.
469 // This is most likely due to an unavailable IP address or port,
470 // which is a configuration issue.
472 }
473}
474
476 queue_mgr_->stopListening();
477 getIOService()->stopAndPoll();
478 queue_mgr_->removeListener();
479}
480
483 // The base class gives a base class pointer to our configuration manager.
484 // Since we are D2, and we need D2 specific extensions, we need a pointer
485 // to D2CfgMgr for some things.
486 return (boost::dynamic_pointer_cast<D2CfgMgr>(getCfgMgr()));
487}
488
490 const char* str;
491 switch (type) {
492 case SD_NORMAL:
493 str = "normal";
494 break;
495 case SD_DRAIN_FIRST:
496 str = "drain_first";
497 break;
498 case SD_NOW:
499 str = "now";
500 break;
501 default:
502 str = "invalid";
503 break;
504 }
505
506 return (str);
507}
508
509void
511 // Get new Unix socket configuration.
512 ConstElementPtr unix_config =
513 getD2CfgMgr()->getUnixControlSocketInfo();
514
515 // Determine if the socket configuration has changed. It has if
516 // both old and new configuration is specified but respective
517 // data elements aren't equal.
518 bool sock_changed = (unix_config && current_unix_control_socket_ &&
519 !unix_config->equals(*current_unix_control_socket_));
520
521 // If the previous or new socket configuration doesn't exist or
522 // the new configuration differs from the old configuration we
523 // close the existing socket and open a new socket as appropriate.
524 // Note that closing an existing socket means the client will not
525 // receive the configuration result.
526 if (!unix_config || !current_unix_control_socket_ || sock_changed) {
527 // Open the new sockets and close old ones, keeping reused.
528 if (unix_config) {
530 } else if (current_unix_control_socket_) {
532 }
533 }
534
535 // Commit the new socket configuration.
536 current_unix_control_socket_ = unix_config;
537
538 // Get new HTTP/HTTPS socket configuration.
539 ConstElementPtr http_config =
540 getD2CfgMgr()->getHttpControlSocketInfo();
541
542 // Open the new sockets and close old ones, keeping reused.
543 if (http_config) {
545 } else if (current_http_control_socket_) {
547 }
548
549 // Commit the new socket configuration.
550 current_http_control_socket_ = http_config;
551}
552
553} // namespace isc::d2
554} // namespace isc
CtrlAgentHooks Hooks
@ NEXT_STEP_DROP
drop the packet
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.
static std::string DEFAULT_AUTHENTICATION_REALM
Default HTTP authentication realm.
void closeCommandSockets()
Close http control sockets.
void addExternalSockets(bool use_external=true)
Use external sockets flag.
static HttpCommandMgr & instance()
HttpCommandMgr is a singleton class.
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the http command manager.
void openCommandSockets(const isc::data::ConstElementPtr config)
Open http control sockets using configuration.
static UnixCommandMgr & instance()
UnixCommandMgr is a singleton class.
void setIOService(const asiolink::IOServicePtr &io_service)
Sets IO service to be used by the unix command manager.
void openCommandSockets(const isc::data::ConstElementPtr config)
Opens unix control socket with parameters specified in socket_info (required parameters: socket-type:...
void closeCommandSockets()
Shuts down any open unix control sockets.
void addExternalSockets(bool use_external=true)
Use external sockets flag.
DHCP-DDNS Configuration Manager.
Definition d2_cfg_mgr.h:183
static process::DControllerBasePtr & instance()
Static singleton instance method.
D2Process(const char *name, const asiolink::IOServicePtr &io_service)
Constructor.
Definition d2_process.cc:62
static const unsigned int IO_SERVICE_RUN_TIME_USECS
Amount of time to allow the main IOSerivce to wait for ready handlers before timimg out in microsecon...
Definition d2_process.h:52
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:99
virtual void init()
Called after instantiation to perform initialization unique to D2.
Definition d2_process.cc:84
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:47
D2UpdateMgr creates and manages update transactions.
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
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
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:367
const isc::log::MessageID DHCP_DDNS_SECURITY_CHECKS_DISABLED
Definition d2_messages.h:84
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECOVERING
Definition d2_messages.h:56
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOP_ERROR
Definition d2_messages.h:64
const isc::log::MessageID DHCP_DDNS_FAILED
Definition d2_messages.h:20
const isc::log::MessageID DHCP_DDNS_LISTENING_ON_ALL_INTERFACES
Definition d2_messages.h:47
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_START_ERROR
Definition d2_messages.h:61
const isc::log::MessageID DHCP_DDNS_SHUTDOWN_COMMAND
Definition d2_messages.h:85
const isc::log::MessageID DHCP_DDNS_CONFIGURE
Definition d2_messages.h:15
const isc::log::MessageID DHCP_DDNS_CLEARED_FOR_SHUTDOWN
Definition d2_messages.h:14
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RECONFIGURING
Definition d2_messages.h:55
const isc::log::MessageID DHCP_DDNS_RUN_EXIT
Definition d2_messages.h:83
const isc::log::MessageID DHCP_DDNS_CONFIGURED_CALLOUT_DROP
Definition d2_messages.h:16
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_RESUME_ERROR
Definition d2_messages.h:58
const isc::log::MessageID DHCP_DDNS_STARTED
Definition d2_messages.h:86
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:59
boost::shared_ptr< D2Params > D2ParamsPtr
Defines a pointer for D2Params instances.
Definition d2_config.h:257
const isc::log::MessageID DHCP_DDNS_QUEUE_MGR_STOPPING
Definition d2_messages.h:63
const isc::log::MessageID DHCP_DDNS_NOT_ON_LOOPBACK
Definition d2_messages.h:48
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, string obscure)
Redact a configuration.
Defines the logger used by the top-level component of kea-lfc.