d_controller.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. // Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <d2/d2_log.h>
  15. #include <d2/d_controller.h>
  16. #include <exceptions/exceptions.h>
  17. #include <log/logger_support.h>
  18. #include <sstream>
  19. namespace isc {
  20. namespace d2 {
  21. DControllerBasePtr DControllerBase::controller_;
  22. // Note that the constructor instantiates the controller's primary IOService.
  23. DControllerBase::DControllerBase(const char* app_name, const char* bin_name)
  24. : app_name_(app_name), bin_name_(bin_name), stand_alone_(false),
  25. verbose_(false), spec_file_name_(""),
  26. io_service_(new isc::asiolink::IOService()){
  27. }
  28. void
  29. DControllerBase::setController(const DControllerBasePtr& controller) {
  30. if (controller_) {
  31. // This shouldn't happen, but let's make sure it can't be done.
  32. // It represents a programmatic error.
  33. isc_throw (DControllerBaseError,
  34. "Multiple controller instances attempted.");
  35. }
  36. controller_ = controller;
  37. }
  38. void
  39. DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
  40. // Step 1 is to parse the command line arguments.
  41. try {
  42. parseArgs(argc, argv);
  43. } catch (const InvalidUsage& ex) {
  44. usage(ex.what());
  45. throw; // rethrow it
  46. }
  47. // Do not initialize logger here if we are running unit tests. It would
  48. // replace an instance of unit test specific logger.
  49. if (!test_mode) {
  50. // Now that we know what the mode flags are, we can init logging.
  51. // If standalone is enabled, do not buffer initial log messages
  52. isc::log::initLogger(bin_name_,
  53. ((verbose_ && stand_alone_)
  54. ? isc::log::DEBUG : isc::log::INFO),
  55. isc::log::MAX_DEBUG_LEVEL, NULL, !stand_alone_);
  56. }
  57. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_STARTING)
  58. .arg(app_name_).arg(getpid());
  59. try {
  60. // Step 2 is to create and initialize the application process object.
  61. initProcess();
  62. } catch (const std::exception& ex) {
  63. LOG_FATAL(dctl_logger, DCTL_INIT_PROCESS_FAIL)
  64. .arg(app_name_).arg(ex.what());
  65. isc_throw (ProcessInitError,
  66. "Application Process initialization failed: " << ex.what());
  67. }
  68. // Next we connect if we are running integrated.
  69. if (stand_alone_) {
  70. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_STANDALONE)
  71. .arg(app_name_);
  72. } else {
  73. try {
  74. establishSession();
  75. } catch (const std::exception& ex) {
  76. LOG_FATAL(dctl_logger, DCTL_SESSION_FAIL).arg(ex.what());
  77. isc_throw (SessionStartError,
  78. "Session start up failed: " << ex.what());
  79. }
  80. }
  81. // Everything is clear for launch, so start the application's
  82. // event loop.
  83. try {
  84. runProcess();
  85. } catch (const std::exception& ex) {
  86. LOG_FATAL(dctl_logger, DCTL_PROCESS_FAILED)
  87. .arg(app_name_).arg(ex.what());
  88. isc_throw (ProcessRunError,
  89. "Application process event loop failed: " << ex.what());
  90. }
  91. // If running integrated, disconnect.
  92. if (!stand_alone_) {
  93. try {
  94. disconnectSession();
  95. } catch (const std::exception& ex) {
  96. LOG_ERROR(dctl_logger, DCTL_DISCONNECT_FAIL)
  97. .arg(app_name_).arg(ex.what());
  98. isc_throw (SessionEndError, "Session end failed: " << ex.what());
  99. }
  100. }
  101. // All done, so bail out.
  102. LOG_INFO(dctl_logger, DCTL_STOPPING).arg(app_name_);
  103. }
  104. void
  105. DControllerBase::parseArgs(int argc, char* argv[])
  106. {
  107. // Iterate over the given command line options. If its a stock option
  108. // ("s" or "v") handle it here. If its a valid custom option, then
  109. // invoke customOption.
  110. int ch;
  111. opterr = 0;
  112. optind = 1;
  113. std::string opts(":vs" + getCustomOpts());
  114. while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
  115. switch (ch) {
  116. case 'v':
  117. // Enables verbose logging.
  118. verbose_ = true;
  119. break;
  120. case 's':
  121. // Enables stand alone or "BINDLESS" operation.
  122. stand_alone_ = true;
  123. break;
  124. case '?': {
  125. // We hit an invalid option.
  126. isc_throw(InvalidUsage, "unsupported option: ["
  127. << static_cast<char>(optopt) << "] "
  128. << (!optarg ? "" : optarg));
  129. break;
  130. }
  131. default:
  132. // We hit a valid custom option
  133. if (!customOption(ch, optarg)) {
  134. // This would be a programmatic error.
  135. isc_throw(InvalidUsage, " Option listed but implemented?: ["
  136. << static_cast<char>(ch) << "] "
  137. << (!optarg ? "" : optarg));
  138. }
  139. break;
  140. }
  141. }
  142. // There was too much information on the command line.
  143. if (argc > optind) {
  144. isc_throw(InvalidUsage, "extraneous command line information");
  145. }
  146. }
  147. bool
  148. DControllerBase::customOption(int /* option */, char* /*optarg*/)
  149. {
  150. // Default implementation returns false.
  151. return (false);
  152. }
  153. void
  154. DControllerBase::initProcess() {
  155. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_INIT_PROCESS).arg(app_name_);
  156. // Invoke virtual method to instantiate the application process.
  157. try {
  158. process_.reset(createProcess());
  159. } catch (const std::exception& ex) {
  160. isc_throw(DControllerBaseError, std::string("createProcess failed: ")
  161. + ex.what());
  162. }
  163. // This is pretty unlikely, but will test for it just to be safe..
  164. if (!process_) {
  165. isc_throw(DControllerBaseError, "createProcess returned NULL");
  166. }
  167. // Invoke application's init method (Note this call should throw
  168. // DProcessBaseError if it fails).
  169. process_->init();
  170. }
  171. void
  172. DControllerBase::establishSession() {
  173. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CCSESSION_STARTING)
  174. .arg(app_name_).arg(spec_file_name_);
  175. // Create the BIND10 command control session with the our IOService.
  176. cc_session_ = SessionPtr(new isc::cc::Session(
  177. io_service_->get_io_service()));
  178. // Create the BIND10 config session with the stub configuration handler.
  179. // This handler is internally invoked by the constructor and on success
  180. // the constructor updates the current session with the configuration that
  181. // had been committed in the previous session. If we do not install
  182. // the dummy handler, the previous configuration would be lost.
  183. config_session_ = ModuleCCSessionPtr(new isc::config::ModuleCCSession(
  184. spec_file_name_, *cc_session_,
  185. dummyConfigHandler, commandHandler,
  186. false));
  187. // Enable configuration even processing.
  188. config_session_->start();
  189. // We initially create ModuleCCSession() with a dummy configHandler, as
  190. // the session module is too eager to send partial configuration.
  191. // Replace the dummy config handler with the real handler.
  192. config_session_->setConfigHandler(configHandler);
  193. // Call the real configHandler with the full configuration retrieved
  194. // from the config session.
  195. isc::data::ConstElementPtr answer = configHandler(
  196. config_session_->getFullConfig());
  197. // Parse the answer returned from the configHandler. Log the error but
  198. // keep running. This provides an opportunity for the user to correct
  199. // the configuration dynamically.
  200. int ret = 0;
  201. isc::data::ConstElementPtr comment = isc::config::parseAnswer(ret, answer);
  202. if (ret) {
  203. LOG_ERROR(dctl_logger, DCTL_CONFIG_LOAD_FAIL)
  204. .arg(app_name_).arg(comment->str());
  205. }
  206. // Lastly, call onConnect. This allows deriving class to execute custom
  207. // logic predicated by session connect.
  208. onSessionConnect();
  209. }
  210. void
  211. DControllerBase::runProcess() {
  212. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_RUN_PROCESS).arg(app_name_);
  213. if (!process_) {
  214. // This should not be possible.
  215. isc_throw(DControllerBaseError, "Process not initialized");
  216. }
  217. // Invoke the application process's run method. This may throw
  218. // DProcessBaseError
  219. process_->run();
  220. }
  221. void DControllerBase::disconnectSession() {
  222. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CCSESSION_ENDING)
  223. .arg(app_name_);
  224. // Call virtual onDisconnect. Allows deriving class to execute custom
  225. // logic prior to session loss.
  226. onSessionDisconnect();
  227. // Destroy the BIND10 config session.
  228. if (config_session_) {
  229. config_session_.reset();
  230. }
  231. // Destroy the BIND10 command and control session.
  232. if (cc_session_) {
  233. cc_session_->disconnect();
  234. cc_session_.reset();
  235. }
  236. }
  237. isc::data::ConstElementPtr
  238. DControllerBase::dummyConfigHandler(isc::data::ConstElementPtr) {
  239. LOG_DEBUG(dctl_logger, DBGLVL_START_SHUT, DCTL_CONFIG_STUB)
  240. .arg(controller_->getAppName());
  241. return (isc::config::createAnswer(0, "Configuration accepted."));
  242. }
  243. isc::data::ConstElementPtr
  244. DControllerBase::configHandler(isc::data::ConstElementPtr new_config) {
  245. LOG_DEBUG(dctl_logger, DBGLVL_COMMAND, DCTL_CONFIG_UPDATE)
  246. .arg(controller_->getAppName()).arg(new_config->str());
  247. // Invoke the instance method on the controller singleton.
  248. return (controller_->updateConfig(new_config));
  249. }
  250. // Static callback which invokes non-static handler on singleton
  251. isc::data::ConstElementPtr
  252. DControllerBase::commandHandler(const std::string& command,
  253. isc::data::ConstElementPtr args) {
  254. LOG_DEBUG(dctl_logger, DBGLVL_COMMAND, DCTL_COMMAND_RECEIVED)
  255. .arg(controller_->getAppName()).arg(command)
  256. .arg(args ? args->str() : "(no args)");
  257. // Invoke the instance method on the controller singleton.
  258. return (controller_->executeCommand(command, args));
  259. }
  260. isc::data::ConstElementPtr
  261. DControllerBase::updateConfig(isc::data::ConstElementPtr new_config) {
  262. isc::data::ConstElementPtr full_config;
  263. if (stand_alone_) {
  264. // @todo Until there is a configuration manager to provide retrieval
  265. // we'll just assume the incoming config is the full configuration set.
  266. // It may also make more sense to isolate the controller from the
  267. // configuration manager entirely. We could do something like
  268. // process_->getFullConfig() here for stand-alone mode?
  269. full_config = new_config;
  270. } else {
  271. if (!config_session_) {
  272. // That should never happen as we install config_handler
  273. // after we instantiate the server.
  274. isc::data::ConstElementPtr answer =
  275. isc::config::createAnswer(1, "Configuration rejected,"
  276. " Session has not started.");
  277. return (answer);
  278. }
  279. // Let's get the existing configuration.
  280. full_config = config_session_->getFullConfig();
  281. }
  282. // The configuration passed to this handler function is partial.
  283. // In other words, it just includes the values being modified.
  284. // In the same time, there may be dependencies between various
  285. // configuration parsers. For example: the option value can
  286. // be set if the definition of this option is set. If someone removes
  287. // an existing option definition then the partial configuration that
  288. // removes that definition is triggered while a relevant option value
  289. // may remain configured. This eventually results in the
  290. // configuration being in the inconsistent state.
  291. // In order to work around this problem we need to merge the new
  292. // configuration with the existing (full) configuration.
  293. // Let's create a new object that will hold the merged configuration.
  294. boost::shared_ptr<isc::data::MapElement>
  295. merged_config(new isc::data::MapElement());
  296. // Merge an existing and new configuration.
  297. merged_config->setValue(full_config->mapValue());
  298. isc::data::merge(merged_config, new_config);
  299. // Send the merged configuration to the application.
  300. return (process_->configure(merged_config));
  301. }
  302. isc::data::ConstElementPtr
  303. DControllerBase::executeCommand(const std::string& command,
  304. isc::data::ConstElementPtr args) {
  305. // Shutdown is universal. If its not that, then try it as
  306. // an custom command supported by the derivation. If that
  307. // doesn't pan out either, than send to it the application
  308. // as it may be supported there.
  309. isc::data::ConstElementPtr answer;
  310. if (command.compare(SHUT_DOWN_COMMAND) == 0) {
  311. answer = shutdown(args);
  312. } else {
  313. // It wasn't shutdown, so may be a custom controller command.
  314. int rcode = 0;
  315. answer = customControllerCommand(command, args);
  316. isc::config::parseAnswer(rcode, answer);
  317. if (rcode == COMMAND_INVALID)
  318. {
  319. // It wasn't controller command, so may be an application command.
  320. answer = process_->command(command, args);
  321. }
  322. }
  323. return (answer);
  324. }
  325. isc::data::ConstElementPtr
  326. DControllerBase::customControllerCommand(const std::string& command,
  327. isc::data::ConstElementPtr /* args */) {
  328. // Default implementation always returns invalid command.
  329. return (isc::config::createAnswer(COMMAND_INVALID,
  330. "Unrecognized command: " + command));
  331. }
  332. isc::data::ConstElementPtr
  333. DControllerBase::shutdown(isc::data::ConstElementPtr args) {
  334. if (process_) {
  335. return (process_->shutdown(args));
  336. }
  337. // Not really a failure, but this condition is worth noting. In reality
  338. // it should be pretty hard to cause this.
  339. LOG_WARN(dctl_logger, DCTL_NOT_RUNNING).arg(app_name_);
  340. return (isc::config::createAnswer(0, "Process has not been initialzed."));
  341. }
  342. void
  343. DControllerBase::usage(const std::string & text)
  344. {
  345. if (text != "") {
  346. std::cerr << "Usage error: " << text << std::endl;
  347. }
  348. std::cerr << "Usage: " << bin_name_ << std::endl;
  349. std::cerr << " -v: verbose output" << std::endl;
  350. std::cerr << " -s: stand-alone mode (don't connect to BIND10)"
  351. << std::endl;
  352. std::cerr << getUsageText() << std::endl;
  353. }
  354. DControllerBase::~DControllerBase() {
  355. }
  356. }; // namespace isc::d2
  357. }; // namespace isc