ccsession.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  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. #ifndef CCSESSION_H
  15. #define CCSESSION_H 1
  16. #include <config/config_data.h>
  17. #include <config/module_spec.h>
  18. #include <cc/session.h>
  19. #include <cc/data.h>
  20. #include <string>
  21. #include <list>
  22. #include <boost/function.hpp>
  23. namespace isc {
  24. namespace config {
  25. ///
  26. /// \brief Creates a standard config/command level success answer message
  27. /// (i.e. of the form { "result": [ 0 ] }
  28. /// \return Standard command/config success answer message
  29. isc::data::ConstElementPtr createAnswer();
  30. ///
  31. /// \brief Creates a standard config/command level answer message
  32. /// (i.e. of the form { "result": [ rcode, arg ] }
  33. /// If rcode != 0, arg must be a StringElement
  34. ///
  35. /// \param rcode The return code (0 for success)
  36. /// \param arg For rcode == 0, this is an optional argument of any
  37. /// Element type. For rcode == 1, this argument is mandatory,
  38. /// and must be a StringElement containing an error description
  39. /// \return Standard command/config answer message
  40. isc::data::ConstElementPtr createAnswer(const int rcode,
  41. isc::data::ConstElementPtr arg);
  42. ///
  43. /// \brief Creates a standard config/command level answer message
  44. /// (i.e. of the form { "result": [ rcode, arg ] }
  45. ///
  46. /// \param rcode The return code (0 for success)
  47. /// \param arg A string to put into the StringElement argument
  48. /// \return Standard command/config answer message
  49. isc::data::ConstElementPtr createAnswer(const int rcode,
  50. const std::string& arg);
  51. ///
  52. /// Parses a standard config/command level answer message
  53. ///
  54. /// \param rcode This value will be set to the return code contained in
  55. /// the message
  56. /// \param msg The message to parse
  57. /// \return The optional argument in the message, or an empty ElementPtr
  58. /// if there was no argument. If rcode != 0, this contains a
  59. /// StringElement with the error description.
  60. isc::data::ConstElementPtr parseAnswer(int &rcode,
  61. isc::data::ConstElementPtr msg);
  62. ///
  63. /// \brief Creates a standard config/command command message with no
  64. /// argument (of the form { "command": [ "my_command" ] }
  65. ///
  66. /// \param command The command string
  67. /// \return The created message
  68. isc::data::ConstElementPtr createCommand(const std::string& command);
  69. ///
  70. /// \brief Creates a standard config/command command message with the
  71. /// given argument (of the form { "command": [ "my_command", arg ] }
  72. ///
  73. /// \param command The command string
  74. /// \param arg The optional argument for the command. This can be of
  75. /// any Element type, but it should conform to the .spec file.
  76. /// \return The created message
  77. isc::data::ConstElementPtr createCommand(const std::string& command,
  78. isc::data::ConstElementPtr arg);
  79. ///
  80. /// \brief Parses the given command into a string containing the actual
  81. /// command and an ElementPtr containing the optional argument.
  82. ///
  83. /// Raises a CCSessionError if this is not a well-formed command
  84. ///
  85. /// Example code: (command_message is a ConstElementPtr that is
  86. /// passed here)
  87. /// \code
  88. /// ElementPtr command_message = Element::fromJSON("{ \"command\": [\"foo\", { \"bar\": 123 } ] }");
  89. /// try {
  90. /// ConstElementPtr args;
  91. /// std::string command_str = parseCommand(args, command_message);
  92. /// if (command_str == "foo") {
  93. /// std::cout << "The command 'foo' was given" << std::endl;
  94. /// if (args->contains("bar")) {
  95. /// std::cout << "It had argument name 'bar' set, which has"
  96. /// << "value "
  97. /// << args->get("bar")->intValue();
  98. /// }
  99. /// } else {
  100. /// std::cout << "Unknown command '" << command_str << std::endl;
  101. /// }
  102. /// } catch (CCSessionError cse) {
  103. /// std::cerr << "Bad command in CC Session: "
  104. /// << cse.what() << std::endl;
  105. /// }
  106. /// \endcode
  107. ///
  108. /// \param arg This value will be set to the ElementPtr pointing to
  109. /// the argument, or to an empty Map (ElementPtr) if there was none.
  110. /// \param command The command message containing the command (as made
  111. /// by createCommand()
  112. /// \return The command name
  113. std::string parseCommand(isc::data::ConstElementPtr& arg,
  114. isc::data::ConstElementPtr command);
  115. ///
  116. /// \brief A standard cc session exception that is thrown if a function
  117. /// is there is a problem with one of the messages
  118. ///
  119. // todo: include types and called function in the exception
  120. class CCSessionError : public isc::Exception {
  121. public:
  122. CCSessionError(const char* file, size_t line, const char* what) :
  123. isc::Exception(file, line, what) {}
  124. };
  125. ///
  126. /// \brief This exception is thrown if the constructor fails
  127. ///
  128. class CCSessionInitError : public isc::Exception {
  129. public:
  130. CCSessionInitError(const char* file, size_t line, const char* what) :
  131. isc::Exception(file, line, what) {}
  132. };
  133. ///
  134. /// \brief This module keeps a connection to the command channel,
  135. /// holds configuration information, and handles messages from
  136. /// the command channel
  137. ///
  138. class ModuleCCSession : public ConfigData {
  139. public:
  140. /**
  141. * Initialize a config/command session
  142. *
  143. * @param spec_file_name The name of the file containing the
  144. * module specification.
  145. * @param session A Session object over which configuration and command
  146. * data are exchanged.
  147. * @param config_handler A callback function pointer to be called when
  148. * configuration of the local module needs to be updated.
  149. * This must refer to a valid object of a concrete derived class of
  150. * AbstractSession without establishing the session.
  151. *
  152. * Note: the design decision on who is responsible for establishing the
  153. * session is in flux, and may change in near future.
  154. *
  155. * \exception CCSessionInitError when the initialization fails,
  156. * either because the file cannot be read or there is
  157. * a communication problem with the config manager.
  158. *
  159. * @param command_handler A callback function pointer to be called when
  160. * a control command from a remote agent needs to be performed on the
  161. * local module.
  162. * @param start_immediately If true (default), start listening to new commands
  163. * and configuration changes asynchronously at the end of the constructor;
  164. * if false, it will be delayed until the start() method is explicitly
  165. * called. (This is a short term workaround for an initialization trouble.
  166. * We'll need to develop a cleaner solution, and then remove this knob)
  167. * @param handle_logging If true, the ModuleCCSession will automatically
  168. * take care of logging configuration through the virtual Logging config
  169. * module. Defaults to true.
  170. */
  171. ModuleCCSession(const std::string& spec_file_name,
  172. isc::cc::AbstractSession& session,
  173. isc::data::ConstElementPtr(*config_handler)(
  174. isc::data::ConstElementPtr new_config) = NULL,
  175. isc::data::ConstElementPtr(*command_handler)(
  176. const std::string& command,
  177. isc::data::ConstElementPtr args) = NULL,
  178. bool start_immediately = true,
  179. bool handle_logging = true
  180. );
  181. ///
  182. /// Destructor
  183. ///
  184. /// The destructor automatically calls sendStopping(), which sends
  185. /// a message to the ConfigManager that this module is stopping
  186. ///
  187. virtual ~ModuleCCSession();
  188. /// Start receiving new commands and configuration changes asynchronously.
  189. ///
  190. /// This method must be called only once, and only when the ModuleCCSession
  191. /// was constructed with start_immediately being false. Otherwise
  192. /// CCSessionError will be thrown.
  193. ///
  194. /// As noted in the constructor, this method should be considered a short
  195. /// term workaround and will be removed in future.
  196. void start();
  197. /**
  198. * Optional optimization for checkCommand loop; returns true
  199. * if there are unhandled queued messages in the cc session.
  200. * (if either this is true or there is data on the socket found
  201. * by the select() call on getSocket(), run checkCommand())
  202. *
  203. * @return true if there are unhandled queued messages
  204. */
  205. bool hasQueuedMsgs() const;
  206. /**
  207. * Check if there is a command or config change on the command
  208. * session. If so, the appropriate handler is called if set.
  209. * If not set, a default answer is returned.
  210. * This is a non-blocking read; if there is nothing this function
  211. * will return 0.
  212. */
  213. int checkCommand();
  214. /**
  215. * The config handler function should expect an ElementPtr containing
  216. * the full configuration where non-default values have been set.
  217. * Later we might want to think about more granular control
  218. * (i.e. this does not scale to for instance lists containing
  219. * 100000 zones, where the whole list is passed every time a single
  220. * thing changes)
  221. */
  222. void setConfigHandler(isc::data::ConstElementPtr(*config_handler)(
  223. isc::data::ConstElementPtr new_config))
  224. {
  225. config_handler_ = config_handler;
  226. }
  227. /**
  228. * Set a command handler; the function that is passed takes an
  229. * ElementPtr, pointing to a list element, containing
  230. * [ module_name, command_name, arg1, arg2, ... ]
  231. * The returned ElementPtr should look like
  232. * { "result": [ return_value, result_value ] }
  233. * result value here is optional and depends on the command
  234. *
  235. * This protocol is very likely to change.
  236. */
  237. void setCommandHandler(isc::data::ConstElementPtr(*command_handler)(
  238. const std::string& command,
  239. isc::data::ConstElementPtr args))
  240. {
  241. command_handler_ = command_handler;
  242. }
  243. /**
  244. * Gives access to the configuration values of a different module
  245. * Once this function has been called with the name of the specification
  246. * file or the module you want the configuration of, you can use
  247. * \c getRemoteConfigValue() to get a specific setting.
  248. * Changes are automatically updated, and you can specify handlers
  249. * for those changes. This function will subscribe to the relevant module
  250. * channel.
  251. *
  252. * This method must be called before calling the \c start() method on the
  253. * ModuleCCSession (it also implies the ModuleCCSession must have been
  254. * constructed with start_immediately being false).
  255. *
  256. * \param spec_name This specifies the module to add. It is either a
  257. * filename of the spec file to use or a name of module
  258. * (in case it's a module name, the spec data is
  259. * downloaded from the configuration manager, therefore
  260. * the configuration manager must know it). If
  261. * spec_is_filename is true (the default), then a
  262. * filename is assumed, otherwise a module name.
  263. * \param handler The handler functor called whenever there's a change.
  264. * Called once initally from this function. May be NULL
  265. * if you don't want any handler to be called and you're
  266. * fine with requesting the data through
  267. * getRemoteConfigValue() each time.
  268. *
  269. * The handler should not throw, or it'll fall trough and
  270. * the exception will get into strange places, probably
  271. * aborting the application.
  272. * \param spec_is_filename Says if spec_name is filename or module name.
  273. * \return The name of the module specified in the given specification
  274. * file
  275. */
  276. typedef boost::function<void(const std::string&,
  277. isc::data::ConstElementPtr,
  278. const ConfigData&)> RemoteHandler;
  279. std::string addRemoteConfig(const std::string& spec_name,
  280. RemoteHandler handler = RemoteHandler(),
  281. bool spec_is_filename = true);
  282. /**
  283. * Removes the module with the given name from the remote config
  284. * settings. If the module was not added with \c addRemoteConfig(),
  285. * nothing happens. If there was a handler for this config, it is
  286. * removed as well.
  287. */
  288. void removeRemoteConfig(const std::string& module_name);
  289. /**
  290. * Returns the current configuration value for the given module
  291. * name at the given identifier. See \c ConfigData::getValue() for
  292. * more details.
  293. * Raises a ModuleCCSessionError if the module name is unknown
  294. * Raises a DataNotFoundError if the identifier does not exist
  295. * in the specification.
  296. *
  297. * \param module_name The name of the module to get a config value for
  298. * \param identifier The identifier of the config value
  299. * \return The configuration setting at the given identifier
  300. */
  301. isc::data::ConstElementPtr getRemoteConfigValue(
  302. const std::string& module_name,
  303. const std::string& identifier) const;
  304. /**
  305. * Send a message to the underlying CC session.
  306. * This has the same interface as isc::cc::Session::group_sendmsg()
  307. *
  308. * \param msg see isc::cc::Session::group_sendmsg()
  309. * \param group see isc::cc::Session::group_sendmsg()
  310. * \param instance see isc::cc::Session::group_sendmsg()
  311. * \param to see isc::cc::Session::group_sendmsg()
  312. * \return see isc::cc::Session::group_sendmsg()
  313. */
  314. int groupSendMsg(isc::data::ConstElementPtr msg,
  315. std::string group,
  316. std::string instance = "*",
  317. std::string to = "*") {
  318. return (session_.group_sendmsg(msg, group, instance, to));
  319. };
  320. /**
  321. * Receive a message from the underlying CC session.
  322. * This has the same interface as isc::cc::Session::group_recvmsg()
  323. *
  324. * \param envelope see isc::cc::Session::group_recvmsg()
  325. * \param msg see isc::cc::Session::group_recvmsg()
  326. * \param nonblock see isc::cc::Session::group_recvmsg()
  327. * \param seq see isc::cc::Session::group_recvmsg()
  328. * \return see isc::cc::Session::group_recvmsg()
  329. */
  330. bool groupRecvMsg(isc::data::ConstElementPtr& envelope,
  331. isc::data::ConstElementPtr& msg,
  332. bool nonblock = true,
  333. int seq = -1) {
  334. return (session_.group_recvmsg(envelope, msg, nonblock, seq));
  335. };
  336. /// \brief Forward declaration of internal data structure.
  337. ///
  338. /// This holds information about one asynchronous request to receive
  339. /// a message. It is declared as public to allow declaring other derived
  340. /// types, but without showing the internal representation.
  341. class AsyncRecvRequest;
  342. /// \brief List of all requests for asynchronous reads.
  343. typedef std::list<AsyncRecvRequest> AsyncRecvRequests;
  344. /// \brief Identifier of single request for asynchronous read.
  345. typedef AsyncRecvRequests::iterator AsyncRecvRequestID;
  346. /// \brief Callback which is called when an asynchronous receive finishes.
  347. ///
  348. /// This is the callback used by groupRecvMsgAsync() function. It is called
  349. /// when a matching message arrives. It receives following parameters when
  350. /// called:
  351. /// - The envelope of the message
  352. /// - The message itself
  353. /// - The ID of the request, as returned by corresponding groupRecvMsgAsync
  354. /// call.
  355. ///
  356. /// It is possible to throw exceptions from the callback, but they will not
  357. /// be caught and they will get propagated out through the checkCommand()
  358. /// call. This, if not handled on higher level, will likely terminate the
  359. /// application. However, the ModuleCCSession internals will be in
  360. /// well-defined state after the call (both the callback and the message
  361. /// will be removed from the queues as already called).
  362. typedef boost::function3<void, const isc::data::ConstElementPtr&,
  363. const isc::data::ConstElementPtr&,
  364. const AsyncRecvRequestID&>
  365. AsyncRecvCallback;
  366. /// \brief Receive a message from the CC session asynchronously.
  367. ///
  368. /// This registers a callback which is called when a matching message
  369. /// is received. This message returns immediately.
  370. ///
  371. /// Once a matching message arrives, the callback is called with the
  372. /// envelope of the message, the message itself and the result of this
  373. /// function call (which might be useful for identifying which of many
  374. /// events the recipient is waiting for this is). This makes the callback
  375. /// used and is not called again even if a message that would match
  376. /// arrives later (this is a single-shot callback).
  377. ///
  378. /// The callback is never called from within this function. Even if there
  379. /// are queued messages, the callback would be called once checkCommand()
  380. /// is invoked (possibly from start() or the constructor).
  381. ///
  382. /// The matching is as follows. If is_reply is true, only replies are
  383. /// considered. In that case, if seq is -1, any reply is accepted. If
  384. /// it is something else than -1, only the reply with matching seq is
  385. /// taken. This may be used to receive replies to commands
  386. /// asynchronously.
  387. ///
  388. /// In case the is_reply is false, the function looks for command messages.
  389. /// The seq parameter is ignored, but the recipient one is considered. If
  390. /// it is an empty string, any command is taken. If it is non-empty, only
  391. /// commands addressed to the recipient channel (eg. group - instance is
  392. /// ignored for now) are taken. This can be used to receive foreign commands
  393. /// or notifications. In such case, it might be desirable to call the
  394. /// groupRecvMsgAsync again from within the callback, to receive any future
  395. /// commands or events of the same type.
  396. ///
  397. /// The interaction with other receiving functions is slightly complicated.
  398. /// The groupRecvMsg call takes precedence. If the message matches its
  399. /// parameters, it steals the message and no callback matching it as well
  400. /// is called. Then, all the queued asynchronous receives are considered,
  401. /// with the oldest active ones taking precedence (they work as FIFO).
  402. /// If none of them matches, generic command and config handling takes
  403. /// place. If it is not handled by that, the message is dropped. However,
  404. /// it is better if there's just one place that wants to receive each given
  405. /// message.
  406. ///
  407. /// \exception std::bad_alloc if there isn't enough memory to store the
  408. /// callback.
  409. /// \param callback is the function to be called when a matching message
  410. /// arrives.
  411. /// \param is_reply specifies if the desired message should be a reply or
  412. /// a command.
  413. /// \param seq specifies the reply sequence number in case a reply is
  414. /// desired. The default -1 means any reply is OK.
  415. /// \param recipient is the CC channel to which the command should be
  416. /// addressed to match (in case is_reply is false). Empty means any
  417. /// command is good one.
  418. /// \return An identifier of the request. This will be passed to the
  419. /// callback or can be used to cancel the request by cancelAsyncRecv.
  420. /// \todo Decide what to do with instance and what was it meant for anyway.
  421. AsyncRecvRequestID groupRecvMsgAsync(const AsyncRecvCallback& callback,
  422. bool is_reply, int seq = -1,
  423. const std::string& recipient =
  424. std::string());
  425. /// \brief Removes yet unused request for asynchronous receive.
  426. ///
  427. /// This function cancels a request previously queued by
  428. /// groupRecvMsgAsync(). You may use it only before the callback was
  429. /// already triggered. If you call it with an ID of callback that
  430. /// already happened or was already canceled, the behaviour is undefined
  431. /// (but something like a crash is very likely, as the function removes
  432. /// an item from a list and this would be removing it from a list that
  433. /// does not contain the item).
  434. ///
  435. /// It is important to cancel requests that are no longer going to happen
  436. /// for some reason, as the request would occupy memory forever.
  437. ///
  438. /// \param id The id of request as returned by groupRecvMsgAsync.
  439. void cancelAsyncRecv(const AsyncRecvRequestID& id);
  440. private:
  441. ModuleSpec readModuleSpecification(const std::string& filename);
  442. void startCheck();
  443. void sendStopping();
  444. /// \brief Check if the message is wanted by asynchronous read
  445. ///
  446. /// It checks if any of the previously queued requests match
  447. /// the message. If so, the callback is dispatched and removed.
  448. ///
  449. /// \param envelope The envelope of the message.
  450. /// \param msg The actual message data.
  451. /// \return True if the message was used for a callback, false
  452. /// otherwise.
  453. bool checkAsyncRecv(const data::ConstElementPtr& envelope,
  454. const data::ConstElementPtr& msg);
  455. /// \brief Checks if a message with this envelope matches the request
  456. bool requestMatch(const AsyncRecvRequest& request,
  457. const data::ConstElementPtr& envelope) const;
  458. bool started_;
  459. std::string module_name_;
  460. isc::cc::AbstractSession& session_;
  461. ModuleSpec module_specification_;
  462. AsyncRecvRequests async_recv_requests_;
  463. isc::data::ConstElementPtr handleConfigUpdate(
  464. isc::data::ConstElementPtr new_config);
  465. isc::data::ConstElementPtr checkConfigUpdateCommand(
  466. const std::string& target_module,
  467. isc::data::ConstElementPtr arg);
  468. isc::data::ConstElementPtr checkModuleCommand(
  469. const std::string& cmd_str,
  470. const std::string& target_module,
  471. isc::data::ConstElementPtr arg) const;
  472. isc::data::ConstElementPtr(*config_handler_)(
  473. isc::data::ConstElementPtr new_config);
  474. isc::data::ConstElementPtr(*command_handler_)(
  475. const std::string& command,
  476. isc::data::ConstElementPtr args);
  477. std::map<std::string, ConfigData> remote_module_configs_;
  478. std::map<std::string, RemoteHandler> remote_module_handlers_;
  479. void updateRemoteConfig(const std::string& module_name,
  480. isc::data::ConstElementPtr new_config);
  481. ModuleSpec fetchRemoteSpec(const std::string& module, bool is_filename);
  482. };
  483. /// \brief Default handler for logging config updates
  484. ///
  485. /// When CCSession is initialized with handle_logging set to true,
  486. /// this callback will be used to update the logger when a configuration
  487. /// change comes in.
  488. ///
  489. /// This function updates the (global) loggers by initializing a
  490. /// LoggerManager and passing the settings as specified in the given
  491. /// configuration update.
  492. ///
  493. /// \param module_name The name of the module
  494. /// \param new_config The modified configuration values
  495. /// \param config_data The full config data for the (remote) logging
  496. /// module.
  497. void
  498. default_logconfig_handler(const std::string& module_name,
  499. isc::data::ConstElementPtr new_config,
  500. const ConfigData& config_data);
  501. /// \brief Returns the loggers related to this module
  502. ///
  503. /// This function does two things;
  504. /// - it drops the configuration parts for loggers for other modules.
  505. /// - it replaces the '*' in the name of the loggers by the name of
  506. /// this module, but *only* if the expanded name is not configured
  507. /// explicitly.
  508. ///
  509. /// Examples: if this is the module b10-resolver,
  510. /// For the config names ['*', 'b10-auth']
  511. /// The '*' is replaced with 'b10-resolver', and this logger is used.
  512. /// 'b10-auth' is ignored (of course, it will not be in the b10-auth
  513. /// module).
  514. ///
  515. /// For ['*', 'b10-resolver']
  516. /// The '*' is ignored, and only 'b10-resolver' is used.
  517. ///
  518. /// For ['*.reslib', 'b10-resolver']
  519. /// Or ['b10-resolver.reslib', '*']
  520. /// Both are used, where the * will be expanded to b10-resolver
  521. ///
  522. /// \note This is a public function at this time, but mostly for
  523. /// the purposes of testing. Once we can directly test what loggers
  524. /// are running, this function may be moved to the unnamed namespace
  525. ///
  526. /// \param loggers the original 'loggers' config list
  527. /// \return ListElement containing only loggers relevant for this
  528. /// module, where * is replaced by the root logger name
  529. isc::data::ConstElementPtr
  530. getRelatedLoggers(isc::data::ConstElementPtr loggers);
  531. } // namespace config
  532. } // namespace isc
  533. #endif // CCSESSION_H
  534. // Local Variables:
  535. // mode: c++
  536. // End: