library_manager.cc 13 KB

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