Parcourir la source

[master] Merge branch 'trac4297' (hook libs can now take parameters)

Tomek Mrugalski il y a 9 ans
Parent
commit
f45d0b5d29
31 fichiers modifiés avec 851 ajouts et 131 suppressions
  1. 36 1
      doc/guide/hooks.xml
  2. 1 1
      src/bin/dhcp4/ctrl_dhcp4_srv.cc
  3. 5 5
      src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
  4. 1 1
      src/bin/dhcp6/ctrl_dhcp6_srv.cc
  5. 6 6
      src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
  6. 1 1
      src/lib/Makefile.am
  7. 5 0
      src/lib/cc/Makefile.am
  8. 27 7
      src/lib/dhcpsrv/parsers/dhcp_parsers.cc
  9. 6 8
      src/lib/dhcpsrv/parsers/dhcp_parsers.h
  10. 6 1
      src/lib/dhcpsrv/tests/Makefile.am
  11. 3 3
      src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc
  12. 5 5
      src/lib/dhcpsrv/tests/alloc_engine_hooks_unittest.cc
  13. 25 0
      src/lib/dhcpsrv/tests/callout_params_library.cc
  14. 95 29
      src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
  15. 6 0
      src/lib/dhcpsrv/tests/test_libraries.h.in
  16. 2 0
      src/lib/hooks/Makefile.am
  17. 15 4
      src/lib/hooks/hooks_manager.cc
  18. 14 3
      src/lib/hooks/hooks_manager.h
  19. 140 7
      src/lib/hooks/hooks_user.dox
  20. 28 0
      src/lib/hooks/libinfo.cc
  21. 42 0
      src/lib/hooks/libinfo.h
  22. 35 0
      src/lib/hooks/library_handle.cc
  23. 64 0
      src/lib/hooks/library_handle.h
  24. 11 1
      src/lib/hooks/library_manager_collection.cc
  25. 17 5
      src/lib/hooks/library_manager_collection.h
  26. 9 1
      src/lib/hooks/tests/Makefile.am
  27. 106 0
      src/lib/hooks/tests/callout_params_library.cc
  28. 68 26
      src/lib/hooks/tests/hooks_manager_unittest.cc
  29. 68 16
      src/lib/hooks/tests/library_manager_collection_unittest.cc
  30. 1 0
      src/lib/hooks/tests/library_manager_unittest.cc
  31. 3 0
      src/lib/hooks/tests/test_libraries.h.in

+ 36 - 1
doc/guide/hooks.xml

@@ -57,13 +57,24 @@
             "library": "/opt/charging.so"
             "library": "/opt/charging.so"
         },
         },
         {
         {
-            "library": "/opt/local/notification.so"
+            "library": "/opt/local/notification.so",
+            "parameters": {
+                "mail": "spam@example.com",
+                "floor": 13,
+                "debug": false,
+                "users": [ "alice", "bob", "charlie" ],
+                "languages": {
+                    "french": "bonjour",
+                    "klingon": "yl'el"
+                }
+            }
         }
         }
     ]
     ]
     :
     :
 }</userinput>
 }</userinput>
 </screen>
 </screen>
       </para>
       </para>
+
       <note><para>
       <note><para>
         This is a change to the syntax used in Kea 0.9.2 and earlier, where
         This is a change to the syntax used in Kea 0.9.2 and earlier, where
         hooks-libraries was a list of strings, each string being the name of
         hooks-libraries was a list of strings, each string being the name of
@@ -71,6 +82,30 @@
         specification of library-specific parameters, a feature that will be
         specification of library-specific parameters, a feature that will be
         added to a future version of Kea.
         added to a future version of Kea.
       </para></note>
       </para></note>
+
+        <note>
+          <para>
+          The library reloading behavior has changed in Kea 1.1. Libraries are
+          reloaded, even if their list hasn't changed. Kea does that, because
+          the parameters specified for the library (or the files those
+          parameters point to) may have changed.
+          </para>
+        </note>
+
+      <para>
+        Libraries may have additional parameters. Those are not mandatory in the
+        sense that there may be libraries that don't require them. However, for
+        specific library there is often specific requirement for specify certain
+        set of parameters. Please consult the documentation for your library
+        for details. In the example above, the first library has no parameters.
+        The second library has five parameters, specifying mail (string
+        parameter), floor (integer parameter), debug (boolean parameter) and
+        even lists (list of strings) and maps (containing strings). Nested
+        parameters could be used if the library supports it. This topic is
+        explained in detail in the Hooks Developer's Guide in Configuring Hooks
+        Libraries section.
+      </para>
+
       <para>
       <para>
       Notes:
       Notes:
         <itemizedlist mark='bullet'>
         <itemizedlist mark='bullet'>

+ 1 - 1
src/bin/dhcp4/ctrl_dhcp4_srv.cc

@@ -45,7 +45,7 @@ ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
 
 
     /// @todo delete any stored CalloutHandles referring to the old libraries
     /// @todo delete any stored CalloutHandles referring to the old libraries
     /// Get list of currently loaded libraries and reload them.
     /// Get list of currently loaded libraries and reload them.
-    vector<string> loaded = HooksManager::getLibraryNames();
+    HookLibsCollection loaded = HooksManager::getLibraryInfo();
     bool status = HooksManager::loadLibraries(loaded);
     bool status = HooksManager::loadLibraries(loaded);
     if (!status) {
     if (!status) {
         LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL);
         LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL);

+ 5 - 5
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -233,15 +233,15 @@ TEST_F(CtrlChannelDhcpv4SrvTest, libreload) {
     ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
     ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
 
 
     // Load two libraries
     // Load two libraries
-    std::vector<std::string> libraries;
-    libraries.push_back(CALLOUT_LIBRARY_1);
-    libraries.push_back(CALLOUT_LIBRARY_2);
+    HookLibsCollection libraries;
+    libraries.push_back(make_pair(CALLOUT_LIBRARY_1, ConstElementPtr()));
+    libraries.push_back(make_pair(CALLOUT_LIBRARY_2, ConstElementPtr()));
     HooksManager::loadLibraries(libraries);
     HooksManager::loadLibraries(libraries);
 
 
     // Check they are loaded.
     // Check they are loaded.
     std::vector<std::string> loaded_libraries =
     std::vector<std::string> loaded_libraries =
         HooksManager::getLibraryNames();
         HooksManager::getLibraryNames();
-    ASSERT_TRUE(libraries == loaded_libraries);
+    ASSERT_TRUE(extractNames(libraries) == loaded_libraries);
 
 
     // ... which also included checking that the marker file created by the
     // ... which also included checking that the marker file created by the
     // load functions exists and holds the correct value (of "12" - the
     // load functions exists and holds the correct value (of "12" - the

+ 1 - 1
src/bin/dhcp6/ctrl_dhcp6_srv.cc

@@ -51,7 +51,7 @@ ConstElementPtr
 ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
 ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
     /// @todo delete any stored CalloutHandles referring to the old libraries
     /// @todo delete any stored CalloutHandles referring to the old libraries
     /// Get list of currently loaded libraries and reload them.
     /// Get list of currently loaded libraries and reload them.
-    vector<string> loaded = HooksManager::getLibraryNames();
+    HookLibsCollection loaded = HooksManager::getLibraryInfo();
     bool status = HooksManager::loadLibraries(loaded);
     bool status = HooksManager::loadLibraries(loaded);
     if (!status) {
     if (!status) {
         LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL);
         LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL);

+ 6 - 6
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -249,14 +249,14 @@ TEST_F(CtrlDhcpv6SrvTest, libreload) {
     ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
     ASSERT_FALSE(checkMarkerFileExists(UNLOAD_MARKER_FILE));
 
 
     // Load two libraries
     // Load two libraries
-    std::vector<std::string> libraries;
-    libraries.push_back(CALLOUT_LIBRARY_1);
-    libraries.push_back(CALLOUT_LIBRARY_2);
+    HookLibsCollection libraries;
+    libraries.push_back(make_pair(CALLOUT_LIBRARY_1, ConstElementPtr()));
+    libraries.push_back(make_pair(CALLOUT_LIBRARY_2, ConstElementPtr()));
     HooksManager::loadLibraries(libraries);
     HooksManager::loadLibraries(libraries);
 
 
     // Check they are loaded.
     // Check they are loaded.
-    std::vector<std::string> loaded_libraries =
-        HooksManager::getLibraryNames();
+    HookLibsCollection loaded_libraries =
+        HooksManager::getLibraryInfo();
     ASSERT_TRUE(libraries == loaded_libraries);
     ASSERT_TRUE(libraries == loaded_libraries);
 
 
     // ... which also included checking that the marker file created by the
     // ... which also included checking that the marker file created by the

+ 1 - 1
src/lib/Makefile.am

@@ -1,3 +1,3 @@
 # The following build order must be maintained.
 # The following build order must be maintained.
-SUBDIRS = exceptions util log hooks cryptolink dns cc asiolink testutils dhcp config \
+SUBDIRS = exceptions util log cryptolink dns cc hooks asiolink testutils dhcp config \
 	      stats asiodns dhcp_ddns eval dhcpsrv cfgrpt
 	      stats asiodns dhcp_ddns eval dhcpsrv cfgrpt

+ 5 - 0
src/lib/cc/Makefile.am

@@ -13,4 +13,9 @@ libkea_cc_la_LIBADD += $(BOOST_LIBS)
 
 
 libkea_cc_la_LDFLAGS = -no-undefined -version-info 1:0:0
 libkea_cc_la_LDFLAGS = -no-undefined -version-info 1:0:0
 
 
+# Since data.h is now used in the hooks interface, it needs to be
+# installed on target system.
+libkea_cc_includedir = $(pkgincludedir)/cc
+libkea_cc_include_HEADERS = data.h
+
 CLEANFILES = *.gcno *.gcda
 CLEANFILES = *.gcno *.gcda

+ 27 - 7
src/lib/dhcpsrv/parsers/dhcp_parsers.cc

@@ -254,6 +254,8 @@ HooksLibrariesParser::build(ConstElementPtr value) {
 
 
     // This is the new syntax.  Iterate through it and get each map.
     // This is the new syntax.  Iterate through it and get each map.
     BOOST_FOREACH(ConstElementPtr library_entry, value->listValue()) {
     BOOST_FOREACH(ConstElementPtr library_entry, value->listValue()) {
+        ConstElementPtr parameters;
+
         // Is it a map?
         // Is it a map?
         if (library_entry->getType() != Element::map) {
         if (library_entry->getType() != Element::map) {
             isc_throw(DhcpConfigError, "hooks library configuration error:"
             isc_throw(DhcpConfigError, "hooks library configuration error:"
@@ -264,6 +266,13 @@ HooksLibrariesParser::build(ConstElementPtr value) {
         // Iterate iterate through each element in the map.  We check
         // Iterate iterate through each element in the map.  We check
         // whether we have found a library element.
         // whether we have found a library element.
         bool lib_found = false;
         bool lib_found = false;
+
+        string libname = "";
+
+        // Let's explicitly reset the parameters, so we won't cover old
+        // values from the previous loop round.
+        parameters.reset();
+
         BOOST_FOREACH(ConfigPair entry_item, library_entry->mapValue()) {
         BOOST_FOREACH(ConfigPair entry_item, library_entry->mapValue()) {
             if (entry_item.first == "library") {
             if (entry_item.first == "library") {
                 if (entry_item.second->getType() != Element::string) {
                 if (entry_item.second->getType() != Element::string) {
@@ -275,7 +284,7 @@ HooksLibrariesParser::build(ConstElementPtr value) {
 
 
                 // Get the name of the library and add it to the list after
                 // Get the name of the library and add it to the list after
                 // removing quotes.
                 // removing quotes.
-                string libname = (entry_item.second)->stringValue();
+                libname = (entry_item.second)->stringValue();
 
 
                 // Remove leading/trailing quotes and any leading/trailing
                 // Remove leading/trailing quotes and any leading/trailing
                 // spaces.
                 // spaces.
@@ -287,10 +296,14 @@ HooksLibrariesParser::build(ConstElementPtr value) {
                         " blank (" <<
                         " blank (" <<
                         entry_item.second->getPosition() << ")");
                         entry_item.second->getPosition() << ")");
                 }
                 }
-                libraries_.push_back(libname);
 
 
                 // Note we have found the library name.
                 // Note we have found the library name.
                 lib_found = true;
                 lib_found = true;
+            } else {
+                // If there are parameters, let's remember them.
+                if (entry_item.first == "parameters") {
+                    parameters = entry_item.second;
+                }
             }
             }
         }
         }
         if (! lib_found) {
         if (! lib_found) {
@@ -299,19 +312,26 @@ HooksLibrariesParser::build(ConstElementPtr value) {
                 " name of the library"  <<
                 " name of the library"  <<
                 " (" << library_entry->getPosition() << ")");
                 " (" << library_entry->getPosition() << ")");
         }
         }
+
+        libraries_.push_back(make_pair(libname, parameters));
     }
     }
 
 
     // Check if the list of libraries has changed.  If not, nothing is done
     // Check if the list of libraries has changed.  If not, nothing is done
     // - the command "DhcpN libreload" is required to reload the same
     // - the command "DhcpN libreload" is required to reload the same
     // libraries (this prevents needless reloads when anything else in the
     // libraries (this prevents needless reloads when anything else in the
     // configuration is changed).
     // configuration is changed).
+
+    // We no longer rely on this. Parameters can change. And even if the
+    // parameters stay the same, they could point to files that could
+    // change.
     vector<string> current_libraries = HooksManager::getLibraryNames();
     vector<string> current_libraries = HooksManager::getLibraryNames();
-    if (current_libraries == libraries_) {
+    if (current_libraries.empty() && libraries_.empty()) {
         return;
         return;
     }
     }
 
 
     // Library list has changed, validate each of the libraries specified.
     // Library list has changed, validate each of the libraries specified.
-    vector<string> error_libs = HooksManager::validateLibraries(libraries_);
+    vector<string> lib_names = isc::hooks::extractNames(libraries_);
+    vector<string> error_libs = HooksManager::validateLibraries(lib_names);
     if (!error_libs.empty()) {
     if (!error_libs.empty()) {
 
 
         // Construct the list of libraries in error for the message.
         // Construct the list of libraries in error for the message.
@@ -334,15 +354,15 @@ HooksLibrariesParser::commit() {
     /// Commits the list of libraries to the configuration manager storage if
     /// Commits the list of libraries to the configuration manager storage if
     /// the list of libraries has changed.
     /// the list of libraries has changed.
     if (changed_) {
     if (changed_) {
-        // TODO Delete any stored CalloutHandles before reloading the
-        // libraries
+        /// @todo: Delete any stored CalloutHandles before reloading the
+        /// libraries
         HooksManager::loadLibraries(libraries_);
         HooksManager::loadLibraries(libraries_);
     }
     }
 }
 }
 
 
 // Method for testing
 // Method for testing
 void
 void
-HooksLibrariesParser::getLibraries(std::vector<std::string>& libraries,
+HooksLibrariesParser::getLibraries(isc::hooks::HookLibsCollection& libraries,
                                    bool& changed) {
                                    bool& changed) {
     libraries = libraries_;
     libraries = libraries_;
     changed = changed_;
     changed = changed_;

+ 6 - 8
src/lib/dhcpsrv/parsers/dhcp_parsers.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -16,6 +16,7 @@
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/subnet.h>
 #include <dhcpsrv/parsers/dhcp_config_parser.h>
 #include <dhcpsrv/parsers/dhcp_config_parser.h>
+#include <hooks/libinfo.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <util/optional_value.h>
 #include <util/optional_value.h>
 
 
@@ -35,9 +36,6 @@ typedef OptionSpaceContainer<OptionContainer, OptionDescriptor,
 /// @brief Shared pointer to option storage.
 /// @brief Shared pointer to option storage.
 typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
 typedef boost::shared_ptr<OptionStorage> OptionStoragePtr;
 
 
-/// @brief Shared pointer to collection of hooks libraries.
-typedef boost::shared_ptr<std::vector<std::string> > HooksLibsStoragePtr;
-
 /// @brief A template class that stores named elements of a given data type.
 /// @brief A template class that stores named elements of a given data type.
 ///
 ///
 /// This template class is provides data value storage for configuration
 /// This template class is provides data value storage for configuration
@@ -218,7 +216,7 @@ public:
     /// the list of current names can be obtained from the HooksManager) or it
     /// the list of current names can be obtained from the HooksManager) or it
     /// is non-null (this is the new list of names, reload the libraries when
     /// is non-null (this is the new list of names, reload the libraries when
     /// possible).
     /// possible).
-    HooksLibsStoragePtr hooks_libraries_;
+    isc::hooks::HookLibsCollectionPtr hooks_libraries_;
 
 
     /// @brief The parsing universe of this context.
     /// @brief The parsing universe of this context.
     Option::Universe universe_;
     Option::Universe universe_;
@@ -508,11 +506,11 @@ public:
     ///        new configuration.
     ///        new configuration.
     /// @param [out] changed true if the list is different from that currently
     /// @param [out] changed true if the list is different from that currently
     ///        loaded.
     ///        loaded.
-    void getLibraries(std::vector<std::string>& libraries, bool& changed);
+    void getLibraries(isc::hooks::HookLibsCollection& libraries, bool& changed);
 
 
 private:
 private:
-    /// List of hooks libraries.
-    std::vector<std::string> libraries_;
+    /// List of hooks libraries with their configuration parameters
+    isc::hooks::HookLibsCollection libraries_;
 
 
     /// Indicator flagging that the list of libraries has changed.
     /// Indicator flagging that the list of libraries has changed.
     bool changed_;
     bool changed_;

+ 6 - 1
src/lib/dhcpsrv/tests/Makefile.am

@@ -40,7 +40,7 @@ if HAVE_GTEST
 # to unexpected errors. For this reason, the --enable-static-link option is
 # to unexpected errors. For this reason, the --enable-static-link option is
 # ignored for unit tests built here.
 # ignored for unit tests built here.
 
 
-noinst_LTLIBRARIES = libco1.la libco2.la
+noinst_LTLIBRARIES = libco1.la libco2.la libco3.la
 
 
 # -rpath /nowhere is a hack to trigger libtool to not create a
 # -rpath /nowhere is a hack to trigger libtool to not create a
 # convenience archive, resulting in shared modules
 # convenience archive, resulting in shared modules
@@ -55,6 +55,11 @@ libco2_la_CXXFLAGS = $(AM_CXXFLAGS)
 libco2_la_CPPFLAGS = $(AM_CPPFLAGS)
 libco2_la_CPPFLAGS = $(AM_CPPFLAGS)
 libco2_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
 libco2_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
 
 
+libco3_la_SOURCES  = callout_params_library.cc
+libco3_la_CXXFLAGS = $(AM_CXXFLAGS)
+libco3_la_CPPFLAGS = $(AM_CPPFLAGS)
+libco3_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
 TESTS += libdhcpsrv_unittests
 TESTS += libdhcpsrv_unittests
 
 
 libdhcpsrv_unittests_SOURCES  = run_unittests.cc
 libdhcpsrv_unittests_SOURCES  = run_unittests.cc

+ 3 - 3
src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc

@@ -847,7 +847,7 @@ public:
             }
             }
         }
         }
 
 
-        vector<string> libraries; // no libraries at this time
+        HookLibsCollection libraries; // no libraries at this time
         HooksManager::loadLibraries(libraries);
         HooksManager::loadLibraries(libraries);
 
 
         // Install a callout: lease4_expire or lease6_expire.
         // Install a callout: lease4_expire or lease6_expire.
@@ -877,7 +877,7 @@ public:
             }
             }
         }
         }
 
 
-        vector<string> libraries; // no libraries at this time
+        HookLibsCollection libraries; // no libraries at this time
         HooksManager::loadLibraries(libraries);
         HooksManager::loadLibraries(libraries);
 
 
         // Install a callout: lease4_expire or lease6_expire.
         // Install a callout: lease4_expire or lease6_expire.
@@ -905,7 +905,7 @@ public:
             expire(i, 2000 - i);
             expire(i, 2000 - i);
         }
         }
 
 
-        vector<string> libraries;
+        HookLibsCollection libraries;
         HooksManager::loadLibraries(libraries);
         HooksManager::loadLibraries(libraries);
 
 
         // Install a callout: lease4_expire or lease6_expire. Each callout
         // Install a callout: lease4_expire or lease6_expire. Each callout

+ 5 - 5
src/lib/dhcpsrv/tests/alloc_engine_hooks_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -137,7 +137,7 @@ TEST_F(HookAllocEngine6Test, lease6_select) {
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
     // Initialize Hooks Manager
     // Initialize Hooks Manager
-    vector<string> libraries; // no libraries at this time
+    HookLibsCollection libraries; // no libraries at this time
     HooksManager::loadLibraries(libraries);
     HooksManager::loadLibraries(libraries);
 
 
     // Install pkt6_receive_callout
     // Install pkt6_receive_callout
@@ -207,7 +207,7 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) {
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
     // Initialize Hooks Manager
     // Initialize Hooks Manager
-    vector<string> libraries; // no libraries at this time
+    HookLibsCollection libraries; // no libraries at this time
     HooksManager::loadLibraries(libraries);
     HooksManager::loadLibraries(libraries);
 
 
     // Install a callout
     // Install a callout
@@ -368,7 +368,7 @@ TEST_F(HookAllocEngine4Test, lease4_select) {
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
     // Initialize Hooks Manager
     // Initialize Hooks Manager
-    vector<string> libraries; // no libraries at this time
+    HookLibsCollection libraries; // no libraries at this time
     HooksManager::loadLibraries(libraries);
     HooksManager::loadLibraries(libraries);
 
 
     // Install pkt4_receive_callout
     // Install pkt4_receive_callout
@@ -435,7 +435,7 @@ TEST_F(HookAllocEngine4Test, change_lease4_select) {
     ASSERT_TRUE(engine);
     ASSERT_TRUE(engine);
 
 
     // Initialize Hooks Manager
     // Initialize Hooks Manager
-    vector<string> libraries; // no libraries at this time
+    HookLibsCollection libraries; // no libraries at this time
     HooksManager::loadLibraries(libraries);
     HooksManager::loadLibraries(libraries);
 
 
     // Install a callout
     // Install a callout

+ 25 - 0
src/lib/dhcpsrv/tests/callout_params_library.cc

@@ -0,0 +1,25 @@
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief Callout Library
+///
+/// This is the source of a test library for the DHCP parser tests that
+/// specify parameters. It will attempt to obtain its own parameters.
+
+#include <config.h>
+
+#include <hooks/hooks.h>
+
+extern "C" {
+
+// Framework functions
+int
+version() {
+    return (KEA_HOOKS_VERSION);
+}
+
+};

+ 95 - 29
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc

@@ -1034,7 +1034,7 @@ TEST_F(ParseConfigTest, noHooksLibraries) {
     ASSERT_TRUE(rcode == 0) << error_text_;
     ASSERT_TRUE(rcode == 0) << error_text_;
 
 
     // Check that the parser recorded nothing.
     // Check that the parser recorded nothing.
-    std::vector<std::string> libraries;
+    isc::hooks::HookLibsCollection libraries;
     bool changed;
     bool changed;
     hooks_libraries_parser_->getLibraries(libraries, changed);
     hooks_libraries_parser_->getLibraries(libraries, changed);
     EXPECT_FALSE(changed);
     EXPECT_FALSE(changed);
@@ -1059,12 +1059,12 @@ TEST_F(ParseConfigTest, oneHooksLibrary) {
     ASSERT_TRUE(rcode == 0) << error_text_;
     ASSERT_TRUE(rcode == 0) << error_text_;
 
 
     // Check that the parser recorded a single library.
     // Check that the parser recorded a single library.
-    std::vector<std::string> libraries;
+    HookLibsCollection libraries;
     bool changed;
     bool changed;
     hooks_libraries_parser_->getLibraries(libraries, changed);
     hooks_libraries_parser_->getLibraries(libraries, changed);
     EXPECT_TRUE(changed);
     EXPECT_TRUE(changed);
     ASSERT_EQ(1, libraries.size());
     ASSERT_EQ(1, libraries.size());
-    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0]);
+    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
 
 
     // Check that the change was propagated to the hooks manager.
     // Check that the change was propagated to the hooks manager.
     hooks_libraries = HooksManager::getLibraryNames();
     hooks_libraries = HooksManager::getLibraryNames();
@@ -1087,13 +1087,13 @@ TEST_F(ParseConfigTest, twoHooksLibraries) {
     ASSERT_TRUE(rcode == 0) << error_text_;
     ASSERT_TRUE(rcode == 0) << error_text_;
 
 
     // Check that the parser recorded two libraries in the expected order.
     // Check that the parser recorded two libraries in the expected order.
-    std::vector<std::string> libraries;
+    HookLibsCollection libraries;
     bool changed;
     bool changed;
     hooks_libraries_parser_->getLibraries(libraries, changed);
     hooks_libraries_parser_->getLibraries(libraries, changed);
     EXPECT_TRUE(changed);
     EXPECT_TRUE(changed);
     ASSERT_EQ(2, libraries.size());
     ASSERT_EQ(2, libraries.size());
-    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0]);
-    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1]);
+    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
+    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1].first);
 
 
     // Verify that the change was propagated to the hooks manager.
     // Verify that the change was propagated to the hooks manager.
     hooks_libraries = HooksManager::getLibraryNames();
     hooks_libraries = HooksManager::getLibraryNames();
@@ -1124,15 +1124,16 @@ TEST_F(ParseConfigTest, reconfigureSameHooksLibraries) {
     rcode = parseConfiguration(config);
     rcode = parseConfiguration(config);
     ASSERT_TRUE(rcode == 0) << error_text_;
     ASSERT_TRUE(rcode == 0) << error_text_;
 
 
-    // The list has not changed between the two parse operations and this is
-    // what we should see.
-    std::vector<std::string> libraries;
+    // The list has not changed between the two parse operations. However,
+    // the paramters (or the files they could point to) could have
+    // changed, so the libraries are reloaded anyway.
+    HookLibsCollection libraries;
     bool changed;
     bool changed;
     hooks_libraries_parser_->getLibraries(libraries, changed);
     hooks_libraries_parser_->getLibraries(libraries, changed);
-    EXPECT_FALSE(changed);
+    EXPECT_TRUE(changed);
     ASSERT_EQ(2, libraries.size());
     ASSERT_EQ(2, libraries.size());
-    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0]);
-    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1]);
+    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
+    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1].first);
 
 
     // ... and check that the same two libraries are still loaded in the
     // ... and check that the same two libraries are still loaded in the
     // HooksManager.
     // HooksManager.
@@ -1167,13 +1168,13 @@ TEST_F(ParseConfigTest, reconfigureReverseHooksLibraries) {
     ASSERT_TRUE(rcode == 0) << error_text_;
     ASSERT_TRUE(rcode == 0) << error_text_;
 
 
     // The list has changed, and this is what we should see.
     // The list has changed, and this is what we should see.
-    std::vector<std::string> libraries;
+    HookLibsCollection libraries;
     bool changed;
     bool changed;
     hooks_libraries_parser_->getLibraries(libraries, changed);
     hooks_libraries_parser_->getLibraries(libraries, changed);
     EXPECT_TRUE(changed);
     EXPECT_TRUE(changed);
     ASSERT_EQ(2, libraries.size());
     ASSERT_EQ(2, libraries.size());
-    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[0]);
-    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[1]);
+    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[0].first);
+    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[1].first);
 
 
     // ... and check that this was propagated to the HooksManager.
     // ... and check that this was propagated to the HooksManager.
     hooks_libraries = HooksManager::getLibraryNames();
     hooks_libraries = HooksManager::getLibraryNames();
@@ -1206,7 +1207,7 @@ TEST_F(ParseConfigTest, reconfigureZeroHooksLibraries) {
     ASSERT_TRUE(rcode == 0) << error_text_;
     ASSERT_TRUE(rcode == 0) << error_text_;
 
 
     // The list has changed, and this is what we should see.
     // The list has changed, and this is what we should see.
-    std::vector<std::string> libraries;
+    HookLibsCollection libraries;
     bool changed;
     bool changed;
     hooks_libraries_parser_->getLibraries(libraries, changed);
     hooks_libraries_parser_->getLibraries(libraries, changed);
     EXPECT_TRUE(changed);
     EXPECT_TRUE(changed);
@@ -1240,14 +1241,14 @@ TEST_F(ParseConfigTest, invalidHooksLibraries) {
 
 
     // Check that the parser recorded the names but, as they were in error,
     // Check that the parser recorded the names but, as they were in error,
     // does not flag them as changed.
     // does not flag them as changed.
-    vector<string> libraries;
+    HookLibsCollection libraries;
     bool changed;
     bool changed;
     hooks_libraries_parser_->getLibraries(libraries, changed);
     hooks_libraries_parser_->getLibraries(libraries, changed);
     EXPECT_FALSE(changed);
     EXPECT_FALSE(changed);
     ASSERT_EQ(3, libraries.size());
     ASSERT_EQ(3, libraries.size());
-    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0]);
-    EXPECT_EQ(NOT_PRESENT_LIBRARY, libraries[1]);
-    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[2]);
+    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
+    EXPECT_EQ(NOT_PRESENT_LIBRARY, libraries[1].first);
+    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[2].first);
 
 
     // ...and check it did not alter the libraries in the hooks manager.
     // ...and check it did not alter the libraries in the hooks manager.
     hooks_libraries = HooksManager::getLibraryNames();
     hooks_libraries = HooksManager::getLibraryNames();
@@ -1284,14 +1285,14 @@ TEST_F(ParseConfigTest, reconfigureInvalidHooksLibraries) {
 
 
     // Check that the parser recorded the names but, as the library set was
     // Check that the parser recorded the names but, as the library set was
     // incorrect, did not mark the configuration as changed.
     // incorrect, did not mark the configuration as changed.
-    vector<string> libraries;
+    HookLibsCollection libraries;
     bool changed;
     bool changed;
     hooks_libraries_parser_->getLibraries(libraries, changed);
     hooks_libraries_parser_->getLibraries(libraries, changed);
     EXPECT_FALSE(changed);
     EXPECT_FALSE(changed);
     ASSERT_EQ(3, libraries.size());
     ASSERT_EQ(3, libraries.size());
-    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0]);
-    EXPECT_EQ(NOT_PRESENT_LIBRARY, libraries[1]);
-    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[2]);
+    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
+    EXPECT_EQ(NOT_PRESENT_LIBRARY, libraries[1].first);
+    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[2].first);
 
 
     // ... but check that the hooks manager was not updated with the incorrect
     // ... but check that the hooks manager was not updated with the incorrect
     // names.
     // names.
@@ -1373,6 +1374,71 @@ TEST_F(ParseConfigTest, invalidSyntaxHooksLibraries) {
         "Error text returned from parse failure is " << error_text_;
         "Error text returned from parse failure is " << error_text_;
 }
 }
 
 
+// Check that some parameters may have configuration parameters configured.
+TEST_F(ParseConfigTest, HooksLibrariesParameters) {
+    // Check that no libraries are currently loaded
+    vector<string> hooks_libraries = HooksManager::getLibraryNames();
+    EXPECT_TRUE(hooks_libraries.empty());
+
+    // Configuration string.  This contains an invalid library which should
+    // trigger an error in the "build" stage.
+    const std::string config = setHooksLibrariesConfig(CALLOUT_LIBRARY_1,
+                                                       CALLOUT_LIBRARY_2,
+                                                       CALLOUT_PARAMS_LIBRARY);
+
+    // Verify that the configuration fails to parse. (Syntactically it's OK,
+    // but the library is invalid).
+    const int rcode = parseConfiguration(config);
+    ASSERT_EQ(0, rcode);
+
+    // Check that the parser recorded the names.
+    HookLibsCollection libraries;
+    bool changed = false;
+    hooks_libraries_parser_->getLibraries(libraries, changed);
+    EXPECT_TRUE(changed);
+    ASSERT_EQ(3, libraries.size());
+    EXPECT_EQ(CALLOUT_LIBRARY_1, libraries[0].first);
+    EXPECT_EQ(CALLOUT_LIBRARY_2, libraries[1].first);
+    EXPECT_EQ(CALLOUT_PARAMS_LIBRARY, libraries[2].first);
+
+    // Also, check that the third library has its parameters specified.
+    // They were set by setHooksLibrariesConfig. The first has no
+    // parameters, the second one has an empty map and the third
+    // one has actual parameters.
+    EXPECT_FALSE(libraries[0].second);
+    EXPECT_TRUE(libraries[1].second);
+    ASSERT_TRUE(libraries[2].second);
+
+    // Ok, get the parameter for the third library.
+    ConstElementPtr params = libraries[2].second;
+
+    // It must be a map.
+    ASSERT_EQ(Element::map, params->getType());
+
+    // This map should have 3 parameters:
+    // - svalue (and will expect its value to be "string value")
+    // - ivalue (and will expect its value to be 42)
+    // - bvalue (and will expect its value to be true)
+    ConstElementPtr svalue = params->get("svalue");
+    ConstElementPtr ivalue = params->get("ivalue");
+    ConstElementPtr bvalue = params->get("bvalue");
+
+    // There should be no extra parameters.
+    EXPECT_FALSE(params->get("nonexistent"));
+
+    ASSERT_TRUE(svalue);
+    ASSERT_TRUE(ivalue);
+    ASSERT_TRUE(bvalue);
+
+    ASSERT_EQ(Element::string, svalue->getType());
+    ASSERT_EQ(Element::integer, ivalue->getType());
+    ASSERT_EQ(Element::boolean, bvalue->getType());
+
+    EXPECT_EQ("string value", svalue->stringValue());
+    EXPECT_EQ(42, ivalue->intValue());
+    EXPECT_EQ(true, bvalue->boolValue());
+}
+
 /// @brief Checks that a valid, enabled D2 client configuration works correctly.
 /// @brief Checks that a valid, enabled D2 client configuration works correctly.
 TEST_F(ParseConfigTest, validD2Config) {
 TEST_F(ParseConfigTest, validD2Config) {
 
 
@@ -1895,8 +1961,8 @@ public:
 
 
 
 
         // Allocate container for hooks libraries and add one library name.
         // Allocate container for hooks libraries and add one library name.
-        ctx.hooks_libraries_.reset(new std::vector<std::string>());
-        ctx.hooks_libraries_->push_back("library1");
+        ctx.hooks_libraries_.reset(new std::vector<HookLibInfo>());
+        ctx.hooks_libraries_->push_back(make_pair("library1", ConstElementPtr()));
 
 
         // We will use ctx_new to assign another context to it or copy
         // We will use ctx_new to assign another context to it or copy
         // construct.
         // construct.
@@ -1977,7 +2043,7 @@ public:
         ASSERT_TRUE(ctx_new->hooks_libraries_);
         ASSERT_TRUE(ctx_new->hooks_libraries_);
         {
         {
             ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
             ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
-            EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0]);
+            EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0].first);
         }
         }
 
 
         // New context has the same universe.
         // New context has the same universe.
@@ -2091,9 +2157,9 @@ public:
         // Change the list of libraries. this should not affect the list in the
         // Change the list of libraries. this should not affect the list in the
         // new context.
         // new context.
         ctx.hooks_libraries_->clear();
         ctx.hooks_libraries_->clear();
-        ctx.hooks_libraries_->push_back("library2");
+        ctx.hooks_libraries_->push_back(make_pair("library2", ConstElementPtr()));
         ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
         ASSERT_EQ(1, ctx_new->hooks_libraries_->size());
-        EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0]);
+        EXPECT_EQ("library1", (*ctx_new->hooks_libraries_)[0].first);
 
 
         // Change the universe. This should not affect the universe value in the
         // Change the universe. This should not affect the universe value in the
         // new context.
         // new context.

+ 6 - 0
src/lib/dhcpsrv/tests/test_libraries.h.in

@@ -20,6 +20,12 @@ namespace {
 static const char* CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
 static const char* CALLOUT_LIBRARY_1 = "@abs_builddir@/.libs/libco1.so";
 static const char* CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
 static const char* CALLOUT_LIBRARY_2 = "@abs_builddir@/.libs/libco2.so";
 
 
+// This library will try to get the following parameters:
+// - svalue (and will expect its value to be "string value")
+// - ivalue (and will expect its value to be 42)
+// - bvalue (and will expect its value to be true)
+static const char* CALLOUT_PARAMS_LIBRARY = "@abs_builddir@/.libs/libco3.so";
+
 // Name of a library which is not present.
 // Name of a library which is not present.
 static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
 static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
 
 

+ 2 - 0
src/lib/hooks/Makefile.am

@@ -43,6 +43,7 @@ libkea_hooks_la_SOURCES += callout_manager.cc callout_manager.h
 libkea_hooks_la_SOURCES += hooks.h
 libkea_hooks_la_SOURCES += hooks.h
 libkea_hooks_la_SOURCES += hooks_log.cc hooks_log.h
 libkea_hooks_la_SOURCES += hooks_log.cc hooks_log.h
 libkea_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
 libkea_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
+libkea_hooks_la_SOURCES += libinfo.cc libinfo.h
 libkea_hooks_la_SOURCES += library_handle.cc library_handle.h
 libkea_hooks_la_SOURCES += library_handle.cc library_handle.h
 libkea_hooks_la_SOURCES += library_manager.cc library_manager.h
 libkea_hooks_la_SOURCES += library_manager.cc library_manager.h
 libkea_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h
 libkea_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h
@@ -58,6 +59,7 @@ libkea_hooks_la_LIBADD  =
 libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
 libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
 libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
 libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/util/threads/libkea-threads.la
 libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
 libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
+libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
 libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
 libkea_hooks_la_LIBADD += $(LOG4CPLUS_LIBS)
 libkea_hooks_la_LIBADD += $(LOG4CPLUS_LIBS)
 
 

+ 15 - 4
src/lib/hooks/hooks_manager.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -65,7 +65,7 @@ HooksManager::callCallouts(int index, CalloutHandle& handle) {
 // (if present) and load new ones.
 // (if present) and load new ones.
 
 
 bool
 bool
-HooksManager::loadLibrariesInternal(const std::vector<std::string>& libraries) {
+HooksManager::loadLibrariesInternal(const HookLibsCollection& libraries) {
     // Unload current set of libraries (if any are loaded).
     // Unload current set of libraries (if any are loaded).
     unloadLibrariesInternal();
     unloadLibrariesInternal();
 
 
@@ -87,7 +87,7 @@ HooksManager::loadLibrariesInternal(const std::vector<std::string>& libraries) {
 }
 }
 
 
 bool
 bool
-HooksManager::loadLibraries(const std::vector<std::string>& libraries) {
+HooksManager::loadLibraries(const HookLibsCollection& libraries) {
     return (getHooksManager().loadLibrariesInternal(libraries));
     return (getHooksManager().loadLibrariesInternal(libraries));
 }
 }
 
 
@@ -131,11 +131,22 @@ HooksManager::getLibraryNamesInternal() const {
                            : std::vector<std::string>());
                            : std::vector<std::string>());
 }
 }
 
 
+HookLibsCollection
+HooksManager::getLibraryInfoInternal() const {
+    return (lm_collection_ ? lm_collection_->getLibraryInfo()
+            : HookLibsCollection());
+}
+
 std::vector<std::string>
 std::vector<std::string>
 HooksManager::getLibraryNames() {
 HooksManager::getLibraryNames() {
     return (getHooksManager().getLibraryNamesInternal());
     return (getHooksManager().getLibraryNamesInternal());
 }
 }
 
 
+HookLibsCollection
+HooksManager::getLibraryInfo() {
+    return (getHooksManager().getLibraryInfoInternal());
+}
+
 // Perform conditional initialization if nothing is loaded.
 // Perform conditional initialization if nothing is loaded.
 
 
 void
 void
@@ -143,7 +154,7 @@ HooksManager::performConditionalInitialization() {
 
 
     // Nothing present, so create the collection with any empty set of
     // Nothing present, so create the collection with any empty set of
     // libraries, and get the CalloutManager.
     // libraries, and get the CalloutManager.
-    vector<string> libraries;
+    HookLibsCollection libraries;
     lm_collection_.reset(new LibraryManagerCollection(libraries));
     lm_collection_.reset(new LibraryManagerCollection(libraries));
     lm_collection_->loadLibraries();
     lm_collection_->loadLibraries();
 
 

+ 14 - 3
src/lib/hooks/hooks_manager.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,7 @@
 #define HOOKS_MANAGER_H
 #define HOOKS_MANAGER_H
 
 
 #include <hooks/server_hooks.h>
 #include <hooks/server_hooks.h>
+#include <hooks/libinfo.h>
 
 
 #include <boost/noncopyable.hpp>
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
@@ -61,7 +62,7 @@ public:
     /// @return true if all libraries loaded without a problem, false if one or
     /// @return true if all libraries loaded without a problem, false if one or
     ///        more libraries failed to load.  In the latter case, message will
     ///        more libraries failed to load.  In the latter case, message will
     ///        be logged that give the reason.
     ///        be logged that give the reason.
-    static bool loadLibraries(const std::vector<std::string>& libraries);
+    static bool loadLibraries(const HookLibsCollection& libraries);
 
 
     /// @brief Unload libraries
     /// @brief Unload libraries
     ///
     ///
@@ -170,6 +171,13 @@ public:
     /// @return List of loaded library names.
     /// @return List of loaded library names.
     static std::vector<std::string> getLibraryNames();
     static std::vector<std::string> getLibraryNames();
 
 
+    /// @brief Return list of loaded libraries with its parameters.
+    ///
+    /// Returns the names of the loaded libraries and their parameters.
+    ///
+    /// @return List of loaded libraries (names + parameters)
+    static HookLibsCollection getLibraryInfo();
+
     /// @brief Validate library list
     /// @brief Validate library list
     ///
     ///
     /// For each library passed to it, checks that the library can be opened
     /// For each library passed to it, checks that the library can be opened
@@ -224,7 +232,7 @@ private:
     /// @return true if all libraries loaded without a problem, false if one or
     /// @return true if all libraries loaded without a problem, false if one or
     ///        more libraries failed to load.  In the latter case, message will
     ///        more libraries failed to load.  In the latter case, message will
     ///        be logged that give the reason.
     ///        be logged that give the reason.
-    bool loadLibrariesInternal(const std::vector<std::string>& libraries);
+    bool loadLibrariesInternal(const HookLibsCollection& libraries);
 
 
     /// @brief Unload libraries
     /// @brief Unload libraries
     void unloadLibrariesInternal();
     void unloadLibrariesInternal();
@@ -266,6 +274,9 @@ private:
     /// @return List of loaded library names.
     /// @return List of loaded library names.
     std::vector<std::string> getLibraryNamesInternal() const;
     std::vector<std::string> getLibraryNamesInternal() const;
 
 
+    /// @brief Return a collection of library names with parameters.
+    HookLibsCollection getLibraryInfoInternal() const;
+
     //@}
     //@}
 
 
     /// @brief Initialization to No Libraries
     /// @brief Initialization to No Libraries

+ 140 - 7
src/lib/hooks/hooks_user.dox

@@ -168,7 +168,7 @@ to namespaces.
 As the names suggest, "load" is called when a library is loaded and
 As the names suggest, "load" is called when a library is loaded and
 "unload" called when it is unloaded.  (It is always guaranteed that
 "unload" called when it is unloaded.  (It is always guaranteed that
 "load" is called: "unload" may not be called in some circumstances,
 "load" is called: "unload" may not be called in some circumstances,
-e.g. if the system shuts down abnormally.)  These functions are the
+e.g., if the system shuts down abnormally.)  These functions are the
 places where any library-wide resources are allocated and deallocated.
 places where any library-wide resources are allocated and deallocated.
 "load" is also the place where any callouts with non-standard names
 "load" is also the place where any callouts with non-standard names
 (names that are not hook point names) can be registered:
 (names that are not hook point names) can be registered:
@@ -344,7 +344,7 @@ an argument as an @c int and the callout attempted to retrieve it as a
 @c long, an exception would be thrown even though any value that can
 @c long, an exception would be thrown even though any value that can
 be stored in an @c int will fit into a @c long.  This restriction also
 be stored in an @c int will fit into a @c long.  This restriction also
 applies the "const" attribute but only as applied to data pointed to by
 applies the "const" attribute but only as applied to data pointed to by
-pointers, e.g. if an argument is defined as a @c char*, an exception will
+pointers, e.g., if an argument is defined as a @c char*, an exception will
 be thrown if an attempt is made to retrieve it into a variable of type
 be thrown if an attempt is made to retrieve it into a variable of type
 @c const @c char*.  (However, if an argument is set as a @c const @c int,
 @c const @c char*.  (However, if an argument is set as a @c const @c int,
 it can be retrieved into an @c int.)  The documentation of each hook
 it can be retrieved into an @c int.)  The documentation of each hook
@@ -434,7 +434,7 @@ status (see @ref hooksdgNextStep) was provided by
 a boolean flag called "Skip". However, since it only allowed to either continue
 a boolean flag called "Skip". However, since it only allowed to either continue
 or skip the next processing step and was not extensible to other decisions,
 or skip the next processing step and was not extensible to other decisions,
 setSkip(bool) call was replaced with a setStatus(enum) in Kea 1.0. This
 setSkip(bool) call was replaced with a setStatus(enum) in Kea 1.0. This
-new approach is extensible. If we decide to add new results (e.g. WAIT
+new approach is extensible. If we decide to add new results (e.g., WAIT
 or RATELIMIT), we will be able to do so without changing the API again.
 or RATELIMIT), we will be able to do so without changing the API again.
 
 
 If you have your hooks libraries that take advantage of skip flag, migrating
 If you have your hooks libraries that take advantage of skip flag, migrating
@@ -473,7 +473,7 @@ if (status == CalloutHandle::NEXT_STEP_SKIP) {
 @subsubsection hooksdgCalloutContext Per-Request Context
 @subsubsection hooksdgCalloutContext Per-Request Context
 
 
 Although the Kea modules can be characterized as handling a single
 Although the Kea modules can be characterized as handling a single
-packet at a time - e.g. the DHCPv4 server receives a DHCPDISCOVER packet,
+packet at a time - e.g., the DHCPv4 server receives a DHCPDISCOVER packet,
 processes it and responds with an DHCPOFFER, this may not always be true.
 processes it and responds with an DHCPOFFER, this may not always be true.
 Future developments may have the server processing multiple packets
 Future developments may have the server processing multiple packets
 simultaneously, or to suspend processing on a packet and resume it at
 simultaneously, or to suspend processing on a packet and resume it at
@@ -962,7 +962,7 @@ only shared pointers have the correct behavior for the copy operation.)
 As briefly mentioned in @ref hooksdgExampleCallouts, the standard is for
 As briefly mentioned in @ref hooksdgExampleCallouts, the standard is for
 callouts in the user library to have the same name as the name of the
 callouts in the user library to have the same name as the name of the
 hook to which they are being attached.  This convention was followed
 hook to which they are being attached.  This convention was followed
-in the tutorial, e.g.  the callout that needed to be attached to the
+in the tutorial, e.g.,  the callout that needed to be attached to the
 "pkt4_receive" hook was named pkt4_receive.
 "pkt4_receive" hook was named pkt4_receive.
 
 
 The reason for this convention is that when the library is loaded, the
 The reason for this convention is that when the library is loaded, the
@@ -1026,7 +1026,7 @@ a hook point.  Although it is likely to be rare for user code to need to
 do this, there may be instances where it make sense.
 do this, there may be instances where it make sense.
 
 
 To register multiple callouts on a hook, just call
 To register multiple callouts on a hook, just call
-@c LibraryHandle::registerCallout multiple times on the same hook, e.g.
+@c LibraryHandle::registerCallout multiple times on the same hook, e.g.,
 
 
 @code
 @code
     libhandle.registerCallout("pkt4_receive", classify);
     libhandle.registerCallout("pkt4_receive", classify);
@@ -1184,7 +1184,7 @@ may be present, but will not affect the context values set by a library's
 callouts.
 callouts.
 
 
 Configuring multiple libraries just requires listing the libraries
 Configuring multiple libraries just requires listing the libraries
-as separate elements of the hooks-libraries configuration element, e.g.
+as separate elements of the hooks-libraries configuration element, e.g.,
 
 
 @code
 @code
 "Dhcp4": {
 "Dhcp4": {
@@ -1271,4 +1271,137 @@ int load() {
 
 
 }
 }
 @endcode
 @endcode
+
+@subsection hooksdgHooksConfig Configuring Hooks Libraries
+
+Sometimes it is useful for the hook library to have some configuration parameters.
+This capability was introduced in Kea 1.1. This is often convenient to follow
+generic Kea configuration approach rather than invent your own configuration
+logic. Consider the following example:
+
+@code
+"hooks-libraries": [
+    {
+        "library": "/opt/first.so"
+    },
+    {
+        "library": "/opt/second.so",
+        "parameters": {
+        }
+    },
+    {
+        "library": "/opt/third.so",
+        "parameters": {
+            "mail": "spam@example.com",
+            "floor": 13,
+            "debug": false,
+            "users": [ "alice", "bob", "charlie" ],
+            "languages": {
+                "french": "bonjour",
+                "klingon": "yl'el"
+            }
+        }
+    }
+]
+@endcode
+
+This example has three hook libraries configured. The first and and second have
+no parameters. Note that parameters map is optional, but it's perfectly ok to
+specify it as an empty map. The third library is more interesting. It has five
+parameters specified. The first one called 'mail' is a string. The second one
+is an integer and the third one is boolean. Fourth and fifth parameters are
+slightly more complicated as they are a list and a map respectively. JSON
+structures can be nested if necessary, e.g., you can have a list, which contains
+maps, maps that contain maps that contain other maps etc. Any valid JSON
+structure can be represented. One important limitation here is that the top
+level "parameters" structure must either be a map or not present at all.
+
+Those parameters can be accessed in load() method. Passed isc::hooks::LibraryHandle
+object has a method called getParameter that returns an instance of
+isc::data::ConstElementPtr or null pointer if there was no parameter specified. This pointer
+will point to an object derived from isc::data::Element class. For detailed
+explanation how to use those objects, see isc::data::Element class.
+
+Here's a brief overview of how to use those elements:
+
+ - x = getParameter("mail") will return instance of isc::data::StringElement. The content
+   can be accessed with x->stringValue() and will return std::string.
+ - x = getParameter("floor") will return an instance of isc::data::IntElement.
+   The content can be accessed with x->intValue() and will return int.
+ - x = getParameter("debug") will return an instance of isc::data::BoolElement.
+   Its value can be accessed with x->boolValue() and will return bool.
+ - x = getParameter("users") will return an instance of isc::data::ListElement.
+   Its content can be accessed with the following methods:
+   x->size(), x->get(index)
+ - x = getParameter("watch-list") will return an instance of isc::data::MapElement.
+   Its content can be accessed with the following methods:
+   x->find("klingon"), x->contains("french"), x->size()
+
+Keep in mind that the user can structure his config file incorrectly.
+Remember to check if the structure has the expected type before using type specific
+method. For example calling stringValue on IntElement will throw an exception.
+You can do this by calling isc::data::Element::getType.
+
+Here's an example that obtains all of the parameters specified above.
+If you want to get nested elements, Element::get(index) and Element::find(name)
+will return ElementPtr, which can be iterated in similar manner.
+
+@code
+int load(LibraryHandle& handle) {
+    ConstElementPtr mail  = handle.getParameter("mail");
+    ConstElementPtr floor = handle.getParameter("floor");
+    ConstElementPtr debug = handle.getParameter("debug");
+    ConstElementPtr users = handle.getParameter("users");
+    ConstElementPtr lang  = handle.getParameter("languages");
+
+    // String handling example
+    if (!mail) {
+        // Handle missing 'mail' parameter here.
+        return (1);
+    }
+    if (mail->getType() != Element::string) {
+        // Handle incorrect 'mail' parameter here.
+        return (1);
+    }
+    std::string mail_str = mail->stringValue();
+
+    // In the following examples safety checks are omitted for clarity.
+    // Make sure you do it properly similar to mail example above
+    // or you risk dereferencing null pointer or at least throwing
+    // an exception!
+
+    // Integer handling example
+    int floor_num = floor->intValue();
+
+    // Boolean handling example
+    bool debug_flag = debug->boolValue();
+
+    // List handling example
+    std::cout << "There are " << users->size() << " users defined." << std::endl;
+    for (int i = 0; i < users->size(); i++) {
+        ConstElementPtr user = users->get(i);
+        std::cout << "User " << user->stringValue() << std::endl;
+    }
+
+    // Map handling example
+    std::cout << "There are " << lang->size() << " languages defined." << std::endl;
+    if (lang->contains("french")) {
+       std::cout << "One of them is French!" << std::endl;
+    }
+    ConstElementPtr greeting = lang->find("klingon");
+    if (greeting) {
+       std::cout << "Lt. Worf says " << greeting->stringValue() << std::endl;
+    }
+
+    // All validation steps were successful. The library has all the parameters
+    // it needs, so we should report a success.
+    return (0);
+}
+@endcode
+
+A good sources of examples could be unit-tests in file src/lib/cc/tests/data_unittests.cc
+which are dedicated to isc::data::Element testing and src/lib/hooks/tests/callout_params_library.cc,
+which is an example library used in testing. This library expects exactly 3 parameters:
+svalue (which is a string), ivalue (which is an integer) and bvalue (which is a boolean).
+
 */
 */

+ 28 - 0
src/lib/hooks/libinfo.cc

@@ -0,0 +1,28 @@
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <hooks/libinfo.h>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Extracts names from HookLibsCollection
+///
+/// @param libraries Hook libraries collection
+/// @return vector of strings with library names
+std::vector<std::string>
+extractNames(const isc::hooks::HookLibsCollection& libraries) {
+    std::vector<std::string> names;
+
+    for (isc::hooks::HookLibsCollection::const_iterator it = libraries.begin();
+         it != libraries.end(); ++it) {
+        names.push_back(it->first);
+    }
+    return (names);
+}
+
+};
+};

+ 42 - 0
src/lib/hooks/libinfo.h

@@ -0,0 +1,42 @@
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HOOKS_LIBINFO_H
+#define HOOKS_LIBINFO_H
+
+#include <cc/data.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+#include <utility>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Entity that holds information about hook libraries and their
+/// parameters.
+///
+/// The first parameter is a full filename with path to the library.
+/// The second parameter is a map of parameters that configure the
+/// library. There's always at least one parameter: "library", which
+/// contains the library name.
+typedef std::pair<std::string, data::ConstElementPtr> HookLibInfo;
+
+/// @brief A storage for information about hook libraries.
+typedef std::vector<HookLibInfo> HookLibsCollection;
+
+/// @brief Shared pointer to collection of hooks libraries.
+typedef boost::shared_ptr<HookLibsCollection> HookLibsCollectionPtr;
+
+/// @brief Extracts library names from full library information structure
+std::vector<std::string> extractNames(const HookLibsCollection& libinfo);
+
+};
+};
+
+#endif

+ 35 - 0
src/lib/hooks/library_handle.cc

@@ -6,6 +6,7 @@
 
 
 #include <hooks/callout_manager.h>
 #include <hooks/callout_manager.h>
 #include <hooks/library_handle.h>
 #include <hooks/library_handle.h>
+#include <hooks/hooks_manager.h>
 
 
 namespace isc {
 namespace isc {
 namespace hooks {
 namespace hooks {
@@ -64,5 +65,39 @@ LibraryHandle::deregisterAllCallouts(const std::string& name) {
     return (status);
     return (status);
 }
 }
 
 
+isc::data::ConstElementPtr
+LibraryHandle::getParameter(const std::string& name) {
+    HookLibsCollection libinfo = HooksManager::getHooksManager().getLibraryInfo();
+
+    int index = index_;
+
+    if (index == -1) {
+        // -1 means that current index is stored in CalloutManager.
+        // So let's get the index from there. See comment for
+        // LibraryHandle::index_.
+        index = callout_manager_->getLibraryIndex();
+    }
+
+    if ((index > libinfo.size()) || (index <= 0)) {
+        // Something is very wrong here. The library index is out of bounds.
+        // However, this is user facing interface, so we should not throw here.
+        return (isc::data::ConstElementPtr());
+    }
+
+    // Some indexes have special meaning:
+    // * 0 - pre-user library callout
+    // * 1..numlib - indexes for actual libraries
+    // * INT_MAX - post-user library callout
+
+    // Try to find appropriate parameter. May return null pointer
+    isc::data::ConstElementPtr params = libinfo[index - 1].second;
+    if (!params) {
+        return (isc::data::ConstElementPtr());
+    }
+
+    // May return null pointer if there's no parameter.
+    return (params->get(name));
+}
+
 } // namespace util
 } // namespace util
 } // namespace isc
 } // namespace isc

+ 64 - 0
src/lib/hooks/library_handle.h

@@ -8,6 +8,7 @@
 #define LIBRARY_HANDLE_H
 #define LIBRARY_HANDLE_H
 
 
 #include <string>
 #include <string>
+#include <cc/data.h>
 
 
 namespace isc {
 namespace isc {
 namespace hooks {
 namespace hooks {
@@ -107,6 +108,69 @@ public:
     /// @throw NoSuchHook Thrown if the hook name is unrecognized.
     /// @throw NoSuchHook Thrown if the hook name is unrecognized.
     bool deregisterAllCallouts(const std::string& name);
     bool deregisterAllCallouts(const std::string& name);
 
 
+
+    /// @brief Returns configuration parameter for the library.
+    ///
+    /// This method returns configuration parameters specified in the
+    /// configuration file. Here's the example. Let's assume that there
+    /// are two hook libraries configured:
+    ///
+    /// "hooks-libraries": [
+    ///     {
+    ///        "library": "/opt/charging.so",
+    ///        "parameters": {}
+    ///    },
+    ///    {
+    ///        "library": "/opt/local/notification.so",
+    ///        "parameters": {
+    ///            "mail": "alarm@example.com",
+    ///            "floor": 42,
+    ///            "debug": false,
+    ///            "users": [ "alice", "bob", "charlie" ],
+    ///            "header": {
+    ///                "french": "bonjour",
+    ///                "klingon": "yl'el"
+    ///            }
+    ///        }
+    ///    }
+    ///]
+    ///
+    /// The first library has no parameters, so regardles of the name
+    /// specified, for that library getParameter will always return NULL.
+    ///
+    /// For the second paramter, depending the following calls will return:
+    /// - x = getParameter("mail") will return instance of
+    ///   isc::data::StringElement. The content can be accessed with
+    ///   x->stringValue() and will return std::string.
+    /// - x = getParameter("floor") will return an instance of isc::data::IntElement.
+    ///   The content can be accessed with x->intValue() and will return int.
+    /// - x = getParameter("debug") will return an instance of isc::data::BoolElement.
+    ///   Its value can be accessed with x->boolValue() and will return bool.
+    /// - x = getParameter("users") will return an instance of ListElement.
+    ///   Its content can be accessed with the following methods:
+    ///   x->size(), x->get(index)
+    /// - x = getParameter("header") will return an instance of isc::data::MapElement.
+    ///   Its content can be accessed with the following methods:
+    ///   x->find("klingon"), x->contains("french"), x->size()
+    ///
+    /// For more examples and complete API, see documentation for
+    /// @ref isc::data::Element class and its derivatives:
+    /// - @ref isc::data::IntElement
+    /// - @ref isc::data::DoubleElement
+    /// - @ref isc::data::BoolElement
+    /// - @ref isc::data::StringElement
+    /// - @ref isc::data::ListElement
+    /// - @ref isc::data::MapElement
+    ///
+    /// Another good way to learn how to use Element interface is to look at the
+    /// unittests in data_unittests.cc.
+    ///
+    /// @param name text name of the parameter.
+    /// @return ElementPtr representing requested parameter (may be null, if
+    ///         there is no such parameter.)
+    isc::data::ConstElementPtr
+    getParameter(const std::string& name);
+
 private:
 private:
     /// @brief Copy constructor
     /// @brief Copy constructor
     ///
     ///

+ 11 - 1
src/lib/hooks/library_manager_collection.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -34,6 +34,16 @@ LibraryManagerCollection::getCalloutManager() const {
     return (callout_manager_);
     return (callout_manager_);
 }
 }
 
 
+LibraryManagerCollection::LibraryManagerCollection(const HookLibsCollection& libraries)
+    :library_info_(libraries) {
+
+    // We need to split hook libs into library names and library parameters.
+    for (HookLibsCollection::const_iterator it = libraries.begin();
+         it != libraries.end(); ++it) {
+        library_names_.push_back(it->first);
+    }
+}
+
 // Load a set of libraries
 // Load a set of libraries
 
 
 bool
 bool

+ 17 - 5
src/lib/hooks/library_manager_collection.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -10,6 +10,7 @@
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
+#include <hooks/libinfo.h>
 
 
 #include <vector>
 #include <vector>
 
 
@@ -71,10 +72,9 @@ public:
     /// @brief Constructor
     /// @brief Constructor
     ///
     ///
     /// @param libraries List of libraries that this collection will manage.
     /// @param libraries List of libraries that this collection will manage.
-    ///        The order of the libraries is important.
-    LibraryManagerCollection(const std::vector<std::string>& libraries)
-        : library_names_(libraries)
-    {}
+    ///        The order of the libraries is important. It holds the library
+    ///        names and its configuration parameters.
+    LibraryManagerCollection(const HookLibsCollection& libraries);
 
 
     /// @brief Destructor
     /// @brief Destructor
     ///
     ///
@@ -115,6 +115,14 @@ public:
         return (library_names_);
         return (library_names_);
     }
     }
 
 
+    /// @brief Returns library info
+    ///
+    /// Returns a collection of libraries, each entry consisting of a library
+    /// name + all its parameters.
+    HookLibsCollection getLibraryInfo() const {
+        return (library_info_);
+    }
+
     /// @brief Get number of loaded libraries
     /// @brief Get number of loaded libraries
     ///
     ///
     /// Mainly for testing, this returns the number of libraries that are
     /// Mainly for testing, this returns the number of libraries that are
@@ -151,6 +159,10 @@ private:
     /// Vector of library managers
     /// Vector of library managers
     std::vector<boost::shared_ptr<LibraryManager> > lib_managers_;
     std::vector<boost::shared_ptr<LibraryManager> > lib_managers_;
 
 
+    /// Vector of library information. Each piece of information
+    /// consists of a pair of (library name, library parameters)
+    HookLibsCollection                              library_info_;
+
     /// Callout manager to be associated with the libraries
     /// Callout manager to be associated with the libraries
     boost::shared_ptr<CalloutManager>               callout_manager_;
     boost::shared_ptr<CalloutManager>               callout_manager_;
 };
 };

+ 9 - 1
src/lib/hooks/tests/Makefile.am

@@ -47,7 +47,7 @@ if HAVE_GTEST
 # ignored for unit tests built here.
 # ignored for unit tests built here.
 
 
 noinst_LTLIBRARIES = libnvl.la  libivl.la libfxl.la libbcl.la liblcl.la \
 noinst_LTLIBRARIES = libnvl.la  libivl.la libfxl.la libbcl.la liblcl.la \
-                     liblecl.la libucl.la libfcl.la
+                     liblecl.la libucl.la libfcl.la libpcl.la
 
 
 # -rpath /nowhere is a hack to trigger libtool to not create a
 # -rpath /nowhere is a hack to trigger libtool to not create a
 # convenience archive, resulting in shared modules
 # convenience archive, resulting in shared modules
@@ -102,6 +102,13 @@ libfcl_la_CXXFLAGS = $(AM_CXXFLAGS)
 libfcl_la_CPPFLAGS = $(AM_CPPFLAGS)
 libfcl_la_CPPFLAGS = $(AM_CPPFLAGS)
 libfcl_la_LDFLAGS  = -avoid-version -export-dynamic -module -rpath /nowhere
 libfcl_la_LDFLAGS  = -avoid-version -export-dynamic -module -rpath /nowhere
 
 
+# The parameters checking callout library - expects
+libpcl_la_SOURCES  = callout_params_library.cc
+libpcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libpcl_la_CPPFLAGS = $(AM_CPPFLAGS)
+libpcl_la_LDFLAGS  = -avoid-version -export-dynamic -module -rpath /nowhere
+libpcl_la_LDFLAGS += $(top_builddir)/src/lib/util/libkea-util.la
+
 TESTS += run_unittests
 TESTS += run_unittests
 run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES  = run_unittests.cc
 run_unittests_SOURCES += callout_handle_unittest.cc
 run_unittests_SOURCES += callout_handle_unittest.cc
@@ -126,6 +133,7 @@ endif
 run_unittests_LDADD     = $(AM_LDADD)
 run_unittests_LDADD     = $(AM_LDADD)
 run_unittests_LDADD    += $(ALL_LIBS)
 run_unittests_LDADD    += $(ALL_LIBS)
 run_unittests_LDADD    += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
 run_unittests_LDADD    += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD    += $(top_builddir)/src/lib/cc/libkea-cc.la
 run_unittests_LDADD    += $(GTEST_LDADD)
 run_unittests_LDADD    += $(GTEST_LDADD)
 # As noted in configure.ac, libtool doesn't work perfectly with Darwin: it
 # As noted in configure.ac, libtool doesn't work perfectly with Darwin: it
 # embeds the final install path in dynamic libraries and loadable modules refer
 # embeds the final install path in dynamic libraries and loadable modules refer

+ 106 - 0
src/lib/hooks/tests/callout_params_library.cc

@@ -0,0 +1,106 @@
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief Callout Library
+///
+/// This is the source of a test library for the DHCP parser tests that
+/// specify parameters. It will attempt to obtain its own parameters.
+
+#include <config.h>
+#include <hooks/hooks.h>
+#include <iostream>
+
+using namespace isc::hooks;
+using namespace isc::data;
+
+extern "C" {
+
+// Framework functions
+int
+version() {
+    return (KEA_HOOKS_VERSION);
+}
+
+/// @brief This method will be called when the hook library is loaded
+///
+/// While its primary usage is for unit-testing, it also doubles as an
+/// illustration referred to from Hooks Developer's Guide. As such, please
+/// keep it simple, tidy and try to avoid referencing unnecessary code.
+/// Parts of it can be used as copy-paste examples.
+///
+/// @param handle passed by the hooks framework
+/// @return 0 if load was successful, non-zero for errors
+int load(LibraryHandle& handle) {
+    ConstElementPtr string_elem  = handle.getParameter("svalue");
+    ConstElementPtr int_elem     = handle.getParameter("ivalue");
+    ConstElementPtr bool_elem    = handle.getParameter("bvalue");
+    ConstElementPtr nonexistent  = handle.getParameter("nonexistent");
+
+    // String handling example.
+    if (!string_elem) {
+        // Parameter was not specified at all.
+        return (1);
+    }
+
+    if (string_elem->getType() != Element::string) {
+        // Parameter is specified, but it's not a string.
+        return (2);
+    }
+
+    std::string str = string_elem->stringValue();
+    if (str != "string value") {
+        // Parameter is specified, is a string, but has unexpected value.
+        //
+        // This library is used for testing, so it expects exact value of the
+        // parameter. Normal library would likely use whatever value user
+        // specified.
+        return (3);
+    }
+
+    // Integer handling example
+    if (!int_elem) {
+        // Parameter was not specified at all.
+        return (4);
+    }
+
+    if (int_elem->getType() != Element::integer) {
+        // Parameter is specified, but it's not an integer.
+        return (5);
+    }
+
+    int int_value = int_elem->intValue();
+    if (int_value != 42) {
+        // Parameter specified, is an integer, but has a value different than
+        // expected.
+        return (6);
+    }
+
+    // Boolean handling example
+    if (!bool_elem) {
+        // Parameter was not specified at all.
+        return (7);
+    }
+
+    if (bool_elem->getType() != Element::boolean) {
+        // Parameter is specified, but it's not a boolean.
+        return (8);
+    }
+
+    bool flag = bool_elem->boolValue();
+    if (flag != true) {
+        // Parameter specified, is a boolean, but has a value different than
+        // expected.
+        return (9);
+    }
+
+    // All validation steps were successful. The library has all the parameters
+    // it needs, so we should report a success.
+    return (0);
+}
+    
+
+};

+ 68 - 26
src/lib/hooks/tests/hooks_manager_unittest.cc

@@ -10,6 +10,7 @@
 
 
 #include <hooks/tests/common_test_class.h>
 #include <hooks/tests/common_test_class.h>
 #include <hooks/tests/test_libraries.h>
 #include <hooks/tests/test_libraries.h>
+#include <cc/data.h>
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
@@ -17,9 +18,9 @@
 #include <algorithm>
 #include <algorithm>
 #include <string>
 #include <string>
 
 
-
 using namespace isc;
 using namespace isc;
 using namespace isc::hooks;
 using namespace isc::hooks;
+using namespace isc::data;
 using namespace std;
 using namespace std;
 
 
 namespace {
 namespace {
@@ -110,9 +111,11 @@ private:
 TEST_F(HooksManagerTest, LoadLibraries) {
 TEST_F(HooksManagerTest, LoadLibraries) {
 
 
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
-    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
 
 
     // Load the libraries.
     // Load the libraries.
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
@@ -152,10 +155,13 @@ TEST_F(HooksManagerTest, LoadLibraries) {
 TEST_F(HooksManagerTest, LoadLibrariesWithError) {
 TEST_F(HooksManagerTest, LoadLibrariesWithError) {
 
 
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
-    library_names.push_back(std::string(INCORRECT_VERSION_LIBRARY));
-    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(INCORRECT_VERSION_LIBRARY),
+                                      data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
 
 
     // Load the libraries.  We expect a failure return because one of the
     // Load the libraries.  We expect a failure return because one of the
     // libraries fails to load.
     // libraries fails to load.
@@ -168,8 +174,9 @@ TEST_F(HooksManagerTest, LoadLibrariesWithError) {
 TEST_F(HooksManagerTest, CalloutHandleUnloadLibrary) {
 TEST_F(HooksManagerTest, CalloutHandleUnloadLibrary) {
 
 
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
 
 
     // Load the libraries.
     // Load the libraries.
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
@@ -202,8 +209,9 @@ TEST_F(HooksManagerTest, CalloutHandleUnloadLibrary) {
 TEST_F(HooksManagerTest, CalloutHandleLoadLibrary) {
 TEST_F(HooksManagerTest, CalloutHandleLoadLibrary) {
 
 
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
 
 
     // Load the libraries.
     // Load the libraries.
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
@@ -225,8 +233,9 @@ TEST_F(HooksManagerTest, CalloutHandleLoadLibrary) {
     // Load a new library that implements the calculation
     // Load a new library that implements the calculation
     //
     //
     // r3 = (10 + d1) * d2 - d3
     // r3 = (10 + d1) * d2 - d3
-    std::vector<std::string> new_library_names;
-    new_library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    HookLibsCollection new_library_names;
+    new_library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                          data::ConstElementPtr()));
 
 
     // Load the libraries.
     // Load the libraries.
     EXPECT_TRUE(HooksManager::loadLibraries(new_library_names));
     EXPECT_TRUE(HooksManager::loadLibraries(new_library_names));
@@ -247,9 +256,11 @@ TEST_F(HooksManagerTest, CalloutHandleLoadLibrary) {
 TEST_F(HooksManagerTest, ReloadSameLibraries) {
 TEST_F(HooksManagerTest, ReloadSameLibraries) {
 
 
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
-    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
 
 
     // Load the libraries.
     // Load the libraries.
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
@@ -273,9 +284,11 @@ TEST_F(HooksManagerTest, ReloadSameLibraries) {
 TEST_F(HooksManagerTest, ReloadLibrariesReverseOrder) {
 TEST_F(HooksManagerTest, ReloadLibrariesReverseOrder) {
 
 
     // Set up the list of libraries to be loaded and load them.
     // Set up the list of libraries to be loaded and load them.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
-    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
 
 
     // Execute the callouts.  The first library implements the calculation.
     // Execute the callouts.  The first library implements the calculation.
@@ -335,8 +348,9 @@ testPostCallout(CalloutHandle& handle) {
 TEST_F(HooksManagerTest, PrePostCalloutTest) {
 TEST_F(HooksManagerTest, PrePostCalloutTest) {
 
 
     // Load a single library.
     // Load a single library.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
 
 
     // Load the pre- and post- callouts.
     // Load the pre- and post- callouts.
@@ -431,9 +445,11 @@ TEST_F(HooksManagerTest, RegisterHooks) {
 TEST_F(HooksManagerTest, LibraryNames) {
 TEST_F(HooksManagerTest, LibraryNames) {
 
 
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
-    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
 
 
     // Check the names before the libraries are loaded.
     // Check the names before the libraries are loaded.
     std::vector<std::string> loaded_names = HooksManager::getLibraryNames();
     std::vector<std::string> loaded_names = HooksManager::getLibraryNames();
@@ -442,7 +458,7 @@ TEST_F(HooksManagerTest, LibraryNames) {
     // Load the libraries and check the names again.
     // Load the libraries and check the names again.
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
     EXPECT_TRUE(HooksManager::loadLibraries(library_names));
     loaded_names = HooksManager::getLibraryNames();
     loaded_names = HooksManager::getLibraryNames();
-    EXPECT_TRUE(library_names == loaded_names);
+    EXPECT_TRUE(extractNames(library_names) == loaded_names);
 
 
     // Unload the libraries and check again.
     // Unload the libraries and check again.
     EXPECT_NO_THROW(HooksManager::unloadLibraries());
     EXPECT_NO_THROW(HooksManager::unloadLibraries());
@@ -523,5 +539,31 @@ TEST_F(HooksManagerTest, validateLibraries) {
     EXPECT_TRUE(failed == expected_failures);
     EXPECT_TRUE(failed == expected_failures);
 }
 }
 
 
+// This test verifies that the specified parameters are accessed properly.
+TEST_F(HooksManagerTest, LibraryParameters) {
+
+    // Prepare paramters for the callout parameters library.
+    ElementPtr params = Element::createMap();
+    params->set("svalue", Element::create("string value"));
+    params->set("ivalue", Element::create(42));
+    params->set("bvalue", Element::create(true));
+
+    // Set up the list of libraries to be loaded.
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(CALLOUT_PARAMS_LIBRARY),
+                                      params));
+
+    // Load the libraries. Note that callout params library checks if
+    // all mandatory parameters are there, so if anything is missing, its
+    // load() function will return error, thus causing the library to not
+    // load.
+    EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+    // Try unloading the libraries.
+    EXPECT_NO_THROW(HooksManager::unloadLibraries());
+}
+
 
 
 } // Anonymous namespace
 } // Anonymous namespace

+ 68 - 16
src/lib/hooks/tests/library_manager_collection_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,7 @@
 #include <hooks/callout_manager.h>
 #include <hooks/callout_manager.h>
 #include <hooks/library_manager.h>
 #include <hooks/library_manager.h>
 #include <hooks/library_manager_collection.h>
 #include <hooks/library_manager_collection.h>
+#include <hooks/libinfo.h>
 
 
 #include <hooks/tests/common_test_class.h>
 #include <hooks/tests/common_test_class.h>
 #include <hooks/tests/test_libraries.h>
 #include <hooks/tests/test_libraries.h>
@@ -30,6 +31,7 @@ namespace {
 class LibraryManagerCollectionTest : public ::testing::Test,
 class LibraryManagerCollectionTest : public ::testing::Test,
                                      public HooksCommonTestClass {
                                      public HooksCommonTestClass {
 private:
 private:
+
     /// To avoid unused variable errors
     /// To avoid unused variable errors
     std::string dummy(int i) {
     std::string dummy(int i) {
         if (i == 0) {
         if (i == 0) {
@@ -54,7 +56,7 @@ public:
     ///
     ///
     /// @param List of libraries that this collection will manage.  The order
     /// @param List of libraries that this collection will manage.  The order
     ///        of the libraries is important.
     ///        of the libraries is important.
-    PublicLibraryManagerCollection(const std::vector<std::string>& libraries)
+    PublicLibraryManagerCollection(const HookLibsCollection& libraries)
         : LibraryManagerCollection(libraries)
         : LibraryManagerCollection(libraries)
     {}
     {}
 
 
@@ -69,9 +71,11 @@ public:
 TEST_F(LibraryManagerCollectionTest, LoadLibraries) {
 TEST_F(LibraryManagerCollectionTest, LoadLibraries) {
 
 
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
-    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
 
 
     // Set up the library manager collection and get the callout manager we'll
     // Set up the library manager collection and get the callout manager we'll
     // be using.
     // be using.
@@ -118,10 +122,13 @@ TEST_F(LibraryManagerCollectionTest, LoadLibraries) {
 TEST_F(LibraryManagerCollectionTest, LoadLibrariesWithError) {
 TEST_F(LibraryManagerCollectionTest, LoadLibrariesWithError) {
 
 
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
-    library_names.push_back(std::string(INCORRECT_VERSION_LIBRARY));
-    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    HookLibsCollection library_names;
+    library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                       data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(INCORRECT_VERSION_LIBRARY),
+                                      data::ConstElementPtr()));
+    library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                      data::ConstElementPtr()));
 
 
     // Set up the library manager collection and get the callout manager we'll
     // Set up the library manager collection and get the callout manager we'll
     // be using.
     // be using.
@@ -139,7 +146,7 @@ TEST_F(LibraryManagerCollectionTest, LoadLibrariesWithError) {
 
 
 TEST_F(LibraryManagerCollectionTest, NoLibrariesLoaded) {
 TEST_F(LibraryManagerCollectionTest, NoLibrariesLoaded) {
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
+    HookLibsCollection library_names;
 
 
     // Set up the library manager collection and get the callout manager we'll
     // Set up the library manager collection and get the callout manager we'll
     // be using.
     // be using.
@@ -159,23 +166,25 @@ TEST_F(LibraryManagerCollectionTest, NoLibrariesLoaded) {
 TEST_F(LibraryManagerCollectionTest, LibraryNames) {
 TEST_F(LibraryManagerCollectionTest, LibraryNames) {
 
 
     // Set up the list of libraries to be loaded.
     // Set up the list of libraries to be loaded.
-    std::vector<std::string> library_names;
-    library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
-    library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+    HookLibsCollection libraries;
+    libraries.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+                                  data::ConstElementPtr()));
+    libraries.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+                                  data::ConstElementPtr()));
 
 
     // Set up the library manager collection and get the callout manager we'll
     // Set up the library manager collection and get the callout manager we'll
     // be using.
     // be using.
-    PublicLibraryManagerCollection lm_collection(library_names);
+    PublicLibraryManagerCollection lm_collection(libraries);
 
 
     // Check the names before the libraries are loaded.
     // Check the names before the libraries are loaded.
     std::vector<std::string> collection_names = lm_collection.getLibraryNames();
     std::vector<std::string> collection_names = lm_collection.getLibraryNames();
-    EXPECT_TRUE(library_names == collection_names);
+    EXPECT_TRUE(extractNames(libraries) == collection_names);
 
 
     // Load the libraries and check the names again.
     // Load the libraries and check the names again.
     EXPECT_TRUE(lm_collection.loadLibraries());
     EXPECT_TRUE(lm_collection.loadLibraries());
     EXPECT_EQ(2, lm_collection.getLoadedLibraryCount());
     EXPECT_EQ(2, lm_collection.getLoadedLibraryCount());
     collection_names = lm_collection.getLibraryNames();
     collection_names = lm_collection.getLibraryNames();
-    EXPECT_TRUE(library_names == collection_names);
+    EXPECT_TRUE(extractNames(libraries) == collection_names);
 }
 }
 
 
 // Test the library validation function.
 // Test the library validation function.
@@ -204,6 +213,7 @@ TEST_F(LibraryManagerCollectionTest, validateLibraries) {
     libraries.push_back(BASIC_CALLOUT_LIBRARY);
     libraries.push_back(BASIC_CALLOUT_LIBRARY);
     libraries.push_back(FULL_CALLOUT_LIBRARY);
     libraries.push_back(FULL_CALLOUT_LIBRARY);
     libraries.push_back(UNLOAD_CALLOUT_LIBRARY);
     libraries.push_back(UNLOAD_CALLOUT_LIBRARY);
+    libraries.push_back(CALLOUT_PARAMS_LIBRARY);
 
 
     failed = LibraryManagerCollection::validateLibraries(libraries);
     failed = LibraryManagerCollection::validateLibraries(libraries);
     EXPECT_TRUE(failed.empty());
     EXPECT_TRUE(failed.empty());
@@ -251,4 +261,46 @@ TEST_F(LibraryManagerCollectionTest, validateLibraries) {
     EXPECT_TRUE(failed == expected_failures);
     EXPECT_TRUE(failed == expected_failures);
 }
 }
 
 
+// This test verifies if getLibraryNames and getLibraryInfo are returning
+// expected values if there are no libraries configured.
+TEST_F(LibraryManagerCollectionTest, libraryGetEmpty) {
+
+    HookLibsCollection empty;
+    boost::shared_ptr<LibraryManagerCollection> mgr;
+
+    // Instantiate library manager collection with no libraries
+    EXPECT_NO_THROW(mgr.reset(new LibraryManagerCollection(empty)));
+
+    // Check that getLibraryInfo returns empty list properly.
+    HookLibsCollection returned = mgr->getLibraryInfo();
+    EXPECT_TRUE(returned.empty());
+
+    // Check that getLibraryNames return empty list, too.
+    vector<string> names(3, "rubbish"); // just put something in it.
+    EXPECT_NO_THROW(names = mgr->getLibraryNames());
+    EXPECT_TRUE(names.empty());
+}
+
+// This test verifies if getLibraryNames and getLibraryInfo are returning
+// expected values when there are libraries configured.
+TEST_F(LibraryManagerCollectionTest, libraryGet) {
+    using namespace data;
+
+    HookLibsCollection libs;
+    ElementPtr param1(Element::fromJSON("{ \"param1\": \"foo\" }"));
+    ElementPtr param2(Element::fromJSON("{ \"param2\": \"bar\" }"));
+    libs.push_back(make_pair("libone", param1));
+    libs.push_back(make_pair("libtwo", param2));
+
+    boost::shared_ptr<LibraryManagerCollection> mgr;
+    EXPECT_NO_THROW(mgr.reset(new LibraryManagerCollection(libs)));
+    EXPECT_TRUE(libs == mgr->getLibraryInfo());
+
+    vector<string> exp_names;
+    exp_names.push_back("libone");
+    exp_names.push_back("libtwo");
+
+    EXPECT_TRUE(exp_names == mgr->getLibraryNames());
+}
+
 } // Anonymous namespace
 } // Anonymous namespace

+ 1 - 0
src/lib/hooks/tests/library_manager_unittest.cc

@@ -560,6 +560,7 @@ TEST_F(LibraryManagerTest, validateLibraries) {
     EXPECT_FALSE(LibraryManager::validateLibrary(NOT_PRESENT_LIBRARY));
     EXPECT_FALSE(LibraryManager::validateLibrary(NOT_PRESENT_LIBRARY));
     EXPECT_FALSE(LibraryManager::validateLibrary(NO_VERSION_LIBRARY));
     EXPECT_FALSE(LibraryManager::validateLibrary(NO_VERSION_LIBRARY));
     EXPECT_TRUE(LibraryManager::validateLibrary(UNLOAD_CALLOUT_LIBRARY));
     EXPECT_TRUE(LibraryManager::validateLibrary(UNLOAD_CALLOUT_LIBRARY));
+    EXPECT_TRUE(LibraryManager::validateLibrary(CALLOUT_PARAMS_LIBRARY));
 }
 }
 
 
 // Check that log messages are properly registered and unregistered.
 // Check that log messages are properly registered and unregistered.

+ 3 - 0
src/lib/hooks/tests/test_libraries.h.in

@@ -46,6 +46,9 @@ static const char* NO_VERSION_LIBRARY = "@abs_builddir@/.libs/libnvl.so";
 // Library where there is an unload() function.
 // Library where there is an unload() function.
 static const char* UNLOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libucl.so";
 static const char* UNLOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libucl.so";
 
 
+// Library where parameters are checked.
+static const char* CALLOUT_PARAMS_LIBRARY = "@abs_builddir@/.libs/libpcl.so";
+
 } // anonymous namespace
 } // anonymous namespace