BIND 10 master, updated. dd01c785f0d8eb616ee4179299dc53e0d2c0015c [master] Added ChangeLog entry for #2980.
BIND 10 source code commits
bind10-changes at lists.isc.org
Wed Jul 10 10:15:23 UTC 2013
The branch, master has been updated
via dd01c785f0d8eb616ee4179299dc53e0d2c0015c (commit)
via 82c997a72890a12af135ace5b9ee100e41c5534e (commit)
via 3430cf9ae922d5af87fef9ca4c202db482f29391 (commit)
via 016c6b97353dd57158d1da0f574912f2823e74e1 (commit)
via bfcf73f7b86036c774b90e1df948e12399269385 (commit)
via 5b7b932c171d48009f722ea20ecdbe852ad2b8aa (commit)
via 43ea555f6ae497ec40672b951d8a00c437b89c0a (commit)
via 04e36988a16fb36ab84ec548a174b148924ed0d7 (commit)
via b61e894db16aa458c9d3d405210c357e1fdde657 (commit)
via dc342c8d7ff4eff388bc596e625e54ac9617c09c (commit)
via 57a25a015e23dca348c1e1a3c5821d64981277b1 (commit)
via d63e340ac318b68e1a04458ba9669f159d6eb508 (commit)
via 1954874770a322999e04315397c25a56cd1f1b3a (commit)
via c77a043a8a6f6216499cc9697d5881c65bd9ad2d (commit)
via 58875c2089c61e5174db855530e9de237920c669 (commit)
via 2bde65105b86a512aa798cac5bcad723bafb38c9 (commit)
via 418f35f58458de0480d6a58299f06721481d9196 (commit)
via ba91cff481836c5c8c1993174727646213cea60b (commit)
via d72ba709f11dee0d0bb90868fc042bd8caaf8626 (commit)
via b4bf719aa55ad684dbfa5faa39aa93ebf89bac18 (commit)
via 49ee4eaf84ad72a044cf4957af016d1e8e15cab0 (commit)
via db3f2d81e975c2c82e575ba2947340f520f763a9 (commit)
via cc8e7feaf34f1028438854453b4811d7283852c0 (commit)
via 8b7ae5faff5a12fb2841e8b35b43253c0d67c22f (commit)
via 7c20771f4296820b7efc22b19a0783e7d226736a (commit)
via 9a306b863a628351a84cfafdabe5700d4840be6a (commit)
via dc50d73a4980ad1900e0445a1ea0dd46bf37245c (commit)
via 017278ca8084b9420fa94a2e9cc44aac9ebcae04 (commit)
via e61a5182aa295c0a96428d09e1d259c9b8b09d0d (commit)
via 8e94ab0a3baabd2b20f181243626168df4a56ec1 (commit)
via 390c1a763df3dbfcf0f24394464c0db89aaede9f (commit)
via 0a4612cfe59ee2149b885550e467f4de42236bbb (commit)
via 03db051a722775e59b803bd44e02d1faad2a0a9a (commit)
via 5b6251fb9bb2cd793fd9f86719d1e359aa427cca (commit)
via 6e6cff79121a939cc961c8f36a489cbb2d0b7074 (commit)
via 7ef14b47d3bd773155e4b6d9c5a739b0213e8f28 (commit)
from 4ae6b8af324d8e7bcd13f3e6ce5a632f3d704ccf (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit dd01c785f0d8eb616ee4179299dc53e0d2c0015c
Author: Stephen Morris <stephen at isc.org>
Date: Wed Jul 10 11:14:51 2013 +0100
[master] Added ChangeLog entry for #2980.
commit 82c997a72890a12af135ace5b9ee100e41c5534e
Merge: 4ae6b8a 3430cf9
Author: Stephen Morris <stephen at isc.org>
Date: Wed Jul 10 10:57:44 2013 +0100
[master] Merge branch 'trac2980'
-----------------------------------------------------------------------
Summary of changes:
ChangeLog | 6 +
configure.ac | 16 +
doc/Doxyfile | 1 +
doc/devel/mainpage.dox | 45 +-
src/lib/Makefile.am | 2 +-
src/lib/hooks/Makefile.am | 56 ++
src/lib/{util => }/hooks/callout_handle.cc | 57 +-
src/lib/{util => }/hooks/callout_handle.h | 54 +-
src/lib/{util => }/hooks/callout_manager.cc | 100 +++-
src/lib/{util => }/hooks/callout_manager.h | 185 +++++--
.../interprocess_util.cc => hooks/hooks.h} | 44 +-
src/lib/hooks/hooks_component_developer.dox | 483 +++++++++++++++++
src/lib/{nsas/nsas_log.cc => hooks/hooks_log.cc} | 8 +-
.../{resolve/resolve_log.h => hooks/hooks_log.h} | 39 +-
src/lib/hooks/hooks_manager.cc | 173 ++++++
src/lib/hooks/hooks_manager.h | 276 ++++++++++
src/lib/hooks/hooks_messages.mes | 167 ++++++
src/lib/hooks/library_handle.cc | 76 +++
src/lib/{util => }/hooks/library_handle.h | 38 +-
src/lib/hooks/library_manager.cc | 292 ++++++++++
src/lib/hooks/library_manager.h | 190 +++++++
src/lib/hooks/library_manager_collection.cc | 114 ++++
src/lib/hooks/library_manager_collection.h | 133 +++++
src/lib/hooks/pointer_converter.h | 121 +++++
src/lib/{util => }/hooks/server_hooks.cc | 95 ++--
src/lib/{util => }/hooks/server_hooks.h | 134 ++---
src/lib/hooks/tests/Makefile.am | 104 ++++
src/lib/hooks/tests/basic_callout_library.cc | 115 ++++
.../tests/callout_handle_unittest.cc | 22 +-
.../tests/callout_manager_unittest.cc | 209 ++++++--
src/lib/hooks/tests/common_test_class.h | 142 +++++
.../hooks/tests/framework_exception_library.cc} | 41 +-
src/lib/hooks/tests/full_callout_library.cc | 137 +++++
src/lib/{util => hooks}/tests/handles_unittest.cc | 91 +++-
src/lib/hooks/tests/hooks_manager_unittest.cc | 454 ++++++++++++++++
.../hooks/tests/incorrect_version_library.cc} | 25 +-
.../tests/library_manager_collection_unittest.cc | 184 +++++++
src/lib/hooks/tests/library_manager_unittest.cc | 555 ++++++++++++++++++++
src/lib/hooks/tests/load_callout_library.cc | 119 +++++
.../hooks/tests/load_error_callout_library.cc} | 43 +-
.../hooks/tests/marker_file.h.in} | 21 +-
.../hooks/tests/no_version_library.cc} | 24 +-
src/lib/{nsas => hooks}/tests/run_unittests.cc | 3 +-
.../{util => hooks}/tests/server_hooks_unittest.cc | 174 ++----
src/lib/hooks/tests/test_libraries.h.in | 79 +++
.../hooks/tests/unload_callout_library.cc} | 54 +-
src/lib/util/Makefile.am | 4 -
src/lib/util/hooks/library_handle.cc | 39 --
src/lib/util/tests/Makefile.am | 4 -
49 files changed, 4955 insertions(+), 593 deletions(-)
create mode 100644 src/lib/hooks/Makefile.am
rename src/lib/{util => }/hooks/callout_handle.cc (64%)
rename src/lib/{util => }/hooks/callout_handle.h (85%)
rename src/lib/{util => }/hooks/callout_manager.cc (66%)
rename src/lib/{util => }/hooks/callout_manager.h (59%)
copy src/lib/{util/unittests/interprocess_util.cc => hooks/hooks.h} (56%)
create mode 100644 src/lib/hooks/hooks_component_developer.dox
copy src/lib/{nsas/nsas_log.cc => hooks/hooks_log.cc} (88%)
copy src/lib/{resolve/resolve_log.h => hooks/hooks_log.h} (56%)
create mode 100644 src/lib/hooks/hooks_manager.cc
create mode 100644 src/lib/hooks/hooks_manager.h
create mode 100644 src/lib/hooks/hooks_messages.mes
create mode 100644 src/lib/hooks/library_handle.cc
rename src/lib/{util => }/hooks/library_handle.h (72%)
create mode 100644 src/lib/hooks/library_manager.cc
create mode 100644 src/lib/hooks/library_manager.h
create mode 100644 src/lib/hooks/library_manager_collection.cc
create mode 100644 src/lib/hooks/library_manager_collection.h
create mode 100644 src/lib/hooks/pointer_converter.h
rename src/lib/{util => }/hooks/server_hooks.cc (70%)
rename src/lib/{util => }/hooks/server_hooks.h (55%)
create mode 100644 src/lib/hooks/tests/Makefile.am
create mode 100644 src/lib/hooks/tests/basic_callout_library.cc
rename src/lib/{util => hooks}/tests/callout_handle_unittest.cc (95%)
rename src/lib/{util => hooks}/tests/callout_manager_unittest.cc (82%)
create mode 100644 src/lib/hooks/tests/common_test_class.h
copy src/{bin/resolver/bench/dummy_work.h => lib/hooks/tests/framework_exception_library.cc} (59%)
create mode 100644 src/lib/hooks/tests/full_callout_library.cc
rename src/lib/{util => hooks}/tests/handles_unittest.cc (94%)
create mode 100644 src/lib/hooks/tests/hooks_manager_unittest.cc
copy src/{bin/resolver/bench/dummy_work.cc => lib/hooks/tests/incorrect_version_library.cc} (65%)
create mode 100644 src/lib/hooks/tests/library_manager_collection_unittest.cc
create mode 100644 src/lib/hooks/tests/library_manager_unittest.cc
create mode 100644 src/lib/hooks/tests/load_callout_library.cc
copy src/{bin/resolver/bench/dummy_work.h => lib/hooks/tests/load_error_callout_library.cc} (59%)
copy src/{bin/resolver/bench/dummy_work.cc => lib/hooks/tests/marker_file.h.in} (75%)
copy src/{bin/resolver/bench/dummy_work.cc => lib/hooks/tests/no_version_library.cc} (72%)
copy src/lib/{nsas => hooks}/tests/run_unittests.cc (99%)
rename src/lib/{util => hooks}/tests/server_hooks_unittest.cc (52%)
create mode 100644 src/lib/hooks/tests/test_libraries.h.in
copy src/{bin/resolver/bench/naive_resolver.h => lib/hooks/tests/unload_callout_library.cc} (52%)
delete mode 100644 src/lib/util/hooks/library_handle.cc
-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index 278ec73..1354073 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+641. [func] stephen
+ Added the hooks framework. This allows shared libraries of
+ user-written functions to be loaded at run-time and the
+ functions called during packet processing.
+ (Trac #2980, git 82c997a72890a12af135ace5b9ee100e41c5534e)
+
640. [func] marcin
b10-dhcp-ddns: Implemented DNSClient class which implements
asynchronous DNS updates using UDP. The TCP and TSIG support
diff --git a/configure.ac b/configure.ac
index bbe27ce..72a825e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -430,6 +430,7 @@ AC_SUBST(B10_CXXFLAGS)
AC_SEARCH_LIBS(inet_pton, [nsl])
AC_SEARCH_LIBS(recvfrom, [socket])
AC_SEARCH_LIBS(nanosleep, [rt])
+AC_SEARCH_LIBS(dlsym, [dl])
# Checks for header files.
@@ -469,6 +470,17 @@ AM_COND_IF([OS_BSD], [AC_DEFINE([OS_BSD], [1], [Running on BSD?])])
AM_CONDITIONAL(OS_SOLARIS, test $OS_TYPE = Solaris)
AM_COND_IF([OS_SOLARIS], [AC_DEFINE([OS_SOLARIS], [1], [Running on Solaris?])])
+# Deal with variants
+AM_CONDITIONAL(OS_FREEBSD, test $system = FreeBSD)
+AM_COND_IF([OS_FREEBSD], [AC_DEFINE([OS_FREEBSD], [1], [Running on FreeBSD?])])
+AM_CONDITIONAL(OS_NETBSD, test $system = NetBSD)
+AM_COND_IF([OS_NETBSD], [AC_DEFINE([OS_NETBSD], [1], [Running on NetBSD?])])
+AM_CONDITIONAL(OS_OPENBSD, test $system = OpenBSD)
+AM_COND_IF([OS_OPENBSD], [AC_DEFINE([OS_OPENBSD], [1], [Running on OpenBSD?])])
+AM_CONDITIONAL(OS_OSX, test $system = Darwin)
+AM_COND_IF([OS_OSX], [AC_DEFINE([OS_OSX], [1], [Running on OSX?])])
+
+
AC_MSG_CHECKING(for sa_len in struct sockaddr)
AC_TRY_COMPILE([
#include <sys/types.h>
@@ -1317,6 +1329,8 @@ AC_CONFIG_FILES([Makefile
src/lib/datasrc/tests/memory/testdata/Makefile
src/lib/xfr/Makefile
src/lib/xfr/tests/Makefile
+ src/lib/hooks/Makefile
+ src/lib/hooks/tests/Makefile
src/lib/log/Makefile
src/lib/log/interprocess/Makefile
src/lib/log/interprocess/tests/Makefile
@@ -1423,6 +1437,8 @@ AC_OUTPUT([doc/version.ent
src/lib/cc/session_config.h.pre
src/lib/cc/tests/session_unittests_config.h
src/lib/datasrc/datasrc_config.h.pre
+ src/lib/hooks/tests/marker_file.h
+ src/lib/hooks/tests/test_libraries.h
src/lib/log/tests/console_test.sh
src/lib/log/tests/destination_test.sh
src/lib/log/tests/init_logger_test.sh
diff --git a/doc/Doxyfile b/doc/Doxyfile
index bcb9285..57c6ce1 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -679,6 +679,7 @@ INPUT = ../src/lib/exceptions \
../src/lib/cache \
../src/lib/server_common/ \
../src/bin/sockcreator/ \
+ ../src/lib/hooks/ \
../src/lib/util/ \
../src/lib/util/io/ \
../src/lib/util/threads/ \
diff --git a/doc/devel/mainpage.dox b/doc/devel/mainpage.dox
index 92f86eb..5c8efa2 100644
--- a/doc/devel/mainpage.dox
+++ b/doc/devel/mainpage.dox
@@ -1,11 +1,33 @@
+// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
/**
- *
* @mainpage BIND10 Developer's Guide
*
* Welcome to BIND10 Developer's Guide. This documentation is addressed
- * at existing and prospecting developers and programmers, who would like
- * to gain insight into internal workings of BIND 10. It could also be useful
- * for existing and prospective contributors.
+ * at existing and prospecting developers and programmers and provides
+ * information needed to both extend and maintain BIND 10.
+ *
+ * If you wish to write "hook" code - code that is loaded by BIND 10 at
+ * run-time and modifies its behavior you should read the section
+ * @ref hookDevelopersGuide.
+ *
+ * BIND 10 maintanenace information is divided into a number of sections
+ * depending on focus. DNS-specific issues are covered in the
+ * @ref dnsMaintenanceGuide while information on DHCP-specific topics can
+ * be found in the @ref dhcpMaintenanceGuide. General BIND 10 topics, not
+ * specific to any protocol, are discussed in @ref miscellaneousTopics.
*
* If you are a user or system administrator, rather than software engineer,
* you should read <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND10
@@ -13,13 +35,15 @@
*
* Regardless of your field of expertise, you are encouraged to visit
* <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
+ * @section hooksFramework Hooks Framework
+ * - @subpage hooksComponentDeveloperGuide
*
- * @section DNS
+ * @section dnsMaintenanceGuide DNS Maintenance Guide
* - Authoritative DNS (todo)
* - Recursive resolver (todo)
* - @subpage DataScrubbing
*
- * @section DHCP
+ * @section dhcpMaintenanceGuide DHCP Maintenance Guide
* - @subpage dhcp4
* - @subpage dhcpv4Session
* - @subpage dhcpv4ConfigParser
@@ -39,7 +63,7 @@
* - @subpage dhcpDatabaseBackends
* - @subpage perfdhcpInternals
*
- * @section misc Miscellaneous topics
+ * @section miscellaneousTopics Miscellaneous topics
* - @subpage LoggingApi
* - @subpage LoggingApiOverview
* - @subpage LoggingApiLoggerNames
@@ -47,7 +71,10 @@
* - @subpage SocketSessionUtility
* - <a href="./doxygen-error.log">Documentation warnings and errors</a>
*
- * @todo: Move this logo to the right (and possibly up). Not sure what
- * is the best way to do it in Doxygen, without using CSS hacks.
* @image html isc-logo.png
*/
+/*
+ * @todo: Move the logo to the right (and possibly up). Not sure what
+ * is the best way to do it in Doxygen, without using CSS hacks.
+ */
+
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index f636c0d..79a8263 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = exceptions util log cryptolink dns cc config acl xfr bench \
+SUBDIRS = exceptions util log hooks cryptolink dns cc config acl xfr bench \
asiolink asiodns nsas cache resolve testutils datasrc \
server_common python dhcp dhcpsrv statistics
diff --git a/src/lib/hooks/Makefile.am b/src/lib/hooks/Makefile.am
new file mode 100644
index 0000000..838564a
--- /dev/null
+++ b/src/lib/hooks/Makefile.am
@@ -0,0 +1,56 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+
+# Define rule to build logging source files from message file
+hooks_messages.h hooks_messages.cc: hooks_messages.mes
+ $(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/hooks/hooks_messages.mes
+
+# Tell automake that the message files are built as part of the build process
+# (so that they are built before the main library is built).
+BUILT_SOURCES = hooks_messages.h hooks_messages.cc
+
+# Ensure that the message file is included in the distribution
+EXTRA_DIST = hooks_messages.mes
+
+# Get rid of generated message files on a clean
+CLEANFILES = *.gcno *.gcda hooks_messages.h hooks_messages.cc
+
+lib_LTLIBRARIES = libb10-hooks.la
+libb10_hooks_la_SOURCES =
+libb10_hooks_la_SOURCES += callout_handle.cc callout_handle.h
+libb10_hooks_la_SOURCES += callout_manager.cc callout_manager.h
+libb10_hooks_la_SOURCES += hooks.h
+libb10_hooks_la_SOURCES += hooks_log.cc hooks_log.h
+libb10_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
+libb10_hooks_la_SOURCES += library_handle.cc library_handle.h
+libb10_hooks_la_SOURCES += library_manager.cc library_manager.h
+libb10_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h
+libb10_hooks_la_SOURCES += pointer_converter.h
+libb10_hooks_la_SOURCES += server_hooks.cc server_hooks.h
+
+nodist_libb10_hooks_la_SOURCES = hooks_messages.cc hooks_messages.h
+
+libb10_hooks_la_CXXFLAGS = $(AM_CXXFLAGS)
+libb10_hooks_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libb10_hooks_la_LDFLAGS = $(AM_LDFLAGS)
+libb10_hooks_la_LIBADD =
+libb10_hooks_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
+libb10_hooks_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
+libb10_hooks_la_LIBADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+
+
+if USE_CLANGPP
+# Disable unused parameter warning caused by some of the
+# Boost headers when compiling with clang.
+libb10_hooks_la_CXXFLAGS += -Wno-unused-parameter
+endif
diff --git a/src/lib/hooks/callout_handle.cc b/src/lib/hooks/callout_handle.cc
new file mode 100644
index 0000000..ce9ef82
--- /dev/null
+++ b/src/lib/hooks/callout_handle.cc
@@ -0,0 +1,162 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Constructor.
+CalloutHandle::CalloutHandle(const boost::shared_ptr<CalloutManager>& manager,
+ const boost::shared_ptr<LibraryManagerCollection>& lmcoll)
+ : lm_collection_(lmcoll), arguments_(), context_collection_(),
+ manager_(manager), skip_(false) {
+
+ // Call the "context_create" hook. We should be OK doing this - although
+ // the constructor has not finished running, all the member variables
+ // have been created.
+ manager_->callCallouts(ServerHooks::CONTEXT_CREATE, *this);
+}
+
+// Destructor
+CalloutHandle::~CalloutHandle() {
+
+ // Call the "context_destroy" hook. We should be OK doing this - although
+ // the destructor is being called, all the member variables are still in
+ // existence.
+ manager_->callCallouts(ServerHooks::CONTEXT_DESTROY, *this);
+
+ // Explicitly clear the argument and context objects. This should free up
+ // all memory that could have been allocated by libraries that were loaded.
+ arguments_.clear();
+ context_collection_.clear();
+
+ // Normal destruction of the remaining variables will include the
+ // destruction of lm_collection_, an action that decrements the reference
+ // count on the library manager collection (which holds the libraries that
+ // could have allocated memory in the argument and context members.) When
+ // that goes to zero, the libraries will be unloaded: at that point nothing
+ // in the hooks framework will be pointing to memory in the libraries'
+ // address space.
+ //
+ // It is possible that some other data structure in the server (the program
+ // using the hooks library) still references the address space and attempts
+ // to access it causing a segmentation fault. That issue is outside the
+ // scope of this framework and is not addressed by it.
+}
+
+// Return the name of all argument items.
+
+vector<string>
+CalloutHandle::getArgumentNames() const {
+
+ vector<string> names;
+ for (ElementCollection::const_iterator i = arguments_.begin();
+ i != arguments_.end(); ++i) {
+ names.push_back(i->first);
+ }
+
+ return (names);
+}
+
+// Return the library handle allowing the callout to access the CalloutManager
+// registration/deregistration functions.
+
+LibraryHandle&
+CalloutHandle::getLibraryHandle() const {
+ return (manager_->getLibraryHandle());
+}
+
+// Return the context for the currently pointed-to library. This version is
+// used by the "setContext()" method and creates a context for the current
+// library if it does not exist.
+
+CalloutHandle::ElementCollection&
+CalloutHandle::getContextForLibrary() {
+ int libindex = manager_->getLibraryIndex();
+
+ // Access a reference to the element collection for the given index,
+ // creating a new element collection if necessary, and return it.
+ return (context_collection_[libindex]);
+}
+
+// The "const" version of the above, used by the "getContext()" method. If
+// the context for the current library doesn't exist, throw an exception.
+
+const CalloutHandle::ElementCollection&
+CalloutHandle::getContextForLibrary() const {
+ int libindex = manager_->getLibraryIndex();
+
+ ContextCollection::const_iterator libcontext =
+ context_collection_.find(libindex);
+ if (libcontext == context_collection_.end()) {
+ isc_throw(NoSuchCalloutContext, "unable to find callout context "
+ "associated with the current library index (" << libindex <<
+ ")");
+ }
+
+ // Return a reference to the context's element collection.
+ return (libcontext->second);
+}
+
+// Return the name of all items in the context associated with the current]
+// library.
+
+vector<string>
+CalloutHandle::getContextNames() const {
+
+ vector<string> names;
+
+ const ElementCollection& elements = getContextForLibrary();
+ for (ElementCollection::const_iterator i = elements.begin();
+ i != elements.end(); ++i) {
+ names.push_back(i->first);
+ }
+
+ return (names);
+}
+
+// Return name of current hook (the hook to which the current callout is
+// attached) or the empty string if not called within the context of a
+// callout.
+
+string
+CalloutHandle::getHookName() const {
+ // Get the current hook index.
+ int index = manager_->getHookIndex();
+
+ // ... and look up the hook.
+ string hook = "";
+ try {
+ hook = ServerHooks::getServerHooks().getName(index);
+ } catch (const NoSuchHook&) {
+ // Hook index is invalid, so this methods probably called from outside
+ // a callout being executed via a call to CalloutManager::callCallouts.
+ // In this case, the empty string is returned.
+ }
+
+ return (hook);
+}
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/callout_handle.h b/src/lib/hooks/callout_handle.h
new file mode 100644
index 0000000..eb57fd4
--- /dev/null
+++ b/src/lib/hooks/callout_handle.h
@@ -0,0 +1,383 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef CALLOUT_HANDLE_H
+#define CALLOUT_HANDLE_H
+
+#include <exceptions/exceptions.h>
+#include <hooks/library_handle.h>
+
+#include <boost/any.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+/// @brief No such argument
+///
+/// Thrown if an attempt is made access an argument that does not exist.
+
+class NoSuchArgument : public Exception {
+public:
+ NoSuchArgument(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief No such callout context item
+///
+/// Thrown if an attempt is made to get an item of data from this callout's
+/// context and either the context or an item in the context with that name
+/// does not exist.
+
+class NoSuchCalloutContext : public Exception {
+public:
+ NoSuchCalloutContext(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+// Forward declaration of the library handle and related collection classes.
+
+class CalloutManager;
+class LibraryHandle;
+class LibraryManagerCollection;
+
+/// @brief Per-packet callout handle
+///
+/// An object of this class is associated with every packet (or request)
+/// processed by the server. It forms the principle means of passing data
+/// between the server and the user-library callouts.
+///
+/// The class allows access to the following information:
+///
+/// - Arguments. When the callouts associated with a hook are called, they
+/// are passed information by the server (and can return information to it)
+/// through name/value pairs. Each of these pairs is an argument and the
+/// information is accessed through the {get,set}Argument() methods.
+///
+/// - Per-packet context. Each packet has a context associated with it, this
+/// context being on a per-library basis. In other words, As a packet passes
+/// through the callouts associated with a given library, the callouts can
+/// associate and retrieve information with the packet. The per-library
+/// nature of the context means that the callouts within a given library can
+/// pass packet-specific information between one another, but they cannot pass
+/// information to callous within another library. Typically such context
+/// is created in the "context_create" callout and destroyed in the
+/// "context_destroy" callout. The information is accessed through the
+/// {get,set}Context() methods.
+///
+/// - Per-library handle (LibraryHandle). The library handle allows the
+/// callout to dynamically register and deregister callouts. In the latter
+/// case, only functions registered by functions in the same library as the
+/// callout doing the deregistration can be removed: callouts registered by
+/// other libraries cannot be modified.
+
+class CalloutHandle {
+public:
+
+ /// Typedef to allow abbreviation of iterator specification in methods.
+ /// The std::string is the argument name and the "boost::any" is the
+ /// corresponding value associated with it.
+ typedef std::map<std::string, boost::any> ElementCollection;
+
+ /// Typedef to allow abbreviations in specifications when accessing
+ /// context. The ElementCollection is the name/value collection for
+ /// a particular context. The "int" corresponds to the index of an
+ /// associated library - there is a 1:1 correspondence between libraries
+ /// and a name.value collection.
+ ///
+ /// The collection of contexts is stored in a map, as not every library
+ /// will require creation of a context associated with each packet. In
+ /// addition, the structure is more flexible in that the size does not
+ /// need to be set when the CalloutHandle is constructed.
+ typedef std::map<int, ElementCollection> ContextCollection;
+
+ /// @brief Constructor
+ ///
+ /// Creates the object and calls the callouts on the "context_create"
+ /// hook.
+ ///
+ /// Of the two arguments passed, only the pointer to the callout manager is
+ /// actively used. The second argument, the pointer to the library manager
+ /// collection, is used for lifetime control: after use, the callout handle
+ /// may contain pointers to memory allocated by the loaded libraries. The
+ /// used of a shared pointer to the collection of library managers means
+ /// that the libraries that could have allocated memory in a callout handle
+ /// will not be unloaded until all such handles have been destroyed. This
+ /// issue is discussed in more detail in the documentation for
+ /// isc::hooks::LibraryManager.
+ ///
+ /// @param manager Pointer to the callout manager object.
+ /// @param lmcoll Pointer to the library manager collection. This has a
+ /// null default for testing purposes.
+ CalloutHandle(const boost::shared_ptr<CalloutManager>& manager,
+ const boost::shared_ptr<LibraryManagerCollection>& lmcoll =
+ boost::shared_ptr<LibraryManagerCollection>());
+
+ /// @brief Destructor
+ ///
+ /// Calls the context_destroy callback to release any per-packet context.
+ /// It also clears stored data to avoid problems during member destruction.
+ ~CalloutHandle();
+
+ /// @brief Set argument
+ ///
+ /// Sets the value of an argument. The argument is created if it does not
+ /// already exist.
+ ///
+ /// @param name Name of the argument.
+ /// @param value Value to set. That can be of any data type.
+ template <typename T>
+ void setArgument(const std::string& name, T value) {
+ arguments_[name] = value;
+ }
+
+ /// @brief Get argument
+ ///
+ /// Gets the value of an argument.
+ ///
+ /// @param name Name of the element in the argument list to get.
+ /// @param value [out] Value to set. The type of "value" is important:
+ /// it must match the type of the value set.
+ ///
+ /// @throw NoSuchArgument No argument with the given name is present.
+ /// @throw boost::bad_any_cast An argument with the given name is present,
+ /// but the data type of the value is not the same as the type of
+ /// the variable provided to receive the value.
+ template <typename T>
+ void getArgument(const std::string& name, T& value) const {
+ ElementCollection::const_iterator element_ptr = arguments_.find(name);
+ if (element_ptr == arguments_.end()) {
+ isc_throw(NoSuchArgument, "unable to find argument with name " <<
+ name);
+ }
+
+ value = boost::any_cast<T>(element_ptr->second);
+ }
+
+ /// @brief Get argument names
+ ///
+ /// Returns a vector holding the names of arguments in the argument
+ /// vector.
+ ///
+ /// @return Vector of strings reflecting argument names.
+ std::vector<std::string> getArgumentNames() const;
+
+ /// @brief Delete argument
+ ///
+ /// Deletes an argument of the given name. If an argument of that name
+ /// does not exist, the method is a no-op.
+ ///
+ /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
+ /// by this method.
+ ///
+ /// @param name Name of the element in the argument list to set.
+ void deleteArgument(const std::string& name) {
+ static_cast<void>(arguments_.erase(name));
+ }
+
+ /// @brief Delete all arguments
+ ///
+ /// Deletes all arguments associated with this context.
+ ///
+ /// N.B. If any elements are raw pointers, the pointed-to data is NOT
+ /// deleted by this method.
+ void deleteAllArguments() {
+ arguments_.clear();
+ }
+
+ /// @brief Set skip flag
+ ///
+ /// Sets the "skip" variable in the callout handle. This variable is
+ /// interrogated by the server to see if the remaining callouts associated
+ /// with the current hook should be bypassed.
+ ///
+ /// @param skip New value of the "skip" flag.
+ void setSkip(bool skip) {
+ skip_ = skip;
+ }
+
+ /// @brief Get skip flag
+ ///
+ /// Gets the current value of the "skip" flag.
+ ///
+ /// @return Current value of the skip flag.
+ bool getSkip() const {
+ return (skip_);
+ }
+
+ /// @brief Access current library handle
+ ///
+ /// Returns a reference to the current library handle. This function is
+ /// only available when called by a callout (which in turn is called
+ /// through the "callCallouts" method), as it is only then that the current
+ /// library index is valid. A callout uses the library handle to
+ /// dynamically register or deregister callouts.
+ ///
+ /// @return Reference to the library handle.
+ ///
+ /// @throw InvalidIndex thrown if this method is called when the current
+ /// library index is invalid (typically if it is called outside of
+ /// the active callout).
+ LibraryHandle& getLibraryHandle() const;
+
+ /// @brief Set context
+ ///
+ /// Sets an element in the context associated with the current library. If
+ /// an element of the name is already present, it is replaced.
+ ///
+ /// @param name Name of the element in the context to set.
+ /// @param value Value to set.
+ template <typename T>
+ void setContext(const std::string& name, T value) {
+ getContextForLibrary()[name] = value;
+ }
+
+ /// @brief Get context
+ ///
+ /// Gets an element from the context associated with the current library.
+ ///
+ /// @param name Name of the element in the context to get.
+ /// @param value [out] Value to set. The type of "value" is important:
+ /// it must match the type of the value set.
+ ///
+ /// @throw NoSuchCalloutContext Thrown if no context element with the name
+ /// "name" is present.
+ /// @throw boost::bad_any_cast Thrown if the context element is present
+ /// but the type of the data is not the same as the type of the
+ /// variable provided to receive its value.
+ template <typename T>
+ void getContext(const std::string& name, T& value) const {
+ const ElementCollection& lib_context = getContextForLibrary();
+
+ ElementCollection::const_iterator element_ptr = lib_context.find(name);
+ if (element_ptr == lib_context.end()) {
+ isc_throw(NoSuchCalloutContext, "unable to find callout context "
+ "item " << name << " in the context associated with "
+ "current library");
+ }
+
+ value = boost::any_cast<T>(element_ptr->second);
+ }
+
+ /// @brief Get context names
+ ///
+ /// Returns a vector holding the names of items in the context associated
+ /// with the current library.
+ ///
+ /// @return Vector of strings reflecting the names of items in the callout
+ /// context associated with the current library.
+ std::vector<std::string> getContextNames() const;
+
+ /// @brief Delete context element
+ ///
+ /// Deletes an item of the given name from the context associated with the
+ /// current library. If an item of that name does not exist, the method is
+ /// a no-op.
+ ///
+ /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
+ /// by this.
+ ///
+ /// @param name Name of the context item to delete.
+ void deleteContext(const std::string& name) {
+ static_cast<void>(getContextForLibrary().erase(name));
+ }
+
+ /// @brief Delete all context items
+ ///
+ /// Deletes all items from the context associated with the current library.
+ ///
+ /// N.B. If any elements are raw pointers, the pointed-to data is NOT
+ /// deleted by this.
+ void deleteAllContext() {
+ getContextForLibrary().clear();
+ }
+
+ /// @brief Get hook name
+ ///
+ /// Get the name of the hook to which the current callout is attached.
+ /// This can be the null string if the CalloutHandle is being accessed
+ /// outside of the CalloutManager's "callCallouts" method.
+ ///
+ /// @return Name of the current hook or the empty string if none.
+ std::string getHookName() const;
+
+private:
+ /// @brief Check index
+ ///
+ /// Gets the current library index, throwing an exception if it is not set
+ /// or is invalid for the current library collection.
+ ///
+ /// @return Current library index, valid for this library collection.
+ ///
+ /// @throw InvalidIndex current library index is not valid for the library
+ /// handle collection.
+ int getLibraryIndex() const;
+
+ /// @brief Return reference to context for current library
+ ///
+ /// Called by all context-setting functions, this returns a reference to
+ /// the callout context for the current library, creating a context if it
+ /// does not exist.
+ ///
+ /// @return Reference to the collection of name/value pairs associated
+ /// with the current library.
+ ///
+ /// @throw InvalidIndex current library index is not valid for the library
+ /// handle collection.
+ ElementCollection& getContextForLibrary();
+
+ /// @brief Return reference to context for current library (const version)
+ ///
+ /// Called by all context-accessing functions, this a reference to the
+ /// callout context for the current library. An exception is thrown if
+ /// it does not exist.
+ ///
+ /// @return Reference to the collection of name/value pairs associated
+ /// with the current library.
+ ///
+ /// @throw NoSuchCalloutContext Thrown if there is no ElementCollection
+ /// associated with the current library.
+ const ElementCollection& getContextForLibrary() const;
+
+ // Member variables
+
+ /// Pointer to the collection of libraries for which this handle has been
+ /// created.
+ boost::shared_ptr<LibraryManagerCollection> lm_collection_;
+
+ /// Collection of arguments passed to the callouts
+ ElementCollection arguments_;
+
+ /// Context collection - there is one entry per library context.
+ ContextCollection context_collection_;
+
+ /// Callout manager.
+ boost::shared_ptr<CalloutManager> manager_;
+
+ /// "Skip" flag, indicating if the caller should bypass remaining callouts.
+ bool skip_;
+};
+
+/// A shared pointer to a CalloutHandle object.
+typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;
+
+} // namespace hooks
+} // namespace isc
+
+
+#endif // CALLOUT_HANDLE_H
diff --git a/src/lib/hooks/callout_manager.cc b/src/lib/hooks/callout_manager.cc
new file mode 100644
index 0000000..4ba3640
--- /dev/null
+++ b/src/lib/hooks/callout_manager.cc
@@ -0,0 +1,256 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/hooks_log.h>
+#include <hooks/pointer_converter.h>
+
+#include <boost/static_assert.hpp>
+
+#include <algorithm>
+#include <climits>
+#include <functional>
+#include <utility>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Check that the index of a library is valid. It can range from 1 - n
+// (n is the number of libraries), 0 (pre-user library callouts), or INT_MAX
+// (post-user library callouts). It can also be -1 to indicate an invalid
+// value.
+
+void
+CalloutManager::checkLibraryIndex(int library_index) const {
+ if (((library_index >= -1) && (library_index <= num_libraries_)) ||
+ (library_index == INT_MAX)) {
+ return;
+ }
+
+ isc_throw(NoSuchLibrary, "library index " << library_index <<
+ " is not valid for the number of loaded libraries (" <<
+ num_libraries_ << ")");
+}
+
+// Set the number of libraries handled by the CalloutManager.
+
+void
+CalloutManager::setNumLibraries(int num_libraries) {
+ if (num_libraries < 0) {
+ isc_throw(isc::BadValue, "number of libraries passed to the "
+ "CalloutManager must be >= 0");
+ }
+
+ num_libraries_ = num_libraries;
+}
+
+// Register a callout for the current library.
+
+void
+CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
+ // Note the registration.
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUT_REGISTRATION)
+ .arg(current_library_).arg(name);
+
+ // Sanity check that the current library index is set to a valid value.
+ checkLibraryIndex(current_library_);
+
+ // Get the index associated with this hook (validating the name in the
+ // process).
+ int hook_index = ServerHooks::getServerHooks().getIndex(name);
+
+ // Iterate through the callout vector for the hook from start to end,
+ // looking for the first entry where the library index is greater than
+ // the present index.
+ for (CalloutVector::iterator i = hook_vector_[hook_index].begin();
+ i != hook_vector_[hook_index].end(); ++i) {
+ if (i->first > current_library_) {
+ // Found an element whose library index number is greater than the
+ // current index, so insert the new element ahead of this one.
+ hook_vector_[hook_index].insert(i, make_pair(current_library_,
+ callout));
+ return;
+ }
+ }
+
+ // Reached the end of the vector, so there is no element in the (possibly
+ // empty) set of callouts with a library index greater than the current
+ // library index. Inset the callout at the end of the list.
+ hook_vector_[hook_index].push_back(make_pair(current_library_, callout));
+}
+
+// Check if callouts are present for a given hook index.
+
+bool
+CalloutManager::calloutsPresent(int hook_index) const {
+ // Validate the hook index.
+ if ((hook_index < 0) || (hook_index >= hook_vector_.size())) {
+ isc_throw(NoSuchHook, "hook index " << hook_index <<
+ " is not valid for the list of registered hooks");
+ }
+
+ // Valid, so are there any callouts associated with that hook?
+ return (!hook_vector_[hook_index].empty());
+}
+
+// Call all the callouts for a given hook.
+
+void
+CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
+
+ // Only initialize and iterate if there are callouts present. This check
+ // also catches the case of an invalid index.
+ if (calloutsPresent(hook_index)) {
+
+ // Clear the "skip" flag so we don't carry state from a previous call.
+ callout_handle.setSkip(false);
+
+ // Set the current hook index. This is used should a callout wish to
+ // determine to what hook it is attached.
+ current_hook_ = hook_index;
+
+ // Duplicate the callout vector for this hook and work through that.
+ // This step is needed because we allow dynamic registration and
+ // deregistration of callouts. If a callout attached to a hook modified
+ // the list of callouts on that hook, the underlying CalloutVector would
+ // change and potentially affect the iteration through that vector.
+ CalloutVector callouts(hook_vector_[hook_index]);
+
+ // Call all the callouts.
+ for (CalloutVector::const_iterator i = callouts.begin();
+ i != callouts.end(); ++i) {
+ // In case the callout tries to register or deregister a callout,
+ // set the current library index to the index associated with the
+ // library that registered the callout being called.
+ current_library_ = i->first;
+
+ // Call the callout
+ try {
+ int status = (*i->second)(callout_handle);
+ if (status == 0) {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
+ HOOKS_CALLOUT_CALLED).arg(current_library_)
+ .arg(ServerHooks::getServerHooks()
+ .getName(current_hook_))
+ .arg(PointerConverter(i->second).dlsymPtr());
+ } else {
+ LOG_ERROR(hooks_logger, HOOKS_CALLOUT_ERROR)
+ .arg(current_library_)
+ .arg(ServerHooks::getServerHooks()
+ .getName(current_hook_))
+ .arg(PointerConverter(i->second).dlsymPtr());
+ }
+ } catch (...) {
+ // Any exception, not just ones based on isc::Exception
+ LOG_ERROR(hooks_logger, HOOKS_CALLOUT_EXCEPTION)
+ .arg(current_library_)
+ .arg(ServerHooks::getServerHooks().getName(current_hook_))
+ .arg(PointerConverter(i->second).dlsymPtr());
+ }
+
+ }
+
+ // Reset the current hook and library indexs to an invalid value to
+ // catch any programming errors.
+ current_hook_ = -1;
+ current_library_ = -1;
+ }
+}
+
+// Deregister a callout registered by the current library on a particular hook.
+
+bool
+CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
+ // Sanity check that the current library index is set to a valid value.
+ checkLibraryIndex(current_library_);
+
+ // Get the index associated with this hook (validating the name in the
+ // process).
+ int hook_index = ServerHooks::getServerHooks().getIndex(name);
+
+ /// Construct a CalloutEntry matching the current library and the callout
+ /// we want to remove.
+ CalloutEntry target(current_library_, callout);
+
+ /// To decide if any entries were removed, we'll record the initial size
+ /// of the callout vector for the hook, and compare it with the size after
+ /// the removal.
+ size_t initial_size = hook_vector_[hook_index].size();
+
+ // The next bit is standard STL (see "Item 33" in "Effective STL" by
+ // Scott Meyers).
+ //
+ // remove_if reorders the hook vector so that all items not matching
+ // the predicate are at the start of the vector and returns a pointer
+ // to the next element. (In this case, the predicate is that the item
+ // is equal to the value of the passed callout.) The erase() call
+ // removes everything from that element to the end of the vector, i.e.
+ // all the matching elements.
+ hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
+ hook_vector_[hook_index].end(),
+ bind1st(equal_to<CalloutEntry>(),
+ target)),
+ hook_vector_[hook_index].end());
+
+ // Return an indication of whether anything was removed.
+ bool removed = initial_size != hook_vector_[hook_index].size();
+ if (removed) {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
+ HOOKS_CALLOUT_DEREGISTERED).arg(current_library_).arg(name);
+ }
+
+ return (removed);
+}
+
+// Deregister all callouts on a given hook.
+
+bool
+CalloutManager::deregisterAllCallouts(const std::string& name) {
+
+ // Get the index associated with this hook (validating the name in the
+ // process).
+ int hook_index = ServerHooks::getServerHooks().getIndex(name);
+
+ /// Construct a CalloutEntry matching the current library (the callout
+ /// pointer is NULL as we are not checking that).
+ CalloutEntry target(current_library_, NULL);
+
+ /// To decide if any entries were removed, we'll record the initial size
+ /// of the callout vector for the hook, and compare it with the size after
+ /// the removal.
+ size_t initial_size = hook_vector_[hook_index].size();
+
+ // Remove all callouts matching this library.
+ hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
+ hook_vector_[hook_index].end(),
+ bind1st(CalloutLibraryEqual(),
+ target)),
+ hook_vector_[hook_index].end());
+
+ // Return an indication of whether anything was removed.
+ bool removed = initial_size != hook_vector_[hook_index].size();
+ if (removed) {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
+ HOOKS_ALL_CALLOUTS_DEREGISTERED).arg(current_library_)
+ .arg(name);
+ }
+
+ return (removed);
+}
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/callout_manager.h b/src/lib/hooks/callout_manager.h
new file mode 100644
index 0000000..4619006
--- /dev/null
+++ b/src/lib/hooks/callout_manager.h
@@ -0,0 +1,404 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef CALLOUT_MANAGER_H
+#define CALLOUT_MANAGER_H
+
+#include <exceptions/exceptions.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <climits>
+#include <map>
+#include <string>
+
+namespace isc {
+namespace hooks {
+
+/// @brief No such library
+///
+/// Thrown if an attempt is made to set the current library index to a value
+/// that is invalid for the number of loaded libraries.
+class NoSuchLibrary : public Exception {
+public:
+ NoSuchLibrary(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Callout Manager
+///
+/// This class manages the registration, deregistration and execution of the
+/// library callouts. It is part of the hooks framework used by the BIND 10
+/// server, and is not for use by user-written code in a hooks library.
+///
+/// In operation, the class needs to know two items of data:
+///
+/// - The list of server hooks, which is used in two ways. Firstly, when a
+/// callout registers or deregisters a hook, it does so by name: the
+/// @ref isc::util::ServerHooks object supplies the names of registered
+/// hooks. Secondly, when the callouts associated with a hook are called by
+/// the server, the server supplies the index of the relevant hook: this is
+/// validated by reference to the list of hook.
+///
+/// - The number of loaded libraries. Each callout registered by a user
+/// library is associated with that library, the callout manager storing both
+/// a pointer to the callout and the index of the library in the list of
+/// loaded libraries. Callouts are allowed to dynamically register and
+/// deregister callouts in the same library (including themselves): they
+/// cannot affect callouts registered by another library. When calling a
+/// callout, the callout manager maintains the idea of a "current library
+/// index": if the callout calls one of the callout registration functions
+/// (indirectly via the @ref LibraryHandle object), the registration
+/// functions use the "current library index" in their processing.
+///
+/// These two items of data are supplied when an object of this class is
+/// constructed. The latter (number of libraries) can be updated after the
+/// class is constructed. (Such an update is used during library loading where
+/// the CalloutManager has to be constructed before the libraries are loaded,
+/// but one of the libraries subsequently fails to load.)
+///
+/// The library index is important because it determines in what order callouts
+/// on a particular hook are called. For each hook, the CalloutManager
+/// maintains a vector of callouts ordered by library index. When a callout
+/// is added to the list, it is added at the end of the callouts associated
+/// with the current library. To clarify this further, suppose that three
+/// libraries are loaded, A (assigned an index 1), B (assigned an index 2) and
+/// C (assigned an index 3). Suppose A registers two callouts on a given hook,
+/// A1 and A2 (in that order) B registers B1 and B2 (in that order) and C
+/// registers C1 and C2 (in that order). Internally, the callouts are stored
+/// in the order A1, A2, B1, B2, C1, and C2: this is also the order in which
+/// the are called. If B now registers another callout (B3), it is added to
+/// the vector after the list of callouts associated with B: the new order is
+/// therefore A1, A2, B1, B2, B3, C1 and C2.
+///
+/// Indexes range between 1 and n (where n is the number of the libraries
+/// loaded) and are assigned to libraries based on the order the libraries
+/// presented to the hooks framework for loading (something that occurs in the
+/// isc::util::HooksManager) class. However, two other indexes are recognised,
+/// 0 and INT_MAX. These are used when the server itself registers callouts -
+/// the server is able to register callouts that get called before any
+/// user-library callouts, and ones that get called after user-library callouts.
+/// In other words, assuming the callouts on a hook are A1, A2, B1, B2, B3, C2,
+/// C2 as before, and that the server registers S1 (to run before the
+/// user-registered callouts) and S2 (to run after them), the callouts are
+/// stored (and executed) in the order S1, A1, A2, B1, B2, B3, C2, C2, S2. In
+/// summary, the recognised index values are:
+///
+/// - < 0: invalid.
+/// - 0: used for server-registered callouts that are called before
+/// user-registered callouts.
+/// - 1 - n: callouts from user libraries.
+/// - INT_MAX: used for server-registered callouts called after
+/// user-registered callouts.
+///
+/// Note that the callout functions do not access the CalloutManager: instead,
+/// they use a LibraryHandle object. This contains an internal pointer to
+/// the CalloutManager, but provides a restricted interface. In that way,
+/// callouts are unable to affect callouts supplied by other libraries.
+
+class CalloutManager {
+private:
+
+ // Private typedefs
+
+ /// Element in the vector of callouts. The elements in the pair are the
+ /// index of the library from which this callout was registered, and a#
+ /// pointer to the callout itself.
+ typedef std::pair<int, CalloutPtr> CalloutEntry;
+
+ /// An element in the hook vector. Each element is a vector of callouts
+ /// associated with a given hook.
+ typedef std::vector<CalloutEntry> CalloutVector;
+
+public:
+
+ /// @brief Constructor
+ ///
+ /// Initializes member variables, in particular sizing the hook vector
+ /// (the vector of callout vectors) to the appropriate size.
+ ///
+ /// @param num_libraries Number of loaded libraries.
+ ///
+ /// @throw isc::BadValue if the number of libraries is less than or equal
+ /// to 0, or if the pointer to the server hooks object is empty.
+ CalloutManager(int num_libraries = 0)
+ : current_hook_(-1), current_library_(-1),
+ hook_vector_(ServerHooks::getServerHooks().getCount()),
+ library_handle_(this), pre_library_handle_(this, 0),
+ post_library_handle_(this, INT_MAX), num_libraries_(num_libraries)
+ {
+ // Check that the number of libraries is OK. (This does a redundant
+ // set of the number of libraries, but it's only a single assignment
+ // and avoids the need for a separate "check" method.
+ setNumLibraries(num_libraries);
+ }
+
+ /// @brief Register a callout on a hook for the current library
+ ///
+ /// Registers a callout function for the current library with a given hook
+ /// (the index of the "current library" being given by the current_library_
+ /// member). The callout is added to the end of the callouts for this
+ /// library that are associated with that hook.
+ ///
+ /// @param name Name of the hook to which the callout is added.
+ /// @param callout Pointer to the callout function to be registered.
+ ///
+ /// @throw NoSuchHook The hook name is unrecognised.
+ /// @throw Unexpected The hook name is valid but an internal data structure
+ /// is of the wrong size.
+ void registerCallout(const std::string& name, CalloutPtr callout);
+
+ /// @brief De-Register a callout on a hook for the current library
+ ///
+ /// Searches through the functions registered by the the current library
+ /// (the index of the "current library" being given by the current_library_
+ /// member) with the named hook and removes all entries matching the
+ /// callout.
+ ///
+ /// @param name Name of the hook from which the callout is removed.
+ /// @param callout Pointer to the callout function to be removed.
+ ///
+ /// @return true if a one or more callouts were deregistered.
+ ///
+ /// @throw NoSuchHook The hook name is unrecognised.
+ /// @throw Unexpected The hook name is valid but an internal data structure
+ /// is of the wrong size.
+ bool deregisterCallout(const std::string& name, CalloutPtr callout);
+
+ /// @brief Removes all callouts on a hook for the current library
+ ///
+ /// Removes all callouts associated with a given hook that were registered
+ /// by the current library (the index of the "current library" being given
+ /// by the current_library_ member).
+ ///
+ /// @param name Name of the hook from which the callouts are removed.
+ ///
+ /// @return true if one or more callouts were deregistered.
+ ///
+ /// @throw NoSuchHook Thrown if the hook name is unrecognised.
+ bool deregisterAllCallouts(const std::string& name);
+
+ /// @brief Checks if callouts are present on a hook
+ ///
+ /// Checks all loaded libraries and returns true if at least one callout
+ /// has been registered by any of them for the given hook.
+ ///
+ /// @param hook_index Hook index for which callouts are checked.
+ ///
+ /// @return true if callouts are present, false if not.
+ ///
+ /// @throw NoSuchHook Given index does not correspond to a valid hook.
+ bool calloutsPresent(int hook_index) const;
+
+ /// @brief Calls the callouts for a given hook
+ ///
+ /// Iterates through the libray handles and calls the callouts associated
+ /// with the given hook index.
+ ///
+ /// @note This method invalidates the current library index set with
+ /// setLibraryIndex().
+ ///
+ /// @param hook_index Index of the hook to call.
+ /// @param callout_handle Reference to the CalloutHandle object for the
+ /// current object being processed.
+ void callCallouts(int hook_index, CalloutHandle& callout_handle);
+
+ /// @brief Get current hook index
+ ///
+ /// Made available during callCallouts, this is the index of the hook
+ /// on which callouts are being called.
+ int getHookIndex() const {
+ return (current_hook_);
+ }
+
+ /// @brief Set number of libraries
+ ///
+ /// Sets the number of libraries. Although the value is passed to the
+ /// constructor, in some cases that is only an estimate and the number
+ /// can only be determined after the CalloutManager is created.
+ ///
+ /// @note If the number if libraries is reset, it must be done *before*
+ /// any callouts are registered.
+ ///
+ /// @param num_libraries Number of libraries served by this CalloutManager.
+ ///
+ /// @throw BadValue Number of libraries must be >= 0.
+ /// @throw LibraryCountChanged Number of libraries has been changed after
+ /// callouts have been registered.
+ void setNumLibraries(int num_libraries);
+
+ /// @brief Get number of libraries
+ ///
+ /// Returns the number of libraries that this CalloutManager is expected
+ /// to serve. This is the number passed to its constructor.
+ ///
+ /// @return Number of libraries served by this CalloutManager.
+ int getNumLibraries() const {
+ return (num_libraries_);
+ }
+
+ /// @brief Get current library index
+ ///
+ /// Returns the index of the "current" library. This the index associated
+ /// with the currently executing callout when callCallouts is executing.
+ /// When callCallouts() is not executing (as is the case when the load()
+ /// function in a user-library is called during the library load process),
+ /// the index can be set by setLibraryIndex().
+ ///
+ /// @note The value set by this method is lost after a call to
+ /// callCallouts.
+ ///
+ /// @return Current library index.
+ int getLibraryIndex() const {
+ return (current_library_);
+ }
+
+ /// @brief Set current library index
+ ///
+ /// Sets the current library index. This has the following valid values:
+ ///
+ /// - -1: invalidate current index.
+ /// - 0: pre-user library callout.
+ /// - 1 - numlib: user-library callout (where "numlib" is the number of
+ /// libraries loaded in the system, this figure being passed to this
+ /// object at construction time).
+ /// - INT_MAX: post-user library callout.
+ ///
+ /// @param library_index New library index.
+ ///
+ /// @throw NoSuchLibrary if the index is not valid.
+ void setLibraryIndex(int library_index) {
+ checkLibraryIndex(library_index);
+ current_library_ = library_index;
+ }
+
+ /// @defgroup calloutManagerLibraryHandles Callout manager library handles
+ ///
+ /// The CalloutManager offers three library handles:
+ ///
+ /// - a "standard" one, used to register and deregister callouts for
+ /// the library index that is marked as current in the CalloutManager.
+ /// When a callout is called, it is passed this one.
+ /// - a pre-library callout handle, used by the server to register
+ // callouts to run prior to user-library callouts.
+ /// - a post-library callout handle, used by the server to register
+ /// callouts to run after the user-library callouts.
+ //@{
+
+ /// @brief Return library handle
+ ///
+ /// The library handle is available to the user callout via the callout
+ /// handle object. It provides a cut-down view of the CalloutManager,
+ /// allowing the callout to register and deregister callouts in the
+ /// library of which it is part, whilst denying access to anything that
+ /// may affect other libraries.
+ ///
+ /// @return Reference to library handle for this manager
+ LibraryHandle& getLibraryHandle() {
+ return (library_handle_);
+ }
+
+ /// @brief Return pre-user callouts library handle
+ ///
+ /// The LibraryHandle to affect callouts that will run before the
+ /// user-library callouts.
+ ///
+ /// @return Reference to pre-user library handle for this manager
+ LibraryHandle& getPreLibraryHandle() {
+ return (pre_library_handle_);
+ }
+
+ /// @brief Return post-user callouts library handle
+ ///
+ /// The LibraryHandle to affect callouts that will run before the
+ /// user-library callouts.
+ ///
+ /// @return Reference to post-user library handle for this manager
+ LibraryHandle& getPostLibraryHandle() {
+ return (post_library_handle_);
+ }
+
+ //@}
+
+private:
+ /// @brief Check library index
+ ///
+ /// Ensures that the current library index is valid. This is called by
+ /// the hook registration functions.
+ ///
+ /// @param library_index Value to check for validity as a library index.
+ /// Valid values are 0 - numlib+1 and -1: see @ref setLibraryIndex
+ /// for the meaning of the various values.
+ ///
+ /// @throw NoSuchLibrary Library index is not valid.
+ void checkLibraryIndex(int library_index) const;
+
+ /// @brief Compare two callout entries for library equality
+ ///
+ /// This is used in callout removal code when all callouts on a hook for a
+ /// given library are being removed. It checks whether two callout entries
+ /// have the same library index.
+ ///
+ /// @param ent1 First callout entry to check
+ /// @param ent2 Second callout entry to check
+ ///
+ /// @return bool true if the library entries are the same
+ class CalloutLibraryEqual :
+ public std::binary_function<CalloutEntry, CalloutEntry, bool> {
+ public:
+ bool operator()(const CalloutEntry& ent1,
+ const CalloutEntry& ent2) const {
+ return (ent1.first == ent2.first);
+ }
+ };
+
+ /// Current hook. When a call is made to callCallouts, this holds the
+ /// index of the current hook. It is set to an invalid value (-1)
+ /// otherwise.
+ int current_hook_;
+
+ /// Current library index. When a call is made to any of the callout
+ /// registration methods, this variable indicates the index of the user
+ /// library that should be associated with the call.
+ int current_library_;
+
+ /// Vector of callout vectors. There is one entry in this outer vector for
+ /// each hook. Each element is itself a vector, with one entry for each
+ /// callout registered for that hook.
+ std::vector<CalloutVector> hook_vector_;
+
+ /// LibraryHandle object user by the callout to access the callout
+ /// registration methods on this CalloutManager object. The object is set
+ /// such that the index of the library associated with any operation is
+ /// whatever is currently set in the CalloutManager.
+ LibraryHandle library_handle_;
+
+ /// LibraryHandle for callouts to be registered as being called before
+ /// the user-registered callouts.
+ LibraryHandle pre_library_handle_;
+
+ /// LibraryHandle for callouts to be registered as being called after
+ /// the user-registered callouts.
+ LibraryHandle post_library_handle_;
+
+ /// Number of libraries.
+ int num_libraries_;
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // CALLOUT_MANAGER_H
diff --git a/src/lib/hooks/hooks.h b/src/lib/hooks/hooks.h
new file mode 100644
index 0000000..a059d79
--- /dev/null
+++ b/src/lib/hooks/hooks.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef HOOKS_H
+#define HOOKS_H
+
+#include <hooks/callout_handle.h>
+#include <hooks/library_handle.h>
+
+namespace {
+
+// Version 1 of the hooks framework.
+const int BIND10_HOOKS_VERSION = 1;
+
+// Names of the framework functions.
+const char* const LOAD_FUNCTION_NAME = "load";
+const char* const UNLOAD_FUNCTION_NAME = "unload";
+const char* const VERSION_FUNCTION_NAME = "version";
+
+// Typedefs for pointers to the framework functions.
+typedef int (*version_function_ptr)();
+typedef int (*load_function_ptr)(isc::hooks::LibraryHandle&);
+typedef int (*unload_function_ptr)();
+
+} // Anonymous namespace
+
+#endif // HOOKS_H
diff --git a/src/lib/hooks/hooks_component_developer.dox b/src/lib/hooks/hooks_component_developer.dox
new file mode 100644
index 0000000..edf59a8
--- /dev/null
+++ b/src/lib/hooks/hooks_component_developer.dox
@@ -0,0 +1,483 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/**
+ at page hooksComponentDeveloperGuide Guide to Hooks for the BIND 10 Component Developer
+
+ at section hooksComponentIntroduction Introduction
+
+The hooks framework is a BIND 10 system that simplifies the way that
+users can write code to modify the behavior of BIND 10. Instead of
+altering the BIND 10 source code, they write functions that are compiled
+and linked into a shared library. The library is specified in the BIND 10
+configuration database and run time, BIND 10 dynamically loads the library
+into its address space. At various points in the processing, the component
+"calls out" to functions in the library, passing to them the data is it
+currently working on. They can examine and modify the data as required.
+
+This guide is aimed at BIND 10 developers who want to write or modify a
+BIND 10 component to use hooks. It shows how the component should be written
+to load a shared library at run-time and how to call functions in it.
+
+For information about writing a hooks library containing functions called by BIND 10
+during its execution, see the document @ref hooksDevelopersGuide.
+
+ at subsection hooksComponentTerminology Terminology
+
+In the remainder of this guide, the following terminology is used:
+
+- Component - a BIND 10 process, e.g. the authoritative DNS server or the
+DHCPv4 server.
+
+- Hook/Hook Point - used interchageably, this is a point in the code at
+which a call to user-written functions is made. Each hook has a name and
+each hook can have any number (including 0) of user-written functions
+attached to it.
+
+- Callout - a user-written function called by the component at a hook
+point. This is so-named because the component "calls out" to the library
+to execute a user-written function.
+
+- User code/user library - non-BIND 10 code that is compiled into a
+shared library and loaded by BIND 10 into its address space. Multiple
+user libraries can be loaded at the same time, each containing callouts for
+the same hooks. The hooks framework calls these libraries one after the
+other. (See the document @ref hooksDevelopersGuide for more details.)
+
+ at subsection hooksComponentLanguages Languages
+
+The core of BIND 10 is written in C++ with some parts in Python. While it is
+the intention to provide the hooks framework for all languages, the initial
+version is for C++. All examples in this guide are in that language.
+
+ at section hooksComponentBasicIdeas Basic Ideas
+
+From the point of view of the component author, the basic ideas of the hooks
+framework are quite simple:
+
+- The location of hook points in the code need to be determined.
+
+- Name the hook points and register them.
+
+- At each hook point, the component needs to complete the following steps to
+ execute callouts registered by the user-library:
+ -# copy data into the object used to pass information to the callout.
+ -# call the callout.
+ -# copy data back from the object used to exchange information.
+ -# take action based on information returned.
+
+Of course, to set up the system the libraries need to be loaded in the first
+place. The component also needs to:
+
+- Define the configuration item that specifies the user libraries for this
+component.
+
+- Handle configuration changes and load/unload the user libraries.
+
+The following sections will describe these tasks in more detail.
+
+ at section hooksComponentDefinition Determing the Hook Points
+
+Before any other action takes place, the location of the hook points
+in the code need to be determined. This of course depends on the
+component but as a general guideline, hook locations should be chosen
+where a callout is able to obtain useful information from BIND 10 and/or
+affect processing. Typically this means at the start or end of a major
+step in the processing of a request, at a point where either useful
+information can be passed to a callout and/or the callout can affect
+the processing of the component. The latter is achieved in either or both
+of the following eays:
+
+- Setting the "skip" flag. This is a boolean flag that the callout can set
+ and is a quick way of passing information back to the component. It is used
+ to indicate that the component should skip the processing step associated with
+ the hook. The exact action is up to the component, but is likely to be one
+ of skipping the processing step (probably because the callout has
+ done its own processing for the action) or dropping the current packet
+ and starting on a new request.
+
+- Modifying data passed to it. The component should be prepared to continue
+ processing with the data returned by the callout. It is up to the component
+ author whether the data is validated before being used, but doing so will
+ have performance implications.
+
+ at section hooksComponentRegistration Naming and Registering the Hooks Points
+
+Once the location of the hook point has been determined, it should be
+given a name. This name should be unique amongst all hook points and is
+subject to certain restrictions (see below).
+
+Before the callouts at any hook point are called and any user libraries
+loaded - so typically during component initialization - the component must
+register the names of all the hooks. The registration is done using
+the static method isc::hooks::HooksManager::registerHook():
+
+ at code
+
+#include <hooks/hooks_manager.h>
+ :
+ int example_index = HooksManager::registerHook("lease_allocate");
+ at endcode
+
+The name of the hook is passed as the sole argument to the registerHook()
+method. The value returned is the index of that hook point and should
+be retained - it is needed to call the callouts attached to that hook.
+
+Note that a hook only needs to be registered once. There is no mechanism for
+unregistering a hook and there is no need to do so.
+
+ at subsection hooksComponentAutomaticRegistration Automatic Registration of Hooks
+
+In some components, it may be convenient to set up a single initialization
+function that registers all hooks. For others, it may be more convenient
+for each module within the component to perform its own initialization.
+Since the isc::hooks::HooksManager object is a singleton and is created when first
+accessed, a useful trick is to automatically register the hooks when
+the module is loaded.
+
+This technique involves declaring an object outside of any execution
+unit in the module. When the module is loaded, the object's constructor
+is run. By placing the hook registration calls in the constructor,
+the hooks in the module are defined at load time, before any function in
+the module is run. The code for such an initialization sequence would
+be similar to:
+
+ at code
+#include <hooks/hooks_manager.h>
+
+namespace {
+
+// Declare structure to perform initialization and store the hook indexes.
+//
+struct MyHooks {
+ int pkt_rcvd; // Index of "packet received" hook
+ int pkt_sent; // Index of "packet sent" hook
+
+ // Constructor
+ MyHooks() {
+ pkt_rcvd = HooksManager::registerHook("pkt_rcvd");
+ pkt_sent = HooksManager::registerHook("pkt_sent");
+ }
+};
+
+// Declare a "MyHooks" object. As this is outside any function or method, it
+// will be instantiated (and the constructor run) when the module is loaded.
+// As a result, the hook indexes will be defined before any method in this
+// module is called.
+MyHooks my_hooks;
+
+} // Anonymous namespace
+
+void Someclass::someFunction() {
+ :
+ // Check if any callouts are defined on the pkt_rcvd hook.
+ if (HooksManager::calloutPresent(my_hooks.pkt_rcvd)) {
+ :
+ }
+ :
+}
+ at endcode
+
+ at subsection hooksComponentHookNames Hook Names
+
+Hook names are strings and in principle, any string can be used as the
+name of a hook, even one containing spaces and non-printable characters.
+However, the following guidelines should be observed:
+
+- The names <b>context_create</b> and <b>context_destroy</b> are reserved to
+the hooks system and are automatically registered: an attempt to register
+one of these will lead to a isc::hooks::DuplicateHook exception being thrown.
+
+- The hook name should be a valid "C" function name. If a user gives a
+callout the same name as one of the hooks, the hooks framework will
+automatically load that callout and attach it to the hook: the user does not
+have to explicitly register it.
+
+- The hook name should not conflict with the name of a function in any of
+the system libraries (e.g. naming a hook "sqrt" could lead to the
+square-root function in the system's maths library being attached to the hook
+as a callout).
+
+- Although hook names can be in any case (including mixed case), the BIND 10
+convention is that they are lower-case.
+
+ at section hooksComponentCallingCallouts Calling Callouts on a Hook
+
+ at subsection hooksComponentArgument The Callout Handle
+
+Before describing how to call user code at a hook point, we must first consider
+how to pass data to it.
+
+Each user callout has the signature:
+ at code
+int callout_name(isc::hooks::CalloutHandle& handle);
+ at endcode
+
+The isc::hooks::CalloutHandle object is the object used to pass data to
+and from the callout. This holds the data as a set of name/value pairs,
+each pair being considered an argument to the callout. If there are
+multiple callouts attached to a hook, the CalloutHandle is passed to
+each in turn. Should a callout modify an argument, the updated data is
+passed subsequent callouts (each of which could also modify it) before
+being returned to the component.
+
+Two methods are provided to get and set the arguments passed to
+the callout called (naturally enough) getArgument and SetArgument.
+Their usage is illustrated by the following code snippets.
+
+ at code
+ int count = 10;
+ boost::shared_ptr<Pkt4> pktptr = ... // Set to appropriate value
+
+ // Assume that "handle_ptr" has been created and is a pointer to a
+ // CalloutHandle.
+ handle_ptr->setArgument("data_count", count);
+ handle_ptr->setArgument("inpacket", pktptr);
+
+ // Call the hook code. lease_assigned_index is the value returned from
+ // HooksManager::registerHook() when the hook was registered.
+ HooksManager::callCallouts(lease_assigned_index, *handle_ptr);
+
+ // Retrieve the modified values
+ handle_ptr->getArgument("data_count", count);
+ handle_ptr->getArgument("inpacket", pktptr);
+ at endcode
+
+As can be seen "getArgument" is used to retrieve data from the
+CalloutHandle, and "setArgument" used to put data into it. If a callout
+wishes to alter data and pass it back to the component, it should retrieve
+the data with getArgument, modify it, and call setArgument to send
+it back.
+
+There are a couple points to be aware of:
+
+- The data type of the variable in the call to getArgument must
+match the data type of the variable passed to the corresponding
+setArgument <B>exactly</B>: using what would normally be considered
+to be a "compatible" type is not enough. For example, if the callout
+passed an argument back to the component as an "int" and the component
+attempted to retrieve it as a "long", an exception would be thrown even
+though any value that can be stored in an "int" will fit into a "long".
+This restriction also applies the "const" attribute but only as applied to
+data pointed to by pointers, e.g. if an argument is defined as a "char*",
+an exception will be thrown if an attempt is made to retrieve it into
+a variable of type "const char*". (However, if an argument is set as a
+"const int", it can be retrieved into an "int".) The documentation of
+a hook point should detail the exact data type of each argument.
+
+- If a pointer to an object is passed to a callout (either a "raw"
+pointer, or a boost smart pointer (as in the example above), and the
+underlying object is altered through that pointer, the change will be
+reflected in the component even if the callout makes no call to setArgument.
+This can be avoided by passing a pointer to a "const" object.
+
+ at subsection hooksComponentSkipFlag The Skip Flag
+
+Although information is passed back to the component from callouts through
+CalloutHandle arguments, a common action for callouts is to inform the component
+that its flow of control should be altered. For example:
+
+- In the DHCP servers, there is a hook at the point at which a lease is
+ about to be assigned. Callouts attached to this hooks may handle the
+ lease assignment in special cases, in which case they set the skip flag
+ to indicate that the server should not perform lease assignment in this
+ case.
+- A server may define a hook just after a packet is received. A callout
+ attached to the hook might inspect the source address and compare it
+ against a blacklist. If the address is on the list, the callout could set
+ the skip flag to indicate to the server that the packet should be dropped.
+
+For ease of processing, the CalloutHandle contains
+two methods, isc::hooks::CalloutHandle::getSkip() and
+isc::hooks::CalloutHandle::setSkip(). It is only meaningful for the
+component to use the "get" method. The skip flag is cleared by the hooks
+framework when the component requests that callouts be executed, so any
+value set by the component is lost. Callouts can both inspect the flag (it
+might have been set by callouts earlier in the callout list for the hook)
+and set it. Note that the setting of the flag by a callout does not
+prevent callouts later in the list from being called: the skip flag is
+just a boolean flag - the only significance comes from its interpretation
+by the component.
+
+An example of use could be:
+ at code
+// Set up arguments for DHCP lease assignment.
+handle->setArgument("query", query);
+handle->setArgument("response", response);
+HooksManager::callCallouts(lease_hook_index, *handle_ptr);
+if (! handle_ptr->getSkip()) {
+ // Skip flag not set, do the address allocation
+ :
+}
+ at endcode
+
+
+ at subsection hooksComponentGettingHandle Getting the Callout Handle
+
+The CalloutHandle object is linked to the loaded libraries
+for lifetime reasons (described below). Components
+should retrieve a isc::hooks::CalloutHandle using
+isc::hooks::HooksManager::createCalloutHandle():
+ at code
+ CalloutHandlePtr handle_ptr = HooksManager::createCalloutHandle();
+ at endcode
+(isc::hooks::CalloutHandlePtr is a typedef for a Boost shared pointer to a
+CalloutHandle.) The CalloutHandle so retrieved may be used for as
+long as the libraries are loaded.
+
+The handle is deleted by resetting the pointer:
+ at code
+ handle_ptr.reset();
+ at endcode
+... or by letting the handle pointer go out of scope. The actual deletion
+occurs when the CallHandle's reference count goes to zero. (The
+current version of the hooks framework does not maintain any other
+pointer to the returned CalloutHandle, so it gets destroyed when the
+shared pointer to it is cleared or destroyed. However, this may change
+in a future version.)
+
+ at subsection hooksComponentCallingCallout Calling the Callout
+
+Calling the callout is a simple matter of executing the
+isc::hooks::HooksManager::callCallouts() method for the hook index in
+question. For example, with the hook index pkt_sent defined as above,
+the hook can be executed by:
+ at code
+ HooksManager::callCallouts(pkt_sent, *handle_ptr);
+ at endcode
+... where "*handle_ptr" is a reference (note: not a pointer) to the
+isc::hooks::CalloutHandle object holding the arguments. No status code
+is returned. If a component needs to get data returned (other than that
+provided by the "skip" flag), it should define an argument through which
+the callout can do so.
+
+ at subsubsection hooksComponentConditionalCallout Conditionally Calling Hook Callouts
+
+Most hooks in a component will not have callouts attached to them. To
+avoid the overhead of setting up arguments in the CalloutHandle, a
+component can check for callouts before doing that processing using
+isc::hooks::HooksManager::calloutsPresent(). Taking the index of a
+hook as its sole argument, the function returns true if there are any
+callouts attached to the hook and false otherwise.
+
+With this check, the code in the component for calling a hook would look
+something like:
+ at code
+if (HooksManager::calloutsPresent(lease_hook_index)) {
+ // Set up arguments for lease assignment
+ handle->setArgument("query", query);
+ handle->setArgument("response", response);
+ HooksManager::callCallouts(lease_hook_index, *handle);
+ if (! handle->getSkip()) {
+ // Skip flag not set, do the address allocation
+ :
+ }
+}
+ at endcode
+
+ at section hooksComponentLoadLibraries Loading the User Libraries
+
+Once hooks are defined, all the hooks code described above will
+work, even if no libraries are loaded (and even if the library
+loading method is not called). The CalloutHandle returned by
+isc::hooks::HooksManager::createCalloutHandle() will be valid,
+isc::hooks::HooksManager::calloutsPresent() will return false for every
+index, and isc::hooks::HooksManager::callCallouts() will be a no-op.
+
+However, if user libraries are specified in the BIND 10 configuration,
+the component should load them. (Note the term "libraries": the hooks
+framework allows multiple user libraries to be loaded.) This should take
+place after the component's configuration has been read, and is achieved
+by the isc::hooks::HooksManager::loadLibraries() method. The method is
+passed a vector of strings, each giving the full file specification of
+a user library:
+ at code
+ std::vector<std::string> libraries = ... // Get array of libraries
+ bool success = HooksManager::loadLibraries(libraries);
+ at endcode
+loadLibraries() returns a boolean status which is true if all libraries
+loaded successfully or false if one or more failed to load. Appropriate
+error messages will have been logged in the latter case, the status
+being more to allow the developer to decide whether the execution
+should proceed in such circumstances.
+
+If loadLibraries() is called a second or subsequent time (as a result
+of a reconfiguration), all existing libraries are unloaded and a new
+set loaded. Libraries can be explicitly unloaded either by calling
+isc::hooks::HooksManager::unloadLibraries() or by calling
+loadLibraries() with an empty vector as an argument.
+
+ at subsection hooksComponentUnloadIssues Unload and Reload Issues
+
+Unloading a shared library works by unmapping the part of the process's
+virtual address space in which the library lies. This may lead to
+problems if there are still references to that address space elsewhere
+in the process.
+
+In many operating systems, heap storage allowed by a shared library will
+lie in the virtual address allocated to the library. This has implications
+in the hooks framework because:
+
+- Argument information stored in a CalloutHandle by a callout in a library
+may lie in the library's address space.
+- Data modified in objects passed as arguments may lie in the address
+space. For example, it is common for a DHCP callout to add "options"
+to a packet: the memory allocated for those options will most likely
+lie in library address space.
+
+The problem really arises because of the extensive use by BIND 10 of boost
+smart pointers. When the pointer is destroyed, the pointed-to memory is
+deallocated. If the pointer points to address space that is unmapped because
+a library has been unloaded, the deletion causes a segmentation fault.
+
+The hooks framework addresses the issue for CalloutHandles by keeping in
+that object a shared pointer to the object controlling library unloading.
+Although a library can be unloaded at any time, it is only when all
+CalloutHandles that could possibly reference address space in the library
+have been deleted that the library will actually be unloaded and the
+address space unmapped.
+
+The hooks framework cannot solve the second issue as the objects in
+question are under control of the component. It is up to the component
+developer to ensure that all such objects have been destroyed before
+libraries are reloaded. In extreme cases this may mean the component
+suspending all processing of incoming requests until all currently
+executing requests have completed and data object destroyed, reloading
+the libraries, then resuming processing.
+
+ at section hooksComponentCallouts Component-Defined Callouts
+
+Previous sections have discussed callout registration by user libraries.
+It is possible for a component to register its own functions (i.e. within
+its own address space) as hook callouts. These functions are called
+in eactly the same way as user callouts, being passed their arguments
+though a CalloutHandle object. (Guidelines for writing callouts can be
+found in @ref hooksDevelopersGuide.)
+
+A component can associate with a hook callouts that run either before
+user-registered callouts or after them. Registration is done via a
+isc::hooks::LibraryHandle object, a reference to one being obtained
+through the methods isc::hooks::HooksManager::preCalloutLibraryHandle()
+(for a handle to register callouts to run before the user library
+callouts) or isc::hooks::HooksManager::postCalloutLibraryHandle() (for
+a handle to register callouts to run after the user callouts). Use of
+the LibraryHandle to register and deregister callouts is described in
+ at ref hooksLibraryHandle.
+
+Finally, it should be noted that callouts registered in this way only
+remain registered until the next call to isc::hooks::loadLibraries().
+It is up to the component to re-register the callouts after this
+method has been called.
+
+*/
diff --git a/src/lib/hooks/hooks_log.cc b/src/lib/hooks/hooks_log.cc
new file mode 100644
index 0000000..360394c
--- /dev/null
+++ b/src/lib/hooks/hooks_log.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// Defines the logger used by the NSAS
+
+#include "hooks/hooks_log.h"
+
+namespace isc {
+namespace hooks {
+
+isc::log::Logger hooks_logger("hooks");
+
+} // namespace hooks
+} // namespace isc
+
diff --git a/src/lib/hooks/hooks_log.h b/src/lib/hooks/hooks_log.h
new file mode 100644
index 0000000..92d429a
--- /dev/null
+++ b/src/lib/hooks/hooks_log.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef HOOKS_LOG_H
+#define HOOKS_LOG_H
+
+#include <log/macros.h>
+#include <hooks/hooks_messages.h>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Hooks debug Logging levels
+///
+/// Defines the levels used to output debug messages in the Hooks framework.
+/// Note that higher numbers equate to more verbose (and detailed) output.
+
+// The first level traces normal operations,
+const int HOOKS_DBG_TRACE = DBGLVL_TRACE_BASIC;
+
+// The next level traces each call to hook code.
+const int HOOKS_DBG_CALLS = DBGLVL_TRACE_BASIC_DATA;
+
+// Additional information on the calls. Report each call to a callout (even
+// if there are multiple callouts on a hook) and each status return.
+const int HOOKS_DBG_EXTENDED_CALLS = DBGLVL_TRACE_DETAIL_DATA;
+
+
+/// @brief Hooks Logger
+///
+/// Define the logger used to log messages. We could define it in multiple
+/// modules, but defining in a single module and linking to it saves time and
+/// space.
+extern isc::log::Logger hooks_logger;
+
+} // namespace hooks
+} // namespace isc
+
+#endif // HOOKS_LOG_H
diff --git a/src/lib/hooks/hooks_manager.cc b/src/lib/hooks/hooks_manager.cc
new file mode 100644
index 0000000..39e1fc5
--- /dev/null
+++ b/src/lib/hooks/hooks_manager.cc
@@ -0,0 +1,173 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/library_manager_collection.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Constructor
+
+HooksManager::HooksManager() {
+}
+
+// Return reference to singleton hooks manager.
+
+HooksManager&
+HooksManager::getHooksManager() {
+ static HooksManager manager;
+ return (manager);
+}
+
+// Are callouts present?
+
+bool
+HooksManager::calloutsPresentInternal(int index) {
+ conditionallyInitialize();
+ return (callout_manager_->calloutsPresent(index));
+}
+
+bool
+HooksManager::calloutsPresent(int index) {
+ return (getHooksManager().calloutsPresentInternal(index));
+}
+
+// Call the callouts
+
+void
+HooksManager::callCalloutsInternal(int index, CalloutHandle& handle) {
+ conditionallyInitialize();
+ return (callout_manager_->callCallouts(index, handle));
+}
+
+void
+HooksManager::callCallouts(int index, CalloutHandle& handle) {
+ return (getHooksManager().callCalloutsInternal(index, handle));
+}
+
+// Load the libraries. This will delete the previously-loaded libraries
+// (if present) and load new ones.
+
+bool
+HooksManager::loadLibrariesInternal(const std::vector<std::string>& libraries) {
+ // Unload current set of libraries (if any are loaded).
+ unloadLibrariesInternal();
+
+ // Create the library manager and load the libraries.
+ lm_collection_.reset(new LibraryManagerCollection(libraries));
+ bool status = lm_collection_->loadLibraries();
+
+ // ... and obtain the callout manager for them.
+ callout_manager_ = lm_collection_->getCalloutManager();
+
+ return (status);
+}
+
+bool
+HooksManager::loadLibraries(const std::vector<std::string>& libraries) {
+ return (getHooksManager().loadLibrariesInternal(libraries));
+}
+
+// Unload the libraries. This just deletes all internal objects which will
+// cause the libraries to be unloaded.
+
+void
+HooksManager::unloadLibrariesInternal() {
+ // The order of deletion does not matter here, as each library manager
+ // holds its own pointer to the callout manager. However, we may as
+ // well delete the library managers first: if there are no other references
+ // to the callout manager, the second statement will delete it, which may
+ // ease debugging.
+ lm_collection_.reset();
+ callout_manager_.reset();
+}
+
+void HooksManager::unloadLibraries() {
+ getHooksManager().unloadLibrariesInternal();
+}
+
+// Create a callout handle
+
+boost::shared_ptr<CalloutHandle>
+HooksManager::createCalloutHandleInternal() {
+ conditionallyInitialize();
+ return (boost::shared_ptr<CalloutHandle>(
+ new CalloutHandle(callout_manager_, lm_collection_)));
+}
+
+boost::shared_ptr<CalloutHandle>
+HooksManager::createCalloutHandle() {
+ return (getHooksManager().createCalloutHandleInternal());
+}
+
+// Perform conditional initialization if nothing is loaded.
+
+void
+HooksManager::performConditionalInitialization() {
+
+ // Nothing present, so create the collection with any empty set of
+ // libraries, and get the CalloutManager.
+ vector<string> libraries;
+ lm_collection_.reset(new LibraryManagerCollection(libraries));
+ lm_collection_->loadLibraries();
+
+ callout_manager_ = lm_collection_->getCalloutManager();
+}
+
+// Shell around ServerHooks::registerHook()
+
+int
+HooksManager::registerHook(const std::string& name) {
+ return (ServerHooks::getServerHooks().registerHook(name));
+}
+
+// Return pre- and post- library handles.
+
+isc::hooks::LibraryHandle&
+HooksManager::preCalloutsLibraryHandleInternal() {
+ conditionallyInitialize();
+ return (callout_manager_->getPreLibraryHandle());
+}
+
+isc::hooks::LibraryHandle&
+HooksManager::preCalloutsLibraryHandle() {
+ return (getHooksManager().preCalloutsLibraryHandleInternal());
+}
+
+isc::hooks::LibraryHandle&
+HooksManager::postCalloutsLibraryHandleInternal() {
+ conditionallyInitialize();
+ return (callout_manager_->getPostLibraryHandle());
+}
+
+isc::hooks::LibraryHandle&
+HooksManager::postCalloutsLibraryHandle() {
+ return (getHooksManager().postCalloutsLibraryHandleInternal());
+}
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/hooks_manager.h b/src/lib/hooks/hooks_manager.h
new file mode 100644
index 0000000..03aa521
--- /dev/null
+++ b/src/lib/hooks/hooks_manager.h
@@ -0,0 +1,276 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef HOOKS_MANAGER_H
+#define HOOKS_MANAGER_H
+
+#include <hooks/server_hooks.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+// Forward declarations
+class CalloutHandle;
+class CalloutManager;
+class LibraryHandle;
+class LibraryManagerCollection;
+
+/// @brief Hooks Manager
+///
+/// This is the overall manager of the hooks framework and is the main class
+/// used by a BIND 10 module when handling hooks. It is responsible for the
+/// loading and unloading of user libraries, and for calling the callouts on
+/// each hook point.
+///
+/// The class is a singleton, the single instance of the object being accessed
+/// through the static getHooksManager() method.
+
+class HooksManager : boost::noncopyable {
+public:
+ /// @brief Get singleton hooks manager
+ ///
+ /// @return Reference to the singleton hooks manager.
+ static HooksManager& getHooksManager();
+
+ /// @brief Load and reload libraries
+ ///
+ /// Loads the list of libraries into the server address space. For each
+ /// library, the "standard" functions (ones with the same names as the
+ /// hook points) are configured and the libraries' "load" function
+ /// called.
+ ///
+ /// If libraries are already loaded, they are unloaded and the new
+ /// libraries loaded.
+ ///
+ /// If any library fails to load, an error message will be logged. The
+ /// remaining libraries will be loaded if possible.
+ ///
+ /// @param libraries List of libraries to be loaded. The order is
+ /// important, as it determines the order that callouts on the same
+ /// hook will be called.
+ ///
+ /// @return true if all libraries loaded without a problem, false if one or
+ /// more libraries failed to load. In the latter case, message will
+ /// be logged that give the reason.
+ static bool loadLibraries(const std::vector<std::string>& libraries);
+
+ /// @brief Unload libraries
+ ///
+ /// Unloads the loaded libraries and leaves the hooks subsystem in the
+ /// state it was after construction but before loadLibraries() is called.
+ ///
+ /// @note: This method should be used with caution - see the notes for
+ /// the class LibraryManager for pitfalls. In general, a server
+ /// should not call this method: library unloading will automatically
+ /// take place when new libraries are loaded, and when appropriate
+ /// objects are destroyed.
+ ///
+ /// @return true if all libraries unloaded successfully, false on an error.
+ /// In the latter case, an error message will have been output.
+ static void unloadLibraries();
+
+ /// @brief Are callouts present?
+ ///
+ /// Checks loaded libraries and returns true if at lease one callout
+ /// has been registered by them for the given hook.
+ ///
+ /// @param index Hooks index for which callouts are checked.
+ ///
+ /// @return true if callouts are present, false if not.
+ /// @throw NoSuchHook Given index does not correspond to a valid hook.
+ static bool calloutsPresent(int index);
+
+ /// @brief Calls the callouts for a given hook
+ ///
+ /// Iterates through the libray handles and calls the callouts associated
+ /// with the given hook index.
+ ///
+ /// @note This method invalidates the current library index set with
+ /// setLibraryIndex().
+ ///
+ /// @param index Index of the hook to call.
+ /// @param handle Reference to the CalloutHandle object for the current
+ /// object being processed.
+ static void callCallouts(int index, CalloutHandle& handle);
+
+ /// @brief Return pre-callouts library handle
+ ///
+ /// Returns a library handle that can be used by the server to register
+ /// callouts on a hook that are called _before_ any callouts belonging
+ /// to a library.
+ ///
+ /// @note Both the reference returned and the callouts registered with
+ /// this handle only remain valid until the next loadLibraries() or
+ /// unloadLibraries() call. If the callouts are to remain registered
+ /// after this time, a new handle will need to be obtained and the
+ /// callouts re-registered.
+ ///
+ /// @return Reference to library handle associated with pre-library callout
+ /// registration.
+ static LibraryHandle& preCalloutsLibraryHandle();
+
+ /// @brief Return post-callouts library handle
+ ///
+ /// Returns a library handle that can be used by the server to register
+ /// callouts on a hook that are called _after any callouts belonging
+ /// to a library.
+ ///
+ /// @note Both the reference returned and the callouts registered with
+ /// this handle only remain valid until the next loadLibraries() or
+ /// unloadLibraries() call. If the callouts are to remain registered
+ /// after this time, a new handle will need to be obtained and the
+ /// callouts re-registered.
+ ///
+ /// @return Reference to library handle associated with post-library callout
+ /// registration.
+ static LibraryHandle& postCalloutsLibraryHandle();
+
+ /// @brief Return callout handle
+ ///
+ /// Returns a callout handle to be associated with a request passed round
+ /// the system.
+ ///
+ /// @note This handle is valid only after a loadLibraries() call and then
+ /// only up to the next loadLibraries() call.
+ ///
+ /// @return Shared pointer to a CalloutHandle object.
+ static boost::shared_ptr<CalloutHandle> createCalloutHandle();
+
+ /// @brief Register Hook
+ ///
+ /// This is just a convenience shell around the ServerHooks::registerHook()
+ /// method. It - along with the definitions of the two hook indexes for
+ /// the context_create and context_destroy methods - means that server
+ /// authors only need to deal with HooksManager and CalloutHandle, and not
+ /// include any other hooks framework classes.
+ ///
+ /// @param name Name of the hook
+ ///
+ /// @return Index of the hook, to be used in subsequent hook-related calls.
+ /// This will be greater than or equal to zero (so allowing a
+ /// negative value to indicate an invalid index).
+ ///
+ /// @throws DuplicateHook A hook with the same name has already been
+ /// registered.
+ static int registerHook(const std::string& name);
+
+ /// Index numbers for pre-defined hooks.
+ static const int CONTEXT_CREATE = ServerHooks::CONTEXT_CREATE;
+ static const int CONTEXT_DESTROY = ServerHooks::CONTEXT_DESTROY;
+
+private:
+
+ /// @brief Constructor
+ ///
+ /// This is private as the object is a singleton and can only be addessed
+ /// through the getHooksManager() static method.
+ HooksManager();
+
+ //@{
+ /// The following methods correspond to similarly-named static methods,
+ /// but actually do the work on the singleton instance of the HooksManager.
+ /// See the descriptions of the static methods for more details.
+
+ /// @brief Load and reload libraries
+ ///
+ /// @param libraries List of libraries to be loaded. The order is
+ /// important, as it determines the order that callouts on the same
+ /// hook will be called.
+ ///
+ /// @return true if all libraries loaded without a problem, false if one or
+ /// more libraries failed to load. In the latter case, message will
+ /// be logged that give the reason.
+ bool loadLibrariesInternal(const std::vector<std::string>& libraries);
+
+ /// @brief Unload libraries
+ void unloadLibrariesInternal();
+
+ /// @brief Are callouts present?
+ ///
+ /// @param index Hooks index for which callouts are checked.
+ ///
+ /// @return true if callouts are present, false if not.
+ /// @throw NoSuchHook Given index does not correspond to a valid hook.
+ bool calloutsPresentInternal(int index);
+
+ /// @brief Calls the callouts for a given hook
+ ///
+ /// @param index Index of the hook to call.
+ /// @param handle Reference to the CalloutHandle object for the current
+ /// object being processed.
+ void callCalloutsInternal(int index, CalloutHandle& handle);
+
+ /// @brief Return callout handle
+ ///
+ /// @return Shared pointer to a CalloutHandle object.
+ boost::shared_ptr<CalloutHandle> createCalloutHandleInternal();
+
+ /// @brief Return pre-callouts library handle
+ ///
+ /// @return Reference to library handle associated with pre-library callout
+ /// registration.
+ LibraryHandle& preCalloutsLibraryHandleInternal();
+
+ /// @brief Return post-callouts library handle
+ ///
+ /// @return Reference to library handle associated with post-library callout
+ /// registration.
+ LibraryHandle& postCalloutsLibraryHandleInternal();
+
+ //@}
+
+ /// @brief Initialization to No Libraries
+ ///
+ /// Initializes the hooks manager with an "empty set" of libraries. This
+ /// method is called if conditionallyInitialize() determines that such
+ /// initialization is needed.
+ void performConditionalInitialization();
+
+ /// @brief Conditional initialization of the hooks manager
+ ///
+ /// loadLibraries() performs the initialization of the HooksManager,
+ /// setting up the internal structures and loading libraries. However,
+ /// in some cases, server authors may not do that. This method is called
+ /// whenever any hooks execution function is invoked (checking callouts,
+ /// calling callouts or returning a callout handle). If the HooksManager
+ /// is unitialised, it will initialize it with an "empty set" of libraries.
+ ///
+ /// For speed, the test of whether initialization is required is done
+ /// in-line here. The actual initialization is performed in
+ /// performConditionalInitialization().
+ void conditionallyInitialize() {
+ if (!lm_collection_) {
+ performConditionalInitialization();
+ }
+ }
+
+ // Members
+
+ /// Set of library managers.
+ boost::shared_ptr<LibraryManagerCollection> lm_collection_;
+
+ /// Callout manager for the set of library managers.
+ boost::shared_ptr<CalloutManager> callout_manager_;
+};
+
+} // namespace util
+} // namespace hooks
+
+#endif // HOOKS_MANAGER_H
diff --git a/src/lib/hooks/hooks_messages.mes b/src/lib/hooks/hooks_messages.mes
new file mode 100644
index 0000000..33c1282
--- /dev/null
+++ b/src/lib/hooks/hooks_messages.mes
@@ -0,0 +1,167 @@
+# Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::hooks
+
+% HOOKS_CALLOUT_CALLED hooks library with index %1 has called a callout on hook %2 that has address %3
+Only output at a high debugging level, this message indicates that
+a callout on the named hook registered by the library with the given
+index (in the list of loaded libraries) has been called and returned a
+success state. The address of the callout is given in the message
+
+% HOOKS_CALLOUT_ERROR error returned by callout on hook %1 registered by library with index %2 (callout address %3)
+If a callout returns an error status when called, this error message
+is issued. It identifies the hook to which the callout is attached, the
+index of the library (in the list of loaded libraries) that registered
+it and the address of the callout. The error is otherwise ignored.
+
+% HOOKS_CALLOUTS_REMOVED callouts removed from hook %1 for library %2
+This is a debug message issued during library unloading. It notes that
+one of more callouts registered by that library have been removed from
+the specified hook. This is similar to the HOOKS_DEREGISTER_ALL_CALLOUTS
+message (and the two are likely to be seen together), but is issued at a
+higher-level in the hook framework.
+
+% HOOKS_CLOSE_ERROR failed to close hook library %1: %2
+BIND 10 has failed to close the named hook library for the stated reason.
+Although this is an error, this should not affect the running system
+other than as a loss of resources. If this error persists, you should
+restart BIND 10.
+
+% HOOKS_CALLOUT_EXCEPTION exception thrown by callout on hook %1 registered by library with index %2 (callout address %3)
+If a callout throws an exception when called, this error message is
+issued. It identifies the hook to which the callout is attached, the
+index of the library (in the list of loaded libraries) that registered
+it and the address of the callout. The error is otherwise ignored.
+
+% HOOKS_ALL_CALLOUTS_DEREGISTERED hook library at index %1 removed all callouts on hook %2
+A debug message issued when all callouts on the specified hook registered
+by the library with the given index were removed. This is similar to
+the HOOKS_CALLOUTS_REMOVED message (and the two are likely to be seen
+together), but is issued at a lower-level in the hook framework.
+
+% HOOKS_CALLOUT_DEREGISTERED hook library at index %1 deregistered a callout on hook %2
+A debug message issued when all instances of a particular callouts on
+the hook identified in the message that were registered by the library
+with the given index have been removed.
+
+% HOOKS_INCORRECT_VERSION hook library %1 is at version %2, require version %3
+BIND 10 has detected that the named hook library has been built against
+a version of BIND 10 that is incompatible with the version of BIND 10
+running on your system. It has not loaded the library.
+
+This is most likely due to the installation of a new version of BIND 10
+without rebuilding the hook library. A rebuild and re-install of the
+library should fix the problem in most cases.
+
+% HOOKS_LIBRARY_LOADED hooks library %1 successfully loaded
+This information message is issued when a user-supplied hooks library
+has been successfully loaded.
+
+% HOOKS_LIBRARY_UNLOADED hooks library %1 successfully unloaded
+This information message is issued when a user-supplied hooks library
+has been successfully unloaded.
+
+% HOOKS_LIBRARY_VERSION hooks library %1 reports its version as %2
+A debug message issued when the version check on the hooks library
+has succeeded.
+
+% HOOKS_LOAD_SUCCESS 'load' function in hook library %1 returned success
+This is a debug message issued when the "load" function has been found
+in a hook library and has been successfully called.
+
+% HOOKS_LOAD_ERROR 'load' function in hook library %1 returned error %2
+A "load" function was found in the library named in the message and
+was called. The function returned a non-zero status (also given in
+the message) which was interpreted as an error. The library has been
+unloaded and no callouts from it will be installed.
+
+% HOOKS_LOAD_EXCEPTION 'load' function in hook library %1 threw an exception
+A "load" function was found in the library named in the message and
+was called. The function threw an exception (an error indication)
+during execution, which is an error condition. The library has been
+unloaded and no callouts from it will be installed.
+
+% HOOKS_LIBRARY_LOADING loading hooks library %1
+This is a debug message output just before the specified library is loaded.
+If the action is successfully, it will be followed by the
+HOOKS_LIBRARY_LOADED informational message.
+
+% HOOKS_NO_LOAD no 'load' function found in hook library %1
+This is a debug message saying that the specified library was loaded
+but no function called "load" was found in it. Providing the library
+contained some "standard" functions (i.e. functions with the names of
+the hooks for the given server), this is not an issue.
+
+% HOOKS_NO_UNLOAD no 'unload' function found in hook library %1
+This is a debug message issued when the library is being unloaded.
+It merely states that the library did not contain an "unload" function.
+
+% HOOKS_NO_VERSION no 'version' function found in hook library %1
+The shared library named in the message was found and successfully loaded,
+but BIND 10 did not find a function named "version" in it. This function
+is required and should return the version of BIND 10 against which the
+library was built. The value is used to check that the library was built
+against a compatible version of BIND 10. The library has not been loaded.
+
+% HOOKS_OPEN_ERROR failed to open hook library %1: %2
+BIND 10 failed to open the specified hook library for the stated
+reason. The library has not been loaded. BIND 10 will continue to
+function, but without the services offered by the library.
+
+% HOOKS_CALLOUT_REGISTRATION hooks library with index %1 registering callout for hook '%2'
+This is a debug message, output when a library (whose index in the list
+of libraries (being) loaded is given) registers a callout.
+
+% HOOKS_HOOK_REGISTERED hook %1 was registered
+This is a debug message indicating that a hook of the specified name
+was registered by a BIND 10 server. The server doing the logging is
+indicated by the full name of the logger used to log this message.
+
+% HOOKS_STD_CALLOUT_REGISTERED hooks library %1 registered standard callout for hook %2 at address %3
+This is a debug message, output when the library loading function has
+located a standard callout (a callout with the same name as a hook point)
+and registered it. The address of the callout is indicated.
+
+% HOOKS_HOOK_LIST_RESET the list of hooks has been reset
+This is a message indicating that the list of hooks has been reset.
+While this is usual when running the BIND 10 test suite, it should not be
+seen when running BIND 10 in a producion environment. If this appears,
+please report a bug through the usual channels.
+
+% HOOKS_UNLOAD_SUCCESS 'unload' function in hook library %1 returned success
+This is a debug message issued when an "unload" function has been found
+in a hook library during the unload process, called, and returned success.
+
+% HOOKS_UNLOAD_ERROR 'unload' function in hook library %1 returned error %2
+During the unloading of a library, an "unload" function was found.
+It was called, but returned an error (non-zero) status, resulting in
+the issuing of this message. The unload process continued after this
+message and the library has been unloaded.
+
+% HOOKS_UNLOAD_EXCEPTION 'unload' function in hook library %1 threw an exception
+During the unloading of a library, an "unload" function was found. It was
+called, but in the process generated an exception (an error indication).
+The unload process continued after this message and the library has
+been unloaded.
+
+% HOOKS_LIBRARY_UNLOADING unloading library %1
+This is a debug message called when the specified library is
+being unloaded. If all is successful, it will be followed by the
+HOOKS_LIBRARY_UNLOADED informational message.
+
+% HOOKS_VERSION_EXCEPTION 'version' function in hook library %1 threw an exception
+This error message is issued if the version() function in the specified
+hooks library was called and generated an exception. The library is
+considered unusable and will not be loaded.
diff --git a/src/lib/hooks/library_handle.cc b/src/lib/hooks/library_handle.cc
new file mode 100644
index 0000000..7f43116
--- /dev/null
+++ b/src/lib/hooks/library_handle.cc
@@ -0,0 +1,76 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+
+namespace isc {
+namespace hooks {
+
+// Callout manipulation - all deferred to the CalloutManager.
+
+void
+LibraryHandle::registerCallout(const std::string& name, CalloutPtr callout) {
+ // Reset library index if required, saving the current value.
+ int saved_index = callout_manager_->getLibraryIndex();
+ if (index_ >= 0) {
+ callout_manager_->setLibraryIndex(index_);
+ }
+
+ // Register the callout.
+ callout_manager_->registerCallout(name, callout);
+
+ // Restore the library index if required. We know that the saved index
+ // is valid for the number of libraries (or is -1, which is an internal
+ // state indicating there is no current library index) as we obtained it
+ // from the callout manager.
+ if (index_ >= 0) {
+ callout_manager_->setLibraryIndex(saved_index);
+ }
+}
+
+bool
+LibraryHandle::deregisterCallout(const std::string& name, CalloutPtr callout) {
+ int saved_index = callout_manager_->getLibraryIndex();
+ if (index_ >= 0) {
+ callout_manager_->setLibraryIndex(index_);
+ }
+
+ bool status = callout_manager_->deregisterCallout(name, callout);
+
+ if (index_ >= 0) {
+ callout_manager_->setLibraryIndex(saved_index);
+ }
+
+ return (status);
+}
+
+bool
+LibraryHandle::deregisterAllCallouts(const std::string& name) {
+ int saved_index = callout_manager_->getLibraryIndex();
+ if (index_ >= 0) {
+ callout_manager_->setLibraryIndex(index_);
+ }
+
+ bool status = callout_manager_->deregisterAllCallouts(name);
+
+ if (index_ >= 0) {
+ callout_manager_->setLibraryIndex(saved_index);
+ }
+
+ return (status);
+}
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/library_handle.h b/src/lib/hooks/library_handle.h
new file mode 100644
index 0000000..4fe47cd
--- /dev/null
+++ b/src/lib/hooks/library_handle.h
@@ -0,0 +1,149 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef LIBRARY_HANDLE_H
+#define LIBRARY_HANDLE_H
+
+#include <string>
+
+namespace isc {
+namespace hooks {
+
+// Forward declarations
+class CalloutHandle;
+class CalloutManager;
+
+/// Typedef for a callout pointer. (Callouts must have "C" linkage.)
+extern "C" {
+ typedef int (*CalloutPtr)(CalloutHandle&);
+};
+
+/// @brief Library handle
+///
+/// This class is accessed by the user library when registering callouts,
+/// either by the library's load() function, or by one of the callouts
+/// themselves.
+///
+/// It is really little more than a shell around the CalloutManager. By
+/// presenting this object to the user-library callouts, callouts can manage
+/// the callout list for their own library, but cannot affect the callouts
+/// registered by other libraries.
+///
+/// (This restriction is achieved by the CalloutManager maintaining the concept
+/// of the "current library". When a callout is registered - either by the
+/// library's load() function, or by a callout in the library - the registration
+/// information includes the library active at the time. When that callout is
+/// called, the CalloutManager uses that information to set the "current
+/// library": the registration functions only operator on data whose
+/// associated library is equal to the "current library".)
+
+class LibraryHandle {
+public:
+
+ /// @brief Constructor
+ ///
+ /// @param manager Back pointer to the containing CalloutManager.
+ /// This pointer is used to access appropriate methods in that
+ /// object. Note that the "raw" pointer is safe - the only
+ /// instance of the LibraryHandle in the system is as a member of
+ /// the CalloutManager to which it points.
+ ///
+ /// @param index Index of the library to which the LibraryHandle applies.
+ /// If negative, the library index as set in the CalloutManager is
+ /// used. Otherwise the current library index is saved, this value
+ /// is set as the current index, the registration call is made, and
+ /// the CalloutManager's library index is reset. Note: although -1
+ /// is a valid argument value for
+ /// isc::hooks::CalloutManager::setLibraryIndex(), in this class is
+ /// is used as a sentinel to indicate that the library index in
+ /// isc::util::CalloutManager should not be set or reset.
+ LibraryHandle(CalloutManager* manager, int index = -1)
+ : callout_manager_(manager), index_(index)
+ {}
+
+ /// @brief Register a callout on a hook
+ ///
+ /// Registers a callout function with a given hook. The callout is added
+ /// to the end of the callouts for the current library that are associated
+ /// with that hook.
+ ///
+ /// @param name Name of the hook to which the callout is added.
+ /// @param callout Pointer to the callout function to be registered.
+ ///
+ /// @throw NoSuchHook The hook name is unrecognised.
+ /// @throw Unexpected The hook name is valid but an internal data structure
+ /// is of the wrong size.
+ void registerCallout(const std::string& name, CalloutPtr callout);
+
+ /// @brief De-Register a callout on a hook
+ ///
+ /// Searches through the functions registered by the current library with
+ /// the named hook and removes all entries matching the callout. It does
+ /// not affect callouts registered by other libraries.
+ ///
+ /// @param name Name of the hook from which the callout is removed.
+ /// @param callout Pointer to the callout function to be removed.
+ ///
+ /// @return true if a one or more callouts were deregistered.
+ ///
+ /// @throw NoSuchHook The hook name is unrecognised.
+ /// @throw Unexpected The hook name is valid but an internal data structure
+ /// is of the wrong size.
+ bool deregisterCallout(const std::string& name, CalloutPtr callout);
+
+ /// @brief Removes all callouts on a hook
+ ///
+ /// Removes all callouts associated with a given hook that were registered.
+ /// by the current library. It does not affect callouts that were
+ /// registered by other libraries.
+ ///
+ /// @param name Name of the hook from which the callouts are removed.
+ ///
+ /// @return true if one or more callouts were deregistered.
+ ///
+ /// @throw NoSuchHook Thrown if the hook name is unrecognised.
+ bool deregisterAllCallouts(const std::string& name);
+
+private:
+ /// @brief Copy constructor
+ ///
+ /// Private (with no implementation) as it makes no sense to copy an object
+ /// of this type. All code receives a reference to an existing handle which
+ /// is tied to a particular CalloutManager. Creating a copy of that handle
+ /// runs the risk of a "dangling pointer" to the original handle's callout
+ /// manager.
+ ///
+ /// @param Unused - should be the object to copy.
+ LibraryHandle(const LibraryHandle&);
+
+ /// @brief Assignment operator
+ ///
+ /// Declared private like the copy constructor for the same reasons. It too
+ /// has no implementation.
+ ///
+ /// @param Unused - should be the object to copy.
+ LibraryHandle& operator=(const LibraryHandle&);
+
+ /// Back pointer to the collection object for the library
+ CalloutManager* callout_manager_;
+
+ /// Library index to which this handle applies. -1 indicates that it
+ /// applies to whatever index is current in the CalloutManager.
+ int index_;
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // LIBRARY_HANDLE_H
diff --git a/src/lib/hooks/library_manager.cc b/src/lib/hooks/library_manager.cc
new file mode 100644
index 0000000..f425508
--- /dev/null
+++ b/src/lib/hooks/library_manager.cc
@@ -0,0 +1,292 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/hooks.h>
+#include <hooks/hooks_log.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/library_manager.h>
+#include <hooks/pointer_converter.h>
+#include <hooks/server_hooks.h>
+
+#include <string>
+#include <vector>
+
+#include <dlfcn.h>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+
+// Open the library
+
+bool
+LibraryManager::openLibrary() {
+
+ // Open the library. We'll resolve names now, so that if there are any
+ // issues we don't bugcheck in the middle of apparently unrelated code.
+ dl_handle_ = dlopen(library_name_.c_str(), RTLD_NOW | RTLD_LOCAL);
+ if (dl_handle_ == NULL) {
+ LOG_ERROR(hooks_logger, HOOKS_OPEN_ERROR).arg(library_name_)
+ .arg(dlerror());
+ }
+
+ return (dl_handle_ != NULL);
+}
+
+// Close the library if not already open
+
+bool
+LibraryManager::closeLibrary() {
+
+ // Close the library if it is open. (If not, this is a no-op.)
+ int status = 0;
+ if (dl_handle_ != NULL) {
+ status = dlclose(dl_handle_);
+ dl_handle_ = NULL;
+ if (status != 0) {
+ LOG_ERROR(hooks_logger, HOOKS_CLOSE_ERROR).arg(library_name_)
+ .arg(dlerror());
+ }
+ }
+
+ return (status == 0);
+}
+
+// Check the version of the library
+
+bool
+LibraryManager::checkVersion() const {
+
+ // Get the pointer to the "version" function.
+ PointerConverter pc(dlsym(dl_handle_, VERSION_FUNCTION_NAME));
+ if (pc.versionPtr() != NULL) {
+ int version = BIND10_HOOKS_VERSION - 1; // This is an invalid value
+ try {
+ version = (*pc.versionPtr())();
+ } catch (...) {
+ LOG_ERROR(hooks_logger, HOOKS_VERSION_EXCEPTION).arg(library_name_);
+ return (false);
+ }
+
+ if (version == BIND10_HOOKS_VERSION) {
+ // All OK, version checks out
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_LIBRARY_VERSION)
+ .arg(library_name_).arg(version);
+ return (true);
+
+ } else {
+ LOG_ERROR(hooks_logger, HOOKS_INCORRECT_VERSION).arg(library_name_)
+ .arg(version).arg(BIND10_HOOKS_VERSION);
+ }
+ } else {
+ LOG_ERROR(hooks_logger, HOOKS_NO_VERSION).arg(library_name_);
+ }
+
+ return (false);
+}
+
+// Register the standard callouts
+
+void
+LibraryManager::registerStandardCallouts() {
+ // Set the library index for doing the registration. This is picked up
+ // when the library handle is created.
+ manager_->setLibraryIndex(index_);
+
+ // Iterate through the list of known hooks
+ vector<string> hook_names = ServerHooks::getServerHooks().getHookNames();
+ for (int i = 0; i < hook_names.size(); ++i) {
+
+ // Look up the symbol
+ void* dlsym_ptr = dlsym(dl_handle_, hook_names[i].c_str());
+ PointerConverter pc(dlsym_ptr);
+ if (pc.calloutPtr() != NULL) {
+ // Found a symbol, so register it.
+ manager_->getLibraryHandle().registerCallout(hook_names[i],
+ pc.calloutPtr());
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_STD_CALLOUT_REGISTERED)
+ .arg(library_name_).arg(hook_names[i]).arg(dlsym_ptr);
+
+ }
+ }
+}
+
+// Run the "load" function if present.
+
+bool
+LibraryManager::runLoad() {
+
+ // Get the pointer to the "load" function.
+ PointerConverter pc(dlsym(dl_handle_, LOAD_FUNCTION_NAME));
+ if (pc.loadPtr() != NULL) {
+
+ // Call the load() function with the library handle. We need to set
+ // the CalloutManager's index appropriately. We'll invalidate it
+ // afterwards.
+
+ int status = -1;
+ try {
+ manager_->setLibraryIndex(index_);
+ status = (*pc.loadPtr())(manager_->getLibraryHandle());
+ } catch (...) {
+ LOG_ERROR(hooks_logger, HOOKS_LOAD_EXCEPTION).arg(library_name_);
+ return (false);
+ }
+
+ if (status != 0) {
+ LOG_ERROR(hooks_logger, HOOKS_LOAD_ERROR).arg(library_name_)
+ .arg(status);
+ return (false);
+ } else {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LOAD_SUCCESS)
+ .arg(library_name_);
+ }
+
+ } else {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_NO_LOAD)
+ .arg(library_name_);
+ }
+
+ return (true);
+}
+
+
+// Run the "unload" function if present.
+
+bool
+LibraryManager::runUnload() {
+
+ // Get the pointer to the "load" function.
+ PointerConverter pc(dlsym(dl_handle_, UNLOAD_FUNCTION_NAME));
+ if (pc.unloadPtr() != NULL) {
+
+ // Call the load() function with the library handle. We need to set
+ // the CalloutManager's index appropriately. We'll invalidate it
+ // afterwards.
+ int status = -1;
+ try {
+ status = (*pc.unloadPtr())();
+ } catch (...) {
+ // Exception generated. Note a warning as the unload will occur
+ // anyway.
+ LOG_WARN(hooks_logger, HOOKS_UNLOAD_EXCEPTION).arg(library_name_);
+ return (false);
+ }
+
+ if (status != 0) {
+ LOG_ERROR(hooks_logger, HOOKS_UNLOAD_ERROR).arg(library_name_)
+ .arg(status);
+ return (false);
+ } else {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_UNLOAD_SUCCESS)
+ .arg(library_name_);
+ }
+ } else {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_NO_UNLOAD)
+ .arg(library_name_);
+ }
+
+ return (true);
+}
+
+// The main library loading function.
+
+bool
+LibraryManager::loadLibrary() {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LIBRARY_LOADING)
+ .arg(library_name_);
+
+ // In the following, if a method such as openLibrary() fails, it will
+ // have issued an error message so there is no need to issue another one
+ // here.
+
+ // Open the library (which is a check that it exists and is accessible).
+ if (openLibrary()) {
+
+ // Library opened OK, see if a version function is present and if so,
+ // check what value it returns.
+ if (checkVersion()) {
+
+ // Version OK, so now register the standard callouts and call the
+ // library's load() function if present.
+ registerStandardCallouts();
+ if (runLoad()) {
+
+ // Success - the library has been successfully loaded.
+ LOG_INFO(hooks_logger, HOOKS_LIBRARY_LOADED).arg(library_name_);
+ return (true);
+
+ } else {
+
+ // The load function failed, so back out. We can't just close
+ // the library as (a) we need to call the library's "unload"
+ // function (if present) in case "load" allocated resources that
+ // need to be freed and (b) we need to remove any callouts that
+ // have been installed.
+ static_cast<void>(unloadLibrary());
+ }
+ }
+
+ // Either the version check or call to load() failed, so close the
+ // library and free up resources. Ignore the status return here - we
+ // already know there's an error and will have output a message.
+ static_cast<void>(closeLibrary());
+ }
+
+ return (false);
+}
+
+// The library unloading function. Call the unload() function (if present),
+// remove callouts from the callout manager, then close the library.
+
+bool
+LibraryManager::unloadLibrary() {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LIBRARY_UNLOADING)
+ .arg(library_name_);
+
+ // Call the unload() function if present. Note that this is done first -
+ // operations take place in the reverse order to which they were done when
+ // the library was loaded.
+ bool result = runUnload();
+
+ // Regardless of status, remove all callouts associated with this library
+ // on all hooks.
+ vector<string> hooks = ServerHooks::getServerHooks().getHookNames();
+ manager_->setLibraryIndex(index_);
+ for (int i = 0; i < hooks.size(); ++i) {
+ bool removed = manager_->deregisterAllCallouts(hooks[i]);
+ if (removed) {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUTS_REMOVED)
+ .arg(hooks[i]).arg(library_name_);
+ }
+ }
+
+ // ... and close the library.
+ result = closeLibrary() && result;
+ if (result) {
+
+ // Issue the informational message only if the library was unloaded
+ // with no problems. If there was an issue, an error message would
+ // have been issued.
+ LOG_INFO(hooks_logger, HOOKS_LIBRARY_UNLOADED).arg(library_name_);
+ }
+
+ return (result);
+}
+
+} // namespace hooks
+} // namespace isc
diff --git a/src/lib/hooks/library_manager.h b/src/lib/hooks/library_manager.h
new file mode 100644
index 0000000..56c770b
--- /dev/null
+++ b/src/lib/hooks/library_manager.h
@@ -0,0 +1,190 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef LIBRARY_MANAGER_H
+#define LIBRARY_MANAGER_H
+
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+
+namespace isc {
+namespace hooks {
+
+class CalloutManager;
+class LibraryHandle;
+class LibraryManager;
+
+/// @brief Library manager
+///
+/// This class handles the loading and unloading of a specific library.
+///
+/// On loading, it opens the library using dlopen and checks the version (set
+/// with the "version" method. If all is OK, it iterates through the list of
+/// known hooks and locates their symbols, registering each callout as it does
+/// so. Finally it locates the "load" function (if present) and calls it.
+///
+/// On unload, it calls the "unload" method if present, clears the callouts on
+/// all hooks, and closes the library.
+///
+/// @note Caution needs to be exercised when using the unload method. During
+/// normal use, data will pass between the server and the library. In
+/// this process, the library may allocate memory and pass it back to the
+/// server. This could happen by the server setting arguments or context
+/// in the CalloutHandle object, or by the library modifying the content
+/// of pointed-to data. If the library is unloaded, this memory may lie
+/// in the virtual address space deleted in that process. (The word "may"
+/// is used, as this could be operating-system specific.) Should this
+/// happen, any reference to the memory will cause a segmentation fault.
+/// This can occur in a quite obscure place, for example in the middle of
+/// a destructor of an STL class when it is deleting memory allocated
+/// when the data structure was extended by a function in the library.
+///
+/// @note The only safe way to run the "unload" function is to ensure that all
+/// possible references to it are removed first. This means that all
+/// CalloutHandles must be destroyed, as must any data items that were
+/// passed to the callouts. In practice, it could mean that a server
+/// suspends processing of new requests until all existing ones have
+/// been serviced and all packet/context structures destroyed before
+/// reloading the libraries.
+
+class LibraryManager {
+public:
+ /// @brief Constructor
+ ///
+ /// Stores the library name. The actual loading is done in loadLibrary().
+ ///
+ /// @param name Name of the library to load. This should be an absolute
+ /// path name.
+ /// @param index Index of this library
+ /// @param manager CalloutManager object
+ LibraryManager(const std::string& name, int index,
+ const boost::shared_ptr<CalloutManager>& manager)
+ : dl_handle_(NULL), index_(index), manager_(manager),
+ library_name_(name)
+ {}
+
+ /// @brief Destructor
+ ///
+ /// If the library is open, closes it. This is principally a safety
+ /// feature to ensure closure in the case of an exception destroying this
+ /// object. However, see the caveat in the class header about when it is
+ /// safe to unload libraries.
+ ~LibraryManager() {
+ static_cast<void>(unloadLibrary());
+ }
+
+ /// @brief Loads a library
+ ///
+ /// Open the library and check the version. If all is OK, load all standard
+ /// symbols then call "load" if present.
+ ///
+ /// @return true if the library loaded successfully, false otherwise. In the
+ /// latter case, the library will be unloaded if possible.
+ bool loadLibrary();
+
+ /// @brief Unloads a library
+ ///
+ /// Calls the libraries "unload" function if present, the closes the
+ /// library.
+ ///
+ /// However, see the caveat in the class header about when it is safe to
+ /// unload libraries.
+ ///
+ /// @return true if the library unloaded successfully, false if an error
+ /// occurred in the process (most likely the unload() function
+ /// (if present) returned an error). Even if an error did occur,
+ /// the library is closed if possible.
+ bool unloadLibrary();
+
+ /// @brief Return library name
+ ///
+ /// @return Name of this library
+ std::string getName() const {
+ return (library_name_);
+ }
+
+protected:
+ // The following methods are protected as they are accessed in testing.
+
+ /// @brief Open library
+ ///
+ /// Opens the library associated with this LibraryManager. A message is
+ /// logged on an error.
+ ///
+ /// @return true if the library opened successfully, false otherwise.
+ bool openLibrary();
+
+ /// @brief Close library
+ ///
+ /// Closes the library associated with this LibraryManager. A message is
+ /// logged on an error.
+ ///
+ /// @return true if the library closed successfully, false otherwise. "true"
+ /// is also returned if the library were already closed when this
+ /// method was called.
+ bool closeLibrary();
+
+ /// @brief Check library version
+ ///
+ /// With the library open, accesses the "version()" function and, if
+ /// present, checks the returned value against the hooks version symbol
+ /// for the currently running BIND 10. The "version()" function is
+ /// mandatory and must be present (and return the correct value) for the
+ /// library to load.
+ ///
+ /// If there is no version() function, or if there is a mismatch in
+ /// version number, a message logged.
+ ///
+ /// @return bool true if the check succeeded
+ bool checkVersion() const;
+
+ /// @brief Register standard callouts
+ ///
+ /// Loops through the list of hook names and searches the library for
+ /// functions with those names. Any that are found are registered as
+ /// callouts for that hook.
+ void registerStandardCallouts();
+
+ /// @brief Run the load function if present
+ ///
+ /// Searches for the "load" framework function and, if present, runs it.
+ ///
+ /// @return bool true if not found or found and run successfully,
+ /// false on an error. In this case, an error message will
+ /// have been output.
+ bool runLoad();
+
+ /// @brief Run the unload function if present
+ ///
+ /// Searches for the "unload" framework function and, if present, runs it.
+ ///
+ /// @return bool true if not found or found and run successfully,
+ /// false on an error. In this case, an error message will
+ /// have been output.
+ bool runUnload();
+
+private:
+ void* dl_handle_; ///< Handle returned by dlopen
+ int index_; ///< Index associated with this library
+ boost::shared_ptr<CalloutManager> manager_;
+ ///< Callout manager for registration
+ std::string library_name_; ///< Name of the library
+
+};
+
+} // namespace hooks
+} // namespace isc
+
+#endif // LIBRARY_MANAGER_H
diff --git a/src/lib/hooks/library_manager_collection.cc b/src/lib/hooks/library_manager_collection.cc
new file mode 100644
index 0000000..d3f7f34
--- /dev/null
+++ b/src/lib/hooks/library_manager_collection.cc
@@ -0,0 +1,114 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_manager.h>
+#include <hooks/library_manager.h>
+#include <hooks/library_manager_collection.h>
+
+namespace isc {
+namespace hooks {
+
+// Return callout manager for the loaded libraries. This call is only valid
+// after one has been created for the loaded libraries (which includes the
+// case of no loaded libraries).
+//
+// Note that there is no real connection between the callout manager and the
+// libraries, other than it knows the number of libraries so can do sanity
+// checks on values passed to it. However, this may change in the future,
+// so the hooks framework is written such that a callout manager is used only
+// with the LibraryManagerCollection that created it. It is also the reason
+// why each LibraryManager contains a pointer to this CalloutManager.
+
+boost::shared_ptr<CalloutManager>
+LibraryManagerCollection::getCalloutManager() const {
+
+ // Only return a pointer if we have a CalloutManager created.
+ if (! callout_manager_) {
+ isc_throw(LoadLibrariesNotCalled, "must load hooks libraries before "
+ "attempting to retrieve a CalloutManager for them");
+ }
+
+ return (callout_manager_);
+}
+
+// Load a set of libraries
+
+bool
+LibraryManagerCollection::loadLibraries() {
+
+ // Unload libraries if any are loaded.
+ static_cast<void>(unloadLibraries());
+
+ // Create the callout manager. A pointer to this is maintained by
+ // each library. Note that the callout manager does not hold any memory
+ // allocated by a library: although a library registers a callout (and so
+ // causes the creation of an entry in the CalloutManager's callout list),
+ // that creation is done by the CalloutManager itself. The CalloutManager
+ // is created within the server.
+ //
+ // The upshot of this is that it is therefore safe for the CalloutManager
+ // to be deleted after all associated libraries are deleted, hence this
+ // link (LibraryManager -> CalloutManager) is safe.
+ callout_manager_.reset(new CalloutManager(library_names_.size()));
+
+ // Now iterate through the libraries are load them one by one. We'll
+ for (int i = 0; i < library_names_.size(); ++i) {
+ // Create a pointer to the new library manager. The index of this
+ // library is determined by the number of library managers currently
+ // loaded: note that the library indexes run from 1 to (number of loaded
+ // libraries).
+ boost::shared_ptr<LibraryManager> manager(
+ new LibraryManager(library_names_[i], lib_managers_.size() + 1,
+ callout_manager_));
+
+ // Load the library. On success, add it to the list of loaded
+ // libraries. On failure, an error will have been logged and the
+ // library closed.
+ if (manager->loadLibrary()) {
+ lib_managers_.push_back(manager);
+ }
+ }
+
+ // Update the CalloutManager's idea of the number of libraries it is
+ // handling.
+ callout_manager_->setNumLibraries(lib_managers_.size());
+
+ // Get an indication of whether all libraries loaded successfully.
+ bool status = (library_names_.size() == lib_managers_.size());
+
+ // Don't need the library names any more, so free up the space.
+ library_names_.clear();
+
+ return (status);
+}
+
+// Unload the libraries.
+
+void
+LibraryManagerCollection::unloadLibraries() {
+
+ // Delete the library managers in the reverse order to which they were
+ // created, then clear the library manager vector.
+ for (int i = lib_managers_.size() - 1; i >= 0; --i) {
+ lib_managers_[i].reset();
+ }
+ lib_managers_.clear();
+
+ // Get rid of the callout manager. (The other member, the list of library
+ // names, was cleared when the libraries were loaded.)
+ callout_manager_.reset();
+}
+
+} // namespace hooks
+} // namespace isc
diff --git a/src/lib/hooks/library_manager_collection.h b/src/lib/hooks/library_manager_collection.h
new file mode 100644
index 0000000..c0f6def
--- /dev/null
+++ b/src/lib/hooks/library_manager_collection.h
@@ -0,0 +1,133 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef LIBRARY_MANAGER_COLLECTION_H
+#define LIBRARY_MANAGER_COLLECTION_H
+
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+/// @brief LoadLibraries not called
+///
+/// Thrown if an attempt is made get a CalloutManager before the libraries
+/// have been loaded.
+class LoadLibrariesNotCalled : public Exception {
+public:
+ LoadLibrariesNotCalled(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+
+// Forward declarations
+class CalloutManager;
+class LibraryManager;
+
+/// @brief Library manager collection
+///
+/// The LibraryManagerCollection class, as the name implies, is responsible for
+/// managing the collection of LibraryManager objects that describe the loaded
+/// libraries. As such, it converts a single operation (e.g load libraries)
+/// into multiple operations, one per library. However, the class does more
+/// than that - it provides a single object with which to manage lifetimes.
+///
+/// As described in the LibraryManager documentation, a CalloutHandle may end
+/// up with pointers to memory within the address space of a loaded library.
+/// If the library is unloaded before this address space is deleted, the
+/// deletion of the CalloutHandle may attempt to free memory into the newly-
+/// unmapped address space and cause a segmentation fault.
+///
+/// To prevent this, each CalloutHandle maintains a shared pointer to the
+/// LibraryManagerCollection current when it was created. In addition, the
+/// containing HooksManager object also maintains a shared pointer to it. A
+/// a LibraryManagerCollection is never explicitly deleted: when a new set
+/// of libraries is loaded, the HooksManager clears its pointer to the
+/// collection. The LibraryManagerCollection is only destroyed when all
+/// CallHandle objects referencing it are destroyed.
+///
+/// Note that this does not completely solve the problem - a hook function may
+/// have modified a packet being processed by the server and that packet may
+/// hold a pointer to memory in the library's virtual address space. To avoid
+/// a segmentation fault, that packet needs to free the memory before the
+/// LibraryManagerCollection is destroyed and this places demands on the server
+/// code. However, the link with the CalloutHandle does at least mean that
+/// authors of server code do not need to be so careful about when they destroy
+/// CalloutHandles.
+
+class LibraryManagerCollection {
+public:
+ /// @brief Constructor
+ ///
+ /// @param 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)
+ {}
+
+ /// @brief Destructor
+ ///
+ /// Unloads all loaded libraries.
+ ~LibraryManagerCollection() {
+ static_cast<void>(unloadLibraries());
+ }
+
+ /// @brief Load libraries
+ ///
+ /// Loads the libraries. This creates the LibraryManager associated with
+ /// each library and calls its loadLibrary() method. If a library fails
+ /// to load, the fact is noted but attempts are made to load the remaining
+ /// libraries.
+ bool loadLibraries();
+
+ /// @brief Get callout manager
+ ///
+ /// Returns a callout manager that can be used with this set of loaded
+ /// libraries (even if the number of loaded libraries is zero). This
+ /// method may only be caslled after loadLibraries() has been called.
+ ///
+ /// @return Pointer to a callout manager for this set of libraries.
+ ///
+ /// @throw LoadLibrariesNotCalled Thrown if this method is called between
+ /// construction and the time loadLibraries() is called.
+ boost::shared_ptr<CalloutManager> getCalloutManager() const;
+
+protected:
+ /// @brief Unload libraries
+ ///
+ /// Unloads and closes all loaded libraries. They are unloaded in the
+ /// reverse order to the order in which they were loaded.
+ void unloadLibraries();
+
+private:
+
+ /// Vector of library names
+ std::vector<std::string> library_names_;
+
+ /// Vector of library managers
+ std::vector<boost::shared_ptr<LibraryManager> > lib_managers_;
+
+ /// Callout manager to be associated with the libraries
+ boost::shared_ptr<CalloutManager> callout_manager_;
+};
+
+} // namespace hooks
+} // namespace isc
+
+
+#endif // LIBRARY_MANAGER_COLLECTION_H
diff --git a/src/lib/hooks/pointer_converter.h b/src/lib/hooks/pointer_converter.h
new file mode 100644
index 0000000..1fe15ac
--- /dev/null
+++ b/src/lib/hooks/pointer_converter.h
@@ -0,0 +1,121 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef POINTER_CONVERTER_H
+#define POINTER_CONVERTER_H
+
+#include <hooks/hooks.h>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Local class for conversion of void pointers to function pointers
+///
+/// Converting between void* and function pointers in C++ is fraught with
+/// difficulty and pitfalls, e.g. see
+/// https://groups.google.com/forum/?hl=en&fromgroups#!topic/comp.lang.c++/37o0l8rtEE0
+///
+/// The method given in that article - convert using a union is used here. A
+/// union is declared (and zeroed) and the appropriate member extracted when
+/// needed.
+
+class PointerConverter {
+public:
+ /// @brief Constructor
+ ///
+ /// Zeroes the union and stores the void* pointer we wish to convert (the
+ /// one returned by dlsym).
+ ///
+ /// @param dlsym_ptr void* pointer returned by call to dlsym()
+ PointerConverter(void* dlsym_ptr) {
+ memset(&pointers_, 0, sizeof(pointers_));
+ pointers_.dlsym_ptr = dlsym_ptr;
+ }
+
+ /// @brief Constructor
+ ///
+ /// Zeroes the union and stores the CalloutPtr pointer we wish to convert.
+ /// This constructor is used in debug messages; output of a pointer to
+ /// an object (including to a function) is, on some compilers, printed as
+ /// "1".
+ ///
+ /// @param callout_ptr Pointer to callout function
+ PointerConverter(CalloutPtr callout_ptr) {
+ memset(&pointers_, 0, sizeof(pointers_));
+ pointers_.callout_ptr = callout_ptr;
+ }
+
+ /// @name Pointer accessor functions
+ ///
+ /// It is up to the caller to ensure that the correct member is called so
+ /// that the correct type of pointer is returned.
+ ///
+ ///@{
+
+ /// @brief Return pointer returned by dlsym call
+ ///
+ /// @return void* pointer returned by the call to dlsym(). This can be
+ /// used in statements that print the hexadecimal value of the
+ /// symbol.
+ void* dlsymPtr() const {
+ return (pointers_.dlsym_ptr);
+ }
+
+ /// @brief Return pointer to callout function
+ ///
+ /// @return Pointer to the callout function
+ CalloutPtr calloutPtr() const {
+ return (pointers_.callout_ptr);
+ }
+
+ /// @brief Return pointer to load function
+ ///
+ /// @return Pointer to the load function
+ load_function_ptr loadPtr() const {
+ return (pointers_.load_ptr);
+ }
+
+ /// @brief Return pointer to unload function
+ ///
+ /// @return Pointer to the unload function
+ unload_function_ptr unloadPtr() const {
+ return (pointers_.unload_ptr);
+ }
+
+ /// @brief Return pointer to version function
+ ///
+ /// @return Pointer to the version function
+ version_function_ptr versionPtr() const {
+ return (pointers_.version_ptr);
+ }
+
+ ///@}
+
+private:
+
+ /// @brief Union linking void* and pointers to functions.
+ union {
+ void* dlsym_ptr; // void* returned by dlsym
+ CalloutPtr callout_ptr; // Pointer to callout
+ load_function_ptr load_ptr; // Pointer to load function
+ unload_function_ptr unload_ptr; // Pointer to unload function
+ version_function_ptr version_ptr; // Pointer to version function
+ } pointers_;
+};
+
+} // namespace hooks
+} // namespace isc
+
+
+#endif // POINTER_CONVERTER_H
diff --git a/src/lib/hooks/server_hooks.cc b/src/lib/hooks/server_hooks.cc
new file mode 100644
index 0000000..1a0b157
--- /dev/null
+++ b/src/lib/hooks/server_hooks.cc
@@ -0,0 +1,144 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+#include <hooks/hooks_log.h>
+#include <hooks/server_hooks.h>
+
+#include <utility>
+#include <vector>
+
+using namespace std;
+using namespace isc;
+
+namespace isc {
+namespace hooks {
+
+// Constructor - register the pre-defined hooks and check that the indexes
+// assigned to them are as expected.
+
+ServerHooks::ServerHooks() {
+ reset();
+}
+
+// Register a hook. The index assigned to the hook is the current number
+// of entries in the collection, so ensuring that hook indexes are unique
+// and non-negative.
+
+int
+ServerHooks::registerHook(const string& name) {
+
+ // Determine index for the new element and insert.
+ int index = hooks_.size();
+ pair<HookCollection::iterator, bool> result =
+ hooks_.insert(make_pair(name, index));
+
+ if (!result.second) {
+ // New element was not inserted because an element with the same name
+ // already existed.
+ isc_throw(DuplicateHook, "hook with name " << name <<
+ " is already registered");
+ }
+
+ // Element was inserted, so add to the inverse hooks collection.
+ inverse_hooks_[index] = name;
+
+ // Log it if debug is enabled
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_HOOK_REGISTERED).arg(name);
+
+ // ... and return numeric index.
+ return (index);
+}
+
+// Reset ServerHooks object to initial state.
+
+void
+ServerHooks::reset() {
+
+ // Clear out the name->index and index->name maps.
+ hooks_.clear();
+ inverse_hooks_.clear();
+
+ // Register the pre-defined hooks.
+ int create = registerHook("context_create");
+ int destroy = registerHook("context_destroy");
+
+ // Check registration went as expected.
+ if ((create != CONTEXT_CREATE) || (destroy != CONTEXT_DESTROY)) {
+ isc_throw(Unexpected, "pre-defined hook indexes are not as expected. "
+ "context_create: expected = " << CONTEXT_CREATE <<
+ ", actual = " << create <<
+ ". context_destroy: expected = " << CONTEXT_DESTROY <<
+ ", actual = " << destroy);
+ }
+
+ // Log a warning - although this is done during testing, it should never be
+ // seen in a production system.
+ LOG_WARN(hooks_logger, HOOKS_HOOK_LIST_RESET);
+}
+
+// Find the name associated with a hook index.
+
+std::string
+ServerHooks::getName(int index) const {
+
+ // Get iterator to matching element.
+ InverseHookCollection::const_iterator i = inverse_hooks_.find(index);
+ if (i == inverse_hooks_.end()) {
+ isc_throw(NoSuchHook, "hook index " << index << " is not recognised");
+ }
+
+ return (i->second);
+}
+
+// Find the index associated with a hook name.
+
+int
+ServerHooks::getIndex(const string& name) const {
+
+ // Get iterator to matching element.
+ HookCollection::const_iterator i = hooks_.find(name);
+ if (i == hooks_.end()) {
+ isc_throw(NoSuchHook, "hook name " << name << " is not recognised");
+ }
+
+ return (i->second);
+}
+
+// Return vector of hook names. The names are not sorted - it is up to the
+// caller to perform sorting if required.
+
+vector<string>
+ServerHooks::getHookNames() const {
+
+ vector<string> names;
+ HookCollection::const_iterator i;
+ for (i = hooks_.begin(); i != hooks_.end(); ++i) {
+ names.push_back(i->first);
+ }
+
+ return (names);
+}
+
+// Return global ServerHooks object
+
+ServerHooks&
+ServerHooks::getServerHooks() {
+ static ServerHooks hooks;
+ return (hooks);
+}
+
+
+} // namespace util
+} // namespace isc
diff --git a/src/lib/hooks/server_hooks.h b/src/lib/hooks/server_hooks.h
new file mode 100644
index 0000000..f075cb8
--- /dev/null
+++ b/src/lib/hooks/server_hooks.h
@@ -0,0 +1,173 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef SERVER_HOOKS_H
+#define SERVER_HOOKS_H
+
+#include <exceptions/exceptions.h>
+
+#include <boost/noncopyable.hpp>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Duplicate hook
+///
+/// Thrown if an attempt is made to register a hook with the same name as a
+/// previously-registered hook.
+class DuplicateHook : public Exception {
+public:
+ DuplicateHook(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+/// @brief Invalid hook
+///
+/// Thrown if an attempt is made to get the index for an invalid hook.
+class NoSuchHook : public Exception {
+public:
+ NoSuchHook(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+
+/// @brief Server hook collection
+///
+/// This class is used by the server-side code to register hooks - points in the
+/// server processing at which libraries can register functions (callouts) that
+/// the server will call. These functions can modify data and so affect the
+/// processing of the server.
+///
+/// The ServerHooks class is little more than a wrapper around the std::map
+/// class. It stores a hook, assigning to it a unique index number. This
+/// number is then used by the server code to identify the hook being called.
+/// (Although it would be feasible to use a name as an index, using an integer
+/// will speed up the time taken to locate the callouts, which may make a
+/// difference in a frequently-executed piece of code.)
+///
+/// ServerHooks is a singleton object and is only accessible by the static
+/// method getServerHooks().
+
+class ServerHooks : public boost::noncopyable {
+public:
+
+ /// Index numbers for pre-defined hooks.
+ static const int CONTEXT_CREATE = 0;
+ static const int CONTEXT_DESTROY = 1;
+
+ /// @brief Reset to Initial State
+ ///
+ /// Resets the collection of hooks to the initial state, with just the
+ /// context_create and context_destroy hooks set. This used during
+ /// construction. It is also used during testing to reset the global
+ /// ServerHooks object.
+ ///
+ /// @throws isc::Unexpected if the registration of the pre-defined hooks
+ /// fails in some way.
+ void reset();
+
+ /// @brief Register a hook
+ ///
+ /// Registers a hook and returns the hook index.
+ ///
+ /// @param name Name of the hook
+ ///
+ /// @return Index of the hook, to be used in subsequent hook-related calls.
+ /// This will be greater than or equal to zero (so allowing a
+ /// negative value to indicate an invalid index).
+ ///
+ /// @throws DuplicateHook A hook with the same name has already been
+ /// registered.
+ int registerHook(const std::string& name);
+
+ /// @brief Get hook name
+ ///
+ /// Returns the name of a hook given the index. This is most likely to be
+ /// used in log messages.
+ ///
+ /// @param index Index of the hoold
+ ///
+ /// @return Name of the hook.
+ ///
+ /// @throw NoSuchHook if the hook index is invalid.
+ std::string getName(int index) const;
+
+ /// @brief Get hook index
+ ///
+ /// Returns the index of a hook.
+ ///
+ /// @param name Name of the hook
+ ///
+ /// @return Index of the hook, to be used in subsequent calls.
+ ///
+ /// @throw NoSuchHook if the hook name is unknown to the caller.
+ int getIndex(const std::string& name) const;
+
+ /// @brief Return number of hooks
+ ///
+ /// Returns the total number of hooks registered.
+ ///
+ /// @return Number of hooks registered.
+ int getCount() const {
+ return (hooks_.size());
+ }
+
+ /// @brief Get hook names
+ ///
+ /// Return list of hooks registered in the object.
+ ///
+ /// @return Vector of strings holding hook names.
+ std::vector<std::string> getHookNames() const;
+
+ /// @brief Return ServerHooks object
+ ///
+ /// Returns the global ServerHooks object.
+ ///
+ /// @return Reference to the global ServerHooks object.
+ static ServerHooks& getServerHooks();
+
+private:
+ /// @brief Constructor
+ ///
+ /// This pre-registers two hooks, context_create and context_destroy, which
+ /// are called by the server before processing a packet and after processing
+ /// for the packet has completed. They allow the server code to allocate
+ /// and destroy per-packet context.
+ ///
+ /// Constructor is declared private to enforce the singleton nature of
+ /// the object. A reference to the singleton is obtainable through the
+ /// getServerHooks() static method.
+ ///
+ /// @throws isc::Unexpected if the registration of the pre-defined hooks
+ /// fails in some way.
+ ServerHooks();
+
+ /// Useful typedefs.
+ typedef std::map<std::string, int> HookCollection;
+ typedef std::map<int, std::string> InverseHookCollection;
+
+ /// Two maps, one for name->index, the other for index->name. (This is
+ /// simpler than using a multi-indexed container.)
+ HookCollection hooks_; ///< Hook name/index collection
+ InverseHookCollection inverse_hooks_; ///< Hook index/name collection
+};
+
+} // namespace util
+} // namespace isc
+
+#endif // SERVER_HOOKS_H
diff --git a/src/lib/hooks/tests/Makefile.am b/src/lib/hooks/tests/Makefile.am
new file mode 100644
index 0000000..37fe238
--- /dev/null
+++ b/src/lib/hooks/tests/Makefile.am
@@ -0,0 +1,104 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(MULTITHREADING_FLAG)
+
+# Some versions of GCC warn about some versions of Boost regarding
+# missing initializer for members in its posix_time.
+# https://svn.boost.org/trac/boost/ticket/3477
+# But older GCC compilers don't have the flag.
+AM_CXXFLAGS = $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
+
+if USE_CLANGPP
+# see ../Makefile.am
+AM_CXXFLAGS += -Wno-unused-parameter
+endif
+
+# Files to clean include the file created by testing.
+CLEANFILES = *.gcno *.gcda $(builddir)/marker_file.dat
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+# Build shared libraries for testing.
+lib_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la liblecl.la \
+ libucl.la libfcl.la
+
+# No version function
+libnvl_la_SOURCES = no_version_library.cc
+libnvl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libnvl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# Incorrect version function
+libivl_la_SOURCES = incorrect_version_library.cc
+libivl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libivl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# All framework functions throw an exception
+libfxl_la_SOURCES = framework_exception_library.cc
+libfxl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libfxl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The basic callout library - contains standard callouts
+libbcl_la_SOURCES = basic_callout_library.cc
+libbcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libbcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The load callout library - contains a load function
+liblcl_la_SOURCES = load_callout_library.cc
+liblcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+liblcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The load error callout library - contains a load function that returns
+# an error.
+liblecl_la_SOURCES = load_error_callout_library.cc
+liblecl_la_CXXFLAGS = $(AM_CXXFLAGS)
+liblecl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The unload callout library - contains an unload function that
+# creates a marker file.
+libucl_la_SOURCES = unload_callout_library.cc
+libucl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libucl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+# The full callout library - contains all three framework functions.
+libfcl_la_SOURCES = full_callout_library.cc
+libfcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libfcl_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += callout_handle_unittest.cc
+run_unittests_SOURCES += callout_manager_unittest.cc
+run_unittests_SOURCES += common_test_class.h
+run_unittests_SOURCES += handles_unittest.cc
+run_unittests_SOURCES += hooks_manager_unittest.cc
+run_unittests_SOURCES += library_manager_collection_unittest.cc
+run_unittests_SOURCES += library_manager_unittest.cc
+run_unittests_SOURCES += server_hooks_unittest.cc
+
+nodist_run_unittests_SOURCES = marker_file.h
+nodist_run_unittests_SOURCES += test_libraries.h
+
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+if USE_STATIC_LINK
+# If specified, only link unit tests static - the test libraries must be
+# build as shared libraries.
+run_unittests_LDFLAGS += -static
+endif
+
+run_unittests_LDADD = $(AM_LDADD) $(GTEST_LDADD)
+
+run_unittests_LDADD += $(top_builddir)/src/lib/hooks/libb10-hooks.la
+run_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+endif
+
+noinst_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = marker_file.h.in test_libraries.h.in
diff --git a/src/lib/hooks/tests/basic_callout_library.cc b/src/lib/hooks/tests/basic_callout_library.cc
new file mode 100644
index 0000000..253de80
--- /dev/null
+++ b/src/lib/hooks/tests/basic_callout_library.cc
@@ -0,0 +1,115 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief Basic callout library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager). The characteristics of the library produced from this
+/// file are:
+///
+/// - Only the "version" framework function is supplied.
+///
+/// - A context_create callout is supplied.
+///
+/// - Three "standard" callouts are supplied corresponding to the hooks
+/// "hookpt_one", "hookpt_two", "hookpt_three". All do some trivial calculations
+/// on the arguments supplied to it and the context variables, returning
+/// intermediate results through the "result" argument. The result of
+/// executing all four callouts in order is:
+///
+/// @f[ (10 + data_1) * data_2 - data_3 @f]
+///
+/// ...where data_1, data_2 and data_3 are the values passed in arguments of
+/// the same name to the three callouts (data_1 passed to hookpt_one, data_2 to
+/// hookpt_two etc.) and the result is returned in the argument "result".
+
+#include <hooks/hooks.h>
+#include <fstream>
+
+using namespace isc::hooks;
+using namespace std;
+
+extern "C" {
+
+// Callouts. All return their result through the "result" argument.
+
+int
+context_create(CalloutHandle& handle) {
+ handle.setContext("result", static_cast<int>(10));
+ handle.setArgument("result", static_cast<int>(10));
+ return (0);
+}
+
+// First callout adds the passed "data_1" argument to the initialized context
+// value of 10. (Note that the value set by context_create is accessed through
+// context and not the argument, so checking that context is correctly passed
+// between callouts in the same library.)
+
+int
+hookpt_one(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_1", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result += data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Second callout multiplies the current context value by the "data_2"
+// argument.
+
+int
+hookpt_two(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_2", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result *= data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Final callout subtracts the result in "data_3".
+
+int
+hookpt_three(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_3", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result -= data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Framework functions. Only version() is supplied here.
+
+int
+version() {
+ return (BIND10_HOOKS_VERSION);
+}
+
+};
+
diff --git a/src/lib/hooks/tests/callout_handle_unittest.cc b/src/lib/hooks/tests/callout_handle_unittest.cc
new file mode 100644
index 0000000..b24a4cf
--- /dev/null
+++ b/src/lib/hooks/tests/callout_handle_unittest.cc
@@ -0,0 +1,329 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+/// @file
+/// @brief Holds the CalloutHandle argument tests
+///
+/// Additional testing of the CalloutHandle - together with the interaction
+/// of the LibraryHandle - is done in the handles_unittests set of tests.
+
+class CalloutHandleTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor
+ ///
+ /// Sets up a callout manager to be referenced by the CalloutHandle in
+ /// these tests. (The "4" for the number of libraries in the
+ /// CalloutManager is arbitrary - it is not used in these tests.)
+ CalloutHandleTest() : manager_(new CalloutManager(4))
+ {}
+
+ /// Obtain hook manager
+ boost::shared_ptr<CalloutManager>& getCalloutManager() {
+ return (manager_);
+ }
+
+private:
+ /// Callout manager accessed by this CalloutHandle.
+ boost::shared_ptr<CalloutManager> manager_;
+};
+
+// *** Argument Tests ***
+//
+// The first set of tests check that the CalloutHandle can store and retrieve
+// arguments. These are very similar to the LibraryHandle context tests.
+
+// Test that we can store multiple values of the same type and that they
+// are distinct.
+
+TEST_F(CalloutHandleTest, ArgumentDistinctSimpleType) {
+ CalloutHandle handle(getCalloutManager());
+
+ // Store and retrieve an int (random value).
+ int a = 42;
+ handle.setArgument("integer1", a);
+ EXPECT_EQ(42, a);
+
+ int b = 0;
+ handle.getArgument("integer1", b);
+ EXPECT_EQ(42, b);
+
+ // Add another integer (another random value).
+ int c = 142;
+ handle.setArgument("integer2", c);
+ EXPECT_EQ(142, c);
+
+ int d = 0;
+ handle.getArgument("integer2", d);
+ EXPECT_EQ(142, d);
+
+ // Add a short (random value).
+ short e = -81;
+ handle.setArgument("short", e);
+ EXPECT_EQ(-81, e);
+
+ short f = 0;
+ handle.getArgument("short", f);
+ EXPECT_EQ(-81, f);
+}
+
+// Test that trying to get an unknown argument throws an exception.
+
+TEST_F(CalloutHandleTest, ArgumentUnknownName) {
+ CalloutHandle handle(getCalloutManager());
+
+ // Set an integer
+ int a = 42;
+ handle.setArgument("integer1", a);
+ EXPECT_EQ(42, a);
+
+ // Check we can retrieve it
+ int b = 0;
+ handle.getArgument("integer1", b);
+ EXPECT_EQ(42, b);
+
+ // Check that getting an unknown name throws an exception.
+ int c = 0;
+ EXPECT_THROW(handle.getArgument("unknown", c), NoSuchArgument);
+}
+
+// Test that trying to get an argument with an incorrect type throws an
+// exception.
+
+TEST_F(CalloutHandleTest, ArgumentIncorrectType) {
+ CalloutHandle handle(getCalloutManager());
+
+ // Set an integer
+ int a = 42;
+ handle.setArgument("integer1", a);
+ EXPECT_EQ(42, a);
+
+ // Check we can retrieve it
+ long b = 0;
+ EXPECT_THROW(handle.getArgument("integer1", b), boost::bad_any_cast);
+}
+
+// Now try with some very complex types. The types cannot be defined within
+// the function and they should contain a copy constructor. For this reason,
+// a simple "struct" is used.
+
+struct Alpha {
+ int a;
+ int b;
+ Alpha(int first = 0, int second = 0) : a(first), b(second) {}
+};
+
+struct Beta {
+ int c;
+ int d;
+ Beta(int first = 0, int second = 0) : c(first), d(second) {}
+};
+
+TEST_F(CalloutHandleTest, ComplexTypes) {
+ CalloutHandle handle(getCalloutManager());
+
+ // Declare two variables of different (complex) types. (Note as to the
+ // variable names: aleph and beth are the first two letters of the Hebrew
+ // alphabet.)
+ Alpha aleph(1, 2);
+ EXPECT_EQ(1, aleph.a);
+ EXPECT_EQ(2, aleph.b);
+ handle.setArgument("aleph", aleph);
+
+ Beta beth(11, 22);
+ EXPECT_EQ(11, beth.c);
+ EXPECT_EQ(22, beth.d);
+ handle.setArgument("beth", beth);
+
+ // Ensure we can extract the data correctly.
+ Alpha aleph2;
+ EXPECT_EQ(0, aleph2.a);
+ EXPECT_EQ(0, aleph2.b);
+ handle.getArgument("aleph", aleph2);
+ EXPECT_EQ(1, aleph2.a);
+ EXPECT_EQ(2, aleph2.b);
+
+ Beta beth2;
+ EXPECT_EQ(0, beth2.c);
+ EXPECT_EQ(0, beth2.d);
+ handle.getArgument("beth", beth2);
+ EXPECT_EQ(11, beth2.c);
+ EXPECT_EQ(22, beth2.d);
+
+ // Ensure that complex types also thrown an exception if we attempt to
+ // get a context element of the wrong type.
+ EXPECT_THROW(handle.getArgument("aleph", beth), boost::bad_any_cast);
+}
+
+// Check that the context can store pointers. And also check that it respects
+// that a "pointer to X" is not the same as a "pointer to const X".
+
+TEST_F(CalloutHandleTest, PointerTypes) {
+ CalloutHandle handle(getCalloutManager());
+
+ // Declare a couple of variables, const and non-const.
+ Alpha aleph(5, 10);
+ const Beta beth(15, 20);
+
+ Alpha* pa = ℵ
+ const Beta* pcb = ℶ
+
+ // Check pointers can be set and retrieved OK.
+ handle.setArgument("non_const_pointer", pa);
+ handle.setArgument("const_pointer", pcb);
+
+ Alpha* pa2 = 0;
+ handle.getArgument("non_const_pointer", pa2);
+ EXPECT_TRUE(pa == pa2);
+
+ const Beta* pcb2 = 0;
+ handle.getArgument("const_pointer", pcb2);
+ EXPECT_TRUE(pcb == pcb2);
+
+ // Check that the "const" is protected in the context.
+ const Alpha* pca3;
+ EXPECT_THROW(handle.getArgument("non_const_pointer", pca3),
+ boost::bad_any_cast);
+
+ Beta* pb3;
+ EXPECT_THROW(handle.getArgument("const_pointer", pb3),
+ boost::bad_any_cast);
+}
+
+// Check that we can get the names of the arguments.
+
+TEST_F(CalloutHandleTest, ContextItemNames) {
+ CalloutHandle handle(getCalloutManager());
+
+ vector<string> expected_names;
+
+ expected_names.push_back("faith");
+ handle.setArgument("faith", 42);
+ expected_names.push_back("hope");
+ handle.setArgument("hope", 43);
+ expected_names.push_back("charity");
+ handle.setArgument("charity", 44);
+
+ // Get the names and check against the expected names. We'll sort
+ // both arrays to simplify the checking.
+ vector<string> actual_names = handle.getArgumentNames();
+
+ sort(actual_names.begin(), actual_names.end());
+ sort(expected_names.begin(), expected_names.end());
+ EXPECT_TRUE(expected_names == actual_names);
+}
+
+// Test that we can delete an argument.
+
+TEST_F(CalloutHandleTest, DeleteArgument) {
+ CalloutHandle handle(getCalloutManager());
+
+ int one = 1;
+ int two = 2;
+ int three = 3;
+ int four = 4;
+ int value; // Return value
+
+ handle.setArgument("one", one);
+ handle.setArgument("two", two);
+ handle.setArgument("three", three);
+ handle.setArgument("four", four);
+
+ // Delete "one".
+ handle.getArgument("one", value);
+ EXPECT_EQ(1, value);
+ handle.deleteArgument("one");
+
+ EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
+ handle.getArgument("two", value);
+ EXPECT_EQ(2, value);
+ handle.getArgument("three", value);
+ EXPECT_EQ(3, value);
+ handle.getArgument("four", value);
+ EXPECT_EQ(4, value);
+
+ // Delete "three".
+ handle.getArgument("three", value);
+ EXPECT_EQ(3, value);
+ handle.deleteArgument("three");
+
+ EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
+ handle.getArgument("two", value);
+ EXPECT_EQ(2, value);
+ EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
+ handle.getArgument("four", value);
+ EXPECT_EQ(4, value);
+}
+
+// Test that we can delete all arguments.
+
+TEST_F(CalloutHandleTest, DeleteAllArguments) {
+ CalloutHandle handle(getCalloutManager());
+
+ int one = 1;
+ int two = 2;
+ int three = 3;
+ int four = 4;
+ int value; // Return value
+
+ // Set the arguments. The previous test verifies that this works.
+ handle.setArgument("one", one);
+ handle.setArgument("two", two);
+ handle.setArgument("three", three);
+ handle.setArgument("four", four);
+
+ // Delete all arguments...
+ handle.deleteAllArguments();
+
+ // ... and check that none are left.
+ EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
+ EXPECT_THROW(handle.getArgument("two", value), NoSuchArgument);
+ EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
+ EXPECT_THROW(handle.getArgument("four", value), NoSuchArgument);
+}
+
+// Test the "skip" flag.
+
+TEST_F(CalloutHandleTest, SkipFlag) {
+ CalloutHandle handle(getCalloutManager());
+
+ // Should be false on construction.
+ EXPECT_FALSE(handle.getSkip());
+
+ handle.setSkip(true);
+ EXPECT_TRUE(handle.getSkip());
+
+ handle.setSkip(false);
+ EXPECT_FALSE(handle.getSkip());
+}
+
+// Further tests of the "skip" flag and tests of getting the name of the
+// hook to which the current callout is attached is in the "handles_unittest"
+// module.
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/callout_manager_unittest.cc b/src/lib/hooks/tests/callout_manager_unittest.cc
new file mode 100644
index 0000000..935987a
--- /dev/null
+++ b/src/lib/hooks/tests/callout_manager_unittest.cc
@@ -0,0 +1,886 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <climits>
+#include <string>
+#include <vector>
+
+/// @file
+/// @brief CalloutManager and LibraryHandle tests
+///
+/// These set of tests check the CalloutManager and LibraryHandle. They are
+/// together in the same file because the LibraryHandle is little more than a
+/// restricted interface to the CalloutManager, and a lot of the support
+/// structure for the tests is common.
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+class CalloutManagerTest : public ::testing::Test {
+public:
+ /// @brief Constructor
+ ///
+ /// Sets up a collection of three LibraryHandle objects to use in the test.
+ CalloutManagerTest() {
+
+ // Set up the server hooks. There is sone singleton for all tests,
+ // so reset it and explicitly set up the hooks for the test.
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+ alpha_index_ = hooks.registerHook("alpha");
+ beta_index_ = hooks.registerHook("beta");
+ gamma_index_ = hooks.registerHook("gamma");
+ delta_index_ = hooks.registerHook("delta");
+
+ // Set up the callout manager with these hooks. Assume a maximum of
+ // four libraries.
+ callout_manager_.reset(new CalloutManager(10));
+
+ // Set up the callout handle.
+ callout_handle_.reset(new CalloutHandle(callout_manager_));
+
+ // Initialize the static variable.
+ callout_value_ = 0;
+ }
+
+ /// @brief Return the callout handle
+ CalloutHandle& getCalloutHandle() {
+ return (*callout_handle_);
+ }
+
+ /// @brief Return the callout manager
+ boost::shared_ptr<CalloutManager> getCalloutManager() {
+ return (callout_manager_);
+ }
+
+ /// Static variable used for accumulating information
+ static int callout_value_;
+
+ /// Hook indexes. These are somewhat ubiquitous, so are made public for
+ /// ease of reference instead of being accessible by a function.
+ int alpha_index_;
+ int beta_index_;
+ int gamma_index_;
+ int delta_index_;
+
+private:
+ /// Callout handle used in calls
+ boost::shared_ptr<CalloutHandle> callout_handle_;
+
+ /// Callout manager used for the test
+ boost::shared_ptr<CalloutManager> callout_manager_;
+};
+
+// Definition of the static variable.
+int CalloutManagerTest::callout_value_ = 0;
+
+// Callout definitions
+//
+// The callouts defined here are structured in such a way that it is possible
+// to determine the order in which they are called and whether they are called
+// at all. The method used is simple - after a sequence of callouts, the digits
+// in the value, reading left to right, determines the order of the callouts
+// called. For example, callout one followed by two followed by three followed
+// by two followed by one results in a value of 12321.
+//
+// Functions return a zero to indicate success.
+
+extern "C" {
+int callout_general(int number) {
+ CalloutManagerTest::callout_value_ =
+ 10 * CalloutManagerTest::callout_value_ + number;
+ return (0);
+}
+
+int callout_one(CalloutHandle&) {
+ return (callout_general(1));
+}
+
+int callout_two(CalloutHandle&) {
+ return (callout_general(2));
+}
+
+int callout_three(CalloutHandle&) {
+ return (callout_general(3));
+}
+
+int callout_four(CalloutHandle&) {
+ return (callout_general(4));
+}
+
+int callout_five(CalloutHandle&) {
+ return (callout_general(5));
+}
+
+int callout_six(CalloutHandle&) {
+ return (callout_general(6));
+}
+
+int callout_seven(CalloutHandle&) {
+ return (callout_general(7));
+}
+
+// The next functions are duplicates of some of the above, but return an error.
+
+int callout_one_error(CalloutHandle& handle) {
+ (void) callout_one(handle);
+ return (1);
+}
+
+int callout_two_error(CalloutHandle& handle) {
+ (void) callout_two(handle);
+ return (1);
+}
+
+int callout_three_error(CalloutHandle& handle) {
+ (void) callout_three(handle);
+ return (1);
+}
+
+int callout_four_error(CalloutHandle& handle) {
+ (void) callout_four(handle);
+ return (1);
+}
+
+}; // extern "C"
+
+// *** Callout Tests ***
+//
+// The next set of tests check that callouts can be called.
+
+// Constructor - check that we trap bad parameters.
+
+TEST_F(CalloutManagerTest, BadConstructorParameters) {
+ boost::scoped_ptr<CalloutManager> cm;
+
+ // Invalid number of libraries
+ EXPECT_THROW(cm.reset(new CalloutManager(-1)), BadValue);
+}
+
+// Check the number of libraries is reported successfully.
+
+TEST_F(CalloutManagerTest, NumberOfLibraries) {
+ boost::scoped_ptr<CalloutManager> cm;
+
+ // Check two valid values of number of libraries to ensure that the
+ // GetNumLibraries() returns the value set.
+ EXPECT_NO_THROW(cm.reset(new CalloutManager()));
+ EXPECT_EQ(0, cm->getNumLibraries());
+
+ EXPECT_NO_THROW(cm.reset(new CalloutManager(0)));
+ EXPECT_EQ(0, cm->getNumLibraries());
+
+ EXPECT_NO_THROW(cm.reset(new CalloutManager(4)));
+ EXPECT_EQ(4, cm->getNumLibraries());
+
+ EXPECT_NO_THROW(cm.reset(new CalloutManager(42)));
+ EXPECT_EQ(42, cm->getNumLibraries());
+
+ // Check that setting the number of libraries alterns the number reported.
+ EXPECT_NO_THROW(cm->setNumLibraries(27));
+ EXPECT_EQ(27, cm->getNumLibraries());
+}
+
+// Check that we can only set the current library index to the correct values.
+
+TEST_F(CalloutManagerTest, CheckLibraryIndex) {
+ // Check valid indexes. As the callout manager is sized for 10 libraries,
+ // we expect:
+ //
+ // -1 to be valid as it is the standard "invalid" value.
+ // 0 to be valid for the pre-user library callouts
+ // 1-10 to be valid for the user-library callouts
+ // INT_MAX to be valid for the post-user library callouts
+ //
+ // All other values to be invalid.
+ for (int i = -1; i < 11; ++i) {
+ EXPECT_NO_THROW(getCalloutManager()->setLibraryIndex(i));
+ EXPECT_EQ(i, getCalloutManager()->getLibraryIndex());
+ }
+ EXPECT_NO_THROW(getCalloutManager()->setLibraryIndex(INT_MAX));
+ EXPECT_EQ(INT_MAX, getCalloutManager()->getLibraryIndex());
+
+ // Check invalid ones
+ EXPECT_THROW(getCalloutManager()->setLibraryIndex(-2), NoSuchLibrary);
+ EXPECT_THROW(getCalloutManager()->setLibraryIndex(11), NoSuchLibrary);
+}
+
+// Check that we can only register callouts on valid hook names.
+
+TEST_F(CalloutManagerTest, ValidHookNames) {
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_NO_THROW(getCalloutManager()->registerCallout("alpha", callout_one));
+ EXPECT_THROW(getCalloutManager()->registerCallout("unknown", callout_one),
+ NoSuchHook);
+}
+
+
+// Check we can register callouts appropriately.
+
+TEST_F(CalloutManagerTest, RegisterCallout) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+
+ // Set up so that hooks "alpha" and "beta" have callouts attached from a
+ // different libraries.
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_one);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("beta", callout_two);
+
+ // Check all is as expected.
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Check that calling the callouts returns as expected. (This is also a
+ // test of the callCallouts method.)
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1, callout_value_);
+
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+ EXPECT_EQ(2, callout_value_);
+
+ // Register some more callouts from different libraries on hook "alpha".
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->registerCallout("alpha", callout_four);
+ getCalloutManager()->setLibraryIndex(3);
+ getCalloutManager()->registerCallout("alpha", callout_five);
+
+ // Check it is as expected.
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1345, callout_value_);
+
+ // ... and check the additional callouts were not registered on the "beta"
+ // hook.
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+ EXPECT_EQ(2, callout_value_);
+
+ // Add another callout to hook "alpha" from library index 2 - this should
+ // appear at the end of the callout list for that library.
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout_six);
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(13465, callout_value_);
+
+ // Add a callout from library index 1 - this should appear between the
+ // callouts from library index 0 and linrary index 2.
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout_seven);
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(173465, callout_value_);
+}
+
+// Check the "calloutsPresent()" method.
+
+TEST_F(CalloutManagerTest, CalloutsPresent) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Set up so that hooks "alpha", "beta" and "delta" have callouts attached
+ // to them, and callout "gamma" does not. (In the statements below, the
+ // exact callouts attached to a hook are not relevant - only the fact
+ // that some callouts are). Chose the libraries for which the callouts
+ // are registered randomly.
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_one);
+
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->registerCallout("beta", callout_two);
+
+ getCalloutManager()->setLibraryIndex(3);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->registerCallout("delta", callout_four);
+
+ // Check all is as expected.
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Check we fail on an invalid hook index.
+ EXPECT_THROW(getCalloutManager()->calloutsPresent(42), NoSuchHook);
+ EXPECT_THROW(getCalloutManager()->calloutsPresent(-1), NoSuchHook);
+}
+
+// Test that calling a hook with no callouts on it returns success.
+
+TEST_F(CalloutManagerTest, CallNoCallouts) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Call the callouts on an arbitrary hook and ensure that nothing happens.
+ callout_value_ = 475;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(475, callout_value_); // Unchanged
+}
+
+// Test that the callouts are called in the correct order (i.e. the callouts
+// from the first library in the order they were registered, then the callouts
+// from the second library in the order they were registered etc.)
+
+TEST_F(CalloutManagerTest, CallCalloutsSuccess) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Each library contributes one callout on hook "alpha".
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout_one);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->setLibraryIndex(3);
+ getCalloutManager()->registerCallout("alpha", callout_four);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1234, callout_value_);
+
+ // Do a random selection of callouts on hook "beta".
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("beta", callout_one);
+ getCalloutManager()->registerCallout("beta", callout_three);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("beta", callout_two);
+ getCalloutManager()->setLibraryIndex(3);
+ getCalloutManager()->registerCallout("beta", callout_four);
+ getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+ EXPECT_EQ(1324, callout_value_);
+
+ // Ensure that calling the callouts on a hook with no callouts works.
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
+ EXPECT_EQ(0, callout_value_);
+}
+
+// Test that the callouts are called in order, but that callouts occurring
+// after a callout that returns an error are not called.
+//
+// (Note: in this test, the callouts that return an error set the value of
+// callout_value_ before they return the error code.)
+
+TEST_F(CalloutManagerTest, CallCalloutsError) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Each library contributing one callout on hook "alpha". The first callout
+ // returns an error (after adding its value to the result).
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_one_error);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->setLibraryIndex(3);
+ getCalloutManager()->registerCallout("alpha", callout_four);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1234, callout_value_);
+
+ // Each library contributing multiple callouts on hook "beta". The last
+ // callout on the first library returns an error.
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("beta", callout_one);
+ getCalloutManager()->registerCallout("beta", callout_one_error);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("beta", callout_two);
+ getCalloutManager()->registerCallout("beta", callout_two);
+ getCalloutManager()->registerCallout("beta", callout_three);
+ getCalloutManager()->registerCallout("beta", callout_three);
+ getCalloutManager()->setLibraryIndex(3);
+ getCalloutManager()->registerCallout("beta", callout_four);
+ getCalloutManager()->registerCallout("beta", callout_four);
+ getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+ EXPECT_EQ(11223344, callout_value_);
+
+ // A callout in a random position in the callout list returns an error.
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("gamma", callout_one);
+ getCalloutManager()->registerCallout("gamma", callout_one);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("gamma", callout_two);
+ getCalloutManager()->registerCallout("gamma", callout_two);
+ getCalloutManager()->setLibraryIndex(3);
+ getCalloutManager()->registerCallout("gamma", callout_four_error);
+ getCalloutManager()->registerCallout("gamma", callout_four);
+ getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
+ EXPECT_EQ(112244, callout_value_);
+
+ // The last callout on a hook returns an error.
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("delta", callout_one);
+ getCalloutManager()->registerCallout("delta", callout_one);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("delta", callout_two);
+ getCalloutManager()->registerCallout("delta", callout_two);
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("delta", callout_three);
+ getCalloutManager()->registerCallout("delta", callout_three);
+ getCalloutManager()->setLibraryIndex(3);
+ getCalloutManager()->registerCallout("delta", callout_four);
+ getCalloutManager()->registerCallout("delta", callout_four_error);
+ getCalloutManager()->callCallouts(delta_index_, getCalloutHandle());
+ EXPECT_EQ(11223344, callout_value_);
+}
+
+// Now test that we can deregister a single callout on a hook.
+
+TEST_F(CalloutManagerTest, DeregisterSingleCallout) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Add a callout to hook "alpha" and check it is added correctly.
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(2, callout_value_);
+
+ // Remove it and check that the no callouts are present. We have to reset
+ // the current library index here as it was invalidated by the call
+ // to callCallouts().
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+}
+
+// Now test that we can deregister a single callout on a hook that has multiple
+// callouts from the same library.
+
+TEST_F(CalloutManagerTest, DeregisterSingleCalloutSameLibrary) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Add multiple callouts to hook "alpha".
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_one);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->registerCallout("alpha", callout_four);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1234, callout_value_);
+
+ // Remove the callout_two callout. We have to reset the current library
+ // index here as it was invalidated by the call to callCallouts().
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(134, callout_value_);
+
+ // Try removing it again.
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(134, callout_value_);
+
+}
+
+// Check we can deregister multiple callouts from the same library.
+
+TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsSameLibrary) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Each library contributes one callout on hook "alpha".
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_one);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->registerCallout("alpha", callout_one);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->registerCallout("alpha", callout_four);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->registerCallout("alpha", callout_four);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(12123434, callout_value_);
+
+ // Remove the callout_two callouts. We have to reset the current library
+ // index here as it was invalidated by the call to callCallouts().
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(113434, callout_value_);
+
+ // Try removing multiple callouts that includes one at the end of the
+ // list of callouts.
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_four));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1133, callout_value_);
+
+ // ... and from the start.
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_one));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(33, callout_value_);
+
+ // ... and the remaining callouts.
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_three));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(0, callout_value_);
+
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+}
+
+// Check we can deregister multiple callouts from multiple libraries.
+
+TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsMultipleLibraries) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Each library contributes two callouts to hook "alpha".
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_one);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->registerCallout("alpha", callout_four);
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout_five);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(123452, callout_value_);
+
+ // Remove the callout_two callout from library 0. It should not affect
+ // the second callout_two callout registered by library 2.
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(13452, callout_value_);
+}
+
+// Check we can deregister all callouts from a single library.
+
+TEST_F(CalloutManagerTest, DeregisterAllCallouts) {
+ // Ensure that no callouts are attached to hook one.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+
+ // Each library contributes two callouts to hook "alpha".
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_one);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->registerCallout("alpha", callout_four);
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout_five);
+ getCalloutManager()->registerCallout("alpha", callout_six);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(123456, callout_value_);
+
+ // Remove all callouts from library index 1.
+ getCalloutManager()->setLibraryIndex(1);
+ EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1256, callout_value_);
+
+ // Remove all callouts from library index 2.
+ getCalloutManager()->setLibraryIndex(2);
+ EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(12, callout_value_);
+}
+
+// Check that we can register/deregister callouts on different libraries
+// and different hooks, and that the callout instances are regarded as
+// unique and do not affect one another.
+
+TEST_F(CalloutManagerTest, MultipleCalloutsLibrariesHooks) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Register callouts on the alpha hook.
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_one);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout_three);
+ getCalloutManager()->registerCallout("alpha", callout_four);
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout_five);
+ getCalloutManager()->registerCallout("alpha", callout_two);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(123452, callout_value_);
+
+ // Register the same callouts on the beta hook, and check that those
+ // on the alpha hook are not affected.
+ callout_value_ = 0;
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("beta", callout_five);
+ getCalloutManager()->registerCallout("beta", callout_one);
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("beta", callout_four);
+ getCalloutManager()->registerCallout("beta", callout_three);
+ getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+ EXPECT_EQ(5143, callout_value_);
+
+ // Check that the order of callouts on the alpha hook has not been affected.
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(123452, callout_value_);
+
+ // Remove callout four from beta and check that alpha is not affected.
+ getCalloutManager()->setLibraryIndex(2);
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("beta", callout_four));
+
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
+ EXPECT_EQ(513, callout_value_);
+
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(123452, callout_value_);
+}
+
+// Library handle tests. As by inspection the LibraryHandle can be seen to be
+// little more than shell around CalloutManager, only a basic set of tests
+// is done concerning registration and deregistration of functions.
+//
+// More extensive tests (i.e. checking that when a callout is called it can
+// only register and deregister callouts within its library) require that
+// the CalloutHandle object pass the appropriate LibraryHandle to the
+// callout. These tests are done in the handles_unittest tests.
+
+TEST_F(CalloutManagerTest, LibraryHandleRegistration) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+
+ // Set up so that hooks "alpha" and "beta" have callouts attached from a
+ // different libraries.
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+ callout_one);
+ getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+ callout_two);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+ callout_three);
+ getCalloutManager()->getLibraryHandle().registerCallout("alpha",
+ callout_four);
+
+ // Check all is as expected.
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Check that calling the callouts returns as expected. (This is also a
+ // test of the callCallouts method.)
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1234, callout_value_);
+
+ // Deregister a callout on library index 0 (after we check we can't
+ // deregister it through library index 1).
+ getCalloutManager()->setLibraryIndex(1);
+ EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1234, callout_value_);
+
+ getCalloutManager()->setLibraryIndex(0);
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(134, callout_value_);
+
+ // Deregister all callouts on library index 1.
+ getCalloutManager()->setLibraryIndex(1);
+ EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1, callout_value_);
+}
+
+// A repeat of the test above, but using the alternate constructor for the
+// LibraryHandle.
+TEST_F(CalloutManagerTest, LibraryHandleAlternateConstructor) {
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
+
+ // Set up so that hooks "alpha" and "beta" have callouts attached from a
+ // different libraries.
+ LibraryHandle lh0(getCalloutManager().get(), 0);
+ lh0.registerCallout("alpha", callout_one);
+ lh0.registerCallout("alpha", callout_two);
+
+ LibraryHandle lh1(getCalloutManager().get(), 1);
+ lh1.registerCallout("alpha", callout_three);
+ lh1.registerCallout("alpha", callout_four);
+
+ // Check all is as expected.
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Check that calling the callouts returns as expected. (This is also a
+ // test of the callCallouts method.)
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1234, callout_value_);
+
+ // Deregister a callout on library index 0 (after we check we can't
+ // deregister it through library index 1).
+ EXPECT_FALSE(lh1.deregisterCallout("alpha", callout_two));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1234, callout_value_);
+
+ EXPECT_TRUE(lh0.deregisterCallout("alpha", callout_two));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(134, callout_value_);
+
+ // Deregister all callouts on library index 1.
+ EXPECT_TRUE(lh1.deregisterAllCallouts("alpha"));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1, callout_value_);
+}
+
+// Check that the pre- and post- user callout library handles work
+// appropriately with no user libraries.
+
+TEST_F(CalloutManagerTest, LibraryHandlePrePostNoLibraries) {
+ // Create a local callout manager and callout handle to reflect no libraries
+ // being loaded.
+ boost::shared_ptr<CalloutManager> manager(new CalloutManager(0));
+ CalloutHandle handle(manager);
+
+ // Ensure that no callouts are attached to any of the hooks.
+ EXPECT_FALSE(manager->calloutsPresent(alpha_index_));
+
+ // Setup the pre-and post callouts.
+ manager->getPostLibraryHandle().registerCallout("alpha", callout_four);
+ manager->getPreLibraryHandle().registerCallout("alpha", callout_one);
+ // Check all is as expected.
+ EXPECT_TRUE(manager->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(manager->calloutsPresent(beta_index_));
+ EXPECT_FALSE(manager->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(manager->calloutsPresent(delta_index_));
+
+ // Check that calling the callouts returns as expected.
+ callout_value_ = 0;
+ manager->callCallouts(alpha_index_, handle);
+ EXPECT_EQ(14, callout_value_);
+
+ // Deregister the pre- library callout.
+ EXPECT_TRUE(manager->getPreLibraryHandle().deregisterAllCallouts("alpha"));
+ callout_value_ = 0;
+ manager->callCallouts(alpha_index_, handle);
+ EXPECT_EQ(4, callout_value_);
+}
+
+// Repeat the tests with one user library.
+
+TEST_F(CalloutManagerTest, LibraryHandlePrePostUserLibrary) {
+
+ // Setup the pre-, library and post callouts.
+ getCalloutManager()->getPostLibraryHandle().registerCallout("alpha",
+ callout_four);
+ getCalloutManager()->getPreLibraryHandle().registerCallout("alpha",
+ callout_one);
+
+ // ... and set up a callout in between, on library number 2.
+ LibraryHandle lh1(getCalloutManager().get(), 2);
+ lh1.registerCallout("alpha", callout_five);
+
+ // Check all is as expected.
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
+ EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
+
+ // Check that calling the callouts returns as expected.
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(154, callout_value_);
+}
+
+// The setting of the hook index is checked in the handles_unittest
+// set of tests, as access restrictions mean it is not easily tested
+// on its own.
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/common_test_class.h b/src/lib/hooks/tests/common_test_class.h
new file mode 100644
index 0000000..803e25c
--- /dev/null
+++ b/src/lib/hooks/tests/common_test_class.h
@@ -0,0 +1,142 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef COMMON_HOOKS_TEST_CLASS_H
+#define COMMON_HOOKS_TEST_CLASS_H
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/server_hooks.h>
+#include <hooks/tests/marker_file.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+/// @brief Common hooks test class
+///
+/// This class is a shared parent of the test fixture class in the tests of the
+/// higher-level hooks classes (LibraryManager, LibraryManagerCollection and
+/// HooksManager). It
+///
+/// - sets the the ServerHooks object with three hooks and stores their
+/// indexes.
+/// - executes the callouts (which are assumed to perform a calculation)
+/// and checks the results.
+
+class HooksCommonTestClass {
+public:
+ /// @brief Constructor
+ HooksCommonTestClass() {
+
+ // Set up the server hooks. ServerHooks is a singleton, so we reset it
+ // between each test.
+ isc::hooks::ServerHooks& hooks =
+ isc::hooks::ServerHooks::getServerHooks();
+ hooks.reset();
+ hookpt_one_index_ = hooks.registerHook("hookpt_one");
+ hookpt_two_index_ = hooks.registerHook("hookpt_two");
+ hookpt_three_index_ = hooks.registerHook("hookpt_three");
+ }
+
+ /// @brief Call callouts test
+ ///
+ /// All of the loaded libraries for which callouts are called register four
+ /// callouts: a context_create callout and three callouts that are attached
+ /// to hooks hookpt_one, hookpt_two and hookpt_three. These four callouts,
+ /// executed in sequence, perform a series of calculations. Data is passed
+ /// between callouts in the argument list, in a variable named "result".
+ ///
+ /// context_create initializes the calculation by setting a seed
+ /// value, called r0 here. This value is dependent on the library being
+ /// loaded. Prior to that, the argument "result" is initialized to -1,
+ /// the purpose being to avoid exceptions when running this test with no
+ /// libraries loaded.
+ ///
+ /// Callout hookpt_one is passed a value d1 and performs a simple arithmetic
+ /// operation on it and r0 yielding a result r1. Hence we can say that
+ /// @f[ r1 = hookpt_one(r0, d1) @f]
+ ///
+ /// Callout hookpt_two is passed a value d2 and peforms another simple
+ /// arithmetic operation on it and d2, yielding r2, i.e.
+ /// @f[ r2 = hookpt_two(d1, d2) @f]
+ ///
+ /// hookpt_three does a similar operation giving
+ /// @f[ r3 = hookpt_three(r2, d3) @f].
+ ///
+ /// The details of the operations hookpt_one, hookpt_two and hookpt_three
+ /// depend on the library, so the results obtained not only depend on
+ /// the data, but also on the library loaded. This method is passed both
+ /// data and expected results. It executes the three callouts in sequence,
+ /// checking the intermediate and final results. Only if the expected
+ /// library has been loaded correctly and the callouts in it registered
+ /// correctly will be the results be as expected.
+ ///
+ /// It is assumed that callout_manager_ has been set up appropriately.
+ ///
+ /// @note The CalloutHandle used in the calls is declared locally here.
+ /// The advantage of this (apart from scope reduction) is that on
+ /// exit, it is destroyed. This removes any references to memory
+ /// allocated by loaded libraries while they are still loaded.
+ ///
+ /// @param manager CalloutManager to use for the test
+ /// @param r0...r3, d1..d3 Data (dN) and expected results (rN) - both
+ /// intermediate and final. The arguments are ordered so that they
+ /// appear in the argument list in the order they are used.
+ void executeCallCallouts(
+ const boost::shared_ptr<isc::hooks::CalloutManager>& manager,
+ int r0, int d1, int r1, int d2, int r2, int d3, int r3) {
+ static const char* COMMON_TEXT = " callout returned the wong value";
+ static const char* RESULT = "result";
+
+ int result;
+
+ // Set up a callout handle for the calls.
+ isc::hooks::CalloutHandle handle(manager);
+
+ // Initialize the argument RESULT. This simplifies testing by
+ // eliminating the generation of an exception when we try the unload
+ // test. In that case, RESULT is unchanged.
+ handle.setArgument(RESULT, -1);
+
+ // Seed the calculation.
+ manager->callCallouts(isc::hooks::ServerHooks::CONTEXT_CREATE, handle);
+ handle.getArgument(RESULT, result);
+ EXPECT_EQ(r0, result) << "context_create" << COMMON_TEXT;
+
+ // Perform the first calculation.
+ handle.setArgument("data_1", d1);
+ manager->callCallouts(hookpt_one_index_, handle);
+ handle.getArgument(RESULT, result);
+ EXPECT_EQ(r1, result) << "hookpt_one" << COMMON_TEXT;
+
+ // ... the second ...
+ handle.setArgument("data_2", d2);
+ manager->callCallouts(hookpt_two_index_, handle);
+ handle.getArgument(RESULT, result);
+ EXPECT_EQ(r2, result) << "hookpt_two" << COMMON_TEXT;
+
+ // ... and the third.
+ handle.setArgument("data_3", d3);
+ manager->callCallouts(hookpt_three_index_, handle);
+ handle.getArgument(RESULT, result);
+ EXPECT_EQ(r3, result) << "hookpt_three" << COMMON_TEXT;
+ }
+
+ /// Hook indexes. These are are made public for ease of reference.
+ int hookpt_one_index_;
+ int hookpt_two_index_;
+ int hookpt_three_index_;
+};
+
+#endif // COMMON_HOOKS_TEST_CLASS_H
diff --git a/src/lib/hooks/tests/framework_exception_library.cc b/src/lib/hooks/tests/framework_exception_library.cc
new file mode 100644
index 0000000..e90fd36
--- /dev/null
+++ b/src/lib/hooks/tests/framework_exception_library.cc
@@ -0,0 +1,47 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief Framework exception library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager). The characteristics of the library produced from this
+/// file are:
+///
+/// - All three framework functions are supplied (version(), load() and
+/// unload()) and all generate an exception.
+
+#include <hooks/hooks.h>
+
+#include <exception>
+
+extern "C" {
+
+int
+version() {
+ throw std::exception();
+}
+
+int
+load(isc::hooks::LibraryHandle& handle) {
+ throw std::exception();
+}
+
+int
+unload() {
+ throw std::exception();
+}
+
+};
+
diff --git a/src/lib/hooks/tests/full_callout_library.cc b/src/lib/hooks/tests/full_callout_library.cc
new file mode 100644
index 0000000..33d5660
--- /dev/null
+++ b/src/lib/hooks/tests/full_callout_library.cc
@@ -0,0 +1,137 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief Full callout library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager). The characteristics of the library produced from this
+/// file are:
+///
+/// The characteristics of this library are:
+///
+/// - All three framework functions are supplied (version(), load() and
+/// unload()), with unload() creating a marker file. The test code checks
+/// for the presence of this file, so verifying that unload() has been run.
+///
+/// - One standard and two non-standard callouts are supplied, with the latter
+/// being registered by the load() function.
+///
+/// All callouts do trivial calculations, the result of all being called in
+/// sequence being
+///
+/// @f[ ((7 * data_1) - data_2) * data_3 @f]
+///
+/// ...where data_1, data_2 and data_3 are the values passed in arguments of
+/// the same name to the three callouts (data_1 passed to hookpt_one, data_2 to
+/// hookpt_two etc.) and the result is returned in the argument "result".
+
+#include <hooks/hooks.h>
+#include <hooks/tests/marker_file.h>
+
+#include <fstream>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Callouts
+
+int
+context_create(CalloutHandle& handle) {
+ handle.setContext("result", static_cast<int>(7));
+ handle.setArgument("result", static_cast<int>(7));
+ return (0);
+}
+
+// First callout adds the passed "data_1" argument to the initialized context
+// value of 7. (Note that the value set by context_create is accessed through
+// context and not the argument, so checking that context is correctly passed
+// between callouts in the same library.)
+
+int
+hookpt_one(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_1", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result *= data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Second callout subtracts the passed value of data_2 from the current
+// running total.
+
+static int
+hook_nonstandard_two(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_2", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result -= data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Final callout multplies the current running total by data_3.
+
+static int
+hook_nonstandard_three(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_3", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result *= data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Framework functions
+
+int
+version() {
+ return (BIND10_HOOKS_VERSION);
+}
+
+int
+load(LibraryHandle& handle) {
+ // Register the non-standard functions
+ handle.registerCallout("hookpt_two", hook_nonstandard_two);
+ handle.registerCallout("hookpt_three", hook_nonstandard_three);
+
+ return (0);
+}
+
+int
+unload() {
+ // Create the marker file.
+ std::fstream marker;
+ marker.open(MARKER_FILE, std::fstream::out);
+ marker.close();
+
+ return (0);
+}
+
+};
+
diff --git a/src/lib/hooks/tests/handles_unittest.cc b/src/lib/hooks/tests/handles_unittest.cc
new file mode 100644
index 0000000..e5364bc
--- /dev/null
+++ b/src/lib/hooks/tests/handles_unittest.cc
@@ -0,0 +1,956 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/server_hooks.h>
+
+#include <boost/lexical_cast.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+
+/// @file
+/// CalloutHandle/LibraryHandle interaction tests
+///
+/// This file holds unit tests checking the interaction between the
+/// CalloutHandle/LibraryHandle and CalloutManager classes. In particular,
+/// they check that:
+///
+/// - A CalloutHandle's context is shared between callouts from the same
+/// library, but there is a separate context for each library.
+///
+/// - The various methods manipulating the items in the CalloutHandle's context
+/// work correctly.
+///
+/// - An active callout can only modify the registration of callouts registered
+/// by its own library.
+
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+class HandlesTest : public ::testing::Test {
+public:
+ /// @brief Constructor
+ ///
+ /// Sets up the various elements used in each test.
+ HandlesTest() {
+ // Set up four hooks, although through gamma
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+ alpha_index_ = hooks.registerHook("alpha");
+ beta_index_ = hooks.registerHook("beta");
+ gamma_index_ = hooks.registerHook("gamma");
+ delta_index_ = hooks.registerHook("delta");
+
+ // Set up for three libraries.
+ manager_.reset(new CalloutManager(3));
+
+ // Initialize remaining variables.
+ common_string_ = "";
+ }
+
+ /// @brief Return callout manager
+ boost::shared_ptr<CalloutManager> getCalloutManager() {
+ return (manager_);
+ }
+
+ /// Hook indexes - these are frequently accessed, so are accessed directly.
+ int alpha_index_;
+ int beta_index_;
+ int gamma_index_;
+ int delta_index_;
+
+ /// String accessible by all callouts whatever the library
+ static std::string common_string_;
+
+private:
+ /// Callout manager. Declared static so that the callout functions can
+ /// access it.
+ boost::shared_ptr<CalloutManager> manager_;
+};
+
+/// Define the common string
+std::string HandlesTest::common_string_;
+
+
+// The next set of functions define the callouts used by the tests. They
+// manipulate the data in such a way that callouts called - and the order in
+// which they were called - can be determined. The functions also check that
+// the "callout context" data areas are separate.
+//
+// Three libraries are assumed, and each supplies four callouts. All callouts
+// manipulate two context elements the CalloutHandle, the elements being called
+// "string" and "int" (which describe the type of data manipulated).
+//
+// For the string item, each callout shifts data to the left and inserts its own
+// data. The data is a string of the form "nmc", where "n" is the number of
+// the library, "m" is the callout number and "y" is the indication of what
+// callout handle was passed as an argument ("1" or "2": "0" is used when no
+// identification has been set in the callout handle).
+//
+// For simplicity, and to cut down the number of functions actually written,
+// the callout indicator ("1" or "2") ) used in the in the CalloutHandle
+// functions is passed via a CalloutArgument. The argument is named "string":
+// use of a name the same as that of one of the context elements serves as a
+// check that the argument name space and argument context space are separate.
+//
+// For integer data, the value starts at zero and an increment is added on each
+// call. This increment is equal to:
+//
+// 100 * library number + 10 * callout number + callout handle
+//
+// Although this gives less information than the string value, the reasons for
+// using it are:
+//
+// - It is a separate item in the context, so checks that the context can
+// handle multiple items.
+// - It provides an item that can be deleted by the context deletion
+// methods.
+
+
+// Values set in the CalloutHandle context. There are three libraries, so
+// there are three contexts for the callout, one for each library.
+
+std::string& resultCalloutString(int index) {
+ static std::string result_callout_string[3];
+ return (result_callout_string[index]);
+}
+
+int& resultCalloutInt(int index) {
+ static int result_callout_int[3];
+ return (result_callout_int[index]);
+}
+
+// A simple function to zero the results.
+
+static void zero_results() {
+ for (int i = 0; i < 3; ++i) {
+ resultCalloutString(i) = "";
+ resultCalloutInt(i) = 0;
+ }
+}
+
+
+// Library callouts.
+
+// Common code for setting the callout context values.
+
+int
+execute(CalloutHandle& callout_handle, int library_num, int callout_num) {
+
+ // Obtain the callout handle number
+ int handle_num = 0;
+ try {
+ callout_handle.getArgument("handle_num", handle_num);
+ } catch (const NoSuchArgument&) {
+ // handle_num argument not set: this is the case in the tests where
+ // the context_create hook check is tested.
+ handle_num = 0;
+ }
+
+ // Create the basic data to be appended to the context value.
+ int idata = 100 * library_num + 10 * callout_num + handle_num;
+ string sdata = boost::lexical_cast<string>(idata);
+
+ // Get the context data. As before, this will not exist for the first
+ // callout called. (In real life, the library should create it when the
+ // "context_create" hook gets called before any packet processing takes
+ // place.)
+ int int_value = 0;
+ try {
+ callout_handle.getContext("int", int_value);
+ } catch (const NoSuchCalloutContext&) {
+ int_value = 0;
+ }
+
+ string string_value = "";
+ try {
+ callout_handle.getContext("string", string_value);
+ } catch (const NoSuchCalloutContext&) {
+ string_value = "";
+ }
+
+ // Update the values and set them back in the callout context.
+ int_value += idata;
+ callout_handle.setContext("int", int_value);
+
+ string_value += sdata;
+ callout_handle.setContext("string", string_value);
+
+ return (0);
+}
+
+// The following functions are the actual callouts - the name is of the
+// form "callout_<library number>_<callout number>"
+
+int
+callout11(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 1, 1));
+}
+
+int
+callout12(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 1, 2));
+}
+
+int
+callout13(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 1, 3));
+}
+
+int
+callout21(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 2, 1));
+}
+
+int
+callout22(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 2, 2));
+}
+
+int
+callout23(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 2, 3));
+}
+
+int
+callout31(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 3, 1));
+}
+
+int
+callout32(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 3, 2));
+}
+
+int
+callout33(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 3, 3));
+}
+
+// Common callout code for the fourth hook (which makes the data available for
+// checking). It copies the library and callout context data to the global
+// variables.
+
+int printExecute(CalloutHandle& callout_handle, int library_num) {
+ callout_handle.getContext("string", resultCalloutString(library_num - 1));
+ callout_handle.getContext("int", resultCalloutInt(library_num - 1));
+
+ return (0);
+}
+
+// These are the actual callouts.
+
+int
+print1(CalloutHandle& callout_handle) {
+ return (printExecute(callout_handle, 1));
+}
+
+int
+print2(CalloutHandle& callout_handle) {
+ return (printExecute(callout_handle, 2));
+}
+
+int
+print3(CalloutHandle& callout_handle) {
+ return (printExecute(callout_handle, 3));
+}
+
+// This test checks the many-faced nature of the context for the CalloutContext.
+
+TEST_F(HandlesTest, ContextAccessCheck) {
+ // Register callouts for the different libraries.
+ CalloutHandle handle(getCalloutManager());
+
+ // Library 0.
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout11);
+ getCalloutManager()->registerCallout("beta", callout12);
+ getCalloutManager()->registerCallout("gamma", callout13);
+ getCalloutManager()->registerCallout("delta", print1);
+
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout21);
+ getCalloutManager()->registerCallout("beta", callout22);
+ getCalloutManager()->registerCallout("gamma", callout23);
+ getCalloutManager()->registerCallout("delta", print2);
+
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout31);
+ getCalloutManager()->registerCallout("beta", callout32);
+ getCalloutManager()->registerCallout("gamma", callout33);
+ getCalloutManager()->registerCallout("delta", print3);
+
+ // Create the callout handles and distinguish them by setting the
+ // "handle_num" argument.
+ CalloutHandle callout_handle_1(getCalloutManager());
+ callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+
+ CalloutHandle callout_handle_2(getCalloutManager());
+ callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+
+ // Now call the callouts attached to the first three hooks. Each hook is
+ // called twice (once for each callout handle) before the next hook is
+ // called.
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+ getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+ getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
+ getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
+ getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
+
+ // Get the results for each callout (the callout on hook "delta" copies
+ // the context values into a location the test can access). Explicitly
+ // zero the variables before getting the results so we are certain that
+ // the values are the results of the callouts.
+
+ zero_results();
+
+ // To explain the expected callout context results.
+ //
+ // Each callout handle maintains a separate context for each library. When
+ // the first call to callCallouts() is made, "111" gets appended to
+ // the context for library 1 maintained by the first callout handle, "211"
+ // gets appended to the context maintained for library 2, and "311" to
+ // the context maintained for library 3. In each case, the first digit
+ // corresponds to the library number, the second to the callout number and
+ // the third to the "handle_num" of the callout handle. For the first call
+ // to callCallouts, handle 1 is used, so the last digit is always 1.
+ //
+ // The next call to callCallouts() calls the same callouts but for the
+ // second callout handle. It also maintains three contexts (one for
+ // each library) and they will get "112", "212", "312" appended to
+ // them. The explanation for the digits is the same as before, except that
+ // in this case, the callout handle is number 2, so the third digit is
+ // always 2. These additions don't affect the contexts maintained by
+ // callout handle 1.
+ //
+ // The process is then repeated for hooks "beta" and "gamma" which, for
+ // callout handle 1, append "121", "221" and "321" for hook "beta" and
+ // "311", "321" and "331" for hook "gamma".
+ //
+ // The expected integer values can be found by summing up the values
+ // corresponding to the elements of the strings.
+
+ // At this point, we have only called the "print" function for callout
+ // handle "1", so the following results are checking the context values
+ // maintained in that callout handle.
+
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+ EXPECT_EQ("111121131", resultCalloutString(0));
+ EXPECT_EQ("211221231", resultCalloutString(1));
+ EXPECT_EQ("311321331", resultCalloutString(2));
+
+ EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
+ EXPECT_EQ((211 + 221 + 231), resultCalloutInt(1));
+ EXPECT_EQ((311 + 321 + 331), resultCalloutInt(2));
+
+ // Repeat the checks for callout 2.
+
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+
+ EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
+ EXPECT_EQ((212 + 222 + 232), resultCalloutInt(1));
+ EXPECT_EQ((312 + 322 + 332), resultCalloutInt(2));
+
+ EXPECT_EQ("112122132", resultCalloutString(0));
+ EXPECT_EQ("212222232", resultCalloutString(1));
+ EXPECT_EQ("312322332", resultCalloutString(2));
+}
+
+// Now repeat the test, but add a deletion callout to the list. The "beta"
+// hook of library 2 will have an additional callout to delete the "int"
+// element: the same hook for library 3 will delete both elements. In
+// addition, the names of context elements for the libraries at this point
+// will be printed.
+
+// List of context item names.
+
+vector<string>&
+getItemNames(int index) {
+ static vector<string> context_items[3];
+ return (context_items[index]);
+}
+
+// Context item deletion functions.
+
+int
+deleteIntContextItem(CalloutHandle& handle) {
+ handle.deleteContext("int");
+ return (0);
+}
+
+int
+deleteAllContextItems(CalloutHandle& handle) {
+ handle.deleteAllContext();
+ return (0);
+}
+
+// Generic print function - copy names in sorted order.
+
+int
+printContextNamesExecute(CalloutHandle& handle, int library_num) {
+ const int index = library_num - 1;
+ getItemNames(index) = handle.getContextNames();
+ sort(getItemNames(index).begin(), getItemNames(index).end());
+ return (0);
+}
+
+int
+printContextNames1(CalloutHandle& handle) {
+ return (printContextNamesExecute(handle, 1));
+}
+
+int
+printContextNames2(CalloutHandle& handle) {
+ return (printContextNamesExecute(handle, 2));
+}
+
+int
+printContextNames3(CalloutHandle& handle) {
+ return (printContextNamesExecute(handle, 3));
+}
+
+// Perform the test including deletion of context items.
+
+TEST_F(HandlesTest, ContextDeletionCheck) {
+
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout11);
+ getCalloutManager()->registerCallout("beta", callout12);
+ getCalloutManager()->registerCallout("beta", printContextNames1);
+ getCalloutManager()->registerCallout("gamma", callout13);
+ getCalloutManager()->registerCallout("delta", print1);
+
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout21);
+ getCalloutManager()->registerCallout("beta", callout22);
+ getCalloutManager()->registerCallout("beta", deleteIntContextItem);
+ getCalloutManager()->registerCallout("beta", printContextNames2);
+ getCalloutManager()->registerCallout("gamma", callout23);
+ getCalloutManager()->registerCallout("delta", print2);
+
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout31);
+ getCalloutManager()->registerCallout("beta", callout32);
+ getCalloutManager()->registerCallout("beta", deleteAllContextItems);
+ getCalloutManager()->registerCallout("beta", printContextNames3);
+ getCalloutManager()->registerCallout("gamma", callout33);
+ getCalloutManager()->registerCallout("delta", print3);
+
+ // Create the callout handles and distinguish them by setting the "long"
+ // argument.
+ CalloutHandle callout_handle_1(getCalloutManager());
+ callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+
+ CalloutHandle callout_handle_2(getCalloutManager());
+ callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+
+ // Now call the callouts attached to the first three hooks. Each hook is
+ // called twice (once for each callout handle) before the next hook is
+ // called.
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+ getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+ getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
+ getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
+ getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
+
+ // Get the results for each callout. Explicitly zero the variables before
+ // getting the results so we are certain that the values are the results
+ // of the callouts.
+
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+
+ // The logic by which the expected results are arrived at is described
+ // in the ContextAccessCheck test. The results here are different
+ // because context items have been modified along the way.
+
+ EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
+ EXPECT_EQ(( 231), resultCalloutInt(1));
+ EXPECT_EQ(( 331), resultCalloutInt(2));
+
+ EXPECT_EQ("111121131", resultCalloutString(0));
+ EXPECT_EQ("211221231", resultCalloutString(1));
+ EXPECT_EQ( "331", resultCalloutString(2));
+
+ // Repeat the checks for callout handle 2.
+
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+
+ EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
+ EXPECT_EQ(( 232), resultCalloutInt(1));
+ EXPECT_EQ(( 332), resultCalloutInt(2));
+
+ EXPECT_EQ("112122132", resultCalloutString(0));
+ EXPECT_EQ("212222232", resultCalloutString(1));
+ EXPECT_EQ( "332", resultCalloutString(2));
+
+ // ... and check what the names of the context items are after the callouts
+ // for hook "beta". We know they are in sorted order.
+
+ EXPECT_EQ(2, getItemNames(0).size());
+ EXPECT_EQ(string("int"), getItemNames(0)[0]);
+ EXPECT_EQ(string("string"), getItemNames(0)[1]);
+
+ EXPECT_EQ(1, getItemNames(1).size());
+ EXPECT_EQ(string("string"), getItemNames(1)[0]);
+
+ EXPECT_EQ(0, getItemNames(2).size());
+}
+
+// Tests that the CalloutHandle's constructor and destructor call the
+// context_create and context_destroy callbacks (if registered). For
+// simplicity, we'll use the same callout functions as used above.
+
+TEST_F(HandlesTest, ConstructionDestructionCallouts) {
+
+ // Register context callouts.
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("context_create", callout11);
+ getCalloutManager()->registerCallout("context_create", print1);
+ getCalloutManager()->registerCallout("context_destroy", callout12);
+ getCalloutManager()->registerCallout("context_destroy", print1);
+
+ // Create the CalloutHandle and check that the constructor callout
+ // has run.
+ zero_results();
+ boost::scoped_ptr<CalloutHandle>
+ callout_handle(new CalloutHandle(getCalloutManager()));
+ EXPECT_EQ("110", resultCalloutString(0));
+ EXPECT_EQ(110, resultCalloutInt(0));
+
+ // Check that the destructor callout runs. Note that the "print1" callout
+ // didn't destroy the library context - it only copied it to where it
+ // could be examined. As a result, the destructor callout appends its
+ // elements to the constructor's values and the result is printed.
+ zero_results();
+ callout_handle.reset();
+
+ EXPECT_EQ("110120", resultCalloutString(0));
+ EXPECT_EQ((110 + 120), resultCalloutInt(0));
+}
+
+// Dynamic callout registration and deregistration.
+// The following are the dynamic registration/deregistration callouts.
+
+
+// Add callout_78_alpha - adds a callout to hook alpha that appends "78x"
+// (where "x" is the callout handle) to the current output.
+
+int
+callout78(CalloutHandle& callout_handle) {
+ return (execute(callout_handle, 7, 8));
+}
+
+int
+add_callout78_alpha(CalloutHandle& callout_handle) {
+ callout_handle.getLibraryHandle().registerCallout("alpha", callout78);
+ return (0);
+}
+
+int
+delete_callout78_alpha(CalloutHandle& callout_handle) {
+ static_cast<void>(
+ callout_handle.getLibraryHandle().deregisterCallout("alpha",
+ callout78));
+ return (0);
+}
+
+// Check that a callout can register another callout on a different hook.
+
+TEST_F(HandlesTest, DynamicRegistrationAnotherHook) {
+ // Register callouts for the different libraries.
+ CalloutHandle handle(getCalloutManager());
+
+ // Set up callouts on "alpha".
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout11);
+ getCalloutManager()->registerCallout("delta", print1);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout21);
+ getCalloutManager()->registerCallout("delta", print2);
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", callout31);
+ getCalloutManager()->registerCallout("delta", print3);
+
+ // ... and on "beta", set up the function to add a hook to alpha (but only
+ // for library 1).
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("beta", add_callout78_alpha);
+
+ // See what we get for calling the callouts on alpha first.
+ CalloutHandle callout_handle_1(getCalloutManager());
+ callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+ EXPECT_EQ("111", resultCalloutString(0));
+ EXPECT_EQ("211", resultCalloutString(1));
+ EXPECT_EQ("311", resultCalloutString(2));
+
+ // All as expected, now call the callouts on beta. This should add a
+ // callout to the list of callouts for alpha, which we should see when
+ // we run the test again.
+ getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+
+ // Use a new callout handle so as to get fresh callout context.
+ CalloutHandle callout_handle_2(getCalloutManager());
+ callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+ EXPECT_EQ("112", resultCalloutString(0));
+ EXPECT_EQ("212782", resultCalloutString(1));
+ EXPECT_EQ("312", resultCalloutString(2));
+}
+
+// Check that a callout can register another callout on the same hook.
+// Note that the registration only applies to a subsequent invocation of
+// callCallouts, not to the current one. In other words, if
+//
+// * the callout list for a library is "A then B then C"
+// * when callCallouts is executed "B" adds "D" to that list,
+//
+// ... the current execution of callCallouts only executes A, B and C. A
+// subsequent invocation will execute A, B, C then D.
+
+TEST_F(HandlesTest, DynamicRegistrationSameHook) {
+ // Register callouts for the different libraries.
+ CalloutHandle handle(getCalloutManager());
+
+ // Set up callouts on "alpha".
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout11);
+ getCalloutManager()->registerCallout("alpha", add_callout78_alpha);
+ getCalloutManager()->registerCallout("delta", print1);
+
+ // See what we get for calling the callouts on alpha first.
+ CalloutHandle callout_handle_1(getCalloutManager());
+ callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+ EXPECT_EQ("111", resultCalloutString(0));
+
+ // Run it again - we should have added something to this hook.
+ CalloutHandle callout_handle_2(getCalloutManager());
+ callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+ EXPECT_EQ("112782", resultCalloutString(0));
+
+ // And a third time...
+ CalloutHandle callout_handle_3(getCalloutManager());
+ callout_handle_3.setArgument("handle_num", static_cast<int>(3));
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_3);
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_3);
+ EXPECT_EQ("113783783", resultCalloutString(0));
+}
+
+// Deregistration of a callout from a different hook
+
+TEST_F(HandlesTest, DynamicDeregistrationDifferentHook) {
+ // Register callouts for the different libraries.
+ CalloutHandle handle(getCalloutManager());
+
+ // Set up callouts on "alpha".
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout11);
+ getCalloutManager()->registerCallout("alpha", callout78);
+ getCalloutManager()->registerCallout("alpha", callout11);
+ getCalloutManager()->registerCallout("delta", print1);
+
+ getCalloutManager()->registerCallout("beta", delete_callout78_alpha);
+
+ // Call the callouts on alpha
+ CalloutHandle callout_handle_1(getCalloutManager());
+ callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+ EXPECT_EQ("111781111", resultCalloutString(0));
+
+ // Run the callouts on hook beta to remove the callout on alpha.
+ getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
+
+ // The run of the callouts should have altered the callout list on the
+ // first library for hook alpha, so call again to make sure.
+ CalloutHandle callout_handle_2(getCalloutManager());
+ callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+ EXPECT_EQ("112112", resultCalloutString(0));
+}
+
+// Deregistration of a callout from the same hook
+
+TEST_F(HandlesTest, DynamicDeregistrationSameHook) {
+ // Register callouts for the different libraries.
+ CalloutHandle handle(getCalloutManager());
+
+ // Set up callouts on "alpha".
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout11);
+ getCalloutManager()->registerCallout("alpha", delete_callout78_alpha);
+ getCalloutManager()->registerCallout("alpha", callout78);
+ getCalloutManager()->registerCallout("delta", print1);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", callout21);
+ getCalloutManager()->registerCallout("alpha", callout78);
+ getCalloutManager()->registerCallout("delta", print2);
+
+ // Call the callouts on alpha
+ CalloutHandle callout_handle_1(getCalloutManager());
+ callout_handle_1.setArgument("handle_num", static_cast<int>(1));
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
+
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
+ EXPECT_EQ("111781", resultCalloutString(0));
+ EXPECT_EQ("211781", resultCalloutString(1));
+
+ // The run of the callouts should have altered the callout list on the
+ // first library for hook alpha, so call again to make sure.
+ CalloutHandle callout_handle_2(getCalloutManager());
+ callout_handle_2.setArgument("handle_num", static_cast<int>(2));
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
+
+ zero_results();
+ getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
+ EXPECT_EQ("112", resultCalloutString(0));
+ EXPECT_EQ("212782", resultCalloutString(1));
+}
+
+// Testing the operation of the "skip" flag. Callouts print the value
+// they see in the flag and either leave it unchanged, set it or clear it.
+
+int
+calloutPrintSkip(CalloutHandle& handle) {
+ static const std::string YES("Y");
+ static const std::string NO("N");
+
+ HandlesTest::common_string_ = HandlesTest::common_string_ +
+ (handle.getSkip() ? YES : NO);
+ return (0);
+}
+
+int
+calloutSetSkip(CalloutHandle& handle) {
+ static_cast<void>(calloutPrintSkip(handle));
+ handle.setSkip(true);
+ return (0);
+}
+
+int
+calloutClearSkip(CalloutHandle& handle) {
+ static_cast<void>(calloutPrintSkip(handle));
+ handle.setSkip(false);
+ return (0);
+}
+
+// Do a series of tests, returning with the skip flag set "true".
+
+TEST_F(HandlesTest, ReturnSkipSet) {
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+
+ CalloutHandle callout_handle(getCalloutManager());
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+ // Check result. For each of visual checking, the expected string is
+ // divided into sections corresponding to the blocks of callouts above.
+ EXPECT_EQ(std::string("NNYY" "NNYYN" "NNYN"), common_string_);
+
+ // ... and check that the skip flag on exit from callCallouts is set.
+ EXPECT_TRUE(callout_handle.getSkip());
+}
+
+// Repeat the test, returning with the skip flag clear.
+TEST_F(HandlesTest, ReturnSkipClear) {
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip);
+
+ CalloutHandle callout_handle(getCalloutManager());
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+ // Check result. For each of visual checking, the expected string is
+ // divided into sections corresponding to the blocks of callouts above.
+ EXPECT_EQ(std::string("NYY" "NNYNYN" "NNNY"), common_string_);
+
+ // ... and check that the skip flag on exit from callCallouts is set.
+ EXPECT_FALSE(callout_handle.getSkip());
+}
+
+// The next set of callouts do a similar thing to the above "skip" tests,
+// but alter the value of a string argument. This is for testing that the
+// a callout is able to change an argument and return it to the caller.
+
+const char* MODIFIED_ARG = "modified_arg";
+
+int
+calloutSetArgumentCommon(CalloutHandle& handle, const char* what) {
+ std::string modified_arg = "";
+
+ handle.getArgument(MODIFIED_ARG, modified_arg);
+ modified_arg = modified_arg + std::string(what);
+ handle.setArgument(MODIFIED_ARG, modified_arg);
+ return (0);
+}
+
+int
+calloutSetArgumentYes(CalloutHandle& handle) {
+ return (calloutSetArgumentCommon(handle, "Y"));
+}
+
+int
+calloutSetArgumentNo(CalloutHandle& handle) {
+ return (calloutSetArgumentCommon(handle, "N"));
+}
+
+// ... and a callout to just copy the argument to the "common_string_" variable
+// but otherwise not alter it.
+
+int
+calloutPrintArgument(CalloutHandle& handle) {
+ handle.getArgument(MODIFIED_ARG, HandlesTest::common_string_);
+ return (0);
+}
+
+TEST_F(HandlesTest, CheckModifiedArgument) {
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+ getCalloutManager()->registerCallout("alpha", calloutPrintArgument);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+
+ getCalloutManager()->setLibraryIndex(2);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
+
+ // Create the argument with an initial empty string value. Then call the
+ // sequence of callouts above.
+ CalloutHandle callout_handle(getCalloutManager());
+ std::string modified_arg = "";
+ callout_handle.setArgument(MODIFIED_ARG, modified_arg);
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+ // Check the intermediate and results. For visual checking, the expected
+ // string is divided into sections corresponding to the blocks of callouts
+ // above.
+ EXPECT_EQ(std::string("YNN" "YY"), common_string_);
+
+ callout_handle.getArgument(MODIFIED_ARG, modified_arg);
+ EXPECT_EQ(std::string("YNN" "YYNN" "YNY"), modified_arg);
+}
+
+// Test that the CalloutHandle provides the name of the hook to which the
+// callout is attached.
+
+int
+callout_hook_name(CalloutHandle& callout_handle) {
+ HandlesTest::common_string_ = callout_handle.getHookName();
+ return (0);
+}
+
+int
+callout_hook_dummy(CalloutHandle&) {
+ return (0);
+}
+
+TEST_F(HandlesTest, HookName) {
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->registerCallout("alpha", callout_hook_name);
+ getCalloutManager()->registerCallout("beta", callout_hook_name);
+
+ // Call alpha and beta callouts and check the hook to which they belong.
+ CalloutHandle callout_handle(getCalloutManager());
+
+ EXPECT_EQ(std::string(""), HandlesTest::common_string_);
+
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+ EXPECT_EQ(std::string("alpha"), HandlesTest::common_string_);
+
+ getCalloutManager()->callCallouts(beta_index_, callout_handle);
+ EXPECT_EQ(std::string("beta"), HandlesTest::common_string_);
+
+ // Make sure that the callout accesses the name even if it is not the
+ // only callout in the list.
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->registerCallout("gamma", callout_hook_dummy);
+ getCalloutManager()->registerCallout("gamma", callout_hook_name);
+ getCalloutManager()->registerCallout("gamma", callout_hook_dummy);
+
+ EXPECT_EQ(std::string("beta"), HandlesTest::common_string_);
+ getCalloutManager()->callCallouts(gamma_index_, callout_handle);
+ EXPECT_EQ(std::string("gamma"), HandlesTest::common_string_);
+}
+
+} // Anonymous namespace
+
diff --git a/src/lib/hooks/tests/hooks_manager_unittest.cc b/src/lib/hooks/tests/hooks_manager_unittest.cc
new file mode 100644
index 0000000..c5dba60
--- /dev/null
+++ b/src/lib/hooks/tests/hooks_manager_unittest.cc
@@ -0,0 +1,454 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_handle.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/server_hooks.h>
+
+#include <hooks/tests/common_test_class.h>
+#include <hooks/tests/test_libraries.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+/// @brief Hooks manager collection test class
+
+class HooksManagerTest : public ::testing::Test,
+ public HooksCommonTestClass {
+public:
+ /// @brief Constructor
+ ///
+ /// Reset the hooks manager. The hooks manager is a singleton, so needs
+ /// to be reset for each test.
+ HooksManagerTest() {
+ HooksManager::unloadLibraries();
+ }
+
+ /// @brief Destructor
+ ///
+ /// Unload all libraries.
+ ~HooksManagerTest() {
+ HooksManager::unloadLibraries();
+ }
+
+
+ /// @brief Call callouts test
+ ///
+ /// See the header for HooksCommonTestClass::execute for details.
+ ///
+ /// @param r0...r3, d1..d3 Values and intermediate values expected. They
+ /// are ordered so that the variables appear in the argument list in
+ /// the order they are used.
+ void executeCallCallouts(int r0, int d1, int r1, int d2, int r2, int d3,
+ int r3) {
+ static const char* COMMON_TEXT = " callout returned the wong value";
+ static const char* RESULT = "result";
+
+ // Get a CalloutHandle for the calculation.
+ CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+
+ // Initialize the argument RESULT. This simplifies testing by
+ // eliminating the generation of an exception when we try the unload
+ // test. In that case, RESULT is unchanged.
+ int result = -1;
+ handle->setArgument(RESULT, result);
+
+ // Seed the calculation.
+ HooksManager::callCallouts(isc::hooks::ServerHooks::CONTEXT_CREATE,
+ *handle);
+ handle->getArgument(RESULT, result);
+ EXPECT_EQ(r0, result) << "context_create" << COMMON_TEXT;
+
+ // Perform the first calculation.
+ handle->setArgument("data_1", d1);
+ HooksManager::callCallouts(hookpt_one_index_, *handle);
+ handle->getArgument(RESULT, result);
+ EXPECT_EQ(r1, result) << "hookpt_one" << COMMON_TEXT;
+
+ // ... the second ...
+ handle->setArgument("data_2", d2);
+ HooksManager::callCallouts(hookpt_two_index_, *handle);
+ handle->getArgument(RESULT, result);
+ EXPECT_EQ(r2, result) << "hookpt_two" << COMMON_TEXT;
+
+ // ... and the third.
+ handle->setArgument("data_3", d3);
+ HooksManager::callCallouts(hookpt_three_index_, *handle);
+ handle->getArgument(RESULT, result);
+ EXPECT_EQ(r3, result) << "hookpt_three" << COMMON_TEXT;
+ }
+
+};
+
+// This is effectively the same test as for LibraryManager, but using the
+// HooksManager object.
+
+TEST_F(HooksManagerTest, LoadLibraries) {
+
+ // 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));
+
+ // Load the libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Execute the callouts. The first library implements the calculation.
+ //
+ // r3 = (7 * d1 - d2) * d3
+ //
+ // The last-loaded library implements the calculation
+ //
+ // r3 = (10 + d1) * d2 - d3
+ //
+ // Putting the processing for each library together in the appropriate
+ // order, we get:
+ //
+ // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+ {
+ SCOPED_TRACE("Calculation with libraries loaded");
+ executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+ }
+
+ // Try unloading the libraries.
+ EXPECT_NO_THROW(HooksManager::unloadLibraries());
+
+ // Re-execute the calculation - callouts can be called but as nothing
+ // happens, the result should always be -1.
+ {
+ SCOPED_TRACE("Calculation with libraries not loaded");
+ executeCallCallouts(-1, 3, -1, 22, -1, 83, -1);
+ }
+}
+
+// This is effectively the same test as above, but with a library generating
+// an error when loaded. It is expected that the failing library will not be
+// loaded, but others will be.
+
+TEST_F(HooksManagerTest, LoadLibrariesWithError) {
+
+ // 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));
+
+ // Load the libraries. We expect a failure return because one of the
+ // libraries fails to load.
+ EXPECT_FALSE(HooksManager::loadLibraries(library_names));
+
+ // Execute the callouts. The first library implements the calculation.
+ //
+ // r3 = (7 * d1 - d2) * d3
+ //
+ // The last-loaded library implements the calculation
+ //
+ // r3 = (10 + d1) * d2 - d3
+ //
+ // Putting the processing for each library together in the appropriate
+ // order, we get:
+ //
+ // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+ {
+ SCOPED_TRACE("Calculation with libraries loaded");
+ executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+ }
+
+ // Try unloading the libraries.
+ EXPECT_NO_THROW(HooksManager::unloadLibraries());
+
+ // Re-execute the calculation - callouts can be called but as nothing
+ // happens, the result should always be -1.
+ {
+ SCOPED_TRACE("Calculation with libraries not loaded");
+ executeCallCallouts(-1, 3, -1, 22, -1, 83, -1);
+ }
+}
+
+// Test that we can unload a set of libraries while we have a CalloutHandle
+// created on them in existence, and can delete the handle afterwards.
+
+TEST_F(HooksManagerTest, CalloutHandleUnloadLibrary) {
+
+ // Set up the list of libraries to be loaded.
+ std::vector<std::string> library_names;
+ library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+
+ // Load the libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Execute the callouts. Thiis library implements:
+ //
+ // r3 = (7 * d1 - d2) * d3
+ {
+ SCOPED_TRACE("Calculation with full callout library loaded");
+ executeCallCallouts(7, 4, 28, 8, 20, 2, 40);
+ }
+
+ // Get an outstanding callout handle on this library.
+ CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+
+ // Execute once of the callouts again to ensure that the handle contains
+ // memory allocated by the library.
+ HooksManager::callCallouts(ServerHooks::CONTEXT_CREATE, *handle);
+
+ // Unload the libraries.
+ HooksManager::unloadLibraries();
+
+ // Deleting the callout handle should not cause a segmentation fault.
+ handle.reset();
+}
+
+// Test that we can load a new set of libraries while we have a CalloutHandle
+// created on them in existence, and can delete the handle afterwards.
+
+TEST_F(HooksManagerTest, CalloutHandleLoadLibrary) {
+
+ // Set up the list of libraries to be loaded.
+ std::vector<std::string> library_names;
+ library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+
+ // Load the libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Execute the callouts. Thiis library implements:
+ //
+ // r3 = (7 * d1 - d2) * d3
+ {
+ SCOPED_TRACE("Calculation with full callout library loaded");
+ executeCallCallouts(7, 4, 28, 8, 20, 2, 40);
+ }
+
+ // Get an outstanding callout handle on this library and execute one of
+ // the callouts again to ensure that the handle contains memory allocated
+ // by the library.
+ CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+ HooksManager::callCallouts(ServerHooks::CONTEXT_CREATE, *handle);
+
+ // Load a new library that implements the calculation
+ //
+ // r3 = (10 + d1) * d2 - d3
+ std::vector<std::string> new_library_names;
+ new_library_names.push_back(std::string(BASIC_CALLOUT_LIBRARY));
+
+ // Load the libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(new_library_names));
+
+ // Execute the calculation. Note that we still have the CalloutHandle
+ // for the old library: however, this should not affect the new calculation.
+ {
+ SCOPED_TRACE("Calculation with basic callout library loaded");
+ executeCallCallouts(10, 7, 17, 3, 51, 16, 35);
+ }
+
+ // Deleting the old callout handle should not cause a segmentation fault.
+ handle.reset();
+}
+
+// This is effectively the same test as the LoadLibraries test.
+
+TEST_F(HooksManagerTest, ReloadSameLibraries) {
+
+ // 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));
+
+ // Load the libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Execute the callouts. See the LoadLibraries test for an explanation of
+ // the calculation.
+ {
+ SCOPED_TRACE("Calculation with libraries loaded");
+ executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+ }
+
+ // Try reloading the libraries and re-execute the calculation - we should
+ // get the same results.
+ EXPECT_NO_THROW(HooksManager::loadLibraries(library_names));
+ {
+ SCOPED_TRACE("Calculation with libraries reloaded");
+ executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+ }
+}
+
+TEST_F(HooksManagerTest, ReloadLibrariesReverseOrder) {
+
+ // 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));
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Execute the callouts. The first library implements the calculation.
+ //
+ // r3 = (7 * d1 - d2) * d3
+ //
+ // The last-loaded library implements the calculation
+ //
+ // r3 = (10 + d1) * d2 - d3
+ //
+ // Putting the processing for each library together in the given order
+ // gives.
+ //
+ // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+ {
+ SCOPED_TRACE("Calculation with libraries loaded");
+ executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+ }
+
+ // Reload the libraries in the reverse order.
+ std::reverse(library_names.begin(), library_names.end());
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // The calculation in the reverse order gives:
+ //
+ // r3 = ((((7 + d1) * d1) * d2 - d2) - d3) * d3
+ {
+ SCOPED_TRACE("Calculation with libraries loaded in reverse order");
+ executeCallCallouts(7, 3, 30, 3, 87, 7, 560);
+ }
+}
+
+// Local callouts for the test of server-registered callouts.
+
+namespace {
+
+ int
+testPreCallout(CalloutHandle& handle) {
+ handle.setArgument("result", static_cast<int>(1027));
+ return (0);
+}
+
+int
+testPostCallout(CalloutHandle& handle) {
+ int result;
+ handle.getArgument("result", result);
+ result *= 2;
+ handle.setArgument("result", result);
+ return (0);
+}
+
+}
+
+// The next test registers the pre and post- callouts above for hook hookpt_two,
+// and checks they are called.
+
+TEST_F(HooksManagerTest, PrePostCalloutTest) {
+
+ // Load a single library.
+ std::vector<std::string> library_names;
+ library_names.push_back(std::string(FULL_CALLOUT_LIBRARY));
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Load the pre- and post- callouts.
+ HooksManager::preCalloutsLibraryHandle().registerCallout("hookpt_two",
+ testPreCallout);
+ HooksManager::postCalloutsLibraryHandle().registerCallout("hookpt_two",
+ testPostCallout);
+
+ // Execute the callouts. hookpt_two implements the calculation:
+ //
+ // "result - data_2"
+ //
+ // With the pre- and post- callouts above, the result expected is
+ //
+ // (1027 - data_2) * 2
+ CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+ handle->setArgument("result", static_cast<int>(0));
+ handle->setArgument("data_2", static_cast<int>(15));
+
+ HooksManager::callCallouts(hookpt_two_index_, *handle);
+
+ int result = 0;
+ handle->getArgument("result", result);
+ EXPECT_EQ(2024, result);
+
+ // ... and check that the pre- and post- callout functions don't survive a
+ // reload.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+ handle = HooksManager::createCalloutHandle();
+
+ handle->setArgument("result", static_cast<int>(0));
+ handle->setArgument("data_2", static_cast<int>(15));
+
+ HooksManager::callCallouts(hookpt_two_index_, *handle);
+
+ result = 0;
+ handle->getArgument("result", result);
+ EXPECT_EQ(-15, result);
+}
+
+// Check that everything works even with no libraries loaded. First that
+// calloutsPresent() always returns false.
+
+TEST_F(HooksManagerTest, NoLibrariesCalloutsPresent) {
+ // No callouts should be present on any hooks.
+ EXPECT_FALSE(HooksManager::calloutsPresent(hookpt_one_index_));
+ EXPECT_FALSE(HooksManager::calloutsPresent(hookpt_two_index_));
+ EXPECT_FALSE(HooksManager::calloutsPresent(hookpt_three_index_));
+}
+
+TEST_F(HooksManagerTest, NoLibrariesCallCallouts) {
+ executeCallCallouts(-1, 3, -1, 22, -1, 83, -1);
+}
+
+// Test the encapsulation of the ServerHooks::registerHook() method.
+
+TEST_F(HooksManagerTest, RegisterHooks) {
+ ServerHooks::getServerHooks().reset();
+ EXPECT_EQ(2, ServerHooks::getServerHooks().getCount());
+
+ // Check that the hook indexes are as expected. (Use temporary variables
+ // as it appears that Google test can't access the constants.)
+ int sh_cc = ServerHooks::CONTEXT_CREATE;
+ int hm_cc = HooksManager::CONTEXT_CREATE;
+ EXPECT_EQ(sh_cc, hm_cc);
+
+ int sh_cd = ServerHooks::CONTEXT_DESTROY;
+ int hm_cd = HooksManager::CONTEXT_DESTROY;
+ EXPECT_EQ(sh_cd, hm_cd);
+
+ // Register a few hooks and check we have the indexes as expected.
+ EXPECT_EQ(2, HooksManager::registerHook(string("alpha")));
+ EXPECT_EQ(3, HooksManager::registerHook(string("beta")));
+ EXPECT_EQ(4, HooksManager::registerHook(string("gamma")));
+ EXPECT_THROW(static_cast<void>(HooksManager::registerHook(string("alpha"))),
+ DuplicateHook);
+
+ // ... an check the hooks are as we expect.
+ EXPECT_EQ(5, ServerHooks::getServerHooks().getCount());
+ vector<string> names = ServerHooks::getServerHooks().getHookNames();
+ sort(names.begin(), names.end());
+
+ EXPECT_EQ(string("alpha"), names[0]);
+ EXPECT_EQ(string("beta"), names[1]);
+ EXPECT_EQ(string("context_create"), names[2]);
+ EXPECT_EQ(string("context_destroy"), names[3]);
+ EXPECT_EQ(string("gamma"), names[4]);
+}
+
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/incorrect_version_library.cc b/src/lib/hooks/tests/incorrect_version_library.cc
new file mode 100644
index 0000000..bb6eedf
--- /dev/null
+++ b/src/lib/hooks/tests/incorrect_version_library.cc
@@ -0,0 +1,33 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief Incorrect version function test
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager). The characteristics of the library produced from this
+/// file are:
+///
+/// - It contains the version() framework function only, which returns an
+/// incorrect version number.
+
+#include <hooks/hooks.h>
+
+extern "C" {
+
+int version() {
+ return (BIND10_HOOKS_VERSION + 1);
+}
+
+};
diff --git a/src/lib/hooks/tests/library_manager_collection_unittest.cc b/src/lib/hooks/tests/library_manager_collection_unittest.cc
new file mode 100644
index 0000000..549142f
--- /dev/null
+++ b/src/lib/hooks/tests/library_manager_collection_unittest.cc
@@ -0,0 +1,184 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_manager.h>
+#include <hooks/library_manager_collection.h>
+
+#include <hooks/tests/common_test_class.h>
+#include <hooks/tests/test_libraries.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+/// @brief Library manager collection test class
+
+class LibraryManagerCollectionTest : public ::testing::Test,
+ public HooksCommonTestClass {
+};
+
+/// @brief Public library manager collection class
+///
+/// This is an instance of the LibraryManagerCollection class but with the
+/// protected methods made public for test purposes.
+
+class PublicLibraryManagerCollection
+ : public isc::hooks::LibraryManagerCollection {
+public:
+ /// @brief Constructor
+ ///
+ /// @param List of libraries that this collection will manage. The order
+ /// of the libraries is important.
+ PublicLibraryManagerCollection(const std::vector<std::string>& libraries)
+ : LibraryManagerCollection(libraries)
+ {}
+
+ /// Public methods that call protected methods on the superclass.
+ using LibraryManagerCollection::unloadLibraries;
+};
+
+
+// This is effectively the same test as for LibraryManager, but using the
+// LibraryManagerCollection object.
+
+TEST_F(LibraryManagerCollectionTest, LoadLibraries) {
+
+ // 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));
+
+ // Set up the library manager collection and get the callout manager we'll
+ // be using.
+ PublicLibraryManagerCollection lm_collection(library_names);
+
+ // Load the libraries.
+ EXPECT_TRUE(lm_collection.loadLibraries());
+ boost::shared_ptr<CalloutManager> manager =
+ lm_collection.getCalloutManager();
+
+ // Execute the callouts. The first library implements the calculation.
+ //
+ // r3 = (7 * d1 - d2) * d3
+ //
+ // The last-loaded library implements the calculation
+ //
+ // r3 = (10 + d1) * d2 - d3
+ //
+ // Putting the processing for each library together in the appropriate
+ // order, we get:
+ //
+ // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+ {
+ SCOPED_TRACE("Doing calculation with libraries loaded");
+ executeCallCallouts(manager, 10, 3, 33, 2, 62, 3, 183);
+ }
+
+ // Try unloading the libraries.
+ EXPECT_NO_THROW(lm_collection.unloadLibraries());
+
+ // Re-execute the calculation - callouts can be called but as nothing
+ // happens, the result should always be -1.
+ {
+ SCOPED_TRACE("Doing calculation with libraries not loaded");
+ executeCallCallouts(manager, -1, 3, -1, 22, -1, 83, -1);
+ }
+}
+
+// This is effectively the same test as above, but with a library generating
+// an error when loaded. It is expected that the failing library will not be
+// loaded, but others will be.
+
+TEST_F(LibraryManagerCollectionTest, LoadLibrariesWithError) {
+
+ // 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));
+
+ // Set up the library manager collection and get the callout manager we'll
+ // be using.
+ PublicLibraryManagerCollection lm_collection(library_names);
+
+ // Load the libraries. We expect a failure status to be returned as
+ // one of the libraries failed to load.
+ EXPECT_FALSE(lm_collection.loadLibraries());
+ boost::shared_ptr<CalloutManager> manager =
+ lm_collection.getCalloutManager();
+
+ // Expect only two libraries were loaded.
+ EXPECT_EQ(2, manager->getNumLibraries());
+
+ // Execute the callouts. The first library implements the calculation.
+ //
+ // r3 = (7 * d1 - d2) * d3
+ //
+ // The last-loaded library implements the calculation
+ //
+ // r3 = (10 + d1) * d2 - d3
+ //
+ // Putting the processing for each library together in the appropriate
+ // order, we get:
+ //
+ // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+ {
+ SCOPED_TRACE("Doing calculation with libraries loaded");
+ executeCallCallouts(manager, 10, 3, 33, 2, 62, 3, 183);
+ }
+
+ // Try unloading the libraries.
+ EXPECT_NO_THROW(lm_collection.unloadLibraries());
+
+ // Re-execute the calculation - callouts can be called but as nothing
+ // happens, the result should always be -1.
+ {
+ SCOPED_TRACE("Doing calculation with libraries not loaded");
+ executeCallCallouts(manager, -1, 3, -1, 22, -1, 83, -1);
+ }
+}
+
+// Check that everything works even with no libraries loaded.
+
+TEST_F(LibraryManagerCollectionTest, NoLibrariesLoaded) {
+ // Set up the list of libraries to be loaded.
+ std::vector<std::string> library_names;
+
+ // Set up the library manager collection and get the callout manager we'll
+ // be using.
+ LibraryManagerCollection lm_collection(library_names);
+ EXPECT_TRUE(lm_collection.loadLibraries());
+ boost::shared_ptr<CalloutManager> manager =
+ lm_collection.getCalloutManager();
+
+ // Load the libraries.
+ EXPECT_TRUE(lm_collection.loadLibraries());
+
+ // Eecute the calculation - callouts can be called but as nothing
+ // happens, the result should always be -1.
+ executeCallCallouts(manager, -1, 3, -1, 22, -1, 83, -1);
+}
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/library_manager_unittest.cc b/src/lib/hooks/tests/library_manager_unittest.cc
new file mode 100644
index 0000000..c2f8cb7
--- /dev/null
+++ b/src/lib/hooks/tests/library_manager_unittest.cc
@@ -0,0 +1,555 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_manager.h>
+#include <hooks/server_hooks.h>
+
+#include <hooks/tests/common_test_class.h>
+#include <hooks/tests/marker_file.h>
+#include <hooks/tests/test_libraries.h>
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <fstream>
+#include <string>
+
+#include <unistd.h>
+
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+/// @brief Library manager test class
+
+class LibraryManagerTest : public ::testing::Test,
+ public HooksCommonTestClass {
+public:
+ /// @brief Constructor
+ ///
+ /// Initializes the CalloutManager object used in the tests. It sets it
+ /// up with the hooks initialized in the HooksCommonTestClass object and
+ /// with four libraries.
+ LibraryManagerTest() {
+ callout_manager_.reset(new CalloutManager(4));
+
+ // Ensure the marker file is not present at the start of a test.
+ static_cast<void>(unlink(MARKER_FILE));
+ }
+
+ /// @brief Destructor
+ ///
+ /// Ensures a marker file is removed after each test.
+ ~LibraryManagerTest() {
+ static_cast<void>(unlink(MARKER_FILE));
+ }
+
+ /// @brief Marker file present
+ ///
+ /// Convenience function to check whether a marker file is present. It
+ /// does this by opening the file.
+ ///
+ /// @return true if the marker file is present.
+ bool markerFilePresent() const {
+
+ // Try to open it.
+ std::fstream marker;
+ marker.open(MARKER_FILE, std::fstream::in);
+
+ // Check if it is open and close it if so.
+ bool exists = marker.is_open();
+ if (exists) {
+ marker.close();
+ }
+
+ return (exists);
+ }
+
+ /// @brief Call callouts test
+ ///
+ /// A wrapper around the method of the same name in the HooksCommonTestClass
+ /// object, this passes this class's CalloutManager to that method.
+ ///
+ /// @param r0...r3, d1..d3 Values and intermediate values expected. They
+ /// are ordered so that the variables appear in the argument list in
+ /// the order they are used. See HooksCommonTestClass::execute for
+ /// a full description. (rN is used to indicate an expected result,
+ /// dN is data to be passed to the calculation.)
+ void executeCallCallouts(int r0, int d1, int r1, int d2, int r2, int d3,
+ int r3) {
+ HooksCommonTestClass::executeCallCallouts(callout_manager_, r0, d1,
+ r1, d2, r2, d3, r3);
+ }
+
+ /// Callout manager used for the test.
+ boost::shared_ptr<CalloutManager> callout_manager_;
+};
+
+/// @brief Library manager class
+///
+/// This is an instance of the LibraryManager class but with the protected
+/// methods made public for test purposes.
+
+class PublicLibraryManager : public isc::hooks::LibraryManager {
+public:
+ /// @brief Constructor
+ ///
+ /// Stores the library name. The actual loading is done in loadLibrary().
+ ///
+ /// @param name Name of the library to load. This should be an absolute
+ /// path name.
+ /// @param index Index of this library. For all these tests, it will be
+ /// zero, as we are only using one library.
+ /// @param manager CalloutManager object
+ PublicLibraryManager(const std::string& name, int index,
+ const boost::shared_ptr<CalloutManager>& manager)
+ : LibraryManager(name, index, manager)
+ {}
+
+ /// Public methods that call protected methods on the superclass.
+ using LibraryManager::openLibrary;
+ using LibraryManager::closeLibrary;
+ using LibraryManager::checkVersion;
+ using LibraryManager::registerStandardCallouts;
+ using LibraryManager::runLoad;
+ using LibraryManager::runUnload;
+};
+
+
+// Check that openLibrary() reports an error when it can't find the specified
+// library.
+
+TEST_F(LibraryManagerTest, NoLibrary) {
+ PublicLibraryManager lib_manager(std::string(NOT_PRESENT_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_FALSE(lib_manager.openLibrary());
+}
+
+// Check that the openLibrary() and closeLibrary() methods work.
+
+TEST_F(LibraryManagerTest, OpenClose) {
+ PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+
+ // Open and close the library
+ EXPECT_TRUE(lib_manager.openLibrary());
+ EXPECT_TRUE(lib_manager.closeLibrary());
+
+ // Check that a second close on an already closed library does not report
+ // an error.
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check that the code handles the case of a library with no version function.
+
+TEST_F(LibraryManagerTest, NoVersion) {
+ PublicLibraryManager lib_manager(std::string(NO_VERSION_LIBRARY),
+ 0, callout_manager_);
+ // Open should succeed.
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Version check should fail.
+ EXPECT_FALSE(lib_manager.checkVersion());
+
+ // Tidy up.
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check that the code handles the case of a library with a version function
+// that returns an incorrect version number.
+
+TEST_F(LibraryManagerTest, WrongVersion) {
+ PublicLibraryManager lib_manager(std::string(INCORRECT_VERSION_LIBRARY),
+ 0, callout_manager_);
+ // Open should succeed.
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Version check should fail.
+ EXPECT_FALSE(lib_manager.checkVersion());
+
+ // Tidy up.
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check that the code handles the case of a library where the version function
+// throws an exception.
+
+TEST_F(LibraryManagerTest, VersionException) {
+ PublicLibraryManager lib_manager(std::string(FRAMEWORK_EXCEPTION_LIBRARY),
+ 0, callout_manager_);
+ // Open should succeed.
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Version check should fail.
+ EXPECT_FALSE(lib_manager.checkVersion());
+
+ // Tidy up.
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Tests that checkVersion() function succeeds in the case of a library with a
+// version function that returns the correct version number.
+
+TEST_F(LibraryManagerTest, CorrectVersionReturned) {
+ PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+ // Open should succeed.
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Version check should succeed.
+ EXPECT_TRUE(lib_manager.checkVersion());
+
+ // Tidy up.
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Checks the registration of standard callouts.
+
+TEST_F(LibraryManagerTest, RegisterStandardCallouts) {
+
+ // Load the only library, specifying the index of 0 as it's the only
+ // library. This should load all callouts.
+ PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Check the version of the library.
+ EXPECT_TRUE(lib_manager.checkVersion());
+
+ // Load the standard callouts
+ EXPECT_NO_THROW(lib_manager.registerStandardCallouts());
+
+ // Now execute the callouts in the order expected. The library performs
+ // the calculation:
+ //
+ // r3 = (10 + d1) * d2 - d3
+ executeCallCallouts(10, 5, 15, 7, 105, 17, 88);
+
+ // Tidy up
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Test that the "load" function is called correctly.
+
+TEST_F(LibraryManagerTest, CheckLoadCalled) {
+
+ // Load the only library, specifying the index of 0 as it's the only
+ // library. This should load all callouts.
+ PublicLibraryManager lib_manager(std::string(LOAD_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Check the version of the library.
+ EXPECT_TRUE(lib_manager.checkVersion());
+
+ // Load the standard callouts
+ EXPECT_NO_THROW(lib_manager.registerStandardCallouts());
+
+ // Check that only context_create and hookpt_one have callouts registered.
+ EXPECT_TRUE(callout_manager_->calloutsPresent(
+ ServerHooks::CONTEXT_CREATE));
+ EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_one_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(
+ ServerHooks::CONTEXT_DESTROY));
+
+ // Call the runLoad() method to run the load() function.
+ EXPECT_TRUE(lib_manager.runLoad());
+ EXPECT_TRUE(callout_manager_->calloutsPresent(
+ ServerHooks::CONTEXT_CREATE));
+ EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_one_index_));
+ EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_two_index_));
+ EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_three_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(
+ ServerHooks::CONTEXT_DESTROY));
+
+ // Now execute the callouts in the order expected. The library performs
+ // the calculation:
+ //
+ // r3 = (5 * d1 + d2) * d3
+ executeCallCallouts(5, 5, 25, 7, 32, 10, 320);
+
+ // Tidy up
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check handling of a "load" function that throws an exception
+
+TEST_F(LibraryManagerTest, CheckLoadException) {
+
+ // Load the only library, specifying the index of 0 as it's the only
+ // library. This should load all callouts.
+ PublicLibraryManager lib_manager(std::string(FRAMEWORK_EXCEPTION_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Running the load function should fail.
+ EXPECT_FALSE(lib_manager.runLoad());
+
+ // Tidy up
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check handling of a "load" function that returns an error.
+
+TEST_F(LibraryManagerTest, CheckLoadError) {
+
+ // Load the only library, specifying the index of 0 as it's the only
+ // library. This should load all callouts.
+ PublicLibraryManager lib_manager(std::string(LOAD_ERROR_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Check that we catch a load error
+ EXPECT_FALSE(lib_manager.runLoad());
+
+ // Tidy up
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// No unload function
+
+TEST_F(LibraryManagerTest, CheckNoUnload) {
+
+ // Load the only library, specifying the index of 0 as it's the only
+ // library. This should load all callouts.
+ PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Check that no unload function returns true.
+ EXPECT_TRUE(lib_manager.runUnload());
+
+ // Tidy up
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Unload function returns an error
+
+TEST_F(LibraryManagerTest, CheckUnloadError) {
+
+ // Load the only library, specifying the index of 0 as it's the only
+ // library. This should load all callouts.
+ PublicLibraryManager lib_manager(std::string(LOAD_ERROR_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Check that unload function returning an error returns false.
+ EXPECT_FALSE(lib_manager.runUnload());
+
+ // Tidy up
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Unload function throws an exception.
+
+TEST_F(LibraryManagerTest, CheckUnloadException) {
+
+ // Load the only library, specifying the index of 0 as it's the only
+ // library. This should load all callouts.
+ PublicLibraryManager lib_manager(std::string(FRAMEWORK_EXCEPTION_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Check that we detect that the unload function throws an exception.
+ EXPECT_FALSE(lib_manager.runUnload());
+
+ // Tidy up
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check that the case of the library's unload() function returning a
+// success is handled correcty.
+
+TEST_F(LibraryManagerTest, CheckUnload) {
+
+ // Load the only library, specifying the index of 0 as it's the only
+ // library. This should load all callouts.
+ PublicLibraryManager lib_manager(std::string(UNLOAD_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+
+ // Check that the marker file is not present (at least that the file
+ // open fails).
+ EXPECT_FALSE(markerFilePresent());
+
+ // Check that unload function runs and returns a success
+ EXPECT_TRUE(lib_manager.runUnload());
+
+ // Check that the marker file was created.
+ EXPECT_TRUE(markerFilePresent());
+
+ // Tidy up
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Test the operation of unloadLibrary(). We load a library with a set
+// of callouts then unload it. We need to check that the callouts have been
+// removed. We'll also check that the library's unload() function was called
+// as well.
+
+TEST_F(LibraryManagerTest, LibUnload) {
+
+ // Load the only library, specifying the index of 0 as it's the only
+ // library. This should load all callouts.
+ PublicLibraryManager lib_manager(std::string(FULL_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Check the version of the library.
+ EXPECT_TRUE(lib_manager.checkVersion());
+
+ // No callouts should be registered at the moment.
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_one_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+
+ // Load the single standard callout and check it is registered correctly.
+ EXPECT_NO_THROW(lib_manager.registerStandardCallouts());
+ EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_one_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+
+ // Call the load function to load the other callouts.
+ EXPECT_TRUE(lib_manager.runLoad());
+ EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_one_index_));
+ EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_two_index_));
+ EXPECT_TRUE(callout_manager_->calloutsPresent(hookpt_three_index_));
+
+ // Unload the library and check that the callouts have been removed from
+ // the CalloutManager.
+ lib_manager.unloadLibrary();
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_one_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+}
+
+// Now come the loadLibrary() tests that make use of all the methods tested
+// above. These tests are really to make sure that the methods have been
+// tied together correctly.
+
+// First test the basic error cases - no library, no version function, version
+// function returning an error.
+
+TEST_F(LibraryManagerTest, LoadLibraryNoLibrary) {
+ LibraryManager lib_manager(std::string(NOT_PRESENT_LIBRARY), 0,
+ callout_manager_);
+ EXPECT_FALSE(lib_manager.loadLibrary());
+}
+
+// Check that the code handles the case of a library with no version function.
+
+TEST_F(LibraryManagerTest, LoadLibraryNoVersion) {
+ LibraryManager lib_manager(std::string(NO_VERSION_LIBRARY), 0,
+ callout_manager_);
+ EXPECT_FALSE(lib_manager.loadLibrary());
+}
+
+// Check that the code handles the case of a library with a version function
+// that returns an incorrect version number.
+
+TEST_F(LibraryManagerTest, LoadLibraryWrongVersion) {
+ LibraryManager lib_manager(std::string(INCORRECT_VERSION_LIBRARY), 0,
+ callout_manager_);
+ EXPECT_FALSE(lib_manager.loadLibrary());
+}
+
+// Check that the full loadLibrary call works.
+
+TEST_F(LibraryManagerTest, LoadLibrary) {
+ LibraryManager lib_manager(std::string(FULL_CALLOUT_LIBRARY), 0,
+ callout_manager_);
+ EXPECT_TRUE(lib_manager.loadLibrary());
+
+ // Now execute the callouts in the order expected. The library performs
+ // the calculation:
+ //
+ // r3 = (7 * d1 - d2) * d3
+ executeCallCallouts(7, 5, 35, 9, 26, 3, 78);
+
+ EXPECT_TRUE(lib_manager.unloadLibrary());
+
+ // Check that the callouts have been removed from the callout manager.
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_one_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_two_index_));
+ EXPECT_FALSE(callout_manager_->calloutsPresent(hookpt_three_index_));
+}
+
+// Now test for multiple libraries. We'll load the full callout library
+// first, then load some of the libraries with missing framework functions.
+// This will check that when searching for framework functions, only the
+// specified library is checked, not other loaded libraries. We will
+// load a second library with suitable callouts and check that the callouts
+// are added correctly. Finally, we'll unload one of the libraries and
+// check that only the callouts belonging to that library were removed.
+
+TEST_F(LibraryManagerTest, LoadMultipleLibraries) {
+ // Load a library with all framework functions.
+ LibraryManager lib_manager_1(std::string(FULL_CALLOUT_LIBRARY), 0,
+ callout_manager_);
+ EXPECT_TRUE(lib_manager_1.loadLibrary());
+
+ // Attempt to load a library with no version() function. We should detect
+ // this and not end up calling the function from the already loaded
+ // library.
+ LibraryManager lib_manager_2(std::string(NO_VERSION_LIBRARY), 1,
+ callout_manager_);
+ EXPECT_FALSE(lib_manager_2.loadLibrary());
+
+ // Attempt to load the library with an incorrect version. This should
+ // be detected.
+ LibraryManager lib_manager_3(std::string(INCORRECT_VERSION_LIBRARY), 1,
+ callout_manager_);
+ EXPECT_FALSE(lib_manager_3.loadLibrary());
+
+ // Load the basic callout library. This only has standard callouts so,
+ // if the first library's load() function gets called, some callouts
+ // will be registered twice and lead to incorrect results.
+ LibraryManager lib_manager_4(std::string(BASIC_CALLOUT_LIBRARY), 1,
+ callout_manager_);
+ EXPECT_TRUE(lib_manager_4.loadLibrary());
+
+ // Execute the callouts. The first library implements the calculation.
+ //
+ // r3 = (7 * d1 - d2) * d3
+ //
+ // The last-loaded library implements the calculation
+ //
+ // r3 = (10 + d1) * d2 - d3
+ //
+ // Putting the processing for each library together in the appropriate
+ // order, we get:
+ //
+ // r3 = ((10 * d1 + d1) - d2) * d2 * d3 - d3
+ executeCallCallouts(10, 3, 33, 2, 62, 3, 183);
+
+ // All done, so unload the first library.
+ EXPECT_TRUE(lib_manager_1.unloadLibrary());
+
+ // Now execute the callouts again and check that the results are as
+ // expected for the new calculation.
+ executeCallCallouts(10, 5, 15, 7, 105, 17, 88);
+
+ // ... and tidy up.
+ EXPECT_TRUE(lib_manager_4.unloadLibrary());
+}
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/load_callout_library.cc b/src/lib/hooks/tests/load_callout_library.cc
new file mode 100644
index 0000000..ae9f470
--- /dev/null
+++ b/src/lib/hooks/tests/load_callout_library.cc
@@ -0,0 +1,119 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief Basic library with load() function
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager). The characteristics of the library produced from this
+/// file are:
+///
+/// - The "version" and "load" framework functions are supplied. One "standard"
+/// callout is supplied ("hookpt_one") and two non-standard ones which are
+/// registered during the call to "load" on the hooks "hookpt_two" and
+/// "hookpt_three".
+///
+/// All callouts do trivial calculations, the result of all being called in
+/// sequence being
+///
+/// @f[ ((5 * data_1) + data_2) * data_3 @f]
+///
+/// ...where data_1, data_2 and data_3 are the values passed in arguments of
+/// the same name to the three callouts (data_1 passed to hookpt_one, data_2 to
+/// hookpt_two etc.) and the result is returned in the argument "result".
+
+#include <hooks/hooks.h>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Callouts
+
+int
+context_create(CalloutHandle& handle) {
+ handle.setContext("result", static_cast<int>(5));
+ handle.setArgument("result", static_cast<int>(5));
+ return (0);
+}
+
+// First callout adds the passed "data_1" argument to the initialized context
+// value of 5. (Note that the value set by context_create is accessed through
+// context and not the argument, so checking that context is correctly passed
+// between callouts in the same library.)
+
+int
+hookpt_one(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_1", data);
+
+ int result;
+ handle.getContext("result", result);
+
+ result *= data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Second callout multiplies the current context value by the "data_2"
+// argument.
+
+static int
+hook_nonstandard_two(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_2", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result += data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Final callout adds "data_3" to the result.
+
+static int
+hook_nonstandard_three(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_3", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result *= data;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Framework functions
+
+int
+version() {
+ return (BIND10_HOOKS_VERSION);
+}
+
+int load(LibraryHandle& handle) {
+ // Register the non-standard functions
+ handle.registerCallout("hookpt_two", hook_nonstandard_two);
+ handle.registerCallout("hookpt_three", hook_nonstandard_three);
+
+ return (0);
+}
+
+};
+
diff --git a/src/lib/hooks/tests/load_error_callout_library.cc b/src/lib/hooks/tests/load_error_callout_library.cc
new file mode 100644
index 0000000..b861d7f
--- /dev/null
+++ b/src/lib/hooks/tests/load_error_callout_library.cc
@@ -0,0 +1,49 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief Error load library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager). The characteristics of the library produced from this
+/// file are:
+///
+/// - All framework functions are supplied. "version" returns the correct
+/// value, but "load" and unload return an error.
+
+#include <hooks/hooks.h>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Framework functions
+
+int
+version() {
+ return (BIND10_HOOKS_VERSION);
+}
+
+int
+load(LibraryHandle&) {
+ return (1);
+}
+
+int
+unload() {
+ return (1);
+}
+
+};
+
diff --git a/src/lib/hooks/tests/marker_file.h.in b/src/lib/hooks/tests/marker_file.h.in
new file mode 100644
index 0000000..e032cdd
--- /dev/null
+++ b/src/lib/hooks/tests/marker_file.h.in
@@ -0,0 +1,27 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef MARKER_FILE_H
+#define MARKER_FILE_H
+
+/// @file
+/// Define a marker file that is used in tests to prove that an "unload"
+/// function has been called.
+
+namespace {
+const char* MARKER_FILE = "@abs_builddir@/marker_file.dat";
+}
+
+#endif // MARKER_FILE_H
+
diff --git a/src/lib/hooks/tests/no_version_library.cc b/src/lib/hooks/tests/no_version_library.cc
new file mode 100644
index 0000000..f5b5b9c
--- /dev/null
+++ b/src/lib/hooks/tests/no_version_library.cc
@@ -0,0 +1,30 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief No version function library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager). The characteristics of the library produced from this
+/// file are:
+///
+/// - No version() function is present.
+
+extern "C" {
+
+int no_version() {
+ return (0);
+}
+
+};
diff --git a/src/lib/hooks/tests/run_unittests.cc b/src/lib/hooks/tests/run_unittests.cc
new file mode 100644
index 0000000..f68a58d
--- /dev/null
+++ b/src/lib/hooks/tests/run_unittests.cc
@@ -0,0 +1,25 @@
+// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <log/logger_support.h>
+#include <util/unittests/run_all.h>
+
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
+ return (isc::util::unittests::run_all());
+}
diff --git a/src/lib/hooks/tests/server_hooks_unittest.cc b/src/lib/hooks/tests/server_hooks_unittest.cc
new file mode 100644
index 0000000..ca9b6f0
--- /dev/null
+++ b/src/lib/hooks/tests/server_hooks_unittest.cc
@@ -0,0 +1,178 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <hooks/server_hooks.h>
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace std;
+
+namespace {
+
+// Checks the registration of hooks and the interrogation methods. As the
+// constructor registers two hooks, this is also a test of the constructor.
+
+TEST(ServerHooksTest, RegisterHooks) {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+
+ // There should be two hooks already registered, with indexes 0 and 1.
+ EXPECT_EQ(2, hooks.getCount());
+ EXPECT_EQ(0, hooks.getIndex("context_create"));
+ EXPECT_EQ(1, hooks.getIndex("context_destroy"));
+
+ // Check that the constants are as expected. (The intermediate variables
+ // are used because of problems with g++ 4.6.1/Ubuntu 11.10 when resolving
+ // the value of the ServerHooks constants when they appeared within the
+ // gtest macro.)
+ const int create_value = ServerHooks::CONTEXT_CREATE;
+ const int destroy_value = ServerHooks::CONTEXT_DESTROY;
+ EXPECT_EQ(0, create_value);
+ EXPECT_EQ(1, destroy_value);
+
+ // Register another couple of hooks. The test on returned index is based
+ // on knowledge that the hook indexes are assigned in ascending order.
+ int alpha = hooks.registerHook("alpha");
+ EXPECT_EQ(2, alpha);
+ EXPECT_EQ(2, hooks.getIndex("alpha"));
+
+ int beta = hooks.registerHook("beta");
+ EXPECT_EQ(3, beta);
+ EXPECT_EQ(3, hooks.getIndex("beta"));
+
+ // Should be four hooks now
+ EXPECT_EQ(4, hooks.getCount());
+}
+
+// Check that duplicate names cannot be registered.
+
+TEST(ServerHooksTest, DuplicateHooks) {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+
+ // Ensure we can't duplicate one of the existing names.
+ EXPECT_THROW(hooks.registerHook("context_create"), DuplicateHook);
+
+ // Check we can't duplicate a newly registered hook.
+ int gamma = hooks.registerHook("gamma");
+ EXPECT_EQ(2, gamma);
+ EXPECT_THROW(hooks.registerHook("gamma"), DuplicateHook);
+}
+
+// Checks that we can get the name of the hooks.
+
+TEST(ServerHooksTest, GetHookNames) {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+ vector<string> expected_names;
+
+ // Add names into the hooks object and to the set of expected names.
+ expected_names.push_back("alpha");
+ expected_names.push_back("beta");
+ expected_names.push_back("gamma");
+ expected_names.push_back("delta");
+ for (int i = 0; i < expected_names.size(); ++i) {
+ hooks.registerHook(expected_names[i].c_str());
+ };
+
+ // Update the expected names to include the pre-defined hook names.
+ expected_names.push_back("context_create");
+ expected_names.push_back("context_destroy");
+
+ // Get the actual hook names
+ vector<string> actual_names = hooks.getHookNames();
+
+ // For comparison, sort the names into alphabetical order and do a straight
+ // vector comparison.
+ sort(expected_names.begin(), expected_names.end());
+ sort(actual_names.begin(), actual_names.end());
+
+ EXPECT_TRUE(expected_names == actual_names);
+}
+
+// Test the inverse hooks functionality (i.e. given an index, get the name).
+
+TEST(ServerHooksTest, GetHookIndexes) {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+
+ int alpha = hooks.registerHook("alpha");
+ int beta = hooks.registerHook("beta");
+ int gamma = hooks.registerHook("gamma");
+
+ EXPECT_EQ(std::string("context_create"),
+ hooks.getName(ServerHooks::CONTEXT_CREATE));
+ EXPECT_EQ(std::string("context_destroy"),
+ hooks.getName(ServerHooks::CONTEXT_DESTROY));
+ EXPECT_EQ(std::string("alpha"), hooks.getName(alpha));
+ EXPECT_EQ(std::string("beta"), hooks.getName(beta));
+ EXPECT_EQ(std::string("gamma"), hooks.getName(gamma));
+
+ // Check for an invalid index
+ EXPECT_THROW(hooks.getName(-1), NoSuchHook);
+ EXPECT_THROW(hooks.getName(42), NoSuchHook);
+}
+
+// Test the reset functionality.
+
+TEST(ServerHooksTest, Reset) {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+
+ int alpha = hooks.registerHook("alpha");
+ int beta = hooks.registerHook("beta");
+ int gamma = hooks.registerHook("gamma");
+
+ // Check the counts before and after a reset.
+ EXPECT_EQ(5, hooks.getCount());
+ hooks.reset();
+ EXPECT_EQ(2, hooks.getCount());
+
+ // ... and check that the hooks are as expected.
+ EXPECT_EQ(0, hooks.getIndex("context_create"));
+ EXPECT_EQ(1, hooks.getIndex("context_destroy"));
+}
+
+// Check that getting an unknown name throws an exception.
+
+TEST(ServerHooksTest, UnknownHookName) {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+
+ EXPECT_THROW(static_cast<void>(hooks.getIndex("unknown")), NoSuchHook);
+}
+
+// Check that the count of hooks is correct.
+
+TEST(ServerHooksTest, HookCount) {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+
+ // Insert the names into the hooks object
+ hooks.registerHook("alpha");
+ hooks.registerHook("beta");
+ hooks.registerHook("gamma");
+ hooks.registerHook("delta");
+
+ // Should be two more hooks that the number we have registered.
+ EXPECT_EQ(6, hooks.getCount());
+}
+
+} // Anonymous namespace
diff --git a/src/lib/hooks/tests/test_libraries.h.in b/src/lib/hooks/tests/test_libraries.h.in
new file mode 100644
index 0000000..bb6a24a
--- /dev/null
+++ b/src/lib/hooks/tests/test_libraries.h.in
@@ -0,0 +1,79 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef TEST_LIBRARIES_H
+#define TEST_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+
+// Take care of differences in DLL naming between operating systems.
+
+#ifdef OS_OSX
+#define DLL_SUFFIX ".dylib"
+
+#else
+#define DLL_SUFFIX ".so"
+
+#endif
+
+
+// Names of the libraries used in these tests. These libraries are built using
+// libtool, so we need to look in the hidden ".libs" directory to locate the
+// .so file. Note that we access the .so file - libtool creates this as a
+// like to the real shared library.
+
+// Basic library with context_create and three "standard" callouts.
+static const char* BASIC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libbcl"
+ DLL_SUFFIX;
+
+// Library with context_create and three "standard" callouts, as well as
+// load() and unload() functions.
+static const char* FULL_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libfcl"
+ DLL_SUFFIX;
+
+// Library where the all framework functions throw an exception
+static const char* FRAMEWORK_EXCEPTION_LIBRARY = "@abs_builddir@/.libs/libfxl"
+ DLL_SUFFIX;
+
+// Library where the version() function returns an incorrect result.
+static const char* INCORRECT_VERSION_LIBRARY = "@abs_builddir@/.libs/libivl"
+ DLL_SUFFIX;
+
+// Library where some of the callout registration is done with the load()
+// function.
+static const char* LOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/liblcl"
+ DLL_SUFFIX;
+
+// Library where the load() function returns an error.
+static const char* LOAD_ERROR_CALLOUT_LIBRARY =
+ "@abs_builddir@/.libs/liblecl" DLL_SUFFIX;
+
+// Name of a library which is not present.
+static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere"
+ DLL_SUFFIX;
+
+// Library that does not include a version function.
+static const char* NO_VERSION_LIBRARY = "@abs_builddir@/.libs/libnvl"
+ DLL_SUFFIX;
+
+// Library where there is an unload() function.
+static const char* UNLOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libucl"
+ DLL_SUFFIX;
+} // anonymous namespace
+
+
+#endif // TEST_LIBRARIES_H
diff --git a/src/lib/hooks/tests/unload_callout_library.cc b/src/lib/hooks/tests/unload_callout_library.cc
new file mode 100644
index 0000000..9baa830
--- /dev/null
+++ b/src/lib/hooks/tests/unload_callout_library.cc
@@ -0,0 +1,52 @@
+// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file
+/// @brief Basic unload library
+///
+/// This is source of a test library for various test (LibraryManager and
+/// HooksManager). The characteristics of the library produced from this
+/// file are:
+///
+/// - The "version" and "unload" framework functions are supplied. "version"
+/// returns a valid value and "unload" creates a marker file and returns
+/// success.
+
+#include <hooks/hooks.h>
+#include <hooks/tests/marker_file.h>
+
+#include <fstream>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Framework functions
+
+int
+version() {
+ return (BIND10_HOOKS_VERSION);
+}
+
+int
+unload() {
+ // Create the marker file.
+ std::fstream marker;
+ marker.open(MARKER_FILE, std::fstream::out);
+ marker.close();
+
+ return (0);
+}
+
+};
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
index 6d07ac8..ff5ef40 100644
--- a/src/lib/util/Makefile.am
+++ b/src/lib/util/Makefile.am
@@ -37,10 +37,6 @@ libb10_util_la_SOURCES += encode/base32hex_from_binary.h
libb10_util_la_SOURCES += encode/base_n.cc encode/hex.h
libb10_util_la_SOURCES += encode/binary_from_base32hex.h
libb10_util_la_SOURCES += encode/binary_from_base16.h
-libb10_util_la_SOURCES += hooks/callout_manager.h hooks/callout_manager.cc
-libb10_util_la_SOURCES += hooks/callout_handle.h hooks/callout_handle.cc
-libb10_util_la_SOURCES += hooks/library_handle.h hooks/library_handle.cc
-libb10_util_la_SOURCES += hooks/server_hooks.h hooks/server_hooks.cc
libb10_util_la_SOURCES += random/qid_gen.h random/qid_gen.cc
libb10_util_la_SOURCES += random/random_number_generator.h
diff --git a/src/lib/util/hooks/callout_handle.cc b/src/lib/util/hooks/callout_handle.cc
deleted file mode 100644
index 8052823..0000000
--- a/src/lib/util/hooks/callout_handle.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <string>
-#include <utility>
-#include <vector>
-
-using namespace std;
-using namespace isc::util;
-
-namespace isc {
-namespace util {
-
-// Constructor.
-CalloutHandle::CalloutHandle(const boost::shared_ptr<CalloutManager>& manager)
- : arguments_(), context_collection_(), manager_(manager), skip_(false) {
-
- // Call the "context_create" hook. We should be OK doing this - although
- // the constructor has not finished running, all the member variables
- // have been created.
- manager_->callCallouts(ServerHooks::CONTEXT_CREATE, *this);
-}
-
-// Destructor
-CalloutHandle::~CalloutHandle() {
-
- // Call the "context_destroy" hook. We should be OK doing this - although
- // the destructor is being called, all the member variables are still in
- // existence.
- manager_->callCallouts(ServerHooks::CONTEXT_DESTROY, *this);
-}
-
-// Return the name of all argument items.
-
-vector<string>
-CalloutHandle::getArgumentNames() const {
-
- vector<string> names;
- for (ElementCollection::const_iterator i = arguments_.begin();
- i != arguments_.end(); ++i) {
- names.push_back(i->first);
- }
-
- return (names);
-}
-
-// Return the library handle allowing the callout to access the CalloutManager
-// registration/deregistration functions.
-
-LibraryHandle&
-CalloutHandle::getLibraryHandle() const {
- return (manager_->getLibraryHandle());
-}
-
-// Return the context for the currently pointed-to library. This version is
-// used by the "setContext()" method and creates a context for the current
-// library if it does not exist.
-
-CalloutHandle::ElementCollection&
-CalloutHandle::getContextForLibrary() {
- int libindex = manager_->getLibraryIndex();
-
- // Access a reference to the element collection for the given index,
- // creating a new element collection if necessary, and return it.
- return (context_collection_[libindex]);
-}
-
-// The "const" version of the above, used by the "getContext()" method. If
-// the context for the current library doesn't exist, throw an exception.
-
-const CalloutHandle::ElementCollection&
-CalloutHandle::getContextForLibrary() const {
- int libindex = manager_->getLibraryIndex();
-
- ContextCollection::const_iterator libcontext =
- context_collection_.find(libindex);
- if (libcontext == context_collection_.end()) {
- isc_throw(NoSuchCalloutContext, "unable to find callout context "
- "associated with the current library index (" << libindex <<
- ")");
- }
-
- // Return a reference to the context's element collection.
- return (libcontext->second);
-}
-
-// Return the name of all items in the context associated with the current]
-// library.
-
-vector<string>
-CalloutHandle::getContextNames() const {
-
- vector<string> names;
-
- const ElementCollection& elements = getContextForLibrary();
- for (ElementCollection::const_iterator i = elements.begin();
- i != elements.end(); ++i) {
- names.push_back(i->first);
- }
-
- return (names);
-}
-
-} // namespace util
-} // namespace isc
diff --git a/src/lib/util/hooks/callout_handle.h b/src/lib/util/hooks/callout_handle.h
deleted file mode 100644
index 0033505..0000000
--- a/src/lib/util/hooks/callout_handle.h
+++ /dev/null
@@ -1,353 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef CALLOUT_HANDLE_H
-#define CALLOUT_HANDLE_H
-
-#include <exceptions/exceptions.h>
-#include <util/hooks/library_handle.h>
-
-#include <boost/any.hpp>
-#include <boost/shared_ptr.hpp>
-
-#include <map>
-#include <string>
-#include <vector>
-
-namespace isc {
-namespace util {
-
-/// @brief No such argument
-///
-/// Thrown if an attempt is made access an argument that does not exist.
-
-class NoSuchArgument : public Exception {
-public:
- NoSuchArgument(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-/// @brief No such callout context item
-///
-/// Thrown if an attempt is made to get an item of data from this callout's
-/// context and either the context or an item in the context with that name
-/// does not exist.
-
-class NoSuchCalloutContext : public Exception {
-public:
- NoSuchCalloutContext(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-// Forward declaration of the library handle and related collection classes.
-
-class CalloutManager;
-class LibraryHandle;
-
-/// @brief Per-packet callout handle
-///
-/// An object of this class is associated with every packet (or request)
-/// processed by the server. It forms the principle means of passing data
-/// between the server and the user-library callouts.
-///
-/// The class allows access to the following information:
-///
-/// - Arguments. When the callouts associated with a hook are called, they
-/// are passed information by the server (and can return information to it)
-/// through name/value pairs. Each of these pairs is an argument and the
-/// information is accessed through the {get,set}Argument() methods.
-///
-/// - Per-packet context. Each packet has a context associated with it, this
-/// context being on a per-library basis. In other words, As a packet passes
-/// through the callouts associated with a given library, the callouts can
-/// associate and retrieve information with the packet. The per-library
-/// nature of the context means that the callouts within a given library can
-/// pass packet-specific information between one another, but they cannot pass
-/// information to callous within another library. Typically such context
-/// is created in the "context_create" callout and destroyed in the
-/// "context_destroy" callout. The information is accessed through the
-/// {get,set}Context() methods.
-///
-/// - Per-library handle. Allows the callout to dynamically register and
-/// deregister callouts. (In the latter case, only functions registered by
-/// functions in the same library as the callout doing the deregistration
-/// can be removed: callouts registered by other libraries cannot be
-/// modified.)
-
-class CalloutHandle {
-public:
-
- /// Typedef to allow abbreviation of iterator specification in methods.
- /// The std::string is the argument name and the "boost::any" is the
- /// corresponding value associated with it.
- typedef std::map<std::string, boost::any> ElementCollection;
-
- /// Typedef to allow abbreviations in specifications when accessing
- /// context. The ElementCollection is the name/value collection for
- /// a particular context. The "int" corresponds to the index of an
- /// associated library - there is a 1:1 correspondence between libraries
- /// and a name.value collection.
- ///
- /// The collection of contexts is stored in a map, as not every library
- /// will require creation of a context associated with each packet. In
- /// addition, the structure is more flexible in that the size does not
- /// need to be set when the CalloutHandle is constructed.
- typedef std::map<int, ElementCollection> ContextCollection;
-
-
- /// @brief Constructor
- ///
- /// Creates the object and calls the callouts on the "context_create"
- /// hook.
- ///
- /// @param manager Pointer to the callout manager object.
- CalloutHandle(const boost::shared_ptr<CalloutManager>& manager);
-
- /// @brief Destructor
- ///
- /// Calls the context_destroy callback to release any per-packet context.
- ~CalloutHandle();
-
- /// @brief Set argument
- ///
- /// Sets the value of an argument. The argument is created if it does not
- /// already exist.
- ///
- /// @param name Name of the argument.
- /// @param value Value to set. That can be of any data type.
- template <typename T>
- void setArgument(const std::string& name, T value) {
- arguments_[name] = value;
- }
-
- /// @brief Get argument
- ///
- /// Gets the value of an argument.
- ///
- /// @param name Name of the element in the argument list to get.
- /// @param value [out] Value to set. The type of "value" is important:
- /// it must match the type of the value set.
- ///
- /// @throw NoSuchArgument No argument with the given name is present.
- /// @throw boost::bad_any_cast An argument with the given name is present,
- /// but the data type of the value is not the same as the type of
- /// the variable provided to receive the value.
- template <typename T>
- void getArgument(const std::string& name, T& value) const {
- ElementCollection::const_iterator element_ptr = arguments_.find(name);
- if (element_ptr == arguments_.end()) {
- isc_throw(NoSuchArgument, "unable to find argument with name " <<
- name);
- }
-
- value = boost::any_cast<T>(element_ptr->second);
- }
-
- /// @brief Get argument names
- ///
- /// Returns a vector holding the names of arguments in the argument
- /// vector.
- ///
- /// @return Vector of strings reflecting argument names.
- std::vector<std::string> getArgumentNames() const;
-
- /// @brief Delete argument
- ///
- /// Deletes an argument of the given name. If an argument of that name
- /// does not exist, the method is a no-op.
- ///
- /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
- /// by this method.
- ///
- /// @param name Name of the element in the argument list to set.
- void deleteArgument(const std::string& name) {
- static_cast<void>(arguments_.erase(name));
- }
-
- /// @brief Delete all arguments
- ///
- /// Deletes all arguments associated with this context.
- ///
- /// N.B. If any elements are raw pointers, the pointed-to data is NOT
- /// deleted by this method.
- void deleteAllArguments() {
- arguments_.clear();
- }
-
- /// @brief Set skip flag
- ///
- /// Sets the "skip" variable in the callout handle. This variable is
- /// interrogated by the server to see if the remaining callouts associated
- /// with the current hook should be bypassed.
- ///
- /// @param skip New value of the "skip" flag.
- void setSkip(bool skip) {
- skip_ = skip;
- }
-
- /// @brief Get skip flag
- ///
- /// Gets the current value of the "skip" flag.
- ///
- /// @return Current value of the skip flag.
- bool getSkip() const {
- return (skip_);
- }
-
- /// @brief Access current library handle
- ///
- /// Returns a reference to the current library handle. This function is
- /// only available when called by a callout (which in turn is called
- /// through the "callCallouts" method), as it is only then that the current
- /// library index is valid. A callout uses the library handle to
- /// dynamically register or deregister callouts.
- ///
- /// @return Reference to the library handle.
- ///
- /// @throw InvalidIndex thrown if this method is called when the current
- /// library index is invalid (typically if it is called outside of
- /// the active callout).
- LibraryHandle& getLibraryHandle() const;
-
- /// @brief Set context
- ///
- /// Sets an element in the context associated with the current library. If
- /// an element of the name is already present, it is replaced.
- ///
- /// @param name Name of the element in the context to set.
- /// @param value Value to set.
- template <typename T>
- void setContext(const std::string& name, T value) {
- getContextForLibrary()[name] = value;
- }
-
- /// @brief Get context
- ///
- /// Gets an element from the context associated with the current library.
- ///
- /// @param name Name of the element in the context to get.
- /// @param value [out] Value to set. The type of "value" is important:
- /// it must match the type of the value set.
- ///
- /// @throw NoSuchCalloutContext Thrown if no context element with the name
- /// "name" is present.
- /// @throw boost::bad_any_cast Thrown if the context element is present
- /// but the type of the data is not the same as the type of the
- /// variable provided to receive its value.
- template <typename T>
- void getContext(const std::string& name, T& value) const {
- const ElementCollection& lib_context = getContextForLibrary();
-
- ElementCollection::const_iterator element_ptr = lib_context.find(name);
- if (element_ptr == lib_context.end()) {
- isc_throw(NoSuchCalloutContext, "unable to find callout context "
- "item " << name << " in the context associated with "
- "current library");
- }
-
- value = boost::any_cast<T>(element_ptr->second);
- }
-
- /// @brief Get context names
- ///
- /// Returns a vector holding the names of items in the context associated
- /// with the current library.
- ///
- /// @return Vector of strings reflecting the names of items in the callout
- /// context associated with the current library.
- std::vector<std::string> getContextNames() const;
-
- /// @brief Delete context element
- ///
- /// Deletes an item of the given name from the context associated with the
- /// current library. If an item of that name does not exist, the method is
- /// a no-op.
- ///
- /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
- /// by this.
- ///
- /// @param name Name of the context item to delete.
- void deleteContext(const std::string& name) {
- static_cast<void>(getContextForLibrary().erase(name));
- }
-
- /// @brief Delete all context items
- ///
- /// Deletes all items from the context associated with the current library.
- ///
- /// N.B. If any elements are raw pointers, the pointed-to data is NOT
- /// deleted by this.
- void deleteAllContext() {
- getContextForLibrary().clear();
- }
-
-
-private:
- /// @brief Check index
- ///
- /// Gets the current library index, throwing an exception if it is not set
- /// or is invalid for the current library collection.
- ///
- /// @return Current library index, valid for this library collection.
- ///
- /// @throw InvalidIndex current library index is not valid for the library
- /// handle collection.
- int getLibraryIndex() const;
-
- /// @brief Return reference to context for current library
- ///
- /// Called by all context-setting functions, this returns a reference to
- /// the callout context for the current library, creating a context if it
- /// does not exist.
- ///
- /// @return Reference to the collection of name/value pairs associated
- /// with the current library.
- ///
- /// @throw InvalidIndex current library index is not valid for the library
- /// handle collection.
- ElementCollection& getContextForLibrary();
-
- /// @brief Return reference to context for current library (const version)
- ///
- /// Called by all context-accessing functions, this a reference to the
- /// callout context for the current library. An exception is thrown if
- /// it does not exist.
- ///
- /// @return Reference to the collection of name/value pairs associated
- /// with the current library.
- ///
- /// @throw NoSuchCalloutContext Thrown if there is no ElementCollection
- /// associated with the current library.
- const ElementCollection& getContextForLibrary() const;
-
- // Member variables
-
- /// Collection of arguments passed to the callouts
- ElementCollection arguments_;
-
- /// Context collection - there is one entry per library context.
- ContextCollection context_collection_;
-
- /// Callout manager.
- boost::shared_ptr<CalloutManager> manager_;
-
- /// "Skip" flag, indicating if the caller should bypass remaining callouts.
- bool skip_;
-};
-
-} // namespace util
-} // namespace isc
-
-
-#endif // CALLOUT_HANDLE_H
diff --git a/src/lib/util/hooks/callout_manager.cc b/src/lib/util/hooks/callout_manager.cc
deleted file mode 100644
index af20619..0000000
--- a/src/lib/util/hooks/callout_manager.cc
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-
-#include <boost/static_assert.hpp>
-
-#include <algorithm>
-#include <functional>
-#include <utility>
-
-using namespace std;
-using namespace isc::util;
-
-namespace isc {
-namespace util {
-
-// Register a callout for the current library.
-
-void
-CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
- // Sanity check that the current library index is set to a valid value.
- checkLibraryIndex(current_library_);
-
- // Get the index associated with this hook (validating the name in the
- // process).
- int hook_index = hooks_->getIndex(name);
-
- // Iterate through the callout vector for the hook from start to end,
- // looking for the first entry where the library index is greater than
- // the present index.
- for (CalloutVector::iterator i = hook_vector_[hook_index].begin();
- i != hook_vector_[hook_index].end(); ++i) {
- if (i->first > current_library_) {
- // Found an element whose library index number is greater than the
- // current index, so insert the new element ahead of this one.
- hook_vector_[hook_index].insert(i, make_pair(current_library_,
- callout));
- return;
- }
- }
-
- // Reached the end of the vector, so there is no element in the (possibly
- // empty) set of callouts with a library index greater than the current
- // library index. Inset the callout at the end of the list.
- hook_vector_[hook_index].push_back(make_pair(current_library_, callout));
-}
-
-// Check if callouts are present for a given hook index.
-
-bool
-CalloutManager::calloutsPresent(int hook_index) const {
- // Validate the hook index.
- if ((hook_index < 0) || (hook_index >= hook_vector_.size())) {
- isc_throw(NoSuchHook, "hook index " << hook_index <<
- " is not valid for the list of registered hooks");
- }
-
- // Valid, so are there any callouts associated with that hook?
- return (!hook_vector_[hook_index].empty());
-}
-
-// Call all the callouts for a given hook.
-
-void
-CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
-
- // Only initialize and iterate if there are callouts present. This check
- // also catches the case of an invalid index.
- if (calloutsPresent(hook_index)) {
-
- // Clear the "skip" flag so we don't carry state from a previous call.
- callout_handle.setSkip(false);
-
- // Duplicate the callout vector for this hook and work through that.
- // This step is needed because we allow dynamic registration and
- // deregistration of callouts. If a callout attached to a hook modified
- // the list of callouts on that hook, the underlying CalloutVector would
- // change and potentially affect the iteration through that vector.
- CalloutVector callouts(hook_vector_[hook_index]);
-
- // Call all the callouts.
- for (CalloutVector::const_iterator i = callouts.begin();
- i != callouts.end(); ++i) {
- // In case the callout tries to register or deregister a callout,
- // set the current library index to the index associated with the
- // library that registered the callout being called.
- current_library_ = i->first;
-
- // Call the callout
- // @todo Log the return status if non-zero
- static_cast<void>((*i->second)(callout_handle));
- }
-
- // Reset the current library index to an invalid value to catch any
- // programming errors.
- current_library_ = -1;
- }
-}
-
-// Deregister a callout registered by the current library on a particular hook.
-
-bool
-CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
- // Sanity check that the current library index is set to a valid value.
- checkLibraryIndex(current_library_);
-
- // Get the index associated with this hook (validating the name in the
- // process).
- int hook_index = hooks_->getIndex(name);
-
- /// Construct a CalloutEntry matching the current library and the callout
- /// we want to remove.
- CalloutEntry target(current_library_, callout);
-
- /// To decide if any entries were removed, we'll record the initial size
- /// of the callout vector for the hook, and compare it with the size after
- /// the removal.
- size_t initial_size = hook_vector_[hook_index].size();
-
- // The next bit is standard STL (see "Item 33" in "Effective STL" by
- // Scott Meyers).
- //
- // remove_if reorders the hook vector so that all items not matching
- // the predicate are at the start of the vector and returns a pointer
- // to the next element. (In this case, the predicate is that the item
- // is equal to the value of the passed callout.) The erase() call
- // removes everything from that element to the end of the vector, i.e.
- // all the matching elements.
- hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
- hook_vector_[hook_index].end(),
- bind1st(equal_to<CalloutEntry>(),
- target)),
- hook_vector_[hook_index].end());
-
- // Return an indication of whether anything was removed.
- return (initial_size != hook_vector_[hook_index].size());
-}
-
-// Deregister all callouts on a given hook.
-
-bool
-CalloutManager::deregisterAllCallouts(const std::string& name) {
-
- // Get the index associated with this hook (validating the name in the
- // process).
- int hook_index = hooks_->getIndex(name);
-
- /// Construct a CalloutEntry matching the current library (the callout
- /// pointer is NULL as we are not checking that).
- CalloutEntry target(current_library_, NULL);
-
- /// To decide if any entries were removed, we'll record the initial size
- /// of the callout vector for the hook, and compare it with the size after
- /// the removal.
- size_t initial_size = hook_vector_[hook_index].size();
-
- // Remove all callouts matching this library.
- hook_vector_[hook_index].erase(remove_if(hook_vector_[hook_index].begin(),
- hook_vector_[hook_index].end(),
- bind1st(CalloutLibraryEqual(),
- target)),
- hook_vector_[hook_index].end());
-
- // Return an indication of whether anything was removed.
- return (initial_size != hook_vector_[hook_index].size());
-}
-
-} // namespace util
-} // namespace isc
diff --git a/src/lib/util/hooks/callout_manager.h b/src/lib/util/hooks/callout_manager.h
deleted file mode 100644
index 7a22433..0000000
--- a/src/lib/util/hooks/callout_manager.h
+++ /dev/null
@@ -1,301 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef CALLOUT_MANAGER_H
-#define CALLOUT_MANAGER_H
-
-#include <exceptions/exceptions.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <map>
-#include <string>
-
-namespace isc {
-namespace util {
-
-/// @brief No such library
-///
-/// Thrown if an attempt is made to set the current library index to a value
-/// that is invalid for the number of loaded libraries.
-class NoSuchLibrary : public Exception {
-public:
- NoSuchLibrary(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-
-/// @brief Callout Manager
-///
-/// This class manages the registration, deregistration and execution of the
-/// library callouts.
-///
-/// In operation, the class needs to know two items of data:
-///
-/// - The list of server hooks, which is used in two ways. Firstly, when a
-/// callout registers or deregisters a hook, it does so by name: the
-/// @ref isc::util::ServerHooks object supplies the names of registered
-/// hooks. Secondly, when the callouts associated with a hook are called by
-/// the server, the server supplies the index of the relevant hook: this is
-/// validated by reference to the list of hook.
-///
-/// - The number of loaded libraries. Each callout registered by a user
-/// library is associated with that library, the callout manager storing both
-/// a pointer to the callout and the index of the library in the list of
-/// loaded libraries. Callouts are allowed to dynamically register and
-/// deregister callouts in the same library (including themselves): they
-/// cannot affect callouts registered by another library. When calling a
-/// callout, the callout manager maintains the idea of a "current library
-/// index": if the callout calls one of the callout registration functions
-/// (indirectly via the @ref LibraryHandle object), the registration
-/// functions use the "current library index" in their processing.
-///
-/// These two items of data are supplied when an object of this class is
-/// constructed.
-///
-/// Note that the callout function do not access the library manager: instead,
-/// they use a LibraryHandle object. This contains an internal pointer to
-/// the CalloutManager, but provides a restricted interface. In that way,
-/// callouts are unable to affect callouts supplied by other libraries.
-
-class CalloutManager {
-private:
-
- // Private typedefs
-
- /// Element in the vector of callouts. The elements in the pair are the
- /// index of the library from which this callout was registered, and a#
- /// pointer to the callout itself.
- typedef std::pair<int, CalloutPtr> CalloutEntry;
-
- /// An element in the hook vector. Each element is a vector of callouts
- /// associated with a given hook.
- typedef std::vector<CalloutEntry> CalloutVector;
-
-public:
-
- /// @brief Constructor
- ///
- /// Initializes member variables, in particular sizing the hook vector
- /// (the vector of callout vectors) to the appropriate size.
- ///
- /// @param hooks Collection of known hook names.
- /// @param num_libraries Number of loaded libraries.
- ///
- /// @throw isc::BadValue if the number of libraries is less than or equal
- /// to 0, or if the pointer to the server hooks object is empty.
- CalloutManager(const boost::shared_ptr<ServerHooks>& hooks,
- int num_libraries)
- : current_library_(-1), hooks_(hooks), hook_vector_(),
- library_handle_(this), num_libraries_(num_libraries)
- {
- if (!hooks) {
- isc_throw(isc::BadValue, "must pass a pointer to a valid server "
- "hooks object to the CalloutManager");
- } else if (num_libraries <= 0) {
- isc_throw(isc::BadValue, "number of libraries passed to the "
- "CalloutManager must be >= 0");
- }
-
- // Parameters OK, do operations that depend on them.
- hook_vector_.resize(hooks_->getCount());
- }
-
- /// @brief Register a callout on a hook for the current library
- ///
- /// Registers a callout function for the current library with a given hook
- /// (the index of the "current library" being given by the current_library_
- /// member). The callout is added to the end of the callouts for this
- /// library that are associated with that hook.
- ///
- /// @param name Name of the hook to which the callout is added.
- /// @param callout Pointer to the callout function to be registered.
- ///
- /// @throw NoSuchHook The hook name is unrecognised.
- /// @throw Unexpected The hook name is valid but an internal data structure
- /// is of the wrong size.
- void registerCallout(const std::string& name, CalloutPtr callout);
-
- /// @brief De-Register a callout on a hook for the current library
- ///
- /// Searches through the functions registered by the the current library
- /// (the index of the "current library" being given by the current_library_
- /// member) with the named hook and removes all entries matching the
- /// callout.
- ///
- /// @param name Name of the hook from which the callout is removed.
- /// @param callout Pointer to the callout function to be removed.
- ///
- /// @return true if a one or more callouts were deregistered.
- ///
- /// @throw NoSuchHook The hook name is unrecognised.
- /// @throw Unexpected The hook name is valid but an internal data structure
- /// is of the wrong size.
- bool deregisterCallout(const std::string& name, CalloutPtr callout);
-
- /// @brief Removes all callouts on a hook for the current library
- ///
- /// Removes all callouts associated with a given hook that were registered
- /// by the current library (the index of the "current library" being given
- /// by the current_library_ member).
- ///
- /// @param name Name of the hook from which the callouts are removed.
- ///
- /// @return true if one or more callouts were deregistered.
- ///
- /// @throw NoSuchHook Thrown if the hook name is unrecognised.
- bool deregisterAllCallouts(const std::string& name);
-
- /// @brief Checks if callouts are present on a hook
- ///
- /// Checks all loaded libraries and returns true if at least one callout
- /// has been registered by any of them for the given hook.
- ///
- /// @param hook_index Hook index for which callouts are checked.
- ///
- /// @return true if callouts are present, false if not.
- ///
- /// @throw NoSuchHook Given index does not correspond to a valid hook.
- bool calloutsPresent(int hook_index) const;
-
- /// @brief Calls the callouts for a given hook
- ///
- /// Iterates through the libray handles and calls the callouts associated
- /// with the given hook index.
- ///
- /// @note This method invalidates the current library index set with
- /// setLibraryIndex().
- ///
- /// @param hook_index Index of the hook to call.
- /// @param callout_handle Reference to the CalloutHandle object for the
- /// current object being processed.
- void callCallouts(int hook_index, CalloutHandle& callout_handle);
-
- /// @brief Get number of libraries
- ///
- /// Returns the number of libraries that this CalloutManager is expected
- /// to serve. This is the number passed to its constructor.
- ///
- /// @return Number of libraries server by this CalloutManager.
- int getNumLibraries() const {
- return (num_libraries_);
- }
-
- /// @brief Get current library index
- ///
- /// Returns the index of the "current" library. This the index associated
- /// with the currently executing callout when callCallouts is executing.
- /// When callCallouts() is not executing (as is the case when the load()
- /// function in a user-library is called during the library load process),
- /// the index can be set by setLibraryIndex().
- ///
- /// @note The value set by this method is lost after a call to
- /// callCallouts.
- ///
- /// @return Current library index.
- int getLibraryIndex() const {
- return (current_library_);
- }
-
- /// @brief Set current library index
- ///
- /// Sets the current library index. This must be in the range 0 to
- /// (numlib - 1), where "numlib" is the number of libraries loaded in the
- /// system, this figure being passed to this object at construction time.
- ///
- /// @param library_index New library index.
- ///
- /// @throw NoSuchLibrary if the index is not valid.
- void setLibraryIndex(int library_index) {
- checkLibraryIndex(library_index);
- current_library_ = library_index;
- }
-
- /// @brief Return library handle
- ///
- /// The library handle is available to the user callout via the callout
- /// handle object. It provides a cut-down view of the CalloutManager,
- /// allowing the callout to register and deregister callouts in the
- /// library of which it is part, whilst denying access to anything that
- /// may affect other libraries.
- ///
- /// @return Reference to callout handle for this manager
- LibraryHandle& getLibraryHandle() {
- return (library_handle_);
- }
-
-private:
- /// @brief Check library index
- ///
- /// Ensures that the current library index is valid. This is called by
- /// the hook registration functions.
- ///
- /// @param library_index Value to check for validity as a library index.
- ///
- /// @throw NoSuchLibrary Library index is not valid.
- void checkLibraryIndex(int library_index) const {
- if ((library_index < 0) || (library_index >= num_libraries_)) {
- isc_throw(NoSuchLibrary, "library index " << library_index <<
- " is not valid for the number of loaded libraries (" <<
- num_libraries_ << ")");
- }
- }
-
- /// @brief Compare two callout entries for library equality
- ///
- /// This is used in callout removal code when all callouts on a hook for a
- /// given library are being removed. It checks whether two callout entries
- /// have the same library index.
- ///
- /// @param ent1 First callout entry to check
- /// @param ent2 Second callout entry to check
- ///
- /// @return bool true if the library entries are the same
- class CalloutLibraryEqual :
- public std::binary_function<CalloutEntry, CalloutEntry, bool> {
- public:
- bool operator()(const CalloutEntry& ent1,
- const CalloutEntry& ent2) const {
- return (ent1.first == ent2.first);
- }
- };
-
- /// Current library index. When a call is made to any of the callout
- /// registration methods, this variable indicates the index of the user
- /// library that should be associated with the call.
- int current_library_;
-
- /// List of server hooks.
- boost::shared_ptr<ServerHooks> hooks_;
-
- /// Vector of callout vectors. There is one entry in this outer vector for
- /// each hook. Each element is itself a vector, with one entry for each
- /// callout registered for that hook.
- std::vector<CalloutVector> hook_vector_;
-
- /// LibraryHandle object user by the callout to access the callout
- /// registration methods on this CalloutManager object.
- LibraryHandle library_handle_;
-
- /// Number of libraries.
- int num_libraries_;
-
-};
-
-} // namespace util
-} // namespace isc
-
-#endif // CALLOUT_MANAGER_H
diff --git a/src/lib/util/hooks/library_handle.cc b/src/lib/util/hooks/library_handle.cc
deleted file mode 100644
index 0a65e54..0000000
--- a/src/lib/util/hooks/library_handle.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-
-namespace isc {
-namespace util {
-
-// Callout manipulation - all deferred to the CalloutManager.
-
-void
-LibraryHandle::registerCallout(const std::string& name, CalloutPtr callout) {
- callout_manager_->registerCallout(name, callout);
-}
-
-bool
-LibraryHandle::deregisterCallout(const std::string& name, CalloutPtr callout) {
- return (callout_manager_->deregisterCallout(name, callout));
-}
-
-bool
-LibraryHandle::deregisterAllCallouts(const std::string& name) {
- return (callout_manager_->deregisterAllCallouts(name));
-}
-
-} // namespace util
-} // namespace isc
diff --git a/src/lib/util/hooks/library_handle.h b/src/lib/util/hooks/library_handle.h
deleted file mode 100644
index 43a78a1..0000000
--- a/src/lib/util/hooks/library_handle.h
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef LIBRARY_HANDLE_H
-#define LIBRARY_HANDLE_H
-
-#include <string>
-
-namespace isc {
-namespace util {
-
-// Forward declarations
-class CalloutHandle;
-class CalloutManager;
-
-/// Typedef for a callout pointer. (Callouts must have "C" linkage.)
-extern "C" {
- typedef int (*CalloutPtr)(CalloutHandle&);
-};
-
-/// @brief Library handle
-///
-/// This class is accessed by the user library when registering callouts,
-/// either by the library's load() function, or by one of the callouts
-/// themselves.
-///
-/// It is really little more than a shell around the CalloutManager. By
-/// presenting this object to the user-library callouts, callouts can manage
-/// the callout list for their own library, but cannot affect the callouts
-/// registered by other libraries.
-///
-/// (This restriction is achieved by the CalloutManager maintaining the concept
-/// of the "current library". When a callout is registered - either by the
-/// library's load() function, or by a callout in the library - the registration
-/// information includes the library active at the time. When that callout is
-/// called, the CalloutManager uses that information to set the "current
-/// library": the registration functions only operator on data whose
-/// associated library is equal to the "current library".)
-
-class LibraryHandle {
-public:
-
- /// @brief Constructor
- ///
- /// @param manager Back pointer to the containing CalloutManager.
- /// This pointer is used to access appropriate methods in that
- /// object. Note that the "raw" pointer is safe - the only
- /// instance of the LibraryHandle in the system is as a member of
- /// the CalloutManager to which it points.
- LibraryHandle(CalloutManager* manager) : callout_manager_(manager)
- {}
-
- /// @brief Register a callout on a hook
- ///
- /// Registers a callout function with a given hook. The callout is added
- /// to the end of the callouts for the current library that are associated
- /// with that hook.
- ///
- /// @param name Name of the hook to which the callout is added.
- /// @param callout Pointer to the callout function to be registered.
- ///
- /// @throw NoSuchHook The hook name is unrecognised.
- /// @throw Unexpected The hook name is valid but an internal data structure
- /// is of the wrong size.
- void registerCallout(const std::string& name, CalloutPtr callout);
-
- /// @brief De-Register a callout on a hook
- ///
- /// Searches through the functions registered by the current library with
- /// the named hook and removes all entries matching the callout. It does
- /// not affect callouts registered by other libraries.
- ///
- /// @param name Name of the hook from which the callout is removed.
- /// @param callout Pointer to the callout function to be removed.
- ///
- /// @return true if a one or more callouts were deregistered.
- ///
- /// @throw NoSuchHook The hook name is unrecognised.
- /// @throw Unexpected The hook name is valid but an internal data structure
- /// is of the wrong size.
- bool deregisterCallout(const std::string& name, CalloutPtr callout);
-
- /// @brief Removes all callouts on a hook
- ///
- /// Removes all callouts associated with a given hook that were registered.
- /// by the current library. It does not affect callouts that were
- /// registered by other libraries.
- ///
- /// @param name Name of the hook from which the callouts are removed.
- ///
- /// @return true if one or more callouts were deregistered.
- ///
- /// @throw NoSuchHook Thrown if the hook name is unrecognised.
- bool deregisterAllCallouts(const std::string& name);
-
-private:
- /// Back pointer to the collection object for the library
- CalloutManager* callout_manager_;
-};
-
-} // namespace util
-} // namespace isc
-
-#endif // LIBRARY_HANDLE_H
diff --git a/src/lib/util/hooks/server_hooks.cc b/src/lib/util/hooks/server_hooks.cc
deleted file mode 100644
index 478d94c..0000000
--- a/src/lib/util/hooks/server_hooks.cc
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-#include <util/hooks/server_hooks.h>
-
-#include <utility>
-#include <vector>
-
-using namespace std;
-using namespace isc;
-
-namespace isc {
-namespace util {
-
-// Constructor - register the pre-defined hooks and check that the indexes
-// assigned to them are as expected.
-
-ServerHooks::ServerHooks() {
- int create = registerHook("context_create");
- int destroy = registerHook("context_destroy");
-
- if ((create != CONTEXT_CREATE) || (destroy != CONTEXT_DESTROY)) {
- isc_throw(Unexpected, "pre-defined hook indexes are not as expected. "
- "context_create: expected = " << CONTEXT_CREATE <<
- ", actual = " << create <<
- ". context_destroy: expected = " << CONTEXT_DESTROY <<
- ", actual = " << destroy);
- }
-}
-
-// Register a hook. The index assigned to the hook is the current number
-// of entries in the collection, so ensuring that hook indexes are unique
-// and non-negative.
-
-int
-ServerHooks::registerHook(const string& name) {
-
- // Determine index for the new element and insert.
- int index = hooks_.size();
- pair<HookCollection::iterator, bool> result =
- hooks_.insert(make_pair(name, index));
-
- if (!result.second) {
- // New element was not inserted because an element with the same name
- // already existed.
- isc_throw(DuplicateHook, "hook with name " << name <<
- " is already registered");
- }
-
- // New element inserted, return numeric index.
- return (index);
-}
-
-// Find the index associated with a hook name.
-
-int
-ServerHooks::getIndex(const string& name) const {
-
- // Get iterator to matching element.
- HookCollection::const_iterator i = hooks_.find(name);
- if (i == hooks_.end()) {
- isc_throw(NoSuchHook, "hook name " << name << " is not recognised");
- }
-
- return (i->second);
-}
-
-// Return vector of hook names. The names are not sorted - it is up to the
-// caller to perform sorting if required.
-
-vector<string>
-ServerHooks::getHookNames() const {
-
- vector<string> names;
- HookCollection::const_iterator i;
- for (i = hooks_.begin(); i != hooks_.end(); ++i) {
- names.push_back(i->first);
- }
-
- return (names);
-}
-
-// Hook registration function methods
-
-// Access the hook registration function vector itself
-
-std::vector<HookRegistrationFunction::RegistrationFunctionPtr>&
-HookRegistrationFunction::getFunctionVector() {
- static std::vector<RegistrationFunctionPtr> reg_functions;
- return (reg_functions);
-}
-
-// Constructor - add a registration function to the function vector
-
-HookRegistrationFunction::HookRegistrationFunction(
- HookRegistrationFunction::RegistrationFunctionPtr reg_func) {
- getFunctionVector().push_back(reg_func);
-}
-
-// Execute all registered registration functions
-
-void
-HookRegistrationFunction::execute(ServerHooks& hooks) {
- std::vector<RegistrationFunctionPtr>& reg_functions = getFunctionVector();
- for (int i = 0; i < reg_functions.size(); ++i) {
- (*reg_functions[i])(hooks);
- }
-}
-
-
-
-} // namespace util
-} // namespace isc
diff --git a/src/lib/util/hooks/server_hooks.h b/src/lib/util/hooks/server_hooks.h
deleted file mode 100644
index ce41fa0..0000000
--- a/src/lib/util/hooks/server_hooks.h
+++ /dev/null
@@ -1,207 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#ifndef SERVER_HOOKS_H
-#define SERVER_HOOKS_H
-
-#include <exceptions/exceptions.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-namespace isc {
-namespace util {
-
-/// @brief Duplicate hook
-///
-/// Thrown if an attempt is made to register a hook with the same name as a
-/// previously-registered hook.
-class DuplicateHook : public Exception {
-public:
- DuplicateHook(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-/// @brief Invalid hook
-///
-/// Thrown if an attempt is made to get the index for an invalid hook.
-class NoSuchHook : public Exception {
-public:
- NoSuchHook(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
-
-/// @brief Server hook collection
-///
-/// This class is used by the server-side code to register hooks - points in the
-/// server processing at which libraries can register functions (callouts) that
-/// the server will call. These functions can modify data and so affect the
-/// processing of the server.
-///
-/// The ServerHooks class is little more than a wrapper around the std::map
-/// class. It stores a hook, assigning to it a unique index number. This
-/// number is then used by the server code to identify the hook being called.
-/// (Although it would be feasible to use a name as an index, using an integer
-/// will speed up the time taken to locate the callouts, which may make a
-/// difference in a frequently-executed piece of code.)
-
-class ServerHooks {
-public:
-
- /// Index numbers for pre-defined hooks.
- static const int CONTEXT_CREATE = 0;
- static const int CONTEXT_DESTROY = 1;
-
- /// @brief Constructor
- ///
- /// This pre-registers two hooks, context_create and context_destroy, which
- /// are called by the server before processing a packet and after processing
- /// for the packet has completed. They allow the server code to allocate
- /// and destroy per-packet context.
- ///
- /// @throws isc::Unexpected if the registration of the pre-defined hooks
- /// fails in some way.
- ServerHooks();
-
- /// @brief Register a hook
- ///
- /// Registers a hook and returns the hook index.
- ///
- /// @param name Name of the hook
- ///
- /// @return Index of the hook, to be used in subsequent hook-related calls.
- /// This will be greater than or equal to zero (so allowing a
- /// negative value to indicate an invalid index).
- ///
- /// @throws DuplicateHook A hook with the same name has already been
- /// registered.
- int registerHook(const std::string& name);
-
- /// @brief Get hook index
- ///
- /// Returns the index of a hook.
- ///
- /// @param name Name of the hook
- ///
- /// @return Index of the hook, to be used in subsequent calls.
- ///
- /// @throw NoSuchHook if the hook name is unknown to the caller.
- int getIndex(const std::string& name) const;
-
- /// @brief Return number of hooks
- ///
- /// Returns the total number of hooks registered.
- ///
- /// @return Number of hooks registered.
- int getCount() const {
- return (hooks_.size());
- }
-
- /// @brief Get hook names
- ///
- /// Return list of hooks registered in the object.
- ///
- /// @return Vector of strings holding hook names.
- std::vector<std::string> getHookNames() const;
-
-private:
- typedef std::map<std::string, int> HookCollection;
-
- HookCollection hooks_; ///< Hook name/index collection
-};
-
-
-/// @brief Hooks Registration
-///
-/// All hooks must be registered before libraries are loaded and callouts
-/// assigned to them. One way of doing this is to have a global list of hooks:
-/// the addition of any hook anywhere would require updating the list. This
-/// is possible and, if desired, the author of a server can do it.
-///
-/// An alternative is the option provided here, where each component of BIND 10
-/// registers the hooks they are using. To do this, the component should
-/// create a hook registration function of the form:
-///
-/// @code
-/// static int hook1_num = -1; // Initialize number for hook 1
-/// static int hook2_num = -1; // Initialize number for hook 2
-///
-/// void myModuleRegisterHooks(ServerHooks& hooks) {
-/// hook1_num = hooks.registerHook("hook1");
-/// hook2_num = hooks.registerHook("hook2");
-/// }
-/// @endcode
-///
-/// ... which registers the hooks and stores the associated hook index. To
-/// avoid the need to add an explicit call to each of the hook registration
-/// functions to the server initialization code, the component should declare
-/// an object of this class in the same file as the registration function,
-/// but outside of any function. The declaration should include the name
-/// of the registration function, i.e.
-///
-/// @code
-/// HookRegistrationFunction f(myModuleRegisterHooks);
-/// @code
-///
-/// The constructor of this object will run prior to main() getting called and
-/// will add the registration function to a list of such functions. The server
-/// then calls the static class method "execute()" to run all the declared
-/// registration functions.
-
-class HookRegistrationFunction {
-public:
- /// @brief Pointer to a hook registration function
- typedef void (*RegistrationFunctionPtr)(ServerHooks&);
-
- /// @brief Constructor
- ///
- /// For variables declared outside functions or methods, the constructors
- /// are run after the program is loaded and before main() is called. This
- /// constructor adds the passed pointer to a vector of such pointers.
- HookRegistrationFunction(RegistrationFunctionPtr reg_func);
-
- /// @brief Access registration function vector
- ///
- /// One of the problems with functions run prior to starting main() is the
- /// "static initialization fiasco". This occurs because the order in which
- /// objects outside functions are constructed is not defined. So if this
- /// constructor were to depend on a vector declared externally, we would
- /// not be able to guarantee that the vector had been initialised properly
- /// before we used it.
- ///
- /// To get round this situation, the vector is declared statically within
- /// a static function. The first time the function is called, the vector
- /// is initialized before it is used.
- ///
- /// This function returns a reference to the vector used to hold the
- /// pointers.
- ///
- /// @return Reference to the (static) list of registration functions
- static std::vector<RegistrationFunctionPtr>& getFunctionVector();
-
- /// @brief Execute registration functions
- ///
- /// Called by the server initialization code, this function executes all
- /// registered hook registration functions.
- ///
- /// @param hooks ServerHooks object to which hook information will be added.
- static void execute(ServerHooks& hooks);
-};
-
-} // namespace util
-} // namespace isc
-
-#endif // SERVER_HOOKS_H
diff --git a/src/lib/util/tests/Makefile.am b/src/lib/util/tests/Makefile.am
index ceb8b95..ab85fa2 100644
--- a/src/lib/util/tests/Makefile.am
+++ b/src/lib/util/tests/Makefile.am
@@ -25,13 +25,10 @@ run_unittests_SOURCES = run_unittests.cc
run_unittests_SOURCES += base32hex_unittest.cc
run_unittests_SOURCES += base64_unittest.cc
run_unittests_SOURCES += buffer_unittest.cc
-run_unittests_SOURCES += callout_handle_unittest.cc
-run_unittests_SOURCES += callout_manager_unittest.cc
run_unittests_SOURCES += fd_share_tests.cc
run_unittests_SOURCES += fd_tests.cc
run_unittests_SOURCES += filename_unittest.cc
run_unittests_SOURCES += hex_unittest.cc
-run_unittests_SOURCES += handles_unittest.cc
run_unittests_SOURCES += io_utilities_unittest.cc
run_unittests_SOURCES += lru_list_unittest.cc
run_unittests_SOURCES += memory_segment_local_unittest.cc
@@ -42,7 +39,6 @@ run_unittests_SOURCES += memory_segment_common_unittest.h
run_unittests_SOURCES += memory_segment_common_unittest.cc
run_unittests_SOURCES += qid_gen_unittest.cc
run_unittests_SOURCES += random_number_generator_unittest.cc
-run_unittests_SOURCES += server_hooks_unittest.cc
run_unittests_SOURCES += sha1_unittest.cc
run_unittests_SOURCES += socketsession_unittest.cc
run_unittests_SOURCES += strutil_unittest.cc
diff --git a/src/lib/util/tests/callout_handle_unittest.cc b/src/lib/util/tests/callout_handle_unittest.cc
deleted file mode 100644
index 76e18d8..0000000
--- a/src/lib/util/tests/callout_handle_unittest.cc
+++ /dev/null
@@ -1,329 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <boost/shared_ptr.hpp>
-
-#include <gtest/gtest.h>
-
-using namespace isc::util;
-using namespace std;
-
-namespace {
-
-/// @file
-/// @brief Holds the CalloutHandle argument tests
-///
-/// Additional testing of the CalloutHandle - together with the interaction
-/// of the LibraryHandle - is done in the handles_unittests set of tests.
-
-class CalloutHandleTest : public ::testing::Test {
-public:
-
- /// @brief Constructor
- ///
- /// Sets up a callout manager to be referenced by the CalloutHandle in
- /// these tests. (The "4" for the number of libraries in the
- /// CalloutManager is arbitrary - it is not used in these tests.)
- CalloutHandleTest()
- : hooks_(new ServerHooks()), manager_(new CalloutManager(hooks_, 4))
- {}
-
- /// Obtain hook manager
- boost::shared_ptr<CalloutManager>& getCalloutManager() {
- return (manager_);
- }
-
-private:
- /// List of server hooks
- boost::shared_ptr<ServerHooks> hooks_;
-
- /// Callout manager accessed by this CalloutHandle.
- boost::shared_ptr<CalloutManager> manager_;
-};
-
-// *** Argument Tests ***
-//
-// The first set of tests check that the CalloutHandle can store and retrieve
-// arguments. These are very similar to the LibraryHandle context tests.
-
-// Test that we can store multiple values of the same type and that they
-// are distinct.
-
-TEST_F(CalloutHandleTest, ArgumentDistinctSimpleType) {
- CalloutHandle handle(getCalloutManager());
-
- // Store and retrieve an int (random value).
- int a = 42;
- handle.setArgument("integer1", a);
- EXPECT_EQ(42, a);
-
- int b = 0;
- handle.getArgument("integer1", b);
- EXPECT_EQ(42, b);
-
- // Add another integer (another random value).
- int c = 142;
- handle.setArgument("integer2", c);
- EXPECT_EQ(142, c);
-
- int d = 0;
- handle.getArgument("integer2", d);
- EXPECT_EQ(142, d);
-
- // Add a short (random value).
- short e = -81;
- handle.setArgument("short", e);
- EXPECT_EQ(-81, e);
-
- short f = 0;
- handle.getArgument("short", f);
- EXPECT_EQ(-81, f);
-}
-
-// Test that trying to get an unknown argument throws an exception.
-
-TEST_F(CalloutHandleTest, ArgumentUnknownName) {
- CalloutHandle handle(getCalloutManager());
-
- // Set an integer
- int a = 42;
- handle.setArgument("integer1", a);
- EXPECT_EQ(42, a);
-
- // Check we can retrieve it
- int b = 0;
- handle.getArgument("integer1", b);
- EXPECT_EQ(42, b);
-
- // Check that getting an unknown name throws an exception.
- int c = 0;
- EXPECT_THROW(handle.getArgument("unknown", c), NoSuchArgument);
-}
-
-// Test that trying to get an argument with an incorrect type throws an
-// exception.
-
-TEST_F(CalloutHandleTest, ArgumentIncorrectType) {
- CalloutHandle handle(getCalloutManager());
-
- // Set an integer
- int a = 42;
- handle.setArgument("integer1", a);
- EXPECT_EQ(42, a);
-
- // Check we can retrieve it
- long b = 0;
- EXPECT_THROW(handle.getArgument("integer1", b), boost::bad_any_cast);
-}
-
-// Now try with some very complex types. The types cannot be defined within
-// the function and they should contain a copy constructor. For this reason,
-// a simple "struct" is used.
-
-struct Alpha {
- int a;
- int b;
- Alpha(int first = 0, int second = 0) : a(first), b(second) {}
-};
-
-struct Beta {
- int c;
- int d;
- Beta(int first = 0, int second = 0) : c(first), d(second) {}
-};
-
-TEST_F(CalloutHandleTest, ComplexTypes) {
- CalloutHandle handle(getCalloutManager());
-
- // Declare two variables of different (complex) types. (Note as to the
- // variable names: aleph and beth are the first two letters of the Hebrew
- // alphabet.)
- Alpha aleph(1, 2);
- EXPECT_EQ(1, aleph.a);
- EXPECT_EQ(2, aleph.b);
- handle.setArgument("aleph", aleph);
-
- Beta beth(11, 22);
- EXPECT_EQ(11, beth.c);
- EXPECT_EQ(22, beth.d);
- handle.setArgument("beth", beth);
-
- // Ensure we can extract the data correctly.
- Alpha aleph2;
- EXPECT_EQ(0, aleph2.a);
- EXPECT_EQ(0, aleph2.b);
- handle.getArgument("aleph", aleph2);
- EXPECT_EQ(1, aleph2.a);
- EXPECT_EQ(2, aleph2.b);
-
- Beta beth2;
- EXPECT_EQ(0, beth2.c);
- EXPECT_EQ(0, beth2.d);
- handle.getArgument("beth", beth2);
- EXPECT_EQ(11, beth2.c);
- EXPECT_EQ(22, beth2.d);
-
- // Ensure that complex types also thrown an exception if we attempt to
- // get a context element of the wrong type.
- EXPECT_THROW(handle.getArgument("aleph", beth), boost::bad_any_cast);
-}
-
-// Check that the context can store pointers. And also check that it respects
-// that a "pointer to X" is not the same as a "pointer to const X".
-
-TEST_F(CalloutHandleTest, PointerTypes) {
- CalloutHandle handle(getCalloutManager());
-
- // Declare a couple of variables, const and non-const.
- Alpha aleph(5, 10);
- const Beta beth(15, 20);
-
- Alpha* pa = ℵ
- const Beta* pcb = ℶ
-
- // Check pointers can be set and retrieved OK.
- handle.setArgument("non_const_pointer", pa);
- handle.setArgument("const_pointer", pcb);
-
- Alpha* pa2 = 0;
- handle.getArgument("non_const_pointer", pa2);
- EXPECT_TRUE(pa == pa2);
-
- const Beta* pcb2 = 0;
- handle.getArgument("const_pointer", pcb2);
- EXPECT_TRUE(pcb == pcb2);
-
- // Check that the "const" is protected in the context.
- const Alpha* pca3;
- EXPECT_THROW(handle.getArgument("non_const_pointer", pca3),
- boost::bad_any_cast);
-
- Beta* pb3;
- EXPECT_THROW(handle.getArgument("const_pointer", pb3),
- boost::bad_any_cast);
-}
-
-// Check that we can get the names of the arguments.
-
-TEST_F(CalloutHandleTest, ContextItemNames) {
- CalloutHandle handle(getCalloutManager());
-
- vector<string> expected_names;
-
- expected_names.push_back("faith");
- handle.setArgument("faith", 42);
- expected_names.push_back("hope");
- handle.setArgument("hope", 43);
- expected_names.push_back("charity");
- handle.setArgument("charity", 44);
-
- // Get the names and check against the expected names. We'll sort
- // both arrays to simplify the checking.
- vector<string> actual_names = handle.getArgumentNames();
-
- sort(actual_names.begin(), actual_names.end());
- sort(expected_names.begin(), expected_names.end());
- EXPECT_TRUE(expected_names == actual_names);
-}
-
-// Test that we can delete an argument.
-
-TEST_F(CalloutHandleTest, DeleteArgument) {
- CalloutHandle handle(getCalloutManager());
-
- int one = 1;
- int two = 2;
- int three = 3;
- int four = 4;
- int value; // Return value
-
- handle.setArgument("one", one);
- handle.setArgument("two", two);
- handle.setArgument("three", three);
- handle.setArgument("four", four);
-
- // Delete "one".
- handle.getArgument("one", value);
- EXPECT_EQ(1, value);
- handle.deleteArgument("one");
-
- EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
- handle.getArgument("two", value);
- EXPECT_EQ(2, value);
- handle.getArgument("three", value);
- EXPECT_EQ(3, value);
- handle.getArgument("four", value);
- EXPECT_EQ(4, value);
-
- // Delete "three".
- handle.getArgument("three", value);
- EXPECT_EQ(3, value);
- handle.deleteArgument("three");
-
- EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
- handle.getArgument("two", value);
- EXPECT_EQ(2, value);
- EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
- handle.getArgument("four", value);
- EXPECT_EQ(4, value);
-}
-
-// Test that we can delete all arguments.
-
-TEST_F(CalloutHandleTest, DeleteAllArguments) {
- CalloutHandle handle(getCalloutManager());
-
- int one = 1;
- int two = 2;
- int three = 3;
- int four = 4;
- int value; // Return value
-
- // Set the arguments. The previous test verifies that this works.
- handle.setArgument("one", one);
- handle.setArgument("two", two);
- handle.setArgument("three", three);
- handle.setArgument("four", four);
-
- // Delete all arguments...
- handle.deleteAllArguments();
-
- // ... and check that none are left.
- EXPECT_THROW(handle.getArgument("one", value), NoSuchArgument);
- EXPECT_THROW(handle.getArgument("two", value), NoSuchArgument);
- EXPECT_THROW(handle.getArgument("three", value), NoSuchArgument);
- EXPECT_THROW(handle.getArgument("four", value), NoSuchArgument);
-}
-
-// Test the "skip" flag.
-
-TEST_F(CalloutHandleTest, SkipFlag) {
- CalloutHandle handle(getCalloutManager());
-
- // Should be false on construction.
- EXPECT_FALSE(handle.getSkip());
-
- handle.setSkip(true);
- EXPECT_TRUE(handle.getSkip());
-
- handle.setSkip(false);
- EXPECT_FALSE(handle.getSkip());
-}
-
-} // Anonymous namespace
diff --git a/src/lib/util/tests/callout_manager_unittest.cc b/src/lib/util/tests/callout_manager_unittest.cc
deleted file mode 100644
index ca9b8ad..0000000
--- a/src/lib/util/tests/callout_manager_unittest.cc
+++ /dev/null
@@ -1,765 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <exceptions/exceptions.h>
-#include <util/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <boost/scoped_ptr.hpp>
-#include <gtest/gtest.h>
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-/// @file
-/// @brief CalloutManager and LibraryHandle tests
-///
-/// These set of tests check the CalloutManager and LibraryHandle. They are
-/// together in the same file because the LibraryHandle is little more than a
-/// restricted interface to the CalloutManager, and a lot of the support
-/// structure for the tests is common.
-
-using namespace isc;
-using namespace isc::util;
-using namespace std;
-
-namespace {
-
-class CalloutManagerTest : public ::testing::Test {
-public:
- /// @brief Constructor
- ///
- /// Sets up a collection of three LibraryHandle objects to use in the test.
- CalloutManagerTest() : hooks_(new ServerHooks()) {
-
- // Set up the server hooks
- alpha_index_ = hooks_->registerHook("alpha");
- beta_index_ = hooks_->registerHook("beta");
- gamma_index_ = hooks_->registerHook("gamma");
- delta_index_ = hooks_->registerHook("delta");
-
- // Set up the callout manager with these hooks. Assume a maximum of
- // four libraries.
- callout_manager_.reset(new CalloutManager(hooks_, 10));
-
- // Set up the callout handle.
- callout_handle_.reset(new CalloutHandle(callout_manager_));
-
- // Initialize the static variable.
- callout_value_ = 0;
- }
-
- /// @brief Return the callout handle
- CalloutHandle& getCalloutHandle() {
- return (*callout_handle_);
- }
-
- /// @brief Return the callout manager
- boost::shared_ptr<CalloutManager> getCalloutManager() {
- return (callout_manager_);
- }
-
- boost::shared_ptr<ServerHooks> getServerHooks() {
- return (hooks_);
- }
-
- /// Static variable used for accumulating information
- static int callout_value_;
-
- /// Hook indexes. These are somewhat ubiquitous, so are made public for
- /// ease of reference instead of being accessible by a function.
- int alpha_index_;
- int beta_index_;
- int gamma_index_;
- int delta_index_;
-
-private:
- /// Callout handle used in calls
- boost::shared_ptr<CalloutHandle> callout_handle_;
-
- /// Callout manager used for the test
- boost::shared_ptr<CalloutManager> callout_manager_;
-
- /// Server hooks
- boost::shared_ptr<ServerHooks> hooks_;
-};
-
-// Definition of the static variable.
-int CalloutManagerTest::callout_value_ = 0;
-
-// Callout definitions
-//
-// The callouts defined here are structured in such a way that it is possible
-// to determine the order in which they are called and whether they are called
-// at all. The method used is simple - after a sequence of callouts, the digits
-// in the value, reading left to right, determines the order of the callouts
-// called. For example, callout one followed by two followed by three followed
-// by two followed by one results in a value of 12321.
-//
-// Functions return a zero to indicate success.
-
-extern "C" {
-int callout_general(int number) {
- CalloutManagerTest::callout_value_ =
- 10 * CalloutManagerTest::callout_value_ + number;
- return (0);
-}
-
-int callout_one(CalloutHandle&) {
- return (callout_general(1));
-}
-
-int callout_two(CalloutHandle&) {
- return (callout_general(2));
-}
-
-int callout_three(CalloutHandle&) {
- return (callout_general(3));
-}
-
-int callout_four(CalloutHandle&) {
- return (callout_general(4));
-}
-
-int callout_five(CalloutHandle&) {
- return (callout_general(5));
-}
-
-int callout_six(CalloutHandle&) {
- return (callout_general(6));
-}
-
-int callout_seven(CalloutHandle&) {
- return (callout_general(7));
-}
-
-// The next functions are duplicates of some of the above, but return an error.
-
-int callout_one_error(CalloutHandle& handle) {
- (void) callout_one(handle);
- return (1);
-}
-
-int callout_two_error(CalloutHandle& handle) {
- (void) callout_two(handle);
- return (1);
-}
-
-int callout_three_error(CalloutHandle& handle) {
- (void) callout_three(handle);
- return (1);
-}
-
-int callout_four_error(CalloutHandle& handle) {
- (void) callout_four(handle);
- return (1);
-}
-
-}; // extern "C"
-
-// *** Callout Tests ***
-//
-// The next set of tests check that callouts can be called.
-
-// Constructor - check that we trap bad parameters.
-
-TEST_F(CalloutManagerTest, BadConstructorParameters) {
- boost::scoped_ptr<CalloutManager> cm;
-
- // Invalid number of libraries
- EXPECT_THROW(cm.reset(new CalloutManager(getServerHooks(), 0)), BadValue);
- EXPECT_THROW(cm.reset(new CalloutManager(getServerHooks(), -1)), BadValue);
-
- // Invalid server hooks pointer.
- boost::shared_ptr<ServerHooks> sh;
- EXPECT_THROW(cm.reset(new CalloutManager(sh, 4)), BadValue);
-}
-
-// Check the number of libraries is reported successfully.
-
-TEST_F(CalloutManagerTest, GetNumLibraries) {
- boost::scoped_ptr<CalloutManager> cm;
-
- // Check two valid values of number of libraries to ensure that the
- // GetNumLibraries() returns the value set.
- EXPECT_NO_THROW(cm.reset(new CalloutManager(getServerHooks(), 4)));
- EXPECT_EQ(4, cm->getNumLibraries());
-
- EXPECT_NO_THROW(cm.reset(new CalloutManager(getServerHooks(), 42)));
- EXPECT_EQ(42, cm->getNumLibraries());
-}
-
-// Check that we can only set the current library index to the correct values.
-
-TEST_F(CalloutManagerTest, CheckLibraryIndex) {
- // Check valid indexes
- for (int i = 0; i < 4; ++i) {
- EXPECT_NO_THROW(getCalloutManager()->setLibraryIndex(i));
- }
-
- // Check invalid ones
- EXPECT_THROW(getCalloutManager()->setLibraryIndex(-1), NoSuchLibrary);
- EXPECT_THROW(getCalloutManager()->setLibraryIndex(15), NoSuchLibrary);
-}
-
-// Check that we can only register callouts on valid hook names.
-
-TEST_F(CalloutManagerTest, ValidHookNames) {
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_NO_THROW(getCalloutManager()->registerCallout("alpha", callout_one));
- EXPECT_THROW(getCalloutManager()->registerCallout("unknown", callout_one),
- NoSuchHook);
-}
-
-
-// Check we can register callouts appropriately.
-
-TEST_F(CalloutManagerTest, RegisterCallout) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
-
- // Set up so that hooks "alpha" and "beta" have callouts attached from a
- // different libraries.
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout_one);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("beta", callout_two);
-
- // Check all is as expected.
- EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Check that calling the callouts returns as expected. (This is also a
- // test of the callCallouts method.)
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1, callout_value_);
-
- callout_value_ = 0;
- getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
- EXPECT_EQ(2, callout_value_);
-
- // Register some more callouts from different libraries on hook "alpha".
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->registerCallout("alpha", callout_four);
- getCalloutManager()->setLibraryIndex(3);
- getCalloutManager()->registerCallout("alpha", callout_five);
-
- // Check it is as expected.
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1345, callout_value_);
-
- // ... and check the additional callouts were not registered on the "beta"
- // hook.
- callout_value_ = 0;
- getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
- EXPECT_EQ(2, callout_value_);
-
- // Add another callout to hook "alpha" from library index 2 - this should
- // appear at the end of the callout list for that library.
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout_six);
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(13465, callout_value_);
-
- // Add a callout from library index 1 - this should appear between the
- // callouts from library index 0 and linrary index 2.
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout_seven);
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(173465, callout_value_);
-}
-
-// Check the "calloutsPresent()" method.
-
-TEST_F(CalloutManagerTest, CalloutsPresent) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Set up so that hooks "alpha", "beta" and "delta" have callouts attached
- // to them, and callout "gamma" does not. (In the statements below, the
- // exact callouts attached to a hook are not relevant - only the fact
- // that some callouts are). Chose the libraries for which the callouts
- // are registered randomly.
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout_one);
-
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->registerCallout("beta", callout_two);
-
- getCalloutManager()->setLibraryIndex(3);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->registerCallout("delta", callout_four);
-
- // Check all is as expected.
- EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_TRUE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_TRUE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Check we fail on an invalid hook index.
- EXPECT_THROW(getCalloutManager()->calloutsPresent(42), NoSuchHook);
- EXPECT_THROW(getCalloutManager()->calloutsPresent(-1), NoSuchHook);
-}
-
-// Test that calling a hook with no callouts on it returns success.
-
-TEST_F(CalloutManagerTest, CallNoCallouts) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Call the callouts on an arbitrary hook and ensure that nothing happens.
- callout_value_ = 475;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(475, callout_value_); // Unchanged
-}
-
-// Test that the callouts are called in the correct order (i.e. the callouts
-// from the first library in the order they were registered, then the callouts
-// from the second library in the order they were registered etc.)
-
-TEST_F(CalloutManagerTest, CallCalloutsSuccess) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Each library contributes one callout on hook "alpha".
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout_one);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->setLibraryIndex(3);
- getCalloutManager()->registerCallout("alpha", callout_four);
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1234, callout_value_);
-
- // Do a random selection of callouts on hook "beta".
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("beta", callout_one);
- getCalloutManager()->registerCallout("beta", callout_three);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("beta", callout_two);
- getCalloutManager()->setLibraryIndex(3);
- getCalloutManager()->registerCallout("beta", callout_four);
- getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
- EXPECT_EQ(1324, callout_value_);
-
- // Ensure that calling the callouts on a hook with no callouts works.
- callout_value_ = 0;
- getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
- EXPECT_EQ(0, callout_value_);
-}
-
-// Test that the callouts are called in order, but that callouts occurring
-// after a callout that returns an error are not called.
-//
-// (Note: in this test, the callouts that return an error set the value of
-// callout_value_ before they return the error code.)
-
-TEST_F(CalloutManagerTest, CallCalloutsError) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Each library contributing one callout on hook "alpha". The first callout
- // returns an error (after adding its value to the result).
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout_one_error);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->setLibraryIndex(3);
- getCalloutManager()->registerCallout("alpha", callout_four);
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1234, callout_value_);
-
- // Each library contributing multiple callouts on hook "beta". The last
- // callout on the first library returns an error.
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("beta", callout_one);
- getCalloutManager()->registerCallout("beta", callout_one_error);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("beta", callout_two);
- getCalloutManager()->registerCallout("beta", callout_two);
- getCalloutManager()->registerCallout("beta", callout_three);
- getCalloutManager()->registerCallout("beta", callout_three);
- getCalloutManager()->setLibraryIndex(3);
- getCalloutManager()->registerCallout("beta", callout_four);
- getCalloutManager()->registerCallout("beta", callout_four);
- getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
- EXPECT_EQ(11223344, callout_value_);
-
- // A callout in a random position in the callout list returns an error.
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("gamma", callout_one);
- getCalloutManager()->registerCallout("gamma", callout_one);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("gamma", callout_two);
- getCalloutManager()->registerCallout("gamma", callout_two);
- getCalloutManager()->setLibraryIndex(3);
- getCalloutManager()->registerCallout("gamma", callout_four_error);
- getCalloutManager()->registerCallout("gamma", callout_four);
- getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
- EXPECT_EQ(112244, callout_value_);
-
- // The last callout on a hook returns an error.
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("delta", callout_one);
- getCalloutManager()->registerCallout("delta", callout_one);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("delta", callout_two);
- getCalloutManager()->registerCallout("delta", callout_two);
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("delta", callout_three);
- getCalloutManager()->registerCallout("delta", callout_three);
- getCalloutManager()->setLibraryIndex(3);
- getCalloutManager()->registerCallout("delta", callout_four);
- getCalloutManager()->registerCallout("delta", callout_four_error);
- getCalloutManager()->callCallouts(delta_index_, getCalloutHandle());
- EXPECT_EQ(11223344, callout_value_);
-}
-
-// Now test that we can deregister a single callout on a hook.
-
-TEST_F(CalloutManagerTest, DeregisterSingleCallout) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Add a callout to hook "alpha" and check it is added correctly.
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(2, callout_value_);
-
- // Remove it and check that the no callouts are present. We have to reset
- // the current library index here as it was invalidated by the call
- // to callCallouts().
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-}
-
-// Now test that we can deregister a single callout on a hook that has multiple
-// callouts from the same library.
-
-TEST_F(CalloutManagerTest, DeregisterSingleCalloutSameLibrary) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Add multiple callouts to hook "alpha".
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout_one);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->registerCallout("alpha", callout_four);
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1234, callout_value_);
-
- // Remove the callout_two callout. We have to reset the current library
- // index here as it was invalidated by the call to callCallouts().
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(134, callout_value_);
-
- // Try removing it again.
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(134, callout_value_);
-
-}
-
-// Check we can deregister multiple callouts from the same library.
-
-TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsSameLibrary) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Each library contributes one callout on hook "alpha".
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout_one);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->registerCallout("alpha", callout_one);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->registerCallout("alpha", callout_four);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->registerCallout("alpha", callout_four);
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(12123434, callout_value_);
-
- // Remove the callout_two callouts. We have to reset the current library
- // index here as it was invalidated by the call to callCallouts().
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(113434, callout_value_);
-
- // Try removing multiple callouts that includes one at the end of the
- // list of callouts.
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_four));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1133, callout_value_);
-
- // ... and from the start.
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_one));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(33, callout_value_);
-
- // ... and the remaining callouts.
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_three));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(0, callout_value_);
-
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-}
-
-// Check we can deregister multiple callouts from multiple libraries.
-
-TEST_F(CalloutManagerTest, DeregisterMultipleCalloutsMultipleLibraries) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Each library contributes two callouts to hook "alpha".
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout_one);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->registerCallout("alpha", callout_four);
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout_five);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(123452, callout_value_);
-
- // Remove the callout_two callout from library 0. It should not affect
- // the second callout_two callout registered by library 2.
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(13452, callout_value_);
-}
-
-// Check we can deregister all callouts from a single library.
-
-TEST_F(CalloutManagerTest, DeregisterAllCallouts) {
- // Ensure that no callouts are attached to hook one.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-
- // Each library contributes two callouts to hook "alpha".
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout_one);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->registerCallout("alpha", callout_four);
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout_five);
- getCalloutManager()->registerCallout("alpha", callout_six);
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(123456, callout_value_);
-
- // Remove all callouts from library index 1.
- getCalloutManager()->setLibraryIndex(1);
- EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1256, callout_value_);
-
- // Remove all callouts from library index 2.
- getCalloutManager()->setLibraryIndex(2);
- EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(12, callout_value_);
-}
-
-// Check that we can register/deregister callouts on different libraries
-// and different hooks, and that the callout instances are regarded as
-// unique and do not affect one another.
-
-TEST_F(CalloutManagerTest, MultipleCalloutsLibrariesHooks) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Register callouts on the alpha hook.
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout_one);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout_three);
- getCalloutManager()->registerCallout("alpha", callout_four);
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout_five);
- getCalloutManager()->registerCallout("alpha", callout_two);
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(123452, callout_value_);
-
- // Register the same callouts on the beta hook, and check that those
- // on the alpha hook are not affected.
- callout_value_ = 0;
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("beta", callout_five);
- getCalloutManager()->registerCallout("beta", callout_one);
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("beta", callout_four);
- getCalloutManager()->registerCallout("beta", callout_three);
- getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
- EXPECT_EQ(5143, callout_value_);
-
- // Check that the order of callouts on the alpha hook has not been affected.
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(123452, callout_value_);
-
- // Remove callout four from beta and check that alpha is not affected.
- getCalloutManager()->setLibraryIndex(2);
- EXPECT_TRUE(getCalloutManager()->deregisterCallout("beta", callout_four));
-
- callout_value_ = 0;
- getCalloutManager()->callCallouts(beta_index_, getCalloutHandle());
- EXPECT_EQ(513, callout_value_);
-
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(123452, callout_value_);
-}
-
-// Library handle tests. As by inspection the LibraryHandle can be seen to be
-// little more than shell around CalloutManager, only a basic set of tests
-// is done concerning registration and deregistration of functions.
-//
-// More extensive tests (i.e. checking that when a callout is called it can
-// only register and deregister callouts within its library) require that
-// the CalloutHandle object pass the appropriate LibraryHandle to the
-// callout. These tests are done in the handles_unittest tests.
-
-TEST_F(CalloutManagerTest, LibraryHandleRegistration) {
- // Ensure that no callouts are attached to any of the hooks.
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(alpha_index_));
-
- // Set up so that hooks "alpha" and "beta" have callouts attached from a
- // different libraries.
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->getLibraryHandle().registerCallout("alpha",
- callout_one);
- getCalloutManager()->getLibraryHandle().registerCallout("alpha",
- callout_two);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->getLibraryHandle().registerCallout("alpha",
- callout_three);
- getCalloutManager()->getLibraryHandle().registerCallout("alpha",
- callout_four);
-
- // Check all is as expected.
- EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(beta_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(gamma_index_));
- EXPECT_FALSE(getCalloutManager()->calloutsPresent(delta_index_));
-
- // Check that calling the callouts returns as expected. (This is also a
- // test of the callCallouts method.)
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1234, callout_value_);
-
- // Deregister a callout on library index 0 (after we check we can't
- // deregister it through library index 1).
- getCalloutManager()->setLibraryIndex(1);
- EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1234, callout_value_);
-
- getCalloutManager()->setLibraryIndex(0);
- EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(134, callout_value_);
-
- // Deregister all callouts on library index 1.
- getCalloutManager()->setLibraryIndex(1);
- EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha"));
- callout_value_ = 0;
- getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
- EXPECT_EQ(1, callout_value_);
-}
-
-
-
-} // Anonymous namespace
diff --git a/src/lib/util/tests/handles_unittest.cc b/src/lib/util/tests/handles_unittest.cc
deleted file mode 100644
index 5e5b237..0000000
--- a/src/lib/util/tests/handles_unittest.cc
+++ /dev/null
@@ -1,917 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/hooks/callout_handle.h>
-#include <util/hooks/callout_manager.h>
-#include <util/hooks/library_handle.h>
-#include <util/hooks/server_hooks.h>
-
-#include <boost/lexical_cast.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <gtest/gtest.h>
-
-#include <algorithm>
-#include <string>
-
-/// @file
-/// CalloutHandle/LibraryHandle interaction tests
-///
-/// This file holds unit tests checking the interaction between the
-/// CalloutHandle/LibraryHandle and CalloutManager classes. In particular,
-/// they check that:
-///
-/// - A CalloutHandle's context is shared between callouts from the same
-/// library, but there is a separate context for each library.
-///
-/// - The various methods manipulating the items in the CalloutHandle's context
-/// work correctly.
-///
-/// - An active callout can only modify the registration of callouts registered
-/// by its own library.
-
-using namespace isc::util;
-using namespace std;
-
-namespace {
-
-class HandlesTest : public ::testing::Test {
-public:
- /// @brief Constructor
- ///
- /// Sets up the various elements used in each test.
- HandlesTest() : hooks_(new ServerHooks()), manager_()
- {
- // Set up four hooks, although through gamma
- alpha_index_ = hooks_->registerHook("alpha");
- beta_index_ = hooks_->registerHook("beta");
- gamma_index_ = hooks_->registerHook("gamma");
- delta_index_ = hooks_->registerHook("delta");
-
- // Set up for three libraries.
- manager_.reset(new CalloutManager(hooks_, 3));
-
- // Initialize remaining variables.
- common_string_ = "";
- }
-
- /// @brief Return callout manager
- boost::shared_ptr<CalloutManager> getCalloutManager() {
- return (manager_);
- }
-
- /// Hook indexes - these are frequently accessed, so are accessed directly.
- int alpha_index_;
- int beta_index_;
- int gamma_index_;
- int delta_index_;
-
- /// String accessible by all callouts whatever the library
- static std::string common_string_;
-
-private:
- /// Server hooks
- boost::shared_ptr<ServerHooks> hooks_;
-
- /// Callout manager. Declared static so that the callout functions can
- /// access it.
- boost::shared_ptr<CalloutManager> manager_;
-};
-
-/// Define the common string
-std::string HandlesTest::common_string_;
-
-
-// The next set of functions define the callouts used by the tests. They
-// manipulate the data in such a way that callouts called - and the order in
-// which they were called - can be determined. The functions also check that
-// the "callout context" data areas are separate.
-//
-// Three libraries are assumed, and each supplies four callouts. All callouts
-// manipulate two context elements the CalloutHandle, the elements being called
-// "string" and "int" (which describe the type of data manipulated).
-//
-// For the string item, each callout shifts data to the left and inserts its own
-// data. The data is a string of the form "nmc", where "n" is the number of
-// the library, "m" is the callout number and "y" is the indication of what
-// callout handle was passed as an argument ("1" or "2": "0" is used when no
-// identification has been set in the callout handle).
-//
-// For simplicity, and to cut down the number of functions actually written,
-// the callout indicator ("1" or "2") ) used in the in the CalloutHandle
-// functions is passed via a CalloutArgument. The argument is named "string":
-// use of a name the same as that of one of the context elements serves as a
-// check that the argument name space and argument context space are separate.
-//
-// For integer data, the value starts at zero and an increment is added on each
-// call. This increment is equal to:
-//
-// 100 * library number + 10 * callout number + callout handle
-//
-// Although this gives less information than the string value, the reasons for
-// using it are:
-//
-// - It is a separate item in the context, so checks that the context can
-// handle multiple items.
-// - It provides an item that can be deleted by the context deletion
-// methods.
-
-
-// Values set in the CalloutHandle context. There are three libraries, so
-// there are three contexts for the callout, one for each library.
-
-std::string& resultCalloutString(int index) {
- static std::string result_callout_string[3];
- return (result_callout_string[index]);
-}
-
-int& resultCalloutInt(int index) {
- static int result_callout_int[3];
- return (result_callout_int[index]);
-}
-
-// A simple function to zero the results.
-
-static void zero_results() {
- for (int i = 0; i < 3; ++i) {
- resultCalloutString(i) = "";
- resultCalloutInt(i) = 0;
- }
-}
-
-
-// Library callouts.
-
-// Common code for setting the callout context values.
-
-int
-execute(CalloutHandle& callout_handle, int library_num, int callout_num) {
-
- // Obtain the callout handle number
- int handle_num = 0;
- try {
- callout_handle.getArgument("handle_num", handle_num);
- } catch (const NoSuchArgument&) {
- // handle_num argument not set: this is the case in the tests where
- // the context_create hook check is tested.
- handle_num = 0;
- }
-
- // Create the basic data to be appended to the context value.
- int idata = 100 * library_num + 10 * callout_num + handle_num;
- string sdata = boost::lexical_cast<string>(idata);
-
- // Get the context data. As before, this will not exist for the first
- // callout called. (In real life, the library should create it when the
- // "context_create" hook gets called before any packet processing takes
- // place.)
- int int_value = 0;
- try {
- callout_handle.getContext("int", int_value);
- } catch (const NoSuchCalloutContext&) {
- int_value = 0;
- }
-
- string string_value = "";
- try {
- callout_handle.getContext("string", string_value);
- } catch (const NoSuchCalloutContext&) {
- string_value = "";
- }
-
- // Update the values and set them back in the callout context.
- int_value += idata;
- callout_handle.setContext("int", int_value);
-
- string_value += sdata;
- callout_handle.setContext("string", string_value);
-
- return (0);
-}
-
-// The following functions are the actual callouts - the name is of the
-// form "callout_<library number>_<callout number>"
-
-int
-callout11(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 1, 1));
-}
-
-int
-callout12(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 1, 2));
-}
-
-int
-callout13(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 1, 3));
-}
-
-int
-callout21(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 2, 1));
-}
-
-int
-callout22(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 2, 2));
-}
-
-int
-callout23(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 2, 3));
-}
-
-int
-callout31(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 3, 1));
-}
-
-int
-callout32(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 3, 2));
-}
-
-int
-callout33(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 3, 3));
-}
-
-// Common callout code for the fourth hook (which makes the data available for
-// checking). It copies the library and callout context data to the global
-// variables.
-
-int printExecute(CalloutHandle& callout_handle, int library_num) {
- callout_handle.getContext("string", resultCalloutString(library_num - 1));
- callout_handle.getContext("int", resultCalloutInt(library_num - 1));
-
- return (0);
-}
-
-// These are the actual callouts.
-
-int
-print1(CalloutHandle& callout_handle) {
- return (printExecute(callout_handle, 1));
-}
-
-int
-print2(CalloutHandle& callout_handle) {
- return (printExecute(callout_handle, 2));
-}
-
-int
-print3(CalloutHandle& callout_handle) {
- return (printExecute(callout_handle, 3));
-}
-
-// This test checks the many-faced nature of the context for the CalloutContext.
-
-TEST_F(HandlesTest, ContextAccessCheck) {
- // Register callouts for the different libraries.
- CalloutHandle handle(getCalloutManager());
-
- // Library 0.
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout11);
- getCalloutManager()->registerCallout("beta", callout12);
- getCalloutManager()->registerCallout("gamma", callout13);
- getCalloutManager()->registerCallout("delta", print1);
-
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout21);
- getCalloutManager()->registerCallout("beta", callout22);
- getCalloutManager()->registerCallout("gamma", callout23);
- getCalloutManager()->registerCallout("delta", print2);
-
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout31);
- getCalloutManager()->registerCallout("beta", callout32);
- getCalloutManager()->registerCallout("gamma", callout33);
- getCalloutManager()->registerCallout("delta", print3);
-
- // Create the callout handles and distinguish them by setting the
- // "handle_num" argument.
- CalloutHandle callout_handle_1(getCalloutManager());
- callout_handle_1.setArgument("handle_num", static_cast<int>(1));
-
- CalloutHandle callout_handle_2(getCalloutManager());
- callout_handle_2.setArgument("handle_num", static_cast<int>(2));
-
- // Now call the callouts attached to the first three hooks. Each hook is
- // called twice (once for each callout handle) before the next hook is
- // called.
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
- getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
- getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
- getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
- getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
-
- // Get the results for each callout (the callout on hook "delta" copies
- // the context values into a location the test can access). Explicitly
- // zero the variables before getting the results so we are certain that
- // the values are the results of the callouts.
-
- zero_results();
-
- // To explain the expected callout context results.
- //
- // Each callout handle maintains a separate context for each library. When
- // the first call to callCallouts() is made, "111" gets appended to
- // the context for library 1 maintained by the first callout handle, "211"
- // gets appended to the context maintained for library 2, and "311" to
- // the context maintained for library 3. In each case, the first digit
- // corresponds to the library number, the second to the callout number and
- // the third to the "handle_num" of the callout handle. For the first call
- // to callCallouts, handle 1 is used, so the last digit is always 1.
- //
- // The next call to callCallouts() calls the same callouts but for the
- // second callout handle. It also maintains three contexts (one for
- // each library) and they will get "112", "212", "312" appended to
- // them. The explanation for the digits is the same as before, except that
- // in this case, the callout handle is number 2, so the third digit is
- // always 2. These additions don't affect the contexts maintained by
- // callout handle 1.
- //
- // The process is then repeated for hooks "beta" and "gamma" which, for
- // callout handle 1, append "121", "221" and "321" for hook "beta" and
- // "311", "321" and "331" for hook "gamma".
- //
- // The expected integer values can be found by summing up the values
- // corresponding to the elements of the strings.
-
- // At this point, we have only called the "print" function for callout
- // handle "1", so the following results are checking the context values
- // maintained in that callout handle.
-
- getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
- EXPECT_EQ("111121131", resultCalloutString(0));
- EXPECT_EQ("211221231", resultCalloutString(1));
- EXPECT_EQ("311321331", resultCalloutString(2));
-
- EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
- EXPECT_EQ((211 + 221 + 231), resultCalloutInt(1));
- EXPECT_EQ((311 + 321 + 331), resultCalloutInt(2));
-
- // Repeat the checks for callout 2.
-
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
-
- EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
- EXPECT_EQ((212 + 222 + 232), resultCalloutInt(1));
- EXPECT_EQ((312 + 322 + 332), resultCalloutInt(2));
-
- EXPECT_EQ("112122132", resultCalloutString(0));
- EXPECT_EQ("212222232", resultCalloutString(1));
- EXPECT_EQ("312322332", resultCalloutString(2));
-}
-
-// Now repeat the test, but add a deletion callout to the list. The "beta"
-// hook of library 2 will have an additional callout to delete the "int"
-// element: the same hook for library 3 will delete both elements. In
-// addition, the names of context elements for the libraries at this point
-// will be printed.
-
-// List of context item names.
-
-vector<string>&
-getItemNames(int index) {
- static vector<string> context_items[3];
- return (context_items[index]);
-}
-
-// Context item deletion functions.
-
-int
-deleteIntContextItem(CalloutHandle& handle) {
- handle.deleteContext("int");
- return (0);
-}
-
-int
-deleteAllContextItems(CalloutHandle& handle) {
- handle.deleteAllContext();
- return (0);
-}
-
-// Generic print function - copy names in sorted order.
-
-int
-printContextNamesExecute(CalloutHandle& handle, int library_num) {
- const int index = library_num - 1;
- getItemNames(index) = handle.getContextNames();
- sort(getItemNames(index).begin(), getItemNames(index).end());
- return (0);
-}
-
-int
-printContextNames1(CalloutHandle& handle) {
- return (printContextNamesExecute(handle, 1));
-}
-
-int
-printContextNames2(CalloutHandle& handle) {
- return (printContextNamesExecute(handle, 2));
-}
-
-int
-printContextNames3(CalloutHandle& handle) {
- return (printContextNamesExecute(handle, 3));
-}
-
-// Perform the test including deletion of context items.
-
-TEST_F(HandlesTest, ContextDeletionCheck) {
-
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout11);
- getCalloutManager()->registerCallout("beta", callout12);
- getCalloutManager()->registerCallout("beta", printContextNames1);
- getCalloutManager()->registerCallout("gamma", callout13);
- getCalloutManager()->registerCallout("delta", print1);
-
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout21);
- getCalloutManager()->registerCallout("beta", callout22);
- getCalloutManager()->registerCallout("beta", deleteIntContextItem);
- getCalloutManager()->registerCallout("beta", printContextNames2);
- getCalloutManager()->registerCallout("gamma", callout23);
- getCalloutManager()->registerCallout("delta", print2);
-
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout31);
- getCalloutManager()->registerCallout("beta", callout32);
- getCalloutManager()->registerCallout("beta", deleteAllContextItems);
- getCalloutManager()->registerCallout("beta", printContextNames3);
- getCalloutManager()->registerCallout("gamma", callout33);
- getCalloutManager()->registerCallout("delta", print3);
-
- // Create the callout handles and distinguish them by setting the "long"
- // argument.
- CalloutHandle callout_handle_1(getCalloutManager());
- callout_handle_1.setArgument("handle_num", static_cast<int>(1));
-
- CalloutHandle callout_handle_2(getCalloutManager());
- callout_handle_2.setArgument("handle_num", static_cast<int>(2));
-
- // Now call the callouts attached to the first three hooks. Each hook is
- // called twice (once for each callout handle) before the next hook is
- // called.
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
- getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
- getCalloutManager()->callCallouts(beta_index_, callout_handle_2);
- getCalloutManager()->callCallouts(gamma_index_, callout_handle_1);
- getCalloutManager()->callCallouts(gamma_index_, callout_handle_2);
-
- // Get the results for each callout. Explicitly zero the variables before
- // getting the results so we are certain that the values are the results
- // of the callouts.
-
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
-
- // The logic by which the expected results are arrived at is described
- // in the ContextAccessCheck test. The results here are different
- // because context items have been modified along the way.
-
- EXPECT_EQ((111 + 121 + 131), resultCalloutInt(0));
- EXPECT_EQ(( 231), resultCalloutInt(1));
- EXPECT_EQ(( 331), resultCalloutInt(2));
-
- EXPECT_EQ("111121131", resultCalloutString(0));
- EXPECT_EQ("211221231", resultCalloutString(1));
- EXPECT_EQ( "331", resultCalloutString(2));
-
- // Repeat the checks for callout handle 2.
-
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
-
- EXPECT_EQ((112 + 122 + 132), resultCalloutInt(0));
- EXPECT_EQ(( 232), resultCalloutInt(1));
- EXPECT_EQ(( 332), resultCalloutInt(2));
-
- EXPECT_EQ("112122132", resultCalloutString(0));
- EXPECT_EQ("212222232", resultCalloutString(1));
- EXPECT_EQ( "332", resultCalloutString(2));
-
- // ... and check what the names of the context items are after the callouts
- // for hook "beta". We know they are in sorted order.
-
- EXPECT_EQ(2, getItemNames(0).size());
- EXPECT_EQ(string("int"), getItemNames(0)[0]);
- EXPECT_EQ(string("string"), getItemNames(0)[1]);
-
- EXPECT_EQ(1, getItemNames(1).size());
- EXPECT_EQ(string("string"), getItemNames(1)[0]);
-
- EXPECT_EQ(0, getItemNames(2).size());
-}
-
-// Tests that the CalloutHandle's constructor and destructor call the
-// context_create and context_destroy callbacks (if registered). For
-// simplicity, we'll use the same callout functions as used above.
-
-TEST_F(HandlesTest, ConstructionDestructionCallouts) {
-
- // Register context callouts.
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("context_create", callout11);
- getCalloutManager()->registerCallout("context_create", print1);
- getCalloutManager()->registerCallout("context_destroy", callout12);
- getCalloutManager()->registerCallout("context_destroy", print1);
-
- // Create the CalloutHandle and check that the constructor callout
- // has run.
- zero_results();
- boost::scoped_ptr<CalloutHandle>
- callout_handle(new CalloutHandle(getCalloutManager()));
- EXPECT_EQ("110", resultCalloutString(0));
- EXPECT_EQ(110, resultCalloutInt(0));
-
- // Check that the destructor callout runs. Note that the "print1" callout
- // didn't destroy the library context - it only copied it to where it
- // could be examined. As a result, the destructor callout appends its
- // elements to the constructor's values and the result is printed.
- zero_results();
- callout_handle.reset();
-
- EXPECT_EQ("110120", resultCalloutString(0));
- EXPECT_EQ((110 + 120), resultCalloutInt(0));
-}
-
-// Dynamic callout registration and deregistration.
-// The following are the dynamic registration/deregistration callouts.
-
-
-// Add callout_78_alpha - adds a callout to hook alpha that appends "78x"
-// (where "x" is the callout handle) to the current output.
-
-int
-callout78(CalloutHandle& callout_handle) {
- return (execute(callout_handle, 7, 8));
-}
-
-int
-add_callout78_alpha(CalloutHandle& callout_handle) {
- callout_handle.getLibraryHandle().registerCallout("alpha", callout78);
- return (0);
-}
-
-int
-delete_callout78_alpha(CalloutHandle& callout_handle) {
- static_cast<void>(
- callout_handle.getLibraryHandle().deregisterCallout("alpha",
- callout78));
- return (0);
-}
-
-// Check that a callout can register another callout on a different hook.
-
-TEST_F(HandlesTest, DynamicRegistrationAnotherHook) {
- // Register callouts for the different libraries.
- CalloutHandle handle(getCalloutManager());
-
- // Set up callouts on "alpha".
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout11);
- getCalloutManager()->registerCallout("delta", print1);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout21);
- getCalloutManager()->registerCallout("delta", print2);
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", callout31);
- getCalloutManager()->registerCallout("delta", print3);
-
- // ... and on "beta", set up the function to add a hook to alpha (but only
- // for library 1).
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("beta", add_callout78_alpha);
-
- // See what we get for calling the callouts on alpha first.
- CalloutHandle callout_handle_1(getCalloutManager());
- callout_handle_1.setArgument("handle_num", static_cast<int>(1));
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
-
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
- EXPECT_EQ("111", resultCalloutString(0));
- EXPECT_EQ("211", resultCalloutString(1));
- EXPECT_EQ("311", resultCalloutString(2));
-
- // All as expected, now call the callouts on beta. This should add a
- // callout to the list of callouts for alpha, which we should see when
- // we run the test again.
- getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
-
- // Use a new callout handle so as to get fresh callout context.
- CalloutHandle callout_handle_2(getCalloutManager());
- callout_handle_2.setArgument("handle_num", static_cast<int>(2));
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
-
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
- EXPECT_EQ("112", resultCalloutString(0));
- EXPECT_EQ("212782", resultCalloutString(1));
- EXPECT_EQ("312", resultCalloutString(2));
-}
-
-// Check that a callout can register another callout on the same hook.
-// Note that the registration only applies to a subsequent invocation of
-// callCallouts, not to the current one. In other words, if
-//
-// * the callout list for a library is "A then B then C"
-// * when callCallouts is executed "B" adds "D" to that list,
-//
-// ... the current execution of callCallouts only executes A, B and C. A
-// subsequent invocation will execute A, B, C then D.
-
-TEST_F(HandlesTest, DynamicRegistrationSameHook) {
- // Register callouts for the different libraries.
- CalloutHandle handle(getCalloutManager());
-
- // Set up callouts on "alpha".
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout11);
- getCalloutManager()->registerCallout("alpha", add_callout78_alpha);
- getCalloutManager()->registerCallout("delta", print1);
-
- // See what we get for calling the callouts on alpha first.
- CalloutHandle callout_handle_1(getCalloutManager());
- callout_handle_1.setArgument("handle_num", static_cast<int>(1));
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
- EXPECT_EQ("111", resultCalloutString(0));
-
- // Run it again - we should have added something to this hook.
- CalloutHandle callout_handle_2(getCalloutManager());
- callout_handle_2.setArgument("handle_num", static_cast<int>(2));
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
- EXPECT_EQ("112782", resultCalloutString(0));
-
- // And a third time...
- CalloutHandle callout_handle_3(getCalloutManager());
- callout_handle_3.setArgument("handle_num", static_cast<int>(3));
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_3);
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_3);
- EXPECT_EQ("113783783", resultCalloutString(0));
-}
-
-// Deregistration of a callout from a different hook
-
-TEST_F(HandlesTest, DynamicDeregistrationDifferentHook) {
- // Register callouts for the different libraries.
- CalloutHandle handle(getCalloutManager());
-
- // Set up callouts on "alpha".
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout11);
- getCalloutManager()->registerCallout("alpha", callout78);
- getCalloutManager()->registerCallout("alpha", callout11);
- getCalloutManager()->registerCallout("delta", print1);
-
- getCalloutManager()->registerCallout("beta", delete_callout78_alpha);
-
- // Call the callouts on alpha
- CalloutHandle callout_handle_1(getCalloutManager());
- callout_handle_1.setArgument("handle_num", static_cast<int>(1));
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
-
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
- EXPECT_EQ("111781111", resultCalloutString(0));
-
- // Run the callouts on hook beta to remove the callout on alpha.
- getCalloutManager()->callCallouts(beta_index_, callout_handle_1);
-
- // The run of the callouts should have altered the callout list on the
- // first library for hook alpha, so call again to make sure.
- CalloutHandle callout_handle_2(getCalloutManager());
- callout_handle_2.setArgument("handle_num", static_cast<int>(2));
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
-
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
- EXPECT_EQ("112112", resultCalloutString(0));
-}
-
-// Deregistration of a callout from the same hook
-
-TEST_F(HandlesTest, DynamicDeregistrationSameHook) {
- // Register callouts for the different libraries.
- CalloutHandle handle(getCalloutManager());
-
- // Set up callouts on "alpha".
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", callout11);
- getCalloutManager()->registerCallout("alpha", delete_callout78_alpha);
- getCalloutManager()->registerCallout("alpha", callout78);
- getCalloutManager()->registerCallout("delta", print1);
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", callout21);
- getCalloutManager()->registerCallout("alpha", callout78);
- getCalloutManager()->registerCallout("delta", print2);
-
- // Call the callouts on alpha
- CalloutHandle callout_handle_1(getCalloutManager());
- callout_handle_1.setArgument("handle_num", static_cast<int>(1));
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_1);
-
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_1);
- EXPECT_EQ("111781", resultCalloutString(0));
- EXPECT_EQ("211781", resultCalloutString(1));
-
- // The run of the callouts should have altered the callout list on the
- // first library for hook alpha, so call again to make sure.
- CalloutHandle callout_handle_2(getCalloutManager());
- callout_handle_2.setArgument("handle_num", static_cast<int>(2));
- getCalloutManager()->callCallouts(alpha_index_, callout_handle_2);
-
- zero_results();
- getCalloutManager()->callCallouts(delta_index_, callout_handle_2);
- EXPECT_EQ("112", resultCalloutString(0));
- EXPECT_EQ("212782", resultCalloutString(1));
-}
-
-// Testing the operation of the "skip" flag. Callouts print the value
-// they see in the flag and either leave it unchanged, set it or clear it.
-
-int
-calloutPrintSkip(CalloutHandle& handle) {
- static const std::string YES("Y");
- static const std::string NO("N");
-
- HandlesTest::common_string_ = HandlesTest::common_string_ +
- (handle.getSkip() ? YES : NO);
- return (0);
-}
-
-int
-calloutSetSkip(CalloutHandle& handle) {
- static_cast<void>(calloutPrintSkip(handle));
- handle.setSkip(true);
- return (0);
-}
-
-int
-calloutClearSkip(CalloutHandle& handle) {
- static_cast<void>(calloutPrintSkip(handle));
- handle.setSkip(false);
- return (0);
-}
-
-// Do a series of tests, returning with the skip flag set "true".
-
-TEST_F(HandlesTest, ReturnSkipSet) {
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
-
- CalloutHandle callout_handle(getCalloutManager());
- getCalloutManager()->callCallouts(alpha_index_, callout_handle);
-
- // Check result. For each of visual checking, the expected string is
- // divided into sections corresponding to the blocks of callouts above.
- EXPECT_EQ(std::string("NNYY" "NNYYN" "NNYN"), common_string_);
-
- // ... and check that the skip flag on exit from callCallouts is set.
- EXPECT_TRUE(callout_handle.getSkip());
-}
-
-// Repeat the test, returning with the skip flag clear.
-TEST_F(HandlesTest, ReturnSkipClear) {
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
- getCalloutManager()->registerCallout("alpha", calloutPrintSkip);
- getCalloutManager()->registerCallout("alpha", calloutSetSkip);
- getCalloutManager()->registerCallout("alpha", calloutClearSkip);
-
- CalloutHandle callout_handle(getCalloutManager());
- getCalloutManager()->callCallouts(alpha_index_, callout_handle);
-
- // Check result. For each of visual checking, the expected string is
- // divided into sections corresponding to the blocks of callouts above.
- EXPECT_EQ(std::string("NYY" "NNYNYN" "NNNY"), common_string_);
-
- // ... and check that the skip flag on exit from callCallouts is set.
- EXPECT_FALSE(callout_handle.getSkip());
-}
-
-// The next set of callouts do a similar thing to the above "skip" tests,
-// but alter the value of a string argument. This is for testing that the
-// a callout is able to change an argument and return it to the caller.
-
-const char* MODIFIED_ARG = "modified_arg";
-
-int
-calloutSetArgumentCommon(CalloutHandle& handle, const char* what) {
- std::string modified_arg = "";
-
- handle.getArgument(MODIFIED_ARG, modified_arg);
- modified_arg = modified_arg + std::string(what);
- handle.setArgument(MODIFIED_ARG, modified_arg);
- return (0);
-}
-
-int
-calloutSetArgumentYes(CalloutHandle& handle) {
- return (calloutSetArgumentCommon(handle, "Y"));
-}
-
-int
-calloutSetArgumentNo(CalloutHandle& handle) {
- return (calloutSetArgumentCommon(handle, "N"));
-}
-
-// ... and a callout to just copy the argument to the "common_string_" variable
-// but otherwise not alter it.
-
-int
-calloutPrintArgument(CalloutHandle& handle) {
- handle.getArgument(MODIFIED_ARG, HandlesTest::common_string_);
- return (0);
-}
-
-TEST_F(HandlesTest, CheckModifiedArgument) {
- getCalloutManager()->setLibraryIndex(0);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
-
- getCalloutManager()->setLibraryIndex(1);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
- getCalloutManager()->registerCallout("alpha", calloutPrintArgument);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
-
- getCalloutManager()->setLibraryIndex(2);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentNo);
- getCalloutManager()->registerCallout("alpha", calloutSetArgumentYes);
-
- // Create the argument with an initial empty string value. Then call the
- // sequence of callouts above.
- CalloutHandle callout_handle(getCalloutManager());
- std::string modified_arg = "";
- callout_handle.setArgument(MODIFIED_ARG, modified_arg);
- getCalloutManager()->callCallouts(alpha_index_, callout_handle);
-
- // Check the intermediate and results. For visual checking, the expected
- // string is divided into sections corresponding to the blocks of callouts
- // above.
- EXPECT_EQ(std::string("YNN" "YY"), common_string_);
-
- callout_handle.getArgument(MODIFIED_ARG, modified_arg);
- EXPECT_EQ(std::string("YNN" "YYNN" "YNY"), modified_arg);
-}
-
-
-} // Anonymous namespace
-
diff --git a/src/lib/util/tests/server_hooks_unittest.cc b/src/lib/util/tests/server_hooks_unittest.cc
deleted file mode 100644
index f029ad2..0000000
--- a/src/lib/util/tests/server_hooks_unittest.cc
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-#include <util/hooks/server_hooks.h>
-
-#include <gtest/gtest.h>
-
-#include <algorithm>
-#include <string>
-#include <vector>
-
-using namespace isc;
-using namespace isc::util;
-using namespace std;
-
-namespace {
-
-// Checks the registration of hooks and the interrogation methods. As the
-// constructor registers two hooks, this is also a test of the constructor.
-
-TEST(ServerHooksTest, RegisterHooks) {
- ServerHooks hooks;
-
- // There should be two hooks already registered, with indexes 0 and 1.
- EXPECT_EQ(2, hooks.getCount());
- EXPECT_EQ(0, hooks.getIndex("context_create"));
- EXPECT_EQ(1, hooks.getIndex("context_destroy"));
-
- // Check that the constants are as expected. (The intermediate variables
- // are used because of problems with g++ 4.6.1/Ubuntu 11.10 when resolving
- // the value of the ServerHooks constants when they appeared within the
- // gtest macro.)
- const int create_value = ServerHooks::CONTEXT_CREATE;
- const int destroy_value = ServerHooks::CONTEXT_DESTROY;
- EXPECT_EQ(0, create_value);
- EXPECT_EQ(1, destroy_value);
-
- // Register another couple of hooks. The test on returned index is based
- // on knowledge that the hook indexes are assigned in ascending order.
- int alpha = hooks.registerHook("alpha");
- EXPECT_EQ(2, alpha);
- EXPECT_EQ(2, hooks.getIndex("alpha"));
-
- int beta = hooks.registerHook("beta");
- EXPECT_EQ(3, beta);
- EXPECT_EQ(3, hooks.getIndex("beta"));
-
- // Should be four hooks now
- EXPECT_EQ(4, hooks.getCount());
-}
-
-// Check that duplicate names cannot be registered.
-
-TEST(ServerHooksTest, DuplicateHooks) {
- ServerHooks hooks;
-
- // Ensure we can't duplicate one of the existing names.
- EXPECT_THROW(hooks.registerHook("context_create"), DuplicateHook);
-
- // Check we can't duplicate a newly registered hook.
- int gamma = hooks.registerHook("gamma");
- EXPECT_EQ(2, gamma);
- EXPECT_THROW(hooks.registerHook("gamma"), DuplicateHook);
-}
-
-// Checks that we can get the name of the hooks.
-
-TEST(ServerHooksTest, GetHookNames) {
- vector<string> expected_names;
- ServerHooks hooks;
-
- // Add names into the hooks object and to the set of expected names.
- expected_names.push_back("alpha");
- expected_names.push_back("beta");
- expected_names.push_back("gamma");
- expected_names.push_back("delta");
- for (int i = 0; i < expected_names.size(); ++i) {
- hooks.registerHook(expected_names[i].c_str());
- };
-
- // Update the expected names to include the pre-defined hook names.
- expected_names.push_back("context_create");
- expected_names.push_back("context_destroy");
-
- // Get the actual hook names
- vector<string> actual_names = hooks.getHookNames();
-
- // For comparison, sort the names into alphabetical order and do a straight
- // vector comparison.
- sort(expected_names.begin(), expected_names.end());
- sort(actual_names.begin(), actual_names.end());
-
- EXPECT_TRUE(expected_names == actual_names);
-}
-
-// Check that getting an unknown name throws an exception.
-
-TEST(ServerHooksTest, UnknownHookName) {
- ServerHooks hooks;
-
- EXPECT_THROW(static_cast<void>(hooks.getIndex("unknown")), NoSuchHook);
-}
-
-// Check that the count of hooks is correct.
-
-TEST(ServerHooksTest, HookCount) {
- ServerHooks hooks;
-
- // Insert the names into the hooks object
- hooks.registerHook("alpha");
- hooks.registerHook("beta");
- hooks.registerHook("gamma");
- hooks.registerHook("delta");
-
- // Should be two more hooks that the number we have registered.
- EXPECT_EQ(6, hooks.getCount());
-}
-
-// HookRegistrationFunction tests
-
-// Declare some hook registration functions.
-
-int alpha = 0;
-int beta = 0;
-int gamma = 0;
-int delta = 0;
-
-void registerAlphaBeta(ServerHooks& hooks) {
- alpha = hooks.registerHook("alpha");
- beta = hooks.registerHook("beta");
-}
-
-void registerGammaDelta(ServerHooks& hooks) {
- gamma = hooks.registerHook("gamma");
- delta = hooks.registerHook("delta");
-}
-
-// Add them to the registration vector. This addition should happen before
-// any tests are run, so we should start off with two functions in the
-// registration vector.
-
-HookRegistrationFunction f1(registerAlphaBeta);
-HookRegistrationFunction f2(registerGammaDelta);
-
-// This is not registered statically: it is used in the latter part of the
-// test.
-
-int epsilon = 0;
-void registerEpsilon(ServerHooks& hooks) {
- epsilon = hooks.registerHook("epsilon");
-}
-
-// Test that the registration functions were defined and can be executed.
-
-TEST(HookRegistrationFunction, Registration) {
-
- // The first part of the tests checks the static registration. As there
- // is only one list of registration functions, we have to do this first
- // as the static registration is done outside our control, before the
- // tests are loaded.
-
- // Ensure that the hook numbers are initialized.
- EXPECT_EQ(0, alpha);
- EXPECT_EQ(0, beta);
- EXPECT_EQ(0, gamma);
- EXPECT_EQ(0, delta);
-
- // Should have two hook registration functions registered.
- EXPECT_EQ(2, HookRegistrationFunction::getFunctionVector().size());
-
- // Execute the functions and check that four new hooks were defined (two
- // from each function).
- ServerHooks hooks;
- EXPECT_EQ(2, hooks.getCount());
- HookRegistrationFunction::execute(hooks);
- EXPECT_EQ(6, hooks.getCount());
-
- // Check the hook names are as expected.
- vector<string> names = hooks.getHookNames();
- ASSERT_EQ(6, names.size());
- sort(names.begin(), names.end());
- EXPECT_EQ(string("alpha"), names[0]);
- EXPECT_EQ(string("beta"), names[1]);
- EXPECT_EQ(string("context_create"), names[2]);
- EXPECT_EQ(string("context_destroy"), names[3]);
- EXPECT_EQ(string("delta"), names[4]);
- EXPECT_EQ(string("gamma"), names[5]);
-
- // Check that numbers in the range 2-5 inclusive were assigned as the
- // hook indexes (0 and 1 being reserved for context_create and
- // context_destroy).
- vector<int> indexes;
- indexes.push_back(alpha);
- indexes.push_back(beta);
- indexes.push_back(gamma);
- indexes.push_back(delta);
- sort(indexes.begin(), indexes.end());
- EXPECT_EQ(2, indexes[0]);
- EXPECT_EQ(3, indexes[1]);
- EXPECT_EQ(4, indexes[2]);
- EXPECT_EQ(5, indexes[3]);
-
- // One last check. We'll test that the constructor of does indeed
- // add a function to the function vector and that the static initialization
- // was not somehow by chance.
- HookRegistrationFunction::getFunctionVector().clear();
- EXPECT_TRUE(HookRegistrationFunction::getFunctionVector().empty());
- epsilon = 0;
-
- // Register a single registration function.
- HookRegistrationFunction f3(registerEpsilon);
- EXPECT_EQ(1, HookRegistrationFunction::getFunctionVector().size());
-
- // Execute it...
- ServerHooks hooks2;
- EXPECT_EQ(0, epsilon);
- EXPECT_EQ(2, hooks2.getCount());
- HookRegistrationFunction::execute(hooks2);
-
- // There should be three hooks, with the new one assigned an index of 2.
- names = hooks2.getHookNames();
- ASSERT_EQ(3, names.size());
- sort(names.begin(), names.end());
- EXPECT_EQ(string("context_create"), names[0]);
- EXPECT_EQ(string("context_destroy"), names[1]);
- EXPECT_EQ(string("epsilon"), names[2]);
-
- EXPECT_EQ(2, epsilon);
-}
-
-} // Anonymous namespace
More information about the bind10-changes
mailing list