ccsession.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  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 <exceptions/exceptions.h>
  32. #include <config/config_log.h>
  33. #include <config/ccsession.h>
  34. #include <log/logger_support.h>
  35. #include <log/logger_specification.h>
  36. #include <log/logger_manager.h>
  37. #include <log/logger_name.h>
  38. using namespace std;
  39. using isc::data::Element;
  40. using isc::data::ConstElementPtr;
  41. using isc::data::ElementPtr;
  42. using isc::data::JSONError;
  43. namespace isc {
  44. namespace config {
  45. /// Creates a standard config/command protocol answer message
  46. ConstElementPtr
  47. createAnswer() {
  48. ElementPtr answer = Element::createMap();
  49. ElementPtr answer_content = Element::createList();
  50. answer_content->add(Element::create(isc::cc::CC_REPLY_SUCCESS));
  51. answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
  52. return (answer);
  53. }
  54. ConstElementPtr
  55. createAnswer(const int rcode, ConstElementPtr arg) {
  56. if (rcode != 0 && (!arg || arg->getType() != Element::string)) {
  57. isc_throw(CCSessionError, "Bad or no argument for rcode != 0");
  58. }
  59. ElementPtr answer = Element::createMap();
  60. ElementPtr answer_content = Element::createList();
  61. answer_content->add(Element::create(rcode));
  62. answer_content->add(arg);
  63. answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
  64. return (answer);
  65. }
  66. ConstElementPtr
  67. createAnswer(const int rcode, const std::string& arg) {
  68. ElementPtr answer = Element::createMap();
  69. ElementPtr answer_content = Element::createList();
  70. answer_content->add(Element::create(rcode));
  71. answer_content->add(Element::create(arg));
  72. answer->set(isc::cc::CC_PAYLOAD_RESULT, answer_content);
  73. return (answer);
  74. }
  75. ConstElementPtr
  76. parseAnswer(int &rcode, ConstElementPtr msg) {
  77. if (msg &&
  78. msg->getType() == Element::map &&
  79. msg->contains(isc::cc::CC_PAYLOAD_RESULT)) {
  80. ConstElementPtr result = msg->get(isc::cc::CC_PAYLOAD_RESULT);
  81. if (result->getType() != Element::list) {
  82. isc_throw(CCSessionError, "Result element in answer message is not a list");
  83. } else if (result->get(0)->getType() != Element::integer) {
  84. isc_throw(CCSessionError, "First element of result is not an rcode in answer message");
  85. }
  86. rcode = result->get(0)->intValue();
  87. if (result->size() > 1) {
  88. if (rcode == 0 || result->get(1)->getType() == Element::string) {
  89. return (result->get(1));
  90. } else {
  91. isc_throw(CCSessionError, "Error description in result with rcode != 0 is not a string");
  92. }
  93. } else {
  94. if (rcode == 0) {
  95. return (ElementPtr());
  96. } else {
  97. isc_throw(CCSessionError, "Result with rcode != 0 does not have an error description");
  98. }
  99. }
  100. } else {
  101. isc_throw(CCSessionError, "No result part in answer message");
  102. }
  103. }
  104. ConstElementPtr
  105. createCommand(const std::string& command) {
  106. return (createCommand(command, ElementPtr()));
  107. }
  108. ConstElementPtr
  109. createCommand(const std::string& command, ConstElementPtr arg) {
  110. ElementPtr cmd = Element::createMap();
  111. ElementPtr cmd_parts = Element::createList();
  112. cmd_parts->add(Element::create(command));
  113. if (arg) {
  114. cmd_parts->add(arg);
  115. }
  116. cmd->set(isc::cc::CC_PAYLOAD_COMMAND, cmd_parts);
  117. return (cmd);
  118. }
  119. std::string
  120. parseCommand(ConstElementPtr& arg, ConstElementPtr command) {
  121. if (command &&
  122. command->getType() == Element::map &&
  123. command->contains(isc::cc::CC_PAYLOAD_COMMAND)) {
  124. ConstElementPtr cmd = command->get(isc::cc::CC_PAYLOAD_COMMAND);
  125. if (cmd->getType() == Element::list &&
  126. !cmd->empty() &&
  127. cmd->get(0)->getType() == Element::string) {
  128. if (cmd->size() > 1) {
  129. arg = cmd->get(1);
  130. } else {
  131. arg = Element::createMap();
  132. }
  133. return (cmd->get(0)->stringValue());
  134. } else {
  135. isc_throw(CCSessionError, "Command part in command message missing, empty, or not a list");
  136. }
  137. } else {
  138. isc_throw(CCSessionError, "Command Element empty or not a map with \"command\"");
  139. }
  140. }
  141. namespace {
  142. // Temporary workaround functions for missing functionality in
  143. // getValue() (main problem described in ticket #993)
  144. // This returns either the value set for the given relative id,
  145. // or its default value
  146. // (intentionally defined here so this interface does not get
  147. // included in ConfigData as it is)
  148. ConstElementPtr getValueOrDefault(ConstElementPtr config_part,
  149. const std::string& relative_id,
  150. const ConfigData& config_data,
  151. const std::string& full_id) {
  152. if (config_part->contains(relative_id)) {
  153. return config_part->get(relative_id);
  154. } else {
  155. return config_data.getDefaultValue(full_id);
  156. }
  157. }
  158. /// @brief Prefix name with "kea-".
  159. ///
  160. /// In BIND 10, modules had names taken from the .spec file, which are typically
  161. /// names starting with a capital letter (e.g. "Resolver", "Auth" etc.). The
  162. /// names of the associated binaries are derived from the module names, being
  163. /// prefixed "b10-" and having the first letter of the module name lower-cased
  164. /// (e.g. "b10-resolver", "b10-auth"). (It is a required convention that there
  165. /// be this relationship between the names.)
  166. ///
  167. /// In Kea we're not using module names, but we do still keep some capability to
  168. /// run Kea servers in Bundy framework. For that reason the whole discussion here
  169. /// applies only to case when Kea is compiled with Bundy configuration backend.
  170. ///
  171. /// Within the binaries the root loggers are named after the binaries themselves.
  172. /// (The reason for this is that the name of the logger is included in the
  173. /// message logged, so making it clear which message comes from which Kea
  174. /// process.) As logging is configured using module names, the configuration code
  175. /// has to match these with the corresponding logger names. This function
  176. /// converts a module name to a root logger name by lowercasing the first letter
  177. /// of the module name and prepending "kea-".
  178. ///
  179. /// \param instring String to convert. (This may be empty, in which case
  180. /// "kea-" will be returned.)
  181. ///
  182. /// \return Converted string.
  183. std::string
  184. keaPrefix(const std::string& instring) {
  185. std::string result = instring;
  186. if (!result.empty()) {
  187. result[0] = tolower(result[0]);
  188. }
  189. return (std::string("kea-") + result);
  190. }
  191. // Reads a output_option subelement of a logger configuration,
  192. // and sets the values thereing to the given OutputOption struct,
  193. // or defaults values if they are not provided (from config_data).
  194. void
  195. readOutputOptionConf(isc::log::OutputOption& output_option,
  196. ConstElementPtr output_option_el,
  197. const ConfigData& config_data)
  198. {
  199. ConstElementPtr destination_el = getValueOrDefault(output_option_el,
  200. "destination", config_data,
  201. "loggers/output_options/destination");
  202. output_option.destination = isc::log::getDestination(destination_el->stringValue());
  203. ConstElementPtr output_el = getValueOrDefault(output_option_el,
  204. "output", config_data,
  205. "loggers/output_options/output");
  206. if (output_option.destination == isc::log::OutputOption::DEST_CONSOLE) {
  207. output_option.stream = isc::log::getStream(output_el->stringValue());
  208. } else if (output_option.destination == isc::log::OutputOption::DEST_FILE) {
  209. output_option.filename = output_el->stringValue();
  210. } else if (output_option.destination == isc::log::OutputOption::DEST_SYSLOG) {
  211. output_option.facility = output_el->stringValue();
  212. }
  213. output_option.flush = getValueOrDefault(output_option_el,
  214. "flush", config_data,
  215. "loggers/output_options/flush")->boolValue();
  216. output_option.maxsize = getValueOrDefault(output_option_el,
  217. "maxsize", config_data,
  218. "loggers/output_options/maxsize")->intValue();
  219. output_option.maxver = getValueOrDefault(output_option_el,
  220. "maxver", config_data,
  221. "loggers/output_options/maxver")->intValue();
  222. }
  223. // Reads a full 'loggers' configuration, and adds the loggers therein
  224. // to the given vector, fills in blanks with defaults from config_data
  225. void
  226. readLoggersConf(std::vector<isc::log::LoggerSpecification>& specs,
  227. ConstElementPtr logger,
  228. const ConfigData& config_data)
  229. {
  230. // Read name, adding prefix as required.
  231. std::string lname = logger->get("name")->stringValue();
  232. ConstElementPtr severity_el = getValueOrDefault(logger,
  233. "severity", config_data,
  234. "loggers/severity");
  235. isc::log::Severity severity = isc::log::getSeverity(
  236. severity_el->stringValue());
  237. int dbg_level = getValueOrDefault(logger, "debuglevel",
  238. config_data,
  239. "loggers/debuglevel")->intValue();
  240. bool additive = getValueOrDefault(logger, "additive", config_data,
  241. "loggers/additive")->boolValue();
  242. isc::log::LoggerSpecification logger_spec(
  243. lname, severity, dbg_level, additive
  244. );
  245. if (logger->contains("output_options")) {
  246. BOOST_FOREACH(ConstElementPtr output_option_el,
  247. logger->get("output_options")->listValue()) {
  248. // create outputoptions
  249. isc::log::OutputOption output_option;
  250. readOutputOptionConf(output_option,
  251. output_option_el,
  252. config_data);
  253. logger_spec.addOutputOption(output_option);
  254. }
  255. }
  256. specs.push_back(logger_spec);
  257. }
  258. // Copies the map for a logger, changing the name of the logger in the process.
  259. // This is used because the map being copied is "const", so in order to
  260. // change the name we need to create a new one.
  261. //
  262. // \param cur_logger Logger being copied.
  263. // \param new_name New value of the "name" element at the top level.
  264. //
  265. // \return Pointer to the map with the updated element.
  266. ConstElementPtr
  267. copyLogger(ConstElementPtr& cur_logger, const std::string& new_name) {
  268. // Since we'll only be updating one first-level element and subsequent
  269. // use won't change the contents of the map, a shallow map copy is enough.
  270. ElementPtr new_logger(Element::createMap());
  271. new_logger->setValue(cur_logger->mapValue());
  272. new_logger->set("name", Element::create(new_name));
  273. return (new_logger);
  274. }
  275. } // end anonymous namespace
  276. ConstElementPtr
  277. getRelatedLoggers(ConstElementPtr loggers) {
  278. // Keep a list of names for easier lookup later
  279. std::set<std::string> our_names;
  280. const std::string& root_name = isc::log::getRootLoggerName();
  281. ElementPtr result = isc::data::Element::createList();
  282. BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
  283. // Need to add the kea- prefix to names ready from the spec file.
  284. const std::string cur_name = cur_logger->get("name")->stringValue();
  285. const std::string mod_name = keaPrefix(cur_name);
  286. if (mod_name == root_name || mod_name.find(root_name + ".") == 0) {
  287. // Note this name so that we don't add a wildcard that matches it.
  288. our_names.insert(mod_name);
  289. // We want to store the logger with the modified name (i.e. with
  290. // the kea- prefix). As we are dealing with const loggers, we
  291. // store a modified copy of the data.
  292. result->add(copyLogger(cur_logger, mod_name));
  293. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_EXPLICIT)
  294. .arg(cur_name);
  295. } else if (!cur_name.empty() && (cur_name[0] != '*')) {
  296. // Not a wildcard logger and we are ignoring it.
  297. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
  298. CONFIG_LOG_IGNORE_EXPLICIT).arg(cur_name);
  299. }
  300. }
  301. // Now find the wildcard names (the one that start with "*").
  302. BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) {
  303. const std::string cur_name = cur_logger->get("name")->stringValue();
  304. // If name is '*', or starts with '*.', replace * with root
  305. // logger name.
  306. if (cur_name == "*" || (cur_name.length() > 1 &&
  307. cur_name[0] == '*' && cur_name[1] == '.')) {
  308. // Substitute the "*" with the root name
  309. std::string mod_name = cur_name;
  310. mod_name.replace(0, 1, root_name);
  311. // Now add it to the result list, but only if a logger with
  312. // that name was not configured explicitly.
  313. if (our_names.find(mod_name) == our_names.end()) {
  314. // We substitute the name here, but as we are dealing with
  315. // consts, we need to copy the data.
  316. result->add(copyLogger(cur_logger, mod_name));
  317. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
  318. CONFIG_LOG_WILD_MATCH).arg(cur_name);
  319. } else if (!cur_name.empty() && (cur_name[0] == '*')) {
  320. // Is a wildcard and we are ignoring it (because the wildcard
  321. // expands to a specification that we already encountered when
  322. // processing explicit names).
  323. LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS,
  324. CONFIG_LOG_IGNORE_WILD).arg(cur_name);
  325. }
  326. }
  327. }
  328. return (result);
  329. }
  330. void
  331. default_logconfig_handler(const std::string& module_name,
  332. ConstElementPtr new_config,
  333. const ConfigData& config_data) {
  334. config_data.getModuleSpec().validateConfig(new_config, true);
  335. std::vector<isc::log::LoggerSpecification> specs;
  336. if (new_config->contains("loggers")) {
  337. ConstElementPtr loggers = getRelatedLoggers(new_config->get("loggers"));
  338. BOOST_FOREACH(ConstElementPtr logger,
  339. loggers->listValue()) {
  340. readLoggersConf(specs, logger, config_data);
  341. }
  342. }
  343. isc::log::LoggerManager logger_manager;
  344. logger_manager.process(specs.begin(), specs.end());
  345. }
  346. }
  347. }