callout_manager.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. // Copyright (C) 2013,2015 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 <hooks/callout_handle.h>
  15. #include <hooks/callout_manager.h>
  16. #include <hooks/hooks_log.h>
  17. #include <hooks/pointer_converter.h>
  18. #include <util/stopwatch.h>
  19. #include <boost/static_assert.hpp>
  20. #include <algorithm>
  21. #include <climits>
  22. #include <functional>
  23. #include <utility>
  24. using namespace std;
  25. namespace isc {
  26. namespace hooks {
  27. // Constructor
  28. CalloutManager::CalloutManager(int num_libraries)
  29. : server_hooks_(ServerHooks::getServerHooks()),
  30. current_hook_(-1), current_library_(-1),
  31. hook_vector_(ServerHooks::getServerHooks().getCount()),
  32. library_handle_(this), pre_library_handle_(this, 0),
  33. post_library_handle_(this, INT_MAX), num_libraries_(num_libraries)
  34. {
  35. if (num_libraries < 0) {
  36. isc_throw(isc::BadValue, "number of libraries passed to the "
  37. "CalloutManager must be >= 0");
  38. }
  39. }
  40. // Check that the index of a library is valid. It can range from 1 - n
  41. // (n is the number of libraries), 0 (pre-user library callouts), or INT_MAX
  42. // (post-user library callouts). It can also be -1 to indicate an invalid
  43. // value.
  44. void
  45. CalloutManager::checkLibraryIndex(int library_index) const {
  46. if (((library_index >= -1) && (library_index <= num_libraries_)) ||
  47. (library_index == INT_MAX)) {
  48. return;
  49. }
  50. isc_throw(NoSuchLibrary, "library index " << library_index <<
  51. " is not valid for the number of loaded libraries (" <<
  52. num_libraries_ << ")");
  53. }
  54. // Register a callout for the current library.
  55. void
  56. CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
  57. // Note the registration.
  58. LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUT_REGISTRATION)
  59. .arg(current_library_).arg(name);
  60. // Sanity check that the current library index is set to a valid value.
  61. checkLibraryIndex(current_library_);
  62. // Get the index associated with this hook (validating the name in the
  63. // process).
  64. int hook_index = server_hooks_.getIndex(name);
  65. // Iterate through the callout vector for the hook from start to end,
  66. // looking for the first entry where the library index is greater than
  67. // the present index.
  68. for (CalloutVector::iterator i = hook_vector_[hook_index].begin();
  69. i != hook_vector_[hook_index].end(); ++i) {
  70. if (i->first > current_library_) {
  71. // Found an element whose library index number is greater than the
  72. // current index, so insert the new element ahead of this one.
  73. hook_vector_[hook_index].insert(i, make_pair(current_library_,
  74. callout));
  75. return;
  76. }
  77. }
  78. // Reached the end of the vector, so there is no element in the (possibly
  79. // empty) set of callouts with a library index greater than the current
  80. // library index. Inset the callout at the end of the list.
  81. hook_vector_[hook_index].push_back(make_pair(current_library_, callout));
  82. }
  83. // Check if callouts are present for a given hook index.
  84. bool
  85. CalloutManager::calloutsPresent(int hook_index) const {
  86. // Validate the hook index.
  87. if ((hook_index < 0) || (hook_index >= hook_vector_.size())) {
  88. isc_throw(NoSuchHook, "hook index " << hook_index <<
  89. " is not valid for the list of registered hooks");
  90. }
  91. // Valid, so are there any callouts associated with that hook?
  92. return (!hook_vector_[hook_index].empty());
  93. }
  94. // Call all the callouts for a given hook.
  95. void
  96. CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
  97. // Clear the "skip" flag so we don't carry state from a previous call.
  98. // This is done regardless of whether callouts are present to avoid passing
  99. // any state from the previous call of callCallouts().
  100. callout_handle.setSkip(false);
  101. // Only initialize and iterate if there are callouts present. This check
  102. // also catches the case of an invalid index.
  103. if (calloutsPresent(hook_index)) {
  104. // Set the current hook index. This is used should a callout wish to
  105. // determine to what hook it is attached.
  106. current_hook_ = hook_index;
  107. // Duplicate the callout vector for this hook and work through that.
  108. // This step is needed because we allow dynamic registration and
  109. // deregistration of callouts. If a callout attached to a hook modified
  110. // the list of callouts on that hook, the underlying CalloutVector would
  111. // change and potentially affect the iteration through that vector.
  112. CalloutVector callouts(hook_vector_[hook_index]);
  113. // This object will be used to measure execution time of each callout
  114. // and the total time spent in callouts for this hook point.
  115. util::Stopwatch stopwatch;
  116. // Mark that the callouts begin for the hook.
  117. LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUTS_BEGIN)
  118. .arg(server_hooks_.getName(current_hook_));
  119. // Call all the callouts.
  120. for (CalloutVector::const_iterator i = callouts.begin();
  121. i != callouts.end(); ++i) {
  122. // In case the callout tries to register or deregister a callout,
  123. // set the current library index to the index associated with the
  124. // library that registered the callout being called.
  125. current_library_ = i->first;
  126. // Call the callout
  127. try {
  128. stopwatch.start();
  129. int status = (*i->second)(callout_handle);
  130. stopwatch.stop();
  131. if (status == 0) {
  132. LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
  133. HOOKS_CALLOUT_CALLED).arg(current_library_)
  134. .arg(server_hooks_.getName(current_hook_))
  135. .arg(PointerConverter(i->second).dlsymPtr())
  136. .arg(stopwatch.logFormatLastDuration());
  137. } else {
  138. LOG_ERROR(hooks_logger, HOOKS_CALLOUT_ERROR)
  139. .arg(current_library_)
  140. .arg(server_hooks_.getName(current_hook_))
  141. .arg(PointerConverter(i->second).dlsymPtr())
  142. .arg(stopwatch.logFormatLastDuration());
  143. }
  144. } catch (const std::exception& e) {
  145. // Any exception, not just ones based on isc::Exception
  146. LOG_ERROR(hooks_logger, HOOKS_CALLOUT_EXCEPTION)
  147. .arg(current_library_)
  148. .arg(server_hooks_.getName(current_hook_))
  149. .arg(PointerConverter(i->second).dlsymPtr())
  150. .arg(e.what())
  151. .arg(stopwatch.logFormatLastDuration());
  152. }
  153. }
  154. // Mark end of callout execution. Include the total execution
  155. // time for callouts.
  156. LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUTS_COMPLETE)
  157. .arg(server_hooks_.getName(current_hook_))
  158. .arg(stopwatch.logFormatTotalDuration());
  159. // Reset the current hook and library indexs to an invalid value to
  160. // catch any programming errors.
  161. current_hook_ = -1;
  162. current_library_ = -1;
  163. }
  164. }
  165. // Deregister a callout registered by the current library on a particular hook.
  166. bool
  167. CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
  168. // Sanity check that the current library index is set to a valid value.
  169. checkLibraryIndex(current_library_);
  170. // Get the index associated with this hook (validating the name in the
  171. // process).
  172. int hook_index = server_hooks_.getIndex(name);
  173. /// Construct a CalloutEntry matching the current library and the callout
  174. /// we want to remove.
  175. CalloutEntry target(current_library_, callout);
  176. /// To decide if any entries were removed, we'll record the initial size
  177. /// of the callout vector for the hook, and compare it with the size after
  178. /// the removal.
  179. size_t initial_size = hook_vector_[hook_index].size();
  180. // The next bit is standard STL (see "Item 33" in "Effective STL" by
  181. // Scott Meyers).
  182. //
  183. // remove_if reorders the hook vector so that all items not matching
  184. // the predicate are at the start of the vector and returns a pointer
  185. // to the next element. (In this case, the predicate is that the item
  186. // is equal to the value of the passed callout.) The erase() call
  187. // removes everything from that element to the end of the vector, i.e.
  188. // all the matching elements.
  189. hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
  190. hook_vector_[hook_index].end(),
  191. bind1st(equal_to<CalloutEntry>(),
  192. target)),
  193. hook_vector_[hook_index].end());
  194. // Return an indication of whether anything was removed.
  195. bool removed = initial_size != hook_vector_[hook_index].size();
  196. if (removed) {
  197. LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
  198. HOOKS_CALLOUT_DEREGISTERED).arg(current_library_).arg(name);
  199. }
  200. return (removed);
  201. }
  202. // Deregister all callouts on a given hook.
  203. bool
  204. CalloutManager::deregisterAllCallouts(const std::string& name) {
  205. // Get the index associated with this hook (validating the name in the
  206. // process).
  207. int hook_index = server_hooks_.getIndex(name);
  208. /// Construct a CalloutEntry matching the current library (the callout
  209. /// pointer is NULL as we are not checking that).
  210. CalloutEntry target(current_library_, NULL);
  211. /// To decide if any entries were removed, we'll record the initial size
  212. /// of the callout vector for the hook, and compare it with the size after
  213. /// the removal.
  214. size_t initial_size = hook_vector_[hook_index].size();
  215. // Remove all callouts matching this library.
  216. hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
  217. hook_vector_[hook_index].end(),
  218. bind1st(CalloutLibraryEqual(),
  219. target)),
  220. hook_vector_[hook_index].end());
  221. // Return an indication of whether anything was removed.
  222. bool removed = initial_size != hook_vector_[hook_index].size();
  223. if (removed) {
  224. LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
  225. HOOKS_ALL_CALLOUTS_DEREGISTERED).arg(current_library_)
  226. .arg(name);
  227. }
  228. return (removed);
  229. }
  230. } // namespace util
  231. } // namespace isc