Kea: d_controller.cc Source File

| Kea 3.2.0-git

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