Kea: d_controller.cc Source File

| Kea 3.0.0

d_controller.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>
8
9#include <asiolink/io_service_mgr.h>
10#include <cc/command_interpreter.h>
11#include <process/cfgrpt/config_report.h>
12#include <cryptolink/crypto_hash.h>
13#include <exceptions/exceptions.h>
14#include <config/command_mgr.h>
15#include <hooks/hooks_manager.h>
16#include <log/logger.h>
17#include <log/logger_support.h>
18#include <util/encode/encode.h>
19#include <util/filesystem.h>
20#include <process/daemon.h>
21#include <process/d_log.h>
22#include <process/d_controller.h>
23#include <process/config_base.h>
24#include <kea_version.h>
25#include <functional>
26#include <sstream>
27#include <string>
28#include <unistd.h>
29#include <signal.h>
30
31using namespace isc::asiolink;
32using namespace isc::config;
33using namespace isc::data;
34using namespace isc::hooks;
35using namespace isc::util;
36namespace ph = std::placeholders;
37
38namespace isc {
39namespace process {
40
41 DControllerBasePtr DControllerBase::controller_;
42
43// Note that the constructor instantiates the controller's primary IOService.
44DControllerBase::DControllerBase(const char* app_name, const char* bin_name)
45 : app_name_(app_name), bin_name_(bin_name),
46 verbose_(false), check_only_(false),
47 io_service_(new isc::asiolink::IOService()),
48 io_signal_set_() {
49}
50
51void
52DControllerBase::setController(const DControllerBasePtr& controller) {
53 if (controller_) {
54 // This shouldn't happen, but let's make sure it can't be done.
55 // It represents a programmatic error.
56 isc_throw(DControllerBaseError, "Multiple controller instances attempted.");
57 }
58
59 controller_ = controller;
60}
61
62 ConstElementPtr
63DControllerBase::parseFile(const std::string&) {
64 ConstElementPtr elements;
65 return (elements);
66}
67
68int
69DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
70
71 // Step 1 is to parse the command line arguments.
72 try {
73 parseArgs(argc, argv);
74 } catch (const InvalidUsage& ex) {
75 usage(ex.what());
76 // rethrow it with an empty message
77 isc_throw(InvalidUsage, "");
78 }
79
80 setProcName(bin_name_);
81
82 if (isCheckOnly()) {
83 checkConfigOnly();
84 return (EXIT_SUCCESS);
85 }
86
87 // It is important that we set a default logger name because this name
88 // will be used when the user doesn't provide the logging configuration
89 // in the Kea configuration file.
90 Daemon::setDefaultLoggerName(bin_name_);
91
92 // Logger's default configuration depends on whether we are in the
93 // verbose mode or not. CfgMgr manages the logger configuration so
94 // the verbose mode is set for CfgMgr.
95 Daemon::setVerbose(verbose_);
96
97 // Do not initialize logger here if we are running unit tests. It would
98 // replace an instance of unit test specific logger.
99 if (!test_mode) {
100 // Now that we know what the mode flags are, we can init logging.
101 Daemon::loggerInit(bin_name_.c_str(), verbose_);
102 }
103
104 try {
105 checkConfigFile();
106 } catch (const std::exception& ex) {
107 LOG_FATAL(dctl_logger, DCTL_CONFIG_FILE_LOAD_FAIL)
108 .arg(app_name_).arg(ex.what());
109 isc_throw(LaunchError, "Launch Failed: " << ex.what());
110 }
111
112 try {
113 createPIDFile();
114 } catch (const DaemonPIDExists& ex) {
115 LOG_FATAL(dctl_logger, DCTL_ALREADY_RUNNING)
116 .arg(bin_name_).arg(ex.what());
117 isc_throw(LaunchError, "Launch Failed: " << ex.what());
118 } catch (const std::exception& ex) {
119 LOG_FATAL(dctl_logger, DCTL_PID_FILE_ERROR)
120 .arg(app_name_).arg(ex.what());
121 isc_throw(LaunchError, "Launch failed: " << ex.what());
122 }
123
124 // Log the starting of the service.
125 LOG_INFO(dctl_logger, DCTL_STARTING)
126 .arg(app_name_)
127 .arg(getpid())
128 .arg(VERSION)
129 .arg(PACKAGE_VERSION_TYPE);
130 // When it is not a stable version dissuade use in production.
131 if (std::string(PACKAGE_VERSION_TYPE) == "development") {
132 LOG_WARN(dctl_logger, DCTL_DEVELOPMENT_VERSION);
133 }
134 try {
135 // Step 2 is to create and initialize the application process object.
136 initProcess();
137 } catch (const std::exception& ex) {
138 LOG_FATAL(dctl_logger, DCTL_INIT_PROCESS_FAIL)
139 .arg(app_name_).arg(ex.what());
140 isc_throw(ProcessInitError,
141 "Application Process initialization failed: " << ex.what());
142 }
143
144 LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, DCTL_STANDALONE)
145 .arg(app_name_);
146
147 // Step 3 is to load configuration from file.
148 int rcode;
149 ConstElementPtr comment = parseAnswer(rcode, configFromFile());
150 if (rcode != 0) {
151 LOG_FATAL(dctl_logger, DCTL_CONFIG_FILE_LOAD_FAIL)
152 .arg(app_name_).arg(comment->stringValue());
153 isc_throw(ProcessInitError, "Could Not load configuration file: "
154 << comment->stringValue());
155 }
156
157 // Note that the controller was started.
158 start_ = boost::posix_time::second_clock::universal_time();
159
160 // Everything is clear for launch, so start the application's
161 // event loop.
162 try {
163 // Now that we have a process, we can set up signal handling.
164 initSignalHandling();
165 runProcess();
166 } catch (const std::exception& ex) {
167 LOG_FATAL(dctl_logger, DCTL_PROCESS_FAILED)
168 .arg(app_name_).arg(ex.what());
169 isc_throw(ProcessRunError,
170 "Application process event loop failed: " << ex.what());
171 }
172
173 // All done, so bail out.
174 LOG_INFO(dctl_logger, DCTL_SHUTDOWN)
175 .arg(app_name_).arg(getpid()).arg(VERSION);
176
177 return (getExitValue());
178}
179
180void
181DControllerBase::checkConfigOnly() {
182 try {
183 // We need to initialize logging, in case any error
184 // messages are to be printed.
185 // This is just a test, so we don't care about lockfile.
186 setenv("KEA_LOCKFILE_DIR", "none", 0);
187 Daemon::setDefaultLoggerName(bin_name_);
188 Daemon::setVerbose(verbose_);
189 Daemon::loggerInit(bin_name_.c_str(), verbose_);
190
191 // Check the syntax first.
192 std::string config_file = getConfigFile();
193 if (config_file.empty()) {
194 // Basic sanity check: file name must not be empty.
195 isc_throw(InvalidUsage, "JSON configuration file not specified");
196 }
197 ConstElementPtr whole_config = parseFile(config_file);
198 if (!whole_config) {
199 // No fallback to fromJSONFile
200 isc_throw(InvalidUsage, "No configuration found");
201 }
202 if (verbose_) {
203 std::cerr << "Syntax check OK" << std::endl;
204 }
205
206 // Check the logic next.
207 ConstElementPtr module_config;
208 module_config = whole_config->get(getAppName());
209 if (!module_config) {
210 isc_throw(InvalidUsage, "Config file " << config_file <<
211 " does not include '" << getAppName() << "' entry");
212 }
213 if (module_config->getType() != Element::map) {
214 isc_throw(InvalidUsage, "Config file " << config_file <<
215 " includes not map '" << getAppName() << "' entry");
216 }
217
218 // Handle other (i.e. not application name) objects.
219 std::string errmsg = handleOtherObjects(whole_config);
220 if (!errmsg.empty()) {
221 isc_throw(InvalidUsage, "Config file " << config_file << errmsg);
222 }
223
224 // Get an application process object.
225 initProcess();
226
227 ConstElementPtr answer = checkConfig(module_config);
228 int rcode = 0;
229 answer = parseAnswer(rcode, answer);
230 if (rcode != 0) {
231 isc_throw(InvalidUsage, "Error encountered: "
232 << answer->stringValue());
233 }
234 } catch (const VersionMessage&) {
235 throw;
236 } catch (const InvalidUsage&) {
237 throw;
238 } catch (const std::exception& ex) {
239 isc_throw(InvalidUsage, "Syntax check failed with: " << ex.what());
240 }
241 return;
242}
243
244void
245DControllerBase::parseArgs(int argc, char* argv[]) {
246
247 if (argc == 1) {
248 isc_throw(InvalidUsage, "");
249 }
250
251 // Iterate over the given command line options. If its a stock option
252 // ("c" or "d") handle it here. If its a valid custom option, then
253 // invoke customOption.
254 int ch;
255 optarg = 0;
256 opterr = 0;
257 optind = 1;
258 std::string opts("dvVWc:t:X" + getCustomOpts());
259
260 // Defer exhausting of arguments to the end.
261 ExhaustOptions e(argc, argv, opts);
262
263 while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
264 switch (ch) {
265 case 'd':
266 // Enables verbose logging.
267 verbose_ = true;
268 break;
269
270 case 'v':
271 // gather Kea version and throw so main() can catch and return
272 // rather than calling exit() here which disrupts gtest.
273 isc_throw(VersionMessage, getVersion(false));
274 break;
275
276 case 'V':
277 // gather Kea version and throw so main() can catch and return
278 // rather than calling exit() here which disrupts gtest.
279 isc_throw(VersionMessage, getVersion(true));
280 break;
281
282 case 'W':
283 // gather Kea config report and throw so main() can catch and
284 // return rather than calling exit() here which disrupts gtest.
285 isc_throw(VersionMessage, isc::detail::getConfigReport());
286 break;
287
288 case 'c':
289 case 't':
290 // config file name
291 if (optarg == NULL) {
292 isc_throw(InvalidUsage, "configuration file name missing");
293 }
294
295 setConfigFile(optarg);
296
297 if (ch == 't') {
298 check_only_ = true;
299 }
300 break;
301
302 case 'X': // relax security checks
303 file::PathChecker::enableEnforcement(false);
304 break;
305
306 case '?': {
307 char const saved_optopt(optopt);
308 std::string const saved_optarg(optarg ? optarg : std::string());
309
310 // We hit an invalid option.
311 isc_throw(InvalidUsage, "unsupported option: -" << saved_optopt <<
312 (saved_optarg.empty() ? std::string() : " " + saved_optarg));
313
314 break;
315 }
316
317 default:
318 // We hit a valid custom option
319 if (!customOption(ch, optarg)) {
320 char const saved_optopt(optopt);
321 std::string const saved_optarg(optarg ? optarg : std::string());
322
323 // We hit an invalid option.
324 isc_throw(InvalidUsage, "unsupported option: -" << saved_optopt <<
325 (saved_optarg.empty() ? std::string() : " " + saved_optarg));
326 }
327 break;
328 }
329 }
330
331 // There was too much information on the command line.
332 if (argc > optind) {
333 isc_throw(InvalidUsage, "extraneous command line information");
334 }
335}
336
337bool
338DControllerBase::customOption(int /* option */, char* /*optarg*/) {
339 // Default implementation returns false.
340 return (false);
341}
342
343void
344DControllerBase::initProcess() {
345 LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, DCTL_INIT_PROCESS)
346 .arg(app_name_);
347
348 // Invoke virtual method to instantiate the application process.
349 try {
350 process_.reset(createProcess());
351 } catch (const std::exception& ex) {
352 isc_throw(DControllerBaseError, std::string("createProcess failed: ") +
353 ex.what());
354 }
355
356 // This is pretty unlikely, but will test for it just to be safe..
357 if (!process_) {
358 isc_throw(DControllerBaseError, "createProcess returned NULL");
359 }
360
361 // Invoke application's init method (Note this call should throw
362 // DProcessBaseError if it fails).
363 process_->init();
364}
365
366 ConstElementPtr
367DControllerBase::configFromFile() {
368 // Rollback any previous staging configuration. For D2, only a
369 // logger configuration is used here.
370 // We're not using cfgmgr to store logging configuration anymore.
371 // isc::dhcp::CfgMgr::instance().rollback();
372
373 // Will hold configuration.
374 ConstElementPtr module_config;
375 // Will receive configuration result.
376 ConstElementPtr answer;
377 try {
378 std::string config_file = getConfigFile();
379 if (config_file.empty()) {
380 // Basic sanity check: file name must not be empty.
381 isc_throw(BadValue, "JSON configuration file not specified. Please "
382 "use -c command line option.");
383 }
384
385 // If parseFile returns an empty pointer, then pass the file onto the
386 // original JSON parser.
387 ConstElementPtr whole_config = parseFile(config_file);
388 if (!whole_config) {
389 // Read contents of the file and parse it as JSON
390 whole_config = Element::fromJSONFile(config_file, true);
391 }
392
393 // Extract derivation-specific portion of the configuration.
394 module_config = whole_config->get(getAppName());
395 if (!module_config) {
396 isc_throw(BadValue, "Config file " << config_file <<
397 " does not include '" <<
398 getAppName() << "' entry.");
399 }
400 if (module_config->getType() != Element::map) {
401 isc_throw(InvalidUsage, "Config file " << config_file <<
402 " includes not map '" << getAppName() << "' entry");
403 }
404
405 // Handle other (i.e. not application name) objects.
406 std::string errmsg = handleOtherObjects(whole_config);
407 if (!errmsg.empty()) {
408 isc_throw(InvalidUsage, "Config file " << config_file << errmsg);
409 }
410
411 // Let's configure logging before applying the configuration,
412 // so we can log things during configuration process.
413
414 // Temporary storage for logging configuration
415 ConfigPtr storage(new ConfigBase());
416
417 // Configure logging to the temporary storage.
418 Daemon::configureLogger(module_config, storage);
419
420 // Let's apply the new logging. We do it early, so we'll be able
421 // to print out what exactly is wrong with the new config in
422 // case of problems.
423 storage->applyLoggingCfg();
424
425 answer = updateConfig(module_config);
426 // In all cases the right logging configuration is in the context.
427 process_->getCfgMgr()->getContext()->applyLoggingCfg();
428 } catch (const std::exception& ex) {
429 // Rollback logging configuration.
430 process_->getCfgMgr()->getContext()->applyLoggingCfg();
431
432 // build an error result
433 ConstElementPtr error = createAnswer(CONTROL_RESULT_ERROR,
434 std::string("Configuration parsing failed: ") + ex.what());
435 return (error);
436 }
437
438 return (answer);
439}
440
441void
442DControllerBase::runProcess() {
443 LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT, DCTL_RUN_PROCESS)
444 .arg(app_name_);
445 if (!process_) {
446 // This should not be possible.
447 isc_throw(DControllerBaseError, "Process not initialized");
448 }
449
450 // Invoke the application process's run method. This may throw
451 // DProcessBaseError
452 process_->run();
453}
454
455// Instance method for handling new config
456 ConstElementPtr
457DControllerBase::updateConfig(ConstElementPtr new_config) {
458 return (process_->configure(new_config, false));
459}
460
461// Instance method for checking new config
462 ConstElementPtr
463DControllerBase::checkConfig(ConstElementPtr new_config) {
464 return (process_->configure(new_config, true));
465}
466
467 ConstElementPtr
468DControllerBase::configGetHandler(const std::string&,
469 ConstElementPtr /*args*/) {
470 ElementPtr config = process_->getCfgMgr()->getContext()->toElement();
471 std::string hash = BaseCommandMgr::getHash(config);
472 config->set("hash", Element::create(hash));
473
474 return (createAnswer(CONTROL_RESULT_SUCCESS, config));
475}
476
477 ConstElementPtr
478DControllerBase::configHashGetHandler(const std::string&,
479 ConstElementPtr /*args*/) {
480 ConstElementPtr config = process_->getCfgMgr()->getContext()->toElement();
481 std::string hash = BaseCommandMgr::getHash(config);
482 ElementPtr params = Element::createMap();
483 params->set("hash", Element::create(hash));
484 return (createAnswer(CONTROL_RESULT_SUCCESS, params));
485}
486
487 ConstElementPtr
488DControllerBase::configWriteHandler(const std::string&,
489 ConstElementPtr args) {
490 std::string filename;
491
492 if (args) {
493 if (args->getType() != Element::map) {
494 return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
495 }
496 ConstElementPtr filename_param = args->get("filename");
497 if (filename_param) {
498 if (filename_param->getType() != Element::string) {
499 return (createAnswer(CONTROL_RESULT_ERROR,
500 "passed parameter 'filename' "
501 "is not a string"));
502 }
503 filename = filename_param->stringValue();
504 }
505 }
506
507 if (filename.empty()) {
508 // filename parameter was not specified, so let's use
509 // whatever we remember
510 filename = getConfigFile();
511 if (filename.empty()) {
512 return (createAnswer(CONTROL_RESULT_ERROR,
513 "Unable to determine filename."
514 "Please specify filename explicitly."));
515 }
516 } else {
517 try {
518 checkWriteConfigFile(filename);
519 } catch (const isc::Exception& ex) {
520 std::ostringstream msg;
521 msg << "not allowed to write config into " << filename
522 << ": " << ex.what();
523 return (createAnswer(CONTROL_RESULT_ERROR, msg.str()));
524 }
525 }
526
527 // Ok, it's time to write the file.
528 size_t size = 0;
529
530 try {
531 ElementPtr cfg = process_->getCfgMgr()->getContext()->toElement();
532 size = writeConfigFile(filename, cfg);
533 } catch (const isc::Exception& ex) {
534 return (createAnswer(CONTROL_RESULT_ERROR,
535 std::string("Error during config-write:")
536 + ex.what()));
537 }
538 if (size == 0) {
539 return (createAnswer(CONTROL_RESULT_ERROR,
540 "Error writing configuration to " + filename));
541 }
542
543 // Ok, it's time to return the successful response.
544 ElementPtr params = Element::createMap();
545 params->set("size", Element::create(static_cast<long long>(size)));
546 params->set("filename", Element::create(filename));
547
548 return (createAnswer(CONTROL_RESULT_SUCCESS, "Configuration written to "
549 + filename + " successful", params));
550}
551
552std::string
553DControllerBase::handleOtherObjects(ConstElementPtr args) {
554 // Check obsolete or unknown (aka unsupported) objects.
555 const std::string& app_name = getAppName();
556 std::string errmsg;
557 for (auto const& obj : args->mapValue()) {
558 const std::string& obj_name = obj.first;
559 if (obj_name == app_name) {
560 continue;
561 }
562 LOG_ERROR(dctl_logger, DCTL_CONFIG_DEPRECATED)
563 .arg("'" + obj_name + "', defining anything in global level besides '"
564 + app_name + "' is no longer supported.");
565 if (errmsg.empty()) {
566 errmsg = " contains unsupported '" + obj_name + "' parameter";
567 } else {
568 errmsg += " (and '" + obj_name + "')";
569 }
570 }
571 return (errmsg);
572}
573
574 ConstElementPtr
575DControllerBase::configTestHandler(const std::string&, ConstElementPtr args) {
576 const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error
577 ConstElementPtr module_config;
578 std::string app_name = getAppName();
579 std::string message;
580
581 // Command arguments are expected to be:
582 // { "Module": { ... } }
583 if (!args) {
584 message = "Missing mandatory 'arguments' parameter.";
585 } else {
586 module_config = args->get(app_name);
587 if (!module_config) {
588 message = "Missing mandatory '" + app_name + "' parameter.";
589 } else if (module_config->getType() != Element::map) {
590 message = "'" + app_name + "' parameter expected to be a map.";
591 }
592 }
593
594 if (message.empty()) {
595 // Handle other (i.e. not application name) objects.
596 std::string errmsg = handleOtherObjects(args);
597 if (!errmsg.empty()) {
598 message = "'arguments' parameter" + errmsg;
599 }
600 }
601
602 if (!message.empty()) {
603 // Something is amiss with arguments, return a failure response.
604 ConstElementPtr result = isc::config::createAnswer(status_code,
605 message);
606 return (result);
607 }
608
609 // We are starting the configuration process so we should remove any
610 // staging configuration that has been created during previous
611 // configuration attempts.
612 // We're not using cfgmgr to store logging information anymore.
613 // isc::dhcp::CfgMgr::instance().rollback();
614
615 // Now we check the server proper.
616 return (checkConfig(module_config));
617}
618
619 ConstElementPtr
620DControllerBase::configReloadHandler(const std::string&, ConstElementPtr) {
621 // Add reload in message?
622 return (configFromFile());
623}
624
625 ConstElementPtr
626DControllerBase::configSetHandler(const std::string&, ConstElementPtr args) {
627 const int status_code = CONTROL_RESULT_ERROR; // 1 indicates an error
628 ConstElementPtr module_config;
629 std::string app_name = getAppName();
630 std::string message;
631
632 // Command arguments are expected to be:
633 // { "Module": { ... } }
634 if (!args) {
635 message = "Missing mandatory 'arguments' parameter.";
636 } else {
637 module_config = args->get(app_name);
638 if (!module_config) {
639 message = "Missing mandatory '" + app_name + "' parameter.";
640 } else if (module_config->getType() != Element::map) {
641 message = "'" + app_name + "' parameter expected to be a map.";
642 }
643 }
644
645 if (!message.empty()) {
646 // Something is amiss with arguments, return a failure response.
647 ConstElementPtr result = isc::config::createAnswer(status_code,
648 message);
649 return (result);
650 }
651
652 try {
653
654 // Handle other (i.e. not application name) objects.
655 handleOtherObjects(args);
656
657 // We are starting the configuration process so we should remove any
658 // staging configuration that has been created during previous
659 // configuration attempts.
660 // We're not using cfgmgr to store logging information anymore.
661 // isc::dhcp::CfgMgr::instance().rollback();
662
663 // Temporary storage for logging configuration
664 ConfigPtr storage(new ConfigBase());
665
666 // Configure logging to the temporary storage.
667 Daemon::configureLogger(module_config, storage);
668
669 // Let's apply the new logging. We do it early, so we'll be able
670 // to print out what exactly is wrong with the new config in
671 // case of problems.
672 storage->applyLoggingCfg();
673
674 ConstElementPtr answer = updateConfig(module_config);
675 int rcode = 0;
676 parseAnswer(rcode, answer);
677 // In all cases the right logging configuration is in the context.
678 process_->getCfgMgr()->getContext()->applyLoggingCfg();
679 return (answer);
680 } catch (const std::exception& ex) {
681 // Rollback logging configuration.
682 process_->getCfgMgr()->getContext()->applyLoggingCfg();
683
684 // build an error result
685 ConstElementPtr error = createAnswer(CONTROL_RESULT_ERROR,
686 std::string("Configuration parsing failed: ") + ex.what());
687 return (error);
688 }
689}
690
691 ConstElementPtr
692DControllerBase::serverTagGetHandler(const std::string&, ConstElementPtr) {
693 const std::string& tag = process_->getCfgMgr()->getContext()->getServerTag();
694 ElementPtr response = Element::createMap();
695 response->set("server-tag", Element::create(tag));
696
697 return (createAnswer(CONTROL_RESULT_SUCCESS, response));
698}
699
700 ConstElementPtr
701DControllerBase::statusGetHandler(const std::string&, ConstElementPtr) {
702 ElementPtr status = Element::createMap();
703 status->set("pid", Element::create(static_cast<int>(getpid())));
704
705 auto now = boost::posix_time::second_clock::universal_time();
706 if (!start_.is_not_a_date_time()) {
707 auto uptime = now - start_;
708 status->set("uptime", Element::create(uptime.total_seconds()));
709 }
710
711 auto last_commit = process_->getCfgMgr()->getContext()->getLastCommitTime();
712 if (!last_commit.is_not_a_date_time()) {
713 auto reload = now - last_commit;
714 status->set("reload", Element::create(reload.total_seconds()));
715 }
716
717 return (createAnswer(CONTROL_RESULT_SUCCESS, status));
718}
719
720 ConstElementPtr
721DControllerBase::versionGetHandler(const std::string&, ConstElementPtr) {
722 ConstElementPtr answer;
723
724 // For version-get put the extended version in arguments
725 ElementPtr extended = Element::create(getVersion(true));
726 ElementPtr arguments = Element::createMap();
727 arguments->set("extended", extended);
728 answer = createAnswer(CONTROL_RESULT_SUCCESS, getVersion(false), arguments);
729 return (answer);
730}
731
732 ConstElementPtr
733DControllerBase::buildReportHandler(const std::string&, ConstElementPtr) {
734 return (createAnswer(CONTROL_RESULT_SUCCESS, isc::detail::getConfigReport()));
735}
736
737 ConstElementPtr
738DControllerBase::shutdownHandler(const std::string&, ConstElementPtr args) {
739 // Shutdown is universal. If its not that, then try it as
740 // a custom command supported by the derivation. If that
741 // doesn't pan out either, than send to it the application
742 // as it may be supported there.
743
744 int exit_value = EXIT_SUCCESS;
745 if (args) {
746 // @todo Should we go ahead and shutdown even if the args are invalid?
747 if (args->getType() != Element::map) {
748 return (createAnswer(CONTROL_RESULT_ERROR, "Argument must be a map"));
749 }
750
751 ConstElementPtr param = args->get("exit-value");
752 if (param) {
753 if (param->getType() != Element::integer) {
754 return (createAnswer(CONTROL_RESULT_ERROR,
755 "parameter 'exit-value' is not an integer"));
756 }
757
758 exit_value = param->intValue();
759 }
760 }
761
762 setExitValue(exit_value);
763 return (shutdownProcess(args));
764}
765
766 ConstElementPtr
767DControllerBase::shutdownProcess(ConstElementPtr args) {
768 if (process_) {
769 return (process_->shutdown(args));
770 }
771
772 // Not really a failure, but this condition is worth noting. In reality
773 // it should be pretty hard to cause this.
774 LOG_WARN(dctl_logger, DCTL_NOT_RUNNING).arg(app_name_);
775 return (createAnswer(CONTROL_RESULT_SUCCESS, "Process has not been initialized"));
776}
777
778void
779DControllerBase::initSignalHandling() {
781
782 // Create our signal set.
783 io_signal_set_.reset(new IOSignalSet(io_service_,
784 std::bind(&DControllerBase::
785 processSignal,
786 this, ph::_1)));
787 // Register for the signals we wish to handle.
788 io_signal_set_->add(SIGHUP);
789 io_signal_set_->add(SIGINT);
790 io_signal_set_->add(SIGTERM);
791}
792
793void
794DControllerBase::processSignal(int signum) {
795 switch (signum) {
796 case SIGHUP:
797 {
798 LOG_INFO(dctl_logger, DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD)
799 .arg(signum).arg(getConfigFile());
800 int rcode;
801 ConstElementPtr comment = parseAnswer(rcode, configFromFile());
802 if (rcode != 0) {
803 LOG_ERROR(dctl_logger, DCTL_CFG_FILE_RELOAD_ERROR)
804 .arg(comment->stringValue());
805 }
806
807 break;
808 }
809
810 case SIGINT:
811 case SIGTERM:
812 {
813 LOG_DEBUG(dctl_logger, isc::log::DBGLVL_START_SHUT,
814 DCTL_SHUTDOWN_SIGNAL_RECVD).arg(signum);
815 ElementPtr arg_set;
816 shutdownHandler(SHUT_DOWN_COMMAND, arg_set);
817 break;
818 }
819
820 default:
821 LOG_WARN(dctl_logger, DCTL_UNSUPPORTED_SIGNAL).arg(signum);
822 break;
823 }
824}
825
826void
827DControllerBase::usage(const std::string & text) {
828 if (text != "") {
829 std::cerr << "Usage error: " << text << std::endl;
830 }
831
832 std::cerr << "Usage: " << bin_name_ << std::endl
833 << " -v: print version number and exit" << std::endl
834 << " -V: print extended version information and exit"
835 << std::endl
836 << " -W: display the configuration report and exit"
837 << std::endl
838 << " -d: optional, verbose output " << std::endl
839 << " -c <config file name> : mandatory,"
840 << " specify name of configuration file" << std::endl
841 << " -t <config file name> : check the"
842 << " configuration file and exit" << std::endl
843 << " -X: disables security restrictions" << std::endl;
844
845 // add any derivation specific usage
846 std::cerr << getUsageText() << std::endl;
847}
848
849DControllerBase::~DControllerBase() {
850 // Explicitly unload hooks
851 HooksManager::prepareUnloadLibraries();
852 if (!HooksManager::unloadLibraries()) {
853 auto names = HooksManager::getLibraryNames();
854 std::string msg;
855 if (!names.empty()) {
856 msg = names[0];
857 for (size_t i = 1; i < names.size(); ++i) {
858 msg += std::string(", ") + names[i];
859 }
860 }
861 LOG_ERROR(dctl_logger, DCTL_UNLOAD_LIBRARIES_ERROR).arg(msg);
862 }
863
864 IOServiceMgr::instance().clearIOServices();
865
866 io_signal_set_.reset();
867 try {
868 getIOService()->poll();
869 } catch (...) {
870 // Don't want to throw exceptions from the destructor. The process
871 // is shutting down anyway.
872 }
873}
874
875std::string
876DControllerBase::getVersion(bool extended) {
877 std::stringstream tmp;
878
879 tmp << VERSION;
880 if (extended) {
881 tmp << " (" << EXTENDED_VERSION << ")" << std::endl;
882 tmp << "premium: " << PREMIUM_EXTENDED_VERSION << std::endl;
883 tmp << "linked with:" << std::endl;
884 tmp << "- " << isc::log::Logger::getVersion() << std::endl;
885 tmp << "- " << isc::cryptolink::CryptoLink::getVersion();
886 }
887
888 return (tmp.str());
889}
890
891} // end of namespace isc::process
892
893} // end of namespace isc
Element::create
static ElementPtr create(const Position &pos=ZERO_POSITION())
Definition data.cc:249
Element::fromJSONFile
static ElementPtr fromJSONFile(const std::string &file_name, bool preproc=false)
Reads contents of specified file and interprets it as JSON.
Definition data.cc:817
Element::map
@ map
Definition data.h:147
Element::integer
@ integer
Definition data.h:140
Element::string
@ string
Definition data.h:144
Element::createMap
static ElementPtr createMap(const Position &pos=ZERO_POSITION())
Creates an empty MapElement type ElementPtr.
Definition data.cc:304
isc::BadValue
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
Definition exceptions/exceptions.h:132
isc::Exception
This is a base class for exceptions thrown from the DNS library module.
Definition exceptions/exceptions.h:23
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition exceptions/exceptions.cc:32
isc::asiolink::IOServiceMgr::clearIOServices
void clearIOServices()
Clear the list of IOService objects.
Definition io_service_mgr.h:53
isc::asiolink::IOServiceMgr::instance
static IOServiceMgr & instance()
Access the IOServiceMgr singleton instance.
Definition io_service_mgr.cc:14
isc::asiolink::IOService
The IOService class is a wrapper for the ASIO io_context class.
Definition io_service.h:32
isc::asiolink::IOSignalSet
Implements an asynchronous "signal" for IOService driven processing.
Definition io_service_signal.h:26
isc::config::BaseCommandMgr::getHash
static std::string getHash(const isc::data::ConstElementPtr &config)
returns a hash of a given Element structure
Definition base_command_mgr.cc:193
isc::cryptolink::CryptoLink::getVersion
static std::string getVersion()
Get version string.
Definition botan_link.cc:76
isc::hooks::HooksManager::getLibraryNames
static std::vector< std::string > getLibraryNames()
Return list of loaded libraries.
Definition hooks_manager.cc:220
isc::hooks::HooksManager::unloadLibraries
static bool unloadLibraries()
Unload libraries.
Definition hooks_manager.cc:177
isc::hooks::HooksManager::prepareUnloadLibraries
static void prepareUnloadLibraries()
Prepare the unloading of libraries.
Definition hooks_manager.cc:191
isc::log::Logger::getVersion
static std::string getVersion()
Version.
Definition log/logger.cc:60
isc::process::ConfigBase
Base class for all configurations.
Definition config_base.h:33
isc::process::DControllerBaseError
Exception thrown when the controller encounters an operational error.
Definition d_controller.h:70
isc::process::DControllerBase::configReloadHandler
isc::data::ConstElementPtr configReloadHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for config-reload command
Definition d_controller.cc:620
isc::process::DControllerBase::buildReportHandler
isc::data::ConstElementPtr buildReportHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for 'build-report' command
Definition d_controller.cc:733
isc::process::DControllerBase::runProcess
void runProcess()
Invokes the application process's event loop,(DBaseProcess::run).
Definition d_controller.cc:442
isc::process::DControllerBase::initProcess
void initProcess()
Instantiates the application process and then initializes it.
Definition d_controller.cc:344
isc::process::DControllerBase::statusGetHandler
isc::data::ConstElementPtr statusGetHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for status-get command
Definition d_controller.cc:701
isc::process::DControllerBase::usage
void usage(const std::string &text)
Prints the program usage text to std error.
Definition d_controller.cc:827
isc::process::DControllerBase::getIOService
asiolink::IOServicePtr & getIOService()
Getter for fetching the controller's IOService.
Definition d_controller.h:469
isc::process::DControllerBase::shutdownProcess
isc::data::ConstElementPtr shutdownProcess(isc::data::ConstElementPtr args)
Initiates shutdown procedure.
Definition d_controller.cc:767
isc::process::DControllerBase::getCustomOpts
virtual const std::string getCustomOpts() const
Virtual method which returns a string containing the option letters for any custom command line optio...
Definition d_controller.h:408
isc::process::DControllerBase::processSignal
virtual void processSignal(int signum)
Application-level signal processing method.
Definition d_controller.cc:794
isc::process::DControllerBase::~DControllerBase
virtual ~DControllerBase()
Destructor.
Definition d_controller.cc:849
isc::process::DControllerBase::configWriteHandler
isc::data::ConstElementPtr configWriteHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for config-write command
Definition d_controller.cc:488
isc::process::DControllerBase::setController
static void setController(const DControllerBasePtr &controller)
Static setter which sets the singleton instance.
Definition d_controller.cc:52
isc::process::DControllerBase::handleOtherObjects
std::string handleOtherObjects(isc::data::ConstElementPtr args)
Deals with other (i.e.
Definition d_controller.cc:553
isc::process::DControllerBase::versionGetHandler
isc::data::ConstElementPtr versionGetHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for version-get command
Definition d_controller.cc:721
isc::process::DControllerBase::configSetHandler
isc::data::ConstElementPtr configSetHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for config-set command
Definition d_controller.cc:626
isc::process::DControllerBase::initSignalHandling
void initSignalHandling()
Initializes signal handling.
Definition d_controller.cc:779
isc::process::DControllerBase::getUsageText
virtual const std::string getUsageText() const
Virtual method which can be used to contribute derivation specific usage text.
Definition d_controller.h:398
isc::process::DControllerBase::checkConfig
virtual isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr new_config)
Instance method invoked by the configuration event handler and which processes the actual configurati...
Definition d_controller.cc:463
isc::process::DControllerBase::createProcess
virtual DProcessBase * createProcess()=0
Abstract method that is responsible for instantiating the application process object.
isc::process::DControllerBase::isCheckOnly
bool isCheckOnly() const
Supplies whether or not check only mode is enabled.
Definition d_controller.h:453
isc::process::DControllerBase::configGetHandler
isc::data::ConstElementPtr configGetHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for config-get command
Definition d_controller.cc:468
isc::process::DControllerBase::launch
virtual int launch(int argc, char *argv[], const bool test_mode)
Acts as the primary entry point into the controller execution and provides the outermost application ...
Definition d_controller.cc:69
isc::process::DControllerBase::parseArgs
void parseArgs(int argc, char *argv[])
Processes the command line arguments.
Definition d_controller.cc:245
isc::process::DControllerBase::customOption
virtual bool customOption(int option, char *optarg)
Virtual method that provides derivations the opportunity to support additional command line options.
Definition d_controller.cc:338
isc::process::DControllerBase::configHashGetHandler
isc::data::ConstElementPtr configHashGetHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for config-hash-get command
Definition d_controller.cc:478
isc::process::DControllerBase::configTestHandler
isc::data::ConstElementPtr configTestHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for config-test command
Definition d_controller.cc:575
isc::process::DControllerBase::serverTagGetHandler
isc::data::ConstElementPtr serverTagGetHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for server-tag-get command
Definition d_controller.cc:692
isc::process::DControllerBase::getAppName
std::string getAppName() const
Fetches the name of the application under control.
Definition d_controller.h:221
isc::process::DControllerBase::parseFile
virtual isc::data::ConstElementPtr parseFile(const std::string &file_name)
Parse a given file into Elements.
Definition d_controller.cc:63
isc::process::DControllerBase::configFromFile
virtual isc::data::ConstElementPtr configFromFile()
Reconfigures the process from a configuration file.
Definition d_controller.cc:367
isc::process::DControllerBase::getVersion
std::string getVersion(bool extended)
returns Kea version on stdout and exit.
Definition d_controller.cc:876
isc::process::DControllerBase::DControllerBase
DControllerBase(const char *app_name, const char *bin_name)
Constructor.
Definition d_controller.cc:44
isc::process::DControllerBase::updateConfig
virtual isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr new_config)
Instance method invoked by the configuration event handler and which processes the actual configurati...
Definition d_controller.cc:457
isc::process::DControllerBase::checkConfigOnly
void checkConfigOnly()
Check the configuration.
Definition d_controller.cc:181
isc::process::DControllerBase::shutdownHandler
isc::data::ConstElementPtr shutdownHandler(const std::string &command, isc::data::ConstElementPtr args)
handler for 'shutdown' command
Definition d_controller.cc:738
isc::process::DaemonPIDExists
Exception thrown when the PID file points to a live PID.
Definition daemon.h:25
isc::process::Daemon::getConfigFile
std::string getConfigFile() const
Returns config file name.
Definition daemon.cc:104
isc::process::Daemon::writeConfigFile
virtual size_t writeConfigFile(const std::string &config_file, isc::data::ConstElementPtr cfg=isc::data::ConstElementPtr()) const
Writes current configuration to specified file.
Definition daemon.cc:250
isc::process::Daemon::setVerbose
static void setVerbose(const bool verbose)
Sets or clears verbose mode.
Definition daemon.cc:79
isc::process::Daemon::configureLogger
static void configureLogger(const isc::data::ConstElementPtr &log_config, const isc::process::ConfigPtr &storage)
Configures logger.
Definition daemon.cc:66
isc::process::Daemon::start_
boost::posix_time::ptime start_
Timestamp of the start of the daemon.
Definition daemon.h:272
isc::process::Daemon::loggerInit
static void loggerInit(const char *log_name, bool verbose)
Initializes logger.
Definition daemon.cc:88
isc::process::Daemon::checkWriteConfigFile
void checkWriteConfigFile(std::string &file)
Checks the to-be-written configuration file name.
Definition daemon.cc:129
isc::process::Daemon::setExitValue
void setExitValue(int value)
Sets the exit value.
Definition daemon.h:236
isc::process::Daemon::checkConfigFile
void checkConfigFile() const
Checks the configuration file name.
Definition daemon.cc:114
isc::process::Daemon::setDefaultLoggerName
static void setDefaultLoggerName(const std::string &logger)
Sets the default logger name.
Definition daemon.h:224
isc::process::Daemon::setProcName
static void setProcName(const std::string &proc_name)
Sets the process name.
Definition daemon.cc:156
isc::process::Daemon::getExitValue
int getExitValue()
Fetches the exit value.
Definition daemon.h:229
isc::process::Daemon::createPIDFile
void createPIDFile(int pid=0)
Creates the PID file.
Definition daemon.cc:224
isc::process::Daemon::setConfigFile
void setConfigFile(const std::string &config_file)
Sets the configuration file name.
Definition daemon.cc:109
isc::process::InvalidUsage
Exception thrown when the command line is invalid.
Definition d_controller.h:30
isc::process::LaunchError
Exception thrown when the controller launch fails.
Definition d_controller.h:48
isc::process::ProcessInitError
Exception thrown when the application process fails.
Definition d_controller.h:55
isc::process::ProcessRunError
Exception thrown when the application process encounters an operation in its event loop (i....
Definition d_controller.h:63
isc::process::VersionMessage
Exception used to convey version info upwards.
Definition d_controller.h:41
isc::util::file::PathChecker::enableEnforcement
static void enableEnforcement(bool enable)
Enables or disables security enforcment checks.
Definition filesystem.cc:345
command_interpreter.h
This file contains several functions and constants that are used for handling commands and responses ...
command_mgr.h
config_base.h
config_report.h
crypto_hash.h
d_controller.h
d_log.h
daemon.h
encode.h
exceptions.h
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
filesystem.h
hooks_manager.h
io_service_mgr.h
usage
void usage()
Print Usage.
Definition log/compiler/message.cc:76
logger.h
logger_support.h
Logging initialization functions.
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition macros.h:32
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition macros.h:20
LOG_WARN
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition macros.h:26
LOG_FATAL
#define LOG_FATAL(LOGGER, MESSAGE)
Macro to conveniently test fatal output and log it.
Definition macros.h:38
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition macros.h:14
asiolink
A wrapper interface for the ASIO library.
isc::asiolink
Definition addr_utilities.cc:170
isc::config
Definition command_interpreter.cc:23
isc::config::parseAnswer
ConstElementPtr parseAnswer(int &rcode, const ConstElementPtr &msg)
Parses a standard config/command level answer and returns arguments or text status code.
Definition command_interpreter.cc:96
isc::config::CONTROL_RESULT_ERROR
const int CONTROL_RESULT_ERROR
Status code indicating a general failure.
Definition command_interpreter.h:45
isc::config::createAnswer
ConstElementPtr createAnswer()
Creates a standard config/command level success answer message (i.e.
Definition command_interpreter.cc:54
isc::config::CONTROL_RESULT_SUCCESS
const int CONTROL_RESULT_SUCCESS
Status code indicating a successful operation.
Definition command_interpreter.h:42
isc::data
Definition base_stamped_element.cc:12
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition data.h:29
isc::data::ElementPtr
boost::shared_ptr< Element > ElementPtr
Definition data.h:28
isc::detail::getConfigReport
std::string getConfigReport()
Definition cfgrpt.cc:20
isc::hooks
Definition callout_handle.cc:21
isc::log::DBGLVL_START_SHUT
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...
Definition log_dbglevels.h:50
isc::process
Definition cb_ctl_base.h:22
isc::process::dctl_logger
isc::log::Logger dctl_logger("dctl")
Defines the logger used within libkea-process library.
Definition d_log.h:18
isc::process::DCTL_DEVELOPMENT_VERSION
const isc::log::MessageID DCTL_DEVELOPMENT_VERSION
Definition process_messages.h:22
isc::process::DCTL_SHUTDOWN
const isc::log::MessageID DCTL_SHUTDOWN
Definition process_messages.h:31
isc::process::DCTL_CFG_FILE_RELOAD_ERROR
const isc::log::MessageID DCTL_CFG_FILE_RELOAD_ERROR
Definition process_messages.h:12
isc::process::DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD
const isc::log::MessageID DCTL_CFG_FILE_RELOAD_SIGNAL_RECVD
Definition process_messages.h:13
isc::process::DCTL_RUN_PROCESS
const isc::log::MessageID DCTL_RUN_PROCESS
Definition process_messages.h:30
isc::process::DCTL_STANDALONE
const isc::log::MessageID DCTL_STANDALONE
Definition process_messages.h:33
isc::process::DCTL_PID_FILE_ERROR
const isc::log::MessageID DCTL_PID_FILE_ERROR
Definition process_messages.h:28
isc::process::ConfigPtr
boost::shared_ptr< ConfigBase > ConfigPtr
Non-const pointer to the ConfigBase.
Definition config_base.h:176
isc::process::DControllerBasePtr
boost::shared_ptr< DControllerBase > DControllerBasePtr
Definition d_controller.h:78
isc::process::DCTL_UNLOAD_LIBRARIES_ERROR
const isc::log::MessageID DCTL_UNLOAD_LIBRARIES_ERROR
Definition process_messages.h:35
isc::process::DCTL_STARTING
const isc::log::MessageID DCTL_STARTING
Definition process_messages.h:34
isc::process::DCTL_PROCESS_FAILED
const isc::log::MessageID DCTL_PROCESS_FAILED
Definition process_messages.h:29
isc::process::DCTL_SHUTDOWN_SIGNAL_RECVD
const isc::log::MessageID DCTL_SHUTDOWN_SIGNAL_RECVD
Definition process_messages.h:32
isc::process::DCTL_INIT_PROCESS_FAIL
const isc::log::MessageID DCTL_INIT_PROCESS_FAIL
Definition process_messages.h:24
isc::process::DCTL_ALREADY_RUNNING
const isc::log::MessageID DCTL_ALREADY_RUNNING
Definition process_messages.h:11
isc::process::DCTL_NOT_RUNNING
const isc::log::MessageID DCTL_NOT_RUNNING
Definition process_messages.h:25
isc::process::DCTL_CONFIG_FILE_LOAD_FAIL
const isc::log::MessageID DCTL_CONFIG_FILE_LOAD_FAIL
Definition process_messages.h:18
isc::process::DCTL_CONFIG_DEPRECATED
const isc::log::MessageID DCTL_CONFIG_DEPRECATED
Definition process_messages.h:16
isc::process::DCTL_UNSUPPORTED_SIGNAL
const isc::log::MessageID DCTL_UNSUPPORTED_SIGNAL
Definition process_messages.h:36
isc::process::DCTL_INIT_PROCESS
const isc::log::MessageID DCTL_INIT_PROCESS
Definition process_messages.h:23
isc::util
Definition time_utils.cc:43
isc
Defines the logger used by the top-level component of kea-lfc.
Definition agent_parser.cc:148