d_controller.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. // Copyright (C) 2013-2016 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. #include <config.h>
  7. #include <d2/d2_log.h>
  8. #include <cc/command_interpreter.h>
  9. #include <d2/d_controller.h>
  10. #include <exceptions/exceptions.h>
  11. #include <log/logger_support.h>
  12. #include <dhcpsrv/cfgmgr.h>
  13. #include <cryptolink/cryptolink.h>
  14. #include <log/logger.h>
  15. #include <cfgrpt/config_report.h>
  16. #ifdef HAVE_MYSQL
  17. #include <dhcpsrv/mysql_lease_mgr.h>
  18. #endif
  19. #ifdef HAVE_PGSQL
  20. #include <dhcpsrv/pgsql_lease_mgr.h>
  21. #endif
  22. #ifdef HAVE_CQL
  23. #include <dhcpsrv/cql_lease_mgr.h>
  24. #endif
  25. #include <dhcpsrv/memfile_lease_mgr.h>
  26. #include <sstream>
  27. #include <unistd.h>
  28. namespace isc {
  29. namespace d2 {
  30. DControllerBasePtr DControllerBase::controller_;
  31. // Note that the constructor instantiates the controller's primary IOService.
  32. DControllerBase::DControllerBase(const char* app_name, const char* bin_name)
  33. : app_name_(app_name), bin_name_(bin_name),
  34. verbose_(false), spec_file_name_(""),
  35. io_service_(new isc::asiolink::IOService()),
  36. io_signal_queue_() {
  37. }
  38. void
  39. DControllerBase::setController(const DControllerBasePtr& controller) {
  40. if (controller_) {
  41. // This shouldn't happen, but let's make sure it can't be done.
  42. // It represents a programmatic error.
  43. isc_throw (DControllerBaseError,
  44. "Multiple controller instances attempted.");
  45. }
  46. controller_ = controller;
  47. }
  48. void
  49. DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
  50. // Step 1 is to parse the command line arguments.
  51. try {
  52. parseArgs(argc, argv);
  53. } catch (const InvalidUsage& ex) {
  54. usage(ex.what());
  55. throw; // rethrow it
  56. }
  57. setProcName(bin_name_);
  58. // It is important that we set a default logger name because this name
  59. // will be used when the user doesn't provide the logging configuration
  60. // in the Kea configuration file.
  61. isc::dhcp::CfgMgr::instance().setDefaultLoggerName(bin_name_);
  62. // Logger's default configuration depends on whether we are in the
  63. // verbose mode or not. CfgMgr manages the logger configuration so
  64. // the verbose mode is set for CfgMgr.
  65. isc::dhcp::CfgMgr::instance().setVerbose(verbose_);
  66. // Do not initialize logger here if we are running unit tests. It would
  67. // replace an instance of unit test specific logger.
  68. if (!test_mode) {
  69. // Now that we know what the mode flags are, we can init logging.
  70. Daemon::loggerInit(bin_name_.c_str(), verbose_);
  71. }
  72. try {
  73. createPIDFile();
  74. } catch (const dhcp::DaemonPIDExists& ex) {
  75. LOG_FATAL(dctl_logger, DHCP_DDNS_ALREADY_RUNNING)
  76. .arg(bin_name_).arg(ex.what());
  77. isc_throw (LaunchError, "Launch Failed: " << ex.what());
  78. } catch (const std::exception& ex) {
  79. LOG_FATAL(dctl_logger, DHCP_DDNS_PID_FILE_ERROR)
  80. .arg(app_name_).arg(ex.what());
  81. isc_throw (LaunchError, "Launch failed: " << ex.what());
  82. }
  83. // Log the starting of the service. Although this is the controller
  84. // module, use a "DHCP_DDNS_" prefix to the module (to conform to the
  85. // principle of least astonishment).
  86. LOG_INFO(dctl_logger, DHCP_DDNS_STARTING).arg(getpid()).arg(VERSION);
  87. try {
  88. // Step 2 is to create and initialize the application process object.
  89. initProcess();
  90. } catch (const std::exception& ex) {
  91. LOG_FATAL(dctl_logger, DCTL_INIT_PROCESS_FAIL)
  92. .arg(app_name_).arg(ex.what());
  93. isc_throw (ProcessInitError,
  94. "Application Process initialization failed: " << ex.what());
  95. }
  96. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_STANDALONE).arg(app_name_);
  97. // Step 3 is to load configuration from file.
  98. int rcode;
  99. isc::data::ConstElementPtr comment
  100. = isc::config::parseAnswer(rcode, configFromFile());
  101. if (rcode != 0) {
  102. LOG_FATAL(dctl_logger, DCTL_CONFIG_FILE_LOAD_FAIL)
  103. .arg(app_name_).arg(comment->stringValue());
  104. isc_throw (ProcessInitError, "Could Not load configuration file: "
  105. << comment->stringValue());
  106. }
  107. // Everything is clear for launch, so start the application's
  108. // event loop.
  109. try {
  110. // Now that we have a proces, we can set up signal handling.
  111. initSignalHandling();
  112. runProcess();
  113. } catch (const std::exception& ex) {
  114. LOG_FATAL(dctl_logger, DCTL_PROCESS_FAILED)
  115. .arg(app_name_).arg(ex.what());
  116. isc_throw (ProcessRunError,
  117. "Application process event loop failed: " << ex.what());
  118. }
  119. // All done, so bail out. Log the event (using a DHCP_DDNS_ prefix
  120. // for the same reason as used for DHCP_DDNS_STARTING).
  121. LOG_INFO(dctl_logger, DHCP_DDNS_SHUTDOWN);
  122. }
  123. void
  124. DControllerBase::parseArgs(int argc, char* argv[])
  125. {
  126. // Iterate over the given command line options. If its a stock option
  127. // ("s" or "v") handle it here. If its a valid custom option, then
  128. // invoke customOption.
  129. int ch;
  130. opterr = 0;
  131. optind = 1;
  132. std::string opts("dvVWc:" + getCustomOpts());
  133. while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
  134. switch (ch) {
  135. case 'd':
  136. // Enables verbose logging.
  137. verbose_ = true;
  138. break;
  139. case 'v':
  140. // gather Kea version and throw so main() can catch and return
  141. // rather than calling exit() here which disrupts gtest.
  142. isc_throw(VersionMessage, getVersion(false));
  143. break;
  144. case 'V':
  145. // gather Kea version and throw so main() can catch and return
  146. // rather than calling exit() here which disrupts gtest.
  147. isc_throw(VersionMessage, getVersion(true));
  148. break;
  149. case 'W':
  150. // gather Kea config report and throw so main() can catch and
  151. // return rather than calling exit() here which disrupts gtest.
  152. isc_throw(VersionMessage, isc::detail::getConfigReport());
  153. break;
  154. case 'c':
  155. // config file name
  156. if (optarg == NULL) {
  157. isc_throw(InvalidUsage, "configuration file name missing");
  158. }
  159. setConfigFile(optarg);
  160. break;
  161. case '?': {
  162. // We hit an invalid option.
  163. isc_throw(InvalidUsage, "unsupported option: ["
  164. << static_cast<char>(optopt) << "] "
  165. << (!optarg ? "" : optarg));
  166. break;
  167. }
  168. default:
  169. // We hit a valid custom option
  170. if (!customOption(ch, optarg)) {
  171. // This would be a programmatic error.
  172. isc_throw(InvalidUsage, " Option listed but implemented?: ["
  173. << static_cast<char>(ch) << "] "
  174. << (!optarg ? "" : optarg));
  175. }
  176. break;
  177. }
  178. }
  179. // There was too much information on the command line.
  180. if (argc > optind) {
  181. isc_throw(InvalidUsage, "extraneous command line information");
  182. }
  183. }
  184. bool
  185. DControllerBase::customOption(int /* option */, char* /*optarg*/)
  186. {
  187. // Default implementation returns false.
  188. return (false);
  189. }
  190. void
  191. DControllerBase::initProcess() {
  192. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_INIT_PROCESS).arg(app_name_);
  193. // Invoke virtual method to instantiate the application process.
  194. try {
  195. process_.reset(createProcess());
  196. } catch (const std::exception& ex) {
  197. isc_throw(DControllerBaseError, std::string("createProcess failed: ")
  198. + ex.what());
  199. }
  200. // This is pretty unlikely, but will test for it just to be safe..
  201. if (!process_) {
  202. isc_throw(DControllerBaseError, "createProcess returned NULL");
  203. }
  204. // Invoke application's init method (Note this call should throw
  205. // DProcessBaseError if it fails).
  206. process_->init();
  207. }
  208. isc::data::ConstElementPtr
  209. DControllerBase::configFromFile() {
  210. // Rollback any previous staging configuration. For D2, only a
  211. // logger configuration is used here.
  212. isc::dhcp::CfgMgr::instance().rollback();
  213. // Will hold configuration.
  214. isc::data::ConstElementPtr module_config;
  215. // Will receive configuration result.
  216. isc::data::ConstElementPtr answer;
  217. try {
  218. std::string config_file = getConfigFile();
  219. if (config_file.empty()) {
  220. // Basic sanity check: file name must not be empty.
  221. isc_throw(BadValue, "JSON configuration file not specified. Please "
  222. "use -c command line option.");
  223. }
  224. // Read contents of the file and parse it as JSON
  225. isc::data::ConstElementPtr whole_config =
  226. isc::data::Element::fromJSONFile(config_file, true);
  227. // Let's configure logging before applying the configuration,
  228. // so we can log things during configuration process.
  229. // Temporary storage for logging configuration
  230. isc::dhcp::SrvConfigPtr storage =
  231. isc::dhcp::CfgMgr::instance().getStagingCfg();
  232. // Get 'Logging' element from the config and use it to set up
  233. // logging. If there's no such element, we'll just pass NULL.
  234. Daemon::configureLogger(whole_config->get("Logging"), storage);
  235. // Extract derivation-specific portion of the configuration.
  236. module_config = whole_config->get(getAppName());
  237. if (!module_config) {
  238. isc_throw(BadValue, "Config file " << config_file <<
  239. " does not include '" <<
  240. getAppName() << "' entry.");
  241. }
  242. answer = updateConfig(module_config);
  243. int rcode = 0;
  244. isc::config::parseAnswer(rcode, answer);
  245. if (!rcode) {
  246. // Configuration successful, so apply the logging configuration
  247. // to log4cplus.
  248. isc::dhcp::CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
  249. isc::dhcp::CfgMgr::instance().commit();
  250. }
  251. } catch (const std::exception& ex) {
  252. // Rollback logging configuration.
  253. isc::dhcp::CfgMgr::instance().rollback();
  254. // build an error result
  255. isc::data::ConstElementPtr error =
  256. isc::config::createAnswer(1,
  257. std::string("Configuration parsing failed: ") + ex.what());
  258. return (error);
  259. }
  260. return (answer);
  261. }
  262. void
  263. DControllerBase::runProcess() {
  264. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_RUN_PROCESS).arg(app_name_);
  265. if (!process_) {
  266. // This should not be possible.
  267. isc_throw(DControllerBaseError, "Process not initialized");
  268. }
  269. // Invoke the application process's run method. This may throw
  270. // DProcessBaseError
  271. process_->run();
  272. }
  273. // Instance method for handling new config
  274. isc::data::ConstElementPtr
  275. DControllerBase::updateConfig(isc::data::ConstElementPtr new_config) {
  276. return (process_->configure(new_config));
  277. }
  278. // Instance method for executing commands
  279. isc::data::ConstElementPtr
  280. DControllerBase::executeCommand(const std::string& command,
  281. isc::data::ConstElementPtr args) {
  282. // Shutdown is universal. If its not that, then try it as
  283. // a custom command supported by the derivation. If that
  284. // doesn't pan out either, than send to it the application
  285. // as it may be supported there.
  286. isc::data::ConstElementPtr answer;
  287. if (command.compare(SHUT_DOWN_COMMAND) == 0) {
  288. answer = shutdownProcess(args);
  289. } else {
  290. // It wasn't shutdown, so it may be a custom controller command.
  291. int rcode = 0;
  292. answer = customControllerCommand(command, args);
  293. isc::config::parseAnswer(rcode, answer);
  294. if (rcode == COMMAND_INVALID)
  295. {
  296. // It wasn't a controller command, so it may be an application command.
  297. answer = process_->command(command, args);
  298. }
  299. }
  300. return (answer);
  301. }
  302. isc::data::ConstElementPtr
  303. DControllerBase::customControllerCommand(const std::string& command,
  304. isc::data::ConstElementPtr /* args */) {
  305. // Default implementation always returns invalid command.
  306. return (isc::config::createAnswer(COMMAND_INVALID,
  307. "Unrecognized command: " + command));
  308. }
  309. isc::data::ConstElementPtr
  310. DControllerBase::shutdownProcess(isc::data::ConstElementPtr args) {
  311. if (process_) {
  312. return (process_->shutdown(args));
  313. }
  314. // Not really a failure, but this condition is worth noting. In reality
  315. // it should be pretty hard to cause this.
  316. LOG_WARN(dctl_logger, DCTL_NOT_RUNNING).arg(app_name_);
  317. return (isc::config::createAnswer(0, "Process has not been initialized."));
  318. }
  319. void
  320. DControllerBase::initSignalHandling() {
  321. /// @todo block everything we don't handle
  322. // Create our signal queue.
  323. io_signal_queue_.reset(new IOSignalQueue(io_service_));
  324. // Install the on-receipt handler
  325. util::SignalSet::setOnReceiptHandler(boost::bind(&DControllerBase::
  326. osSignalHandler,
  327. this, _1));
  328. // Register for the signals we wish to handle.
  329. signal_set_.reset(new util::SignalSet(SIGHUP,SIGINT,SIGTERM));
  330. }
  331. bool
  332. DControllerBase::osSignalHandler(int signum) {
  333. // Create a IOSignal to propagate the signal to IOService.
  334. io_signal_queue_->pushSignal(signum, boost::bind(&DControllerBase::
  335. ioSignalHandler,
  336. this, _1));
  337. return (true);
  338. }
  339. void
  340. DControllerBase::ioSignalHandler(IOSignalId sequence_id) {
  341. // Pop the signal instance off the queue. This should make us
  342. // the only one holding it, so when we leave it should be freed.
  343. // (note that popSignal will throw if signal is not found, which
  344. // in turn will caught, logged, and swallowed by IOSignal callback
  345. // invocation code.)
  346. IOSignalPtr io_signal = io_signal_queue_->popSignal(sequence_id);
  347. // Now call virtual signal processing method.
  348. processSignal(io_signal->getSignum());
  349. }
  350. void
  351. DControllerBase::processSignal(int signum) {
  352. switch (signum) {
  353. case SIGHUP:
  354. {
  355. LOG_INFO(dctl_logger, DHCP_DDNS_CFG_FILE_RELOAD_SIGNAL_RECVD)
  356. .arg(signum).arg(getConfigFile());
  357. int rcode;
  358. isc::data::ConstElementPtr comment = isc::config::
  359. parseAnswer(rcode,
  360. configFromFile());
  361. if (rcode != 0) {
  362. LOG_ERROR(dctl_logger, DHCP_DDNS_CFG_FILE_RELOAD_ERROR)
  363. .arg(comment->stringValue());
  364. }
  365. break;
  366. }
  367. case SIGINT:
  368. case SIGTERM:
  369. {
  370. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT,
  371. DHCP_DDNS_SHUTDOWN_SIGNAL_RECVD).arg(signum);
  372. isc::data::ElementPtr arg_set;
  373. executeCommand(SHUT_DOWN_COMMAND, arg_set);
  374. break;
  375. }
  376. default:
  377. LOG_WARN(dctl_logger, DHCP_DDNS_UNSUPPORTED_SIGNAL).arg(signum);
  378. break;
  379. }
  380. }
  381. void
  382. DControllerBase::usage(const std::string & text)
  383. {
  384. if (text != "") {
  385. std::cerr << "Usage error: " << text << std::endl;
  386. }
  387. std::cerr << "Usage: " << bin_name_ << std::endl
  388. << " -v: print version number and exit" << std::endl
  389. << " -V: print extended version information and exit"
  390. << std::endl
  391. << " -W: display the configuration report and exit"
  392. << std::endl
  393. << " -d: optional, verbose output " << std::endl
  394. << " -c <config file name> : mandatory,"
  395. << " specifies name of configuration file " << std::endl;
  396. // add any derivation specific usage
  397. std::cerr << getUsageText() << std::endl;
  398. }
  399. DControllerBase::~DControllerBase() {
  400. }
  401. // Refer to config_report so it will be embedded in the binary
  402. const char* const* d2_config_report = isc::detail::config_report;
  403. std::string
  404. DControllerBase::getVersion(bool extended) {
  405. std::stringstream tmp;
  406. tmp << VERSION;
  407. if (extended) {
  408. tmp << std::endl << EXTENDED_VERSION << std::endl;
  409. tmp << "linked with:" << std::endl;
  410. tmp << isc::log::Logger::getVersion() << std::endl;
  411. tmp << isc::cryptolink::CryptoLink::getVersion() << std::endl;
  412. tmp << "database:" << std::endl;
  413. #ifdef HAVE_MYSQL
  414. tmp << isc::dhcp::MySqlLeaseMgr::getDBVersion() << std::endl;
  415. #endif
  416. #ifdef HAVE_PGSQL
  417. tmp << isc::dhcp::PgSqlLeaseMgr::getDBVersion() << std::endl;
  418. #endif
  419. #ifdef HAVE_CQL
  420. tmp << isc::dhcp::CqlLeaseMgr::getDBVersion() << std::endl;
  421. #endif
  422. tmp << isc::dhcp::Memfile_LeaseMgr::getDBVersion();
  423. // @todo: more details about database runtime
  424. }
  425. return (tmp.str());
  426. }
  427. }; // namespace isc::d2
  428. }; // namespace isc