Browse Source

[2980] Extended library manager functionality

Added ability to run the load function and checked that it can
register callouts.
Stephen Morris 12 years ago
parent
commit
8e94ab0a3b

+ 19 - 3
src/lib/hooks/hooks_messages.mes

@@ -29,9 +29,25 @@ This is most likely due to the installation of a new version of BIND 10
 without rebuilding the hook library.  A rebuild and re-install of the library
 without rebuilding the hook library.  A rebuild and re-install of the library
 should fix the problem in most cases.
 should fix the problem in most cases.
 
 
-% HOOKS_NO_VERSION no version() function found in hook library %1
+% HOOKS_LOAD 'load' function in hook library %1 found and successfully called
+This is a debug message issued when the "load" function has been found in a
+hook library and has been successfully called.
+
+% HOOKS_LOAD_ERROR hook library %1 has 'load' function returing error code %2
+A "load" function was found in the library named in the message and was
+called.  The function returned a non-zero status (also given in the message)
+which was interpreted as an error.  The library has been unloaded and
+no callouts from it will be installed.
+
+% HOOKS_NO_LOAD no 'load' function found in hook library %1
+This is a debug message saying that the specified library was loaded but
+no function called "load" was found in it.  Providing the library contained
+some "standard" functions (i.e. functions with the names of the hooks for
+the given server), this is not an issue.
+
+% HOOKS_NO_VERSION no 'version' function found in hook library %1
 The shared library named in the message was found and successfully loaded, but
 The shared library named in the message was found and successfully loaded, but
-BIND 10 did not find a function named 'version' in it.  This function is
+BIND 10 did not find a function named "version" in it.  This function is
 required and should return the version of BIND 10 against which the library
 required and should return the version of BIND 10 against which the library
 was built.  The value is used to check that the library was built against a
 was built.  The value is used to check that the library was built against a
 compatible version of BIND 10.  The library has not been loaded.
 compatible version of BIND 10.  The library has not been loaded.
@@ -41,7 +57,7 @@ BIND 10 failed to open the specified hook library for the stated reason. The
 library has not been loaded.  BIND 10 will continue to function, but without
 library has not been loaded.  BIND 10 will continue to function, but without
 the services offered by the library.
 the services offered by the library.
 
 
-% HOOKS_REGISTER_CALLOUT library %1 has registered a callout for hook %2
+% HOOKS_REGISTER_CALLOUT library %1 has registered a callout for hook '%2'
 This is a debug message, output when the library loading function has located
 This is a debug message, output when the library loading function has located
 a standard callout (a callout with the same name as a hook point) and
 a standard callout (a callout with the same name as a hook point) and
 registered it.
 registered it.

+ 197 - 0
src/lib/hooks/library_manager.cc

@@ -0,0 +1,197 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/hooks.h>
+#include <hooks/hooks_log.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/library_manager.h>
+#include <hooks/server_hooks.h>
+
+#include <string>
+#include <vector>
+
+#include <dlfcn.h>
+
+namespace {
+
+// String constants
+
+const char* LOAD_FUNCTION_NAME = "load";
+// const char* UNLOAD = "unload";
+const char* VERSION_FUNCTION_NAME = "version";
+}
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Open the library
+
+bool
+LibraryManager::openLibrary() {
+
+    // Open the library.  We'll resolve names now, so that if there are any
+    // issues we don't bugcheck in the middle of apparently unrelated code.
+    dl_handle_ = dlopen(library_name_.c_str(), RTLD_NOW | RTLD_DEEPBIND);
+    if (dl_handle_ == NULL) {
+        LOG_ERROR(hooks_logger, HOOKS_OPEN_ERROR).arg(library_name_)
+                  .arg(dlerror());
+    }
+
+    return (dl_handle_ != NULL);
+}
+
+// Close the library if not already open
+
+bool
+LibraryManager::closeLibrary() {
+
+    // Close the library if it is open. (If not, this is a no-op.)
+    int status = 0;
+    if (dl_handle_ != NULL) {
+        status = dlclose(dl_handle_);
+        dl_handle_ = NULL;
+        if (status != 0) {
+            LOG_ERROR(hooks_logger, HOOKS_CLOSE_ERROR).arg(library_name_)
+                      .arg(dlerror());
+        }
+    }
+
+    return (status == 0);
+}
+
+// Check the version of the library
+
+bool
+LibraryManager::checkVersion() const {
+
+    // Look up the "version" string in the library.  This is returned as
+    // "void*": without any other information, we must assume that it is of
+    // the correct type of version_function_ptr.
+    //
+    // Note that converting between void* and function pointers in C++ is
+    // fraught with difficulty and pitfalls (e.g. see
+    // https://groups.google.com/forum/?hl=en&fromgroups#!topic/
+    // comp.lang.c++/37o0l8rtEE0)
+    // The method given in that article - convert using a union is used here.
+    union {
+        version_function_ptr    ver_ptr;
+        void*                   dlsym_ptr;
+    } pointers;
+
+    // Zero the union, whatever the size of the pointers.
+    pointers.ver_ptr = NULL;
+    pointers.dlsym_ptr = NULL;
+
+    // Get the pointer to the "version" function.
+    pointers.dlsym_ptr = dlsym(dl_handle_, VERSION_FUNCTION_NAME);
+    if (pointers.ver_ptr != NULL) {
+        int version = (*pointers.ver_ptr)();
+        if (version == BIND10_HOOKS_VERSION) {
+            // All OK, version checks out
+            return (true);
+
+        } else {
+            LOG_ERROR(hooks_logger, HOOKS_INCORRECT_VERSION).arg(library_name_)
+                      .arg(version).arg(BIND10_HOOKS_VERSION);
+        }
+    } else {
+        LOG_ERROR(hooks_logger, HOOKS_NO_VERSION).arg(library_name_);
+    }
+
+    return (false);
+}
+
+// Register the standard callouts
+
+void
+LibraryManager::registerStandardCallouts() {
+    // Create a library handle for doing the registration.  We also need to
+    // set the current library index to indicate the current library.
+    manager_->setLibraryIndex(index_);
+    LibraryHandle library_handle(manager_.get());
+
+    // Iterate through the list of known hooks
+    vector<string> hook_names = ServerHooks::getServerHooks().getHookNames();
+    for (int i = 0; i < hook_names.size(); ++i) {
+
+        // Convert void* to function pointers using the same tricks as
+        // described above.
+        union {
+            CalloutPtr  callout_ptr;
+            void*       dlsym_ptr;
+        } pointers;
+        pointers.callout_ptr = NULL;
+        pointers.dlsym_ptr = NULL;
+
+        // Look up the symbol
+        pointers.dlsym_ptr = dlsym(dl_handle_, hook_names[i].c_str());
+        if (pointers.callout_ptr != NULL) {
+            // Found a symbol, so register it.
+            //library_handle.registerCallout(hook_names[i], callout_ptr);
+            LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_REGISTER_CALLOUT)
+                .arg(library_name_).arg(hook_names[i]);
+            library_handle.registerCallout(hook_names[i], pointers.callout_ptr);
+
+        }
+    }
+}
+
+// Run the "load" function if present.
+
+bool
+LibraryManager::runLoad() {
+
+    // Look up the "load" function in the library.  The code here is similar
+    // to that in "checkVersion".
+    union {
+        load_function_ptr   load_ptr;
+        void*               dlsym_ptr;
+    } pointers;
+
+    // Zero the union, whatever the size of the pointers.
+    pointers.load_ptr = NULL;
+    pointers.dlsym_ptr = NULL;
+
+    // Get the pointer to the "load" function.
+    pointers.dlsym_ptr = dlsym(dl_handle_, LOAD_FUNCTION_NAME);
+    if (pointers.load_ptr != NULL) {
+
+        // Call the load() function with the library handle.  We need to set
+        // the CalloutManager's index appropriately.  We'll invalidate it
+        // afterwards.
+        manager_->setLibraryIndex(index_);
+        int status = (*pointers.load_ptr)(manager_->getLibraryHandle());
+        manager_->setLibraryIndex(index_);
+        if (status != 0) {
+            LOG_ERROR(hooks_logger, HOOKS_LOAD_ERROR).arg(library_name_)
+                      .arg(status);
+            return (false);
+        } else {
+        LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LOAD)
+            .arg(library_name_);
+        }
+    } else {
+        LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_NO_LOAD)
+            .arg(library_name_);
+    }
+
+    return (true);
+}
+
+
+} // namespace hooks
+} // namespace isc

+ 44 - 10
src/lib/hooks/library_manager.h

@@ -33,17 +33,38 @@ class LibraryManager;
 /// with the "version" method.  If all is OK, it iterates through the list of
 /// with the "version" method.  If all is OK, it iterates through the list of
 /// known hooks and locates their symbols, registering each callout as it
 /// known hooks and locates their symbols, registering each callout as it
 /// does so.  Finally it locates the "load" and "unload" functions (if present),
 /// does so.  Finally it locates the "load" and "unload" functions (if present),
-/// calling the "load" callouts if present.
+/// calling the "load" callout if present.
 ///
 ///
 /// On unload, it calls the "unload" method if one was located, clears the
 /// On unload, it calls the "unload" method if one was located, clears the
 /// callouts from all hooks and closes the library.
 /// callouts from all hooks and closes the library.
+///
+/// @note Caution needs to be exercised whtn using the unload method. During
+///       use, data will pass between the server and the library.  In this
+///       process, the library may allocate memory and pass it back to the
+///       server.  This could happen by the server setting arguments or context
+///       in the CalloutHandle object, or by the library modifying the content
+///       of pointed-to data. A problem arises when the library is unloaded,
+///       because the addresses of allocated day may lie in the virtual
+///       address space deleted in that process.  If this happens, any
+///       reference to the memory will cause a segmentation fault.  This can
+///       occur in a quite obscure place, for example in the middle of a
+///       destructor of an STL class when it is deleting memory allocated
+///       when the data structure was extended.
+///
+/// @par  The only safe way to run the "unload" function is to ensure that all
+///       possible references to it are removed first.  This means that all
+///       CalloutHandles must be destroyed, as must any data items that were
+///       passed to the callouts.  In practice, it could mean that a server
+///       suspends processing of new requests until all existing ones have
+///       been serviced and all packet/context structures destroyed before
+///       reloading the libraries.
 
 
 class LibraryManager {
 class LibraryManager {
 private:
 private:
     /// Useful typedefs for the framework functions
     /// Useful typedefs for the framework functions
-    typedef int (*version_function_ptr)();          ///< version() signature
-    typedef int (*load_function_ptr)();             ///< load() signature
-    typedef int (*unload_function_ptr)(LibraryHandle&); ///< unload() signature
+    typedef int (*version_function_ptr)();            ///< version() signature
+    typedef int (*load_function_ptr)(LibraryHandle&); ///< load() signature
+    typedef int (*unload_function_ptr)();             ///< unload() signature
 
 
 public:
 public:
     /// @brief Constructor
     /// @brief Constructor
@@ -57,7 +78,7 @@ public:
     LibraryManager(const std::string& name, int index,
     LibraryManager(const std::string& name, int index,
                    const boost::shared_ptr<CalloutManager>& manager)
                    const boost::shared_ptr<CalloutManager>& manager)
         : dl_handle_(NULL), index_(index), manager_(manager),
         : dl_handle_(NULL), index_(index), manager_(manager),
-          library_name_(name), load_func_(NULL), unload_func_(NULL)
+          library_name_(name)
     {}
     {}
 
 
     /// @brief Destructor
     /// @brief Destructor
@@ -65,21 +86,27 @@ public:
     /// If the library is open, closes it.  This is principally a safety
     /// If the library is open, closes it.  This is principally a safety
     /// feature to ensure closure in the case of an exception destroying
     /// feature to ensure closure in the case of an exception destroying
     /// this object.
     /// this object.
+    ///
+    /// However, see the caveat in the class header about when it is safe
+    /// to unload libraries.
     ~LibraryManager() {
     ~LibraryManager() {
-        static_cast<void>(closeLibrary());
+        static_cast<void>(unloadLibrary());
     }
     }
 
 
     /// @brief Loads a library
     /// @brief Loads a library
     ///
     ///
     /// Open the library and check the version.  If all is OK, load all
     /// Open the library and check the version.  If all is OK, load all
     /// standard symbols then call "load" if present.
     /// standard symbols then call "load" if present.
-    void loadLibrary() {}
+    bool loadLibrary() {return true;}
 
 
     /// @brief Unloads a library
     /// @brief Unloads a library
     ///
     ///
     /// Calls the libraries "unload" function if present, the closes the
     /// Calls the libraries "unload" function if present, the closes the
     /// library.
     /// library.
-    void unloadLibrary() {}
+    ///
+    /// However, see the caveat in the class header about when it is safe
+    /// to unload libraries.
+    bool unloadLibrary() {return false;}
 
 
     /// @brief Return library name
     /// @brief Return library name
     ///
     ///
@@ -128,6 +155,15 @@ protected:
     /// callouts for that hook.
     /// callouts for that hook.
     void registerStandardCallouts();
     void registerStandardCallouts();
 
 
+    /// @brief Run the load function if present
+    ///
+    /// Searches for the "load" framework function and, if present, runs it.
+    ///
+    /// @return bool true if not found or found and run successfully,
+    ///         false on an error.  In this case, an error message will
+    ///         have been output.
+    bool runLoad();
+
 private:
 private:
     void*       dl_handle_;     ///< Handle returned by dlopen
     void*       dl_handle_;     ///< Handle returned by dlopen
     int         index_;         ///< Index associated with this library
     int         index_;         ///< Index associated with this library
@@ -135,8 +171,6 @@ private:
                                 ///< Callout manager for registration
                                 ///< Callout manager for registration
     std::string library_name_;  ///< Name of the library
     std::string library_name_;  ///< Name of the library
 
 
-    load_function_ptr       load_func_;     ///< Pointer to "load" function
-    unload_function_ptr     unload_func_;   ///< Pointer to "unload" function
 };
 };
 
 
 } // namespace hooks
 } // namespace hooks

+ 21 - 16
src/lib/hooks/tests/Makefile.am

@@ -25,28 +25,33 @@ TESTS_ENVIRONMENT = \
 	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 	$(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
 
 
 # Build shared libraries for testing.
 # Build shared libraries for testing.
-lib_LTLIBRARIES = libnv.la libiv.la libbco.la
+lib_LTLIBRARIES = libnvl.la libivl.la libbcl.la liblcl.la liblecl.la
  
  
 # No version function
 # No version function
-libnv_la_SOURCES  = no_version_library.cc
-libnv_la_CXXFLAGS = $(AM_CXXFLAGS)
-libnv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libnvl_la_SOURCES  = no_version_library.cc
+libnvl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libnvl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
  
  
 # Incorrect version function
 # Incorrect version function
-libiv_la_SOURCES  = incorrect_version_library.cc
-libiv_la_CXXFLAGS = $(AM_CXXFLAGS)
-libiv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libivl_la_SOURCES  = incorrect_version_library.cc
+libivl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libilv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 
 
+# The basic callout library - contains standard callouts
+libbcl_la_SOURCES  = basic_callout_library.cc
+libbcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libbcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 
 
-# The basic callout library
-libbco_la_SOURCES  = basic_callout_library.cc
-libbco_la_CXXFLAGS = $(AM_CXXFLAGS)
-libbco_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
-libbco_la_LIBADD  =
-libbco_la_LIBADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
-libbco_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
-libbco_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
-libbco_la_LIBADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+# The load callout library - contains a load function
+liblcl_la_SOURCES  = load_callout_library.cc
+liblcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+liblcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The load error callout library - contains a load function that returns
+# an error.
+liblecl_la_SOURCES  = load_error_callout_library.cc
+liblecl_la_CXXFLAGS = $(AM_CXXFLAGS)
+liblecl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST

+ 21 - 10
src/lib/hooks/tests/basic_callout_library.cc

@@ -20,14 +20,22 @@
 ///
 ///
 /// The characteristics of this library are:
 /// The characteristics of this library are:
 ///
 ///
-/// - Only the "version" framework function is supplied.  The other callouts
-///   are assumed to be "standard" callouts.
+/// - Only the "version" framework function is supplied.
 ///
 ///
 /// - A context_create callout is supplied.
 /// - A context_create callout is supplied.
 ///
 ///
-/// - Three other callouts are supplied.  All do some trivial calculationsll
+/// - Three "standard" callouts are supplied corresponding to the hooks
+///   "lm_one", "lm_two", "lm_three".  All do some trivial calculations
 ///   on the arguments supplied to it and the context variables, returning
 ///   on the arguments supplied to it and the context variables, returning
-///   intermediate results through the "result" argument.
+///   intermediate results through the "result" argument. The result of
+///   the calculation is:
+///
+///   @f[ (10 + data_1) * data_2 - data_3 @f]
+///
+///   ...where data_1, data_2 and data_3 are the values passed in arguments
+///   of the same name to the three callouts (data_1 passed to lm_one,
+///   data_2 to lm_two etc.) and the result is returned in the argument
+///   "result".
 
 
 #include <hooks/hooks.h>
 #include <hooks/hooks.h>
 #include <iostream>
 #include <iostream>
@@ -36,11 +44,6 @@ using namespace isc::hooks;
 
 
 extern "C" {
 extern "C" {
 
 
-int
-version() {
-    return (BIND10_HOOKS_VERSION);
-}
-
 // Callouts
 // Callouts
 
 
 int
 int
@@ -53,7 +56,8 @@ context_create(CalloutHandle& handle) {
 // First callout adds the passed "data_1" argument to the initialized context
 // First callout adds the passed "data_1" argument to the initialized context
 // value of 10.
 // value of 10.
 
 
-int lm_one(CalloutHandle& handle) {
+int
+lm_one(CalloutHandle& handle) {
     int data;
     int data;
     handle.getArgument("data_1", data);
     handle.getArgument("data_1", data);
 
 
@@ -101,5 +105,12 @@ lm_three(CalloutHandle& handle) {
     return (0);
     return (0);
 }
 }
 
 
+// Framework functions
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
 };
 };
 
 

+ 99 - 5
src/lib/hooks/tests/library_manager_unittest.cc.in

@@ -101,16 +101,20 @@ public:
     using LibraryManager::closeLibrary;
     using LibraryManager::closeLibrary;
     using LibraryManager::checkVersion;
     using LibraryManager::checkVersion;
     using LibraryManager::registerStandardCallouts;
     using LibraryManager::registerStandardCallouts;
+    using LibraryManager::runLoad;
 };
 };
 
 
 // Names of the libraries used in these tests.  These libraries are built using
 // Names of the libraries used in these tests.  These libraries are built using
 // libtool, so we need to look in the hidden ".libs" directory to locate the
 // libtool, so we need to look in the hidden ".libs" directory to locate the
 // .so file.  Note that we access the .so file - libtool creates this as a
 // .so file.  Note that we access the .so file - libtool creates this as a
 // like to the real shared library.
 // like to the real shared library.
+static const char* BASIC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libbcl.so";
+static const char* INCORRECT_VERSION_LIBRARY = "@abs_builddir@/.libs/libivl.so";
+static const char* LOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/liblcl.so";
+static const char* LOAD_ERROR_CALLOUT_LIBRARY =
+    "@abs_builddir@/.libs/liblecl.so";
 static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
 static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
-static const char* NO_VERSION_LIBRARY = "@abs_builddir@/.libs/libnv.so";
-static const char* INCORRECT_VERSION_LIBRARY = "@abs_builddir@/.libs/libiv.so";
-static const char* BASIC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libbco.so";
+static const char* NO_VERSION_LIBRARY = "@abs_builddir@/.libs/libnvl.so";
 
 
 
 
 namespace {
 namespace {
@@ -213,13 +217,13 @@ TEST_F(LibraryManagerTest, RegisterStandardCallouts) {
     callout_handle_->getArgument("result", result);
     callout_handle_->getArgument("result", result);
     EXPECT_EQ(15, result);
     EXPECT_EQ(15, result);
 
 
-    // Second callout multiples the context value by 7
+    // Second callout multiples the running total by 7
     callout_handle_->setArgument("data_2", static_cast<int>(7));
     callout_handle_->setArgument("data_2", static_cast<int>(7));
     callout_manager_->callCallouts(lm_two_index_, *callout_handle_);
     callout_manager_->callCallouts(lm_two_index_, *callout_handle_);
     callout_handle_->getArgument("result", result);
     callout_handle_->getArgument("result", result);
     EXPECT_EQ(105, result);
     EXPECT_EQ(105, result);
 
 
-    // Third callout subtracts 17.
+    // Third callout subtracts 17 from the running total.
     callout_handle_->setArgument("data_3", static_cast<int>(17));
     callout_handle_->setArgument("data_3", static_cast<int>(17));
     callout_manager_->callCallouts(lm_three_index_, *callout_handle_);
     callout_manager_->callCallouts(lm_three_index_, *callout_handle_);
     callout_handle_->getArgument("result", result);
     callout_handle_->getArgument("result", result);
@@ -233,4 +237,94 @@ TEST_F(LibraryManagerTest, RegisterStandardCallouts) {
     EXPECT_TRUE(lib_manager.closeLibrary());
     EXPECT_TRUE(lib_manager.closeLibrary());
 }
 }
 
 
+// Test that the "load" function is called correctly.
+
+TEST_F(LibraryManagerTest, CheckLoadCalled) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(LOAD_CALLOUT_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Check the version of the library.
+    EXPECT_TRUE(lib_manager.checkVersion());
+
+    // Load the standard callouts
+    EXPECT_NO_THROW(lib_manager.registerStandardCallouts());
+
+    int result = 0;
+
+    // Check that only context_create and lm_one have callouts registered.
+    EXPECT_TRUE(callout_manager_->calloutsPresent(
+                ServerHooks::CONTEXT_CREATE));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(lm_one_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(lm_two_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(lm_three_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(
+                 ServerHooks::CONTEXT_DESTROY));
+
+    // Call the runLoad() method to run the load() function.
+    EXPECT_TRUE(lib_manager.runLoad());
+    EXPECT_TRUE(callout_manager_->calloutsPresent(
+                ServerHooks::CONTEXT_CREATE));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(lm_one_index_));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(lm_two_index_));
+    EXPECT_TRUE(callout_manager_->calloutsPresent(lm_three_index_));
+    EXPECT_FALSE(callout_manager_->calloutsPresent(
+                 ServerHooks::CONTEXT_DESTROY));
+
+    // Now execute the callouts in the order expected.
+    // only the first callout should be executed and the
+    // always comes first.  This sets the context value to 10.
+    callout_manager_->callCallouts(ServerHooks::CONTEXT_CREATE,
+                                   *callout_handle_);
+
+    // First callout multiplies the passed data by 5.
+    callout_handle_->setArgument("data_1", static_cast<int>(5));
+    callout_manager_->callCallouts(lm_one_index_, *callout_handle_);
+    callout_handle_->getArgument("result", result);
+    EXPECT_EQ(25, result);
+
+    // Second callout adds 7 to the stored data.
+    callout_handle_->setArgument("data_2", static_cast<int>(7));
+    callout_manager_->callCallouts(lm_two_index_, *callout_handle_);
+    callout_handle_->getArgument("result", result);
+    EXPECT_EQ(32, result);
+
+    // Third callout multiplies the running total by 10
+    callout_handle_->setArgument("data_3", static_cast<int>(10));
+    callout_manager_->callCallouts(lm_three_index_, *callout_handle_);
+    callout_handle_->getArgument("result", result);
+    EXPECT_EQ(320, result);
+
+    // Explicitly clear the callout_handle_ so that we can delete the library.
+    // This is the only object to contain memory allocated by it.
+    callout_handle_.reset();
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check handling of a "load" function that returns an error.
+
+TEST_F(LibraryManagerTest, CheckLoadError) {
+
+    // Load the only library, specifying the index of 0 as it's the only
+    // library.  This should load all callouts.
+    PublicLibraryManager lib_manager(std::string(LOAD_ERROR_CALLOUT_LIBRARY),
+                                     0, callout_manager_);
+    EXPECT_TRUE(lib_manager.openLibrary());
+
+    // Check that we catch a load error
+    EXPECT_FALSE(lib_manager.runLoad());
+
+    // Explicitly clear the callout_handle_ so that we can delete the library.
+    // This is the only object to contain memory allocated by it.
+    callout_handle_.reset();
+
+    // Tidy up
+    EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
 } // Anonymous namespace
 } // Anonymous namespace

+ 121 - 0
src/lib/hooks/tests/load_callout_library.cc

@@ -0,0 +1,121 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief Basic Load Library
+///
+/// This is a test file for the LibraryManager test.  It produces a library
+/// that allows for tests of the basic library manager functions.
+///
+/// The characteristics of this library are:
+///
+/// - The "version" and "load" framework functions are supplied.  One "standard"
+///   callout is supplied ("lm_one") and two non-standard ones which are
+///   registered during the call to "load" on the hooks "lm_two" and
+///   "lm_three". 
+///
+///   All callouts do trivial calculations, the result of the calculation being
+///
+///   @f[ ((5 * data_1) + data_2) * data_3 @f]
+///
+///   ...where data_1, data_2 and data_3 are the values passed in arguments
+///   of the same name to the three callouts (data_1 passed to lm_one,
+///   data_2 to lm_two etc.) and the result is returned in the argument
+///   "result".
+
+#include <hooks/hooks.h>
+#include <iostream>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Callouts
+
+int
+context_create(CalloutHandle& handle) {
+    handle.setContext("result", static_cast<int>(5));
+    handle.setArgument("result", static_cast<int>(5));
+    return (0);
+}
+
+// First callout multiples the passed "data_1" argument to the initialized
+// context value of 5.
+
+int
+lm_one(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_1", data);
+
+    int result;
+    handle.getContext("result", result);
+
+    result *= data;
+    handle.setContext("result", result);
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Second callout multiplies the current context value by the "data_2"
+// argument.
+
+static int
+lm_nonstandard_two(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_2", data);
+
+    int result;
+    handle.getContext("result", result);
+
+    result += data;
+    handle.setContext("result", result);
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Final callout adds the result in "data_3" and.
+
+static int
+lm_nonstandard_three(CalloutHandle& handle) {
+    int data;
+    handle.getArgument("data_3", data);
+
+    int result;
+    handle.getContext("result", result);
+
+    result *= data;
+    handle.setArgument("result", result);
+
+    return (0);
+}
+
+// Framework functions
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+int load(LibraryHandle& handle) {
+    // Register the non-standard functions
+    handle.registerCallout("lm_two", lm_nonstandard_two);
+    handle.registerCallout("lm_three", lm_nonstandard_three);
+
+    return (0);
+}
+
+};
+

+ 45 - 0
src/lib/hooks/tests/load_error_callout_library.cc

@@ -0,0 +1,45 @@
+// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief Error Load Library
+///
+/// This is a test file for the LibraryManager test.  It produces a library
+/// that allows for tests of the basic library manager functions.
+///
+/// The characteristics of this library are:
+///
+/// - The "version" and "load" framework functions are supplied.  "version"
+///   returns the correct value, but "load" returns an error.
+
+#include <hooks/hooks.h>
+#include <iostream>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Framework functions
+
+int
+version() {
+    return (BIND10_HOOKS_VERSION);
+}
+
+int load(LibraryHandle&) {
+    return (1);
+}
+
+};
+