datasrc_clients_mgr.h 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. // Copyright (C) 2012 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 DATASRC_CLIENTS_MGR_H
  15. #define DATASRC_CLIENTS_MGR_H 1
  16. #include <util/threads/thread.h>
  17. #include <util/threads/sync.h>
  18. #include <log/logger_support.h>
  19. #include <log/log_dbglevels.h>
  20. #include <dns/rrclass.h>
  21. #include <cc/data.h>
  22. #include <datasrc/exceptions.h>
  23. #include <datasrc/client_list.h>
  24. #include <datasrc/memory/zone_writer.h>
  25. #include <asiolink/io_service.h>
  26. #include <asiolink/local_socket.h>
  27. #include <auth/auth_log.h>
  28. #include <auth/datasrc_config.h>
  29. #include <boost/array.hpp>
  30. #include <boost/bind.hpp>
  31. #include <boost/shared_ptr.hpp>
  32. #include <boost/noncopyable.hpp>
  33. #include <boost/function.hpp>
  34. #include <boost/foreach.hpp>
  35. #include <exception>
  36. #include <cassert>
  37. #include <cerrno>
  38. #include <list>
  39. #include <utility>
  40. #include <sys/types.h>
  41. #include <sys/socket.h>
  42. namespace isc {
  43. namespace auth {
  44. /// \brief An exception that is thrown if initial checks for a command fail
  45. ///
  46. /// This is raised *before* the command to the thread is constructed and
  47. /// sent, so the application can still handle them (and therefore it is
  48. /// public, as opposed to InternalCommandError).
  49. ///
  50. /// And example of its use is currently in loadZone().
  51. class CommandError : public isc::Exception {
  52. public:
  53. CommandError(const char* file, size_t line, const char* what) :
  54. isc::Exception(file, line, what) {}
  55. };
  56. namespace datasrc_clientmgr_internal {
  57. // This namespace is essentially private for DataSrcClientsMgr(Base) and
  58. // DataSrcClientsBuilder(Base). This is exposed in the public header
  59. // only because these classes are templated (for testing purposes) and
  60. // class internal has to be defined here.
  61. /// \brief ID of commands from the DataSrcClientsMgr to DataSrcClientsBuilder.
  62. enum CommandID {
  63. NOOP, ///< Do nothing. Only useful for tests; no argument
  64. RECONFIGURE, ///< Reconfigure the datasource client lists,
  65. /// the argument to the command is the full new
  66. /// datasources configuration.
  67. LOADZONE, ///< Load a new version of zone into a memory,
  68. /// the argument to the command is a map containing 'class'
  69. /// and 'origin' elements, both should have been validated.
  70. SEGMENT_INFO_UPDATE, ///< The memory manager sent an update about segments.
  71. SHUTDOWN, ///< Shutdown the builder; no argument
  72. NUM_COMMANDS
  73. };
  74. /// \brief Callback to be called when the command is completed.
  75. typedef boost::function<void ()> FinishedCallback;
  76. /// \brief The data type passed from DataSrcClientsMgr to
  77. /// DataSrcClientsBuilder.
  78. ///
  79. /// This just holds the data items together, no logic or protection
  80. /// is present here.
  81. struct Command {
  82. /// \brief Constructor
  83. ///
  84. /// It just initializes the member variables of the same names
  85. /// as the parameters.
  86. Command(CommandID id, const data::ConstElementPtr& params,
  87. const FinishedCallback& callback) :
  88. id(id),
  89. params(params),
  90. callback(callback)
  91. {}
  92. /// \brief The command to execute
  93. CommandID id;
  94. /// \brief Argument of the command.
  95. ///
  96. /// If the command takes no argument, it should be null pointer.
  97. ///
  98. /// This may be a null pointer if the command takes no parameters.
  99. data::ConstElementPtr params;
  100. /// \brief A callback to be called once the command finishes.
  101. ///
  102. /// This may be an empty boost::function. In such case, no callback
  103. /// will be called after completion.
  104. FinishedCallback callback;
  105. };
  106. } // namespace datasrc_clientmgr_internal
  107. /// \brief Frontend to the manager object for data source clients.
  108. ///
  109. /// This class provides interfaces for configuring and updating a set of
  110. /// data source clients "in the background". The user of this class can
  111. /// assume any operation on this class can be done effectively non-blocking,
  112. /// not suspending any delay-sensitive operations such as DNS query
  113. /// processing. The only exception is the time when this class object
  114. /// is destroyed (normally as a result of an implicit call to the destructor);
  115. /// in the current implementation it can take time depending on what is
  116. /// running "in the background" at the time of the call.
  117. ///
  118. /// Internally, an object of this class invokes a separate thread to perform
  119. /// time consuming operations such as loading large zone data into memory,
  120. /// but such details are completely hidden from the user of this class.
  121. ///
  122. /// This class is templated only so that we can test the class without
  123. /// involving actual threads or mutex. Normal applications will only
  124. /// need one specific specialization that has a typedef of
  125. /// \c DataSrcClientsMgr.
  126. template <typename ThreadType, typename BuilderType, typename MutexType,
  127. typename CondVarType>
  128. class DataSrcClientsMgrBase : boost::noncopyable {
  129. private:
  130. typedef std::map<dns::RRClass,
  131. boost::shared_ptr<datasrc::ConfigurableClientList> >
  132. ClientListsMap;
  133. class FDGuard : boost::noncopyable {
  134. public:
  135. FDGuard(DataSrcClientsMgrBase *mgr) :
  136. mgr_(mgr)
  137. {}
  138. ~FDGuard() {
  139. if (mgr_->read_fd_ != -1) {
  140. close(mgr_->read_fd_);
  141. }
  142. if (mgr_->write_fd_ != -1) {
  143. close(mgr_->write_fd_);
  144. }
  145. }
  146. private:
  147. DataSrcClientsMgrBase* mgr_;
  148. };
  149. friend class FDGuard;
  150. public:
  151. /// \brief Thread-safe accessor to the data source client lists.
  152. ///
  153. /// This class provides a simple wrapper for searching the client lists
  154. /// stored in the DataSrcClientsMgr in a thread-safe manner.
  155. /// It ensures the result of \c getClientList() can be used without
  156. /// causing a race condition with other threads that can possibly use
  157. /// the same manager throughout the lifetime of the holder object.
  158. ///
  159. /// This also means the holder object is expected to have a short lifetime.
  160. /// The application shouldn't try to keep it unnecessarily long.
  161. /// It's normally expected to create the holder object on the stack
  162. /// of a small scope and automatically let it be destroyed at the end
  163. /// of the scope.
  164. class Holder {
  165. public:
  166. Holder(DataSrcClientsMgrBase& mgr) :
  167. mgr_(mgr), locker_(mgr_.map_mutex_)
  168. {}
  169. /// \brief Find a data source client list of a specified RR class.
  170. ///
  171. /// It returns a pointer to the list stored in the manager if found,
  172. /// otherwise it returns NULL. The manager keeps the ownership of
  173. /// the pointed object. Also, it's not safe to get access to the
  174. /// object beyond the scope of the holder object.
  175. ///
  176. /// \note Since the ownership isn't transferred the return value
  177. /// could be a bare pointer (and it's probably better in several
  178. /// points). Unfortunately, some unit tests currently don't work
  179. /// unless this method effectively shares the ownership with the
  180. /// tests. That's the only reason why we return a shared pointer
  181. /// for now. We should eventually fix it and change the return value
  182. /// type (see Trac ticket #2395). Other applications must not
  183. /// assume the ownership is actually shared.
  184. boost::shared_ptr<datasrc::ConfigurableClientList> findClientList(
  185. const dns::RRClass& rrclass)
  186. {
  187. const ClientListsMap::const_iterator
  188. it = mgr_.clients_map_->find(rrclass);
  189. if (it == mgr_.clients_map_->end()) {
  190. return (boost::shared_ptr<datasrc::ConfigurableClientList>());
  191. } else {
  192. return (it->second);
  193. }
  194. }
  195. /// \brief Return list of classes that are present.
  196. ///
  197. /// Get the list of classes for which there's a client list. It is
  198. /// returned in form of a vector, copied from the internals. As the
  199. /// number of classes in there is expected to be small, it is not
  200. /// a performance issue.
  201. ///
  202. /// \return The list of classes.
  203. /// \throw std::bad_alloc for problems allocating the result.
  204. std::vector<dns::RRClass> getClasses() const {
  205. std::vector<dns::RRClass> result;
  206. for (ClientListsMap::const_iterator it =
  207. mgr_.clients_map_->begin(); it != mgr_.clients_map_->end();
  208. ++it) {
  209. result.push_back(it->first);
  210. }
  211. return (result);
  212. }
  213. private:
  214. DataSrcClientsMgrBase& mgr_;
  215. typename MutexType::Locker locker_;
  216. };
  217. /// \brief Constructor.
  218. ///
  219. /// It internally invokes a separate thread and waits for further
  220. /// operations from the user application.
  221. ///
  222. /// This method is basically exception free except in case of really
  223. /// rare system-level errors. When that happens the only reasonable
  224. /// action that the application can take would be to terminate the program
  225. /// in practice.
  226. ///
  227. /// \throw std::bad_alloc internal memory allocation failure.
  228. /// \throw isc::Unexpected general unexpected system errors.
  229. DataSrcClientsMgrBase(asiolink::IOService& service) :
  230. clients_map_(new ClientListsMap),
  231. fd_guard_(new FDGuard(this)),
  232. read_fd_(-1), write_fd_(-1),
  233. builder_(&command_queue_, &callback_queue_, &cond_, &queue_mutex_,
  234. &clients_map_, &map_mutex_, createFds()),
  235. builder_thread_(boost::bind(&BuilderType::run, &builder_)),
  236. wakeup_socket_(service, read_fd_)
  237. {
  238. // Schedule wakeups when callbacks are pushed.
  239. wakeup_socket_.asyncRead(
  240. boost::bind(&DataSrcClientsMgrBase::processCallbacks, this, _1),
  241. buffer, 1);
  242. }
  243. /// \brief The destructor.
  244. ///
  245. /// It tells the internal thread to stop and waits for it completion.
  246. /// In the current implementation, it can block for some unpredictably
  247. /// long period depending on what the thread is doing at that time
  248. /// (in future we may want to implement a rapid way of killing the thread
  249. /// and/or provide a separate interface for waiting so that the application
  250. /// can choose the timing).
  251. ///
  252. /// The waiting operation can result in an exception, but this method
  253. /// catches any of them so this method itself is exception free.
  254. ~DataSrcClientsMgrBase() {
  255. // We share class member variables with the builder, which will be
  256. // invalidated after the call to the destructor, so we need to make
  257. // sure the builder thread is terminated. Depending on the timing
  258. // this could take a long time; if we don't want that to happen in
  259. // this context, we may want to introduce a separate 'shutdown()'
  260. // method.
  261. // Also, since we don't want to propagate exceptions from a destructor,
  262. // we catch any possible ones. In fact the only really expected one
  263. // is Thread::UncaughtException when the builder thread died due to
  264. // an exception. We specifically log it and just ignore others.
  265. try {
  266. sendCommand(datasrc_clientmgr_internal::SHUTDOWN,
  267. data::ConstElementPtr());
  268. builder_thread_.wait();
  269. } catch (const util::thread::Thread::UncaughtException& ex) {
  270. // technically, logging this could throw, which will be propagated.
  271. // But such an exception would be a fatal one anyway, so we
  272. // simply let it go through.
  273. LOG_ERROR(auth_logger, AUTH_DATASRC_CLIENTS_SHUTDOWN_ERROR).
  274. arg(ex.what());
  275. } catch (...) {
  276. LOG_ERROR(auth_logger,
  277. AUTH_DATASRC_CLIENTS_SHUTDOWN_UNEXPECTED_ERROR);
  278. }
  279. processCallbacks(); // Any leftover callbacks
  280. cleanup(); // see below
  281. }
  282. /// \brief Handle new full configuration for data source clients.
  283. ///
  284. /// This method simply passes the new configuration to the builder
  285. /// and immediately returns. This method is basically exception free
  286. /// as long as the caller passes a non NULL value for \c config_arg;
  287. /// it doesn't validate the argument further.
  288. ///
  289. /// \brief isc::InvalidParameter config_arg is NULL.
  290. /// \brief std::bad_alloc
  291. ///
  292. /// \param config_arg The new data source configuration. Must not be NULL.
  293. /// \param callback Called once the reconfigure command completes. It is
  294. /// called in the main thread (not in the work one). It should be
  295. /// exceptionless.
  296. void reconfigure(const data::ConstElementPtr& config_arg,
  297. const datasrc_clientmgr_internal::FinishedCallback&
  298. callback = datasrc_clientmgr_internal::FinishedCallback())
  299. {
  300. if (!config_arg) {
  301. isc_throw(InvalidParameter, "Invalid null config argument");
  302. }
  303. sendCommand(datasrc_clientmgr_internal::RECONFIGURE, config_arg,
  304. callback);
  305. reconfigureHook(); // for test's customization
  306. }
  307. /// \brief Set the underlying data source client lists to new lists.
  308. ///
  309. /// This is provided only for some existing tests until we support a
  310. /// cleaner way to use faked data source clients. Non test code or
  311. /// newer tests must not use this.
  312. void setDataSrcClientLists(datasrc::ClientListMapPtr new_lists) {
  313. typename MutexType::Locker locker(map_mutex_);
  314. clients_map_ = new_lists;
  315. }
  316. /// \brief Instruct internal thread to (re)load a zone
  317. ///
  318. /// \param args Element argument that should be a map of the form
  319. /// { "class": "IN", "origin": "example.com" }
  320. /// (but class is optional and will default to IN)
  321. /// \param callback Called once the loadZone command completes. It
  322. /// is called in the main thread, not in the work thread. It should
  323. /// be exceptionless.
  324. ///
  325. /// \exception CommandError if the args value is null, or not in
  326. /// the expected format, or contains
  327. /// a bad origin or class string
  328. void
  329. loadZone(const data::ConstElementPtr& args,
  330. const datasrc_clientmgr_internal::FinishedCallback& callback =
  331. datasrc_clientmgr_internal::FinishedCallback())
  332. {
  333. if (!args) {
  334. isc_throw(CommandError, "loadZone argument empty");
  335. }
  336. if (args->getType() != isc::data::Element::map) {
  337. isc_throw(CommandError, "loadZone argument not a map");
  338. }
  339. if (!args->contains("origin")) {
  340. isc_throw(CommandError,
  341. "loadZone argument has no 'origin' value");
  342. }
  343. // Also check if it really is a valid name
  344. try {
  345. dns::Name(args->get("origin")->stringValue());
  346. } catch (const isc::Exception& exc) {
  347. isc_throw(CommandError, "bad origin: " << exc.what());
  348. }
  349. if (args->get("origin")->getType() != data::Element::string) {
  350. isc_throw(CommandError,
  351. "loadZone argument 'origin' value not a string");
  352. }
  353. if (args->contains("class")) {
  354. if (args->get("class")->getType() != data::Element::string) {
  355. isc_throw(CommandError,
  356. "loadZone argument 'class' value not a string");
  357. }
  358. // Also check if it is a valid class
  359. try {
  360. dns::RRClass(args->get("class")->stringValue());
  361. } catch (const isc::Exception& exc) {
  362. isc_throw(CommandError, "bad class: " << exc.what());
  363. }
  364. }
  365. // Note: we could do some more advanced checks here,
  366. // e.g. check if the zone is known at all in the configuration.
  367. // For now these are skipped, but one obvious way to
  368. // implement it would be to factor out the code from
  369. // the start of doLoadZone(), and call it here too
  370. sendCommand(datasrc_clientmgr_internal::LOADZONE, args, callback);
  371. }
  372. void segmentInfoUpdate(const data::ConstElementPtr& args,
  373. const datasrc_clientmgr_internal::FinishedCallback&
  374. callback =
  375. datasrc_clientmgr_internal::FinishedCallback()) {
  376. // Some minimal validation
  377. if (!args) {
  378. isc_throw(CommandError, "segmentInfoUpdate argument empty");
  379. }
  380. if (args->getType() != isc::data::Element::map) {
  381. isc_throw(CommandError, "segmentInfoUpdate argument not a map");
  382. }
  383. const char* params[] = {
  384. "data-source-name",
  385. "data-source-class",
  386. "segment-params",
  387. NULL
  388. };
  389. for (const char** param = params; *param; ++param) {
  390. if (!args->contains(*param)) {
  391. isc_throw(CommandError,
  392. "segmentInfoUpdate argument has no '" << param <<
  393. "' value");
  394. }
  395. }
  396. sendCommand(datasrc_clientmgr_internal::SEGMENT_INFO_UPDATE, args,
  397. callback);
  398. }
  399. private:
  400. // This is expected to be called at the end of the destructor. It
  401. // actually does nothing, but provides a customization point for
  402. // specialized class for tests so that the tests can inspect the last
  403. // state of the class.
  404. void cleanup() {}
  405. // same as cleanup(), for reconfigure().
  406. void reconfigureHook() {}
  407. void sendCommand(datasrc_clientmgr_internal::CommandID command,
  408. const data::ConstElementPtr& arg,
  409. const datasrc_clientmgr_internal::FinishedCallback&
  410. callback = datasrc_clientmgr_internal::FinishedCallback())
  411. {
  412. // The lock will be held until the end of this method. Only
  413. // push_back has to be protected, but we can avoid having an extra
  414. // block this way.
  415. typename MutexType::Locker locker(queue_mutex_);
  416. command_queue_.push_back(
  417. datasrc_clientmgr_internal::Command(command, arg, callback));
  418. cond_.signal();
  419. }
  420. int createFds() {
  421. int fds[2];
  422. int result = socketpair(AF_LOCAL, SOCK_STREAM, 0, fds);
  423. if (result != 0) {
  424. isc_throw(Unexpected, "Can't create socket pair: " <<
  425. strerror(errno));
  426. }
  427. read_fd_ = fds[0];
  428. write_fd_ = fds[1];
  429. return write_fd_;
  430. }
  431. void processCallbacks(const std::string& error = std::string()) {
  432. // Schedule the next read.
  433. wakeup_socket_.asyncRead(
  434. boost::bind(&DataSrcClientsMgrBase::processCallbacks, this, _1),
  435. buffer, 1);
  436. if (!error.empty()) {
  437. // Generally, there should be no errors (as we are the other end
  438. // as well), but check just in case.
  439. isc_throw(Unexpected, error);
  440. }
  441. // Steal the callbacks into local copy.
  442. std::list<datasrc_clientmgr_internal::FinishedCallback> queue;
  443. {
  444. typename MutexType::Locker locker(queue_mutex_);
  445. queue.swap(callback_queue_);
  446. }
  447. // Execute the callbacks
  448. BOOST_FOREACH(const datasrc_clientmgr_internal::FinishedCallback&
  449. callback, queue) {
  450. callback();
  451. }
  452. }
  453. //
  454. // The following are shared with the builder.
  455. //
  456. // The list is used as a one-way queue: back-in, front-out
  457. std::list<datasrc_clientmgr_internal::Command> command_queue_;
  458. // Similar to above, for the callbacks that are ready to be called.
  459. // While the command queue is for sending commands from the main thread
  460. // to the work thread, this one is for the other direction. Protected
  461. // by the same mutex (queue_mutex_).
  462. std::list<datasrc_clientmgr_internal::FinishedCallback> callback_queue_;
  463. CondVarType cond_; // condition variable for queue operations
  464. MutexType queue_mutex_; // mutex to protect the queue
  465. datasrc::ClientListMapPtr clients_map_;
  466. // map of actual data source client objects
  467. boost::scoped_ptr<FDGuard> fd_guard_; // A guard to close the fds.
  468. int read_fd_, write_fd_; // Descriptors for wakeup
  469. MutexType map_mutex_; // mutex to protect the clients map
  470. BuilderType builder_;
  471. ThreadType builder_thread_; // for safety this should be placed last
  472. isc::asiolink::LocalSocket wakeup_socket_; // For integration of read_fd_
  473. // to the asio loop
  474. char buffer[1]; // Buffer for the wakeup socket.
  475. };
  476. namespace datasrc_clientmgr_internal {
  477. /// \brief A class that maintains a set of data source clients.
  478. ///
  479. /// An object of this class is supposed to run on a dedicated thread, whose
  480. /// main function is a call to its \c run() method. It runs in a loop
  481. /// waiting for commands from the manager and handles each command (including
  482. /// reloading a new version of zone data into memory or fully reconfiguration
  483. /// of specific set of data source clients). When it receives a SHUTDOWN
  484. /// command, it exits from the loop, which will terminate the thread.
  485. ///
  486. /// While this class is defined in a publicly visible namespace, it's
  487. /// essentially private to \c DataSrcClientsMgr. Except for tests,
  488. /// applications should not directly access this class.
  489. ///
  490. /// This class is templated so that we can test it without involving actual
  491. /// threads or locks.
  492. template <typename MutexType, typename CondVarType>
  493. class DataSrcClientsBuilderBase : boost::noncopyable {
  494. private:
  495. typedef std::map<dns::RRClass,
  496. boost::shared_ptr<datasrc::ConfigurableClientList> >
  497. ClientListsMap;
  498. public:
  499. /// \brief Internal errors in handling commands.
  500. ///
  501. /// This exception is expected to be caught within the
  502. /// \c DataSrcClientsBuilder implementation, but is defined as public
  503. /// so tests can be checked it.
  504. class InternalCommandError : public isc::Exception {
  505. public:
  506. InternalCommandError(const char* file, size_t line, const char* what) :
  507. isc::Exception(file, line, what) {}
  508. };
  509. /// \brief Constructor.
  510. ///
  511. /// It simply sets up a local copy of shared data with the manager.
  512. ///
  513. /// \throw None
  514. DataSrcClientsBuilderBase(std::list<Command>* command_queue,
  515. std::list<FinishedCallback>* callback_queue,
  516. CondVarType* cond, MutexType* queue_mutex,
  517. datasrc::ClientListMapPtr* clients_map,
  518. MutexType* map_mutex,
  519. int wake_fd
  520. ) :
  521. command_queue_(command_queue), callback_queue_(callback_queue),
  522. cond_(cond), queue_mutex_(queue_mutex),
  523. clients_map_(clients_map), map_mutex_(map_mutex), wake_fd_(wake_fd)
  524. {}
  525. /// \brief The main loop.
  526. void run();
  527. /// \brief Handle one command from the manager.
  528. ///
  529. /// This is a dedicated subroutine of run() and is essentially private,
  530. /// but is defined as a separate public method so we can test each
  531. /// command test individually. In any case, this class itself is
  532. /// generally considered private.
  533. ///
  534. /// \return true if the builder should keep running; false otherwise.
  535. bool handleCommand(const Command& command);
  536. private:
  537. // NOOP command handler. We use this so tests can override it; the default
  538. // implementation really does nothing.
  539. void doNoop() {}
  540. void doReconfigure(const data::ConstElementPtr& config) {
  541. if (config) {
  542. LOG_INFO(auth_logger,
  543. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_STARTED);
  544. try {
  545. // Define new_clients_map outside of the block that
  546. // has the lock scope; this way, after the swap,
  547. // the lock is guaranteed to be released before
  548. // the old data is destroyed, minimizing the lock
  549. // duration.
  550. datasrc::ClientListMapPtr new_clients_map =
  551. configureDataSource(config);
  552. {
  553. typename MutexType::Locker locker(*map_mutex_);
  554. new_clients_map.swap(*clients_map_);
  555. } // lock is released by leaving scope
  556. LOG_INFO(auth_logger,
  557. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_SUCCESS);
  558. } catch (const datasrc::ConfigurableClientList::ConfigurationError&
  559. config_error) {
  560. LOG_ERROR(auth_logger,
  561. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_CONFIG_ERROR).
  562. arg(config_error.what());
  563. } catch (const datasrc::DataSourceError& ds_error) {
  564. LOG_ERROR(auth_logger,
  565. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_DATASRC_ERROR).
  566. arg(ds_error.what());
  567. } catch (const isc::Exception& isc_error) {
  568. LOG_ERROR(auth_logger,
  569. AUTH_DATASRC_CLIENTS_BUILDER_RECONFIGURE_ERROR).
  570. arg(isc_error.what());
  571. }
  572. // other exceptions are propagated, see
  573. // http://bind10.isc.org/ticket/2210#comment:13
  574. // old clients_map_ data is released by leaving scope
  575. }
  576. }
  577. void doSegmentUpdate(const isc::data::ConstElementPtr& arg) {
  578. // TODO: Error handling. Invalid RRClass, non-existing stuff, exceptions
  579. const isc::dns::RRClass
  580. rrclass(arg->get("data-source-class")->stringValue());
  581. const std::string& name(arg->get("data-source-name")->stringValue());
  582. const isc::data::ConstElementPtr& segment_params =
  583. arg->get("segment-params");
  584. typename MutexType::Locker locker(*map_mutex_);
  585. const boost::shared_ptr<isc::datasrc::ConfigurableClientList>& list =
  586. (**clients_map_)[rrclass];
  587. if (!list) {
  588. // TODO: Log error
  589. return;
  590. }
  591. list->resetMemorySegment(name,
  592. isc::datasrc::memory::ZoneTableSegment::READ_ONLY, segment_params);
  593. }
  594. void doLoadZone(const isc::data::ConstElementPtr& arg);
  595. boost::shared_ptr<datasrc::memory::ZoneWriter> getZoneWriter(
  596. datasrc::ConfigurableClientList& client_list,
  597. const dns::RRClass& rrclass, const dns::Name& origin);
  598. // The following are shared with the manager
  599. std::list<Command>* command_queue_;
  600. std::list<FinishedCallback> *callback_queue_;
  601. CondVarType* cond_;
  602. MutexType* queue_mutex_;
  603. datasrc::ClientListMapPtr* clients_map_;
  604. MutexType* map_mutex_;
  605. int wake_fd_;
  606. };
  607. // Shortcut typedef for normal use
  608. typedef DataSrcClientsBuilderBase<util::thread::Mutex, util::thread::CondVar>
  609. DataSrcClientsBuilder;
  610. template <typename MutexType, typename CondVarType>
  611. void
  612. DataSrcClientsBuilderBase<MutexType, CondVarType>::run() {
  613. LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STARTED);
  614. try {
  615. bool keep_running = true;
  616. while (keep_running) {
  617. std::list<Command> current_commands;
  618. {
  619. // Move all new commands to local queue under the protection of
  620. // queue_mutex_.
  621. typename MutexType::Locker locker(*queue_mutex_);
  622. while (command_queue_->empty()) {
  623. cond_->wait(*queue_mutex_);
  624. }
  625. current_commands.swap(*command_queue_);
  626. } // the lock is released here.
  627. while (keep_running && !current_commands.empty()) {
  628. try {
  629. keep_running = handleCommand(current_commands.front());;
  630. } catch (const InternalCommandError& e) {
  631. LOG_ERROR(auth_logger,
  632. AUTH_DATASRC_CLIENTS_BUILDER_COMMAND_ERROR).
  633. arg(e.what());
  634. }
  635. if (current_commands.front().callback) {
  636. // Lock the queue
  637. typename MutexType::Locker locker(*queue_mutex_);
  638. callback_queue_->
  639. push_back(current_commands.front().callback);
  640. // Wake up the other end. If it would block, there are data
  641. // and it'll wake anyway.
  642. int result = send(wake_fd_, "w", 1, MSG_DONTWAIT);
  643. if (result == -1 &&
  644. (errno != EWOULDBLOCK && errno != EAGAIN)) {
  645. // Note: the strerror might not be thread safe, as
  646. // subsequent call to it might change the returned
  647. // string. But that is unlikely and strerror_r is
  648. // not portable and we are going to terminate anyway,
  649. // so that's better than nothing.
  650. //
  651. // Also, this error handler is not tested. It should
  652. // be generally impossible to happen, so it is hard
  653. // to trigger in controlled way.
  654. LOG_FATAL(auth_logger,
  655. AUTH_DATASRC_CLIENTS_BUILDER_WAKE_ERR).
  656. arg(strerror(errno));
  657. std::terminate();
  658. }
  659. }
  660. current_commands.pop_front();
  661. }
  662. }
  663. LOG_INFO(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_STOPPED);
  664. } catch (const std::exception& ex) {
  665. // We explicitly catch exceptions so we can log it as soon as possible.
  666. LOG_FATAL(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED).
  667. arg(ex.what());
  668. std::terminate();
  669. } catch (...) {
  670. LOG_FATAL(auth_logger, AUTH_DATASRC_CLIENTS_BUILDER_FAILED_UNEXPECTED);
  671. std::terminate();
  672. }
  673. }
  674. template <typename MutexType, typename CondVarType>
  675. bool
  676. DataSrcClientsBuilderBase<MutexType, CondVarType>::handleCommand(
  677. const Command& command)
  678. {
  679. const CommandID cid = command.id;
  680. if (cid >= NUM_COMMANDS) {
  681. // This shouldn't happen except for a bug within this file.
  682. isc_throw(Unexpected, "internal bug: invalid command, ID: " << cid);
  683. }
  684. const boost::array<const char*, NUM_COMMANDS> command_desc = {
  685. {"NOOP", "RECONFIGURE", "LOADZONE", "SEGMENT_INFO_UPDATE", "SHUTDOWN"}
  686. };
  687. LOG_DEBUG(auth_logger, DBGLVL_TRACE_BASIC,
  688. AUTH_DATASRC_CLIENTS_BUILDER_COMMAND).arg(command_desc.at(cid));
  689. switch (command.id) {
  690. case RECONFIGURE:
  691. doReconfigure(command.params);
  692. break;
  693. case LOADZONE:
  694. doLoadZone(command.params);
  695. break;
  696. case SEGMENT_INFO_UPDATE:
  697. doSegmentUpdate(command.params);
  698. break;
  699. case SHUTDOWN:
  700. return (false);
  701. case NOOP:
  702. doNoop();
  703. break;
  704. case NUM_COMMANDS:
  705. assert(false); // we rejected this case above
  706. }
  707. return (true);
  708. }
  709. template <typename MutexType, typename CondVarType>
  710. void
  711. DataSrcClientsBuilderBase<MutexType, CondVarType>::doLoadZone(
  712. const isc::data::ConstElementPtr& arg)
  713. {
  714. // We assume some basic level validation as this method can only be
  715. // called via the manager in practice. manager is expected to do the
  716. // minimal validation.
  717. assert(arg);
  718. assert(arg->get("origin"));
  719. // TODO: currently, we hardcode IN as the default for the optional
  720. // 'class' argument. We should really derive this from the specification,
  721. // but at the moment the config/command API does not allow that to be
  722. // done easily. Once that is in place (tickets have yet to be created,
  723. // as we need to do a tiny bit of design work for that), this
  724. // code can be replaced with the original part:
  725. // assert(arg->get("class"));
  726. // const dns::RRClass(arg->get("class")->stringValue());
  727. isc::data::ConstElementPtr class_elem = arg->get("class");
  728. const dns::RRClass rrclass(class_elem ?
  729. dns::RRClass(class_elem->stringValue()) :
  730. dns::RRClass::IN());
  731. const dns::Name origin(arg->get("origin")->stringValue());
  732. ClientListsMap::iterator found = (*clients_map_)->find(rrclass);
  733. if (found == (*clients_map_)->end()) {
  734. isc_throw(InternalCommandError, "failed to load a zone " << origin <<
  735. "/" << rrclass << ": not configured for the class");
  736. }
  737. boost::shared_ptr<datasrc::ConfigurableClientList> client_list =
  738. found->second;
  739. assert(client_list);
  740. try {
  741. boost::shared_ptr<datasrc::memory::ZoneWriter> zwriter =
  742. getZoneWriter(*client_list, rrclass, origin);
  743. if (!zwriter) {
  744. return;
  745. }
  746. zwriter->load(); // this can take time but doesn't cause a race
  747. { // install() can cause a race and must be in a critical section
  748. typename MutexType::Locker locker(*map_mutex_);
  749. zwriter->install();
  750. }
  751. LOG_DEBUG(auth_logger, DBG_AUTH_OPS,
  752. AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE)
  753. .arg(origin).arg(rrclass);
  754. // same as load(). We could let the destructor do it, but do it
  755. // ourselves explicitly just in case.
  756. zwriter->cleanup();
  757. } catch (const InternalCommandError& ex) {
  758. throw; // this comes from getZoneWriter. just let it go through.
  759. } catch (const isc::Exception& ex) {
  760. // We catch our internal exceptions (which will be just ignored) and
  761. // propagated others (which should generally be considered fatal and
  762. // will make the thread terminate)
  763. isc_throw(InternalCommandError, "failed to load a zone " << origin <<
  764. "/" << rrclass << ": error occurred in reload: " <<
  765. ex.what());
  766. }
  767. }
  768. // A dedicated subroutine of doLoadZone(). Separated just for keeping the
  769. // main method concise.
  770. template <typename MutexType, typename CondVarType>
  771. boost::shared_ptr<datasrc::memory::ZoneWriter>
  772. DataSrcClientsBuilderBase<MutexType, CondVarType>::getZoneWriter(
  773. datasrc::ConfigurableClientList& client_list,
  774. const dns::RRClass& rrclass, const dns::Name& origin)
  775. {
  776. // getCachedZoneWriter() could get access to an underlying data source
  777. // that can cause a race condition with the main thread using that data
  778. // source for lookup. So we need to protect the access here.
  779. datasrc::ConfigurableClientList::ZoneWriterPair writerpair;
  780. {
  781. typename MutexType::Locker locker(*map_mutex_);
  782. writerpair = client_list.getCachedZoneWriter(origin, false);
  783. }
  784. switch (writerpair.first) {
  785. case datasrc::ConfigurableClientList::ZONE_SUCCESS:
  786. assert(writerpair.second);
  787. return (writerpair.second);
  788. case datasrc::ConfigurableClientList::ZONE_NOT_FOUND:
  789. isc_throw(InternalCommandError, "failed to load zone " << origin
  790. << "/" << rrclass << ": not found in any configured "
  791. "data source.");
  792. case datasrc::ConfigurableClientList::ZONE_NOT_CACHED:
  793. LOG_DEBUG(auth_logger, DBG_AUTH_OPS,
  794. AUTH_DATASRC_CLIENTS_BUILDER_LOAD_ZONE_NOCACHE)
  795. .arg(origin).arg(rrclass);
  796. break; // return NULL below
  797. case datasrc::ConfigurableClientList::CACHE_NOT_WRITABLE:
  798. // This is an internal error. Auth server should skip reloading zones
  799. // on non writable caches.
  800. isc_throw(InternalCommandError, "failed to load zone " << origin
  801. << "/" << rrclass << ": internal failure, in-memory cache "
  802. "is not writable");
  803. case datasrc::ConfigurableClientList::CACHE_DISABLED:
  804. // This is an internal error. Auth server must have the cache
  805. // enabled.
  806. isc_throw(InternalCommandError, "failed to load zone " << origin
  807. << "/" << rrclass << ": internal failure, in-memory cache "
  808. "is somehow disabled");
  809. default: // other cases can really never happen
  810. isc_throw(Unexpected, "Impossible result in getting data source "
  811. "ZoneWriter: " << writerpair.first);
  812. }
  813. return (boost::shared_ptr<datasrc::memory::ZoneWriter>());
  814. }
  815. } // namespace datasrc_clientmgr_internal
  816. /// \brief Shortcut type for normal data source clients manager.
  817. ///
  818. /// In fact, for non test applications this is the only type of this kind
  819. /// to be considered.
  820. typedef DataSrcClientsMgrBase<
  821. util::thread::Thread,
  822. datasrc_clientmgr_internal::DataSrcClientsBuilder,
  823. util::thread::Mutex, util::thread::CondVar> DataSrcClientsMgr;
  824. } // namespace auth
  825. } // namespace isc
  826. #endif // DATASRC_CLIENTS_MGR_H
  827. // Local Variables:
  828. // mode: c++
  829. // End: