library_manager.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // Copyright (C) 2013 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/hooks.h>
  15. #include <hooks/hooks_log.h>
  16. #include <hooks/callout_manager.h>
  17. #include <hooks/library_handle.h>
  18. #include <hooks/library_manager.h>
  19. #include <hooks/pointer_converter.h>
  20. #include <hooks/server_hooks.h>
  21. #include <string>
  22. #include <vector>
  23. #include <dlfcn.h>
  24. using namespace std;
  25. namespace isc {
  26. namespace hooks {
  27. // Constructor (used by external agency)
  28. LibraryManager::LibraryManager(const std::string& name, int index,
  29. const boost::shared_ptr<CalloutManager>& manager)
  30. : dl_handle_(NULL), index_(index), manager_(manager),
  31. library_name_(name)
  32. {
  33. if (!manager) {
  34. isc_throw(NoCalloutManager, "must specify a CalloutManager when "
  35. "instantiating a LibraryManager object");
  36. }
  37. }
  38. // Constructor (used by "validate" for library validation). Note that this
  39. // sets "manager_" to not point to anything, which means that methods such as
  40. // registerStandardCallout() will fail, probably with a segmentation fault.
  41. // There are no checks for this condition in those methods: this constructor
  42. // is declared "private", so can only be executed by a method in this class.
  43. // The only method to do so is "validateLibrary", which takes care not to call
  44. // methods requiring a non-NULL manager.
  45. LibraryManager::LibraryManager(const std::string& name)
  46. : dl_handle_(NULL), index_(-1), manager_(), library_name_(name)
  47. {}
  48. // Destructor.
  49. LibraryManager::~LibraryManager() {
  50. if (manager_) {
  51. // LibraryManager instantiated to load a library, so ensure that
  52. // it is unloaded before exiting.
  53. static_cast<void>(unloadLibrary());
  54. } else {
  55. // LibraryManager instantiated to validate a library, so just ensure
  56. // that it is closed before exiting.
  57. static_cast<void>(closeLibrary());
  58. }
  59. }
  60. // Open the library
  61. bool
  62. LibraryManager::openLibrary() {
  63. // Open the library. We'll resolve names now, so that if there are any
  64. // issues we don't bugcheck in the middle of apparently unrelated code.
  65. dl_handle_ = dlopen(library_name_.c_str(), RTLD_NOW | RTLD_LOCAL);
  66. if (dl_handle_ == NULL) {
  67. LOG_ERROR(hooks_logger, HOOKS_OPEN_ERROR).arg(library_name_)
  68. .arg(dlerror());
  69. }
  70. return (dl_handle_ != NULL);
  71. }
  72. // Close the library if not already open
  73. bool
  74. LibraryManager::closeLibrary() {
  75. // Close the library if it is open. (If not, this is a no-op.)
  76. int status = 0;
  77. if (dl_handle_ != NULL) {
  78. status = dlclose(dl_handle_);
  79. dl_handle_ = NULL;
  80. if (status != 0) {
  81. LOG_ERROR(hooks_logger, HOOKS_CLOSE_ERROR).arg(library_name_)
  82. .arg(dlerror());
  83. }
  84. }
  85. return (status == 0);
  86. }
  87. // Check the version of the library
  88. bool
  89. LibraryManager::checkVersion() const {
  90. // Get the pointer to the "version" function.
  91. PointerConverter pc(dlsym(dl_handle_, VERSION_FUNCTION_NAME));
  92. if (pc.versionPtr() != NULL) {
  93. int version = BIND10_HOOKS_VERSION - 1; // This is an invalid value
  94. try {
  95. version = (*pc.versionPtr())();
  96. } catch (...) {
  97. LOG_ERROR(hooks_logger, HOOKS_VERSION_EXCEPTION).arg(library_name_);
  98. return (false);
  99. }
  100. if (version == BIND10_HOOKS_VERSION) {
  101. // All OK, version checks out
  102. LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_LIBRARY_VERSION)
  103. .arg(library_name_).arg(version);
  104. return (true);
  105. } else {
  106. LOG_ERROR(hooks_logger, HOOKS_INCORRECT_VERSION).arg(library_name_)
  107. .arg(version).arg(BIND10_HOOKS_VERSION);
  108. }
  109. } else {
  110. LOG_ERROR(hooks_logger, HOOKS_NO_VERSION).arg(library_name_);
  111. }
  112. return (false);
  113. }
  114. // Register the standard callouts
  115. void
  116. LibraryManager::registerStandardCallouts() {
  117. // Set the library index for doing the registration. This is picked up
  118. // when the library handle is created.
  119. manager_->setLibraryIndex(index_);
  120. // Iterate through the list of known hooks
  121. vector<string> hook_names = ServerHooks::getServerHooks().getHookNames();
  122. for (int i = 0; i < hook_names.size(); ++i) {
  123. // Look up the symbol
  124. void* dlsym_ptr = dlsym(dl_handle_, hook_names[i].c_str());
  125. PointerConverter pc(dlsym_ptr);
  126. if (pc.calloutPtr() != NULL) {
  127. // Found a symbol, so register it.
  128. manager_->getLibraryHandle().registerCallout(hook_names[i],
  129. pc.calloutPtr());
  130. LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS,
  131. HOOKS_STD_CALLOUT_REGISTERED).arg(library_name_)
  132. .arg(hook_names[i]).arg(dlsym_ptr);
  133. }
  134. }
  135. }
  136. // Run the "load" function if present.
  137. bool
  138. LibraryManager::runLoad() {
  139. // Get the pointer to the "load" function.
  140. PointerConverter pc(dlsym(dl_handle_, LOAD_FUNCTION_NAME));
  141. if (pc.loadPtr() != NULL) {
  142. // Call the load() function with the library handle. We need to set
  143. // the CalloutManager's index appropriately. We'll invalidate it
  144. // afterwards.
  145. int status = -1;
  146. try {
  147. manager_->setLibraryIndex(index_);
  148. status = (*pc.loadPtr())(manager_->getLibraryHandle());
  149. } catch (...) {
  150. LOG_ERROR(hooks_logger, HOOKS_LOAD_EXCEPTION).arg(library_name_);
  151. return (false);
  152. }
  153. if (status != 0) {
  154. LOG_ERROR(hooks_logger, HOOKS_LOAD_ERROR).arg(library_name_)
  155. .arg(status);
  156. return (false);
  157. } else {
  158. LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LOAD_SUCCESS)
  159. .arg(library_name_);
  160. }
  161. } else {
  162. LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_NO_LOAD)
  163. .arg(library_name_);
  164. }
  165. return (true);
  166. }
  167. // Run the "unload" function if present.
  168. bool
  169. LibraryManager::runUnload() {
  170. // Get the pointer to the "load" function.
  171. PointerConverter pc(dlsym(dl_handle_, UNLOAD_FUNCTION_NAME));
  172. if (pc.unloadPtr() != NULL) {
  173. // Call the load() function with the library handle. We need to set
  174. // the CalloutManager's index appropriately. We'll invalidate it
  175. // afterwards.
  176. int status = -1;
  177. try {
  178. status = (*pc.unloadPtr())();
  179. } catch (...) {
  180. // Exception generated. Note a warning as the unload will occur
  181. // anyway.
  182. LOG_WARN(hooks_logger, HOOKS_UNLOAD_EXCEPTION).arg(library_name_);
  183. return (false);
  184. }
  185. if (status != 0) {
  186. LOG_ERROR(hooks_logger, HOOKS_UNLOAD_ERROR).arg(library_name_)
  187. .arg(status);
  188. return (false);
  189. } else {
  190. LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_UNLOAD_SUCCESS)
  191. .arg(library_name_);
  192. }
  193. } else {
  194. LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_NO_UNLOAD)
  195. .arg(library_name_);
  196. }
  197. return (true);
  198. }
  199. // The main library loading function.
  200. bool
  201. LibraryManager::loadLibrary() {
  202. LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LIBRARY_LOADING)
  203. .arg(library_name_);
  204. // In the following, if a method such as openLibrary() fails, it will
  205. // have issued an error message so there is no need to issue another one
  206. // here.
  207. // Open the library (which is a check that it exists and is accessible).
  208. if (openLibrary()) {
  209. // Library opened OK, see if a version function is present and if so,
  210. // check what value it returns.
  211. if (checkVersion()) {
  212. // Version OK, so now register the standard callouts and call the
  213. // library's load() function if present.
  214. registerStandardCallouts();
  215. if (runLoad()) {
  216. // Success - the library has been successfully loaded.
  217. LOG_INFO(hooks_logger, HOOKS_LIBRARY_LOADED).arg(library_name_);
  218. return (true);
  219. } else {
  220. // The load function failed, so back out. We can't just close
  221. // the library as (a) we need to call the library's "unload"
  222. // function (if present) in case "load" allocated resources that
  223. // need to be freed and (b) we need to remove any callouts that
  224. // have been installed.
  225. static_cast<void>(unloadLibrary());
  226. }
  227. }
  228. // Either the version check or call to load() failed, so close the
  229. // library and free up resources. Ignore the status return here - we
  230. // already know there's an error and will have output a message.
  231. static_cast<void>(closeLibrary());
  232. }
  233. return (false);
  234. }
  235. // The library unloading function. Call the unload() function (if present),
  236. // remove callouts from the callout manager, then close the library. This is
  237. // only run if the library is still loaded and is a no-op if the library is
  238. // not open.
  239. bool
  240. LibraryManager::unloadLibrary() {
  241. bool result = true;
  242. if (dl_handle_ != NULL) {
  243. LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LIBRARY_UNLOADING)
  244. .arg(library_name_);
  245. // Call the unload() function if present. Note that this is done first
  246. // - operations take place in the reverse order to which they were done
  247. // when the library was loaded.
  248. result = runUnload();
  249. // Regardless of status, remove all callouts associated with this
  250. // library on all hooks.
  251. vector<string> hooks = ServerHooks::getServerHooks().getHookNames();
  252. manager_->setLibraryIndex(index_);
  253. for (int i = 0; i < hooks.size(); ++i) {
  254. bool removed = manager_->deregisterAllCallouts(hooks[i]);
  255. if (removed) {
  256. LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUTS_REMOVED)
  257. .arg(hooks[i]).arg(library_name_);
  258. }
  259. }
  260. // ... and close the library.
  261. result = closeLibrary() && result;
  262. if (result) {
  263. // Issue the informational message only if the library was unloaded
  264. // with no problems. If there was an issue, an error message would
  265. // have been issued.
  266. LOG_INFO(hooks_logger, HOOKS_LIBRARY_UNLOADED).arg(library_name_);
  267. }
  268. }
  269. return (result);
  270. }
  271. // Validate the library. We must be able to open it, and the version function
  272. // must both exist and return the right number. Note that this is a static
  273. // method.
  274. bool
  275. LibraryManager::validateLibrary(const std::string& name) {
  276. // Instantiate a library manager for the validation. We use the private
  277. // constructor as we don't supply a CalloutManager.
  278. LibraryManager manager(name);
  279. // Try to open it and, if we succeed, check the version.
  280. bool validated = manager.openLibrary() && manager.checkVersion();
  281. // Regardless of whether the version checked out, close the library. (This
  282. // is a no-op if the library failed to open.)
  283. static_cast<void>(manager.closeLibrary());
  284. return (validated);
  285. }
  286. } // namespace hooks
  287. } // namespace isc