ccsession.cc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. // Copyright (C) 2009 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 <config.h>
  15. #include <stdexcept>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <sys/time.h>
  19. #include <ctype.h>
  20. #include <algorithm>
  21. #include <cerrno>
  22. #include <fstream>
  23. #include <iostream>
  24. #include <set>
  25. #include <sstream>
  26. #include <string>
  27. #include <boost/bind.hpp>
  28. #include <boost/foreach.hpp>
  29. #include <cc/data.h>
  30. #include <config/module_spec.h>
  31. #include <cc/session.h>
  32. #include <exceptions/exceptions.h>
  33. #include <config/config_log.h>
  34. #include <config/ccsession.h>
  35. #include <log/logger_support.h>
  36. #include <log/logger_specification.h>
  37. #include <log/logger_manager.h>
  38. #include <log/logger_name.h>
  39. using namespace std;
  40. using isc::data::Element;
  41. using isc::data::ConstElementPtr;
  42. using isc::data::ElementPtr;
  43. using isc::data::JSONError;
  44. namespace isc {
  45. namespace config {
  46. /// Creates a standard config/command protocol answer message
  47. ConstElementPtr
  48. createAnswer() {
  49. ElementPtr answer = Element::createMap();
  50. ElementPtr answer_content = Element::createList();
  51. answer_content->add(Element::create(isc::cc::CC_REPLY_SUCCESS));
  52. answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
  53. return (answer);
  54. }
  55. ConstElementPtr
  56. createAnswer(const int rcode, ConstElementPtr arg) {
  57. if (rcode != 0 && (!arg || arg->getType() != Element::string)) {
  58. isc_throw(CCSessionError, "Bad or no argument for rcode != 0");
  59. }
  60. ElementPtr answer = Element::createMap();
  61. ElementPtr answer_content = Element::createList();
  62. answer_content->add(Element::create(rcode));
  63. answer_content->add(arg);
  64. answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
  65. return (answer);
  66. }
  67. ConstElementPtr
  68. createAnswer(const int rcode, const std::string& arg) {
  69. ElementPtr answer = Element::createMap();
  70. ElementPtr answer_content = Element::createList();
  71. answer_content->add(Element::create(rcode));
  72. answer_content->add(Element::create(arg));
  73. answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
  74. return (answer);
  75. }
  76. ConstElementPtr
  77. parseAnswer(int &rcode, ConstElementPtr msg) {
  78. if (msg &&
  79. msg->getType() == Element::map &&
  80. msg->contains(isc::cc::CC_PAYLOAD_RESULT)) {
  81. ConstElementPtr result = msg->get(isc::cc::CC_PAYLOAD_RESULT);
  82. if (result->getType() != Element::list) {
  83. isc_throw(CCSessionError, "Result element in answer message is not a list");
  84. } else if (result->get(0)->getType() != Element::integer) {
  85. isc_throw(CCSessionError, "First element of result is not an rcode in answer message");
  86. }
  87. rcode = result->get(0)->intValue();
  88. if (result->size() > 1) {
  89. if (rcode == 0 || result->get(1)->getType() == Element::string) {
  90. return (result->get(1));
  91. } else {
  92. isc_throw(CCSessionError, "Error description in result with rcode != 0 is not a string");
  93. }
  94. } else {
  95. if (rcode == 0) {
  96. return (ElementPtr());
  97. } else {
  98. isc_throw(CCSessionError, "Result with rcode != 0 does not have an error description");
  99. }
  100. }
  101. } else {
  102. isc_throw(CCSessionError, "No result part in answer message");
  103. }
  104. }
  105. ConstElementPtr
  106. createCommand(const std::string& command) {
  107. return (createCommand(command, ElementPtr()));
  108. }
  109. ConstElementPtr
  110. createCommand(const std::string& command, ConstElementPtr arg) {
  111. ElementPtr cmd = Element::createMap();
  112. ElementPtr cmd_parts = Element::createList();
  113. cmd_parts->add(Element::create(command));
  114. if (arg) {
  115. cmd_parts->add(arg);
  116. }
  117. cmd->set(isc::cc::CC_PAYLOAD_COMMAND, cmd_parts);
  118. return (cmd);
  119. }
  120. std::string
  121. parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
  122. if (command &&
  123. command->getType() == Element::map &&
  124. command->contains(isc::cc::CC_PAYLOAD_COMMAND)) {
  125. ConstElementPtr cmd = command->get(isc::cc::CC_PAYLOAD_COMMAND);
  126. if (cmd->getType() == Element::list &&
  127. cmd->size() > 0 &&
  128. cmd->get(0)->getType() == Element::string) {
  129. if (cmd->size() > 1) {
  130. arg = cmd->get(1);
  131. } else {
  132. arg = Element::createMap();
  133. }
  134. return (cmd->get(0)->stringValue());
  135. } else {
  136. isc_throw(CCSessionError, "Command part in command message missing, empty, or not a list");
  137. }
  138. } else {
  139. isc_throw(CCSessionError, "Command Element empty or not a map with \"command\"");
  140. }
  141. }
  142. namespace {
  143. // Temporary workaround functions for missing functionality in
  144. // getValue() (main problem described in ticket #993)
  145. // This returns either the value set for the given relative id,
  146. // or its default value
  147. // (intentially defined here so this interface does not get
  148. // included in ConfigData as it is)
  149. ConstElementPtr getValueOrDefault(ConstElementPtr config_part,
  150. const std::string& relative_id,
  151. const ConfigData& config_data,
  152. const std::string& full_id) {
  153. if (config_part->contains(relative_id)) {
  154. return config_part->get(relative_id);
  155. } else {
  156. return config_data.getDefaultValue(full_id);
  157. }
  158. }
  159. // Prefix name with "b10-".
  160. //
  161. // In BIND 10, modules have names taken from the .spec file, which are typically
  162. // names starting with a capital letter (e.g. "Resolver", "Auth" etc.). The
  163. // names of the associated binaries are derived from the module names, being
  164. // prefixed "b10-" and having the first letter of the module name lower-cased
  165. // (e.g. "b10-resolver", "b10-auth"). (It is a required convention that there
  166. // be this relationship between the names.)
  167. //
  168. // Within the binaries the root loggers are named after the binaries themselves.
  169. // (The reason for this is that the name of the logger is included in the
  170. // message logged, so making it clear which message comes from which BIND 10
  171. // process.) As logging is configured using module names, the configuration code
  172. // has to match these with the corresponding logger names. This function
  173. // converts a module name to a root logger name by lowercasing the first letter
  174. // of the module name and prepending "b10-".
  175. //
  176. // \param instring String to convert. (This may be empty, in which case
  177. // "b10-" will be returned.)
  178. //
  179. // \return Converted string.
  180. std::string
  181. b10Prefix(const std::string& instring) {
  182. std::string result = instring;
  183. if (!result.empty()) {
  184. result[0] = tolower(result[0]);
  185. }
  186. return (std::string("b10-") + result);
  187. }
  188. // Reads a output_option subelement of a logger configuration,
  189. // and sets the values thereing to the given OutputOption struct,
  190. // or defaults values if they are not provided (from config_data).
  191. void
  192. readOutputOptionConf(isc::log::OutputOption& output_option,
  193. ConstElementPtr output_option_el,
  194. const ConfigData& config_data)
  195. {
  196. ConstElementPtr destination_el = getValueOrDefault(output_option_el,
  197. "destination", config_data,
  198. "loggers/output_options/destination");
  199. output_option.destination = isc::log::getDestination(destination_el->stringValue());
  200. ConstElementPtr output_el = getValueOrDefault(output_option_el,
  201. "output", config_data,
  202. "loggers/output_options/output");
  203. if (output_option.destination == isc::log::OutputOption::DEST_CONSOLE) {
  204. output_option.stream = isc::log::getStream(output_el->stringValue());
  205. } else if (output_option.destination == isc::log::OutputOption::DEST_FILE) {
  206. output_option.filename = output_el->stringValue();
  207. } else if (output_option.destination == isc::log::OutputOption::DEST_SYSLOG) {
  208. output_option.facility = output_el->stringValue();
  209. }
  210. output_option.flush = getValueOrDefault(output_option_el,
  211. "flush", config_data,
  212. "loggers/output_options/flush")->boolValue();
  213. output_option.maxsize = getValueOrDefault(output_option_el,
  214. "maxsize", config_data,
  215. "loggers/output_options/maxsize")->intValue();
  216. output_option.maxver = getValueOrDefault(output_option_el,
  217. "maxver", config_data,
  218. "loggers/output_options/maxver")->intValue();
  219. }
  220. // Reads a full 'loggers' configuration, and adds the loggers therein
  221. // to the given vector, fills in blanks with defaults from config_data
  222. void
  223. readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
  224. ConstElementPtr logger,
  225. const ConfigData& config_data)
  226. {
  227. // Read name, adding prefix as required.
  228. std::string lname = logger->get("name")->stringValue();
  229. ConstElementPtr severity_el = getValueOrDefault(logger,
  230. "severity", config_data,
  231. "loggers/severity");
  232. isc::log::Severity severity = isc::log::getSeverity(
  233. severity_el->stringValue());
  234. int dbg_level = getValueOrDefault(logger, "debuglevel",
  235. config_data,
  236. "loggers/debuglevel")->intValue();
  237. bool additive = getValueOrDefault(logger, "additive", config_data,
  238. "loggers/additive")->boolValue();
  239. isc::log::LoggerSpecification logger_spec(
  240. lname, severity, dbg_level, additive
  241. );
  242. if (logger->contains("output_options")) {
  243. BOOST_FOREACH(ConstElementPtr output_option_el,
  244. logger->get("output_options")->listValue()) {
  245. // create outputoptions
  246. isc::log::OutputOption output_option;
  247. readOutputOptionConf(output_option,
  248. output_option_el,
  249. config_data);
  250. logger_spec.addOutputOption(output_option);
  251. }
  252. }
  253. specs.push_back(logger_spec);
  254. }
  255. // Copies the map for a logger, changing the name of the logger in the process.
  256. // This is used because the map being copied is "const", so in order to
  257. // change the name we need to create a new one.
  258. //
  259. // \param cur_logger Logger being copied.
  260. // \param new_name New value of the "name" element at the top level.
  261. //
  262. // \return Pointer to the map with the updated element.
  263. ConstElementPtr
  264. copyLogger(ConstElementPtr& cur_logger, const std::string& new_name) {
  265. // Since we'll only be updating one first-level element and subsequent
  266. // use won't change the contents of the map, a shallow map copy is enough.
  267. ElementPtr new_logger(Element::createMap());
  268. new_logger->setValue(cur_logger->mapValue());
  269. new_logger->set("name", Element::create(new_name));
  270. return (new_logger);
  271. }
  272. } // end anonymous namespace
  273. ConstElementPtr
  274. getRelatedLoggers(ConstElementPtr loggers) {
  275. // Keep a list of names for easier lookup later
  276. std::set<std::string> our_names;
  277. const std::string& root_name = isc::log::getRootLoggerName();
  278. ElementPtr result = isc::data::Element::createList();
  279. BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
  280. // Need to add the b10- prefix to names ready from the spec file.
  281. const std::string cur_name = cur_logger->get("name")->stringValue();
  282. const std::string mod_name = b10Prefix(cur_name);
  283. if (mod_name == root_name || mod_name.find(root_name + ".") == 0) {
  284. // Note this name so that we don't add a wildcard that matches it.
  285. our_names.insert(mod_name);
  286. // We want to store the logger with the modified name (i.e. with
  287. // the b10- prefix). As we are dealing with const loggers, we
  288. // store a modified copy of the data.
  289. result->add(copyLogger(cur_logger, mod_name));
  290. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_EXPLICIT)
  291. .arg(cur_name);
  292. } else if (!cur_name.empty() && (cur_name[0] != '*')) {
  293. // Not a wildcard logger and we are ignoring it.
  294. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
  295. CONFIG_LOG_IGNORE_EXPLICIT).arg(cur_name);
  296. }
  297. }
  298. // Now find the wildcard names (the one that start with "*").
  299. BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
  300. const std::string cur_name = cur_logger->get("name")->stringValue();
  301. // If name is '*', or starts with '*.', replace * with root
  302. // logger name.
  303. if (cur_name == "*" || (cur_name.length() > 1 &&
  304. cur_name[0] == '*' && cur_name[1] == '.')) {
  305. // Substitute the "*" with the root name
  306. std::string mod_name = cur_name;
  307. mod_name.replace(0, 1, root_name);
  308. // Now add it to the result list, but only if a logger with
  309. // that name was not configured explicitly.
  310. if (our_names.find(mod_name) == our_names.end()) {
  311. // We substitute the name here, but as we are dealing with
  312. // consts, we need to copy the data.
  313. result->add(copyLogger(cur_logger, mod_name));
  314. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
  315. CONFIG_LOG_WILD_MATCH).arg(cur_name);
  316. } else if (!cur_name.empty() && (cur_name[0] == '*')) {
  317. // Is a wildcard and we are ignoring it (because the wildcard
  318. // expands to a specification that we already encountered when
  319. // processing explicit names).
  320. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
  321. CONFIG_LOG_IGNORE_WILD).arg(cur_name);
  322. }
  323. }
  324. }
  325. return (result);
  326. }
  327. void
  328. default_logconfig_handler(const std::string& module_name,
  329. ConstElementPtr new_config,
  330. const ConfigData& config_data) {
  331. config_data.getModuleSpec().validateConfig(new_config, true);
  332. std::vector<isc::log::LoggerSpecification> specs;
  333. if (new_config->contains("loggers")) {
  334. ConstElementPtr loggers = getRelatedLoggers(new_config->get("loggers"));
  335. BOOST_FOREACH(ConstElementPtr logger,
  336. loggers->listValue()) {
  337. readLoggersConf(specs, logger, config_data);
  338. }
  339. }
  340. isc::log::LoggerManager logger_manager;
  341. logger_manager.process(specs.begin(), specs.end());
  342. }
  343. ModuleSpec
  344. ModuleCCSession::readModuleSpecification(const std::string& filename) {
  345. std::ifstream file;
  346. ModuleSpec module_spec;
  347. // this file should be declared in a @something@ directive
  348. file.open(filename.c_str());
  349. if (!file) {
  350. LOG_ERROR(config_logger, CONFIG_OPEN_FAIL).arg(filename).arg(strerror(errno));
  351. isc_throw(CCSessionInitError, strerror(errno));
  352. }
  353. try {
  354. module_spec = moduleSpecFromFile(file, true);
  355. } catch (const JSONError& pe) {
  356. LOG_ERROR(config_logger, CONFIG_JSON_PARSE).arg(filename).arg(pe.what());
  357. isc_throw(CCSessionInitError, pe.what());
  358. } catch (const ModuleSpecError& dde) {
  359. LOG_ERROR(config_logger, CONFIG_MOD_SPEC_FORMAT).arg(filename).arg(dde.what());
  360. isc_throw(CCSessionInitError, dde.what());
  361. }
  362. file.close();
  363. return (module_spec);
  364. }
  365. void
  366. ModuleCCSession::startCheck() {
  367. // data available on the command channel. process it in the synchronous
  368. // mode.
  369. checkCommand();
  370. // start asynchronous read again.
  371. session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
  372. }
  373. ModuleCCSession::ModuleCCSession(
  374. const std::string& spec_file_name,
  375. isc::cc::AbstractSession& session,
  376. isc::data::ConstElementPtr(*config_handler)(
  377. isc::data::ConstElementPtr new_config),
  378. isc::data::ConstElementPtr(*command_handler)(
  379. const std::string& command, isc::data::ConstElementPtr args),
  380. bool start_immediately,
  381. bool handle_logging
  382. ) :
  383. started_(false),
  384. session_(session)
  385. {
  386. module_specification_ = readModuleSpecification(spec_file_name);
  387. setModuleSpec(module_specification_);
  388. module_name_ = module_specification_.getFullSpec()->get("module_name")->stringValue();
  389. config_handler_ = config_handler;
  390. command_handler_ = command_handler;
  391. session_.establish(NULL);
  392. session_.subscribe(module_name_, "*");
  393. // send the data specification
  394. ConstElementPtr spec_msg = createCommand("module_spec",
  395. module_specification_.getFullSpec());
  396. unsigned int seq = session_.group_sendmsg(spec_msg, "ConfigManager");
  397. ConstElementPtr answer, env;
  398. session_.group_recvmsg(env, answer, false, seq);
  399. int rcode = -1;
  400. ConstElementPtr err = parseAnswer(rcode, answer);
  401. if (rcode != 0) {
  402. LOG_ERROR(config_logger, CONFIG_MOD_SPEC_REJECT).arg(answer->str());
  403. isc_throw(CCSessionInitError, answer->str());
  404. }
  405. setLocalConfig(Element::createMap());
  406. // get any stored configuration from the manager
  407. if (config_handler_) {
  408. ConstElementPtr cmd =
  409. createCommand("get_config",
  410. Element::fromJSON("{\"module_name\":\"" +
  411. module_name_ + "\"}"));
  412. seq = session_.group_sendmsg(cmd, "ConfigManager");
  413. session_.group_recvmsg(env, answer, false, seq);
  414. ConstElementPtr new_config = parseAnswer(rcode, answer);
  415. if (rcode == 0) {
  416. handleConfigUpdate(new_config);
  417. } else {
  418. LOG_ERROR(config_logger, CONFIG_GET_FAIL).arg(new_config->str());
  419. isc_throw(CCSessionInitError, answer->str());
  420. }
  421. }
  422. // Keep track of logging settings automatically
  423. if (handle_logging) {
  424. addRemoteConfig("Logging", default_logconfig_handler, false);
  425. }
  426. if (start_immediately) {
  427. start();
  428. }
  429. }
  430. ModuleCCSession::~ModuleCCSession() {
  431. try {
  432. sendStopping();
  433. } catch (const std::exception& exc) {
  434. LOG_ERROR(config_logger,
  435. CONFIG_CCSESSION_STOPPING).arg(exc.what());
  436. } catch (...) {
  437. LOG_ERROR(config_logger,
  438. CONFIG_CCSESSION_STOPPING_UNKNOWN);
  439. }
  440. };
  441. void
  442. ModuleCCSession::start() {
  443. if (started_) {
  444. isc_throw(CCSessionError, "Module CC session already started");
  445. }
  446. // register callback for asynchronous read
  447. session_.startRead(boost::bind(&ModuleCCSession::startCheck, this));
  448. started_ = true;
  449. }
  450. /// Validates the new config values, if they are correct,
  451. /// call the config handler with the values that have changed
  452. /// If that results in success, store the new config
  453. ConstElementPtr
  454. ModuleCCSession::handleConfigUpdate(ConstElementPtr new_config) {
  455. ConstElementPtr answer;
  456. ElementPtr errors = Element::createList();
  457. if (!config_handler_) {
  458. answer = createAnswer(1, module_name_ + " does not have a config handler");
  459. } else if (!module_specification_.validateConfig(new_config, false,
  460. errors)) {
  461. std::stringstream ss;
  462. ss << "Error in config validation: ";
  463. BOOST_FOREACH(ConstElementPtr error, errors->listValue()) {
  464. ss << error->stringValue();
  465. }
  466. answer = createAnswer(2, ss.str());
  467. } else {
  468. // remove the values that have not changed
  469. ConstElementPtr diff = removeIdentical(new_config, getLocalConfig());
  470. // handle config update
  471. answer = config_handler_(diff);
  472. int rcode = -1;
  473. parseAnswer(rcode, answer);
  474. if (rcode == 0) {
  475. ElementPtr local_config = getLocalConfig();
  476. isc::data::merge(local_config, diff);
  477. setLocalConfig(local_config);
  478. }
  479. }
  480. return (answer);
  481. }
  482. bool
  483. ModuleCCSession::hasQueuedMsgs() const {
  484. return (session_.hasQueuedMsgs());
  485. }
  486. ConstElementPtr
  487. ModuleCCSession::checkConfigUpdateCommand(const std::string& target_module,
  488. ConstElementPtr arg)
  489. {
  490. if (target_module == module_name_) {
  491. return (handleConfigUpdate(arg));
  492. } else {
  493. // ok this update is not for us, if we have this module
  494. // in our remote config list, update that
  495. updateRemoteConfig(target_module, arg);
  496. // we're not supposed to answer to this, so return
  497. return (ElementPtr());
  498. }
  499. }
  500. ConstElementPtr
  501. ModuleCCSession::checkModuleCommand(const std::string& cmd_str,
  502. const std::string& target_module,
  503. ConstElementPtr arg) const
  504. {
  505. if (target_module == module_name_) {
  506. if (command_handler_) {
  507. ElementPtr errors = Element::createList();
  508. if (module_specification_.validateCommand(cmd_str,
  509. arg,
  510. errors)) {
  511. return (command_handler_(cmd_str, arg));
  512. } else {
  513. std::stringstream ss;
  514. ss << "Error in command validation: ";
  515. BOOST_FOREACH(ConstElementPtr error,
  516. errors->listValue()) {
  517. ss << error->stringValue();
  518. }
  519. return (createAnswer(3, ss.str()));
  520. }
  521. } else {
  522. return (createAnswer(1,
  523. "Command given but no "
  524. "command handler for module"));
  525. }
  526. }
  527. return (ElementPtr());
  528. }
  529. int
  530. ModuleCCSession::checkCommand() {
  531. ConstElementPtr cmd, routing, data;
  532. if (session_.group_recvmsg(routing, data, true)) {
  533. // In case the message is wanted asynchronously, it gets used.
  534. if (checkAsyncRecv(routing, data)) {
  535. return (0);
  536. }
  537. /* ignore result messages (in case we're out of sync, to prevent
  538. * pingpongs */
  539. if (data->getType() != Element::map ||
  540. data->contains(isc::cc::CC_PAYLOAD_RESULT)) {
  541. return (0);
  542. }
  543. ConstElementPtr arg;
  544. ConstElementPtr answer;
  545. try {
  546. std::string cmd_str = parseCommand(arg, data);
  547. std::string target_module =
  548. routing->get(isc::cc::CC_HEADER_GROUP)->stringValue();
  549. if (cmd_str == "config_update") {
  550. answer = checkConfigUpdateCommand(target_module, arg);
  551. } else {
  552. answer = checkModuleCommand(cmd_str, target_module, arg);
  553. }
  554. } catch (const CCSessionError& re) {
  555. LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG).arg(re.what());
  556. } catch (const std::exception& stde) {
  557. // No matter what unexpected error happens, we do not want
  558. // to crash because of an incoming event, so we log the
  559. // exception and continue to run
  560. LOG_ERROR(config_logger, CONFIG_CCSESSION_MSG_INTERNAL).arg(stde.what());
  561. }
  562. if (!isNull(answer)) {
  563. session_.reply(routing, answer);
  564. }
  565. }
  566. return (0);
  567. }
  568. ModuleSpec
  569. ModuleCCSession::fetchRemoteSpec(const std::string& module, bool is_filename) {
  570. if (is_filename) {
  571. // It is a filename, simply load it.
  572. return (readModuleSpecification(module));
  573. } else {
  574. // It's module name, request it from config manager
  575. // Send the command
  576. ConstElementPtr cmd(createCommand("get_module_spec",
  577. Element::fromJSON("{\"module_name\": \"" + module +
  578. "\"}")));
  579. unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
  580. ConstElementPtr env, answer;
  581. session_.group_recvmsg(env, answer, false, seq);
  582. int rcode = -1;
  583. ConstElementPtr spec_data = parseAnswer(rcode, answer);
  584. if (rcode == 0 && spec_data) {
  585. // received OK, construct the spec out of it
  586. ModuleSpec spec = ModuleSpec(spec_data);
  587. if (module != spec.getModuleName()) {
  588. // It's a different module!
  589. isc_throw(CCSessionError, "Module name mismatch");
  590. }
  591. return (spec);
  592. } else {
  593. isc_throw(CCSessionError, "Error getting config for " +
  594. module + ": " + answer->str());
  595. }
  596. }
  597. }
  598. std::string
  599. ModuleCCSession::addRemoteConfig(const std::string& spec_name,
  600. RemoteHandler handler,
  601. bool spec_is_filename)
  602. {
  603. // First get the module name, specification and default config
  604. const ModuleSpec rmod_spec(fetchRemoteSpec(spec_name, spec_is_filename));
  605. const std::string module_name(rmod_spec.getModuleName());
  606. ConfigData rmod_config(rmod_spec);
  607. // Get the current configuration values from config manager
  608. ConstElementPtr cmd(createCommand("get_config",
  609. Element::fromJSON("{\"module_name\": \"" +
  610. module_name + "\"}")));
  611. const unsigned int seq = session_.group_sendmsg(cmd, "ConfigManager");
  612. ConstElementPtr env, answer;
  613. session_.group_recvmsg(env, answer, false, seq);
  614. int rcode = -1;
  615. ConstElementPtr new_config = parseAnswer(rcode, answer);
  616. ElementPtr local_config;
  617. if (rcode == 0 && new_config) {
  618. // Merge the received config into existing local config
  619. local_config = rmod_config.getLocalConfig();
  620. isc::data::merge(local_config, new_config);
  621. rmod_config.setLocalConfig(local_config);
  622. } else {
  623. isc_throw(CCSessionError, "Error getting config for " + module_name + ": " + answer->str());
  624. }
  625. // all ok, add it
  626. remote_module_configs_[module_name] = rmod_config;
  627. if (handler) {
  628. remote_module_handlers_[module_name] = handler;
  629. handler(module_name, local_config, rmod_config);
  630. }
  631. // Make sure we get updates in future
  632. session_.subscribe(module_name);
  633. return (module_name);
  634. }
  635. void
  636. ModuleCCSession::removeRemoteConfig(const std::string& module_name) {
  637. std::map<std::string, ConfigData>::iterator it;
  638. it = remote_module_configs_.find(module_name);
  639. if (it != remote_module_configs_.end()) {
  640. remote_module_configs_.erase(it);
  641. remote_module_handlers_.erase(module_name);
  642. session_.unsubscribe(module_name);
  643. }
  644. }
  645. ConstElementPtr
  646. ModuleCCSession::getRemoteConfigValue(const std::string& module_name,
  647. const std::string& identifier) const
  648. {
  649. std::map<std::string, ConfigData>::const_iterator it =
  650. remote_module_configs_.find(module_name);
  651. if (it != remote_module_configs_.end()) {
  652. return ((*it).second.getValue(identifier));
  653. } else {
  654. isc_throw(CCSessionError,
  655. "Remote module " + module_name + " not found.");
  656. }
  657. }
  658. void
  659. ModuleCCSession::updateRemoteConfig(const std::string& module_name,
  660. ConstElementPtr new_config)
  661. {
  662. std::map<std::string, ConfigData>::iterator it;
  663. it = remote_module_configs_.find(module_name);
  664. if (it != remote_module_configs_.end()) {
  665. ElementPtr rconf = (*it).second.getLocalConfig();
  666. isc::data::merge(rconf, new_config);
  667. std::map<std::string, RemoteHandler>::iterator hit =
  668. remote_module_handlers_.find(module_name);
  669. if (hit != remote_module_handlers_.end()) {
  670. hit->second(module_name, new_config, it->second);
  671. }
  672. }
  673. }
  674. void
  675. ModuleCCSession::sendStopping() {
  676. // Inform the configuration manager that this module is stopping
  677. ConstElementPtr cmd(createCommand("stopping",
  678. Element::fromJSON(
  679. "{\"module_name\": \"" +
  680. module_name_ + "\"}")));
  681. // It's just an FYI, configmanager is not expected to respond.
  682. session_.group_sendmsg(cmd, "ConfigManager");
  683. }
  684. class ModuleCCSession::AsyncRecvRequest {
  685. public: // Everything is public here, as the definition is hidden anyway
  686. AsyncRecvRequest(const AsyncRecvCallback& cb, const string& rcp, int sq,
  687. bool reply) :
  688. callback(cb),
  689. recipient(rcp),
  690. seq(sq),
  691. is_reply(reply)
  692. {}
  693. const AsyncRecvCallback callback;
  694. const string recipient;
  695. const int seq;
  696. const bool is_reply;
  697. };
  698. ModuleCCSession::AsyncRecvRequestID
  699. ModuleCCSession::groupRecvMsgAsync(const AsyncRecvCallback& callback,
  700. bool is_reply, int seq,
  701. const string& recipient) {
  702. // This just stores the request, the handling is done in checkCommand()
  703. // push_back would be simpler, but it does not return the iterator we need
  704. return (async_recv_requests_.insert(async_recv_requests_.end(),
  705. AsyncRecvRequest(callback, recipient,
  706. seq, is_reply)));
  707. }
  708. bool
  709. ModuleCCSession::checkAsyncRecv(const ConstElementPtr& envelope,
  710. const ConstElementPtr& msg)
  711. {
  712. for (AsyncRecvRequestID request(async_recv_requests_.begin());
  713. request != async_recv_requests_.end(); ++request) {
  714. // Just go through all the requests and look for a matching one
  715. if (requestMatch(*request, envelope)) {
  716. // We want the request to be still alive at the time we
  717. // call the callback. But we need to remove it on an exception
  718. // too, so we use the class. If just C++ had the finally keyword.
  719. class RequestDeleter {
  720. public:
  721. RequestDeleter(AsyncRecvRequests& requests,
  722. AsyncRecvRequestID& request) :
  723. requests_(requests),
  724. request_(request)
  725. { }
  726. ~RequestDeleter() {
  727. requests_.erase(request_);
  728. }
  729. private:
  730. AsyncRecvRequests& requests_;
  731. AsyncRecvRequestID& request_;
  732. };
  733. RequestDeleter deleter(async_recv_requests_, request);
  734. // Call the callback
  735. request->callback(envelope, msg, request);
  736. return (true);
  737. }
  738. }
  739. return (false);
  740. }
  741. bool
  742. ModuleCCSession::requestMatch(const AsyncRecvRequest& request,
  743. const ConstElementPtr& envelope) const
  744. {
  745. if (request.is_reply != envelope->contains(isc::cc::CC_HEADER_REPLY)) {
  746. // Wrong type of message
  747. return (false);
  748. }
  749. if (request.is_reply &&
  750. (request.seq == -1 ||
  751. request.seq == envelope->get(isc::cc::CC_HEADER_REPLY)->intValue())) {
  752. // This is the correct reply
  753. return (true);
  754. }
  755. if (!request.is_reply &&
  756. (request.recipient.empty() || request.recipient ==
  757. envelope->get(isc::cc::CC_HEADER_GROUP)->stringValue())) {
  758. // This is the correct command
  759. return (true);
  760. }
  761. // If nothing from the above, we don't want it
  762. return (false);
  763. }
  764. void
  765. ModuleCCSession::cancelAsyncRecv(const AsyncRecvRequestID& id) {
  766. async_recv_requests_.erase(id);
  767. }
  768. ConstElementPtr
  769. ModuleCCSession::rpcCall(const std::string &command, const std::string &group,
  770. const std::string &instance, const std::string &to,
  771. const ConstElementPtr &params)
  772. {
  773. ConstElementPtr command_el(createCommand(command, params));
  774. const int seq = groupSendMsg(command_el, group, instance, to, true);
  775. ConstElementPtr env, answer;
  776. LOG_DEBUG(config_logger, DBGLVL_TRACE_DETAIL, CONFIG_RPC_SEQ).arg(command).
  777. arg(group).arg(seq);
  778. groupRecvMsg(env, answer, true, seq);
  779. int rcode;
  780. const ConstElementPtr result(parseAnswer(rcode, answer));
  781. if (rcode == isc::cc::CC_REPLY_NO_RECPT) {
  782. isc_throw(RPCRecipientMissing, result);
  783. } else if (rcode != isc::cc::CC_REPLY_SUCCESS) {
  784. isc_throw_1(RPCError, result, rcode);
  785. } else {
  786. return (result);
  787. }
  788. }
  789. }
  790. }