summaryrefslogtreecommitdiffstats
path: root/src/lib/hooks
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/hooks')
-rw-r--r--src/lib/hooks/Makefile.am104
-rw-r--r--src/lib/hooks/Makefile.in1140
-rw-r--r--src/lib/hooks/callout_handle.cc167
-rw-r--r--src/lib/hooks/callout_handle.h512
-rw-r--r--src/lib/hooks/callout_handle_associate.cc34
-rw-r--r--src/lib/hooks/callout_handle_associate.h63
-rw-r--r--src/lib/hooks/callout_manager.cc348
-rw-r--r--src/lib/hooks/callout_manager.h438
-rw-r--r--src/lib/hooks/hooks.h71
-rw-r--r--src/lib/hooks/hooks_component_developer.dox520
-rw-r--r--src/lib/hooks/hooks_config.cc127
-rw-r--r--src/lib/hooks/hooks_config.h108
-rw-r--r--src/lib/hooks/hooks_log.cc28
-rw-r--r--src/lib/hooks/hooks_log.h49
-rw-r--r--src/lib/hooks/hooks_maintenance.dox381
-rw-r--r--src/lib/hooks/hooks_manager.cc275
-rw-r--r--src/lib/hooks/hooks_manager.h499
-rw-r--r--src/lib/hooks/hooks_messages.cc93
-rw-r--r--src/lib/hooks/hooks_messages.h50
-rw-r--r--src/lib/hooks/hooks_messages.mes201
-rw-r--r--src/lib/hooks/hooks_parser.cc110
-rw-r--r--src/lib/hooks/hooks_parser.h65
-rw-r--r--src/lib/hooks/hooks_user.dox1670
-rw-r--r--src/lib/hooks/images/DataScopeArgument.diabin0 -> 1887 bytes
-rw-r--r--src/lib/hooks/images/DataScopeArgument.pngbin0 -> 11672 bytes
-rw-r--r--src/lib/hooks/images/DataScopeContext.diabin0 -> 2161 bytes
-rw-r--r--src/lib/hooks/images/DataScopeContext.pngbin0 -> 14180 bytes
-rw-r--r--src/lib/hooks/images/HooksUml.diabin0 -> 2354 bytes
-rw-r--r--src/lib/hooks/images/HooksUml.pngbin0 -> 13841 bytes
-rw-r--r--src/lib/hooks/libinfo.cc30
-rw-r--r--src/lib/hooks/libinfo.h42
-rw-r--r--src/lib/hooks/library_handle.cc131
-rw-r--r--src/lib/hooks/library_handle.h242
-rw-r--r--src/lib/hooks/library_manager.cc435
-rw-r--r--src/lib/hooks/library_manager.h244
-rw-r--r--src/lib/hooks/library_manager_collection.cc153
-rw-r--r--src/lib/hooks/library_manager_collection.h187
-rw-r--r--src/lib/hooks/parking_lots.h426
-rw-r--r--src/lib/hooks/pointer_converter.h122
-rw-r--r--src/lib/hooks/server_hooks.cc221
-rw-r--r--src/lib/hooks/server_hooks.h246
-rw-r--r--src/lib/hooks/tests/Makefile.am153
-rw-r--r--src/lib/hooks/tests/Makefile.in1525
-rw-r--r--src/lib/hooks/tests/async_callout_library.cc153
-rw-r--r--src/lib/hooks/tests/basic_callout_library.cc145
-rw-r--r--src/lib/hooks/tests/callout_handle_associate_unittest.cc35
-rw-r--r--src/lib/hooks/tests/callout_handle_unittest.cc384
-rw-r--r--src/lib/hooks/tests/callout_manager_unittest.cc877
-rw-r--r--src/lib/hooks/tests/callout_params_library.cc129
-rw-r--r--src/lib/hooks/tests/common_test_class.h174
-rw-r--r--src/lib/hooks/tests/framework_exception_library.cc41
-rw-r--r--src/lib/hooks/tests/full_callout_library.cc177
-rw-r--r--src/lib/hooks/tests/handles_unittest.cc777
-rw-r--r--src/lib/hooks/tests/hooks_manager_unittest.cc1081
-rw-r--r--src/lib/hooks/tests/incorrect_version_library.cc26
-rw-r--r--src/lib/hooks/tests/library_manager_collection_unittest.cc314
-rw-r--r--src/lib/hooks/tests/library_manager_unittest.cc741
-rw-r--r--src/lib/hooks/tests/load_callout_library.cc152
-rw-r--r--src/lib/hooks/tests/load_error_callout_library.cc47
-rw-r--r--src/lib/hooks/tests/marker_file.h.in19
-rw-r--r--src/lib/hooks/tests/no_version_library.cc24
-rw-r--r--src/lib/hooks/tests/parking_lots_unittest.cc339
-rw-r--r--src/lib/hooks/tests/run_unittests.cc19
-rw-r--r--src/lib/hooks/tests/server_hooks_unittest.cc232
-rw-r--r--src/lib/hooks/tests/test_libraries.h.in61
-rw-r--r--src/lib/hooks/tests/unload_callout_library.cc46
66 files changed, 17203 insertions, 0 deletions
diff --git a/src/lib/hooks/Makefile.am b/src/lib/hooks/Makefile.am
new file mode 100644
index 0000000..da8c7a7
--- /dev/null
+++ b/src/lib/hooks/Makefile.am
@@ -0,0 +1,104 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+# Ensure that the message file is included in the distribution
+EXTRA_DIST = hooks_messages.mes
+
+# Include developer's guide files
+EXTRA_DIST += hooks_user.dox hooks_maintenance.dox hooks_component_developer.dox
+
+# Include images used in Developer's guide
+EXTRA_DIST += images/DataScopeArgument.dia images/DataScopeArgument.png
+EXTRA_DIST += images/DataScopeContext.dia images/DataScopeContext.png
+EXTRA_DIST += images/HooksUml.dia images/HooksUml.png
+
+CLEANFILES = *.gcno *.gcda
+
+lib_LTLIBRARIES = libkea-hooks.la
+libkea_hooks_la_SOURCES =
+libkea_hooks_la_SOURCES += callout_handle.cc callout_handle.h
+libkea_hooks_la_SOURCES += callout_handle_associate.cc callout_handle_associate.h
+libkea_hooks_la_SOURCES += callout_manager.cc callout_manager.h
+libkea_hooks_la_SOURCES += hooks.h
+libkea_hooks_la_SOURCES += hooks_log.cc hooks_log.h
+libkea_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
+libkea_hooks_la_SOURCES += hooks_config.cc hooks_config.h
+libkea_hooks_la_SOURCES += hooks_parser.cc hooks_parser.h
+libkea_hooks_la_SOURCES += libinfo.cc libinfo.h
+libkea_hooks_la_SOURCES += library_handle.cc library_handle.h
+libkea_hooks_la_SOURCES += library_manager.cc library_manager.h
+libkea_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h
+libkea_hooks_la_SOURCES += parking_lots.h
+libkea_hooks_la_SOURCES += pointer_converter.h
+libkea_hooks_la_SOURCES += server_hooks.cc server_hooks.h
+libkea_hooks_la_SOURCES += hooks_messages.cc hooks_messages.h
+
+libkea_hooks_la_CXXFLAGS = $(AM_CXXFLAGS)
+libkea_hooks_la_CPPFLAGS = $(AM_CPPFLAGS)
+libkea_hooks_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 57:0:0
+libkea_hooks_la_LIBADD =
+libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
+libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
+libkea_hooks_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libkea_hooks_la_LIBADD += $(BOOST_LIBS)
+libkea_hooks_la_LIBADD += $(LOG4CPLUS_LIBS)
+
+# If we want to get rid of all generated messages files, we need to use
+# make maintainer-clean. The proper way to introduce custom commands for
+# that operation is to define maintainer-clean-local target. However,
+# make maintainer-clean also removes Makefile, so running configure script
+# is required. To make it easy to rebuild messages without going through
+# reconfigure, a new target messages-clean has been added.
+maintainer-clean-local:
+ rm -f hooks_messages.h hooks_messages.cc
+
+# To regenerate messages files, one can do:
+#
+# make messages-clean
+# make messages
+#
+# This is needed only when a .mes file is modified.
+messages-clean: maintainer-clean-local
+
+if GENERATE_MESSAGES
+
+# Define rule to build logging source files from message file
+messages: hooks_messages.h hooks_messages.cc
+ @echo Message files regenerated
+
+hooks_messages.h hooks_messages.cc: hooks_messages.mes
+ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/hooks/hooks_messages.mes
+
+else
+
+messages hooks_messages.h hooks_messages.cc:
+ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
+
+endif
+
+# Specify the headers for copying into the installation directory tree.
+libkea_hooks_includedir = $(pkgincludedir)/hooks
+libkea_hooks_include_HEADERS = \
+ callout_handle.h \
+ callout_handle_associate.h \
+ callout_manager.h \
+ hooks.h \
+ hooks_config.h \
+ hooks_log.h \
+ hooks_manager.h \
+ hooks_messages.h \
+ hooks_parser.h \
+ libinfo.h \
+ library_handle.h \
+ library_manager.h \
+ library_manager_collection.h \
+ parking_lots.h \
+ pointer_converter.h \
+ server_hooks.h
+
+
diff --git a/src/lib/hooks/Makefile.in b/src/lib/hooks/Makefile.in
new file mode 100644
index 0000000..8f6d546
--- /dev/null
+++ b/src/lib/hooks/Makefile.in
@@ -0,0 +1,1140 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/lib/hooks
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp11.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_sysrepo.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(libkea_hooks_include_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" \
+ "$(DESTDIR)$(libkea_hooks_includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libkea_hooks_la_DEPENDENCIES = \
+ $(top_builddir)/src/lib/cc/libkea-cc.la \
+ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+ $(top_builddir)/src/lib/log/libkea-log.la \
+ $(top_builddir)/src/lib/util/libkea-util.la \
+ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libkea_hooks_la_OBJECTS = libkea_hooks_la-callout_handle.lo \
+ libkea_hooks_la-callout_handle_associate.lo \
+ libkea_hooks_la-callout_manager.lo \
+ libkea_hooks_la-hooks_log.lo libkea_hooks_la-hooks_manager.lo \
+ libkea_hooks_la-hooks_config.lo \
+ libkea_hooks_la-hooks_parser.lo libkea_hooks_la-libinfo.lo \
+ libkea_hooks_la-library_handle.lo \
+ libkea_hooks_la-library_manager.lo \
+ libkea_hooks_la-library_manager_collection.lo \
+ libkea_hooks_la-server_hooks.lo \
+ libkea_hooks_la-hooks_messages.lo
+libkea_hooks_la_OBJECTS = $(am_libkea_hooks_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libkea_hooks_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) \
+ $(libkea_hooks_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/libkea_hooks_la-callout_handle.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-callout_handle_associate.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-callout_manager.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-hooks_config.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-hooks_log.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-hooks_manager.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-hooks_messages.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-hooks_parser.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-libinfo.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-library_handle.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-library_manager.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-library_manager_collection.Plo \
+ ./$(DEPDIR)/libkea_hooks_la-server_hooks.Plo
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libkea_hooks_la_SOURCES)
+DIST_SOURCES = $(libkea_hooks_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(libkea_hooks_include_HEADERS)
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_SYSREPO = @HAVE_SYSREPO@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = . tests
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \
+ $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+# Ensure that the message file is included in the distribution
+
+# Include developer's guide files
+
+# Include images used in Developer's guide
+EXTRA_DIST = hooks_messages.mes hooks_user.dox hooks_maintenance.dox \
+ hooks_component_developer.dox images/DataScopeArgument.dia \
+ images/DataScopeArgument.png images/DataScopeContext.dia \
+ images/DataScopeContext.png images/HooksUml.dia \
+ images/HooksUml.png
+CLEANFILES = *.gcno *.gcda
+lib_LTLIBRARIES = libkea-hooks.la
+libkea_hooks_la_SOURCES = callout_handle.cc callout_handle.h \
+ callout_handle_associate.cc callout_handle_associate.h \
+ callout_manager.cc callout_manager.h hooks.h hooks_log.cc \
+ hooks_log.h hooks_manager.cc hooks_manager.h hooks_config.cc \
+ hooks_config.h hooks_parser.cc hooks_parser.h libinfo.cc \
+ libinfo.h library_handle.cc library_handle.h \
+ library_manager.cc library_manager.h \
+ library_manager_collection.cc library_manager_collection.h \
+ parking_lots.h pointer_converter.h server_hooks.cc \
+ server_hooks.h hooks_messages.cc hooks_messages.h
+libkea_hooks_la_CXXFLAGS = $(AM_CXXFLAGS)
+libkea_hooks_la_CPPFLAGS = $(AM_CPPFLAGS)
+libkea_hooks_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 57:0:0
+libkea_hooks_la_LIBADD = $(top_builddir)/src/lib/cc/libkea-cc.la \
+ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+ $(top_builddir)/src/lib/log/libkea-log.la \
+ $(top_builddir)/src/lib/util/libkea-util.la \
+ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+ $(BOOST_LIBS) $(LOG4CPLUS_LIBS)
+
+# Specify the headers for copying into the installation directory tree.
+libkea_hooks_includedir = $(pkgincludedir)/hooks
+libkea_hooks_include_HEADERS = \
+ callout_handle.h \
+ callout_handle_associate.h \
+ callout_manager.h \
+ hooks.h \
+ hooks_config.h \
+ hooks_log.h \
+ hooks_manager.h \
+ hooks_messages.h \
+ hooks_parser.h \
+ libinfo.h \
+ library_handle.h \
+ library_manager.h \
+ library_manager_collection.h \
+ parking_lots.h \
+ pointer_converter.h \
+ server_hooks.h
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/hooks/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib/hooks/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libkea-hooks.la: $(libkea_hooks_la_OBJECTS) $(libkea_hooks_la_DEPENDENCIES) $(EXTRA_libkea_hooks_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libkea_hooks_la_LINK) -rpath $(libdir) $(libkea_hooks_la_OBJECTS) $(libkea_hooks_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-callout_handle.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-callout_handle_associate.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-callout_manager.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-hooks_config.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-hooks_log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-hooks_manager.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-hooks_messages.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-hooks_parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-libinfo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-library_handle.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-library_manager.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-library_manager_collection.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkea_hooks_la-server_hooks.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+libkea_hooks_la-callout_handle.lo: callout_handle.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-callout_handle.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-callout_handle.Tpo -c -o libkea_hooks_la-callout_handle.lo `test -f 'callout_handle.cc' || echo '$(srcdir)/'`callout_handle.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-callout_handle.Tpo $(DEPDIR)/libkea_hooks_la-callout_handle.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_handle.cc' object='libkea_hooks_la-callout_handle.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-callout_handle.lo `test -f 'callout_handle.cc' || echo '$(srcdir)/'`callout_handle.cc
+
+libkea_hooks_la-callout_handle_associate.lo: callout_handle_associate.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-callout_handle_associate.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-callout_handle_associate.Tpo -c -o libkea_hooks_la-callout_handle_associate.lo `test -f 'callout_handle_associate.cc' || echo '$(srcdir)/'`callout_handle_associate.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-callout_handle_associate.Tpo $(DEPDIR)/libkea_hooks_la-callout_handle_associate.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_handle_associate.cc' object='libkea_hooks_la-callout_handle_associate.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-callout_handle_associate.lo `test -f 'callout_handle_associate.cc' || echo '$(srcdir)/'`callout_handle_associate.cc
+
+libkea_hooks_la-callout_manager.lo: callout_manager.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-callout_manager.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-callout_manager.Tpo -c -o libkea_hooks_la-callout_manager.lo `test -f 'callout_manager.cc' || echo '$(srcdir)/'`callout_manager.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-callout_manager.Tpo $(DEPDIR)/libkea_hooks_la-callout_manager.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_manager.cc' object='libkea_hooks_la-callout_manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-callout_manager.lo `test -f 'callout_manager.cc' || echo '$(srcdir)/'`callout_manager.cc
+
+libkea_hooks_la-hooks_log.lo: hooks_log.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-hooks_log.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-hooks_log.Tpo -c -o libkea_hooks_la-hooks_log.lo `test -f 'hooks_log.cc' || echo '$(srcdir)/'`hooks_log.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-hooks_log.Tpo $(DEPDIR)/libkea_hooks_la-hooks_log.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hooks_log.cc' object='libkea_hooks_la-hooks_log.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-hooks_log.lo `test -f 'hooks_log.cc' || echo '$(srcdir)/'`hooks_log.cc
+
+libkea_hooks_la-hooks_manager.lo: hooks_manager.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-hooks_manager.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-hooks_manager.Tpo -c -o libkea_hooks_la-hooks_manager.lo `test -f 'hooks_manager.cc' || echo '$(srcdir)/'`hooks_manager.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-hooks_manager.Tpo $(DEPDIR)/libkea_hooks_la-hooks_manager.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hooks_manager.cc' object='libkea_hooks_la-hooks_manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-hooks_manager.lo `test -f 'hooks_manager.cc' || echo '$(srcdir)/'`hooks_manager.cc
+
+libkea_hooks_la-hooks_config.lo: hooks_config.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-hooks_config.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-hooks_config.Tpo -c -o libkea_hooks_la-hooks_config.lo `test -f 'hooks_config.cc' || echo '$(srcdir)/'`hooks_config.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-hooks_config.Tpo $(DEPDIR)/libkea_hooks_la-hooks_config.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hooks_config.cc' object='libkea_hooks_la-hooks_config.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-hooks_config.lo `test -f 'hooks_config.cc' || echo '$(srcdir)/'`hooks_config.cc
+
+libkea_hooks_la-hooks_parser.lo: hooks_parser.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-hooks_parser.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-hooks_parser.Tpo -c -o libkea_hooks_la-hooks_parser.lo `test -f 'hooks_parser.cc' || echo '$(srcdir)/'`hooks_parser.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-hooks_parser.Tpo $(DEPDIR)/libkea_hooks_la-hooks_parser.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hooks_parser.cc' object='libkea_hooks_la-hooks_parser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-hooks_parser.lo `test -f 'hooks_parser.cc' || echo '$(srcdir)/'`hooks_parser.cc
+
+libkea_hooks_la-libinfo.lo: libinfo.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-libinfo.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-libinfo.Tpo -c -o libkea_hooks_la-libinfo.lo `test -f 'libinfo.cc' || echo '$(srcdir)/'`libinfo.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-libinfo.Tpo $(DEPDIR)/libkea_hooks_la-libinfo.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='libinfo.cc' object='libkea_hooks_la-libinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-libinfo.lo `test -f 'libinfo.cc' || echo '$(srcdir)/'`libinfo.cc
+
+libkea_hooks_la-library_handle.lo: library_handle.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-library_handle.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-library_handle.Tpo -c -o libkea_hooks_la-library_handle.lo `test -f 'library_handle.cc' || echo '$(srcdir)/'`library_handle.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-library_handle.Tpo $(DEPDIR)/libkea_hooks_la-library_handle.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='library_handle.cc' object='libkea_hooks_la-library_handle.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-library_handle.lo `test -f 'library_handle.cc' || echo '$(srcdir)/'`library_handle.cc
+
+libkea_hooks_la-library_manager.lo: library_manager.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-library_manager.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-library_manager.Tpo -c -o libkea_hooks_la-library_manager.lo `test -f 'library_manager.cc' || echo '$(srcdir)/'`library_manager.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-library_manager.Tpo $(DEPDIR)/libkea_hooks_la-library_manager.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='library_manager.cc' object='libkea_hooks_la-library_manager.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-library_manager.lo `test -f 'library_manager.cc' || echo '$(srcdir)/'`library_manager.cc
+
+libkea_hooks_la-library_manager_collection.lo: library_manager_collection.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-library_manager_collection.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-library_manager_collection.Tpo -c -o libkea_hooks_la-library_manager_collection.lo `test -f 'library_manager_collection.cc' || echo '$(srcdir)/'`library_manager_collection.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-library_manager_collection.Tpo $(DEPDIR)/libkea_hooks_la-library_manager_collection.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='library_manager_collection.cc' object='libkea_hooks_la-library_manager_collection.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-library_manager_collection.lo `test -f 'library_manager_collection.cc' || echo '$(srcdir)/'`library_manager_collection.cc
+
+libkea_hooks_la-server_hooks.lo: server_hooks.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-server_hooks.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-server_hooks.Tpo -c -o libkea_hooks_la-server_hooks.lo `test -f 'server_hooks.cc' || echo '$(srcdir)/'`server_hooks.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-server_hooks.Tpo $(DEPDIR)/libkea_hooks_la-server_hooks.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='server_hooks.cc' object='libkea_hooks_la-server_hooks.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-server_hooks.lo `test -f 'server_hooks.cc' || echo '$(srcdir)/'`server_hooks.cc
+
+libkea_hooks_la-hooks_messages.lo: hooks_messages.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -MT libkea_hooks_la-hooks_messages.lo -MD -MP -MF $(DEPDIR)/libkea_hooks_la-hooks_messages.Tpo -c -o libkea_hooks_la-hooks_messages.lo `test -f 'hooks_messages.cc' || echo '$(srcdir)/'`hooks_messages.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkea_hooks_la-hooks_messages.Tpo $(DEPDIR)/libkea_hooks_la-hooks_messages.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hooks_messages.cc' object='libkea_hooks_la-hooks_messages.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkea_hooks_la_CPPFLAGS) $(CPPFLAGS) $(libkea_hooks_la_CXXFLAGS) $(CXXFLAGS) -c -o libkea_hooks_la-hooks_messages.lo `test -f 'hooks_messages.cc' || echo '$(srcdir)/'`hooks_messages.cc
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-libkea_hooks_includeHEADERS: $(libkea_hooks_include_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(libkea_hooks_include_HEADERS)'; test -n "$(libkea_hooks_includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libkea_hooks_includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libkea_hooks_includedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libkea_hooks_includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(libkea_hooks_includedir)" || exit $$?; \
+ done
+
+uninstall-libkea_hooks_includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libkea_hooks_include_HEADERS)'; test -n "$(libkea_hooks_includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(libkea_hooks_includedir)'; $(am__uninstall_files_from_dir)
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES) $(HEADERS)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libkea_hooks_includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-callout_handle.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-callout_handle_associate.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-callout_manager.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_config.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_log.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_manager.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_messages.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_parser.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-libinfo.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-library_handle.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-library_manager.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-library_manager_collection.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-server_hooks.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-libkea_hooks_includeHEADERS
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-callout_handle.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-callout_handle_associate.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-callout_manager.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_config.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_log.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_manager.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_messages.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-hooks_parser.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-libinfo.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-library_handle.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-library_manager.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-library_manager_collection.Plo
+ -rm -f ./$(DEPDIR)/libkea_hooks_la-server_hooks.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic \
+ maintainer-clean-local
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-libLTLIBRARIES \
+ uninstall-libkea_hooks_includeHEADERS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES \
+ install-libkea_hooks_includeHEADERS install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic \
+ maintainer-clean-local mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \
+ uninstall-libkea_hooks_includeHEADERS
+
+.PRECIOUS: Makefile
+
+
+# If we want to get rid of all generated messages files, we need to use
+# make maintainer-clean. The proper way to introduce custom commands for
+# that operation is to define maintainer-clean-local target. However,
+# make maintainer-clean also removes Makefile, so running configure script
+# is required. To make it easy to rebuild messages without going through
+# reconfigure, a new target messages-clean has been added.
+maintainer-clean-local:
+ rm -f hooks_messages.h hooks_messages.cc
+
+# To regenerate messages files, one can do:
+#
+# make messages-clean
+# make messages
+#
+# This is needed only when a .mes file is modified.
+messages-clean: maintainer-clean-local
+
+# Define rule to build logging source files from message file
+@GENERATE_MESSAGES_TRUE@messages: hooks_messages.h hooks_messages.cc
+@GENERATE_MESSAGES_TRUE@ @echo Message files regenerated
+
+@GENERATE_MESSAGES_TRUE@hooks_messages.h hooks_messages.cc: hooks_messages.mes
+@GENERATE_MESSAGES_TRUE@ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/hooks/hooks_messages.mes
+
+@GENERATE_MESSAGES_FALSE@messages hooks_messages.h hooks_messages.cc:
+@GENERATE_MESSAGES_FALSE@ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/lib/hooks/callout_handle.cc b/src/lib/hooks/callout_handle.cc
new file mode 100644
index 0000000..6877b65
--- /dev/null
+++ b/src/lib/hooks/callout_handle.cc
@@ -0,0 +1,167 @@
+// Copyright (C) 2013-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#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), server_hooks_(ServerHooks::getServerHooks()),
+ current_library_(-1), current_hook_(-1), next_step_(NEXT_STEP_CONTINUE) {
+
+ // 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);
+}
+
+ParkingLotHandlePtr
+CalloutHandle::getParkingLotHandlePtr() const {
+ return (boost::make_shared<ParkingLotHandle>(server_hooks_.getParkingLotPtr(current_hook_)));
+}
+
+// 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() {
+ // Access a reference to the element collection for the given index,
+ // creating a new element collection if necessary, and return it.
+ return (context_collection_[current_library_]);
+}
+
+// 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 {
+ auto libcontext = context_collection_.find(current_library_);
+ if (libcontext == context_collection_.end()) {
+ isc_throw(NoSuchCalloutContext, "unable to find callout context "
+ "associated with the current library index (" << current_library_ <<
+ ")");
+ }
+
+ // 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 {
+ string hook = "";
+ try {
+ hook = server_hooks_.getName(current_hook_);
+ } 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);
+}
+
+ScopedCalloutHandleState::
+ScopedCalloutHandleState(const CalloutHandlePtr& callout_handle)
+ : callout_handle_(callout_handle) {
+ if (!callout_handle_) {
+ isc_throw(BadValue, "callout_handle argument must not be null");
+ }
+
+ resetState();
+}
+
+ScopedCalloutHandleState::~ScopedCalloutHandleState() {
+ resetState();
+
+ if (on_completion_) {
+ on_completion_();
+ }
+}
+
+void
+ScopedCalloutHandleState::resetState() {
+ // No need to check if the handle is null because the constructor
+ // already checked that.
+ callout_handle_->deleteAllArguments();
+ callout_handle_->setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+}
+
+} // namespace hooks
+} // namespace isc
diff --git a/src/lib/hooks/callout_handle.h b/src/lib/hooks/callout_handle.h
new file mode 100644
index 0000000..fb4b30a
--- /dev/null
+++ b/src/lib/hooks/callout_handle.h
@@ -0,0 +1,512 @@
+// Copyright (C) 2013-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef CALLOUT_HANDLE_H
+#define CALLOUT_HANDLE_H
+
+#include <exceptions/exceptions.h>
+#include <hooks/library_handle.h>
+#include <hooks/parking_lots.h>
+#include <util/dhcp_space.h>
+
+#include <boost/any.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+class ServerHooks;
+
+/// @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 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.
+
+class CalloutHandle {
+public:
+
+ /// @brief Specifies allowed next steps
+ ///
+ /// Those values are used to designate the next step in packet processing.
+ /// They are set by hook callouts and read by the Kea server. See
+ /// @ref setStatus for detailed description of each value.
+ enum CalloutNextStep {
+ NEXT_STEP_CONTINUE = 0, ///< continue normally
+ NEXT_STEP_SKIP = 1, ///< skip the next processing step
+ NEXT_STEP_DROP = 2, ///< drop the packet
+ NEXT_STEP_PARK = 3 ///< park the packet
+ };
+
+
+ /// 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 Sets the next processing step.
+ ///
+ /// This method is used by the callouts to determine the next step
+ /// in processing. This method replaces former setSkip() method
+ /// that allowed only two values.
+ ///
+ /// Currently there are three possible value allowed:
+ /// NEXT_STEP_CONTINUE - tells the server to continue processing as usual
+ /// (equivalent of previous setSkip(false) )
+ ///
+ /// NEXT_STEP_SKIP - tells the server to skip the processing. Exact meaning
+ /// is hook specific. See hook documentation for details.
+ /// (equivalent of previous setSkip(true))
+ ///
+ /// NEXT_STEP_DROP - tells the server to unconditionally drop the packet
+ /// and do not process it further.
+ ///
+ /// NEXT_STEP_PARK - tells the server to "park" the packet. The packet will
+ /// wait in the queue for being unparked, e.g. as a result
+ /// of completion of the asynchronous performed by the
+ /// hooks library operation.
+ ///
+ /// This variable is interrogated by the server to see if the remaining
+ /// callouts associated with the current hook should be bypassed.
+ ///
+ /// @param next New value of the next step status.
+ void setStatus(const CalloutNextStep next) {
+ next_step_ = next;
+ }
+
+ /// @brief Returns the next processing step.
+ ///
+ /// Gets the current value of the next step. See @ref setStatus for detailed
+ /// definition.
+ ///
+ /// @return Current value of the skip flag.
+ CalloutNextStep getStatus() const {
+ return (next_step_);
+ }
+
+ /// @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;
+
+ /// @brief Returns pointer to the parking lot handle for this hook point.
+ ///
+ /// @return pointer to the parking lot handle
+ ParkingLotHandlePtr getParkingLotHandlePtr() const;
+
+ /// @brief Get current library index
+ ///
+ /// @return The current library index
+ int getCurrentLibrary() const {
+ return (current_library_);
+ }
+
+ /// @brief Set current library index
+ ///
+ /// @param library_index The library index
+ void setCurrentLibrary(int library_index) {
+ current_library_ = library_index;
+ }
+
+ /// @brief Get current hook index
+ ///
+ /// @return The current hook index
+ int getCurrentHook() const {
+ return (current_hook_);
+ }
+
+ /// @brief Set current hook index
+ ///
+ /// @param hook_index The hook index
+ void setCurrentHook(int hook_index) {
+ current_hook_ = hook_index;
+ }
+
+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_;
+
+ /// Reference to the singleton ServerHooks object. See the
+ /// @ref hooksmgMaintenanceGuide for information as to why the class holds
+ /// a reference instead of accessing the singleton within the code.
+ ServerHooks& server_hooks_;
+
+ /// @brief Current library.
+ ///
+ /// When a call is made to @ref CalloutManager::callCallouts, this holds
+ /// the index of the current library. It is set to an invalid value (-1)
+ /// otherwise.
+ int current_library_;
+
+ /// @brief Current hook.
+ ///
+ /// When a call is made to @ref CalloutManager::callCallouts, this holds
+ /// the index of the current hook. It is set to an invalid value (-1)
+ /// otherwise.
+ int current_hook_;
+
+ /// Next processing step, indicating what the server should do next.
+ CalloutNextStep next_step_;
+};
+
+/// A shared pointer to a CalloutHandle object.
+typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;
+
+/// @brief Wrapper class around callout handle which automatically
+/// resets handle's state.
+///
+/// The Kea servers often require to associate processed packets with
+/// @c CalloutHandle instances. This is to facilitate the case when the
+/// hooks library passes information between the callouts using the
+/// 'context' stored in the callout handle. The callouts invoked throughout
+/// the packet lifetime have access to the context information for the
+/// given packet.
+///
+/// The association between the packets and the callout handles is
+/// achieved by giving the ownership of the @c CalloutHandle objects to
+/// the @c Pkt objects. When the @c Pkt object goes out of scope, it should
+/// also release the pointer to the owned @c CalloutHandle object.
+/// However, this causes a risk of circular dependency between the shared
+/// pointer to the @c Pkt object and the shared pointer to the
+/// @c CalloutHandle it owns, because the pointer to the packet is often
+/// set as an argument of the callout handle prior to invoking a callout.
+///
+/// In order to break the circular dependency, the arguments of the
+/// callout handle must be deleted as soon as they are not needed
+/// anymore. This class is a wrapper around the callout handle object,
+/// which resets its state during construction and destruction. All
+/// Kea hook points must use this class within the scope where the
+/// @c HooksManager::callCallouts is invoked to reset the state of the
+/// callout handle. The state is reset when this object goes out of
+/// scope.
+///
+/// Currently, the following operations are performed during the reset:
+/// - all arguments of the callout handle are deleted,
+/// - the next step status is set to @c CalloutHandle::NEXT_STEP CONTINUE
+///
+/// This class must never be modified to also delete the context
+/// information from the callout handle. The context is intended
+/// to be used to share stateful data across callouts and hook points
+/// and its contents must exist for the duration of the packet lifecycle.
+/// Otherwise, we could simply re-create the callout handle for
+/// each hook point and we wouldn't need this RAII class.
+class ScopedCalloutHandleState {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Resets state of the callout handle.
+ ///
+ /// @param callout_handle reference to the pointer to the callout
+ /// handle which state should be reset.
+ /// @throw isc::BadValue if the callout handle is null.
+ explicit ScopedCalloutHandleState(const CalloutHandlePtr& callout_handle);
+
+ /// @brief Destructor.
+ ///
+ /// Resets state of the callout handle.
+ ~ScopedCalloutHandleState();
+
+ /// @brief Continuation callback.
+ std::function<void()> on_completion_;
+
+private:
+
+ /// @brief Resets the callout handle state.
+ ///
+ /// It is used internally by the constructor and destructor.
+ void resetState();
+
+ /// @brief Holds pointer to the wrapped callout handle.
+ CalloutHandlePtr callout_handle_;
+};
+
+} // namespace hooks
+} // namespace isc
+
+
+#endif // CALLOUT_HANDLE_H
diff --git a/src/lib/hooks/callout_handle_associate.cc b/src/lib/hooks/callout_handle_associate.cc
new file mode 100644
index 0000000..dc6a3dc
--- /dev/null
+++ b/src/lib/hooks/callout_handle_associate.cc
@@ -0,0 +1,34 @@
+// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/callout_handle_associate.h>
+#include <hooks/hooks_manager.h>
+
+namespace isc {
+namespace hooks {
+
+CalloutHandleAssociate::CalloutHandleAssociate()
+ : callout_handle_() {
+}
+
+CalloutHandlePtr
+CalloutHandleAssociate::getCalloutHandle() {
+ if (!callout_handle_) {
+ callout_handle_ = HooksManager::createCalloutHandle();
+ }
+
+ return (callout_handle_);
+}
+
+void
+CalloutHandleAssociate::resetCalloutHandle() {
+ callout_handle_.reset();
+}
+
+} // end of namespace isc::hooks
+} // end of namespace isc
diff --git a/src/lib/hooks/callout_handle_associate.h b/src/lib/hooks/callout_handle_associate.h
new file mode 100644
index 0000000..c222590
--- /dev/null
+++ b/src/lib/hooks/callout_handle_associate.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef CALLOUT_HANDLE_ASSOCIATE_H
+#define CALLOUT_HANDLE_ASSOCIATE_H
+
+#include <hooks/callout_handle.h>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Base class for classes which need to be associated with
+/// a @c CalloutHandle object.
+///
+/// The @c CalloutHandle is an object used to pass various parameters
+/// between Kea and the callouts. The Kea servers usually invoke
+/// multiple different callouts for a single packet such as DHCP
+/// packet, control command etc. Therefore, it is required to
+/// associate this packet with an instance of the callout handle, so
+/// this instance can be used for all callouts invoked for this
+/// packet.
+///
+/// Previously this association was made by the @c CalloutHandleStore
+/// class. However, with the introduction of parallel processing
+/// of packets (DHCP packets parking) it became awkward to use.
+/// Attempts to extend this class to hold a map of associations
+/// failed because of no easy way to garbage collect unused handles.
+///
+/// The easiest way to deal with this is to provide ownership of the
+/// @c CalloutHandle to the object with which it is associated. The
+/// class of this object needs to derive from this class. When the
+/// object (e.g. DHCP packet) goes out of scope and is destroyed
+/// this instance is destroyed as well.
+class CalloutHandleAssociate {
+public:
+
+ /// @brief Constructor.
+ CalloutHandleAssociate();
+
+ /// @brief Returns callout handle.
+ ///
+ /// The callout handle is created if it doesn't exist. Subsequent
+ /// calls to this method always return the same handle.
+ ///
+ /// @return Pointer to the callout handle.
+ CalloutHandlePtr getCalloutHandle();
+
+ /// @brief Reset callout handle.
+ void resetCalloutHandle();
+
+protected:
+
+ /// @brief Callout handle stored.
+ CalloutHandlePtr callout_handle_;
+};
+
+} // end of isc::hooks
+} // end of isc
+
+#endif
diff --git a/src/lib/hooks/callout_manager.cc b/src/lib/hooks/callout_manager.cc
new file mode 100644
index 0000000..5505567
--- /dev/null
+++ b/src/lib/hooks/callout_manager.cc
@@ -0,0 +1,348 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/hooks_log.h>
+#include <hooks/pointer_converter.h>
+#include <util/stopwatch.h>
+
+#include <boost/static_assert.hpp>
+
+#include <algorithm>
+#include <climits>
+#include <functional>
+#include <utility>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Constructor
+CalloutManager::CalloutManager(int num_libraries)
+ : server_hooks_(ServerHooks::getServerHooks()), 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) {
+ if (num_libraries < 0) {
+ isc_throw(isc::BadValue, "number of libraries passed to the "
+ "CalloutManager must be >= 0");
+ }
+}
+
+// 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_ << ")");
+}
+
+// Register a callout for the current library.
+
+void
+CalloutManager::registerCallout(const std::string& name,
+ CalloutPtr callout,
+ int library_index) {
+ // Note the registration.
+ LOG_DEBUG(callouts_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUT_REGISTRATION)
+ .arg(library_index).arg(name);
+
+ // Sanity check that the current library index is set to a valid value.
+ checkLibraryIndex(library_index);
+
+ // New hooks could have been registered since the manager was constructed.
+ ensureHookLibsVectorSize();
+
+ // Get the index associated with this hook (validating the name in the
+ // process).
+ int hook_index = server_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 > library_index) {
+ // 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(library_index,
+ 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(library_index, 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());
+}
+
+bool
+CalloutManager::commandHandlersPresent(const std::string& command_name) const {
+ // Check if the hook point for the specified command exists.
+ int index = ServerHooks::getServerHooks().findIndex(
+ ServerHooks::commandToHookName(command_name));
+ if (index >= 0) {
+ // The hook point exits but it is possible that there are no
+ // callouts/command handlers. This is possible if there was a
+ // hook library supporting this command attached, but it was
+ // later unloaded. The hook points are not deregistered in
+ // this case. Only callouts are deregistered.
+ // Let's check if callouts are present for this hook point.
+ return (calloutsPresent(index));
+ }
+
+ // Hook point not created, so we don't support this command in
+ // any of the hooks libraries.
+ return (false);
+}
+
+
+// Call all the callouts for a given hook.
+
+void
+CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
+ // Clear the "skip" flag so we don't carry state from a previous call.
+ // This is done regardless of whether callouts are present to avoid passing
+ // any state from the previous call of callCallouts().
+ callout_handle.setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+
+ // Only initialize and iterate if there are callouts present. This check
+ // also catches the case of an invalid index.
+ if (calloutsPresent(hook_index)) {
+
+ // Set the current hook index. This is used should a callout wish to
+ // determine to what hook it is attached.
+ callout_handle.setCurrentHook(hook_index);
+
+ // This object will be used to measure execution time of each callout
+ // and the total time spent in callouts for this hook point.
+ util::Stopwatch stopwatch;
+
+ // Mark that the callouts begin for the hook.
+ LOG_DEBUG(callouts_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUTS_BEGIN)
+ .arg(server_hooks_.getName(callout_handle.getCurrentHook()));
+
+ // Call all the callouts.
+ for (CalloutVector::const_iterator i = hook_vector_[hook_index].begin();
+ i != hook_vector_[hook_index].end(); ++i) {
+ // In case the callout requires access to the context associated
+ // with the library, set the current library index to the index
+ // associated with the library that registered the callout being
+ // called.
+ callout_handle.setCurrentLibrary(i->first);
+
+ // Call the callout
+ try {
+ stopwatch.start();
+ int status = (*i->second)(callout_handle);
+ stopwatch.stop();
+ if (status == 0) {
+ LOG_DEBUG(callouts_logger, HOOKS_DBG_EXTENDED_CALLS,
+ HOOKS_CALLOUT_CALLED)
+ .arg(callout_handle.getCurrentLibrary())
+ .arg(server_hooks_.getName(callout_handle.getCurrentHook()))
+ .arg(PointerConverter(i->second).dlsymPtr())
+ .arg(stopwatch.logFormatLastDuration());
+ } else {
+ LOG_ERROR(callouts_logger, HOOKS_CALLOUT_ERROR)
+ .arg(server_hooks_.getName(callout_handle.getCurrentHook()))
+ .arg(callout_handle.getCurrentLibrary())
+ .arg(PointerConverter(i->second).dlsymPtr())
+ .arg(stopwatch.logFormatLastDuration());
+ }
+ } catch (const std::exception& e) {
+ // If an exception occurred, the stopwatch.stop() hasn't been
+ // called, so we have to call it here.
+ stopwatch.stop();
+ // Any exception, not just ones based on isc::Exception
+ LOG_ERROR(callouts_logger, HOOKS_CALLOUT_EXCEPTION)
+ .arg(server_hooks_.getName(callout_handle.getCurrentHook()))
+ .arg(callout_handle.getCurrentLibrary())
+ .arg(PointerConverter(i->second).dlsymPtr())
+ .arg(e.what())
+ .arg(stopwatch.logFormatLastDuration());
+ }
+
+ }
+
+ // Mark end of callout execution. Include the total execution
+ // time for callouts.
+ LOG_DEBUG(callouts_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUTS_COMPLETE)
+ .arg(server_hooks_.getName(callout_handle.getCurrentHook()))
+ .arg(stopwatch.logFormatTotalDuration());
+
+ // Reset the current hook and library indexes to an invalid value to
+ // catch any programming errors.
+ callout_handle.setCurrentHook(-1);
+ callout_handle.setCurrentLibrary(-1);
+ }
+}
+
+void
+CalloutManager::callCommandHandlers(const std::string& command_name,
+ CalloutHandle& callout_handle) {
+ // Get the index of the hook point for the specified command.
+ // This will throw an exception if the hook point doesn't exist.
+ // The caller should check if the hook point exists by calling
+ // commandHandlersPresent.
+ int index = ServerHooks::getServerHooks().getIndex(
+ ServerHooks::commandToHookName(command_name));
+ // Call the handlers for this command.
+ callCallouts(index, callout_handle);
+}
+
+
+// Deregister a callout registered by the current library on a particular hook.
+
+bool
+CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout,
+ int library_index) {
+ // Sanity check that the current library index is set to a valid value.
+ checkLibraryIndex(library_index);
+
+ // New hooks could have been registered since the manager was constructed.
+ ensureHookLibsVectorSize();
+
+ // Get the index associated with this hook (validating the name in the
+ // process).
+ int hook_index = server_hooks_.getIndex(name);
+
+ // New hooks can have been registered since the manager was constructed.
+ if (hook_index >= hook_vector_.size()) {
+ return (false);
+ }
+
+ /// Construct a CalloutEntry matching the current library and the callout
+ /// we want to remove.
+ CalloutEntry target(library_index, 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(),
+ [&target] (CalloutEntry x) {
+ return (x == 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(callouts_logger, HOOKS_DBG_EXTENDED_CALLS,
+ HOOKS_CALLOUT_DEREGISTERED).arg(library_index).arg(name);
+ }
+
+ return (removed);
+}
+
+// Deregister all callouts on a given hook.
+
+bool
+CalloutManager::deregisterAllCallouts(const std::string& name,
+ int library_index) {
+ // New hooks could have been registered since the manager was constructed.
+ ensureHookLibsVectorSize();
+
+ // Get the index associated with this hook (validating the name in the
+ // process).
+ int hook_index = server_hooks_.getIndex(name);
+
+ /// Construct a CalloutEntry matching the current library (the callout
+ /// pointer is NULL as we are not checking that).
+ CalloutEntry target(library_index, static_cast<CalloutPtr>(0));
+
+ /// 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(),
+ [&target] (CalloutEntry x) {
+ return (x.first == target.first);
+ }),
+ 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(callouts_logger, HOOKS_DBG_EXTENDED_CALLS,
+ HOOKS_ALL_CALLOUTS_DEREGISTERED).arg(library_index).arg(name);
+ }
+
+ return (removed);
+}
+
+void
+CalloutManager::registerCommandHook(const std::string& command_name) {
+ // New hooks could have been registered since the manager was constructed.
+ ensureHookLibsVectorSize();
+
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ int hook_index = hooks.findIndex(ServerHooks::commandToHookName(command_name));
+ if (hook_index < 0) {
+ // Hook for this command doesn't exist. Let's create one.
+ hooks.registerHook(ServerHooks::commandToHookName(command_name));
+ // Callout Manager's vector of hooks have to be resized to hold the
+ // information about callouts for this new hook point. This should
+ // add new element at the end of the hook_vector_. The index of this
+ // element will match the index of the hook point in the ServerHooks
+ // because ServerHooks allocates indexes incrementally.
+ hook_vector_.resize(server_hooks_.getCount());
+ }
+}
+
+void
+CalloutManager::ensureHookLibsVectorSize() {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ if (hooks.getCount() > hook_vector_.size()) {
+ // Uh oh, there are more hook points that our vector allows.
+ hook_vector_.resize(hooks.getCount());
+ }
+}
+
+} // 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..3ee1efd
--- /dev/null
+++ b/src/lib/hooks/callout_manager.h
@@ -0,0 +1,438 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef 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 Kea
+/// 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
+/// library registers or deregisters a hook, it does so by name: the
+/// @ref isc::hooks::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 hooks.
+///
+/// - 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. When calling a callout, the callout manager maintains
+/// the idea of a "current library index": this is used to access the context
+/// associated with the library.
+///
+/// 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
+/// they are called.
+///
+/// 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::hooks::HooksManager) class. However, two other indexes are recognized,
+/// 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 recognized 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.
+///
+/// Since Kea 1.3.0 release hook libraries can register callouts as control
+/// command handlers. Such handlers are associated with dynamically created
+/// hook points which names are created after command names. For example,
+/// if a command name is 'foo-bar', the name of the hook point to which
+/// callouts/command handlers are registered is '$foo_bar'. Prefixing the
+/// hook point name with the dollar sign eliminates potential conflicts
+/// between hook points dedicated to commands handling and other (fixed)
+/// hook points.
+///
+/// Prefixing hook names for command handlers with a dollar sign precludes
+/// auto registration of command handlers, i.e. hooks framework is unable
+/// to match hook points with names of functions implementing command
+/// handlers, because the dollar sign is not legal in C++ function names.
+/// This is intended because we want hook libraries to explicitly register
+/// commands handlers for supported commands and not rely on Kea to register
+/// hook points for them. Should we find use cases for auto registration of
+/// command handlers, we may modify the
+/// @ref ServerHooks::commandToHookName to use an encoding of hook
+/// point names for command handlers that would only contain characters
+/// allowed in function names.
+///
+/// The @ref CalloutManager::registerCommandHook has been added to allow for
+/// dynamically creating hook points for which command handlers are registered.
+/// This method is called from the @ref LibraryHandle::registerCommandCallout
+/// as a result of registering the command handlers by the hook library in
+/// its @c load() function. If the hook point for the given command already
+/// exists, this function doesn't do anything. The
+/// @ref LibraryHandle::registerCommandCallout can install callouts on this
+/// hook point.
+///
+/// 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 0,
+ CalloutManager(int num_libraries = 0);
+
+ /// @brief Register a callout on a hook for the current library
+ ///
+ /// Registers a callout function for the current library with a given hook.
+ /// 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.
+ /// @param library_index Library index used for registering the callout.
+ ///
+ /// @throw NoSuchHook The hook name is unrecognized.
+ /// @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,
+ int library_index);
+
+ /// @brief De-Register a callout on a hook for the current library
+ ///
+ /// Searches through the functions registered by the current library
+ /// 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.
+ /// @param library_index Library index used for deregistering the callout.
+ ///
+ /// @return true if a one or more callouts were deregistered.
+ ///
+ /// @throw NoSuchHook The hook name is unrecognized.
+ /// @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,
+ int library_index);
+
+ /// @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.
+ ///
+ /// @param name Name of the hook from which the callouts are removed.
+ /// @param library_index Library index used for deregistering all callouts.
+ ///
+ /// @return true if one or more callouts were deregistered.
+ ///
+ /// @throw NoSuchHook Thrown if the hook name is unrecognized.
+ bool deregisterAllCallouts(const std::string& name, int library_index);
+
+ /// @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 Checks if control command handlers are present for the
+ /// specified command.
+ ///
+ /// @param command_name Command name for which handlers' presence should
+ /// be checked.
+ ///
+ /// @return true if there is a hook point associated with the specified
+ /// command and callouts/command handlers are installed for this hook
+ /// point, false otherwise.
+ bool commandHandlersPresent(const std::string& command_name) const;
+
+ /// @brief Calls the callouts for a given hook
+ ///
+ /// Iterates through the library 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 Calls the callouts/command handlers for a given command name.
+ ///
+ /// Iterates through the library handles and calls the command handlers
+ /// associated with the given command. It expects that the hook point
+ /// for this command exists (with a name being a command_name prefixed
+ /// with a dollar sign and with hyphens replaced with underscores).
+ ///
+ /// @param command_name Command name for which handlers should be called.
+ /// @param callout_handle Reference to the CalloutHandle object for the
+ /// current object being processed.
+ ///
+ /// @throw NoSuchHook if the hook point for the specified command does
+ /// not exist.
+ void callCommandHandlers(const std::string& command_name,
+ CalloutHandle& callout_handle);
+
+ /// @brief Registers a hook point for the specified command name.
+ ///
+ /// If the hook point for such command already exists, this function
+ /// doesn't do anything. The registered hook point name is created
+ /// after command_name by prefixing it with a dollar sign and replacing
+ /// all hyphens with underscores, e.g. for the 'foo-bar' command the
+ /// following hook point name will be generated: '$foo_bar'.
+ ///
+ /// @param command_name Command name for which the hook point should be
+ /// registered.
+ void registerCommandHook(const std::string& command_name);
+
+ /// @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_);
+ }
+
+ //@}
+
+ /// @brief Return number of currently available hooks
+ size_t getHookLibsVectorSize() const {
+ return (hook_vector_.size());
+ }
+
+private:
+
+ /// @brief This method checks whether the hook_vector_ size is sufficient
+ /// and extends it if necessary.
+ ///
+ /// The problem is as follows: some hooks are initialized statically
+ /// from global static objects. ServerHooks object creates hooks_ collection
+ /// and CalloutManager creates its own hook_vector_ and both are initialized
+ /// to the same size. All works well so far. However, if some code at a
+ /// later time (e.g. a hook library) registers new hook point, then
+ /// ServerHooks::registerHook() will extend its hooks_ collection, but
+ /// the CalloutManager will keep the old hook_vector_ that is too small by
+ /// one. Now when the library is unloaded, deregisterAllCallouts will
+ /// go through all hook points and will eventually hit the one that
+ /// will return index greater than the hook_vector_ size.
+ ///
+ /// To solve this problem, ensureVectorSize was implemented. It should
+ /// be called (presumably from ServerHooks) every time a new hook point
+ /// is registered. It checks whether the vector size is sufficient and
+ /// extends it if necessary. It is safe to call it multiple times. It
+ /// may grow the vector size, but will never shrink it.
+ void ensureHookLibsVectorSize();
+
+ /// @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;
+
+ // Member variables
+
+ /// Reference to the singleton ServerHooks object. See the
+ /// @ref hooksmgMaintenanceGuide for information as to why the class holds
+ /// a reference instead of accessing the singleton within the code.
+ ServerHooks& server_hooks_;
+
+ /// 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..e2d4f5e
--- /dev/null
+++ b/src/lib/hooks/hooks.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2013-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HOOKS_H
+#define HOOKS_H
+
+#include <hooks/callout_handle.h>
+#include <hooks/library_handle.h>
+
+namespace {
+
+// Version 20200 of the hooks framework, set for Kea 2.2.0
+const int KEA_HOOKS_VERSION = 20200;
+
+// 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";
+const char* const MULTI_THREADING_COMPATIBLE_FUNCTION_NAME =
+ "multi_threading_compatible";
+
+// 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)();
+typedef int (*multi_threading_compatible_function_ptr)();
+
+} // Anonymous namespace
+
+namespace isc {
+namespace hooks {
+
+/// @brief User-Library Initialization for Statically-Linked Kea
+///
+/// If Kea is statically-linked, a user-created hooks library will not be
+/// able to access symbols in it. In particular, it will not be able to access
+/// singleton objects.
+///
+/// The hooks framework handles some of this. For example, although there is
+/// a singleton ServerHooks object, hooks framework objects store a reference
+/// to it when they are created. When the user library needs to register a
+/// callout (which requires access to the ServerHooks information), it accesses
+/// the ServerHooks object through a pointer passed from the Kea image.
+///
+/// The logging framework is more problematical. Here the code is partly
+/// statically linked (the Kea logging library) and partly shared (the
+/// log4cplus). The state of the former is not accessible to the user library,
+/// but the state of the latter is. So within the user library, we need to
+/// initialize the Kea logging library but not initialize the log4cplus
+/// code. Some of the initialization is done when the library is loaded, but
+/// other parts are done at run-time.
+///
+/// This function - to be called by the user library code in its load() function
+/// when running against a statically linked Kea - initializes the Kea
+/// logging library. In particular, it loads the message dictionary with the
+/// text of the Kea messages.
+///
+/// @note This means that the virtual address space is loaded with two copies
+/// of the message dictionary. Depending on how the user libraries are linked,
+/// loading multiple user libraries may involve loading one message dictionary
+/// per library.
+
+void hooksStaticLinkInit();
+
+} // namespace hooks
+} // namespace isc
+
+#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..baeb5ef
--- /dev/null
+++ b/src/lib/hooks/hooks_component_developer.dox
@@ -0,0 +1,520 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+@page hooksComponentDeveloperGuide Guide to Hooks for the Kea Component Developer
+
+@section hooksComponentIntroduction Introduction
+
+The hooks framework is a Kea system that simplifies the way that
+users can write code to modify the behavior of Kea. Instead of
+altering the Kea source code, they write functions that are compiled
+and linked into one or more dynamic shared objects, called here (for
+historical reasons), shared libraries. The library is specified in the Kea
+configuration and at run time Kea 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 Kea developers who want to write or modify a
+Kea 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 Kea
+during its execution, see the document @ref hooksdgDevelopersGuide.
+
+
+@subsection hooksComponentTerminology Terminology
+
+In the remainder of this guide, the following terminology is used:
+
+- Component - a Kea process, e.g. the DHCPv4 or DHCPv6 server.
+
+- Hook/Hook Point - used interchangeably, 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-Kea code that is compiled into a
+shared library and loaded by Kea 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 hooksdgDevelopersGuide for more details.)
+
+
+@subsection hooksComponentLanguages Languages
+
+The core of Kea is written in C++ with some remaining legacy 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.
+
+
+@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.
+
+
+@section hooksComponentDefinition Determining 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 located
+where a callout is able to obtain useful information from Kea 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 ways:
+
+- Setting the nest step status. This is an enum 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 perform certain actions. Currently
+ there are three statuses defined: CONTINUE (this is the default, the server
+ is expected to continue as usual), SKIP (the server is expected to skip the
+ next processing step, but otherwise continue as usual) and DROP (the server
+ is expected to drop the packet or request completely. The exact action is up
+ to the component.
+
+- 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.
+
+
+@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 @c isc::hooks::HooksManager::registerHook():
+
+@code
+
+#include <hooks/hooks_manager.h>
+ :
+ int example_index = HooksManager::registerHook("lease_allocate");
+@endcode
+
+The name of the hook is passed as the sole argument to the @c 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.
+
+
+@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 @c 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:
+
+@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)) {
+ :
+ }
+ :
+}
+@endcode
+
+
+@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 @c 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 Kea
+convention is that they are lower-case.
+
+
+@section hooksComponentCallingCallouts Calling Callouts on a Hook
+
+
+@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:
+@code
+int callout_name(isc::hooks::CalloutHandle& handle);
+@endcode
+
+The @c 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 @c 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) @c getArgument and @c setArgument.
+Their usage is illustrated by the following code snippets.
+
+@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);
+@endcode
+
+As can be seen @c getArgument is used to retrieve data from the
+@c CalloutHandle, and @c 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 @c getArgument must
+match the data type of the variable passed to the corresponding
+@c 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 @c int and the component
+attempted to retrieve it as a @c long, an exception would be thrown even
+though any value that can be stored in an @c int will fit into a @c long.
+This restriction also applies the "const" attribute but only as applied to
+data pointed to by pointers, e.g. if an argument is defined as a @c char*,
+an exception will be thrown if an attempt is made to retrieve it into
+a variable of type @c const @c char*. (However, if an argument is set as a
+@c const @c int, it can be retrieved into an @c 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.
+
+
+@subsection hooksComponentSkipFlag The Skip Flag (obsolete)
+
+
+@subsection hooksComponentNextStep The next step status
+
+Although information is passed back to the component from callouts through
+@c 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 next step
+ status to SKIP 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 DROP flag to indicate to the server that the packet should be dropped.
+
+For ease of processing, the @c CalloutHandle contains
+two methods, @c isc::hooks::CalloutHandle::getStatus() and
+@c isc::hooks::CalloutHandle::setStatus(). It is only meaningful for the
+component to use the "get" method. The next step status is cleared (set to
+the default value of CONTINUE) 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 status (it
+might have been set by callouts earlier in the callout list for the hook)
+and set it. Note that the setting of the status by a callout does not
+prevent callouts later in the list from being called: the next step status is
+just an enum value - the only significance comes from its interpretation
+by the component.
+
+An example of use could be:
+@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->getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
+ // Skip flag not set, do the address allocation
+ :
+}
+@endcode
+
+
+@subsection hooksComponentGettingHandle Getting the Callout Handle
+
+The @c CalloutHandle object is linked to the loaded libraries
+for lifetime reasons (described below). Components
+should retrieve a @c isc::hooks::CalloutHandle using
+@c isc::hooks::HooksManager::createCalloutHandle():
+@code
+ CalloutHandlePtr handle_ptr = HooksManager::createCalloutHandle();
+@endcode
+(@c 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:
+@code
+ handle_ptr.reset();
+@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.)
+
+When the handle is not created locally it is not destroyed so it can
+keep ownership on arguments. In such case the code must call @c
+isc::hooks::CalloutHandle::deleteAllArguments or simply use the RAII
+helper @c isc::hooks::ScopedCalloutHandleState as in:
+@code
+ CalloutHandlePtr handle_ptr = getCalloutHandle(query);
+ ScopedCalloutHandleState state(handle_ptr);
+@endcode
+
+@subsection hooksComponentCallingCallout Calling the Callout
+
+Calling the callout is a simple matter of executing the
+@c 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:
+@code
+ HooksManager::callCallouts(pkt_sent, *handle_ptr);
+@endcode
+... where "*handle_ptr" is a reference (note: not a pointer) to the
+@c 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 next step status), it should define an argument through which
+the callout can do so.
+
+@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 @c CalloutHandle, a
+component can check for callouts before doing that processing using
+@c 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:
+@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->getStatus() != CalloutHandle::NEXT_STEP_DROP) {
+ // Next step allows us to continue, do the address allocation
+ :
+ }
+}
+@endcode
+
+@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 @c CalloutHandle returned by
+@c isc::hooks::HooksManager::createCalloutHandle() will be valid,
+@c isc::hooks::HooksManager::calloutsPresent() will return false for every
+index, and @c isc::hooks::HooksManager::callCallouts() will be a no-op.
+
+However, if user libraries are specified in the Kea 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 @c isc::hooks::HooksManager::loadLibraries() method. The method is
+passed a vector of strings, each giving the full file specification of
+a user library:
+@code
+ std::vector<std::string> libraries = ... // Get array of libraries
+ bool success = HooksManager::loadLibraries(libraries);
+@endcode
+@c 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.
+
+Before @c loadLibraries() can be called a second or subsequent time
+(as a result of a reconfiguration), all existing libraries must be
+successfully unloaded. If a library stays in memory from a programming
+bug in Kea (for instance when no libraries were loaded) or in a
+library (@ref hooksMemoryManagement) @c loadLibraries() throws a not
+recoverable error.
+
+Unloading is done in two phases since Kea version 1.7.10:
+
+- call to @c isc::hooks::HooksManager::prepareUnloadLibraries() which
+calls all unload() entry points and deregisters callout points.
+
+- call to @c isc::hooks::HooksManager::unloadLibraries() even when
+the prepare failed.
+
+If a failure of @c unloadLibraries() is ignored any call to @c loadLibraries()
+will throw.
+
+
+@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 @c 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 Kea 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.
+
+Since Kea 1.7.10 the unload() entry point is called as the first phase
+of unloading. This gives more chance to hooks writer to perform
+necessary cleanup actions so the second phase, memory unmapping
+can safely happen. The @c isc::hooks::unloadLibraries() function
+was updated too to return false when at least one active callout
+handle remained.
+
+
+@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 exactly the same way as user callouts, being passed their arguments
+though a CalloutHandle object. (Guidelines for writing callouts can be
+found in @ref hooksdgDevelopersGuide.)
+
+A component can associate with a hook callouts that run either before
+user-registered callouts or after them. Registration is done via a
+@c isc::hooks::LibraryHandle object, a reference to one being obtained
+through the methods @c isc::hooks::HooksManager::preCalloutLibraryHandle()
+(for a handle to register callouts to run before the user library
+callouts) or @c isc::hooks::HooksManager::postCalloutLibraryHandle() (for
+a handle to register callouts to run after the user callouts). Use of
+the @c LibraryHandle to register and deregister callouts is described in
+@ref hooksdgLibraryHandle.
+
+Finally, it should be noted that callouts registered in this way only
+remain registered until the next call to @c 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_config.cc b/src/lib/hooks/hooks_config.cc
new file mode 100644
index 0000000..c6bf9a2
--- /dev/null
+++ b/src/lib/hooks/hooks_config.cc
@@ -0,0 +1,127 @@
+// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/hooks_config.h>
+#include <hooks/hooks_manager.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::data;
+
+namespace isc {
+namespace hooks {
+
+void
+HooksConfig::verifyLibraries(const Element::Position& position) const {
+ // The code used to follow this logic:
+ //
+ // Check if the list of libraries has changed. If not, nothing is done
+ // - the command "DhcpN libreload" is required to reload the same
+ // libraries (this prevents needless reloads when anything else in the
+ // configuration is changed).
+ //
+ // We no longer rely on this. Parameters can change. And even if the
+ // parameters stay the same, they could point to files that could
+ // change. We can skip loading routines only if there were and there still
+ // are no libraries specified.
+ vector<string> current_libraries = HooksManager::getLibraryNames();
+ if (current_libraries.empty() && libraries_.empty()) {
+ return;
+ }
+
+ // Library list has changed, validate each of the libraries specified.
+ vector<string> lib_names = isc::hooks::extractNames(libraries_);
+ vector<string> error_libs = HooksManager::validateLibraries(lib_names);
+ if (!error_libs.empty()) {
+
+ // Construct the list of libraries in error for the message.
+ string error_list = error_libs[0];
+ for (size_t i = 1; i < error_libs.size(); ++i) {
+ error_list += (string(", ") + error_libs[i]);
+ }
+ isc_throw(InvalidHooksLibraries,
+ "hooks libraries failed to validate - "
+ "library or libraries in error are: "
+ << error_list << " (" << position << ")");
+ }
+}
+
+void
+HooksConfig::loadLibraries() const {
+ /// Commits the list of libraries to the configuration manager storage if
+ /// the list of libraries has changed.
+ /// @todo: Delete any stored CalloutHandles before reloading the
+ /// libraries
+ if (!HooksManager::loadLibraries(libraries_)) {
+ isc_throw(InvalidHooksLibraries,
+ "One or more hook libraries failed to load");
+ }
+}
+
+bool
+HooksConfig::equal(const HooksConfig& other) const {
+
+ /// @todo: This comparision assumes that the library order is not relevant,
+ /// so [ lib1, lib2 ] is equal to [ lib2, lib1 ]. However, this is not strictly
+ /// true, because callouts execution is called in other they're loaded. Therefore
+ /// changing the libraries order may change the server behavior.
+ ///
+ /// We don't have any libraries that are interacting (or would change their behavior
+ /// depending on the order in which their callouts are executed), so the code is
+ /// ok for now.
+ for (isc::hooks::HookLibsCollection::const_iterator this_it = libraries_.begin();
+ this_it != libraries_.end(); ++this_it) {
+ bool match = false;
+ for (isc::hooks::HookLibsCollection::const_iterator other_it =
+ other.libraries_.begin(); other_it != other.libraries_.end(); ++other_it) {
+ if (this_it->first != other_it->first) {
+ continue;
+ }
+ if (isNull(this_it->second) && isNull(other_it->second)) {
+ match = true;
+ break;
+ }
+ if (isNull(this_it->second) || isNull(other_it->second)) {
+ continue;
+ }
+ if (this_it->second->equals(*other_it->second)) {
+ match = true;
+ break;
+ }
+ }
+ // No match found for the particular hooks library so return false.
+ if (!match) {
+ return (false);
+ }
+ }
+ return (true);
+}
+
+ElementPtr
+HooksConfig::toElement() const {
+ // hooks-libraries is a list of maps
+ ElementPtr result = Element::createList();
+ // Iterate through libraries
+ for (HookLibsCollection::const_iterator hl = libraries_.begin();
+ hl != libraries_.end(); ++hl) {
+ // Entries are maps
+ ElementPtr map = Element::createMap();
+ // Set the library name
+ map->set("library", Element::create(hl->first));
+ // Set parameters (not set vs set empty map)
+ if (!isNull(hl->second)) {
+ map->set("parameters", hl->second);
+ }
+ // Push to the list
+ result->add(map);
+ }
+ return (result);
+}
+
+};
+};
diff --git a/src/lib/hooks/hooks_config.h b/src/lib/hooks/hooks_config.h
new file mode 100644
index 0000000..837727e
--- /dev/null
+++ b/src/lib/hooks/hooks_config.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HOOKS_CONFIG_H
+#define HOOKS_CONFIG_H
+
+#include <exceptions/exceptions.h>
+#include <cc/data.h>
+#include <cc/cfg_to_element.h>
+#include <hooks/libinfo.h>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Exception thrown when a library failed to validate
+class InvalidHooksLibraries : public Exception {
+ public:
+ InvalidHooksLibraries(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Wrapper class that holds hooks libraries configuration
+///
+/// This was moved from HooksLibrariesParser
+///
+/// However, this class does more than just check the list of library names.
+/// It does two other things:
+/// # validate libraries
+/// # load libraries
+/// and it provides a toElement() method to unparse a configuration.
+///
+/// @todo add toElement() unit tests
+class HooksConfig : public isc::data::CfgToElement {
+public:
+ /// @brief Default constructor.
+ ///
+ HooksConfig() : libraries_() { }
+
+ /// @brief Adds additional hooks libraries.
+ ///
+ /// @param libname full filename with path to the library.
+ /// @param parameters map of parameters that configure the library.
+ void add(std::string libname, isc::data::ConstElementPtr parameters) {
+ libraries_.push_back(make_pair(libname, parameters));
+ }
+
+ /// @brief Provides access to the configured hooks libraries.
+ ///
+ /// @note The const reference returned is only valid as long as the
+ /// object that returned it.
+ const isc::hooks::HookLibsCollection& get() const {
+ return libraries_;
+ }
+
+ /// @brief Removes all configured hooks libraries.
+ void clear() {
+ libraries_.clear();
+ }
+
+ /// @brief Compares two Hooks Config classes for equality
+ ///
+ /// @param other other hooksconfig to compare with
+ bool equal(const HooksConfig& other) const;
+
+ /// @brief Verifies that libraries stored in libraries_ are valid.
+ ///
+ /// This method is a smart wrapper around @ref
+ /// isc::hooks::HooksManager::validateLibraries().
+ /// It tries to validate all the libraries stored in libraries_.
+ ///
+ /// @param position position of the hooks-library map for error reporting
+ /// @throw InvalidHooksLibraries if any issue is discovered.
+ void verifyLibraries(const isc::data::Element::Position& position) const;
+
+ /// @brief Commits hooks libraries configuration.
+ ///
+ /// This method calls necessary methods in HooksManager that will unload
+ /// any libraries that may be currently loaded and will load the actual
+ /// libraries. Providing that the specified libraries are valid and are
+ /// different to those already loaded, this method loads the new set of
+ /// libraries (and unloads the existing set).
+ ///
+ /// @throw InvalidHooksLibraries if the call to HooksManager fails.
+ void loadLibraries() const;
+
+ /// @brief Unparse a configuration object
+ ///
+ /// Returns an element which must parse into the same object, i.e.
+ /// @code
+ /// for all valid config C parse(parse(C)->toElement()) == parse(C)
+ /// @endcode
+ ///
+ /// @return a pointer to a configuration which can be parsed into
+ /// the initial configuration object
+ isc::data::ElementPtr toElement() const;
+
+private:
+ /// @brief List of hooks libraries with their configuration parameters
+ isc::hooks::HookLibsCollection libraries_;
+};
+
+};
+};
+
+#endif // HOOKS_CONFIG_H
diff --git a/src/lib/hooks/hooks_log.cc b/src/lib/hooks/hooks_log.cc
new file mode 100644
index 0000000..141ab63
--- /dev/null
+++ b/src/lib/hooks/hooks_log.cc
@@ -0,0 +1,28 @@
+// Copyright (C) 2011-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// Defines the logger used by the Hooks
+
+#include <config.h>
+
+#include <hooks/hooks_log.h>
+#include <log/macros.h>
+
+namespace isc {
+namespace hooks {
+
+isc::log::Logger hooks_logger("hooks");
+
+isc::log::Logger callouts_logger("callouts");
+
+extern const int HOOKS_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC;
+extern const int HOOKS_DBG_CALLS = isc::log::DBGLVL_TRACE_BASIC_DATA;
+extern const int HOOKS_DBG_EXTENDED_CALLS = isc::log::DBGLVL_TRACE_DETAIL_DATA;
+
+
+} // 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..6b567c9
--- /dev/null
+++ b/src/lib/hooks/hooks_log.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2013-2015,2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HOOKS_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,
+extern const int HOOKS_DBG_TRACE;
+
+// The next level traces each call to hook code.
+extern const int HOOKS_DBG_CALLS;
+
+// Additional information on the calls. Report each call to a callout (even
+// if there are multiple callouts on a hook) and each status return.
+extern const int HOOKS_DBG_EXTENDED_CALLS;
+
+
+/// @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;
+
+/// @brief Callouts logger
+///
+/// This is the specialized logger used to log messages pertaining to the
+/// callouts execution. In particular, it logs when the callout is invoked
+/// and when it ends. It also logs the callout execution times.
+extern isc::log::Logger callouts_logger;
+
+} // namespace hooks
+} // namespace isc
+
+#endif // HOOKS_LOG_H
diff --git a/src/lib/hooks/hooks_maintenance.dox b/src/lib/hooks/hooks_maintenance.dox
new file mode 100644
index 0000000..75e1227
--- /dev/null
+++ b/src/lib/hooks/hooks_maintenance.dox
@@ -0,0 +1,381 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// Note: the prefix "hooksmg" to all labels is an abbreviation for "Hooks
+// Maintenance Guide" and is used to prevent a clash with symbols in any
+// other Doxygen file.
+
+/**
+ @page hooksmgMaintenanceGuide Hooks Maintenance Guide
+
+ @section hooksmgIntroduction Introduction
+
+ This document is aimed at Kea maintainers responsible for the hooks
+ system. It provides an overview of the classes that make up the hooks
+ framework and notes important aspects of processing. More detailed
+ information can be found in the source code.
+
+ It is assumed that the reader is familiar with the contents of the @ref
+ hooksdgDevelopersGuide and the @ref hooksComponentDeveloperGuide.
+
+ @section hooksmgObjects Hooks Framework Objects
+
+ The relationships between the various objects in the hooks framework
+ is shown below:
+
+ @image html HooksUml.png "High-Level Class Diagram of the Hooks Framework"
+
+ (To avoid clutter, the @ref hooksmgServerHooks object, used to pass
+ information about registered hooks to the components, is not shown on
+ the diagram.)
+
+ The hooks framework objects can be split into user-side objects and
+ server-side objects. The former are those objects used or referenced
+ by user-written hooks libraries. The latter are those objects used in
+ the hooks framework.
+
+ @subsection hooksmgUserObjects User-Side Objects
+
+ The user-side code is able to access two objects in the framework,
+ the @ref hooksmgCalloutHandle and the @ref hooksmgLibraryHandle.
+ The @ref hooksmgCalloutHandle is used to pass data between the Kea
+ component and the loaded library; the @ref hooksmgLibraryHandle is used
+ for registering callouts.
+
+ @subsubsection hooksmgCalloutHandle Callout Handle
+
+ The @ref isc::hooks::CalloutHandle has two functions: passing arguments
+ between the Kea component and the user-written library, and storing
+ per-request context between library calls. In both cases the data is
+ stored in a @c std::map structure, keyed by argument (or context item) name.
+ The actual data is stored in a @c boost::any object, which allows any
+ data type to be stored, although a penalty for this flexibility is
+ the restriction (mentioned in the @ref hooksdgDevelopersGuide) that
+ the type of data retrieved must be identical (and not just compatible)
+ with that stored.
+
+ The storage of context data is slightly complex because there is
+ separate context for each user library. For this reason, the @ref
+ hooksmgCalloutHandle has multiple maps, one for each library loaded.
+ The maps are stored in another map, the appropriate map being identified
+ by the "current library index" (this index is explained further below).
+ The reason for the second map (rather than a structure such as a vector)
+ is to avoid creating individual context maps unless needed; given the
+ key to the map (in this case the current library index) accessing an
+ element in a map using the operator[] method returns the element in
+ question if it exists, or creates a new one (and stores it in the map)
+ if its doesn't.
+
+ @subsubsection hooksmgLibraryHandle Library Handle
+
+ Little more than a restricted interface to the @ref
+ hooksmgCalloutManager, the @ref isc::hooks::LibraryHandle allows a
+ callout to register and deregister callouts. However, there are some
+ quirks to callout registration which, although the processing involved
+ is in the @ref hooksmgCalloutManager, are best described here.
+
+ Firstly, a callout can be deregistered by a function within a user
+ library only if it was registered by a function within that library. That
+ is to say, if library A registers the callout A_func() on hook "alpha"
+ and library B registers B_func(), functions within library A are only
+ able to remove A_func() (and functions in library B remove B_func()).
+ The restriction - here to prevent one library interfering with the
+ callouts of another - is enforced by means of the current library index.
+ As described below, each entry in the vector of callouts associated with
+ a hook is a pair object, comprising a pointer to the callout and
+ the index of the library with which it is associated. A callout
+ can only modify entries in that vector where the current library index
+ matches the index element of the pair.
+
+ A second quirk is that when dynamically modifying the list of callouts,
+ the change only takes effect when the current call out from the server
+ completes. To clarify this, suppose that functions A_func(), B_func()
+ and C_func() are registered on a hook, and the server executes a callout
+ on the hook. Suppose also during this call, A_func() removes the callout
+ C_func() and that B_func() adds D_func(). As changes only take effect
+ when the current call out completes, the user callouts executed will be
+ A_func(), B_func() then C_func(). When the server calls the hook callouts
+ again, the functions executed will be A_func(), B_func() and D_func().
+
+ This restriction is down to implementation. When a set of callouts on a hook
+ is being called, the @ref hooksmgCalloutManager iterates through a
+ vector (the "callout vector") of (index, callout pointer) pairs. Since
+ registration or deregistration of a callout on that hook would change the
+ vector (and so potentially invalidate the iterators used to access the it),
+ a copy of the vector is taken before the iteration starts. The @ref
+ hooksmgCalloutManager iterates over this copy while any changes made
+ by the callout registration functions affect the relevant callout vector.
+ Such approach was chosen because of performance considerations.
+
+ @subsection hooksmgServerObjects Server-Side Objects
+
+ Those objects are not accessible by user libraries. Please do not
+ attempt to use them if you are developing user callouts.
+
+ @subsubsection hooksmgServerHooks Server Hooks
+
+ The singleton @ref isc::hooks::ServerHooks object is used to register
+ hooks. It is little more than a wrapper around a map of (hook index,
+ hook name), generating a unique number (the hook index) for each
+ hook registered. It also handles the registration of the pre-defined
+ context_create and context_destroy hooks.
+
+ In operation, the @ref hooksmgHooksManager provides a thin wrapper
+ around it, so that the Kea component developer does not have to
+ worry about another object.
+
+ @subsubsection hooksmgLibraryManager Library Manager
+
+ An @ref isc::hooks::LibraryManager is created by the @ref
+ hooksmgHooksManager object for each shared library loaded. It
+ controls the loading and unloading of the library and in essence
+ represents the library in the hooks framework. It also handles the
+ registration of the standard callouts (functions in the library with
+ the same name as the hook name).
+
+ Of particular importance is the "library's index", a number associated
+ with the library. This is passed to the LibraryManager at creation
+ time and is used to tag the callout pointers. It is discussed
+ further below.
+
+ As the @c LibraryManager provides all the methods needed to manage the
+ shared library, it is the natural home for the static @c validateLibrary()
+ method. The function called the parsing of the Kea configuration, when
+ the "hooks-libraries" element is processed. It checks that shared library
+ exists, that it can be opened, that it contains the @c version() function
+ and that that function returns a valid value. It then closes the shared
+ library and returns an appropriate indication as to the library status.
+
+ @subsubsection hooksmgLibraryManagerCollection Library Manager Collection
+
+ The hooks framework can handle multiple libraries and as
+ a result will create a @ref hooksmgLibraryManager for each
+ of them. The collection of LibraryManagers is managed by the
+ @ref isc::hooks::LibraryManagerCollection object which, in most
+ cases has a method corresponding to a @ref hooksmgLibraryManager
+ method, e.g. it has a @c loadLibraries() that corresponds to the @ref
+ hooksmgLibraryManager's loadLibrary() call. As would be expected, methods
+ on the @c LibraryManagerCollection iterate through all specified libraries,
+ calling the corresponding LibraryManager method for each library.
+
+ One point of note is that @c LibraryManagerCollection operates on an "all
+ or none" principle. When @c loadLibraries() is called, on exit either all
+ libraries have been successfully opened or none of them have. There
+ is no use-case in Kea where, after a user has specified the shared
+ libraries they want to load, the system will operate with only some of
+ them loaded.
+
+ The @c LibraryManagerCollection is the place where each library's index is set.
+ Each library is assigned a number ranging from 1 through to the number
+ of libraries being loaded. As mentioned in the previous section, this
+ index is used to tag callout pointers, something that is discussed
+ in the next section.
+
+ (Whilst on the subject of library index numbers, two additional
+ numbers - 0 and @c INT_MAX - are also valid as "current library index".
+ For flexibility, the Kea component is able to register its own
+ functions as hook callouts. It does this by obtaining a suitable @ref
+ hooksmgLibraryHandle from the @ref hooksmgHooksManager. A choice
+ of two is available: one @ref hooksmgLibraryHandle (with an index
+ of 0) can be used to register a callout on a hook to execute before
+ any user-supplied callouts. The second (with an index of @c INT_MAX)
+ is used to register a callout to run after user-specified callouts.
+ Apart from the index number, the hooks framework does not treat these
+ callouts any differently from user-supplied ones.)
+
+ @subsubsection hooksmgCalloutManager Callout Manager
+
+ The @ref isc::hooks::CalloutManager is the core of the framework insofar
+ as the registration and calling of callouts is concerned.
+
+ It maintains a "hook vector" - a vector with one element for
+ each registered hook. Each element in this vector is itself a
+ vector (the callout vector), each element of which is a pair of
+ (library index, callback pointer). When a callout is registered, the
+ CalloutManager's current library index is used to supply the "library
+ index" part of the pair. The library index is set explicitly by the
+ @ref hooksmgLibraryManager prior to calling the user library's load()
+ function (and prior to registering the standard callbacks).
+
+ The situation is slightly more complex when a callout is executing. In
+ order to execute a callout, the CalloutManager's @c callCallouts()
+ method must be called. This iterates through the callout vector for
+ a hook and for each element in the vector, uses the "library index"
+ part of the pair to set the "current library index" before calling the
+ callout function recorded in the second part of the pair. In most cases,
+ the setting of the library index has no effect on the callout. However,
+ if the callout wishes to dynamically register or deregister a callout,
+ the @ref hooksmgLibraryHandle (see above) calls a method on the
+ @ref hooksmgCalloutManager which in turn uses that information.
+
+ @subsubsection hooksmgHooksManager Hooks Manager
+
+ The @ref isc::hooks::HooksManager is the main object insofar as the
+ server is concerned. It controls the creation of the library-related
+ objects and provides the framework in which they interact. It also
+ provides a shell around objects such as @ref hooksmgServerHooks so that all
+ interaction with the hooks framework by the server is through the
+ HooksManager object. Apart from this, it supplies no functionality to
+ the hooks framework.
+
+ @section hooksmgOtherIssues Other Issues
+
+ @subsection hooksmgMemoryAllocation Memory Allocation
+
+ 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 @ref hooksmgCalloutHandle 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 Kea 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 the @ref hooksmgCalloutHandle
+ by keeping in that object a shared pointer to the object controlling
+ library unloading (the @ref hooksmgLibraryManagerCollection). Although
+ the libraries can be unloaded at any time, it is only when every
+ @ref hooksmgCalloutHandle 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 Kea server incorporating the
+ hooks. It is up to the server developer to ensure that all such objects
+ have been destroyed before libraries are reloaded. In extreme cases
+ this may mean the server suspending all processing of incoming requests
+ until all currently executing requests have completed and data object
+ destroyed, reloading the libraries, then resuming processing.
+
+ Since Kea 1.7.10 the unload() entry point is called as the first phase
+ of unloading. This gives more chance to hooks writer to perform
+ necessary cleanup actions so the second phase, memory unmapping
+ can safely happen. The @c isc::hooks::unloadLibraries() function
+ was updated too to return false when at least one active callout
+ handle remained.
+
+ @subsection hooksmgStaticLinking Hooks and Statically-Linked Kea
+
+ Kea has the configuration option to allow static linking. What this
+ means is that it links against the static Kea libraries and not
+ the sharable ones - although it links against the sharable system
+ libraries like "libc" and "libstdc++" and well as the sharable libraries
+ for third-party packages such as log4cplus and MySql.
+
+ Static linking poses a problem for dynamically-loaded hooks libraries
+ as some of the code in them - in particular the hooks framework and
+ the logging code - depend on global objects created within the Kea
+ libraries. In the normal course of events (Kea linked against
+ shared libraries), when Kea is run and the operating system loads
+ a Kea shared library containing a global object, address space
+ is assigned for it. When the hooks framework loads a user-library
+ linked against the same Kea shared library, the operating system
+ recognizes that the library is already loaded (and initialized) and
+ uses its definition of the global object. Thus both the code in the
+ Kea image and the code in the user-written shared library
+ reference the same object.
+
+ If Kea is statically linked, the linker allocates address space
+ in the Kea image for the global object and does not include any
+ reference to the shared library containing it. When Kea now loads
+ the user-written shared library - and so loads the Kea library code
+ containing the global object - the operating system does not know that
+ the object already exists. Instead, it allocates new address space.
+ The version of Kea in memory therefore has two copies of the object:
+ one referenced by code in the Kea image, and one referenced by code
+ in the user-written hooks library. This causes problems - information
+ put in one copy is not available to the other.
+
+ Particular problems were encountered with global objects the hooks library
+ and in the logging library, so some code to alleviate the problem has been
+ included.
+
+ The issue in the hooks library is the singleton @ref
+ isc::hooks::ServerHooks object, used by the user-written hooks library
+ if it attempts to register or deregister callouts. The contents of the
+ singleton - the names of the hook points and their index - are set by
+ the relevant Kea server; this information is not available in the
+ singleton created in the user's hooks library.
+
+ Within the code users by the user's hooks library, the @c ServerHooks
+ object is used by @ref isc::hooks::CalloutHandle and @ref
+ isc::hooks::CalloutManager objects. Both these objects are passed to the
+ hooks library code when a callout is called: the former directly through
+ the callout argument list, the latter indirectly as a pointer to it is
+ stored in the CalloutHandle. This allows a solution to the problem:
+ instead of accessing the singleton via @c ServerHooks::getServerHooks(),
+ the constructors of these objects store a reference to the singleton
+ @c ServerHooks when they are created and use that reference to access
+ @c ServerHooks data. Since both @c CalloutHandle and @c CalloutManager are
+ created in the statically-linked Kea server, use of the reference
+ means that it is the singleton within the server - and not the one
+ within the user's hooks library - that is referenced.
+
+ The solution of the logging problem is not so straightforward. Within
+ Kea, there are two logging components, the Kea logging framework
+ and the log4cplus libraries. Owing to static linking, there are two
+ instances of the former; but as static linking uses shared libraries of
+ third-party products, there is one instance of the latter. What further
+ complicates matters is that initialization of the logging framework is
+ in two parts: static initialization and run-time initialization.
+
+ The logging initialization comprises the following:
+
+ -# Static initialization of the log4cplus global variables.
+ -# Static initialization of messages in the various Kea libraries.
+ -# Static initialization of logging framework.
+ -# Run-time initialization of the logging framework.
+ -# Run-time initialization of log4cplus
+
+ As both the Kea server and the user-written hooks libraries use the
+ log4cplus shared library, item 1 - the static initialization of the log4cplus
+ global variables is performed once.
+
+ The next two tasks - static initialization of the messages in the Kea
+ libraries and the static initialization of the logging framework -
+ are performed twice, once in the context of the Kea server and
+ once in the context of the hooks library. For this reason, run-time
+ initialization of the logging framework needs to be performed twice,
+ once in the context of the Kea server and once in the context of the
+ user-written hooks library. However, the standard logging framework
+ initialization code also performs the last task, initialization of
+ log4cplus, something that causes problems if executed more than once.
+
+ To get round this, the function @ref isc::hooks::hooksStaticLinkInit()
+ has been written. It executes the only part of the logging framework
+ run-time initialization that actually pertains to the logging framework
+ and not log4cplus, namely loading the message dictionary with the
+ statically-initialized messages in the Kea libraries.
+ This should be executed by any hooks library linking against a statically
+ initialized Kea. (In fact, running it against a dynamically-linked
+ Kea should have no effect, as the load operation discards any duplicate
+ message entries.) The hooks library tests do this, the code being
+ conditionally compiled within a test of the @c USE_STATIC_LINK macro, set
+ by the configure script.
+
+ @note Not everything is completely rosy with logging and static linking.
+ In particular, there appears to be an issue with the scenario where a
+ user-written hooks library is run by a statically-linked Kea and then
+ unloaded. As far as can be determined, on unload the system attempts to
+ delete the same logger twice. This is alleviated by explicitly clearing
+ the loggerptr_ variable in the @ref isc::log::Logger destructor, but there
+ is a suspicion that some memory might be lost in these circumstances.
+ This is still under investigation.
+*/
diff --git a/src/lib/hooks/hooks_manager.cc b/src/lib/hooks/hooks_manager.cc
new file mode 100644
index 0000000..82b741e
--- /dev/null
+++ b/src/lib/hooks/hooks_manager.cc
@@ -0,0 +1,275 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/callout_handle.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 <boost/weak_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Constructor
+
+HooksManager::HooksManager() : test_mode_(false) {
+ // Nothing present, so create the collection with any empty set of
+ // libraries, and get the CalloutManager.
+ HookLibsCollection libraries;
+ lm_collection_.reset(new LibraryManagerCollection(libraries));
+ lm_collection_->loadLibraries();
+ callout_manager_ = lm_collection_->getCalloutManager();
+}
+
+// Return reference to singleton hooks manager.
+
+HooksManager&
+HooksManager::getHooksManager() {
+ static HooksManager manager;
+ return (manager);
+}
+
+// Are callouts present?
+
+bool
+HooksManager::calloutsPresentInternal(int index) {
+ return (callout_manager_->calloutsPresent(index));
+}
+
+bool
+HooksManager::calloutsPresent(int index) {
+ return (getHooksManager().calloutsPresentInternal(index));
+}
+
+bool
+HooksManager::commandHandlersPresentInternal(const std::string& command_name) {
+ return (callout_manager_->commandHandlersPresent(command_name));
+}
+
+bool
+HooksManager::commandHandlersPresent(const std::string& command_name) {
+ return (getHooksManager().commandHandlersPresentInternal(command_name));
+}
+
+// Call the callouts
+
+void
+HooksManager::callCalloutsInternal(int index, CalloutHandle& handle) {
+ callout_manager_->callCallouts(index, handle);
+}
+
+void
+HooksManager::callCallouts(int index, CalloutHandle& handle) {
+ getHooksManager().callCalloutsInternal(index, handle);
+}
+
+void
+HooksManager::callCommandHandlersInternal(const std::string& command_name,
+ CalloutHandle& handle) {
+ callout_manager_->callCommandHandlers(command_name, handle);
+}
+
+void
+HooksManager::callCommandHandlers(const std::string& command_name,
+ CalloutHandle& handle) {
+ getHooksManager().callCommandHandlersInternal(command_name, handle);
+}
+
+// Load the libraries. This will delete the previously-loaded libraries
+// (if present) and load new ones. If loading libraries fails, initialize with
+// empty list.
+
+bool
+HooksManager::loadLibrariesInternal(const HookLibsCollection& libraries) {
+ if (test_mode_) {
+ return (true);
+ }
+
+ ServerHooks::getServerHooks().getParkingLotsPtr()->clear();
+
+ // Keep a weak pointer on the existing library manager collection.
+ boost::weak_ptr<LibraryManagerCollection> weak_lmc(lm_collection_);
+
+ // Create the library manager collection.
+ lm_collection_.reset(new LibraryManagerCollection(libraries));
+
+ // If there was another owner the previous library manager collection
+ // was not destroyed and libraries not closed.
+ if (!weak_lmc.expired()) {
+ isc_throw(LibrariesStillOpened, "some libraries are still opened");
+ }
+
+ // Load the libraries.
+ bool status = lm_collection_->loadLibraries();
+
+ if (status) {
+ // ... and obtain the callout manager for them if successful.
+ callout_manager_ = lm_collection_->getCalloutManager();
+ } else {
+ // Unable to load libraries, reset to state before this function was
+ // called.
+ static_cast<void>(unloadLibrariesInternal());
+ }
+
+ return (status);
+}
+
+bool
+HooksManager::loadLibraries(const HookLibsCollection& libraries) {
+ return (getHooksManager().loadLibrariesInternal(libraries));
+}
+
+// Unload the libraries. This just deletes all internal objects (which will
+// cause the libraries to be unloaded) and initializes them with empty list if
+// requested.
+
+bool
+HooksManager::unloadLibrariesInternal() {
+ if (test_mode_) {
+ return (true);
+ }
+
+ ServerHooks::getServerHooks().getParkingLotsPtr()->clear();
+
+ // Keep a weak pointer on the existing library manager collection.
+ boost::weak_ptr<LibraryManagerCollection> weak_lmc(lm_collection_);
+
+ // Create the collection with any empty set of libraries.
+ HookLibsCollection libraries;
+ lm_collection_.reset(new LibraryManagerCollection(libraries));
+
+ // If there was another owner the previous library manager collection
+ // was not destroyed and libraries not closed.
+ boost::shared_ptr<LibraryManagerCollection> still_here = weak_lmc.lock();
+ if (still_here) {
+ // Restore the library manager collection.
+ lm_collection_ = still_here;
+ return (false);
+ }
+
+ // Load the empty set of libraries.
+ lm_collection_->loadLibraries();
+
+ // Get the CalloutManager.
+ callout_manager_ = lm_collection_->getCalloutManager();
+
+ return (true);
+}
+
+bool
+HooksManager::unloadLibraries() {
+ return (getHooksManager().unloadLibrariesInternal());
+}
+
+void
+HooksManager::prepareUnloadLibrariesInternal() {
+ if (test_mode_) {
+ return;
+ }
+
+ static_cast<void>(lm_collection_->prepareUnloadLibraries());
+}
+
+void
+HooksManager::prepareUnloadLibraries() {
+ getHooksManager().prepareUnloadLibrariesInternal();
+}
+
+// Create a callout handle
+
+boost::shared_ptr<CalloutHandle>
+HooksManager::createCalloutHandleInternal() {
+ return (boost::make_shared<CalloutHandle>(callout_manager_, lm_collection_));
+}
+
+boost::shared_ptr<CalloutHandle>
+HooksManager::createCalloutHandle() {
+ return (getHooksManager().createCalloutHandleInternal());
+}
+
+// Get the list of the names of loaded libraries.
+
+std::vector<std::string>
+HooksManager::getLibraryNamesInternal() const {
+ return (lm_collection_->getLibraryNames());
+}
+
+HookLibsCollection
+HooksManager::getLibraryInfoInternal() const {
+ return (lm_collection_->getLibraryInfo());
+}
+
+std::vector<std::string>
+HooksManager::getLibraryNames() {
+ return (getHooksManager().getLibraryNamesInternal());
+}
+
+HookLibsCollection
+HooksManager::getLibraryInfo() {
+ return (getHooksManager().getLibraryInfoInternal());
+}
+
+// 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() {
+ return (callout_manager_->getPreLibraryHandle());
+}
+
+isc::hooks::LibraryHandle&
+HooksManager::preCalloutsLibraryHandle() {
+ return (getHooksManager().preCalloutsLibraryHandleInternal());
+}
+
+isc::hooks::LibraryHandle&
+HooksManager::postCalloutsLibraryHandleInternal() {
+ return (callout_manager_->getPostLibraryHandle());
+}
+
+isc::hooks::LibraryHandle&
+HooksManager::postCalloutsLibraryHandle() {
+ return (getHooksManager().postCalloutsLibraryHandleInternal());
+}
+
+// Validate libraries
+
+std::vector<std::string>
+HooksManager::validateLibraries(const std::vector<std::string>& libraries) {
+ return (LibraryManagerCollection::validateLibraries(libraries));
+}
+
+// Test mode
+
+void
+HooksManager::setTestMode(bool mode) {
+ getHooksManager().test_mode_ = mode;
+}
+
+bool
+HooksManager::getTestMode() {
+ return (getHooksManager().test_mode_);
+}
+
+} // 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..647e14a
--- /dev/null
+++ b/src/lib/hooks/hooks_manager.h
@@ -0,0 +1,499 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HOOKS_MANAGER_H
+#define HOOKS_MANAGER_H
+
+#include <hooks/server_hooks.h>
+#include <hooks/libinfo.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Libraries still opened.
+///
+/// Thrown if an attempt is made to load libraries when some are still
+/// in memory likely because they were not unloaded (logic error in Kea)
+/// or they have some visible dangling pointers (logic error in a hook
+/// library).
+class LibrariesStillOpened : public Exception {
+public:
+ LibrariesStillOpened(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+// 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 Kea 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 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.
+ ///
+ /// @note this method now requires the libraries are unloaded before
+ /// being called.
+ ///
+ /// 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.
+ /// @throw LibrariesStillOpened when some libraries are already loaded.
+ static bool loadLibraries(const HookLibsCollection& 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 called after @ref prepareUnloadLibraries
+ /// in order to destroy appropriate objects. See notes for
+ /// the class LibraryManager for pitfalls.
+ /// @note: if even after @ref prepareUnloadLibraries there are still
+ /// visible pointers (i.e. callout handles owning the
+ /// library manager collection) the method will fail to close
+ /// libraries and returns false. It is a fatal error as there
+ /// is no possible recovery. It is a logic error in the hook
+ /// code too so the solution is to fix it and to restart
+ /// the server with a correct hook library binary.
+ ///
+ /// @return true if all libraries unloaded successfully, false if they
+ /// are still in memory.
+ static bool unloadLibraries();
+
+ /// @brief Prepare the unloading of libraries
+ ///
+ /// Calls the unload functions when they exist and removes callouts.
+ ///
+ /// @note: after the call to this method there should be no visible
+ /// dangling pointers (i.e. callout handles owning the library
+ /// manager collection) nor invisible dangling pointers.
+ /// In the first case it will be impossible to close libraries
+ /// so they will remain in memory, in the second case a crash
+ /// is possible in particular at exit time during global
+ /// object finalization. In both cases the hook library code
+ /// causing the problem is incorrect and must be fixed.
+ /// @note: it is a logic error to not call this method before
+ /// @ref unloadLibraries even it hurts only with particular
+ /// hooks libraries.
+ static void prepareUnloadLibraries();
+
+ /// @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 Checks if control command handlers are present for the
+ /// specified command.
+ ///
+ /// @param command_name Command name for which handlers' presence should
+ /// be checked.
+ ///
+ /// @return true if there is a hook point associated with the specified
+ /// command and callouts/command handlers are installed for this hook
+ /// point, false otherwise.
+ static bool commandHandlersPresent(const std::string& command_name);
+
+ /// @brief Calls the callouts for a given hook
+ ///
+ /// Iterates through the library 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 Calls the callouts/command handlers for a given command name.
+ ///
+ /// Iterates through the library handles and calls the command handlers
+ /// associated with the given command. It expects that the hook point
+ /// for this command exists (with a name being a command_name prefixed
+ /// with a dollar sign and with hyphens replaced with underscores).
+ ///
+ /// @param command_name Command name for which handlers should be called.
+ /// @param handle Reference to the CalloutHandle object for the current
+ /// object being processed.
+ ///
+ /// @throw NoSuchHook if the hook point for the specified command does
+ /// not exist.
+ static void callCommandHandlers(const std::string& command_name,
+ 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);
+
+ /// @brief Return list of loaded libraries
+ ///
+ /// Returns the names of the loaded libraries.
+ ///
+ /// @return List of loaded library names.
+ static std::vector<std::string> getLibraryNames();
+
+ /// @brief Return list of loaded libraries with its parameters.
+ ///
+ /// Returns the names of the loaded libraries and their parameters.
+ ///
+ /// @return List of loaded libraries (names + parameters)
+ static HookLibsCollection getLibraryInfo();
+
+ /// @brief Validate library list
+ ///
+ /// For each library passed to it, checks that the library can be opened
+ /// and that the "version" function is present and gives the right answer.
+ /// Each library is closed afterwards.
+ ///
+ /// This is used during the configuration parsing - when the list of hooks
+ /// libraries is changed, each of the new libraries is checked before the
+ /// change is committed.
+ ///
+ /// @param libraries List of libraries to be validated.
+ ///
+ /// @return An empty vector if all libraries validated. Otherwise it
+ /// holds the names of the libraries that failed validation.
+ static std::vector<std::string> validateLibraries(
+ const std::vector<std::string>& libraries);
+
+ /// Index numbers for pre-defined hooks.
+ static const int CONTEXT_CREATE = ServerHooks::CONTEXT_CREATE;
+ static const int CONTEXT_DESTROY = ServerHooks::CONTEXT_DESTROY;
+
+ /// @brief Park an object (packet).
+ ///
+ /// The typical use case for parking an object is when the server needs to
+ /// suspend processing of a packet to perform an asynchronous operation,
+ /// before the response is sent to a client. In this case, the object type
+ /// is a pointer to the processed packet. Therefore, further in this
+ /// description we're going to refer to the parked objects as "parked
+ /// packets". However, any other object can be parked if necessary.
+ ///
+ /// The following is the typical flow when packets are parked. The callouts
+ /// responsible for performing an asynchronous operation signal this need
+ /// to the server by returning the status @c NEXT_STEP_PARK, which instructs
+ /// the server to call this function. This function stops processing the
+ /// packet and puts it in, so called, parking lot. In order to be able to
+ /// resume the packet processing when instructed by the hooks library, the
+ /// parked packet is associated with the callback which, when called, will
+ /// resume packet processing.
+ ///
+ /// The hook library must increase a reference count on the parked object
+ /// by calling @c ParkingLotHandle::reference prior to returning the
+ /// @c NEXT_STEP_PARK status. This is important when multiple callouts
+ /// are installed on the same hook point and each of them schedules an
+ /// asynchronous operation. In this case, the packet must not be unparked
+ /// until all hook libraries call @c ParkingLotHandle::unpark to mark
+ /// that respective asynchronous operations are completed.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param hook_name name of the hook point for which the packet is parked.
+ /// @param parked_object packet to be parked.
+ /// @param unpark_callback callback invoked when the packet is unparked.
+ template<typename T>
+ static void park(const std::string& hook_name, T parked_object,
+ std::function<void()> unpark_callback) {
+ ServerHooks::getServerHooks().
+ getParkingLotPtr(hook_name)->park(parked_object, unpark_callback);
+ }
+
+ /// @brief Forces unparking the object (packet).
+ ///
+ /// This method unparks the object regardless of the reference counting
+ /// value. This is used in the situations when the callouts fail to unpark
+ /// the packet for some reason.
+ ///
+ /// @tparam T type of the parked object.
+ /// @param hook_name name of the hook point for which the packet is parked.
+ /// @param parked_object parked object to be unparked.
+ /// @return true if the specified object has been found, false otherwise.
+ template<typename T>
+ static bool unpark(const std::string& hook_name, T parked_object) {
+ return (ServerHooks::getServerHooks().
+ getParkingLotPtr(hook_name)->unpark(parked_object, true));
+ }
+
+ /// @brief Removes parked object without calling a callback.
+ ///
+ /// @tparam T type of the parked object.
+ /// @param hook_name name of the hook point for which the packet is parked.
+ /// @param parked_object parked object to be removed.
+ /// @return true if the specified object has been found false otherwise.
+ template<typename T>
+ static bool drop(const std::string& hook_name, T parked_object) {
+ return (ServerHooks::getServerHooks().
+ getParkingLotPtr(hook_name)->drop(parked_object));
+ }
+
+ /// @brief Increases reference counter for the parked object.
+ ///
+ /// Reference counter must be increased at least to 1 before the @c park()
+ /// method can be called.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param hook_name name of the hook point for which the packet is parked.
+ /// @param parked_object parked object for which reference counter should
+ /// be increased.
+ template<typename T>
+ static void reference(const std::string& hook_name, T parked_object) {
+ ServerHooks::getServerHooks().
+ getParkingLotPtr(hook_name)->reference(parked_object);
+ }
+
+ /// @brief Clears any parking packets.
+ ///
+ /// This method should be called during reconfiguration to ensure there
+ /// are no dangling pointers that could possibly prevent the library
+ /// from being unloaded.
+ static void clearParkingLots() {
+ ServerHooks::getServerHooks().getParkingLotsPtr()->clear();
+ }
+
+ /// @brief Set test mode
+ ///
+ /// If enabled by unit tests will permit to register callouts before calling
+ /// @ref loadLibraries which will return immediately without changing
+ /// current internal state.
+ ///
+ /// @param mode the test mode flag which enables or disabled the
+ /// functionality.
+ static void setTestMode(bool mode);
+
+ /// @brief Get test mode
+ ///
+ /// @return the test mode flag.
+ static bool getTestMode();
+
+private:
+
+ /// @brief Constructor
+ ///
+ /// This is private as the object is a singleton and can only be addressed
+ /// through the getHooksManager() static method.
+ HooksManager();
+
+ /// @brief Get singleton hooks manager
+ ///
+ /// @return Reference to the singleton hooks manager.
+ static HooksManager& getHooksManager();
+
+ //@{
+ /// 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 Validate library list
+ ///
+ /// @param List of libraries to be validated.
+ ///
+ /// @return An empty string if all libraries validated. Otherwise it is
+ /// the name of the first library that failed validation. The
+ /// configuration code can return this to bindctl as an indication
+ /// of the problem.
+ std::string validateLibrariesInternal(
+ const std::vector<std::string>& libraries) const;
+
+ /// @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 HookLibsCollection& libraries);
+
+ /// @brief Unload libraries
+ ///
+ /// @return true if all libraries unloaded successfully, false on an error.
+ /// In the latter case, an error message will have been output.
+ bool unloadLibrariesInternal();
+
+ /// @brief Prepare the unloading of libraries
+ void prepareUnloadLibrariesInternal();
+
+ /// @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 Checks if control command handlers are present for the
+ /// specified command.
+ ///
+ /// @param command_name Command name for which handlers' presence should
+ /// be checked.
+ ///
+ /// @return true if there is a hook point associated with the specified
+ /// command and callouts/command handlers are installed for this hook
+ /// point, false otherwise.
+ bool commandHandlersPresentInternal(const std::string& command_name);
+
+ /// @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 Calls the callouts/command handlers for a given command name.
+ ///
+ /// @param command_name Command name for which handlers should be called.
+ /// @param handle Reference to the CalloutHandle object for the current
+ /// object being processed.
+ ///
+ /// @throw NoSuchHook if the hook point for the specified command does
+ /// not exist.
+ void callCommandHandlersInternal(const std::string& command_name,
+ 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 Return list of loaded libraries
+ ///
+ /// @return List of loaded library names.
+ std::vector<std::string> getLibraryNamesInternal() const;
+
+ /// @brief Return a collection of library names with parameters.
+ HookLibsCollection getLibraryInfoInternal() const;
+
+ //@}
+
+ // Members
+
+ /// Set of library managers.
+ ///
+ /// @note: This should never be null.
+ boost::shared_ptr<LibraryManagerCollection> lm_collection_;
+
+ /// Callout manager for the set of library managers.
+ ///
+ /// @note: This should never be null.
+ boost::shared_ptr<CalloutManager> callout_manager_;
+
+ /// Test flag to keep @ref callout_manager_ when calling @ref loadLibraries
+ /// from unit tests (called by @ref configureDhcp[46]Server).
+ ///
+ /// @note: This will effectively make @ref loadLibraries return immediately.
+ bool test_mode_;
+};
+
+} // namespace util
+} // namespace hooks
+
+#endif // HOOKS_MANAGER_H
diff --git a/src/lib/hooks/hooks_messages.cc b/src/lib/hooks/hooks_messages.cc
new file mode 100644
index 0000000..dc75fb4
--- /dev/null
+++ b/src/lib/hooks/hooks_messages.cc
@@ -0,0 +1,93 @@
+// File created from ../../../src/lib/hooks/hooks_messages.mes
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+namespace isc {
+namespace hooks {
+
+extern const isc::log::MessageID HOOKS_ALL_CALLOUTS_DEREGISTERED = "HOOKS_ALL_CALLOUTS_DEREGISTERED";
+extern const isc::log::MessageID HOOKS_CALLOUTS_BEGIN = "HOOKS_CALLOUTS_BEGIN";
+extern const isc::log::MessageID HOOKS_CALLOUTS_COMPLETE = "HOOKS_CALLOUTS_COMPLETE";
+extern const isc::log::MessageID HOOKS_CALLOUTS_REMOVED = "HOOKS_CALLOUTS_REMOVED";
+extern const isc::log::MessageID HOOKS_CALLOUT_CALLED = "HOOKS_CALLOUT_CALLED";
+extern const isc::log::MessageID HOOKS_CALLOUT_DEREGISTERED = "HOOKS_CALLOUT_DEREGISTERED";
+extern const isc::log::MessageID HOOKS_CALLOUT_ERROR = "HOOKS_CALLOUT_ERROR";
+extern const isc::log::MessageID HOOKS_CALLOUT_EXCEPTION = "HOOKS_CALLOUT_EXCEPTION";
+extern const isc::log::MessageID HOOKS_CALLOUT_REGISTRATION = "HOOKS_CALLOUT_REGISTRATION";
+extern const isc::log::MessageID HOOKS_CLOSE_ERROR = "HOOKS_CLOSE_ERROR";
+extern const isc::log::MessageID HOOKS_HOOK_LIST_RESET = "HOOKS_HOOK_LIST_RESET";
+extern const isc::log::MessageID HOOKS_INCORRECT_VERSION = "HOOKS_INCORRECT_VERSION";
+extern const isc::log::MessageID HOOKS_LIBRARY_CLOSED = "HOOKS_LIBRARY_CLOSED";
+extern const isc::log::MessageID HOOKS_LIBRARY_LOADED = "HOOKS_LIBRARY_LOADED";
+extern const isc::log::MessageID HOOKS_LIBRARY_LOADING = "HOOKS_LIBRARY_LOADING";
+extern const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE = "HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE";
+extern const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE = "HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE";
+extern const isc::log::MessageID HOOKS_LIBRARY_UNLOADED = "HOOKS_LIBRARY_UNLOADED";
+extern const isc::log::MessageID HOOKS_LIBRARY_UNLOADING = "HOOKS_LIBRARY_UNLOADING";
+extern const isc::log::MessageID HOOKS_LIBRARY_VERSION = "HOOKS_LIBRARY_VERSION";
+extern const isc::log::MessageID HOOKS_LOAD_ERROR = "HOOKS_LOAD_ERROR";
+extern const isc::log::MessageID HOOKS_LOAD_EXCEPTION = "HOOKS_LOAD_EXCEPTION";
+extern const isc::log::MessageID HOOKS_LOAD_FRAMEWORK_EXCEPTION = "HOOKS_LOAD_FRAMEWORK_EXCEPTION";
+extern const isc::log::MessageID HOOKS_LOAD_SUCCESS = "HOOKS_LOAD_SUCCESS";
+extern const isc::log::MessageID HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION = "HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION";
+extern const isc::log::MessageID HOOKS_NO_LOAD = "HOOKS_NO_LOAD";
+extern const isc::log::MessageID HOOKS_NO_UNLOAD = "HOOKS_NO_UNLOAD";
+extern const isc::log::MessageID HOOKS_NO_VERSION = "HOOKS_NO_VERSION";
+extern const isc::log::MessageID HOOKS_OPEN_ERROR = "HOOKS_OPEN_ERROR";
+extern const isc::log::MessageID HOOKS_STD_CALLOUT_REGISTERED = "HOOKS_STD_CALLOUT_REGISTERED";
+extern const isc::log::MessageID HOOKS_UNLOAD_ERROR = "HOOKS_UNLOAD_ERROR";
+extern const isc::log::MessageID HOOKS_UNLOAD_EXCEPTION = "HOOKS_UNLOAD_EXCEPTION";
+extern const isc::log::MessageID HOOKS_UNLOAD_FRAMEWORK_EXCEPTION = "HOOKS_UNLOAD_FRAMEWORK_EXCEPTION";
+extern const isc::log::MessageID HOOKS_UNLOAD_SUCCESS = "HOOKS_UNLOAD_SUCCESS";
+extern const isc::log::MessageID HOOKS_VERSION_EXCEPTION = "HOOKS_VERSION_EXCEPTION";
+
+} // namespace hooks
+} // namespace isc
+
+namespace {
+
+const char* values[] = {
+ "HOOKS_ALL_CALLOUTS_DEREGISTERED", "hook library at index %1 removed all callouts on hook %2",
+ "HOOKS_CALLOUTS_BEGIN", "begin all callouts for hook %1",
+ "HOOKS_CALLOUTS_COMPLETE", "completed callouts for hook %1 (total callouts duration: %2)",
+ "HOOKS_CALLOUTS_REMOVED", "callouts removed from hook %1 for library %2",
+ "HOOKS_CALLOUT_CALLED", "hooks library with index %1 has called a callout on hook %2 that has address %3 (callout duration: %4)",
+ "HOOKS_CALLOUT_DEREGISTERED", "hook library at index %1 deregistered a callout on hook %2",
+ "HOOKS_CALLOUT_ERROR", "error returned by callout on hook %1 registered by library with index %2 (callout address %3) (callout duration %4)",
+ "HOOKS_CALLOUT_EXCEPTION", "exception thrown by callout on hook %1 registered by library with index %2 (callout address %3): %4 (callout duration: %5)",
+ "HOOKS_CALLOUT_REGISTRATION", "hooks library with index %1 registering callout for hook '%2'",
+ "HOOKS_CLOSE_ERROR", "failed to close hook library %1: %2",
+ "HOOKS_HOOK_LIST_RESET", "the list of hooks has been reset",
+ "HOOKS_INCORRECT_VERSION", "hook library %1 is at version %2, require version %3",
+ "HOOKS_LIBRARY_CLOSED", "hooks library %1 successfully closed",
+ "HOOKS_LIBRARY_LOADED", "hooks library %1 successfully loaded",
+ "HOOKS_LIBRARY_LOADING", "loading hooks library %1",
+ "HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE", "hooks library %1 reports its multi-threading compatibility as %2",
+ "HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE", "hooks library %1 is not compatible with multi-threading",
+ "HOOKS_LIBRARY_UNLOADED", "hooks library %1 successfully unloaded",
+ "HOOKS_LIBRARY_UNLOADING", "unloading library %1",
+ "HOOKS_LIBRARY_VERSION", "hooks library %1 reports its version as %2",
+ "HOOKS_LOAD_ERROR", "'load' function in hook library %1 returned error %2",
+ "HOOKS_LOAD_EXCEPTION", "'load' function in hook library %1 threw an exception",
+ "HOOKS_LOAD_FRAMEWORK_EXCEPTION", "'load' function in hook library %1 threw an exception: reason %2",
+ "HOOKS_LOAD_SUCCESS", "'load' function in hook library %1 returned success",
+ "HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION", "'multi_threading_compatible' function in hook library %1 threw an exception",
+ "HOOKS_NO_LOAD", "no 'load' function found in hook library %1",
+ "HOOKS_NO_UNLOAD", "no 'unload' function found in hook library %1",
+ "HOOKS_NO_VERSION", "no 'version' function found in hook library %1",
+ "HOOKS_OPEN_ERROR", "failed to open hook library %1: %2",
+ "HOOKS_STD_CALLOUT_REGISTERED", "hooks library %1 registered standard callout for hook %2 at address %3",
+ "HOOKS_UNLOAD_ERROR", "'unload' function in hook library %1 returned error %2",
+ "HOOKS_UNLOAD_EXCEPTION", "'unload' function in hook library %1 threw an exception",
+ "HOOKS_UNLOAD_FRAMEWORK_EXCEPTION", "'unload' function in hook library %1 threw an exception, reason %2",
+ "HOOKS_UNLOAD_SUCCESS", "'unload' function in hook library %1 returned success",
+ "HOOKS_VERSION_EXCEPTION", "'version' function in hook library %1 threw an exception",
+ NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+
diff --git a/src/lib/hooks/hooks_messages.h b/src/lib/hooks/hooks_messages.h
new file mode 100644
index 0000000..a80626c
--- /dev/null
+++ b/src/lib/hooks/hooks_messages.h
@@ -0,0 +1,50 @@
+// File created from ../../../src/lib/hooks/hooks_messages.mes
+
+#ifndef HOOKS_MESSAGES_H
+#define HOOKS_MESSAGES_H
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace hooks {
+
+extern const isc::log::MessageID HOOKS_ALL_CALLOUTS_DEREGISTERED;
+extern const isc::log::MessageID HOOKS_CALLOUTS_BEGIN;
+extern const isc::log::MessageID HOOKS_CALLOUTS_COMPLETE;
+extern const isc::log::MessageID HOOKS_CALLOUTS_REMOVED;
+extern const isc::log::MessageID HOOKS_CALLOUT_CALLED;
+extern const isc::log::MessageID HOOKS_CALLOUT_DEREGISTERED;
+extern const isc::log::MessageID HOOKS_CALLOUT_ERROR;
+extern const isc::log::MessageID HOOKS_CALLOUT_EXCEPTION;
+extern const isc::log::MessageID HOOKS_CALLOUT_REGISTRATION;
+extern const isc::log::MessageID HOOKS_CLOSE_ERROR;
+extern const isc::log::MessageID HOOKS_HOOK_LIST_RESET;
+extern const isc::log::MessageID HOOKS_INCORRECT_VERSION;
+extern const isc::log::MessageID HOOKS_LIBRARY_CLOSED;
+extern const isc::log::MessageID HOOKS_LIBRARY_LOADED;
+extern const isc::log::MessageID HOOKS_LIBRARY_LOADING;
+extern const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE;
+extern const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE;
+extern const isc::log::MessageID HOOKS_LIBRARY_UNLOADED;
+extern const isc::log::MessageID HOOKS_LIBRARY_UNLOADING;
+extern const isc::log::MessageID HOOKS_LIBRARY_VERSION;
+extern const isc::log::MessageID HOOKS_LOAD_ERROR;
+extern const isc::log::MessageID HOOKS_LOAD_EXCEPTION;
+extern const isc::log::MessageID HOOKS_LOAD_FRAMEWORK_EXCEPTION;
+extern const isc::log::MessageID HOOKS_LOAD_SUCCESS;
+extern const isc::log::MessageID HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION;
+extern const isc::log::MessageID HOOKS_NO_LOAD;
+extern const isc::log::MessageID HOOKS_NO_UNLOAD;
+extern const isc::log::MessageID HOOKS_NO_VERSION;
+extern const isc::log::MessageID HOOKS_OPEN_ERROR;
+extern const isc::log::MessageID HOOKS_STD_CALLOUT_REGISTERED;
+extern const isc::log::MessageID HOOKS_UNLOAD_ERROR;
+extern const isc::log::MessageID HOOKS_UNLOAD_EXCEPTION;
+extern const isc::log::MessageID HOOKS_UNLOAD_FRAMEWORK_EXCEPTION;
+extern const isc::log::MessageID HOOKS_UNLOAD_SUCCESS;
+extern const isc::log::MessageID HOOKS_VERSION_EXCEPTION;
+
+} // namespace hooks
+} // namespace isc
+
+#endif // HOOKS_MESSAGES_H
diff --git a/src/lib/hooks/hooks_messages.mes b/src/lib/hooks/hooks_messages.mes
new file mode 100644
index 0000000..e33e7e4
--- /dev/null
+++ b/src/lib/hooks/hooks_messages.mes
@@ -0,0 +1,201 @@
+# Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+$NAMESPACE isc::hooks
+
+% 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_CALLOUTS_BEGIN begin all callouts for hook %1
+This debug message is issued when callout manager begins to invoke callouts
+for the hook. The argument specifies the hook name.
+
+% HOOKS_CALLOUTS_COMPLETE completed callouts for hook %1 (total callouts duration: %2)
+This debug message is issued when callout manager has completed execution
+of all callouts for the particular hook. The arguments specify the hook
+name and total execution time for all callouts in milliseconds.
+
+% 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_CALLOUT_CALLED hooks library with index %1 has called a callout on hook %2 that has address %3 (callout duration: %4)
+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.
+The message includes the callout execution time in milliseconds.
+
+% 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_CALLOUT_ERROR error returned by callout on hook %1 registered by library with index %2 (callout address %3) (callout duration %4)
+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.
+The error message includes the callout execution time in milliseconds.
+
+% HOOKS_CALLOUT_EXCEPTION exception thrown by callout on hook %1 registered by library with index %2 (callout address %3): %4 (callout duration: %5)
+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.
+The error message includes the callout execution time in milliseconds.
+
+% 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_CLOSE_ERROR failed to close hook library %1: %2
+Kea 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 Kea.
+
+% 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 Kea test suite, it should not be
+seen when running Kea in a production environment. If this appears,
+please report a bug through the usual channels.
+
+% HOOKS_INCORRECT_VERSION hook library %1 is at version %2, require version %3
+Kea has detected that the named hook library has been built against
+a version of Kea that is incompatible with the version of Kea
+running on your system. It has not loaded the library.
+
+This is most likely due to the installation of a new version of Kea
+without rebuilding the hook library. A rebuild and re-install of the
+library should fix the problem in most cases.
+
+% HOOKS_LIBRARY_CLOSED hooks library %1 successfully closed
+This information message is issued when a user-supplied hooks library
+has been successfully closed.
+
+% 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_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_LIBRARY_MULTI_THREADING_COMPATIBLE hooks library %1 reports its multi-threading compatibility as %2
+A debug message issued when the "multi_threading_compatible" function was
+called. The returned value (0 means not compatible, others compatible)
+is displayed.
+
+% HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE hooks library %1 is not compatible with multi-threading
+When multi-threading is enabled and the library is not compatible (either
+because the "multi_threading_compatible" function returned 0 or was not
+implemented) this error message is issued. The library must be removed
+from the configuration or the multi-threading disabled.
+
+% 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_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_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_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_LOAD_FRAMEWORK_EXCEPTION 'load' function in hook library %1 threw an exception: reason %2
+A "load" function was found in the library named in the message and
+was called. Either the hooks framework or the function threw an
+exception (an error indication) during execution, which is an error
+condition; the cause of the exception is recorded in the message.
+The library has been unloaded and no callouts from it will be
+installed.
+
+% 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_MULTI_THREADING_COMPATIBLE_EXCEPTION 'multi_threading_compatible' function in hook library %1 threw an exception
+This error message is issued if the multi_threading_compatible()
+function in the specified hooks library was called and generated an
+exception. The library is considered unusable and will not be loaded.
+
+% 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 Kea did not find a function named "version" in it. This function
+is required and should return the version of Kea against which the
+library was built. The value is used to check that the library was built
+against a compatible version of Kea. The library has not been loaded.
+
+% HOOKS_OPEN_ERROR failed to open hook library %1: %2
+Kea failed to open the specified hook library for the stated
+reason. The library has not been loaded. Kea will continue to
+function, but without the services offered by the library.
+
+% 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_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_UNLOAD_FRAMEWORK_EXCEPTION 'unload' function in hook library %1 threw an exception, reason %2
+During the unloading of a library, an "unload" function was found.
+It was called, but in the process either it or the hooks framework
+generated an exception (an error indication); the cause of the error
+is recorded in the message. The unload process continued after
+this message and the library has been unloaded.
+
+% 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_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/hooks_parser.cc b/src/lib/hooks/hooks_parser.cc
new file mode 100644
index 0000000..cfd4a7e
--- /dev/null
+++ b/src/lib/hooks/hooks_parser.cc
@@ -0,0 +1,110 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <cc/data.h>
+#include <cc/dhcp_config_error.h>
+#include <hooks/hooks_parser.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
+#include <util/strutil.h>
+#include <vector>
+
+using namespace std;
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace isc::dhcp;
+
+namespace isc {
+namespace hooks {
+
+// @todo use the flat style, split into list and item
+
+void
+HooksLibrariesParser::parse(HooksConfig& libraries, ConstElementPtr value) {
+ // Initialize.
+ libraries.clear();
+
+ if (!value) {
+ isc_throw(DhcpConfigError, "Tried to parse null hooks libraries");
+ }
+
+ // This is the new syntax. Iterate through it and get each map.
+ BOOST_FOREACH(ConstElementPtr library_entry, value->listValue()) {
+ ConstElementPtr parameters;
+
+ // Is it a map?
+ if (library_entry->getType() != Element::map) {
+ isc_throw(DhcpConfigError, "hooks library configuration error:"
+ " one or more entries in the hooks-libraries list is not"
+ " a map (" << library_entry->getPosition() << ")");
+ }
+
+ // Iterate through each element in the map. We check
+ // whether we have found a library element.
+ bool lib_found = false;
+
+ string libname = "";
+
+ // Let's explicitly reset the parameters, so we won't cover old
+ // values from the previous loop round.
+ parameters.reset();
+
+ BOOST_FOREACH(auto entry_item, library_entry->mapValue()) {
+ if (entry_item.first == "library") {
+ if (entry_item.second->getType() != Element::string) {
+ isc_throw(DhcpConfigError, "hooks library configuration"
+ " error: value of 'library' element is not a string"
+ " giving the path to a hooks library (" <<
+ entry_item.second->getPosition() << ")");
+ }
+
+ // Get the name of the library and add it to the list after
+ // removing quotes.
+ libname = (entry_item.second)->stringValue();
+
+ // Remove leading/trailing quotes and any leading/trailing
+ // spaces.
+ boost::erase_all(libname, "\"");
+ libname = isc::util::str::trim(libname);
+ if (libname.empty()) {
+ isc_throw(DhcpConfigError, "hooks library configuration"
+ " error: value of 'library' element must not be"
+ " blank (" <<
+ entry_item.second->getPosition() << ")");
+ }
+
+ // Note we have found the library name.
+ lib_found = true;
+ continue;
+ }
+
+ // If there are parameters, let's remember them.
+ if (entry_item.first == "parameters") {
+ parameters = entry_item.second;
+ continue;
+ }
+
+ // For all other parameters we will throw.
+ isc_throw(DhcpConfigError, "unknown hooks library parameter: "
+ << entry_item.first << "("
+ << library_entry->getPosition() << ")");
+ }
+
+ if (! lib_found) {
+ isc_throw(DhcpConfigError, "hooks library configuration error:"
+ " one or more hooks-libraries elements are missing the"
+ " name of the library" <<
+ " (" << library_entry->getPosition() << ")");
+ }
+
+ libraries.add(libname, parameters);
+ }
+}
+
+}
+}
diff --git a/src/lib/hooks/hooks_parser.h b/src/lib/hooks/hooks_parser.h
new file mode 100644
index 0000000..4bec9a6
--- /dev/null
+++ b/src/lib/hooks/hooks_parser.h
@@ -0,0 +1,65 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HOOKS_PARSER_H
+#define HOOKS_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <hooks/hooks_config.h>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Parser for hooks library list
+///
+/// This parser handles the list of hooks libraries. This is an optional list,
+/// which may be empty, and is encapsulated into a @ref HooksConfig object.
+class HooksLibrariesParser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Parses parameters value
+ ///
+ /// Parses configuration entry (list of parameters) and adds each element
+ /// to the hooks libraries list. The method also checks whether the
+ /// list of libraries is the same as that already loaded. If not, it
+ /// checks each of the libraries in the list for validity (they exist and
+ /// have a "version" function that returns the correct value).
+ ///
+ /// The syntax for specifying hooks libraries allow for library-specific
+ /// parameters to be specified along with the library, e.g.
+ ///
+ /// @code
+ /// "hooks-libraries": [
+ /// {
+ /// "library": "hook-lib-1.so",
+ /// "parameters": {
+ /// "alpha": "a string",
+ /// "beta": 42
+ /// }
+ /// },
+ /// :
+ /// ]
+ /// @endcode
+ ///
+ /// The parsing code only checks that:
+ ///
+ /// -# Each element in the hooks-libraries list is a map
+ /// -# The map contains an element "library" whose value is a not blank string
+ /// -# That there is an optional 'parameters' element.
+ /// -# That there are no other element.
+ ///
+ /// This method stores parsed libraries in libraries.
+ ///
+ /// @param libraries parsed libraries information will be stored here
+ /// @param value pointer to the content to be parsed
+ void parse(HooksConfig& libraries, isc::data::ConstElementPtr value);
+};
+
+}; // namespace isc::hooks
+}; // namespace isc
+
+#endif
diff --git a/src/lib/hooks/hooks_user.dox b/src/lib/hooks/hooks_user.dox
new file mode 100644
index 0000000..2a51b93
--- /dev/null
+++ b/src/lib/hooks/hooks_user.dox
@@ -0,0 +1,1670 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// Note: the prefix "hooksdg" to all labels is an abbreviation for "Hooks
+// Developer's Guide" and is used to prevent a clash with symbols in any
+// other Doxygen file.
+
+/**
+ @page hooksdgDevelopersGuide Hooks Developer's Guide
+
+ @section hooksdgIntroduction Introduction
+
+Although the Kea framework and its DHCP programs
+provide comprehensive functionality, there will be times when it does
+not quite do what you require: the processing has to be extended in some
+way to solve your problem.
+
+Since the Kea source code is freely available (Kea being an
+open-source project), one option is to modify it to do what
+you want. Whilst perfectly feasible, there are drawbacks:
+
+- Although well-documented, Kea is a large program. Just
+understanding how it works will take a significant amount of time. In
+addition, despite the fact that its object-oriented design keeps the
+coupling between modules to a minimum, an inappropriate change to one
+part of the program during the extension could cause another to
+behave oddly or to stop working altogether.
+
+- The change may need to be re-applied or re-written with every new
+version of Kea. As new functionality is added or bugs are fixed,
+the code or algorithms in the core software may change - and may change
+significantly.
+
+To overcome these problems, Kea provides the "Hooks" interface -
+a defined interface for third-party or user-written code. (For ease of
+reference in the rest of this document, all such code will be referred
+to as "user code".) At specific points in its processing
+("hook points") Kea will make a call to this code. The call passes
+data that the user code can examine and, if required, modify.
+Kea uses the modified data in the remainder of its processing.
+
+In order to minimize the interaction between Kea and the user code,
+the latter is built independently of Kea in the form of one or more
+dynamic shared objects, called here (for historical reasons), shared
+libraries. These are made known to Kea through its configuration
+mechanism, and Kea loads the library at run time. Libraries can be
+unloaded and reloaded as needed while Kea is running.
+
+Use of a defined API and the Kea configuration mechanism means that
+as new versions of Kea are released, there is no need to modify
+the user code. Unless there is a major change in an interface
+(which will be clearly documented), all that will be required is a rebuild
+of the libraries.
+
+@note Although the defined interface should not change, the internals
+of some of the classes and structures referenced by the user code may
+change between versions of Kea. These changes have to be reflected
+in the compiled version of the software, hence the need for a rebuild.
+
+
+@subsection hooksdgLanguages Languages
+
+The core of Kea is written in C++. While it is the intention to
+provide interfaces into user code written in other languages, the initial
+versions of the Hooks system required that user code be written in C++.
+It is no longer the case and there are examples of hooks written for
+instance in Python but this guide does not document how to do that.
+All examples in this guide are in C++.
+
+
+@subsection hooksdgTerminology Terminology
+
+In the remainder of this guide, the following terminology is used:
+
+- Hook/Hook Point - used interchangeably, this is a point in the code at
+which a call to user functions is made. Each hook has a name and
+each hook can have any number (including 0) of user functions
+attached to it.
+
+- Callout - a user function called by the server at a hook
+point. This is so-named because the server "calls out" to the library
+to execute a user function.
+
+- Framework function - the functions that a user library needs to
+supply in order for the hooks framework to load and unload the library.
+
+- User code/user library - non-Kea code that is compiled into a
+shared library and loaded by Kea into its address space.
+
+
+@section hooksdgTutorial Tutorial
+
+To illustrate how to write code that integrates with Kea, we will
+use the following (rather contrived) example:
+
+<i>The Kea DHCPv4 server is used to allocate IPv4 addresses to clients
+(as well as to pass them other information such as the address of DNS
+servers). We will suppose that we need to classify clients requesting
+IPv4 addresses according to their hardware address, and want to log both
+the hardware address and allocated IP address for the clients of interest.</i>
+
+The following sections describe how to implement these requirements.
+The code presented here is not efficient and there are better ways of
+doing the task. The aim however, is to illustrate the main features of
+user hooks code, not to provide an optimal solution.
+
+
+@subsection hooksdgFrameworkFunctions Framework Functions
+
+Loading and initializing a library holding user code makes use
+of three (user-supplied) functions:
+
+- version - defines the version of Kea code with which the user-library
+is built
+- load - called when the library is loaded by the server.
+- unload - called when the library is unloaded by the server.
+- multi_threading_compatible - defines the compatibility (or not) of
+the user-library and a multi-threaded DHCP service.
+
+Of these, only "version" is mandatory, although in our example, all four
+are used.
+
+@subsubsection hooksdgVersionFunction The "version" Function
+
+"version" is used by the hooks framework to check that the libraries
+it is loading are compatible with the version of Kea being run.
+Although the hooks system allows Kea and user code to interface
+through a defined API, the relationship is somewhat tight in that the
+user code will depend on the internal structures of Kea. If these
+change - as they can between Kea releases - and Kea is run with
+a version of user code built against an earlier version of Kea, a program
+crash could result.
+
+To guard against this, the "version" function must be provided in every
+library. It returns a constant defined in header files of the version
+of Kea against which it was built. The hooks framework checks this
+for compatibility with the running version of Kea before loading
+the library.
+
+In this tutorial, we'll put "version" in its own file, version.cc. The
+contents are:
+
+@code
+// version.cc
+
+#include <hooks/hooks.h>
+
+extern "C" {
+
+int version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+}
+@endcode
+
+The file "hooks/hooks.h" is specified relative to the Kea libraries
+source directory - this is covered later in the section @ref hooksdgBuild.
+It defines the symbol KEA_HOOKS_VERSION, which has a value that changes
+on every release of Kea: this is the value that needs to be returned
+to the hooks framework.
+
+A final point to note is that the definition of "version" is enclosed
+within 'extern "C"' braces. All functions accessed by the hooks
+framework use C linkage, mainly to avoid the name mangling that
+accompanies use of the C++ compiler, but also to avoid issues related
+to namespaces.
+
+@subsubsection hooksdgLoadUnloadFunctions The "load" and "unload" Functions
+
+As the names suggest, "load" is called when a library is loaded and
+"unload" called when it is unloaded. (It is always guaranteed that
+"load" is called: "unload" may not be called in some circumstances,
+e.g., if the system shuts down abnormally.) These functions are the
+places where any library-wide resources are allocated and deallocated.
+"load" is also the place where any callouts with non-standard names
+(names that are not hook point names) can be registered:
+this is covered further in the section @ref hooksdgCalloutRegistration.
+
+The example does not make any use callouts with non-standard names. However,
+as our design requires that the log file be open while Kea is active
+and the library loaded, we'll open the file in the "load" function and close
+it in "unload".
+
+We create two files, one for the file handle declaration:
+
+@code
+// library_common.h
+
+#ifndef LIBRARY_COMMON_H
+#define LIBRARY_COMMON_H
+
+#include <fstream>
+
+// "Interesting clients" log file handle declaration.
+extern std::fstream interesting;
+
+#endif // LIBRARY_COMMON_H
+@endcode
+
+... and one to hold the "load" and "unload" functions:
+
+@code
+// load_unload.cc
+
+#include <hooks/hooks.h>
+#include "library_common.h"
+
+using namespace isc::hooks;
+
+// "Interesting clients" log file handle definition.
+std::fstream interesting;
+
+extern "C" {
+
+int load(LibraryHandle&) {
+ interesting.open("/data/clients/interesting.log",
+ std::fstream::out | std::fstream::app);
+ return (interesting ? 0 : 1);
+}
+
+int unload() {
+ if (interesting) {
+ interesting.close();
+ }
+ return (0);
+}
+
+}
+@endcode
+
+Notes:
+- The file handle ("interesting") is declared in a header file and defined
+outside of any function. This means it can be accessed by any function
+within the user library. For convenience, the definition is in the
+load_unload.cc file.
+- "load" is called with a LibraryHandle argument, this being used in
+the registration of functions. As no functions are being registered
+in this example, the argument specification omits the variable name
+(whilst retaining the type) to avoid an "unused variable" compiler
+warning. (The LibraryHandle and its use is discussed in the section
+@ref hooksdgLibraryHandle.)
+- In the initial version of the hooks framework, it was not possible to pass
+any configuration information to the "load" function. The name of the log
+file had therefore to be hard-coded as an absolute path name or communicated
+to the user code by some other means.
+- "load" must return 0 on success and non-zero on error. The hooks framework
+will abandon the loading of the library if "load" returns an error status.
+(In this example, "interesting" can be tested as a boolean value,
+returning "true" if the file opened successfully.)
+- "unload" closes the log file if it is open and is a no-op otherwise. As
+with "load", a zero value must be returned on success and a non-zero value
+on an error. The hooks framework will record a non-zero status return
+as an error in the current Kea log but otherwise ignore it.
+- As before, the function definitions are enclosed in 'extern "C"' braces.
+
+In some cases to restrict the library loading to DHCP servers so it
+cannot be loaded by the DDNS server or the Control Agent.
+The best way to perform this is to check the process name returned
+by @c isc::dhcp::Daemon::getProcName() static / class method declared
+in process/daemon.h header against "kea-dhcp4" and "kea-dhcp6"
+(other values are "kea-dhcp-ddns", "kea-ctrl-agent" and "kea-netconf").
+If you'd like to check the address family too it is returned in DHCP servers
+by isc::dhcp::CfgMgr::instance().getFamily() declared in dhcpsrv/cfgmgr.h
+with AF_INET and AF_INET6 values.
+
+@subsubsection hooksdgMultiThreadingCompatibleFunction The "multi_threading_compatible" function
+
+"multi_threading_compatible" is used by the hooks framework to check
+if the libraries it is loading are compatible with the DHCPv4 or DHCPv6
+server multi-threading configuration. The value 0 means not compatible
+and is the default when the function is not implemented. Non 0 values
+mean compatible.
+
+If your code implements it and returns the value 0 it is recommended
+to document the reason so someone revisiting the code will not by
+accident change the code.
+
+To be compatible means:
+- the code associated with DHCP packet processing callouts e.g.
+pkt4_receive or pkt6_send must be thread safe so the multi-threaded
+DHCP service can simultaneously call more than once one of these callouts.
+- commands registered by a library are not required to be thread safe because
+commands are executed by the main thread. Now it is a good idea to make
+them thread safe and to document cases where they are not.
+- when a library implements a thread safe backend API (e.g. host data
+source) the service methods must be thread safe.
+- a library which modifies the internal configuration of the server,
+e.g. creates or deletes a subnet, it must enter a critical section using
+the @c isc::util::MultiThreadingCriticalSection RAII class.
+
+In the tutorial, we'll put "multi_threading_compatible" in its own file,
+multi_threading_compatible.cc. The contents are:
+
+@code
+// multi_threading_compatible.cc
+
+extern "C" {
+
+int multi_threading_compatible() {
+ return (1);
+}
+
+}
+@endcode
+
+and for a command creating a new subnet:
+
+@code
+#include <util/multi_threading_mgr.h>
+
+int commandHandler(CalloutHandle& handle) {
+ ...
+ {
+ // Enter the critical section.
+ isc::util::MultiThreadingCriticalSection ct;
+ <add the subnet>
+ // Leave the critical section.
+ }
+ ...
+}
+@endcode
+
+@ref hooksMultiThreading provides more details about thread safety
+requirements.
+
+@subsection hooksdgCallouts Callouts
+
+Having sorted out the framework, we now come to the functions that
+actually do something. These functions are known as "callouts" because
+the Kea code "calls out" to them. Each Kea server has a number of
+hooks to which callouts can be attached: server-specific documentation
+describes in detail the points in the server at which the hooks are
+present together with the data passed to callouts attached to them.
+
+Before we continue with the example, we'll discuss how arguments are
+passed to callouts and information is returned to the server. We will
+also discuss how information can be moved between callouts.
+
+@subsubsection hooksdgCalloutSignature The Callout Signature
+
+All callouts are declared with the signature:
+@code
+extern "C" {
+int callout(CalloutHandle& handle);
+}
+@endcode
+
+(As before, the callout is declared with "C" linkage.) Information is passed
+between Kea and the callout through name/value pairs in the @c CalloutHandle
+object. The object is also used to pass information between callouts on a
+per-request basis. (Both of these concepts are explained below.)
+
+A callout returns an @c int as a status return. A value of 0 indicates
+success, anything else signifies an error. The status return has no
+effect on server processing; the only difference between a success
+and error code is that if the latter is returned, the server will
+log an error, specifying both the library and hook that generated it.
+Effectively the return status provides a quick way for a callout to log
+error information to the Kea logging system.
+
+@subsubsection hooksdgArguments Callout Arguments
+
+The @c CalloutHandle object provides two methods to get and set the
+arguments passed to the callout. These methods are called (naturally
+enough) getArgument and setArgument. Their usage is illustrated by the
+following code snippets.
+
+@code
+ // Server-side code snippet to show the setting of arguments
+
+ int count = 10;
+ boost::shared_ptr<Pkt4> pktptr = ... // Set to appropriate value
+
+ // Assume that "handle" has been created
+ handle.setArgument("data_count", count);
+ handle.setArgument("inpacket", pktptr);
+
+ // Call the callouts attached to the hook
+ ...
+
+ // Retrieve the modified values
+ handle.getArgument("data_count", count);
+ handle.getArgument("inpacket", pktptr);
+@endcode
+
+In the callout
+
+@code
+ int number;
+ boost::shared_ptr<Pkt4> packet;
+
+ // Retrieve data set by the server.
+ handle.getArgument("data_count", number);
+ handle.getArgument("inpacket", packet);
+
+ // Modify "number"
+ number = ...;
+
+ // Update the arguments to send the value back to the server.
+ handle.setArgument("data_count", number);
+@endcode
+
+As can be seen @c getArgument is used to retrieve data from the
+@c CalloutHandle, and @c setArgument used to put data into it. If a callout
+wishes to alter data and pass it back to the server, it should retrieve
+the data with @c getArgument, modify it, and call @c setArgument to send
+it back.
+
+There are several points to be aware of:
+
+- the data type of the variable in the call to @c getArgument must match
+the data type of the variable passed to the corresponding @c setArgument
+<B>exactly</B>: using what would normally be considered to be a
+"compatible" type is not enough. For example, if the server passed
+an argument as an @c int and the callout attempted to retrieve it as a
+@c long, an exception would be thrown even though any value that can
+be stored in an @c int will fit into a @c long. This restriction also
+applies the "const" attribute but only as applied to data pointed to by
+pointers, e.g., if an argument is defined as a @c char*, an exception will
+be thrown if an attempt is made to retrieve it into a variable of type
+@c const @c char*. (However, if an argument is set as a @c const @c int,
+it can be retrieved into an @c int.) The documentation of each hook
+point will detail the data type of each argument.
+- Although all arguments can be modified, some altered values may not
+be read by the server. (These would be ones that the server considers
+"read-only".) Consult the documentation of each hook to see whether an
+argument can be used to transfer data back to the server.
+- 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 server even if no call is made to setArgument.
+
+In all cases, consult the documentation for the particular hook to see whether
+parameters can be modified. As a general rule:
+
+- Do not alter arguments unless you mean the change to be reflected in
+the server.
+- If you alter an argument, call @c CalloutHandle::setArgument to update the
+value in the @c CalloutHandle object.
+
+@subsubsection hooksdgNextStep The Next step status
+
+Note: This functionality used to be provided in Kea 0.9.2 and earlier using
+boolean skip flag. See @ref hooksdgSkipFlag for explanation and tips how
+to migrate your hooks code to this new API.
+
+When a to callouts attached to a hook returns, the server will usually continue
+its processing. However, a callout might have done something that means that
+the server should follow another path. Possible actions a server could take
+include:
+
+- Continue as usual. This is the default value. Unless callouts explicitly
+change the status, the server will continue processing. There is no need
+to set the status, unless one callout wants to override the status set
+by another callout. This action is represented by CalloutHandle::NEXT_STEP_CONTINUE.
+
+- Skip the next stage of processing because the callout has already
+done it. For example, a hook is located just before the DHCP server
+allocates an address to the client. A callout may decide to allocate
+special addresses for certain clients, in which case it needs to tell
+the server not to allocate an address in this case. This action is
+hook specific and is represented by CalloutHandle::NEXT_STEP_SKIP.
+
+- Drop the packet and continue with the next request. A possible scenario
+is a server where a callout inspects the hardware address of the client
+sending the packet and compares it against a black list; if the address
+is on it, the callout notifies the server to drop the packet. This
+action is represented by CalloutHandle::NEXT_STEP_DROP.
+
+To handle these common cases, the @c CalloutHandle has a setStatus method.
+This is set by a callout when it wishes the server to change the normal
+processing. Exact meaning is hook specific. Please consult hook API
+documentation for details. For historic reasons (Kea 0.9.2 used a single
+boolean flag called skip that also doubled in some cases as an indicator
+to drop the packet) several hooks use SKIP status to drop the packet.
+
+The methods to get and set the "skip" or "drop" state are getStatus and
+setStatus. Their usage is intuitive:
+
+@code
+ // Get the current setting of the next step status.
+ auto status = handle.getStatus();
+
+ if (status == CalloutHandle::NEXT_STEP_DROP)
+ // Do something...
+ :
+
+ if (status == CalloutHandle::NEXT_STEP_SKIP)
+ // Do something...
+ :
+
+ // Do some processing...
+ :
+ if (lease_allocated) {
+ // Flag the server to skip the next step of the processing as we
+ // already have an address.
+ handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+ }
+ return;
+
+@endcode
+
+Like arguments, the next step status is passed to all callouts on a hook. Callouts
+later in the list are able to examine (and modify) the settings of earlier ones.
+
+If using multiple libraries, when the library wants to drop the current packet,
+the DROP status must be used instead of the SKIP status so that the packet
+processing ends at that specific hook point.
+
+It is recommended for all callouts to check the status before doing any
+processing. As callouts can modify the status, it is recommended to take good
+care when doing so, because this will have impact on all remaining hooks as well.
+It is highly recommended to not reset the SKIP or DROP status to CONTINUE, even
+though possible, so that the rest of the loaded hooks and the server can check
+and perform the proper action.
+
+Some hook points handle special functionality for the server, like pkt4_receive,
+pkt6_receive, which handle unpacking of the received packet, pkt4_send, pkt6_send,
+which handle packing of the response packet.
+
+If the hook handles these actions and sets the next step flag to SKIP, it should
+also perform a check for the SKIP flag before anything else. If it is already
+set, do not pack/unpack the packet (other library, or even the same library, if
+loaded multiple times, has done it already). Some libraries might also need to
+throw exceptions in such cases because they need to perform specific actions before
+pack/unpack (eg. addOption/delOption before pack action), which have no effect if
+pack/unpack action is done previously by some other library.
+
+@code
+ // Check if other library has already set SKIP flag and performed unpack
+ // so that unpack is skipped
+ if (handle.getStatus() != CalloutHandle::NEXT_STEP_SKIP) {
+ query->unpack();
+ }
+@endcode
+
+@code
+ // Check the status state.
+ auto status = handle.getStatus();
+ if (status == CalloutHandle::NEXT_STEP_SKIP) {
+ isc_throw(InvalidOperation, "packet pack already handled");
+ }
+ ...
+ response->delOption(DEL_OPTION_CODE);
+ ...
+ response->addOption(ADD_OPTION_CODE);
+ ...
+ response->pack();
+@endcode
+
+As stated before, the order of loading libraries is critical in achieving the
+desired behavior, so please read @ref hooksdgMultipleLibraries when configuring
+multiple libraries.
+
+@subsubsection hooksdgSkipFlag The "Skip" Flag (deprecated)
+
+In releases 0.9.2 and earlier, the functionality currently offered by next step
+status (see @ref hooksdgNextStep) was provided by
+a boolean flag called "Skip". However, since it only allowed to either continue
+or skip the next processing step and was not extensible to other decisions,
+setSkip(bool) call was replaced with a setStatus(enum) in Kea 1.0. This
+new approach is extensible. If we decide to add new results (e.g., WAIT
+or RATELIMIT), we will be able to do so without changing the API again.
+
+If you have your hooks libraries that take advantage of skip flag, migrating
+to the next step status is very easy. See @ref hooksdgNextStep for detailed
+explanation of the new status field.
+
+To migrate, replace this old code:
+@code
+handle.setSkip(false); // This is the default.
+
+handle.setSkip(true); // Tell the server to skip the next processing step.
+
+bool skip = hangle.getSkip(); // Check the skip flag state.
+if (skip) {
+ ...
+}
+@endcode
+
+with this:
+
+@code
+// This is the default.
+handle.setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+
+// Tell the server to skip the next processing step.
+handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+
+// Check the status state.
+auto status = handle.getStatus();
+if (status == CalloutHandle::NEXT_STEP_SKIP) {
+ ...
+}
+@endcode
+
+@subsubsection hooksdgCalloutContext Per-Request Context
+
+Although the Kea modules can be characterized as handling a single
+packet at a time - e.g., the DHCPv4 server receives a DHCPDISCOVER packet,
+processes it and responds with an DHCPOFFER, this may not always be true.
+Future developments may have the server processing multiple packets
+simultaneously, or to suspend processing on a packet and resume it at
+a later time after other packets have been processed.
+
+As well as argument information, the @c CalloutHandle object can be used by
+callouts to attach information to a packet being handled by the server.
+This information (known as "context") is not used by the server: its purpose
+is to allow callouts to pass information between one another on a
+per-packet basis.
+
+Context associated with a packet only exists only for the duration of the
+processing of that packet: when processing is completed, the context is
+destroyed. A new packet starts with a new (empty) context. Context is
+particularly useful in servers that may be processing multiple packets
+simultaneously: callouts can effectively attach data to a packet that
+follows the packet around the system.
+
+Context information is held as name/value pairs in the same way
+as arguments, being accessed by the pair of methods @c setContext and
+@c getContext. They have the same restrictions as the @c setArgument and
+@c getArgument methods - the type of data retrieved from context must
+<B>exactly</B> match the type of the data set.
+
+The example in the next section illustrates their use.
+
+
+@subsection hooksdgExampleCallouts Example Callouts
+
+Continuing with the tutorial, the requirements need us to retrieve the
+hardware address of the incoming packet, classify it, and write it,
+together with the assigned IP address, to a log file. Although we could
+do this in one callout, for this example we'll use two:
+
+- pkt4_receive - a callout on this hook is invoked when a packet has been
+received and has been parsed. It is passed a single argument, "query4"
+which is an isc::dhcp::Pkt4Ptr object, holding a pointer to the
+isc::dhcp::Pkt4 object (representing a DHCPv4 packet). We will do the
+classification here.
+
+- pkt4_send - called when a response is just about to be sent back to
+the client. It is passed a single argument "response4". This is the
+point at which the example code will write the hardware and IP addresses
+to the log file.
+
+The standard for naming callouts is to give them the same name as
+the hook. If this is done, the callouts will be automatically found
+by the Hooks system (this is discussed further in section @ref
+hooksdgCalloutRegistration). For our example, we will assume this is the
+case, so the code for the first callout (used to classify the client's
+hardware address) is:
+
+@code
+// pkt_receive4.cc
+
+#include <hooks/hooks.h>
+#include <dhcp/pkt4.h>
+#include "library_common.h"
+
+#include <string>
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace std;
+
+extern "C" {
+
+// This callout is called at the "pkt4_receive" hook.
+int pkt4_receive(CalloutHandle& handle) {
+
+ // A pointer to the packet is passed to the callout via a "boost" smart
+ // pointer. The include file "pkt4.h" typedefs a pointer to the Pkt4
+ // object as Pkt4Ptr. Retrieve a pointer to the object.
+ Pkt4Ptr query4_ptr;
+ handle.getArgument("query4", query4_ptr);
+
+ // Point to the hardware address.
+ HWAddrPtr hwaddr_ptr = query4_ptr->getHWAddr();
+
+ // The hardware address is held in a public member variable. We'll classify
+ // it as interesting if the sum of all the bytes in it is divisible by 4.
+ // (This is a contrived example after all!)
+ long sum = 0;
+ for (int i = 0; i < hwaddr_ptr->hwaddr_.size(); ++i) {
+ sum += hwaddr_ptr->hwaddr_[i];
+ }
+
+ // Classify it.
+ if (sum % 4 == 0) {
+ // Store the text form of the hardware address in the context to pass
+ // to the next callout.
+ string hwaddr = hwaddr_ptr->toText();
+ handle.setContext("hwaddr", hwaddr);
+ }
+
+ return (0);
+};
+
+}
+@endcode
+
+The "pkt4_receive" callout placed the hardware address of an interesting client in
+the "hwaddr" context for the packet. Turning now to the callout that will
+write this information to the log file:
+
+@code
+// pkt4_send.cc
+
+#include <hooks/hooks.h>
+#include <dhcp/pkt4.h>
+#include "library_common.h"
+
+#include <string>
+
+using namespace isc::dhcp;
+using namespace isc::hooks;
+using namespace std;
+
+extern "C" {
+
+// This callout is called at the "pkt4_send" hook.
+int pkt4_send(CalloutHandle& handle) {
+
+ // Obtain the hardware address of the "interesting" client. We have to
+ // use a try...catch block here because if the client was not interesting,
+ // no information would be set and getArgument would thrown an exception.
+ string hwaddr;
+ try {
+ handle.getContext("hwaddr", hwaddr);
+
+ // getContext didn't throw so the client is interesting. Get a pointer
+ // to the reply.
+ Pkt4Ptr response4_ptr;
+ handle.getArgument("response4", response4_ptr);
+
+ // Get the string form of the IP address.
+ string ipaddr = response4_ptr->getYiaddr().toText();
+
+ // Write the information to the log file.
+ interesting << hwaddr << " " << ipaddr << "\n";
+
+ // ... and to guard against a crash, we'll flush the output stream.
+ flush(interesting);
+
+ } catch (const NoSuchCalloutContext&) {
+ // No such element in the per-request context with the name "hwaddr".
+ // This means that the request was not an interesting, so do nothing
+ // and dismiss the exception.
+ }
+
+ return (0);
+}
+
+}
+@endcode
+
+
+@subsection hooksdgLogging Logging in the Hooks Library
+
+Hooks libraries take part in the DHCP message processing. They also often
+modify the server's behavior by taking responsibility for processing
+the DHCP message at certain stages and instructing the server to skip
+the default processing for that stage. Thus, hooks libraries play an
+important role in the DHCP server operation and, depending on their
+purpose, they may have high complexity, which increases likelihood of the
+defects in the libraries.
+
+All hooks libraries should use Kea logging system to facilitate diagnostics
+of the defects in the libraries and issues with the DHCP server's operation.
+Even if the issue doesn't originate in the hooks library itself, the use
+of the library may uncover issues in the Kea code that only
+manifest themselves in some special circumstances.
+
+Hooks libraries use the Kea logging system in the same way as any other
+standard Kea library. A hooks library should have at least one logger
+defined, but may have multiple loggers if it is desired
+to separate log messages from different functional parts of the library.
+
+Assuming that it has been decided to use logging in the hooks library, the
+implementor must select a unique name for the logger. Ideally the name
+should have some relationship with the name of the library so that it is
+easy to distinguish messages logged from this library. For example,
+if the hooks library is used to capture incoming and outgoing DHCP
+messages, and the name of the library is "libkea-packet-capture",
+a suitable logger name could be "packet-capture".
+
+In order to use a logger within the library, the logger should be declared
+in a header file, which must be included in all files using
+the logger:
+
+@code
+#ifndef PACKET_CAPTURE_LOG_H
+#define PACKET_CAPTURE_LOG_H
+
+#include <log/message_initializer.h>
+#include <log/macros.h>
+#include <user_chk_messages.h>
+
+namespace packet_capture {
+
+extern isc::log::Logger packet_capture_logger;
+
+}
+
+#endif
+@endcode
+
+The logger should be defined and initialized in the implementation file,
+as illustrated below:
+
+@code
+#include <packet_capture_log.h>
+
+namespace packet_capture {
+
+isc::log::Logger packet_capture_logger("packet-capture");
+
+}
+@endcode
+
+These files may contain multiple logger declarations and initializations
+when the use of more than one logger is desired.
+
+The next step is to add the appropriate message file as described in the
+@ref logMessageFiles.
+
+The implementor must make sure that log messages appear in the right
+places and that they are logged at the appropriate level. The choice
+of the place where the message should appear is not always obvious:
+it depends if the particular function being called already logs enough
+information and whether adding log message before and/or after the
+call to this function would simply duplicate some messages. Sometimes
+the choice whether the log message should appear within the function or
+outside of it depends on the level of details available for logging. For
+example, in many cases it is desirable to include the client identifier
+or transaction id of the DHCP packet being processed in logging message.
+If this information is available at the higher level but not in the
+function being called, it is often better to place the log message at
+higher level. However, the function parameters list could be extended
+to include the additional information, and to be logged and the logging
+call made from within the function.
+
+Ideally, the hooks library should contain debug log messages (traces)
+in all significant decision points in the code, with the information as to
+how the code hit this decision point, how it will proceed and why.
+However, care should be taken when selecting the log level for those
+messages, because selecting too high logging level may impact the
+performance of the system. For this reason, traces (messages of
+the debug severity) should use different debug levels for the
+messages of different importance or having different performance
+requirements to generate the log message. For example, generation of
+a log message, which prints full details of a packet, usually requires
+more CPU bandwidth than the generation of the message which only prints
+the packet type and length. Thus, the former should be logged at
+lower debug level (see @ref logSeverity for details of using
+various debug levels using "dbglevel" parameter).
+
+All loggers defined within the hooks libraries derive the default
+configuration from the root logger. For example, when the hooks
+library is attached to the DHCPv4 server, the root logger name is
+"kea-dhcp4", and the library by default uses configuration of this
+logger. The configuration of the library's logger can
+be modified by adding a configuration entry for it
+to the configuration file. In case of the "packet-capture"
+logger declared above, the full name of the logger in the
+configuration file will be "kea-dhcp4.packet-capture". The
+configuration specified for this logger will override the default
+configuration derived from the root logger.
+
+
+@subsection hooksdgBuild Building the Library
+
+Building the code requires building a sharable library. This requires
+the the code be compiled as position-independent code (using the
+compiler's "-fpic" switch) and linked as a shared library (with the
+linker's "-shared" switch). The build command also needs to point to
+the Kea include directory and link in the appropriate libraries.
+
+Assuming that Kea has been installed in the default location, the
+command line needed to create the library using the Gnu C++ compiler on a
+Linux system is:
+
+@code
+g++ -I <install-dir>/include/kea -L <install-dir>/lib -fpic -shared -o example.so \
+ load_unload.cc pkt4_receive.cc pkt4_send.cc version.cc \
+ -lkea-dhcpsrv -lkea-dhcp++ -lkea-hooks -lkea-log -lkea-util -lkea-exceptions
+@endcode
+
+Notes:
+- Replace "<install-dir>" with the location in which you installed Kea. Unless
+you specified the "--prefix" switch on the "configure" command line when
+building Kea, it will be installed in the default location, usually /usr/local.
+- The compilation command and switches required may vary depending on
+your operating system and compiler - consult the relevant documentation
+for details.
+- The list of libraries that need to be included in the command line
+depends on the functionality used by the hook code and the module to
+which they are attached. Depending on operating system, you may also need
+to explicitly list libraries on which the Kea libraries you link against depend.
+
+
+@subsection hooksdgConfiguration Configuring the Hooks Library
+
+The final step is to make the library known to Kea. The configuration
+keywords of all Kea modules to which hooks can be added contain the
+"hooks-libraries" element and user libraries are added to this. (The Kea
+hooks system can handle multiple libraries - this is discussed below.)
+
+To add the example library (assumed to be in /usr/local/lib) to the
+DHCPv4 module, it must be listed in the "hooks-libraries" element of the
+"Dhcp4" part of the configuration file:
+
+@code
+"Dhcp4": {
+ :
+ "hooks-libraries": [
+ {
+ "library": "/usr/local/lib/example.so"
+ }
+ ]
+ :
+}
+@endcode
+(Note that "hooks" is plural.)
+
+Each entry in the "hooks-libraries" list is a structure (a "map" in JSON
+parlance) that holds the following element:
+- library - the name of the library to load. This must be a string.
+
+@note The syntax of the hooks-libraries configuration element has changed
+since kea 0.9.2 (in that version, "hooks-libraries" was just a list of
+libraries). This change is in preparation for the introduction of
+library-specific parameters, which will be added to Kea in a version after 1.0.
+
+The DHCPv4 server will load the library and execute the callouts each time a
+request is received.
+
+@note All the above assumes that the hooks library will be used with a
+version of Kea that is dynamically-linked. For information regarding
+running hooks libraries against a statically-linked Kea, see @ref
+hooksdgStaticallyLinkedKea.
+
+@section hooksdgAdvancedTopics Advanced Topics
+
+
+@subsection hooksdgContextCreateDestroy Context Creation and Destruction
+
+As well as the hooks defined by the server, the hooks framework defines
+two hooks of its own, "context_create" and "context_destroy". The first
+is called when a request is created in the server, before any of the
+server-specific hooks gets called. It's purpose it to allow a library
+to initialize per-request context. The second is called after all
+server-defined hooks have been processed, and is to allow a library to
+tidy up.
+
+As an example, the "pkt4_send" example above required that the code
+check for an exception being thrown when accessing the "hwaddr" context
+item in case it was not set. An alternative strategy would have been to
+provide a callout for the "context_create" hook and set the context item
+"hwaddr" to an empty string. Instead of needing to handle an exception,
+"pkt4_send" would be guaranteed to get something when looking for
+the hwaddr item and so could write or not write the output depending on
+the value.
+
+In most cases, "context_destroy" is not needed as the Hooks system
+automatically deletes context. An example where it could be required
+is where memory has been allocated by a callout during the processing
+of a request and a raw pointer to it stored in the context object. On
+destruction of the context, that memory will not be automatically
+released. Freeing in the memory in the "context_destroy" callout will solve
+that problem.
+
+Actually, when the context is destroyed, the destructor
+associated with any objects stored in it are run. Rather than point to
+allocated memory with a raw pointer, a better idea would be to point to
+it with a boost "smart" pointer and store that pointer in the context.
+When the context is destroyed, the smart pointer's destructor is run,
+which will automatically delete the pointed-to object.
+
+These approaches are illustrated in the following examples.
+Here it is assumed that the hooks library is performing some form of
+security checking on the packet and needs to maintain information in
+a user-specified "SecurityInformation" object. (The details of this
+fictitious object are of no concern here.) The object is created in
+the "context_create" callout and used in both the "pkt4_receive" and the
+"pkt4_send" callouts.
+
+@code
+// Storing information in a "raw" pointer. Assume that the
+
+#include <hooks/hooks.h>
+ :
+
+extern "C" {
+
+// context_create callout - called when the request is created.
+int context_create(CalloutHandle& handle) {
+ // Create the security information and store it in the context
+ // for this packet.
+ SecurityInformation* si = new SecurityInformation();
+ handle.setContext("security_information", si);
+}
+
+// Callouts that use the context
+int pkt4_receive(CalloutHandle& handle) {
+ // Retrieve the pointer to the SecurityInformation object
+ SecurityInformation* si;
+ handle.getContext("security_information", si);
+ :
+ :
+ // Set the security information
+ si->setSomething(...);
+
+ // The pointed-to information has been updated but the pointer has not been
+ // altered, so there is no need to call setContext() again.
+}
+
+int pkt4_send(CalloutHandle& handle) {
+ // Retrieve the pointer to the SecurityInformation object
+ SecurityInformation* si;
+ handle.getContext("security_information", si);
+ :
+ :
+ // Retrieve security information
+ bool active = si->getSomething(...);
+ :
+}
+
+// Context destruction. We need to delete the pointed-to SecurityInformation
+// object because we will lose the pointer to it when the @c CalloutHandle is
+// destroyed.
+int context_destroy(CalloutHandle& handle) {
+ // Retrieve the pointer to the SecurityInformation object
+ SecurityInformation* si;
+ handle.getContext("security_information", si);
+
+ // Delete the pointed-to memory.
+ delete si;
+}
+@endcode
+
+The requirement for the "context_destroy" callout can be eliminated if
+a Boost shared ptr is used to point to the allocated memory:
+
+@code
+// Storing information in a "raw" pointer. Assume that the
+
+#include <hooks/hooks.h>
+#include <boost/shared_ptr.hpp>
+ :
+
+extern "C" {
+
+// context_create callout - called when the request is created.
+
+int context_create(CalloutHandle& handle) {
+ // Create the security information and store it in the context for this
+ // packet.
+ boost::shared_ptr<SecurityInformation> si(new SecurityInformation());
+ handle.setContext("security_information", si);
+}
+
+// Other than the data type, a shared pointer has similar semantics to a "raw"
+// pointer. Only the code from "pkt4_receive" is shown here.
+
+int pkt4_receive(CalloutHandle& handle) {
+ // Retrieve the pointer to the SecurityInformation object
+ boost::shared_ptr<SecurityInformation> si;
+ handle.setContext("security_information", si);
+ :
+ :
+ // Modify the security information
+ si->setSomething(...);
+
+ // The pointed-to information has been updated but the pointer has not
+ // altered, so there is no need to reset the context.
+}
+
+// No context_destroy callout is needed to delete the allocated
+// SecurityInformation object. When the @c CalloutHandle is destroyed, the shared
+// pointer object will be destroyed. If that is the last shared pointer to the
+// allocated memory, then it too will be deleted.
+@endcode
+
+(Note that a Boost shared pointer - rather than any other Boost smart pointer -
+should be used, as the pointer objects are copied within the hooks framework and
+only shared pointers have the correct behavior for the copy operation.)
+
+
+@subsection hooksdgCalloutRegistration Registering Callouts
+
+As briefly mentioned in @ref hooksdgExampleCallouts, the standard is for
+callouts in the user library to have the same name as the name of the
+hook to which they are being attached. This convention was followed
+in the tutorial, e.g., the callout that needed to be attached to the
+"pkt4_receive" hook was named pkt4_receive.
+
+The reason for this convention is that when the library is loaded, the
+hook framework automatically searches the library for functions with
+the same names as the server hooks. When it finds one, it attaches it
+to the appropriate hook point. This simplifies the loading process and
+bookkeeping required to create a library of callouts.
+
+However, the hooks system is flexible in this area: callouts can have
+non-standard names, and multiple callouts can be registered on a hook.
+
+
+@subsubsection hooksdgLibraryHandle The LibraryHandle Object
+
+The way into the part of the hooks framework that allows callout
+registration is through the LibraryHandle object. This was briefly
+introduced in the discussion of the framework functions, in that
+an object of this type is pass to the "load" function. A LibraryHandle
+can also be obtained from within a callout by calling the CalloutHandle's
+@c getLibraryHandle() method.
+
+The LibraryHandle provides three methods to manipulate callouts:
+
+- @c registerCallout - register a callout on a hook.
+- @c deregisterCallout - deregister a callout from a hook.
+- @c deregisterAllCallouts - deregister all callouts on a hook.
+
+The following sections cover some of the ways in which these can be used.
+
+@subsubsection hooksdgNonstandardCalloutNames Non-Standard Callout Names
+
+The example in the tutorial used standard names for the callouts. As noted
+above, it is possible to use non-standard names. Suppose, instead of the
+callout names "pkt4_receive" and "pkt4_send", we had named our callouts
+"classify" and "write_data". The hooks framework would not have registered
+these callouts, so we would have needed to do it ourself. The place to
+do this is the "load" framework function, and its code would have had to
+been modified to:
+
+@code
+int load(LibraryHandle& libhandle) {
+ // Register the callouts on the hooks. We assume that a header file
+ // declares the "classify" and "write_data" functions.
+ libhandle.registerCallout("pkt4_receive", classify);
+ libhandle.registerCallout("pkt4_send", write_data);
+
+ // Open the log file
+ interesting.open("/data/clients/interesting.log",
+ std::fstream::out | std::fstream::app);
+ return (interesting ? 0 : 1);
+}
+@endcode
+
+It is possible for a library to contain callouts with both standard and
+non-standard names: ones with standard names will be registered automatically,
+ones with non-standard names need to be registered manually.
+
+@subsubsection hooksdgCommandHandlers Using Callouts as Command handlers
+
+Kea servers natively support a set of control commands to retrieve and update
+runtime information, e.g. server configuration, basic statistics etc. In
+many cases, however, DHCP deployments require support for additional commands
+or the natively supported commands don't exactly fulfill one's requirements.
+
+Taking advantage of Kea's modularity and hooks framework, it is now possible
+to easily extend the pool of supported commands by implementing additional
+(non-standard) commands within hook libraries.
+
+A hook library needs to register command handlers for control commands within
+its @c load function as follows:
+
+@code
+int load(LibraryHandle& handle) {
+ handle.registerCommandCallout("diagnostics-enable", diagnostics_enable);
+ handle.registerCommandCallout("diagnostics-dump", diagnostics_dump);
+ return (0);
+}
+@endcode
+
+Internally, the @c LibraryHandle associates command handlers @c diagnostics_enable
+and @c diagnostics_dump with dedicated hook points. These hook points are
+given names after the command names, i.e. "$diagnostics_enable" and
+"$diagnostics_dump". The dollar sign before the hook point name indicates
+that the hook point is dedicated for a command handler, i.e. is not one of
+the standard hook points used by the Kea servers. This is just a naming convention,
+usually invisible to the hook library implementation and is mainly aimed at
+minimizing a risk of collision between names of the hook points registered with
+command handlers and standard hook points.
+
+Once the hook library is loaded and the command handlers supported by the
+library are registered, the Kea servers will be able to recognize that those
+specific commands are supported and will dispatch commands with the corresponding
+names to the hook library (or multiple hook libraries) for processing. See the
+documentation of the @ref isc::config::HookedCommandMgr for more details how
+it uses @c HooksManager::commandHandlersPresent to determine if the received
+command should be dispatched to a hook library for processing.
+
+The @c diagnostics_enable and @c diagnostics_dump command
+handlers must be implemented within the hook library in analogous way to
+regular callouts:
+
+@code
+int diagnostics_enable(CalloutHandle& handle) {
+ ConstElementPtr response;
+
+ try {
+ ConstElementPtr command;
+ handle.getArgument("command", command);
+ ConstElementPtr args;
+ static_cast<void>(isc::config::parseCommand(args, command));
+
+ // ...
+ // handle command here.
+ // ...
+
+ response = createAnswer(CONTROL_RESULT_SUCCESS, "successful");
+
+ } catch (const std::exception& ex) {
+ response = createAnswer(CONTROL_RESULT_ERROR, ex.what());
+ }
+
+ handle.setArgument("response", response);
+
+ return (0);
+}
+@endcode
+
+The sample code above retrieves the "command" argument which is always provided.
+It represents the control command as sent by the controlling client. It includes
+command name and command specific arguments. The generic @ref isc::config::parseCommand
+can be used to retrieve arguments included in the command. The callout then interprets
+these arguments, takes appropriate action and creates a response to the client.
+Care should be taken to catch any non-fatal exceptions that may arise during the callout
+that should be reported as a failure to the controlling client. In such case, the response
+with @c CONTROL_RESULT_ERROR is returned and the callout should return the value of 0.
+The non-zero result should only be returned by the callout in case of fatal errors, i.e.
+errors which result in inability to generate a response to the client. If the response
+is generated, the command handler must set it as "response" argument prior to return.
+
+It is uncommon but valid scenario to have multiple hook libraries providing command
+handlers for the same command. They are invoked sequentially and each of them
+can freely modify a response set by a previous callout. This includes entirely
+replacing the response provided by previous callouts, if necessary.
+
+@subsubsection hooksdgMultipleCallouts Multiple Callouts on a Hook
+
+The Kea hooks framework allows multiple callouts to be attached to
+a hook point. Although it is likely to be rare for user code to need to
+do this, there may be instances where it make sense.
+
+To register multiple callouts on a hook, just call
+@c LibraryHandle::registerCallout multiple times on the same hook, e.g.,
+
+@code
+ libhandle.registerCallout("pkt4_receive", classify);
+ libhandle.registerCallout("pkt4_receive", write_data);
+@endcode
+
+The hooks framework will call the callouts in the order they are
+registered. The same @c CalloutHandle is passed between them, so any
+change made to the CalloutHandle's arguments, "skip" flag, or per-request
+context by the first is visible to the second.
+
+
+@subsection hooksdgMultipleLibraries Multiple User Libraries
+
+As alluded to in the section @ref hooksdgConfiguration, Kea can load
+multiple libraries. The libraries are loaded in the order specified in
+the configuration, and the callouts attached to the hooks in the order
+presented by the libraries.
+
+The following picture illustrates this, and also illustrates the scope of
+data passed around the system.
+
+@image html DataScopeArgument.png "Scope of Arguments"
+
+In this illustration, a server has three hook points, alpha, beta
+and gamma. Two libraries are configured, library 1 and library 2.
+Library 1 registers the callout "authorize" for hook alpha, "check" for
+hook beta and "add_option" for hook gamma. Library 2 registers "logpkt",
+"validate" and "putopt"
+
+The horizontal red lines represent arguments to callouts. When the server
+calls hook alpha, it creates an argument list and calls the
+first callout for the hook, "authorize". When that callout returns, the
+same (but possibly modified) argument list is passed to the next callout
+in the chain, "logpkt". Another, separate argument list is created for
+hook beta and passed to the callouts "check" and "validate" in
+that order. A similar sequence occurs for hook gamma.
+
+The next picture shows the scope of the context associated with a
+request.
+
+@image html DataScopeContext.png "Illustration of per-library context"
+
+The vertical blue lines represent callout context. Context is
+per-packet but also per-library. When the server calls "authorize",
+the CalloutHandle's @c getContext and @c setContext methods access a context
+created purely for library 1. The next callout on the hook will access
+context created for library 2. These contexts are passed to the callouts
+associated with the next hook. So when "check" is called, it gets the
+context data that was set by "authorize", when "validate" is called,
+it gets the context data set by "logpkt".
+
+It is stressed that the context for callouts associated with different
+libraries is entirely separate. For example, suppose "authorize" sets
+the CalloutHandle's context item "foo" to 2 and "logpkt" sets an item of
+the same name to the string "bar". When "check" accesses the context
+item "foo", it gets a value of 2; when "validate" accesses an item of
+the same name, it gets the value "bar".
+
+It is also stressed that all this context exists only for the life of the
+request being processed. When that request is complete, all the
+context associated with that request - for all libraries - is destroyed,
+and new context created for the next request.
+
+This structure means that library authors can use per-request context
+without worrying about the presence of other libraries. Other libraries
+may be present, but will not affect the context values set by a library's
+callouts.
+
+Configuring multiple libraries just requires listing the libraries
+as separate elements of the hooks-libraries configuration element, e.g.,
+
+@code
+"Dhcp4": {
+ :
+ "hooks-libraries": [
+ {
+ "library": "/usr/lib/library1.so"
+ },
+ {
+ "library": "/opt/library2.so"
+ }
+ :
+ ]
+}
+@endcode
+
+
+@subsection hooksdgInterLibraryData Passing Data Between Libraries
+
+In rare cases, it is possible that one library may want to pass
+data to another. This can be done in a limited way by means of the
+CalloutHandle's @c setArgument and @c getArgument calls. For example, in the
+above diagram, the callout "add_option" can pass a value to "putopt"
+by setting a name.value pair in the hook's argument list. "putopt"
+would be able to read this, but would not be able to return information
+back to "add_option".
+
+All argument names used by Kea will be a combination of letters
+(both upper- and lower-case), digits, hyphens and underscores: no
+other characters will be used. As argument names are simple strings,
+it is suggested that if such a mechanism be used, the names of the data
+values passed between the libraries include a special character such as
+the dollar symbol or percent sign. In this way there is no danger that
+a name will conflict with any existing or future Kea argument names.
+
+
+@subsection hooksdgStaticallyLinkedKea Running Against a Statically-Linked Kea
+
+If Kea is built with the --enable-static-link switch (set when
+running the "configure" script), no shared Kea libraries are built;
+instead, archive libraries are created and Kea is linked to them.
+If you create a hooks library also linked against these archive libraries,
+when the library is loaded you end up with two copies of the library code,
+one in Kea and one in your library.
+
+To run successfully, your library needs to perform run-time initialization
+of the Kea code in your library (something performed by Kea
+in the case of shared libraries). To do this, call the function
+isc::hooks::hooksStaticLinkInit() as the first statement of the load()
+function. (If your library does not include a load() function, you need
+to add one.) For example:
+
+@code
+#include <hooks/hooks.h>
+
+extern "C" {
+
+int version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+int load() {
+ isc::hooks::hooksStaticLinkInit();
+ :
+}
+
+// Other callout functions
+ :
+
+}
+@endcode
+
+
+@subsection hooksdgHooksConfig Configuring Hooks Libraries
+
+Sometimes it is useful for the hook library to have some configuration parameters.
+This capability was introduced in Kea 1.1. This is often convenient to follow
+generic Kea configuration approach rather than invent your own configuration
+logic. Consider the following example:
+
+@code
+"hooks-libraries": [
+ {
+ "library": "/opt/first.so"
+ },
+ {
+ "library": "/opt/second.so",
+ "parameters": {
+ }
+ },
+ {
+ "library": "/opt/third.so",
+ "parameters": {
+ "mail": "spam@example.com",
+ "floor": 13,
+ "debug": false,
+ "users": [ "alice", "bob", "charlie" ],
+ "languages": {
+ "french": "bonjour",
+ "klingon": "yl'el"
+ }
+ }
+ }
+]
+@endcode
+
+This example has three hook libraries configured. The first and second have
+no parameters. Note that parameters map is optional, but it's perfectly okay to
+specify it as an empty map. The third library is more interesting. It has five
+parameters specified. The first one called 'mail' is a string. The second one
+is an integer and the third one is boolean. Fourth and fifth parameters are
+slightly more complicated as they are a list and a map respectively. JSON
+structures can be nested if necessary, e.g., you can have a list, which contains
+maps, maps that contain maps that contain other maps etc. Any valid JSON
+structure can be represented. One important limitation here is that the top
+level "parameters" structure must either be a map or not present at all.
+
+Those parameters can be accessed in load() method. Passed isc::hooks::LibraryHandle
+object has a method called getParameter that returns an instance of
+isc::data::ConstElementPtr or null pointer if there was no parameter specified. This pointer
+will point to an object derived from isc::data::Element class. For detailed
+explanation how to use those objects, see isc::data::Element class.
+
+Here's a brief overview of how to use those elements:
+
+ - x = getParameter("mail") will return instance of isc::data::StringElement. The content
+ can be accessed with x->stringValue() and will return std::string.
+ - x = getParameter("floor") will return an instance of isc::data::IntElement.
+ The content can be accessed with x->intValue() and will return int.
+ - x = getParameter("debug") will return an instance of isc::data::BoolElement.
+ Its value can be accessed with x->boolValue() and will return bool.
+ - x = getParameter("users") will return an instance of isc::data::ListElement.
+ Its content can be accessed with the following methods:
+ x->size(), x->get(index)
+ - x = getParameter("watch-list") will return an instance of isc::data::MapElement.
+ Its content can be accessed with the following methods:
+ x->find("klingon"), x->contains("french"), x->size()
+
+Keep in mind that the user can structure his config file incorrectly.
+Remember to check if the structure has the expected type before using type specific
+method. For example calling stringValue on IntElement will throw an exception.
+You can do this by calling isc::data::Element::getType.
+
+Here's an example that obtains all of the parameters specified above.
+If you want to get nested elements, Element::get(index) and Element::find(name)
+will return ElementPtr, which can be iterated in similar manner.
+
+@code
+int load(LibraryHandle& handle) {
+ ConstElementPtr mail = handle.getParameter("mail");
+ ConstElementPtr floor = handle.getParameter("floor");
+ ConstElementPtr debug = handle.getParameter("debug");
+ ConstElementPtr users = handle.getParameter("users");
+ ConstElementPtr lang = handle.getParameter("languages");
+
+ // String handling example
+ if (!mail) {
+ // Handle missing 'mail' parameter here.
+ return (1);
+ }
+ if (mail->getType() != Element::string) {
+ // Handle incorrect 'mail' parameter here.
+ return (1);
+ }
+ std::string mail_str = mail->stringValue();
+
+ // In the following examples safety checks are omitted for clarity.
+ // Make sure you do it properly similar to mail example above
+ // or you risk dereferencing null pointer or at least throwing
+ // an exception!
+
+ // Integer handling example
+ int floor_num = floor->intValue();
+
+ // Boolean handling example
+ bool debug_flag = debug->boolValue();
+
+ // List handling example
+ std::cout << "There are " << users->size() << " users defined." << std::endl;
+ for (int i = 0; i < users->size(); i++) {
+ ConstElementPtr user = users->get(i);
+ std::cout << "User " << user->stringValue() << std::endl;
+ }
+
+ // Map handling example
+ std::cout << "There are " << lang->size() << " languages defined." << std::endl;
+ if (lang->contains("french")) {
+ std::cout << "One of them is French!" << std::endl;
+ }
+ ConstElementPtr greeting = lang->find("klingon");
+ if (greeting) {
+ std::cout << "Lt. Worf says " << greeting->stringValue() << std::endl;
+ }
+
+ // All validation steps were successful. The library has all the parameters
+ // it needs, so we should report a success.
+ return (0);
+}
+@endcode
+
+A good sources of examples could be unit-tests in file src/lib/cc/tests/data_unittests.cc
+which are dedicated to isc::data::Element testing and src/lib/hooks/tests/callout_params_library.cc,
+which is an example library used in testing. This library expects exactly 3 parameters:
+svalue (which is a string), ivalue (which is an integer) and bvalue (which is a boolean).
+
+@subsection hooksMemoryManagement Memory Management Considerations for Hooks Writer
+
+Both Kea server memory space and hook library memory space share a common
+address space between the opening of the hook (call to dlopen() as the first
+phase of the hook library loading) and the closing of the hook (call to
+dlclose() as the last phase of the hook library unloading). There are
+pointers between the two memory spaces with at least two bad consequences
+when they are not correctly managed:
+
+- Kea uses shared pointers for its objects. If the hook ownership keeps
+ownership of an object, this object will never be destroyed, leading to
+a trivial memory leak. Some care is recommended when the hook library
+uses a garbage collector to not postpone releases of no longer used
+objects. Cycles should be avoided too, for instance using weak pointers.
+Of course at the opposite, if a Kea object is needed ownership on, it must
+be kept in order to not get a dangling pointer when it will be destroyed
+at the end of its last reference lifetime.
+
+- Kea can take some pointers to the hook library memory space, for instance
+when a hook object is registered. If these pointers are not destroyed
+before the hook library memory space is unmapped by dlclose() this likely
+leads to a crash.
+
+Communication between Kea code and hook library code is provided by
+callout handles. For callout points related to a packet, the callout
+handle is associated with the packet allowing to get the same callout handle
+for all callout points called during processing of a query.
+
+Hook libraries are closed i.e. hook library memory spaces are unmapped
+only when there is no active callout handles. This enforces a correct
+behavior at two conditions:
+
+- there is no "wild" dangling pointers, for instance no registered
+objects.
+
+- this can happen i.e. the hook library does not keep a shared pointer
+to a query packet.
+
+To allow hook writers to fulfill these two conditions the unload() entry
+point is called in the first phase of the unloading process since Kea
+version 1.7.10. For instance if the hook library uses the PIMPL code
+pattern the unload() entry point must reset the pointer to the
+hook library implementation.
+
+@subsection hooksMultiThreading Multi-Threading Considerations for Hooks Writers
+
+Multi-threading programming in C++ is not easy. For instance STL containers
+do not support simultaneous read and write accesses. Kea is written in C++
+so a priori for all Kea APIs one should never assume thread safety.
+
+When a hook library is internally multi-threaded, its code and any Kea API
+used simultaneously by different threads must be thread safe. To mark
+the difference between this and the other thread safety requirement this
+is called "generic thread safe".
+
+When multi-threaded packet processing is enabled, Kea servers perform
+some actions by the main thread and packet processing by members of
+a thread pool. The multi-threading mode is returned by:
+@code
+isc::util::MultiThreadingMgr::instance().getMode()
+@endcode
+When it is false, Kea is single threaded and there is no thread safety
+requirement, when it is true, the requirement is named Kea packet processing
+thread safe shorten into "Kea thread safe".
+
+A typical Kea thread safe looks like:
+@code
+int Foo() {
+ if (MultiThreadingMgr::instance().getMode()) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ return (FooInternal());
+ } else {
+ return (FooInternal());
+ }
+}
+@endcode
+
+The overhead of mutexes and other synchronization tools is far greater
+than a test and branch so it is the recommended way to implement Kea
+thread safety.
+
+When a hook library entry point can be called from a packet processing
+thread, typically from a packet processing callout but also when
+implementing a lease or host backend API, the entry point code must
+be Kea thread safe. If it is not possible the hook library must
+be marked as not multi-threading compatible (i.e. return 0 from
+multi_threading_compatible).
+
+At the opposite during (re)configuration including libload command
+and config backend, only the main thread runs, so version, load, unload,
+multi_threading_compatible, dhcp4_srv_configured, dhcp6_srv_configured,
+cb4_updated and cb6_updated have no thread safety requirements.
+
+Other hook library entry points are called by the main thread:
+ - io service (io context is recent boost versions) is polled by the main
+ thread
+ - external socket callbacks are executed by the main thread
+ - commands including command_process
+
+The packet processing threads are not stopped so either the entry
+point code is Kea thread safe or it uses a critical section
+(@c isc::util::MultiThreadingCriticalSection) to stop the packet
+processing threads during the execution of the not Kea thread safe code.
+Of course critical sections have an impact on performance so they should
+be used only for particular cases where no better choice is available.
+
+Some Kea APIs were made thread safe mainly because they are used by the
+packet processing:
+ - logging is generic thread safe and even multi process safe i.e.
+ messages logged into a file or by syslog from multiple processes
+ do not mix.
+ - statistics update is Kea thread safe.
+ - lease and host database backends are Kea thread safe. Note if you need to
+ perform a direct MySQL or PostgreSQL query you must use the connection pool.
+ - state model and watched thread are generic thread safe (libkea-util)
+ - interval timer setup and cancel are generic thread safe (libkea-asiolink)
+ - parking lots are generic thread safe (libkea-hooks)
+ - external sockets are generic thread safe (libkea-dhcp++)
+ - http client is Kea thread safe (libkea-http)
+
+Some other Kea APIs are intrinsically thread safe because they do not
+involve a shared structure so for instance despite of its name the
+interface manager send methods are generic thread safe.
+
+Per library documentation details thread safety to help hooks writers
+and to provide an exhaustive list of Kea thread safe APIs:
+ - @ref utilMTConsiderations
+ - @ref logMTConsiderations
+ - @ref asiolinkMTConsiderations
+ - @ref ccMTConsiderations
+ - @ref databaseMTConsiderations
+ - @ref ctrlSocketMTConsiderations
+ - @ref libdhcpMTConsiderations
+ - @ref statsMTConsiderations
+ - @ref yangMTConsiderations
+ - @ref libdhcp_ddnsMTConsiderations
+ - @ref dhcpEvalMTConsiderations
+ - @ref cplMTConsiderations
+ - @ref dhcpDatabaseBackendsMTConsiderations
+ - @ref libdhcpsrvMTConsiderations
+ - @ref httpMTConsiderations
+
+*/
diff --git a/src/lib/hooks/images/DataScopeArgument.dia b/src/lib/hooks/images/DataScopeArgument.dia
new file mode 100644
index 0000000..02a4f17
--- /dev/null
+++ b/src/lib/hooks/images/DataScopeArgument.dia
Binary files differ
diff --git a/src/lib/hooks/images/DataScopeArgument.png b/src/lib/hooks/images/DataScopeArgument.png
new file mode 100644
index 0000000..34a5bd1
--- /dev/null
+++ b/src/lib/hooks/images/DataScopeArgument.png
Binary files differ
diff --git a/src/lib/hooks/images/DataScopeContext.dia b/src/lib/hooks/images/DataScopeContext.dia
new file mode 100644
index 0000000..1e39f5b
--- /dev/null
+++ b/src/lib/hooks/images/DataScopeContext.dia
Binary files differ
diff --git a/src/lib/hooks/images/DataScopeContext.png b/src/lib/hooks/images/DataScopeContext.png
new file mode 100644
index 0000000..ba18875
--- /dev/null
+++ b/src/lib/hooks/images/DataScopeContext.png
Binary files differ
diff --git a/src/lib/hooks/images/HooksUml.dia b/src/lib/hooks/images/HooksUml.dia
new file mode 100644
index 0000000..0972fca
--- /dev/null
+++ b/src/lib/hooks/images/HooksUml.dia
Binary files differ
diff --git a/src/lib/hooks/images/HooksUml.png b/src/lib/hooks/images/HooksUml.png
new file mode 100644
index 0000000..3859e6a
--- /dev/null
+++ b/src/lib/hooks/images/HooksUml.png
Binary files differ
diff --git a/src/lib/hooks/libinfo.cc b/src/lib/hooks/libinfo.cc
new file mode 100644
index 0000000..2abf3a1
--- /dev/null
+++ b/src/lib/hooks/libinfo.cc
@@ -0,0 +1,30 @@
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/libinfo.h>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Extracts names from HookLibsCollection
+///
+/// @param libraries Hook libraries collection
+/// @return vector of strings with library names
+std::vector<std::string>
+extractNames(const isc::hooks::HookLibsCollection& libraries) {
+ std::vector<std::string> names;
+
+ for (isc::hooks::HookLibsCollection::const_iterator it = libraries.begin();
+ it != libraries.end(); ++it) {
+ names.push_back(it->first);
+ }
+ return (names);
+}
+
+};
+};
diff --git a/src/lib/hooks/libinfo.h b/src/lib/hooks/libinfo.h
new file mode 100644
index 0000000..42b0441
--- /dev/null
+++ b/src/lib/hooks/libinfo.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HOOKS_LIBINFO_H
+#define HOOKS_LIBINFO_H
+
+#include <cc/data.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+#include <vector>
+#include <utility>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Entity that holds information about hook libraries and their
+/// parameters.
+///
+/// The first parameter is a full filename with path to the library.
+/// The second parameter is a map of parameters that configure the
+/// library. There's always at least one parameter: "library", which
+/// contains the library name.
+typedef std::pair<std::string, data::ConstElementPtr> HookLibInfo;
+
+/// @brief A storage for information about hook libraries.
+typedef std::vector<HookLibInfo> HookLibsCollection;
+
+/// @brief Shared pointer to collection of hooks libraries.
+typedef boost::shared_ptr<HookLibsCollection> HookLibsCollectionPtr;
+
+/// @brief Extracts library names from full library information structure
+std::vector<std::string> extractNames(const HookLibsCollection& libinfo);
+
+};
+};
+
+#endif
diff --git a/src/lib/hooks/library_handle.cc b/src/lib/hooks/library_handle.cc
new file mode 100644
index 0000000..b070448
--- /dev/null
+++ b/src/lib/hooks/library_handle.cc
@@ -0,0 +1,131 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/callout_manager.h>
+#include <hooks/library_handle.h>
+#include <hooks/hooks_manager.h>
+
+#include <iostream>
+
+namespace isc {
+namespace hooks {
+
+// Callout manipulation - all deferred to the CalloutManager.
+
+void
+LibraryHandle::registerCallout(const std::string& name, CalloutPtr callout) {
+ int index = index_;
+
+ if (index_ == -1) {
+ // -1 means that current index is stored in CalloutManager.
+ // So let's get the index from there. See comment for
+ // LibraryHandle::index_.
+ index = callout_manager_.getLibraryIndex();
+ }
+
+ // Register the callout.
+ callout_manager_.registerCallout(name, callout, index);
+}
+
+void
+LibraryHandle::registerCommandCallout(const std::string& command_name,
+ CalloutPtr callout) {
+ // Register hook point for this command, if one doesn't exist.
+ callout_manager_.registerCommandHook(command_name);
+ // Register the command handler as a callout.
+ registerCallout(ServerHooks::commandToHookName(command_name), callout);
+}
+
+
+bool
+LibraryHandle::deregisterCallout(const std::string& name, CalloutPtr callout) {
+ int index = index_;
+
+ if (index_ == -1) {
+ // -1 means that current index is stored in CalloutManager.
+ // So let's get the index from there. See comment for
+ // LibraryHandle::index_.
+ index = callout_manager_.getLibraryIndex();
+ }
+
+ return (callout_manager_.deregisterCallout(name, callout, index));
+}
+
+bool
+LibraryHandle::deregisterAllCallouts(const std::string& name) {
+ int index = index_;
+
+ if (index_ == -1) {
+ // -1 means that current index is stored in CalloutManager.
+ // So let's get the index from there. See comment for
+ // LibraryHandle::index_.
+ index = callout_manager_.getLibraryIndex();
+ }
+
+ return (callout_manager_.deregisterAllCallouts(name, index));
+}
+
+isc::data::ConstElementPtr
+LibraryHandle::getParameters() {
+ HookLibsCollection libinfo = HooksManager::getLibraryInfo();
+
+ int index = index_;
+
+ if (index == -1) {
+ // -1 means that current index is stored in CalloutManager.
+ // So let's get the index from there. See comment for
+ // LibraryHandle::index_.
+ index = callout_manager_.getLibraryIndex();
+ }
+
+ if ((index > libinfo.size()) || (index <= 0)) {
+ // Something is very wrong here. The library index is out of bounds.
+ // However, this is user facing interface, so we should not throw here.
+ return (isc::data::ConstElementPtr());
+ }
+
+ // Some indexes have special meaning:
+ // * 0 - pre-user library callout
+ // * 1 -> numlib - indexes for actual libraries
+ // * INT_MAX - post-user library callout
+
+ return (libinfo[index - 1].second);
+}
+
+isc::data::ConstElementPtr
+LibraryHandle::getParameter(const std::string& name) {
+ // Try to find appropriate parameter. May return null pointer
+ isc::data::ConstElementPtr params = getParameters();
+ if (!params || (params->getType() != isc::data::Element::map)) {
+ return (isc::data::ConstElementPtr());
+ }
+
+ // May return null pointer if there's no parameter.
+ return (params->get(name));
+}
+
+std::vector<std::string>
+LibraryHandle::getParameterNames() {
+ std::vector<std::string> names;
+ // Find all parameter names.
+ isc::data::ConstElementPtr params = getParameters();
+ if (!params ||
+ (params->getType() != isc::data::Element::map) ||
+ (params->size() == 0)) {
+ return (names);
+ }
+ auto map = params->mapValue();
+ for (auto elem = map.begin(); elem != map.end(); ++elem) {
+ names.push_back(elem->first);
+ }
+ return (names);
+}
+
+
+} // 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..14af40c
--- /dev/null
+++ b/src/lib/hooks/library_handle.h
@@ -0,0 +1,242 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef LIBRARY_HANDLE_H
+#define LIBRARY_HANDLE_H
+
+#include <string>
+#include <cc/data.h>
+
+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".)
+///
+/// As of Kea 1.3.0 release, the @ref LibraryHandle can be used by the hook
+/// libraries to install control command handlers and dynamically register
+/// hook points with which the handlers are associated. For example, if the
+/// hook library supports control-command 'foo-bar' it should register its
+/// handler similarly to this:
+/// @code
+/// int load(LibraryHandle& libhandle) {
+/// libhandle.registerCommandCallout("foo-bar", foo_bar_handler);
+/// return (0);
+/// }
+/// @endcode
+///
+/// which will result in automatic creation of the hook point for the command
+/// (if one doesn't exist) and associating the callout 'foo_bar_handler' with
+/// this hook point as a handler for the command.
+
+class LibraryHandle {
+public:
+
+ /// @brief Constructor
+ ///
+ /// @param manager Back reference to the containing CalloutManager.
+ /// This reference is used to access appropriate methods in that
+ /// object. Note that the reference 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. Note: although -1 is a valid argument value for
+ /// @ref isc::hooks::CalloutManager::setLibraryIndex(), in this class
+ /// it is used as a sentinel to indicate that the library index in
+ /// @ref isc::hooks::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 unrecognized.
+ /// @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 Register control command handler
+ ///
+ /// Registers control command handler by creating a hook point for this
+ /// command (if it doesn't exist) and associating the callout as a command
+ /// handler. It is possible to register multiple command handlers for the
+ /// same control command because command handlers are implemented as callouts.
+ ///
+ /// @param command_name Command name for which handler should be installed.
+ /// @param callout Pointer to the command handler implemented as a callout.
+ void registerCommandCallout(const std::string& command_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 unrecognized.
+ /// @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 unrecognized.
+ bool deregisterAllCallouts(const std::string& name);
+
+
+ /// @brief Returns configuration parameter for the library.
+ ///
+ /// This method returns configuration parameters specified in the
+ /// configuration file. Here's the example. Let's assume that there
+ /// are two hook libraries configured:
+ ///
+ /// "hooks-libraries": [
+ /// {
+ /// "library": "/opt/charging.so",
+ /// "parameters": {}
+ /// },
+ /// {
+ /// "library": "/opt/local/notification.so",
+ /// "parameters": {
+ /// "mail": "alarm@example.com",
+ /// "floor": 42,
+ /// "debug": false,
+ /// "users": [ "alice", "bob", "charlie" ],
+ /// "header": {
+ /// "french": "bonjour",
+ /// "klingon": "yl'el"
+ /// }
+ /// }
+ /// }
+ ///]
+ ///
+ /// The first library has no parameters, so regardless of the name
+ /// specified, for that library getParameter will always return NULL.
+ ///
+ /// For the second parameter, depending the following calls will return:
+ /// - x = getParameter("mail") will return instance of
+ /// isc::data::StringElement. The content can be accessed with
+ /// x->stringValue() and will return std::string.
+ /// - x = getParameter("floor") will return an instance of isc::data::IntElement.
+ /// The content can be accessed with x->intValue() and will return int.
+ /// - x = getParameter("debug") will return an instance of isc::data::BoolElement.
+ /// Its value can be accessed with x->boolValue() and will return bool.
+ /// - x = getParameter("users") will return an instance of ListElement.
+ /// Its content can be accessed with the following methods:
+ /// x->size(), x->get(index)
+ /// - x = getParameter("header") will return an instance of isc::data::MapElement.
+ /// Its content can be accessed with the following methods:
+ /// x->find("klingon"), x->contains("french"), x->size()
+ ///
+ /// For more examples and complete API, see documentation for
+ /// @ref isc::data::Element class and its derivatives:
+ /// - @ref isc::data::IntElement
+ /// - @ref isc::data::DoubleElement
+ /// - @ref isc::data::BoolElement
+ /// - @ref isc::data::StringElement
+ /// - @ref isc::data::ListElement
+ /// - @ref isc::data::MapElement
+ ///
+ /// Another good way to learn how to use Element interface is to look at the
+ /// unittests in data_unittests.cc.
+ ///
+ /// @param name text name of the parameter.
+ /// @return ElementPtr representing requested parameter (may be null, if
+ /// there is no such parameter.)
+ isc::data::ConstElementPtr
+ getParameter(const std::string& name);
+
+ /// @brief Get configuration parameter common code.
+ ///
+ /// @return configuration parameters.
+ isc::data::ConstElementPtr getParameters();
+
+ /// @brief Returns names of configuration parameters for the library.
+ ///
+ /// This method returns a vector of strings reflecting names of
+ /// configuration parameters specified in the configuration file.
+ ///
+ /// @note: kept for backward compatibility.
+ /// @return a vector with parameter entry names.
+ std::vector<std::string> getParameterNames();
+
+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..447c9eb
--- /dev/null
+++ b/src/lib/hooks/library_manager.cc
@@ -0,0 +1,435 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#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 <log/logger_manager.h>
+#include <log/logger_support.h>
+#include <log/message_initializer.h>
+#include <util/multi_threading_mgr.h>
+
+#include <string>
+#include <vector>
+
+#include <dlfcn.h>
+
+using namespace std;
+
+namespace isc {
+namespace hooks {
+
+// Constructor (used by external agency)
+LibraryManager::LibraryManager(const std::string& name, int index,
+ const boost::shared_ptr<CalloutManager>& manager)
+ : dl_handle_(NULL), index_(index), manager_(manager),
+ library_name_(name),
+ server_hooks_(ServerHooks::getServerHooksPtr())
+{
+ if (!manager) {
+ isc_throw(NoCalloutManager, "must specify a CalloutManager when "
+ "instantiating a LibraryManager object");
+ }
+}
+
+// Constructor (used by "validate" for library validation). Note that this
+// sets "manager_" to not point to anything, which means that methods such as
+// registerStandardCallout() will fail, probably with a segmentation fault.
+// There are no checks for this condition in those methods: this constructor
+// is declared "private", so can only be executed by a method in this class.
+// The only method to do so is "validateLibrary", which takes care not to call
+// methods requiring a non-NULL manager.
+LibraryManager::LibraryManager(const std::string& name)
+ : dl_handle_(NULL), index_(-1), manager_(), library_name_(name)
+{}
+
+// Destructor.
+LibraryManager::~LibraryManager() {
+ if (index_ >= 0) {
+ // LibraryManager instantiated to load a library, so ensure that
+ // it is unloaded before exiting.
+ static_cast<void>(prepareUnloadLibrary());
+ }
+
+ // LibraryManager instantiated to validate a library, so just ensure
+ // that it is closed before exiting.
+ static_cast<void>(closeLibrary());
+}
+
+// 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());
+ } else {
+ LOG_INFO(hooks_logger, HOOKS_LIBRARY_CLOSED).arg(library_name_);
+ }
+ }
+
+ 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 = KEA_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 == KEA_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(KEA_HOOKS_VERSION);
+ }
+ } else {
+ LOG_ERROR(hooks_logger, HOOKS_NO_VERSION).arg(library_name_);
+ }
+
+ return (false);
+}
+
+// Check the multi-threading compatibility of the library
+
+bool
+LibraryManager::checkMultiThreadingCompatible() const {
+
+ // Compatible with single-threaded.
+ if (!util::MultiThreadingMgr::instance().getMode()) {
+ return (true);
+ }
+
+ // Get the pointer to the "multi_threading_compatible" function.
+ PointerConverter pc(dlsym(dl_handle_, MULTI_THREADING_COMPATIBLE_FUNCTION_NAME));
+ int compatible = 0;
+ if (pc.multiThreadingCompatiblePtr()) {
+ try {
+ compatible = (*pc.multiThreadingCompatiblePtr())();
+ } catch (...) {
+ LOG_ERROR(hooks_logger, HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION)
+ .arg(library_name_);
+ return (false);
+ }
+
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS,
+ HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE)
+ .arg(library_name_)
+ .arg(compatible);
+ }
+ if (compatible == 0) {
+ LOG_ERROR(hooks_logger, HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE)
+ .arg(library_name_);
+ }
+ return (compatible != 0);
+}
+
+// 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 (size_t 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 (const isc::Exception& ex) {
+ LOG_ERROR(hooks_logger, HOOKS_LOAD_FRAMEWORK_EXCEPTION)
+ .arg(library_name_).arg(ex.what());
+ return (false);
+ } 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::prepareUnloadLibrary() {
+
+ // Nothing to do.
+ if (dl_handle_ == NULL) {
+ return (true);
+ }
+
+ // Call once.
+ if (index_ < 0) {
+ return (true);
+ }
+
+ // Get the pointer to the "load" function.
+ bool result = false;
+ 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())();
+ result = true;
+ } catch (const isc::Exception& ex) {
+ LOG_ERROR(hooks_logger, HOOKS_UNLOAD_FRAMEWORK_EXCEPTION)
+ .arg(library_name_).arg(ex.what());
+ } catch (...) {
+ // Exception generated. Note a warning as the unload will occur
+ // anyway.
+ LOG_WARN(hooks_logger, HOOKS_UNLOAD_EXCEPTION).arg(library_name_);
+ }
+
+ if (result) {
+ if (status != 0) {
+ LOG_ERROR(hooks_logger, HOOKS_UNLOAD_ERROR).arg(library_name_)
+ .arg(status);
+ result = 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_);
+ result = true;
+ }
+
+ // Regardless of status, remove all callouts associated with this
+ // library on all hooks.
+ vector<string> hooks = ServerHooks::getServerHooks().getHookNames();
+ manager_->setLibraryIndex(index_);
+ for (size_t i = 0; i < hooks.size(); ++i) {
+ bool removed = manager_->deregisterAllCallouts(hooks[i], index_);
+ if (removed) {
+ LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUTS_REMOVED)
+ .arg(hooks[i]).arg(library_name_);
+ }
+ }
+
+ // Mark as unload() ran.
+ index_ = -1;
+
+ return (result);
+}
+
+// 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()) {
+
+ // The hook libraries provide their own log messages and logger
+ // instances. This step is required to register log messages for
+ // the library being loaded in the global dictionary. Ideally, this
+ // should be called after all libraries have been loaded but we're
+ // going to call the version() and load() functions here and these
+ // functions may already contain logging statements.
+ isc::log::MessageInitializer::loadDictionary();
+
+ // The log messages registered by the new hook library may duplicate
+ // some of the existing messages. Log warning for each duplicated
+ // message now.
+ isc::log::LoggerManager::logDuplicatedMessages();
+
+ // Library opened OK, see if a version function is present and if so,
+ // check what value it returns. Check multi-threading compatibility.
+ if (checkVersion() && checkMultiThreadingCompatible()) {
+ // 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>(prepareUnloadLibrary());
+ }
+ }
+
+ // 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. This is
+// only run if the library is still loaded and is a no-op if the library is
+// not open.
+
+bool
+LibraryManager::unloadLibrary() {
+ bool result = true;
+ if (dl_handle_ != NULL) {
+ 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.
+ if (index_ >= 0) {
+ result = prepareUnloadLibrary();
+ }
+
+ // ... 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);
+}
+
+// Validate the library. We must be able to open it, and the version function
+// must both exist and return the right number. Note that this is a static
+// method.
+
+bool
+LibraryManager::validateLibrary(const std::string& name) {
+ // Instantiate a library manager for the validation. We use the private
+ // constructor as we don't supply a CalloutManager.
+ LibraryManager manager(name);
+
+ // Try to open it and, if we succeed, check the version.
+ bool validated = manager.openLibrary() && manager.checkVersion() &&
+ manager.checkMultiThreadingCompatible();
+
+ // Regardless of whether the version checked out, close the library. (This
+ // is a no-op if the library failed to open.)
+ static_cast<void>(manager.closeLibrary());
+
+ return (validated);
+}
+
+// @note Moved from its own hooks.cc file to avoid undefined reference
+// with static link.
+void hooksStaticLinkInit() {
+ if (!isc::log::isLoggingInitialized()) {
+ isc::log::initLogger(std::string("userlib"));
+ }
+}
+
+} // 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..f3a376f
--- /dev/null
+++ b/src/lib/hooks/library_manager.h
@@ -0,0 +1,244 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef LIBRARY_MANAGER_H
+#define LIBRARY_MANAGER_H
+
+#include <exceptions/exceptions.h>
+#include <hooks/server_hooks.h>
+#include <boost/shared_ptr.hpp>
+
+#include <string>
+
+namespace isc {
+namespace hooks {
+
+/// @brief No Callout Manager
+///
+/// Thrown if a library manager is instantiated by an external agency without
+/// specifying a CalloutManager object.
+class NoCalloutManager : public Exception {
+public:
+ NoCalloutManager(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+class CalloutManager;
+class LibraryHandle;
+class LibraryManager;
+
+/// @brief Library manager
+///
+/// This class handles the loading and unloading of a specific library. It also
+/// provides a static method for checking that a library is valid (this is used
+/// in configuration parsing).
+///
+/// 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 clears the callouts on all hooks, 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 close 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 closed, 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 "close" 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.
+///
+/// When validating a library, only the fact that the library can be opened and
+/// version() exists and returns the correct number is checked. The library
+/// is closed after the validation.
+
+class LibraryManager {
+public:
+ /// @brief Constructor
+ ///
+ /// This constructor is used by external agencies (i.e. the
+ /// LibraryManagerCollection) when instantiating a LibraryManager. It
+ /// stores the library name - the actual 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
+ ///
+ /// @throw NoCalloutManager Thrown if the manager argument is NULL.
+ LibraryManager(const std::string& name, int index,
+ const boost::shared_ptr<CalloutManager>& manager);
+
+ /// @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 close libraries.
+ ~LibraryManager();
+
+ /// @brief Validate library
+ ///
+ /// A static method that is used to validate a library. Validation checks
+ /// that the library can be opened, that "version" exists, and that it
+ /// returns the right number, and the multi-threading compatibility.
+ ///
+ /// @param name Name of the library to validate
+ ///
+ /// @return true if the library validated, false if not. If the library
+ /// fails to validate, the reason for the failure is logged.
+ static bool validateLibrary(const std::string& name);
+
+ /// @brief Loads a library
+ ///
+ /// Open the library, check the version and the multi-threading
+ /// compatibility. If all is OK, load all standard symbols then
+ /// call "load" if present.
+ ///
+ /// It also calls the @c isc::log::MessageInitializer::loadDictionary,
+ /// prior to invoking the @c version function of the library, to
+ /// update the global logging dictionary with the log messages
+ /// registered by the loaded library.
+ ///
+ /// @return true if the library loaded successfully, false otherwise.
+ /// In the latter case, the library will be unloaded if possible.
+ bool loadLibrary();
+
+ /// @brief Prepares library unloading
+ ///
+ /// Searches for the "unload" framework function and, if present, runs it.
+ /// Regardless of status, remove all callouts associated with this
+ /// library on all hooks.
+ ///
+ /// @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 prepareUnloadLibrary();
+
+ /// @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 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 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 Kea. 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 Check multi-threading compatibility
+ ///
+ /// If the multi-threading mode is false returns true, else with
+ /// the library open, accesses the "multi_threading_compatible()"
+ /// function and returns false if not exists or has value 0, returns
+ /// true otherwise.
+ ///
+ /// @return bool true if the check succeeded
+ bool checkMultiThreadingCompatible() 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();
+
+private:
+ /// @brief Validating constructor
+ ///
+ /// Constructor used when the LibraryManager is instantiated to validate
+ /// a library (i.e. by the "validateLibrary" static method).
+ ///
+ /// @param name Name of the library to load. This should be an absolute
+ /// path name.
+ LibraryManager(const std::string& name);
+
+ // Member variables
+
+ 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
+
+ ServerHooksPtr server_hooks_; ///< Stores a pointer to ServerHooks.
+
+};
+
+} // 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..6c32b76
--- /dev/null
+++ b/src/lib/hooks/library_manager_collection.cc
@@ -0,0 +1,153 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/callout_manager.h>
+#include <hooks/hooks_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_);
+}
+
+LibraryManagerCollection::LibraryManagerCollection(const HookLibsCollection& libraries)
+ : library_info_(libraries) {
+
+ // We need to split hook libs into library names and library parameters.
+ for (HookLibsCollection::const_iterator it = libraries.begin();
+ it != libraries.end(); ++it) {
+ library_names_.push_back(it->first);
+ }
+}
+
+// Load a set of libraries
+
+bool
+LibraryManagerCollection::loadLibraries() {
+
+ // There must be no libraries still in memory.
+ if (!lib_managers_.empty()) {
+ isc_throw(LibrariesStillOpened, "some libraries are still opened");
+ }
+
+ // Access the callout manager, (re)creating it if required.
+ //
+ // A pointer to the callout manager is maintained by each as well as by
+ // the HooksManager itself. 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.
+ //
+ // The call of this function will result in re-creating the callout manager.
+ // This deletes all callouts (including the pre-library and post-
+ // library) ones. It is up to the libraries to re-register their callouts.
+ // The pre-library and post-library callouts will also need to be
+ // re-registered.
+ callout_manager_.reset(new CalloutManager(library_names_.size()));
+
+ // Now iterate through the libraries are load them one by one. We'll
+ for (size_t 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, unload all currently loaded libraries,
+ // leaving the object in the state it was in before loadLibraries was
+ // called.
+ if (manager->loadLibrary()) {
+ lib_managers_.push_back(manager);
+ } else {
+ static_cast<void>(unloadLibraries());
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+// 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.
+ while (!lib_managers_.empty()) {
+ lib_managers_.pop_back();
+ }
+
+ // Get rid of the callout manager. (The other member, the list of library
+ // names, was cleared when the libraries were loaded.)
+ callout_manager_.reset();
+}
+
+// Prepare the unloading of libraries.
+bool
+LibraryManagerCollection::prepareUnloadLibraries() {
+ bool result = true;
+ // Iterate on library managers in reverse order.
+ for (auto lm = lib_managers_.rbegin(); lm != lib_managers_.rend(); ++lm) {
+ result = (*lm)->prepareUnloadLibrary() && result;
+ }
+ return (result);
+}
+
+// Return number of loaded libraries.
+int
+LibraryManagerCollection::getLoadedLibraryCount() const {
+ return (lib_managers_.size());
+}
+
+// Validate the libraries.
+std::vector<std::string>
+LibraryManagerCollection::validateLibraries(
+ const std::vector<std::string>& libraries) {
+
+ std::vector<std::string> failures;
+ for (size_t i = 0; i < libraries.size(); ++i) {
+ if (!LibraryManager::validateLibrary(libraries[i])) {
+ failures.push_back(libraries[i]);
+ }
+ }
+
+ return (failures);
+}
+
+} // 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..53a9669
--- /dev/null
+++ b/src/lib/hooks/library_manager_collection.h
@@ -0,0 +1,187 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef LIBRARY_MANAGER_COLLECTION_H
+#define LIBRARY_MANAGER_COLLECTION_H
+
+#include <exceptions/exceptions.h>
+
+#include <boost/shared_ptr.hpp>
+#include <hooks/libinfo.h>
+
+#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 closed 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 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.
+///
+/// The collection object also provides a utility function to validate a set
+/// of libraries. The function checks that each library exists, can be opened,
+/// that the "version" function exists and return the right number.
+
+class LibraryManagerCollection {
+public:
+ /// @brief Constructor
+ ///
+ /// @param libraries List of libraries that this collection will manage.
+ /// The order of the libraries is important. It holds the library
+ /// names and its configuration parameters.
+ LibraryManagerCollection(const HookLibsCollection& 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 loading is abandoned and all libraries loaded so far
+ /// are unloaded.
+ ///
+ /// @return true if all libraries loaded, false if one or more failed t
+ //// load.
+ 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 called 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;
+
+ /// @brief Get library names
+ ///
+ /// Returns the list of library names. If called before loadLibraries(),
+ /// the list is the list of names to be loaded; if called afterwards, it
+ /// is the list of libraries that have been loaded.
+ std::vector<std::string> getLibraryNames() const {
+ return (library_names_);
+ }
+
+ /// @brief Returns library info
+ ///
+ /// Returns a collection of libraries, each entry consisting of a library
+ /// name + all its parameters.
+ HookLibsCollection getLibraryInfo() const {
+ return (library_info_);
+ }
+
+ /// @brief Get number of loaded libraries
+ ///
+ /// Mainly for testing, this returns the number of libraries that are
+ /// loaded.
+ ///
+ /// @return Number of libraries that are loaded.
+ int getLoadedLibraryCount() const;
+
+ /// @brief Validate libraries
+ ///
+ /// Utility function to validate libraries. It checks that the libraries
+ /// exist, can be opened, that a "version" function is present in them, and
+ /// that it returns the right number. All errors are logged.
+ ///
+ /// @param libraries List of libraries to validate
+ ///
+ /// @return Vector of libraries that failed to validate, or an empty vector
+ /// if all validated.
+ static std::vector<std::string>
+ validateLibraries(const std::vector<std::string>& libraries);
+
+ /// @brief Prepare libaries unloading
+ ///
+ /// Utility function to call before closing libraries. It runs the
+ /// unload() function when it exists and removes associated callout.
+ /// When this function returns either there is only one owner
+ /// (the hook manager) or some visible dangling pointers so
+ /// libraries are not closed to lower the probability of a crash.
+ /// See @ref LibraryManager::prepareUnloadLibrary.
+ ///
+ /// @return true if all libraries unload were not found or run
+ /// successfully, false on an error.
+ bool prepareUnloadLibraries();
+
+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_;
+
+ /// Vector of library information. Each piece of information
+ /// consists of a pair of (library name, library parameters)
+ HookLibsCollection library_info_;
+
+ /// Callout manager to be associated with the libraries
+ boost::shared_ptr<CalloutManager> callout_manager_;
+};
+
+} // namespace hooks
+} // namespace isc
+
+
+#endif // LIBRARY_MANAGER_COLLECTION_H
diff --git a/src/lib/hooks/parking_lots.h b/src/lib/hooks/parking_lots.h
new file mode 100644
index 0000000..e4e82bd
--- /dev/null
+++ b/src/lib/hooks/parking_lots.h
@@ -0,0 +1,426 @@
+// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef PARKING_LOTS_H
+#define PARKING_LOTS_H
+
+#include <exceptions/exceptions.h>
+#include <boost/any.hpp>
+#include <boost/make_shared.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <list>
+#include <unordered_map>
+#include <mutex>
+#include <thread>
+
+namespace isc {
+namespace hooks {
+
+/// @brief Parking lot for objects, e.g. packets, for a hook point.
+///
+/// Callouts may instruct the servers to "park" processed packets, i.e. suspend
+/// their processing until explicitly unparked. This is useful in cases when
+/// callouts need to perform asynchronous operations related to the packet
+/// processing and the packet must not be further processed until the
+/// asynchronous operations are completed. While the packet is parked, the
+/// new packets can be processed, so the server remains responsive to the
+/// new requests.
+///
+/// Parking lots are created per hook point, so the callouts installed on the
+/// particular hook point only have access to the parking lots dedicated to
+/// them.
+///
+/// The parking lot object supports 5 actions: "park", "reference",
+/// "dereference", "unpark", and "drop".
+///
+/// In the typical case, the server parks the object and the callouts reference
+/// and unpark the objects. Therefore, the @ref ParkingLot object is not passed
+/// directly to the callouts. Instead, a ParkingLotHandle object is provided
+/// to the callout, which only provides access to "reference", "dereference",
+/// and "unpark" operations.
+///
+/// Parking an object is performed, proactively by the server, before callouts
+/// are invoked. Referencing (and dereferencing) an object is performed by the
+/// callouts before the @c CalloutHandle::NEXT_STEP_PARK is returned to the
+/// server.
+///
+/// Trying to reference (or deference) and unparked object will result
+/// in error. Referencing (reference counting) is an important part of the
+/// parking mechanism, which allows multiple callouts, installed on the same
+/// hook point, to perform asynchronous operations and guarantees that the
+/// object remains parked until all those asynchronous operations complete.
+/// Each such callout must call @c unpark() when it desires the object to
+/// be unparked, but the object will only be unparked when all callouts call
+/// this function, i.e. when all callouts signal completion of their respective
+/// asynchronous operations.
+///
+/// Dereferencing, decrements the reference count without invoking the unpark
+/// callback. This allows hook callouts to proactively reference the object
+/// in a callout and then cancel the reference should further processing
+/// deem it the reference unnecessary.
+///
+/// The types of the parked objects provided as T parameter of respective
+/// functions are most often shared pointers. One should not use references
+/// to parked objects nor references to shared pointers to avoid premature
+/// destruction of the parked objects.
+class ParkingLot {
+public:
+ /// @brief Parks an object.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param parked_object object to be parked, e.g. pointer to a packet.
+ /// @param unpark_callback callback function to be invoked when the object
+ /// is unparked.
+ /// @throw InvalidOperation if this object has already been parked.
+ template<typename T>
+ void park(T parked_object, std::function<void()> unpark_callback) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto it = find(parked_object);
+ if (it != parking_.end()) {
+ isc_throw(InvalidOperation, "object is already parked!");
+ }
+
+ // Add the object to the parking lot. At this point refcount = 0.
+ ParkingInfo pinfo(parked_object, unpark_callback);
+ parking_[makeKey(parked_object)] = pinfo;
+ }
+
+ /// @brief Increases reference counter for the parked object.
+ ///
+ /// This method is called by the callouts to increase a reference count
+ /// on the object to be parked. It may only be called after the object
+ /// has been parked
+ ///
+ /// @tparam Type of the parked object.
+ /// @param parked_object object which will be parked.
+ /// @return the integer number of references for this object.
+ template<typename T>
+ int reference(T parked_object) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto it = find(parked_object);
+ if (it == parking_.end()) {
+ isc_throw(InvalidOperation, "cannot reference an object"
+ " that has not been parked.");
+ }
+
+ // Bump and return the reference count
+ return (++it->second.refcount_);
+ }
+
+ /// @brief Decreases the reference counter for the parked object.
+ ///
+ /// This method is called by the callouts to decrease the reference count
+ /// on a parked object.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param parked_object parked object whose count should be reduced.
+ /// @return the integer number of references for this object.
+ template<typename T>
+ int dereference(T parked_object) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto it = find(parked_object);
+ if (it == parking_.end()) {
+ isc_throw(InvalidOperation, "cannot dereference an object"
+ " that has not been parked.");
+ }
+
+ // Decrement and return the reference count.
+ return (--it->second.refcount_);
+ }
+
+ /// @brief Signals that the object should be unparked.
+ ///
+ /// If the specified object is parked in this parking lot, the reference
+ /// count is decreased as a result of this method. If the reference count
+ /// is 0, the object is unparked and the callback is invoked. Typically, the
+ /// callback points to a function which resumes processing of a packet.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param parked_object parked object to be unparked.
+ /// @param force boolean value indicating if the reference counting should
+ /// be ignored and the object should be unparked immediately.
+ /// @return false if the object couldn't be unparked because there is
+ /// no such object, true otherwise.
+ template<typename T>
+ bool unpark(T parked_object, bool force = false) {
+ // Initialize as the empty function.
+ std::function<void()> cb;
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto it = find(parked_object);
+ if (it == parking_.end()) {
+ // No such parked object.
+ return (false);
+ }
+
+ if (force) {
+ it->second.refcount_ = 0;
+ } else {
+ --it->second.refcount_;
+ }
+
+ if (it->second.refcount_ <= 0) {
+ // Unpark the packet and set the callback.
+ cb = it->second.unpark_callback_;
+ parking_.erase(it);
+ }
+ }
+
+ // Invoke the callback if not empty.
+ if (cb) {
+ cb();
+ }
+
+ // Parked object found, so return true to indicate that the
+ // operation was successful. It doesn't necessarily mean
+ // that the object was unparked, but at least the reference
+ // count was decreased.
+ return (true);
+ }
+
+ /// @brief Removes parked object without calling a callback.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param parked_object parked object to be removed.
+ /// @return false if the object couldn't be removed because there is
+ /// no such object, true otherwise.
+ template<typename T>
+ bool drop(T parked_object) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ auto it = find(parked_object);
+ if (it != parking_.end()) {
+ // Parked object found.
+ parking_.erase(it);
+ return (true);
+ }
+
+ // No such object.
+ return (false);
+ }
+
+ /// @brief Returns the current number of objects.
+ size_t size() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ return (parking_.size());
+ }
+
+public:
+
+ /// @brief Holds information about parked object.
+ struct ParkingInfo {
+ /// @brief The parked object.
+ boost::any parked_object_;
+
+ /// @brief The pointer to callback.
+ std::function<void()> unpark_callback_;
+
+ /// @brief The current reference count.
+ int refcount_;
+
+ /// @brief Constructor.
+ ///
+ /// Default constructor.
+ ParkingInfo() : refcount_(0) {}
+
+ /// @brief Constructor.
+ ///
+ /// @param parked_object object being parked.
+ /// @param callback pointer to the callback.
+ ParkingInfo(const boost::any& parked_object,
+ std::function<void()> callback = 0)
+ : parked_object_(parked_object), unpark_callback_(callback),
+ refcount_(0) {}
+
+ /// @brief Update parking information.
+ ///
+ /// @param parked_object parked object.
+ /// @param callback pointer to the callback.
+ void update(const boost::any& parked_object,
+ std::function<void()> callback) {
+ parked_object_ = parked_object;
+ unpark_callback_ = callback;
+ }
+ };
+
+private:
+
+ /// @brief Map which stores parked objects.
+ typedef std::unordered_map<std::string, ParkingInfo> ParkingInfoList;
+
+ /// @brief Type of the iterator in the list of parked objects.
+ typedef ParkingInfoList::iterator ParkingInfoListIterator;
+
+ /// @brief Container holding parked objects for this parking lot.
+ ParkingInfoList parking_;
+
+ /// @brief Construct the key for a given parked object.
+ ///
+ /// @tparam T parked object type.
+ /// @param parked_object object from which the key should be constructed.
+ /// @return string containing the object's key.
+ template<typename T>
+ std::string makeKey(T parked_object) {
+ std::stringstream ss;
+ ss << boost::any_cast<T>(parked_object);
+ return (ss.str());
+ }
+
+ /// @brief Search for the information about the parked object.
+ ///
+ /// @tparam T parked object type.
+ /// @param parked_object object for which to search.
+ /// @return Iterator pointing to the parked object, or @c parking_.end()
+ /// if no such object found.
+ template<typename T>
+ ParkingInfoListIterator find(T parked_object) {
+ return (parking_.find(makeKey(parked_object)));
+ }
+
+ /// @brief The mutex to protect parking lot internal state.
+ ///
+ /// All public methods must enter of lock guard with the mutex
+ /// before any access to the @c parking_ member.
+ std::mutex mutex_;
+};
+
+/// @brief Type of the pointer to the parking lot.
+typedef boost::shared_ptr<ParkingLot> ParkingLotPtr;
+
+/// @brief Provides a limited view to the @c ParkingLot.
+///
+/// The handle is provided to the callouts which can reference and unpark
+/// parked objects. The callouts should not park objects, therefore this
+/// operation is not available.
+///
+/// The types of the parked objects provided as T parameter of respective
+/// functions are most often shared pointers. One should not use references
+/// to parked objects nor references to shared pointers to avoid premature
+/// destruction of the parked objects.
+class ParkingLotHandle {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param parking_lot pointer to the parking lot for which the handle is
+ /// created.
+ ParkingLotHandle(const ParkingLotPtr& parking_lot)
+ : parking_lot_(parking_lot) {
+ }
+
+ /// @brief Increases reference counter for the parked object.
+ ///
+ /// This method is called by the callouts to increase a reference count
+ /// on the object to be parked. It must be called before the object is
+ /// actually parked.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param parked_object object which will be parked.
+ /// @return new reference count as an integer
+ template<typename T>
+ int reference(T parked_object) {
+ return (parking_lot_->reference(parked_object));
+ }
+
+ /// @brief Decreases the reference counter for the parked object.
+ ///
+ /// This method is called by the callouts to decrease the reference count
+ /// of a parked object.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param parked_object object which will be parked.
+ /// @return new reference count as an integer
+ template<typename T>
+ int dereference(T parked_object) {
+ return (parking_lot_->dereference(parked_object));
+ }
+
+ /// @brief Signals that the object should be unparked.
+ ///
+ /// If the specified object is parked in this parking lot, the reference
+ /// count is decreased as a result of this method. If the reference count
+ /// is 0, the object is unparked and the callback is invoked. Typically, the
+ /// callback points to a function which resumes processing of a packet.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param parked_object parked object to be unparked.
+ /// @return false if the object couldn't be unparked because there is
+ /// no such object, true otherwise.
+ template<typename T>
+ bool unpark(T parked_object) {
+ return (parking_lot_->unpark(parked_object));
+ }
+
+ /// @brief Removes parked object without calling a callback.
+ ///
+ /// It ignores any reference counts on the parked object.
+ ///
+ /// @tparam Type of the parked object.
+ /// @param parked_object parked object to be removed.
+ /// @return false if the object couldn't be removed because there is
+ /// no such object, true otherwise.
+ template<typename T>
+ bool drop(T parked_object) {
+ return (parking_lot_->drop(parked_object));
+ }
+
+private:
+
+ /// @brief Parking lot to which this handle points.
+ ParkingLotPtr parking_lot_;
+
+};
+
+/// @brief Pointer to the parking lot handle.
+typedef boost::shared_ptr<ParkingLotHandle> ParkingLotHandlePtr;
+
+/// @brief Collection of parking lots for various hook points.
+class ParkingLots {
+public:
+
+ /// @brief Removes all parked objects.
+ ///
+ /// It doesn't invoke callbacks associated with the removed objects.
+ void clear() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ parking_lots_.clear();
+ }
+
+ /// @brief Returns pointer to the parking lot for a hook points.
+ ///
+ /// If the parking lot for the specified hook point doesn't exist, it is
+ /// created.
+ ///
+ /// @param hook_index index of the hook point with which the parking
+ /// lot is associated.
+ /// @return Pointer to the parking lot.
+ ParkingLotPtr getParkingLotPtr(const int hook_index) {
+ std::lock_guard<std::mutex> lock(mutex_);
+ if (parking_lots_.count(hook_index) == 0) {
+ parking_lots_[hook_index] = boost::make_shared<ParkingLot>();
+ }
+ return (parking_lots_[hook_index]);
+ }
+
+private:
+
+ /// @brief Container holding parking lots for various hook points.
+ std::unordered_map<int, ParkingLotPtr> parking_lots_;
+
+ /// @brief The mutex to protect parking lots internal state.
+ std::mutex mutex_;
+};
+
+/// @brief Type of the pointer to the parking lots.
+typedef boost::shared_ptr<ParkingLots> ParkingLotsPtr;
+
+} // end of namespace hooks
+} // end of namespace isc
+
+#endif
diff --git a/src/lib/hooks/pointer_converter.h b/src/lib/hooks/pointer_converter.h
new file mode 100644
index 0000000..5f51d8d
--- /dev/null
+++ b/src/lib/hooks/pointer_converter.h
@@ -0,0 +1,122 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef 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);
+ }
+
+ /// @brief Return pointer to multi_threading_compatible function
+ ///
+ /// @return Pointer to the multi_threading_compatible function
+ multi_threading_compatible_function_ptr multiThreadingCompatiblePtr() const {
+ return (pointers_.multi_threading_compatible_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
+ multi_threading_compatible_function_ptr multi_threading_compatible_ptr;
+ // Pointer to multi_threading_compatible 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..5ca1a50
--- /dev/null
+++ b/src/lib/hooks/server_hooks.cc
@@ -0,0 +1,221 @@
+// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <hooks/hooks_log.h>
+#include <hooks/server_hooks.h>
+
+#include <algorithm>
+#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.
+//
+// Note that there are no logging messages here or in registerHooks(). The
+// recommended way to initialize hook names is to use static initialization.
+// Here, a static object is declared in a file outside of any function or
+// method. As a result, it is instantiated and its constructor run before the
+// program starts. By putting calls to ServerHooks::registerHook() in there,
+// hooks names are already registered when the program runs. However, at that
+// point, the logging system is not initialized, so messages are unable to
+// be output.
+
+ServerHooks::ServerHooks() {
+ initialize();
+}
+
+// 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));
+
+ /// @todo: We also need to call CalloutManager::ensureVectorSize(), so it
+ /// adjusts its vector. Since CalloutManager is not a singleton, there's
+ /// no getInstance() or similar. Also, CalloutManager uses ServerHooks,
+ /// so such a call would induce circular dependencies. Ugh.
+
+ if (!result.second) {
+
+ // There's a problem with hook libraries that need to be linked with
+ // libdhcpsrv. For example host_cmds hook library requires host
+ // parser, so it needs to be linked with libdhcpsrv. However, when
+ // unit-tests are started, the hook points are not registered.
+ // When the library is loaded new hook points are registered.
+ // This causes issues in the hooks framework, especially when
+ // LibraryManager::unloadLibrary() iterates through all hooks
+ // and then calls deregisterAllCallouts. This method gets
+ // hook_index that is greater than number of elements in
+ // hook_vector_ and then we have a read past the array boundary.
+ /// @todo: See ticket 5251 and 5208 for details.
+ return (getIndex(name));
+
+ // 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;
+
+ // ... and return numeric index.
+ return (index);
+}
+
+// Set ServerHooks object to initial state.
+
+void
+ServerHooks::initialize() {
+
+ // Clear out the name->index and index->name maps.
+ hooks_.clear();
+ inverse_hooks_.clear();
+ parking_lots_.reset(new ParkingLots());
+
+ // 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);
+ }
+}
+
+// Reset ServerHooks object to initial state.
+
+void
+ServerHooks::reset() {
+
+ // Clear all hooks then initialize the pre-defined ones.
+ initialize();
+
+ // 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 recognized");
+ }
+
+ 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 recognized");
+ }
+
+ return (i->second);
+}
+
+int
+ServerHooks::findIndex(const std::string& name) const {
+ // Get iterator to matching element.
+ auto i = hooks_.find(name);
+ return ((i == hooks_.end()) ? -1 : 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() {
+ return (*getServerHooksPtr());
+}
+
+ServerHooksPtr
+ServerHooks::getServerHooksPtr() {
+ static ServerHooksPtr hooks(new ServerHooks());
+ return (hooks);
+}
+
+ParkingLotsPtr
+ServerHooks::getParkingLotsPtr() const {
+ return (parking_lots_);
+}
+
+ParkingLotPtr
+ServerHooks::getParkingLotPtr(const int hook_index) {
+ return (parking_lots_->getParkingLotPtr(hook_index));
+}
+
+ParkingLotPtr
+ServerHooks::getParkingLotPtr(const std::string& hook_name) {
+ return (parking_lots_->getParkingLotPtr(getServerHooks().getIndex(hook_name)));
+}
+
+std::string
+ServerHooks::commandToHookName(const std::string& command_name) {
+ // Prefix the command name with a dollar sign.
+ std::string hook_name = std::string("$") + command_name;
+ // Replace all hyphens with underscores.
+ std::replace(hook_name.begin(), hook_name.end(), '-', '_');
+ return (hook_name);
+}
+
+std::string
+ServerHooks::hookToCommandName(const std::string& hook_name) {
+ if (!hook_name.empty() && hook_name.front() == '$') {
+ std::string command_name = hook_name.substr(1);
+ std::replace(command_name.begin(), command_name.end(), '_', '-');
+ return (command_name);
+ }
+ return ("");
+}
+
+
+
+} // namespace hooks
+} // namespace isc
diff --git a/src/lib/hooks/server_hooks.h b/src/lib/hooks/server_hooks.h
new file mode 100644
index 0000000..2c9df97
--- /dev/null
+++ b/src/lib/hooks/server_hooks.h
@@ -0,0 +1,246 @@
+// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef SERVER_HOOKS_H
+#define SERVER_HOOKS_H
+
+#include <exceptions/exceptions.h>
+#include <hooks/parking_lots.h>
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.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) {}
+};
+
+class ServerHooks;
+typedef boost::shared_ptr<ServerHooks> ServerHooksPtr;
+
+/// @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
+ /// testing to reset the global ServerHooks object; it should never be
+ /// used in production.
+ ///
+ /// @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 hook
+ ///
+ /// @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 Find hook index
+ ///
+ /// Provides exception safe method of retrieving an index of the
+ /// specified hook.
+ ///
+ /// @param name Name of the hook
+ ///
+ /// @return Index of the hook if the hook point exists, or -1 if the
+ /// hook point doesn't exist.
+ int findIndex(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();
+
+ /// @brief Returns pointer to ServerHooks object.
+ ///
+ /// @return Pointer to the global ServerHooks object.
+ static ServerHooksPtr getServerHooksPtr();
+
+ /// @brief Returns pointer to all parking lots.
+ ///
+ /// @return pointer to all parking lots.
+ ParkingLotsPtr getParkingLotsPtr() const;
+
+ /// @brief Returns pointer to the ParkingLot for the specified hook index.
+ ///
+ /// @param hook_index index of the hook point for which the parking lot
+ /// should be returned.
+ /// @return Pointer to the ParkingLot object.
+ ParkingLotPtr getParkingLotPtr(const int hook_index);
+
+ /// @brief Returns pointer to the ParkingLot for the specified hook name.
+ ///
+ /// @param hook_name name of the hook point for which the parking lot
+ /// should be returned.
+ /// @return Pointer to the ParkingLot object.
+ ParkingLotPtr getParkingLotPtr(const std::string& hook_name);
+
+ /// @brief Generates hook point name for the given control command name.
+ ///
+ /// This function is called to generate the name of the hook point
+ /// when the hook point is used to install command handlers for the
+ /// given control command.
+ ///
+ /// The name of the hook point is generated as follows:
+ /// - command name is prefixed with a dollar sign,
+ /// - all hyphens are replaced with underscores.
+ ///
+ /// For example, if the command_name is 'foo-bar', the resulting hook
+ /// point name will be '$foo_bar'.
+ ///
+ /// @param command_name Command name for which the hook point name is
+ /// to be generated.
+ ///
+ /// @return Hook point name, or an empty string if the command name
+ /// can't be converted to a hook name (e.g. when it lacks dollar sign).
+ static std::string commandToHookName(const std::string& command_name);
+
+ /// @brief Returns command name for a specified hook name.
+ ///
+ /// This function removes leading dollar sign and replaces underscores
+ /// with hyphens.
+ ///
+ /// @param hook_name Hook name for which command name should be returned.
+ ///
+ /// @return Command name.
+ static std::string hookToCommandName(const std::string& hook_name);
+
+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.
+ ///
+ /// The 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();
+
+ /// @brief Initialize hooks
+ ///
+ /// Sets the collection of hooks to the initial state, with just the
+ /// context_create and context_destroy hooks set. This is used during
+ /// construction.
+ ///
+ /// @throws isc::Unexpected if the registration of the pre-defined hooks
+ /// fails in some way.
+ void initialize();
+
+ /// 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
+
+ ParkingLotsPtr parking_lots_;
+};
+
+} // 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..b6fe8b9
--- /dev/null
+++ b/src/lib/hooks/tests/Makefile.am
@@ -0,0 +1,153 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+# Kea libraries against which the test user libraries are linked.
+HOOKS_LIB = $(top_builddir)/src/lib/hooks/libkea-hooks.la
+LOG_LIB = $(top_builddir)/src/lib/log/libkea-log.la
+UTIL_LIB = $(top_builddir)/src/lib/util/libkea-util.la
+EXCEPTIONS_LIB = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+
+ALL_LIBS = $(HOOKS_LIB) $(LOG_LIB) $(UTIL_LIB) $(EXCEPTIONS_LIB) $(LOG4CPLUS_LIBS)
+
+# Files to clean include the file created by testing.
+CLEANFILES = *.gcno *.gcda $(builddir)/marker_file.dat
+
+# Files generated by configure
+DISTCLEANFILES = marker_file.h test_libraries.h
+
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+TESTS =
+if HAVE_GTEST
+# Build shared libraries for testing. The libtool way to create a shared library
+# is to specify "-avoid-version -export-dynamic -module" in the library LDFLAGS
+# (see http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html).
+# Use of these switches will guarantee that the .so files are created in the
+# .libs folder and they can be dlopened.
+#
+# Note that the shared libraries with callouts should not be used together with
+# the --enable-static-link option. With this option, the bind10 libraries are
+# statically linked with the program and if the callout invokes the methods
+# which belong to these libraries, the library with the callout will get its
+# own copy of the static objects (e.g. logger, ServerHooks) and that will lead
+# to unexpected errors. For this reason, the --enable-static-link option is
+# ignored for unit tests built here.
+
+noinst_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la \
+ liblecl.la libucl.la libfcl.la libpcl.la libacl.la
+
+# -rpath /nowhere is a hack to trigger libtool to not create a
+# convenience archive, resulting in shared modules
+
+# No version function
+libnvl_la_SOURCES = no_version_library.cc
+libnvl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libnvl_la_CPPFLAGS = $(AM_CPPFLAGS)
+libnvl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# Incorrect version function
+libivl_la_SOURCES = incorrect_version_library.cc
+libivl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libivl_la_CPPFLAGS = $(AM_CPPFLAGS)
+libivl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# All framework functions throw an exception
+libfxl_la_SOURCES = framework_exception_library.cc
+libfxl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libfxl_la_CPPFLAGS = $(AM_CPPFLAGS)
+libfxl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The basic callout library - contains standard callouts
+libbcl_la_SOURCES = basic_callout_library.cc
+libbcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libbcl_la_CPPFLAGS = $(AM_CPPFLAGS)
+libbcl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# 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)
+liblcl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# 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)
+liblecl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# 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)
+libucl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# 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)
+libfcl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The parameters checking callout library - expects
+libpcl_la_SOURCES = callout_params_library.cc
+libpcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libpcl_la_CPPFLAGS = $(AM_CPPFLAGS)
+libpcl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+libpcl_la_LDFLAGS += $(top_builddir)/src/lib/util/libkea-util.la
+
+# The async callout library - parks object for asynchronous task
+libacl_la_SOURCES = async_callout_library.cc
+libacl_la_CXXFLAGS = $(AM_CXXFLAGS)
+libacl_la_CPPFLAGS = $(AM_CPPFLAGS)
+libacl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+TESTS += run_unittests
+run_unittests_SOURCES = run_unittests.cc
+run_unittests_SOURCES += callout_handle_unittest.cc
+run_unittests_SOURCES += callout_handle_associate_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 += parking_lots_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_CXXFLAGS = $(AM_CXXFLAGS)
+run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
+if USE_STATIC_LINK
+run_unittests_LDFLAGS += -static -export-dynamic
+endif
+
+run_unittests_LDADD = $(AM_LDADD)
+run_unittests_LDADD += $(ALL_LIBS)
+run_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+run_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+run_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la
+run_unittests_LDADD += $(GTEST_LDADD) $(LOG4CPLUS_LIBS) $(BOOST_LIBS)
+# As noted in configure.ac, libtool doesn't work perfectly with Darwin: it
+# embeds the final install path in dynamic libraries and loadable modules refer
+# to that path even if its loaded within the source tree, so preventing tests
+# from working - but only when linking statically. The solution used in other
+# Makefiles (setting the path to the dynamic libraries via an environment
+# variable) don't seem to work. What does work is to run the unit test using
+# libtool and specifying paths via -dlopen switches. So... If running in an
+# environment where we have to set the library path AND if linking statically,
+# override the "check" target and run the unit tests ourselves.
+
+endif
+
+noinst_PROGRAMS = $(TESTS)
+
+EXTRA_DIST = marker_file.h.in test_libraries.h.in
diff --git a/src/lib/hooks/tests/Makefile.in b/src/lib/hooks/tests/Makefile.in
new file mode 100644
index 0000000..a8ba259
--- /dev/null
+++ b/src/lib/hooks/tests/Makefile.in
@@ -0,0 +1,1525 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+TESTS = $(am__EXEEXT_1)
+@HAVE_GTEST_TRUE@am__append_1 = run_unittests
+@HAVE_GTEST_TRUE@@USE_STATIC_LINK_TRUE@am__append_2 = -static -export-dynamic
+noinst_PROGRAMS = $(am__EXEEXT_2)
+subdir = src/lib/hooks/tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp11.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_sysrepo.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = marker_file.h test_libraries.h
+CONFIG_CLEAN_VPATH_FILES =
+@HAVE_GTEST_TRUE@am__EXEEXT_1 = run_unittests$(EXEEXT)
+am__EXEEXT_2 = $(am__EXEEXT_1)
+PROGRAMS = $(noinst_PROGRAMS)
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libacl_la_LIBADD =
+am__libacl_la_SOURCES_DIST = async_callout_library.cc
+@HAVE_GTEST_TRUE@am_libacl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libacl_la-async_callout_library.lo
+libacl_la_OBJECTS = $(am_libacl_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libacl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libacl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(libacl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libacl_la_rpath =
+libbcl_la_LIBADD =
+am__libbcl_la_SOURCES_DIST = basic_callout_library.cc
+@HAVE_GTEST_TRUE@am_libbcl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libbcl_la-basic_callout_library.lo
+libbcl_la_OBJECTS = $(am_libbcl_la_OBJECTS)
+libbcl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libbcl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(libbcl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libbcl_la_rpath =
+libfcl_la_LIBADD =
+am__libfcl_la_SOURCES_DIST = full_callout_library.cc
+@HAVE_GTEST_TRUE@am_libfcl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libfcl_la-full_callout_library.lo
+libfcl_la_OBJECTS = $(am_libfcl_la_OBJECTS)
+libfcl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libfcl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(libfcl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libfcl_la_rpath =
+libfxl_la_LIBADD =
+am__libfxl_la_SOURCES_DIST = framework_exception_library.cc
+@HAVE_GTEST_TRUE@am_libfxl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libfxl_la-framework_exception_library.lo
+libfxl_la_OBJECTS = $(am_libfxl_la_OBJECTS)
+libfxl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libfxl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(libfxl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libfxl_la_rpath =
+libivl_la_LIBADD =
+am__libivl_la_SOURCES_DIST = incorrect_version_library.cc
+@HAVE_GTEST_TRUE@am_libivl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libivl_la-incorrect_version_library.lo
+libivl_la_OBJECTS = $(am_libivl_la_OBJECTS)
+libivl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libivl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(libivl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libivl_la_rpath =
+liblcl_la_LIBADD =
+am__liblcl_la_SOURCES_DIST = load_callout_library.cc
+@HAVE_GTEST_TRUE@am_liblcl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ liblcl_la-load_callout_library.lo
+liblcl_la_OBJECTS = $(am_liblcl_la_OBJECTS)
+liblcl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(liblcl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(liblcl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_liblcl_la_rpath =
+liblecl_la_LIBADD =
+am__liblecl_la_SOURCES_DIST = load_error_callout_library.cc
+@HAVE_GTEST_TRUE@am_liblecl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ liblecl_la-load_error_callout_library.lo
+liblecl_la_OBJECTS = $(am_liblecl_la_OBJECTS)
+liblecl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(liblecl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(liblecl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_liblecl_la_rpath =
+libnvl_la_LIBADD =
+am__libnvl_la_SOURCES_DIST = no_version_library.cc
+@HAVE_GTEST_TRUE@am_libnvl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libnvl_la-no_version_library.lo
+libnvl_la_OBJECTS = $(am_libnvl_la_OBJECTS)
+libnvl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libnvl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(libnvl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libnvl_la_rpath =
+libpcl_la_LIBADD =
+am__libpcl_la_SOURCES_DIST = callout_params_library.cc
+@HAVE_GTEST_TRUE@am_libpcl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libpcl_la-callout_params_library.lo
+libpcl_la_OBJECTS = $(am_libpcl_la_OBJECTS)
+libpcl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libpcl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(libpcl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libpcl_la_rpath =
+libucl_la_LIBADD =
+am__libucl_la_SOURCES_DIST = unload_callout_library.cc
+@HAVE_GTEST_TRUE@am_libucl_la_OBJECTS = \
+@HAVE_GTEST_TRUE@ libucl_la-unload_callout_library.lo
+libucl_la_OBJECTS = $(am_libucl_la_OBJECTS)
+libucl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libucl_la_CXXFLAGS) \
+ $(CXXFLAGS) $(libucl_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_GTEST_TRUE@am_libucl_la_rpath =
+am__run_unittests_SOURCES_DIST = run_unittests.cc \
+ callout_handle_unittest.cc \
+ callout_handle_associate_unittest.cc \
+ callout_manager_unittest.cc common_test_class.h \
+ handles_unittest.cc hooks_manager_unittest.cc \
+ library_manager_collection_unittest.cc \
+ library_manager_unittest.cc parking_lots_unittest.cc \
+ server_hooks_unittest.cc
+@HAVE_GTEST_TRUE@am_run_unittests_OBJECTS = \
+@HAVE_GTEST_TRUE@ run_unittests-run_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-callout_handle_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-callout_handle_associate_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-callout_manager_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-handles_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-hooks_manager_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-library_manager_collection_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-library_manager_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-parking_lots_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ run_unittests-server_hooks_unittest.$(OBJEXT)
+nodist_run_unittests_OBJECTS =
+run_unittests_OBJECTS = $(am_run_unittests_OBJECTS) \
+ $(nodist_run_unittests_OBJECTS)
+am__DEPENDENCIES_1 =
+am__DEPENDENCIES_2 = $(HOOKS_LIB) $(LOG_LIB) $(UTIL_LIB) \
+ $(EXCEPTIONS_LIB) $(am__DEPENDENCIES_1)
+@HAVE_GTEST_TRUE@run_unittests_DEPENDENCIES = $(am__DEPENDENCIES_2) \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1)
+run_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(run_unittests_CXXFLAGS) $(CXXFLAGS) $(run_unittests_LDFLAGS) \
+ $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/libacl_la-async_callout_library.Plo \
+ ./$(DEPDIR)/libbcl_la-basic_callout_library.Plo \
+ ./$(DEPDIR)/libfcl_la-full_callout_library.Plo \
+ ./$(DEPDIR)/libfxl_la-framework_exception_library.Plo \
+ ./$(DEPDIR)/libivl_la-incorrect_version_library.Plo \
+ ./$(DEPDIR)/liblcl_la-load_callout_library.Plo \
+ ./$(DEPDIR)/liblecl_la-load_error_callout_library.Plo \
+ ./$(DEPDIR)/libnvl_la-no_version_library.Plo \
+ ./$(DEPDIR)/libpcl_la-callout_params_library.Plo \
+ ./$(DEPDIR)/libucl_la-unload_callout_library.Plo \
+ ./$(DEPDIR)/run_unittests-callout_handle_associate_unittest.Po \
+ ./$(DEPDIR)/run_unittests-callout_handle_unittest.Po \
+ ./$(DEPDIR)/run_unittests-callout_manager_unittest.Po \
+ ./$(DEPDIR)/run_unittests-handles_unittest.Po \
+ ./$(DEPDIR)/run_unittests-hooks_manager_unittest.Po \
+ ./$(DEPDIR)/run_unittests-library_manager_collection_unittest.Po \
+ ./$(DEPDIR)/run_unittests-library_manager_unittest.Po \
+ ./$(DEPDIR)/run_unittests-parking_lots_unittest.Po \
+ ./$(DEPDIR)/run_unittests-run_unittests.Po \
+ ./$(DEPDIR)/run_unittests-server_hooks_unittest.Po
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libacl_la_SOURCES) $(libbcl_la_SOURCES) \
+ $(libfcl_la_SOURCES) $(libfxl_la_SOURCES) $(libivl_la_SOURCES) \
+ $(liblcl_la_SOURCES) $(liblecl_la_SOURCES) \
+ $(libnvl_la_SOURCES) $(libpcl_la_SOURCES) $(libucl_la_SOURCES) \
+ $(run_unittests_SOURCES) $(nodist_run_unittests_SOURCES)
+DIST_SOURCES = $(am__libacl_la_SOURCES_DIST) \
+ $(am__libbcl_la_SOURCES_DIST) $(am__libfcl_la_SOURCES_DIST) \
+ $(am__libfxl_la_SOURCES_DIST) $(am__libivl_la_SOURCES_DIST) \
+ $(am__liblcl_la_SOURCES_DIST) $(am__liblecl_la_SOURCES_DIST) \
+ $(am__libnvl_la_SOURCES_DIST) $(am__libpcl_la_SOURCES_DIST) \
+ $(am__libucl_la_SOURCES_DIST) \
+ $(am__run_unittests_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/marker_file.h.in \
+ $(srcdir)/test_libraries.h.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_SYSREPO = @HAVE_SYSREPO@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = .
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \
+ $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+# Kea libraries against which the test user libraries are linked.
+HOOKS_LIB = $(top_builddir)/src/lib/hooks/libkea-hooks.la
+LOG_LIB = $(top_builddir)/src/lib/log/libkea-log.la
+UTIL_LIB = $(top_builddir)/src/lib/util/libkea-util.la
+EXCEPTIONS_LIB = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+ALL_LIBS = $(HOOKS_LIB) $(LOG_LIB) $(UTIL_LIB) $(EXCEPTIONS_LIB) $(LOG4CPLUS_LIBS)
+
+# Files to clean include the file created by testing.
+CLEANFILES = *.gcno *.gcda $(builddir)/marker_file.dat
+
+# Files generated by configure
+DISTCLEANFILES = marker_file.h test_libraries.h
+TESTS_ENVIRONMENT = \
+ $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+
+# Build shared libraries for testing. The libtool way to create a shared library
+# is to specify "-avoid-version -export-dynamic -module" in the library LDFLAGS
+# (see http://www.gnu.org/software/libtool/manual/html_node/Link-mode.html).
+# Use of these switches will guarantee that the .so files are created in the
+# .libs folder and they can be dlopened.
+#
+# Note that the shared libraries with callouts should not be used together with
+# the --enable-static-link option. With this option, the bind10 libraries are
+# statically linked with the program and if the callout invokes the methods
+# which belong to these libraries, the library with the callout will get its
+# own copy of the static objects (e.g. logger, ServerHooks) and that will lead
+# to unexpected errors. For this reason, the --enable-static-link option is
+# ignored for unit tests built here.
+@HAVE_GTEST_TRUE@noinst_LTLIBRARIES = libnvl.la libivl.la libfxl.la libbcl.la liblcl.la \
+@HAVE_GTEST_TRUE@ liblecl.la libucl.la libfcl.la libpcl.la libacl.la
+
+
+# -rpath /nowhere is a hack to trigger libtool to not create a
+# convenience archive, resulting in shared modules
+
+# No version function
+@HAVE_GTEST_TRUE@libnvl_la_SOURCES = no_version_library.cc
+@HAVE_GTEST_TRUE@libnvl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libnvl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libnvl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# Incorrect version function
+@HAVE_GTEST_TRUE@libivl_la_SOURCES = incorrect_version_library.cc
+@HAVE_GTEST_TRUE@libivl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libivl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libivl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# All framework functions throw an exception
+@HAVE_GTEST_TRUE@libfxl_la_SOURCES = framework_exception_library.cc
+@HAVE_GTEST_TRUE@libfxl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libfxl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libfxl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The basic callout library - contains standard callouts
+@HAVE_GTEST_TRUE@libbcl_la_SOURCES = basic_callout_library.cc
+@HAVE_GTEST_TRUE@libbcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libbcl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libbcl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The load callout library - contains a load function
+@HAVE_GTEST_TRUE@liblcl_la_SOURCES = load_callout_library.cc
+@HAVE_GTEST_TRUE@liblcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@liblcl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@liblcl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The load error callout library - contains a load function that returns
+# an error.
+@HAVE_GTEST_TRUE@liblecl_la_SOURCES = load_error_callout_library.cc
+@HAVE_GTEST_TRUE@liblecl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@liblecl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@liblecl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The unload callout library - contains an unload function that
+# creates a marker file.
+@HAVE_GTEST_TRUE@libucl_la_SOURCES = unload_callout_library.cc
+@HAVE_GTEST_TRUE@libucl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libucl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libucl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The full callout library - contains all three framework functions.
+@HAVE_GTEST_TRUE@libfcl_la_SOURCES = full_callout_library.cc
+@HAVE_GTEST_TRUE@libfcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libfcl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libfcl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+
+# The parameters checking callout library - expects
+@HAVE_GTEST_TRUE@libpcl_la_SOURCES = callout_params_library.cc
+@HAVE_GTEST_TRUE@libpcl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libpcl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libpcl_la_LDFLAGS = -avoid-version -export-dynamic \
+@HAVE_GTEST_TRUE@ -module -rpath /nowhere \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la
+
+# The async callout library - parks object for asynchronous task
+@HAVE_GTEST_TRUE@libacl_la_SOURCES = async_callout_library.cc
+@HAVE_GTEST_TRUE@libacl_la_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@libacl_la_CPPFLAGS = $(AM_CPPFLAGS)
+@HAVE_GTEST_TRUE@libacl_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere
+@HAVE_GTEST_TRUE@run_unittests_SOURCES = run_unittests.cc \
+@HAVE_GTEST_TRUE@ callout_handle_unittest.cc \
+@HAVE_GTEST_TRUE@ callout_handle_associate_unittest.cc \
+@HAVE_GTEST_TRUE@ callout_manager_unittest.cc \
+@HAVE_GTEST_TRUE@ common_test_class.h handles_unittest.cc \
+@HAVE_GTEST_TRUE@ hooks_manager_unittest.cc \
+@HAVE_GTEST_TRUE@ library_manager_collection_unittest.cc \
+@HAVE_GTEST_TRUE@ library_manager_unittest.cc \
+@HAVE_GTEST_TRUE@ parking_lots_unittest.cc \
+@HAVE_GTEST_TRUE@ server_hooks_unittest.cc
+@HAVE_GTEST_TRUE@nodist_run_unittests_SOURCES = marker_file.h \
+@HAVE_GTEST_TRUE@ test_libraries.h
+@HAVE_GTEST_TRUE@run_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
+@HAVE_GTEST_TRUE@run_unittests_LDFLAGS = $(AM_LDFLAGS) \
+@HAVE_GTEST_TRUE@ $(GTEST_LDFLAGS) $(am__append_2)
+@HAVE_GTEST_TRUE@run_unittests_LDADD = $(AM_LDADD) $(ALL_LIBS) \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/unittests/libutil_unittests.la \
+@HAVE_GTEST_TRUE@ $(GTEST_LDADD) $(LOG4CPLUS_LIBS) \
+@HAVE_GTEST_TRUE@ $(BOOST_LIBS)
+EXTRA_DIST = marker_file.h.in test_libraries.h.in
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/hooks/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/lib/hooks/tests/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+marker_file.h: $(top_builddir)/config.status $(srcdir)/marker_file.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+test_libraries.h: $(top_builddir)/config.status $(srcdir)/test_libraries.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libacl.la: $(libacl_la_OBJECTS) $(libacl_la_DEPENDENCIES) $(EXTRA_libacl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libacl_la_LINK) $(am_libacl_la_rpath) $(libacl_la_OBJECTS) $(libacl_la_LIBADD) $(LIBS)
+
+libbcl.la: $(libbcl_la_OBJECTS) $(libbcl_la_DEPENDENCIES) $(EXTRA_libbcl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libbcl_la_LINK) $(am_libbcl_la_rpath) $(libbcl_la_OBJECTS) $(libbcl_la_LIBADD) $(LIBS)
+
+libfcl.la: $(libfcl_la_OBJECTS) $(libfcl_la_DEPENDENCIES) $(EXTRA_libfcl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libfcl_la_LINK) $(am_libfcl_la_rpath) $(libfcl_la_OBJECTS) $(libfcl_la_LIBADD) $(LIBS)
+
+libfxl.la: $(libfxl_la_OBJECTS) $(libfxl_la_DEPENDENCIES) $(EXTRA_libfxl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libfxl_la_LINK) $(am_libfxl_la_rpath) $(libfxl_la_OBJECTS) $(libfxl_la_LIBADD) $(LIBS)
+
+libivl.la: $(libivl_la_OBJECTS) $(libivl_la_DEPENDENCIES) $(EXTRA_libivl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libivl_la_LINK) $(am_libivl_la_rpath) $(libivl_la_OBJECTS) $(libivl_la_LIBADD) $(LIBS)
+
+liblcl.la: $(liblcl_la_OBJECTS) $(liblcl_la_DEPENDENCIES) $(EXTRA_liblcl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(liblcl_la_LINK) $(am_liblcl_la_rpath) $(liblcl_la_OBJECTS) $(liblcl_la_LIBADD) $(LIBS)
+
+liblecl.la: $(liblecl_la_OBJECTS) $(liblecl_la_DEPENDENCIES) $(EXTRA_liblecl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(liblecl_la_LINK) $(am_liblecl_la_rpath) $(liblecl_la_OBJECTS) $(liblecl_la_LIBADD) $(LIBS)
+
+libnvl.la: $(libnvl_la_OBJECTS) $(libnvl_la_DEPENDENCIES) $(EXTRA_libnvl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libnvl_la_LINK) $(am_libnvl_la_rpath) $(libnvl_la_OBJECTS) $(libnvl_la_LIBADD) $(LIBS)
+
+libpcl.la: $(libpcl_la_OBJECTS) $(libpcl_la_DEPENDENCIES) $(EXTRA_libpcl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libpcl_la_LINK) $(am_libpcl_la_rpath) $(libpcl_la_OBJECTS) $(libpcl_la_LIBADD) $(LIBS)
+
+libucl.la: $(libucl_la_OBJECTS) $(libucl_la_DEPENDENCIES) $(EXTRA_libucl_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libucl_la_LINK) $(am_libucl_la_rpath) $(libucl_la_OBJECTS) $(libucl_la_LIBADD) $(LIBS)
+
+run_unittests$(EXEEXT): $(run_unittests_OBJECTS) $(run_unittests_DEPENDENCIES) $(EXTRA_run_unittests_DEPENDENCIES)
+ @rm -f run_unittests$(EXEEXT)
+ $(AM_V_CXXLD)$(run_unittests_LINK) $(run_unittests_OBJECTS) $(run_unittests_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libacl_la-async_callout_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbcl_la-basic_callout_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfcl_la-full_callout_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libfxl_la-framework_exception_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libivl_la-incorrect_version_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblcl_la-load_callout_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblecl_la-load_error_callout_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnvl_la-no_version_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpcl_la-callout_params_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libucl_la-unload_callout_library.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-callout_handle_associate_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-callout_handle_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-callout_manager_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-handles_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-hooks_manager_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-library_manager_collection_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-library_manager_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-parking_lots_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-run_unittests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/run_unittests-server_hooks_unittest.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+libacl_la-async_callout_library.lo: async_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libacl_la_CPPFLAGS) $(CPPFLAGS) $(libacl_la_CXXFLAGS) $(CXXFLAGS) -MT libacl_la-async_callout_library.lo -MD -MP -MF $(DEPDIR)/libacl_la-async_callout_library.Tpo -c -o libacl_la-async_callout_library.lo `test -f 'async_callout_library.cc' || echo '$(srcdir)/'`async_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libacl_la-async_callout_library.Tpo $(DEPDIR)/libacl_la-async_callout_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='async_callout_library.cc' object='libacl_la-async_callout_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libacl_la_CPPFLAGS) $(CPPFLAGS) $(libacl_la_CXXFLAGS) $(CXXFLAGS) -c -o libacl_la-async_callout_library.lo `test -f 'async_callout_library.cc' || echo '$(srcdir)/'`async_callout_library.cc
+
+libbcl_la-basic_callout_library.lo: basic_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbcl_la_CPPFLAGS) $(CPPFLAGS) $(libbcl_la_CXXFLAGS) $(CXXFLAGS) -MT libbcl_la-basic_callout_library.lo -MD -MP -MF $(DEPDIR)/libbcl_la-basic_callout_library.Tpo -c -o libbcl_la-basic_callout_library.lo `test -f 'basic_callout_library.cc' || echo '$(srcdir)/'`basic_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libbcl_la-basic_callout_library.Tpo $(DEPDIR)/libbcl_la-basic_callout_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='basic_callout_library.cc' object='libbcl_la-basic_callout_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbcl_la_CPPFLAGS) $(CPPFLAGS) $(libbcl_la_CXXFLAGS) $(CXXFLAGS) -c -o libbcl_la-basic_callout_library.lo `test -f 'basic_callout_library.cc' || echo '$(srcdir)/'`basic_callout_library.cc
+
+libfcl_la-full_callout_library.lo: full_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfcl_la_CPPFLAGS) $(CPPFLAGS) $(libfcl_la_CXXFLAGS) $(CXXFLAGS) -MT libfcl_la-full_callout_library.lo -MD -MP -MF $(DEPDIR)/libfcl_la-full_callout_library.Tpo -c -o libfcl_la-full_callout_library.lo `test -f 'full_callout_library.cc' || echo '$(srcdir)/'`full_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfcl_la-full_callout_library.Tpo $(DEPDIR)/libfcl_la-full_callout_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='full_callout_library.cc' object='libfcl_la-full_callout_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfcl_la_CPPFLAGS) $(CPPFLAGS) $(libfcl_la_CXXFLAGS) $(CXXFLAGS) -c -o libfcl_la-full_callout_library.lo `test -f 'full_callout_library.cc' || echo '$(srcdir)/'`full_callout_library.cc
+
+libfxl_la-framework_exception_library.lo: framework_exception_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfxl_la_CPPFLAGS) $(CPPFLAGS) $(libfxl_la_CXXFLAGS) $(CXXFLAGS) -MT libfxl_la-framework_exception_library.lo -MD -MP -MF $(DEPDIR)/libfxl_la-framework_exception_library.Tpo -c -o libfxl_la-framework_exception_library.lo `test -f 'framework_exception_library.cc' || echo '$(srcdir)/'`framework_exception_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libfxl_la-framework_exception_library.Tpo $(DEPDIR)/libfxl_la-framework_exception_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='framework_exception_library.cc' object='libfxl_la-framework_exception_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libfxl_la_CPPFLAGS) $(CPPFLAGS) $(libfxl_la_CXXFLAGS) $(CXXFLAGS) -c -o libfxl_la-framework_exception_library.lo `test -f 'framework_exception_library.cc' || echo '$(srcdir)/'`framework_exception_library.cc
+
+libivl_la-incorrect_version_library.lo: incorrect_version_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libivl_la_CPPFLAGS) $(CPPFLAGS) $(libivl_la_CXXFLAGS) $(CXXFLAGS) -MT libivl_la-incorrect_version_library.lo -MD -MP -MF $(DEPDIR)/libivl_la-incorrect_version_library.Tpo -c -o libivl_la-incorrect_version_library.lo `test -f 'incorrect_version_library.cc' || echo '$(srcdir)/'`incorrect_version_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libivl_la-incorrect_version_library.Tpo $(DEPDIR)/libivl_la-incorrect_version_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='incorrect_version_library.cc' object='libivl_la-incorrect_version_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libivl_la_CPPFLAGS) $(CPPFLAGS) $(libivl_la_CXXFLAGS) $(CXXFLAGS) -c -o libivl_la-incorrect_version_library.lo `test -f 'incorrect_version_library.cc' || echo '$(srcdir)/'`incorrect_version_library.cc
+
+liblcl_la-load_callout_library.lo: load_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblcl_la_CPPFLAGS) $(CPPFLAGS) $(liblcl_la_CXXFLAGS) $(CXXFLAGS) -MT liblcl_la-load_callout_library.lo -MD -MP -MF $(DEPDIR)/liblcl_la-load_callout_library.Tpo -c -o liblcl_la-load_callout_library.lo `test -f 'load_callout_library.cc' || echo '$(srcdir)/'`load_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblcl_la-load_callout_library.Tpo $(DEPDIR)/liblcl_la-load_callout_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='load_callout_library.cc' object='liblcl_la-load_callout_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblcl_la_CPPFLAGS) $(CPPFLAGS) $(liblcl_la_CXXFLAGS) $(CXXFLAGS) -c -o liblcl_la-load_callout_library.lo `test -f 'load_callout_library.cc' || echo '$(srcdir)/'`load_callout_library.cc
+
+liblecl_la-load_error_callout_library.lo: load_error_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblecl_la_CPPFLAGS) $(CPPFLAGS) $(liblecl_la_CXXFLAGS) $(CXXFLAGS) -MT liblecl_la-load_error_callout_library.lo -MD -MP -MF $(DEPDIR)/liblecl_la-load_error_callout_library.Tpo -c -o liblecl_la-load_error_callout_library.lo `test -f 'load_error_callout_library.cc' || echo '$(srcdir)/'`load_error_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblecl_la-load_error_callout_library.Tpo $(DEPDIR)/liblecl_la-load_error_callout_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='load_error_callout_library.cc' object='liblecl_la-load_error_callout_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblecl_la_CPPFLAGS) $(CPPFLAGS) $(liblecl_la_CXXFLAGS) $(CXXFLAGS) -c -o liblecl_la-load_error_callout_library.lo `test -f 'load_error_callout_library.cc' || echo '$(srcdir)/'`load_error_callout_library.cc
+
+libnvl_la-no_version_library.lo: no_version_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnvl_la_CPPFLAGS) $(CPPFLAGS) $(libnvl_la_CXXFLAGS) $(CXXFLAGS) -MT libnvl_la-no_version_library.lo -MD -MP -MF $(DEPDIR)/libnvl_la-no_version_library.Tpo -c -o libnvl_la-no_version_library.lo `test -f 'no_version_library.cc' || echo '$(srcdir)/'`no_version_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnvl_la-no_version_library.Tpo $(DEPDIR)/libnvl_la-no_version_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='no_version_library.cc' object='libnvl_la-no_version_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libnvl_la_CPPFLAGS) $(CPPFLAGS) $(libnvl_la_CXXFLAGS) $(CXXFLAGS) -c -o libnvl_la-no_version_library.lo `test -f 'no_version_library.cc' || echo '$(srcdir)/'`no_version_library.cc
+
+libpcl_la-callout_params_library.lo: callout_params_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpcl_la_CPPFLAGS) $(CPPFLAGS) $(libpcl_la_CXXFLAGS) $(CXXFLAGS) -MT libpcl_la-callout_params_library.lo -MD -MP -MF $(DEPDIR)/libpcl_la-callout_params_library.Tpo -c -o libpcl_la-callout_params_library.lo `test -f 'callout_params_library.cc' || echo '$(srcdir)/'`callout_params_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libpcl_la-callout_params_library.Tpo $(DEPDIR)/libpcl_la-callout_params_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_params_library.cc' object='libpcl_la-callout_params_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpcl_la_CPPFLAGS) $(CPPFLAGS) $(libpcl_la_CXXFLAGS) $(CXXFLAGS) -c -o libpcl_la-callout_params_library.lo `test -f 'callout_params_library.cc' || echo '$(srcdir)/'`callout_params_library.cc
+
+libucl_la-unload_callout_library.lo: unload_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libucl_la_CPPFLAGS) $(CPPFLAGS) $(libucl_la_CXXFLAGS) $(CXXFLAGS) -MT libucl_la-unload_callout_library.lo -MD -MP -MF $(DEPDIR)/libucl_la-unload_callout_library.Tpo -c -o libucl_la-unload_callout_library.lo `test -f 'unload_callout_library.cc' || echo '$(srcdir)/'`unload_callout_library.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libucl_la-unload_callout_library.Tpo $(DEPDIR)/libucl_la-unload_callout_library.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='unload_callout_library.cc' object='libucl_la-unload_callout_library.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libucl_la_CPPFLAGS) $(CPPFLAGS) $(libucl_la_CXXFLAGS) $(CXXFLAGS) -c -o libucl_la-unload_callout_library.lo `test -f 'unload_callout_library.cc' || echo '$(srcdir)/'`unload_callout_library.cc
+
+run_unittests-run_unittests.o: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+
+run_unittests-run_unittests.obj: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/run_unittests-run_unittests.Tpo -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-run_unittests.Tpo $(DEPDIR)/run_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='run_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+
+run_unittests-callout_handle_unittest.o: callout_handle_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-callout_handle_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-callout_handle_unittest.Tpo -c -o run_unittests-callout_handle_unittest.o `test -f 'callout_handle_unittest.cc' || echo '$(srcdir)/'`callout_handle_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-callout_handle_unittest.Tpo $(DEPDIR)/run_unittests-callout_handle_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_handle_unittest.cc' object='run_unittests-callout_handle_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-callout_handle_unittest.o `test -f 'callout_handle_unittest.cc' || echo '$(srcdir)/'`callout_handle_unittest.cc
+
+run_unittests-callout_handle_unittest.obj: callout_handle_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-callout_handle_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-callout_handle_unittest.Tpo -c -o run_unittests-callout_handle_unittest.obj `if test -f 'callout_handle_unittest.cc'; then $(CYGPATH_W) 'callout_handle_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/callout_handle_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-callout_handle_unittest.Tpo $(DEPDIR)/run_unittests-callout_handle_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_handle_unittest.cc' object='run_unittests-callout_handle_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-callout_handle_unittest.obj `if test -f 'callout_handle_unittest.cc'; then $(CYGPATH_W) 'callout_handle_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/callout_handle_unittest.cc'; fi`
+
+run_unittests-callout_handle_associate_unittest.o: callout_handle_associate_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-callout_handle_associate_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-callout_handle_associate_unittest.Tpo -c -o run_unittests-callout_handle_associate_unittest.o `test -f 'callout_handle_associate_unittest.cc' || echo '$(srcdir)/'`callout_handle_associate_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-callout_handle_associate_unittest.Tpo $(DEPDIR)/run_unittests-callout_handle_associate_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_handle_associate_unittest.cc' object='run_unittests-callout_handle_associate_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-callout_handle_associate_unittest.o `test -f 'callout_handle_associate_unittest.cc' || echo '$(srcdir)/'`callout_handle_associate_unittest.cc
+
+run_unittests-callout_handle_associate_unittest.obj: callout_handle_associate_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-callout_handle_associate_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-callout_handle_associate_unittest.Tpo -c -o run_unittests-callout_handle_associate_unittest.obj `if test -f 'callout_handle_associate_unittest.cc'; then $(CYGPATH_W) 'callout_handle_associate_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/callout_handle_associate_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-callout_handle_associate_unittest.Tpo $(DEPDIR)/run_unittests-callout_handle_associate_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_handle_associate_unittest.cc' object='run_unittests-callout_handle_associate_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-callout_handle_associate_unittest.obj `if test -f 'callout_handle_associate_unittest.cc'; then $(CYGPATH_W) 'callout_handle_associate_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/callout_handle_associate_unittest.cc'; fi`
+
+run_unittests-callout_manager_unittest.o: callout_manager_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-callout_manager_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-callout_manager_unittest.Tpo -c -o run_unittests-callout_manager_unittest.o `test -f 'callout_manager_unittest.cc' || echo '$(srcdir)/'`callout_manager_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-callout_manager_unittest.Tpo $(DEPDIR)/run_unittests-callout_manager_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_manager_unittest.cc' object='run_unittests-callout_manager_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-callout_manager_unittest.o `test -f 'callout_manager_unittest.cc' || echo '$(srcdir)/'`callout_manager_unittest.cc
+
+run_unittests-callout_manager_unittest.obj: callout_manager_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-callout_manager_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-callout_manager_unittest.Tpo -c -o run_unittests-callout_manager_unittest.obj `if test -f 'callout_manager_unittest.cc'; then $(CYGPATH_W) 'callout_manager_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/callout_manager_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-callout_manager_unittest.Tpo $(DEPDIR)/run_unittests-callout_manager_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='callout_manager_unittest.cc' object='run_unittests-callout_manager_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-callout_manager_unittest.obj `if test -f 'callout_manager_unittest.cc'; then $(CYGPATH_W) 'callout_manager_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/callout_manager_unittest.cc'; fi`
+
+run_unittests-handles_unittest.o: handles_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-handles_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-handles_unittest.Tpo -c -o run_unittests-handles_unittest.o `test -f 'handles_unittest.cc' || echo '$(srcdir)/'`handles_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-handles_unittest.Tpo $(DEPDIR)/run_unittests-handles_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='handles_unittest.cc' object='run_unittests-handles_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-handles_unittest.o `test -f 'handles_unittest.cc' || echo '$(srcdir)/'`handles_unittest.cc
+
+run_unittests-handles_unittest.obj: handles_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-handles_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-handles_unittest.Tpo -c -o run_unittests-handles_unittest.obj `if test -f 'handles_unittest.cc'; then $(CYGPATH_W) 'handles_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/handles_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-handles_unittest.Tpo $(DEPDIR)/run_unittests-handles_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='handles_unittest.cc' object='run_unittests-handles_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-handles_unittest.obj `if test -f 'handles_unittest.cc'; then $(CYGPATH_W) 'handles_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/handles_unittest.cc'; fi`
+
+run_unittests-hooks_manager_unittest.o: hooks_manager_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-hooks_manager_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-hooks_manager_unittest.Tpo -c -o run_unittests-hooks_manager_unittest.o `test -f 'hooks_manager_unittest.cc' || echo '$(srcdir)/'`hooks_manager_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-hooks_manager_unittest.Tpo $(DEPDIR)/run_unittests-hooks_manager_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hooks_manager_unittest.cc' object='run_unittests-hooks_manager_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-hooks_manager_unittest.o `test -f 'hooks_manager_unittest.cc' || echo '$(srcdir)/'`hooks_manager_unittest.cc
+
+run_unittests-hooks_manager_unittest.obj: hooks_manager_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-hooks_manager_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-hooks_manager_unittest.Tpo -c -o run_unittests-hooks_manager_unittest.obj `if test -f 'hooks_manager_unittest.cc'; then $(CYGPATH_W) 'hooks_manager_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/hooks_manager_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-hooks_manager_unittest.Tpo $(DEPDIR)/run_unittests-hooks_manager_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='hooks_manager_unittest.cc' object='run_unittests-hooks_manager_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-hooks_manager_unittest.obj `if test -f 'hooks_manager_unittest.cc'; then $(CYGPATH_W) 'hooks_manager_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/hooks_manager_unittest.cc'; fi`
+
+run_unittests-library_manager_collection_unittest.o: library_manager_collection_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-library_manager_collection_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-library_manager_collection_unittest.Tpo -c -o run_unittests-library_manager_collection_unittest.o `test -f 'library_manager_collection_unittest.cc' || echo '$(srcdir)/'`library_manager_collection_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-library_manager_collection_unittest.Tpo $(DEPDIR)/run_unittests-library_manager_collection_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='library_manager_collection_unittest.cc' object='run_unittests-library_manager_collection_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-library_manager_collection_unittest.o `test -f 'library_manager_collection_unittest.cc' || echo '$(srcdir)/'`library_manager_collection_unittest.cc
+
+run_unittests-library_manager_collection_unittest.obj: library_manager_collection_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-library_manager_collection_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-library_manager_collection_unittest.Tpo -c -o run_unittests-library_manager_collection_unittest.obj `if test -f 'library_manager_collection_unittest.cc'; then $(CYGPATH_W) 'library_manager_collection_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/library_manager_collection_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-library_manager_collection_unittest.Tpo $(DEPDIR)/run_unittests-library_manager_collection_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='library_manager_collection_unittest.cc' object='run_unittests-library_manager_collection_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-library_manager_collection_unittest.obj `if test -f 'library_manager_collection_unittest.cc'; then $(CYGPATH_W) 'library_manager_collection_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/library_manager_collection_unittest.cc'; fi`
+
+run_unittests-library_manager_unittest.o: library_manager_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-library_manager_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-library_manager_unittest.Tpo -c -o run_unittests-library_manager_unittest.o `test -f 'library_manager_unittest.cc' || echo '$(srcdir)/'`library_manager_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-library_manager_unittest.Tpo $(DEPDIR)/run_unittests-library_manager_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='library_manager_unittest.cc' object='run_unittests-library_manager_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-library_manager_unittest.o `test -f 'library_manager_unittest.cc' || echo '$(srcdir)/'`library_manager_unittest.cc
+
+run_unittests-library_manager_unittest.obj: library_manager_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-library_manager_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-library_manager_unittest.Tpo -c -o run_unittests-library_manager_unittest.obj `if test -f 'library_manager_unittest.cc'; then $(CYGPATH_W) 'library_manager_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/library_manager_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-library_manager_unittest.Tpo $(DEPDIR)/run_unittests-library_manager_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='library_manager_unittest.cc' object='run_unittests-library_manager_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-library_manager_unittest.obj `if test -f 'library_manager_unittest.cc'; then $(CYGPATH_W) 'library_manager_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/library_manager_unittest.cc'; fi`
+
+run_unittests-parking_lots_unittest.o: parking_lots_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-parking_lots_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-parking_lots_unittest.Tpo -c -o run_unittests-parking_lots_unittest.o `test -f 'parking_lots_unittest.cc' || echo '$(srcdir)/'`parking_lots_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-parking_lots_unittest.Tpo $(DEPDIR)/run_unittests-parking_lots_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parking_lots_unittest.cc' object='run_unittests-parking_lots_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-parking_lots_unittest.o `test -f 'parking_lots_unittest.cc' || echo '$(srcdir)/'`parking_lots_unittest.cc
+
+run_unittests-parking_lots_unittest.obj: parking_lots_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-parking_lots_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-parking_lots_unittest.Tpo -c -o run_unittests-parking_lots_unittest.obj `if test -f 'parking_lots_unittest.cc'; then $(CYGPATH_W) 'parking_lots_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/parking_lots_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-parking_lots_unittest.Tpo $(DEPDIR)/run_unittests-parking_lots_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parking_lots_unittest.cc' object='run_unittests-parking_lots_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-parking_lots_unittest.obj `if test -f 'parking_lots_unittest.cc'; then $(CYGPATH_W) 'parking_lots_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/parking_lots_unittest.cc'; fi`
+
+run_unittests-server_hooks_unittest.o: server_hooks_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-server_hooks_unittest.o -MD -MP -MF $(DEPDIR)/run_unittests-server_hooks_unittest.Tpo -c -o run_unittests-server_hooks_unittest.o `test -f 'server_hooks_unittest.cc' || echo '$(srcdir)/'`server_hooks_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-server_hooks_unittest.Tpo $(DEPDIR)/run_unittests-server_hooks_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='server_hooks_unittest.cc' object='run_unittests-server_hooks_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-server_hooks_unittest.o `test -f 'server_hooks_unittest.cc' || echo '$(srcdir)/'`server_hooks_unittest.cc
+
+run_unittests-server_hooks_unittest.obj: server_hooks_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -MT run_unittests-server_hooks_unittest.obj -MD -MP -MF $(DEPDIR)/run_unittests-server_hooks_unittest.Tpo -c -o run_unittests-server_hooks_unittest.obj `if test -f 'server_hooks_unittest.cc'; then $(CYGPATH_W) 'server_hooks_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/server_hooks_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/run_unittests-server_hooks_unittest.Tpo $(DEPDIR)/run_unittests-server_hooks_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='server_hooks_unittest.cc' object='run_unittests-server_hooks_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(run_unittests_CPPFLAGS) $(CPPFLAGS) $(run_unittests_CXXFLAGS) $(CXXFLAGS) -c -o run_unittests-server_hooks_unittest.obj `if test -f 'server_hooks_unittest.cc'; then $(CYGPATH_W) 'server_hooks_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/server_hooks_unittest.cc'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ fi; \
+ echo "$${col}$$dashes$${std}"; \
+ echo "$${col}$$banner$${std}"; \
+ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+ test -z "$$report" || echo "$${col}$$report$${std}"; \
+ echo "$${col}$$dashes$${std}"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile $(PROGRAMS) $(LTLIBRARIES)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/libacl_la-async_callout_library.Plo
+ -rm -f ./$(DEPDIR)/libbcl_la-basic_callout_library.Plo
+ -rm -f ./$(DEPDIR)/libfcl_la-full_callout_library.Plo
+ -rm -f ./$(DEPDIR)/libfxl_la-framework_exception_library.Plo
+ -rm -f ./$(DEPDIR)/libivl_la-incorrect_version_library.Plo
+ -rm -f ./$(DEPDIR)/liblcl_la-load_callout_library.Plo
+ -rm -f ./$(DEPDIR)/liblecl_la-load_error_callout_library.Plo
+ -rm -f ./$(DEPDIR)/libnvl_la-no_version_library.Plo
+ -rm -f ./$(DEPDIR)/libpcl_la-callout_params_library.Plo
+ -rm -f ./$(DEPDIR)/libucl_la-unload_callout_library.Plo
+ -rm -f ./$(DEPDIR)/run_unittests-callout_handle_associate_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-callout_handle_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-callout_manager_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-handles_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-hooks_manager_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-library_manager_collection_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-library_manager_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-parking_lots_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-server_hooks_unittest.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/libacl_la-async_callout_library.Plo
+ -rm -f ./$(DEPDIR)/libbcl_la-basic_callout_library.Plo
+ -rm -f ./$(DEPDIR)/libfcl_la-full_callout_library.Plo
+ -rm -f ./$(DEPDIR)/libfxl_la-framework_exception_library.Plo
+ -rm -f ./$(DEPDIR)/libivl_la-incorrect_version_library.Plo
+ -rm -f ./$(DEPDIR)/liblcl_la-load_callout_library.Plo
+ -rm -f ./$(DEPDIR)/liblecl_la-load_error_callout_library.Plo
+ -rm -f ./$(DEPDIR)/libnvl_la-no_version_library.Plo
+ -rm -f ./$(DEPDIR)/libpcl_la-callout_params_library.Plo
+ -rm -f ./$(DEPDIR)/libucl_la-unload_callout_library.Plo
+ -rm -f ./$(DEPDIR)/run_unittests-callout_handle_associate_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-callout_handle_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-callout_manager_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-handles_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-hooks_manager_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-library_manager_collection_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-library_manager_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-parking_lots_unittest.Po
+ -rm -f ./$(DEPDIR)/run_unittests-run_unittests.Po
+ -rm -f ./$(DEPDIR)/run_unittests-server_hooks_unittest.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-TESTS check-am clean clean-generic \
+ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/lib/hooks/tests/async_callout_library.cc b/src/lib/hooks/tests/async_callout_library.cc
new file mode 100644
index 0000000..5f40e52
--- /dev/null
+++ b/src/lib/hooks/tests/async_callout_library.cc
@@ -0,0 +1,153 @@
+// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief Async callout library
+///
+/// This is source of a test library for testing a "parking" feature, i.e.
+/// the callouts can schedule asynchronous operation and indicate that the
+/// packet should be parked until the asynchronous operation completes and
+/// the hooks library indicates that packet processing should be resumed.
+
+#include <config.h>
+#include <hooks/hooks.h>
+#include <hooks/parking_lots.h>
+#include <log/logger.h>
+#include <log/macros.h>
+#include <log/message_initializer.h>
+#include <algorithm>
+#include <functional>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace isc::hooks;
+using namespace isc::log;
+using namespace std;
+
+namespace {
+
+/// @brief Logger used by the library.
+isc::log::Logger logger("acl");
+
+/// @brief Log messages.
+const char* log_messages[] = {
+ "ACL_LOAD_START", "async callout load %1",
+ "ACL_LOAD_END", "async callout load end",
+ "ACL_LOAD_END", "duplicate of async callout load end",
+ NULL
+};
+
+/// @brief Initializer for log messages.
+const MessageInitializer message_initializer(log_messages);
+
+/// @brief Simple callback which unparks parked object.
+///
+/// @param parking_lot parking lot where the object is parked.
+/// @param parked_object parked object.
+void unpark(ParkingLotHandlePtr parking_lot, const std::string& parked_object) {
+ parking_lot->unpark(parked_object);
+}
+
+} // end of anonymous namespace
+
+extern "C" {
+
+/// @brief Callout scheduling object parking and providing function to unpark
+/// it.
+///
+/// This callout is crafted to test the following scenario. The callout returns
+/// status "park" to indicate that the packet should be parked. The callout
+/// performs asynchronous operation and indicates that the packet should be
+/// unparked when this operation completes. Unparking the packet triggers a
+/// function associated with the parked packet, e.g. a function which continues
+/// processing of this packet.
+///
+/// This test callout "parks" a string object instead of a packet. It assumes
+/// that there might be multiple callouts installed on this hook point, which
+/// all trigger asynchronous operation. The object must be unparked when the
+/// last asynchronous operation completes. Therefore, it calls the @c reference
+/// function on the parking lot object to increase the reference count. The
+/// object remains parked as long as the reference counter is greater than
+/// 0.
+///
+/// The callout returns 1 or more pointers to the functions which should be
+/// called by the unit tests to simulate completion of the asynchronous tasks.
+/// When the test calls those functions, @c unpark function is called, which
+/// decreases reference count on the parked object, or actually unparks the
+/// object when the reference count reaches 0.
+///
+/// @param handle Reference to callout handle used to set/get arguments.
+int
+hookpt_one(CalloutHandle& handle) {
+ // Using a string as "parked" object.
+ std::string parked_object;
+ handle.getArgument("parked_object", parked_object);
+
+ // Retrieve the parking lot handle for this hook point. It allows for
+ // increasing a reference count on the parked object and also for
+ // scheduling packet unparking.
+ ParkingLotHandlePtr parking_lot = handle.getParkingLotHandlePtr();
+
+ // Increase the reference count to indicate that this callout needs the
+ // object to remain parked until the asynchronous operation completes.
+ // Otherwise, other callouts could potentially call unpark and cause the
+ // packet processing to continue before the asynchronous operation
+ // completes.
+ parking_lot->reference(parked_object);
+
+ // Create pointer to the function that the test should call to simulate
+ // completion of the asynchronous operation scheduled by this callout.
+ std::function<void()> unpark_trigger_func =
+ std::bind(unpark, parking_lot, parked_object);
+
+ // Every callout (if multiple callouts installed on this hook point) should
+ // return the function pointer under unique name. The base name is
+ // "unpark_trigger" and the callouts append consecutive numbers to this
+ // base name, e.g. "unpark_trigger1", "unpark_trigger2" etc.
+
+ std::string fun_name;
+ std::vector<std::string> args = handle.getArgumentNames();
+ unsigned i = 1;
+ do {
+ std::ostringstream candidate_name;
+ candidate_name << "unpark_trigger" << i;
+ if (std::find(args.begin(), args.end(), candidate_name.str()) ==
+ args.end()) {
+ fun_name = candidate_name.str();
+
+ } else {
+ ++i;
+ }
+ } while (fun_name.empty());
+
+ handle.setArgument(fun_name, unpark_trigger_func);
+
+ handle.setStatus(CalloutHandle::NEXT_STEP_PARK);
+
+ return (0);
+}
+
+// Framework functions.
+
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+// load() initializes the user library if the main image was statically linked.
+int
+load(isc::hooks::LibraryHandle&) {
+#ifdef USE_STATIC_LINK
+ hooksStaticLinkInit();
+#endif
+ LOG_INFO(logger, "ACL_LOAD_START").arg("argument");
+ LOG_INFO(logger, "ACL_LOAD_END");
+ return (0);
+}
+
+}
+
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..bd80555
--- /dev/null
+++ b/src/lib/hooks/tests/basic_callout_library.cc
@@ -0,0 +1,145 @@
+// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief 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".
+///
+/// - The logger instance is created and some log messages are defined. Some
+/// log messages are duplicated purposely, to check that the logger handles
+/// the duplicates correctly.
+
+#include <config.h>
+#include <hooks/hooks.h>
+#include <fstream>
+#include <log/logger.h>
+#include <log/macros.h>
+#include <log/message_initializer.h>
+
+using namespace isc::hooks;
+using namespace isc::log;
+using namespace std;
+
+namespace {
+
+/// @brief Logger used by the library.
+isc::log::Logger logger("bcl");
+
+/// @brief Log messages.
+const char* log_messages[] = {
+ "BCL_LOAD_START", "basic callout load %1",
+ "BCL_LOAD_END", "basic callout load end",
+ "BCL_LOAD_END", "duplicate of basic callout load end",
+ NULL
+};
+
+/// @brief Initializer for log messages.
+const MessageInitializer message_initializer(log_messages);
+
+}
+
+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.
+
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+// load() initializes the user library if the main image was statically linked.
+int
+load(isc::hooks::LibraryHandle&) {
+#ifdef USE_STATIC_LINK
+ hooksStaticLinkInit();
+#endif
+ LOG_INFO(logger, "BCL_LOAD_START").arg("argument");
+ LOG_INFO(logger, "BCL_LOAD_END");
+ return (0);
+}
+
+}
+
diff --git a/src/lib/hooks/tests/callout_handle_associate_unittest.cc b/src/lib/hooks/tests/callout_handle_associate_unittest.cc
new file mode 100644
index 0000000..cf09388
--- /dev/null
+++ b/src/lib/hooks/tests/callout_handle_associate_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_handle_associate.h>
+#include <gtest/gtest.h>
+
+using namespace isc::hooks;
+
+namespace {
+
+// This test verifies that the callout handle can be created and
+// retrieved from the CalloutHandleAssociate.
+TEST(CalloutHandleAssociate, getCalloutHandle) {
+ CalloutHandleAssociate associate;
+ // The handle should be initialized and returned.
+ CalloutHandlePtr callout_handle = associate.getCalloutHandle();
+ ASSERT_TRUE(callout_handle);
+
+ // When calling the second time, the same handle should be returned.
+ CalloutHandlePtr callout_handle2 = associate.getCalloutHandle();
+ EXPECT_TRUE(callout_handle == callout_handle2);
+
+ // A different associate should produce a different handle.
+ CalloutHandleAssociate associate2;
+ callout_handle2 = associate2.getCalloutHandle();
+ EXPECT_FALSE(callout_handle == callout_handle2);
+}
+
+} // end of anonymous namespace
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..bb20e0c
--- /dev/null
+++ b/src/lib/hooks/tests/callout_handle_unittest.cc
@@ -0,0 +1,384 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#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 = &aleph;
+ const Beta* pcb = &beth;
+
+ // 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 "status" field.
+TEST_F(CalloutHandleTest, StatusField) {
+ CalloutHandle handle(getCalloutManager());
+
+ // Should be continue on construction.
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, handle.getStatus());
+
+ handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_SKIP, handle.getStatus());
+
+ handle.setStatus(CalloutHandle::NEXT_STEP_DROP);
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_DROP, handle.getStatus());
+
+ handle.setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, handle.getStatus());
+}
+
+// Tests that ScopedCalloutHandleState object resets CalloutHandle state
+// during construction and destruction.
+TEST_F(CalloutHandleTest, scopedState) {
+ // Create pointer to the handle to be wrapped.
+ CalloutHandlePtr handle(new CalloutHandle(getCalloutManager()));
+
+ // Set two arguments and the non-default status.
+ int one = 1;
+ int two = 2;
+ int three = 3;
+ handle->setArgument("one", one);
+ handle->setArgument("two", two);
+ handle->setContext("three", three);
+ handle->setStatus(CalloutHandle::NEXT_STEP_DROP);
+
+
+ int value = 0;
+ EXPECT_NO_THROW(handle->getArgument("one", value));
+ EXPECT_NO_THROW(handle->getArgument("two", value));
+ EXPECT_NO_THROW(handle->getContext("three", value));
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_DROP, handle->getStatus());
+
+ {
+ // Wrap the callout handle with the scoped state object, which should
+ // reset the state of the handle.
+ ScopedCalloutHandleState scoped_state(handle);
+
+ // When state is reset, all arguments should be removed and the
+ // default status should be set.
+ EXPECT_THROW(handle->getArgument("one", value), NoSuchArgument);
+ EXPECT_THROW(handle->getArgument("two", value), NoSuchArgument);
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, handle->getStatus());
+
+ // Context should be intact.
+ ASSERT_NO_THROW(handle->getContext("three", value));
+ EXPECT_EQ(three, value);
+
+ // Set the arguments and status again prior to the destruction of
+ // the wrapper.
+ handle->setArgument("one", one);
+ handle->setArgument("two", two);
+ handle->setStatus(CalloutHandle::NEXT_STEP_DROP);
+
+ EXPECT_NO_THROW(handle->getArgument("one", value));
+ EXPECT_NO_THROW(handle->getArgument("two", value));
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_DROP, handle->getStatus());
+ }
+
+ // Arguments should be gone again and the status should be set to
+ // a default value.
+ EXPECT_THROW(handle->getArgument("one", value), NoSuchArgument);
+ EXPECT_THROW(handle->getArgument("two", value), NoSuchArgument);
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, handle->getStatus());
+
+ // Context should be intact.
+ ASSERT_NO_THROW(handle->getContext("three", value));
+ EXPECT_EQ(three, value);
+}
+
+// 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..b487a36
--- /dev/null
+++ b/src/lib/hooks/tests/callout_manager_unittest.cc
@@ -0,0 +1,877 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#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 one 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 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) {
+ EXPECT_NO_THROW(getCalloutManager()->registerCallout("alpha", callout_one, 0));
+ EXPECT_THROW(getCalloutManager()->registerCallout("unknown", callout_one, 0),
+ 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()->registerCallout("alpha", callout_one, 0);
+ getCalloutManager()->registerCallout("beta", callout_two, 1);
+
+ // 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()->registerCallout("alpha", callout_three, 2);
+ getCalloutManager()->registerCallout("alpha", callout_four, 2);
+ getCalloutManager()->registerCallout("alpha", callout_five, 3);
+
+ // 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()->registerCallout("alpha", callout_six, 2);
+ 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 library index 2.
+ getCalloutManager()->registerCallout("alpha", callout_seven, 1);
+ 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()->registerCallout("alpha", callout_one, 0);
+ getCalloutManager()->registerCallout("alpha", callout_two, 1);
+ getCalloutManager()->registerCallout("beta", callout_two, 1);
+ getCalloutManager()->registerCallout("alpha", callout_three, 3);
+ getCalloutManager()->registerCallout("delta", callout_four, 3);
+
+ // 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()->registerCallout("alpha", callout_one, 1);
+ getCalloutManager()->registerCallout("alpha", callout_two, 1);
+ getCalloutManager()->registerCallout("alpha", callout_three, 2);
+ getCalloutManager()->registerCallout("alpha", callout_four, 3);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1234, callout_value_);
+
+ // Do a random selection of callouts on hook "beta".
+ callout_value_ = 0;
+ getCalloutManager()->registerCallout("beta", callout_one, 0);
+ getCalloutManager()->registerCallout("beta", callout_three, 0);
+ getCalloutManager()->registerCallout("beta", callout_two, 1);
+ getCalloutManager()->registerCallout("beta", callout_four, 3);
+ 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()->registerCallout("alpha", callout_one_error, 0);
+ getCalloutManager()->registerCallout("alpha", callout_two, 1);
+ getCalloutManager()->registerCallout("alpha", callout_three, 2);
+ getCalloutManager()->registerCallout("alpha", callout_four, 3);
+ 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()->registerCallout("beta", callout_one, 0);
+ getCalloutManager()->registerCallout("beta", callout_one_error, 0);
+ getCalloutManager()->registerCallout("beta", callout_two, 1);
+ getCalloutManager()->registerCallout("beta", callout_two, 1);
+ getCalloutManager()->registerCallout("beta", callout_three, 1);
+ getCalloutManager()->registerCallout("beta", callout_three, 1);
+ getCalloutManager()->registerCallout("beta", callout_four, 3);
+ getCalloutManager()->registerCallout("beta", callout_four, 3);
+ 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()->registerCallout("gamma", callout_one, 0);
+ getCalloutManager()->registerCallout("gamma", callout_one, 0);
+ getCalloutManager()->registerCallout("gamma", callout_two, 1);
+ getCalloutManager()->registerCallout("gamma", callout_two, 1);
+ getCalloutManager()->registerCallout("gamma", callout_four_error, 3);
+ getCalloutManager()->registerCallout("gamma", callout_four, 3);
+ getCalloutManager()->callCallouts(gamma_index_, getCalloutHandle());
+ EXPECT_EQ(112244, callout_value_);
+
+ // The last callout on a hook returns an error.
+ callout_value_ = 0;
+ getCalloutManager()->registerCallout("delta", callout_one, 0);
+ getCalloutManager()->registerCallout("delta", callout_one, 0);
+ getCalloutManager()->registerCallout("delta", callout_two, 1);
+ getCalloutManager()->registerCallout("delta", callout_two, 1);
+ getCalloutManager()->registerCallout("delta", callout_three, 2);
+ getCalloutManager()->registerCallout("delta", callout_three, 2);
+ getCalloutManager()->registerCallout("delta", callout_four, 3);
+ getCalloutManager()->registerCallout("delta", callout_four_error, 3);
+ 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()->registerCallout("alpha", callout_two, 0);
+ 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().
+ EXPECT_TRUE(getCalloutManager()->calloutsPresent(alpha_index_));
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two, 0));
+ 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()->registerCallout("alpha", callout_one, 0);
+ getCalloutManager()->registerCallout("alpha", callout_two, 0);
+ getCalloutManager()->registerCallout("alpha", callout_three, 0);
+ getCalloutManager()->registerCallout("alpha", callout_four, 0);
+ 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().
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two, 0));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(134, callout_value_);
+
+ // Try removing it again.
+ EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two, 0));
+ 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()->registerCallout("alpha", callout_one, 0);
+ getCalloutManager()->registerCallout("alpha", callout_two, 0);
+ getCalloutManager()->registerCallout("alpha", callout_one, 0);
+ getCalloutManager()->registerCallout("alpha", callout_two, 0);
+ getCalloutManager()->registerCallout("alpha", callout_three, 0);
+ getCalloutManager()->registerCallout("alpha", callout_four, 0);
+ getCalloutManager()->registerCallout("alpha", callout_three, 0);
+ getCalloutManager()->registerCallout("alpha", callout_four, 0);
+ 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().
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two, 0));
+ 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.
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_four, 0));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1133, callout_value_);
+
+ // ... and from the start.
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_one, 0));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(33, callout_value_);
+
+ // ... and the remaining callouts.
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_three, 0));
+ 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()->registerCallout("alpha", callout_one, 0);
+ getCalloutManager()->registerCallout("alpha", callout_two, 0);
+ getCalloutManager()->registerCallout("alpha", callout_three, 1);
+ getCalloutManager()->registerCallout("alpha", callout_four, 1);
+ getCalloutManager()->registerCallout("alpha", callout_five, 2);
+ getCalloutManager()->registerCallout("alpha", callout_two, 2);
+ 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.
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two, 0));
+ 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()->registerCallout("alpha", callout_one, 0);
+ getCalloutManager()->registerCallout("alpha", callout_two, 0);
+ getCalloutManager()->registerCallout("alpha", callout_three, 1);
+ getCalloutManager()->registerCallout("alpha", callout_four, 1);
+ getCalloutManager()->registerCallout("alpha", callout_five, 2);
+ getCalloutManager()->registerCallout("alpha", callout_six, 2);
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(123456, callout_value_);
+
+ // Remove all callouts from library index 1.
+ EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha", 1));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1256, callout_value_);
+
+ // Remove all callouts from library index 2.
+ EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha", 2));
+ 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()->registerCallout("alpha", callout_one, 0);
+ getCalloutManager()->registerCallout("alpha", callout_two, 0);
+ getCalloutManager()->registerCallout("alpha", callout_three, 1);
+ getCalloutManager()->registerCallout("alpha", callout_four, 1);
+ getCalloutManager()->registerCallout("alpha", callout_five, 2);
+ getCalloutManager()->registerCallout("alpha", callout_two, 2);
+ 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()->registerCallout("beta", callout_five, 0);
+ getCalloutManager()->registerCallout("beta", callout_one, 0);
+ getCalloutManager()->registerCallout("beta", callout_four, 2);
+ getCalloutManager()->registerCallout("beta", callout_three, 2);
+ 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.
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("beta", callout_four, 2));
+
+ 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).
+ EXPECT_FALSE(getCalloutManager()->deregisterCallout("alpha", callout_two, 1));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(1234, callout_value_);
+
+ EXPECT_TRUE(getCalloutManager()->deregisterCallout("alpha", callout_two, 0));
+ callout_value_ = 0;
+ getCalloutManager()->callCallouts(alpha_index_, getCalloutHandle());
+ EXPECT_EQ(134, callout_value_);
+
+ // Deregister all callouts on library index 1.
+ EXPECT_TRUE(getCalloutManager()->deregisterAllCallouts("alpha", 1));
+ 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_);
+}
+
+// Test that control command handlers can be installed as callouts.
+
+TEST_F(CalloutManagerTest, LibraryHandleRegisterCommandHandler) {
+ CalloutHandle handle(getCalloutManager());
+
+ // Simulate creation of the two hook libraries. Fist library implements two
+ // handlers for the control command 'command-one'. Second library implements
+ // two control command handlers: one for the 'command-one', another one for
+ // 'command-two'. Each of the handlers for the 'command-one' must be called
+ // and they must be called in the appropriate order. Command handler for
+ // 'command-two' should also be called.
+
+ getCalloutManager()->setLibraryIndex(0);
+ getCalloutManager()->getLibraryHandle().registerCommandCallout("command-one", callout_one);
+ getCalloutManager()->getLibraryHandle().registerCommandCallout("command-one", callout_four);
+ getCalloutManager()->setLibraryIndex(1);
+ getCalloutManager()->getLibraryHandle().registerCommandCallout("command-one", callout_two);
+ getCalloutManager()->getLibraryHandle().registerCommandCallout("command-two", callout_three);
+
+ // Command handlers are installed for commands: 'command-one' and 'command-two'.
+ EXPECT_TRUE(getCalloutManager()->commandHandlersPresent("command-one"));
+ EXPECT_TRUE(getCalloutManager()->commandHandlersPresent("command-two"));
+ // There should be no handlers installed for 'command-three' and 'command-four'.
+ EXPECT_FALSE(getCalloutManager()->commandHandlersPresent("command-three"));
+ EXPECT_FALSE(getCalloutManager()->commandHandlersPresent("command-four"));
+
+ // Call handlers for 'command-one'. There should be three handlers called in
+ // the following order: 1, 4, 2.
+ callout_value_ = 0;
+ ASSERT_NO_THROW(getCalloutManager()->callCommandHandlers("command-one", handle));
+ EXPECT_EQ(142, callout_value_);
+
+ // There should be one handler invoked for the 'command-two'. This handler has
+ // index of 3.
+ callout_value_ = 0;
+ ASSERT_NO_THROW(getCalloutManager()->callCommandHandlers("command-two", handle));
+ EXPECT_EQ(3, callout_value_);
+
+ // An attempt to call handlers for the commands for which no hook points
+ // were created should result in exception.
+ EXPECT_THROW(getCalloutManager()->callCommandHandlers("command-three", handle),
+ NoSuchHook);
+ EXPECT_THROW(getCalloutManager()->callCommandHandlers("command-four", handle),
+ NoSuchHook);
+}
+
+// This test checks if the CalloutManager can adjust its own hook_vector_ size.
+TEST_F(CalloutManagerTest, VectorSize) {
+
+ size_t s = getCalloutManager()->getHookLibsVectorSize();
+
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+
+ EXPECT_NO_THROW(hooks.registerHook("a_new_one"));
+
+ // Now load a callout. Name of the hook point the new callout is installed
+ // on doesn't matter. CM should do sanity checks and adjust anyway.
+ getCalloutManager()->getPostLibraryHandle().registerCallout("alpha",
+ callout_four);
+
+ // The vector size should have been increased by one, because there's
+ // one new hook point now.
+ EXPECT_EQ(s + 1, getCalloutManager()->getHookLibsVectorSize());
+}
+
+
+// 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/callout_params_library.cc b/src/lib/hooks/tests/callout_params_library.cc
new file mode 100644
index 0000000..d35fc00
--- /dev/null
+++ b/src/lib/hooks/tests/callout_params_library.cc
@@ -0,0 +1,129 @@
+// Copyright (C) 2016-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief Callout Library
+///
+/// This is the source of a test library for the DHCP parser tests that
+/// specify parameters. It will attempt to obtain its own parameters.
+
+#include <config.h>
+#include <hooks/hooks.h>
+#include <iostream>
+
+using namespace std;
+using namespace isc::hooks;
+using namespace isc::data;
+
+extern "C" {
+
+// Framework functions
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+/// @brief This method will be called when the hook library is loaded
+///
+/// While its primary usage is for unit-testing, it also doubles as an
+/// illustration referred to from Hooks Developer's Guide. As such, please
+/// keep it simple, tidy and try to avoid referencing unnecessary code.
+/// Parts of it can be used as copy-paste examples.
+///
+/// @param handle passed by the hooks framework
+/// @return 0 if load was successful, non-zero for errors
+int load(LibraryHandle& handle) {
+ ConstElementPtr elems = handle.getParameters();
+ ConstElementPtr string_elem = handle.getParameter("svalue");
+ ConstElementPtr int_elem = handle.getParameter("ivalue");
+ ConstElementPtr bool_elem = handle.getParameter("bvalue");
+ ConstElementPtr nonexistent = handle.getParameter("nonexistent");
+ vector<string> names = handle.getParameterNames();
+
+ // String handling example.
+ if (!string_elem) {
+ // Parameter was not specified at all.
+ return (1);
+ }
+
+ if (string_elem->getType() != Element::string) {
+ // Parameter is specified, but it's not a string.
+ return (2);
+ }
+
+ string str = string_elem->stringValue();
+ if (str != "string value") {
+ // Parameter is specified, is a string, but has unexpected value.
+ //
+ // This library is used for testing, so it expects exact value of the
+ // parameter. Normal library would likely use whatever value user
+ // specified.
+ return (3);
+ }
+
+ // Integer handling example
+ if (!int_elem) {
+ // Parameter was not specified at all.
+ return (4);
+ }
+
+ if (int_elem->getType() != Element::integer) {
+ // Parameter is specified, but it's not an integer.
+ return (5);
+ }
+
+ int int_value = int_elem->intValue();
+ if (int_value != 42) {
+ // Parameter specified, is an integer, but has a value different than
+ // expected.
+ return (6);
+ }
+
+ // Boolean handling example
+ if (!bool_elem) {
+ // Parameter was not specified at all.
+ return (7);
+ }
+
+ if (bool_elem->getType() != Element::boolean) {
+ // Parameter is specified, but it's not a boolean.
+ return (8);
+ }
+
+ bool flag = bool_elem->boolValue();
+ if (flag != true) {
+ // Parameter specified, is a boolean, but has a value different than
+ // expected.
+ return (9);
+ }
+
+ // Check names. As a side effect of what maps using strings are
+ // implemented names are sorted in alphabetical order.
+ if ((names.size() != 3) || (names[0] != "bvalue") ||
+ (names[1] != "ivalue") || (names[2] != "svalue")) {
+ // Expect 3 names: bvalue, ivalue, svalue.
+ return (10);
+ }
+
+ // Check elems map.
+ if (!elems) {
+ return (11);
+ }
+ string expected_str = "{ "
+ "\"bvalue\": true, "
+ "\"ivalue\": 42, "
+ "\"svalue\": \"string value\""
+ " }";
+ if (expected_str != elems->str()) {
+ return (12);
+ }
+
+ // All validation steps were successful. The library has all the parameters
+ // it needs, so we should report a success.
+ return (0);
+}
+
+}
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..aa34bcf
--- /dev/null
+++ b/src/lib/hooks/tests/common_test_class.h
@@ -0,0 +1,174 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef 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 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 performs 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 wrong 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;
+ }
+
+ /// @brief Call command handlers test.
+ ///
+ /// This test is similar to @c executeCallCallouts but it uses
+ /// @ref CalloutManager::callCommandHandlers to execute the command
+ /// handlers for the following commands: 'command-one' and 'command-two'.
+ ///
+ /// @param manager CalloutManager to use for the test
+ /// @param r1..r2, d1..d2 Data (dN) and expected results (rN).
+ void executeCallCommandHandlers(
+ const boost::shared_ptr<isc::hooks::CalloutManager>& manager,
+ int d1, int r1, int d2, int r2) {
+ static const char* COMMON_TEXT = " command handler returned the wrong 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);
+
+ // Perform the first calculation: it should assign the data to the
+ // result.
+ handle.setArgument("data_1", d1);
+ manager->callCommandHandlers("command-one", handle);
+ handle.getArgument(RESULT, result);
+ EXPECT_EQ(r1, result) << "command-one" << COMMON_TEXT;
+
+ // Perform the second calculation: it should multiply the data by 10
+ // and return in the result.
+ handle.setArgument("data_2", d2);
+ manager->callCommandHandlers("command-two", handle);
+ handle.getArgument(RESULT, result);
+ EXPECT_EQ(r2, result) << "command-two" << 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..c60c3f8
--- /dev/null
+++ b/src/lib/hooks/tests/framework_exception_library.cc
@@ -0,0 +1,41 @@
+// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief 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 <config.h>
+
+#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..58ea8c7
--- /dev/null
+++ b/src/lib/hooks/tests/full_callout_library.cc
@@ -0,0 +1,177 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief 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 four framework functions are supplied (version(), load(),
+/// unload() and multi_threading_compatible()), 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 <config.h>
+
+#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 multiplies 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);
+}
+
+// First command handler assigns data to a result.
+
+static int
+command_handler_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 command handler multiples the result by data by 10.
+
+static int
+command_handler_two(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_2", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result *= data * 10;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Framework functions
+
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+int
+load(LibraryHandle& handle) {
+ // Initialize if the main image was statically linked
+#ifdef USE_STATIC_LINK
+ hooksStaticLinkInit();
+#endif
+ // Register the non-standard functions
+ handle.registerCallout("hookpt_two", hook_nonstandard_two);
+ handle.registerCallout("hookpt_three", hook_nonstandard_three);
+
+ // Register command_handler_one as control command handler.
+ handle.registerCommandCallout("command-one", command_handler_one);
+ handle.registerCommandCallout("command-two", command_handler_two);
+
+ return (0);
+}
+
+int
+unload() {
+ // Create the marker file.
+ std::fstream marker;
+ marker.open(MARKER_FILE, std::fstream::out);
+ marker.close();
+
+ return (0);
+}
+
+int
+multi_threading_compatible() {
+ return (1);
+}
+
+};
+
diff --git a/src/lib/hooks/tests/handles_unittest.cc b/src/lib/hooks/tests/handles_unittest.cc
new file mode 100644
index 0000000..a9292d8
--- /dev/null
+++ b/src/lib/hooks/tests/handles_unittest.cc
@@ -0,0 +1,777 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#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());
+
+ getCalloutManager()->registerCallout("alpha", callout11, 0);
+ getCalloutManager()->registerCallout("beta", callout12, 0);
+ getCalloutManager()->registerCallout("gamma", callout13, 0);
+ getCalloutManager()->registerCallout("delta", print1, 0);
+
+ getCalloutManager()->registerCallout("alpha", callout21, 1);
+ getCalloutManager()->registerCallout("beta", callout22, 1);
+ getCalloutManager()->registerCallout("gamma", callout23, 1);
+ getCalloutManager()->registerCallout("delta", print2, 1);
+
+ getCalloutManager()->registerCallout("alpha", callout31, 2);
+ getCalloutManager()->registerCallout("beta", callout32, 2);
+ getCalloutManager()->registerCallout("gamma", callout33, 2);
+ getCalloutManager()->registerCallout("delta", print3, 2);
+
+ // 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()->registerCallout("alpha", callout11, 0);
+ getCalloutManager()->registerCallout("beta", callout12, 0);
+ getCalloutManager()->registerCallout("beta", printContextNames1, 0);
+ getCalloutManager()->registerCallout("gamma", callout13, 0);
+ getCalloutManager()->registerCallout("delta", print1, 0);
+
+ getCalloutManager()->registerCallout("alpha", callout21, 1);
+ getCalloutManager()->registerCallout("beta", callout22, 1);
+ getCalloutManager()->registerCallout("beta", deleteIntContextItem, 1);
+ getCalloutManager()->registerCallout("beta", printContextNames2, 1);
+ getCalloutManager()->registerCallout("gamma", callout23, 1);
+ getCalloutManager()->registerCallout("delta", print2, 1);
+
+ getCalloutManager()->registerCallout("alpha", callout31, 2);
+ getCalloutManager()->registerCallout("beta", callout32, 2);
+ getCalloutManager()->registerCallout("beta", deleteAllContextItems, 2);
+ getCalloutManager()->registerCallout("beta", printContextNames3, 2);
+ getCalloutManager()->registerCallout("gamma", callout33, 2);
+ getCalloutManager()->registerCallout("delta", print3, 2);
+
+ // 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()->registerCallout("context_create", callout11, 0);
+ getCalloutManager()->registerCallout("context_create", print1, 0);
+ getCalloutManager()->registerCallout("context_destroy", callout12, 0);
+ getCalloutManager()->registerCallout("context_destroy", print1, 0);
+
+ // 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));
+}
+
+// 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");
+ static const std::string DROP("D");
+ static const std::string PARK("P");
+
+ switch (handle.getStatus()) {
+ case CalloutHandle::NEXT_STEP_CONTINUE:
+ HandlesTest::common_string_ += NO; // skip = no
+ break;
+ case CalloutHandle::NEXT_STEP_SKIP:
+ HandlesTest::common_string_ += YES; // skip = yes
+ break;
+ case CalloutHandle::NEXT_STEP_DROP:
+ HandlesTest::common_string_ += DROP; // drop
+ break;
+ case CalloutHandle::NEXT_STEP_PARK:
+ HandlesTest::common_string_ += PARK; // park
+ break;
+ }
+ return (0);
+}
+
+int
+calloutSetSkip(CalloutHandle& handle) {
+ static_cast<void>(calloutPrintSkip(handle));
+ handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+ return (0);
+}
+
+int
+calloutClearSkip(CalloutHandle& handle) {
+ static_cast<void>(calloutPrintSkip(handle));
+ handle.setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+ return (0);
+}
+
+// Do a series of tests, returning with the skip flag set "true".
+
+TEST_F(HandlesTest, ReturnSkipSet) {
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip, 0);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 0);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 0);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 0);
+
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 1);
+
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip, 2);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 2);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 2);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 2);
+
+ CalloutHandle callout_handle(getCalloutManager());
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+ // Check result. For ease 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_EQ(CalloutHandle::NEXT_STEP_SKIP, callout_handle.getStatus());
+}
+
+// Repeat the test, returning with the skip flag clear.
+TEST_F(HandlesTest, ReturnSkipClear) {
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 0);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 0);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 0);
+
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 1);
+
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 2);
+ getCalloutManager()->registerCallout("alpha", calloutPrintSkip, 2);
+ getCalloutManager()->registerCallout("alpha", calloutSetSkip, 2);
+ getCalloutManager()->registerCallout("alpha", calloutClearSkip, 2);
+
+ CalloutHandle callout_handle(getCalloutManager());
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+
+ // Check result. For ease 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_EQ(CalloutHandle::NEXT_STEP_CONTINUE, callout_handle.getStatus());
+}
+
+// Check that the skip flag is cleared when callouts are called - even if
+// there are no callouts.
+
+TEST_F(HandlesTest, NoCalloutsSkipTest) {
+ // Note - no callouts are registered on any hook.
+ CalloutHandle callout_handle(getCalloutManager());
+
+ // Clear the skip flag and call a hook with no callouts.
+ callout_handle.setStatus(CalloutHandle::NEXT_STEP_CONTINUE);
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, callout_handle.getStatus());
+
+ // Set the skip flag and call a hook with no callouts.
+ callout_handle.setStatus(CalloutHandle::NEXT_STEP_SKIP);
+ getCalloutManager()->callCallouts(alpha_index_, callout_handle);
+ EXPECT_EQ(CalloutHandle::NEXT_STEP_CONTINUE, callout_handle.getStatus());
+}
+
+// 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
+calloutSetArgumentSkip(CalloutHandle& handle) {
+ return (calloutSetArgumentCommon(handle, "S"));
+}
+
+int
+calloutSetArgumentContinue(CalloutHandle& handle) {
+ return (calloutSetArgumentCommon(handle, "C"));
+}
+
+int
+calloutSetArgumentDrop(CalloutHandle& handle) {
+ return (calloutSetArgumentCommon(handle, "D"));
+}
+
+int
+calloutSetArgumentPark(CalloutHandle& handle) {
+ return (calloutSetArgumentCommon(handle, "P"));
+}
+
+// ... 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);
+}
+
+// This test verifies that the next step status is processed appropriately.
+// The test checks the following next step statuses: CONTINUE, SKIP, DROP.
+TEST_F(HandlesTest, CheckModifiedArgument) {
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentSkip, 0);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentContinue, 0);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentContinue, 0);
+
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentSkip, 1);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentDrop, 1);
+ getCalloutManager()->registerCallout("alpha", calloutPrintArgument, 1);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentDrop, 1);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentContinue, 1);
+
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentSkip, 2);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentContinue, 2);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentPark, 2);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentSkip, 2);
+ getCalloutManager()->registerCallout("alpha", calloutSetArgumentPark, 2);
+
+ // 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("SCC" "SD"), common_string_);
+
+ callout_handle.getArgument(MODIFIED_ARG, modified_arg);
+ EXPECT_EQ(std::string("SCC" "SDDC" "SCPSP"), 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()->registerCallout("alpha", callout_hook_name, 0);
+ getCalloutManager()->registerCallout("beta", callout_hook_name, 0);
+
+ // 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()->registerCallout("gamma", callout_hook_dummy, 1);
+ getCalloutManager()->registerCallout("gamma", callout_hook_name, 1);
+ getCalloutManager()->registerCallout("gamma", callout_hook_dummy, 1);
+
+ 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..a36d42c
--- /dev/null
+++ b/src/lib/hooks/tests/hooks_manager_unittest.cc
@@ -0,0 +1,1081 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/callout_handle.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/server_hooks.h>
+
+#include <hooks/tests/common_test_class.h>
+#define TEST_ASYNC_CALLOUT
+#include <hooks/tests/test_libraries.h>
+#include <cc/data.h>
+
+#include <boost/shared_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <fstream>
+#include <string>
+
+#include <unistd.h>
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace isc::data;
+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::setTestMode(false);
+ HooksManager::prepareUnloadLibraries();
+ bool status = HooksManager::unloadLibraries();
+ if (!status) {
+ cerr << "(fixture ctor) unloadLibraries failed" << endl;
+ }
+ // Ensure the marker file is not present at the start of a test.
+ static_cast<void>(remove(MARKER_FILE));
+ }
+
+ /// @brief Destructor
+ ///
+ /// Unload all libraries.
+ ~HooksManagerTest() {
+ static_cast<void>(remove(MARKER_FILE));
+ HooksManager::setTestMode(false);
+ HooksManager::prepareUnloadLibraries();
+ bool status = HooksManager::unloadLibraries();
+ if (!status) {
+ cerr << "(fixture dtor) unloadLibraries failed" << endl;
+ }
+ }
+
+ /// @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
+ ///
+ /// 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 wrong 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;
+ }
+
+ /// @brief Call command handlers test.
+ ///
+ /// This test is similar to @c executeCallCallouts but it uses
+ /// @ref HooksManager::callCommandHandlers to execute the command
+ /// handlers for the following commands: 'command-one' and 'command-two'.
+ ///
+ /// @param r1..r2, d1..d2 Data (dN) and expected results (rN).
+ void executeCallCommandHandlers(int d1, int r1, int d2, int r2) {
+ static const char* COMMON_TEXT = " command handler returned the wrong value";
+ static const char* RESULT = "result";
+
+ int result;
+
+ // Set up a callout handle for the calls.
+ 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.
+ handle->setArgument(RESULT, -1);
+
+ // Perform the first calculation: it should assign the data to the
+ // result.
+ handle->setArgument("data_1", d1);
+ HooksManager::callCommandHandlers("command-one", *handle);
+ handle->getArgument(RESULT, result);
+ EXPECT_EQ(r1, result) << "command-one" << COMMON_TEXT;
+
+ // Perform the second calculation: it should multiply the data by 10
+ // and return in the result.
+ handle->setArgument("data_2", d2);
+ HooksManager::callCommandHandlers("command-two", *handle);
+ handle->getArgument(RESULT, result);
+ EXPECT_EQ(r2, result) << "command-two" << COMMON_TEXT;
+ }
+
+private:
+ /// To avoid unused variable errors
+ std::string dummy(int i) {
+ if (i == 0) {
+ return (LOAD_CALLOUT_LIBRARY);
+ } else {
+ return (LOAD_ERROR_CALLOUT_LIBRARY);
+ }
+ }
+};
+
+// 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.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the libraries.
+ 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);
+ }
+
+ // r2 = 5 * 7 * 10
+ {
+ SCOPED_TRACE("Calculation using command handlers");
+ executeCallCommandHandlers(5, 5, 7, 350);
+ }
+
+ // Try unloading the libraries.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+
+ // 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.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(INCORRECT_VERSION_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the libraries. We expect a failure return because one of the
+ // libraries fails to load.
+ EXPECT_FALSE(HooksManager::loadLibraries(library_names));
+}
+
+// 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.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Execute the callouts. This 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::prepareUnloadLibraries();
+ EXPECT_FALSE(HooksManager::unloadLibraries());
+
+ // Deleting the callout handle should not cause a segmentation fault.
+ handle.reset();
+
+ // And allows unload.
+ EXPECT_TRUE(HooksManager::unloadLibraries());
+}
+
+// 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.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Execute the callouts. This 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
+ HookLibsCollection new_library_names;
+ new_library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the libraries.
+ EXPECT_THROW(HooksManager::loadLibraries(new_library_names),
+ LibrariesStillOpened);
+
+ // Deleting the old callout handle should not cause a segmentation fault.
+ handle.reset();
+
+ // But it allows the load of the new library.
+ EXPECT_TRUE(HooksManager::loadLibraries(new_library_names));
+
+ // Execute the calculation.
+ {
+ SCOPED_TRACE("Calculation with basic callout library loaded");
+ executeCallCallouts(10, 7, 17, 3, 51, 16, 35);
+ }
+}
+
+// This is effectively the same test as the LoadLibraries test.
+
+TEST_F(HooksManagerTest, ReloadSameLibraries) {
+
+ // Set up the list of libraries to be loaded.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the libraries.
+ 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.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // 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.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ 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);
+
+ // Reset the handle to allow a reload.
+ handle.reset();
+
+ // ... and check that the pre- and post- callout functions don't survive a
+ // reload.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+ 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);
+}
+
+// Test with test mode enabled and the pre- and post- callout functions survive
+// a reload
+
+TEST_F(HooksManagerTest, TestModeEnabledPrePostSurviveLoad) {
+
+ // Load a single library.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the pre- and post- callouts.
+ HooksManager::preCalloutsLibraryHandle().registerCallout("hookpt_two",
+ testPreCallout);
+ HooksManager::postCalloutsLibraryHandle().registerCallout("hookpt_two",
+ testPostCallout);
+
+ HooksManager::setTestMode(true);
+
+ // With the pre- and post- callouts above, the result expected is
+ //
+ // 1027 * 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(2054, result);
+
+ // Reset the handle to allow a reload.
+ handle.reset();
+
+ // ... and check that the pre- and post- callout functions survive a
+ // reload.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+ 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);
+
+ // Expect same value i.e. 1027 * 2
+ result = 0;
+ handle->getArgument("result", result);
+ EXPECT_EQ(2054, result);
+}
+
+// Test with test mode disabled and the pre- and post- callout functions do not
+// survive a reload
+
+TEST_F(HooksManagerTest, TestModeDisabledPrePostDoNotSurviveLoad) {
+
+ // Load a single library.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the pre- and post- callouts.
+ HooksManager::preCalloutsLibraryHandle().registerCallout("hookpt_two",
+ testPreCallout);
+ HooksManager::postCalloutsLibraryHandle().registerCallout("hookpt_two",
+ testPostCallout);
+
+ HooksManager::setTestMode(false);
+
+ // With the pre- and post- callouts above, the result expected is
+ //
+ // 1027 * 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(2054, result);
+
+ // Reset the handle to allow a reload.
+ handle.reset();
+
+ // ... and check that the pre- and post- callout functions don't survive a
+ // reload.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+ 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);
+}
+
+// Test with test mode enabled and the pre- and post- callout functions do not
+// survive a reload if the test mode is set too late.
+
+TEST_F(HooksManagerTest, TestModeEnabledTooLatePrePostDoNotSurvive) {
+
+ // Load a single library.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the pre- and post- callouts.
+ HooksManager::preCalloutsLibraryHandle().registerCallout("hookpt_two",
+ testPreCallout);
+ HooksManager::postCalloutsLibraryHandle().registerCallout("hookpt_two",
+ testPostCallout);
+
+ // With the pre- and post- callouts above, the result expected is
+ //
+ // 1027 * 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(2054, result);
+
+ // Reset the handle to allow a reload.
+ handle.reset();
+
+ // ... and check that the pre- and post- callout functions don't survive a
+ // reload.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+ handle = HooksManager::createCalloutHandle();
+
+ HooksManager::setTestMode(true);
+
+ 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_));
+ EXPECT_FALSE(HooksManager::commandHandlersPresent("command-one"));
+ EXPECT_FALSE(HooksManager::commandHandlersPresent("command-two"));
+}
+
+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")));
+
+
+ // The code used to throw, but it now allows to register the same
+ // hook several times. It simply returns existing index.
+ //EXPECT_THROW(static_cast<void>(HooksManager::registerHook(string("alpha"))),
+ // DuplicateHook);
+ EXPECT_EQ(2, HooksManager::registerHook(string("alpha")));
+
+ // ... 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]);
+}
+
+// Check that we can get the names of the libraries.
+
+TEST_F(HooksManagerTest, LibraryNames) {
+
+ // Set up the list of libraries to be loaded.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Check the names before the libraries are loaded.
+ std::vector<std::string> loaded_names = HooksManager::getLibraryNames();
+ EXPECT_TRUE(loaded_names.empty());
+
+ // Load the libraries and check the names again.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+ loaded_names = HooksManager::getLibraryNames();
+ EXPECT_TRUE(extractNames(library_names) == loaded_names);
+
+ // Unload the libraries and check again.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+ loaded_names = HooksManager::getLibraryNames();
+ EXPECT_TRUE(loaded_names.empty());
+}
+
+// Test the library validation function.
+
+TEST_F(HooksManagerTest, validateLibraries) {
+ // Vector of libraries that failed validation
+ std::vector<std::string> failed;
+
+ // Test different vectors of libraries.
+
+ // No libraries should return a success.
+ std::vector<std::string> libraries;
+
+ failed = HooksManager::validateLibraries(libraries);
+ EXPECT_TRUE(failed.empty());
+
+ // Single valid library should validate.
+ libraries.clear();
+ libraries.push_back(BASIC_CALLOUT_LIBRARY);
+
+ failed = HooksManager::validateLibraries(libraries);
+ EXPECT_TRUE(failed.empty());
+
+ // Multiple valid libraries should succeed.
+ libraries.clear();
+ libraries.push_back(BASIC_CALLOUT_LIBRARY);
+ libraries.push_back(FULL_CALLOUT_LIBRARY);
+ libraries.push_back(UNLOAD_CALLOUT_LIBRARY);
+
+ failed = HooksManager::validateLibraries(libraries);
+ EXPECT_TRUE(failed.empty());
+
+ // Single invalid library should fail.
+ libraries.clear();
+ libraries.push_back(NOT_PRESENT_LIBRARY);
+
+ failed = HooksManager::validateLibraries(libraries);
+ EXPECT_TRUE(failed == libraries);
+
+ // Multiple invalid libraries should fail.
+ libraries.clear();
+ libraries.push_back(INCORRECT_VERSION_LIBRARY);
+ libraries.push_back(NO_VERSION_LIBRARY);
+ libraries.push_back(FRAMEWORK_EXCEPTION_LIBRARY);
+
+ failed = HooksManager::validateLibraries(libraries);
+ EXPECT_TRUE(failed == libraries);
+
+ // Combination of valid and invalid (first one valid) should fail.
+ libraries.clear();
+ libraries.push_back(FULL_CALLOUT_LIBRARY);
+ libraries.push_back(INCORRECT_VERSION_LIBRARY);
+ libraries.push_back(NO_VERSION_LIBRARY);
+
+ std::vector<std::string> expected_failures;
+ expected_failures.push_back(INCORRECT_VERSION_LIBRARY);
+ expected_failures.push_back(NO_VERSION_LIBRARY);
+
+ failed = HooksManager::validateLibraries(libraries);
+ EXPECT_TRUE(failed == expected_failures);
+
+ // Combination of valid and invalid (first one invalid) should fail.
+ libraries.clear();
+ libraries.push_back(NO_VERSION_LIBRARY);
+ libraries.push_back(FULL_CALLOUT_LIBRARY);
+ libraries.push_back(INCORRECT_VERSION_LIBRARY);
+
+ expected_failures.clear();
+ expected_failures.push_back(NO_VERSION_LIBRARY);
+ expected_failures.push_back(INCORRECT_VERSION_LIBRARY);
+
+ failed = HooksManager::validateLibraries(libraries);
+ EXPECT_TRUE(failed == expected_failures);
+}
+
+// This test verifies that unload is called by the prepare method.
+TEST_F(HooksManagerTest, prepareUnload) {
+
+ // Set up the list of libraries to be loaded and load them.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(UNLOAD_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Check that the marker file is not present.
+ EXPECT_FALSE(markerFilePresent());
+
+ // Prepare unload libraries runs unload functions.
+ HooksManager::prepareUnloadLibraries();
+
+ // Now the marker file is present.
+ EXPECT_TRUE(markerFilePresent());
+}
+
+// This test verifies that the specified parameters are accessed properly.
+TEST_F(HooksManagerTest, LibraryParameters) {
+
+ // Prepare parameters for the callout parameters library.
+ ElementPtr params = Element::createMap();
+ params->set("svalue", Element::create("string value"));
+ params->set("ivalue", Element::create(42));
+ params->set("bvalue", Element::create(true));
+
+ // Set up the list of libraries to be loaded.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(CALLOUT_PARAMS_LIBRARY),
+ params));
+
+ // Load the libraries. Note that callout params library checks if
+ // all mandatory parameters are there, so if anything is missing, its
+ // load() function will return error, thus causing the library to not
+ // load.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Try unloading the libraries.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+}
+
+// This test verifies that an object can be parked in two different
+// callouts and that it is unparked when the last callout calls the
+// unpark function.
+TEST_F(HooksManagerTest, Parking) {
+ // Load the same library twice. Both installed callouts will trigger
+ // asynchronous operation.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Load the libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+
+ // We could be parked any object. Typically it will be a pointer to the
+ // packet. In this case, however, it is simpler to just use a string.
+ std::string parked_object = "foo";
+ handle->setArgument("parked_object", parked_object);
+
+ // This boolean value will be set to true when the packet gets unparked.
+ bool unparked = false;
+
+ // The callouts instruct us to park the object. We associated the callback
+ // function with the parked object, which sets "unparked" flag to true. We
+ // can later test the value of this flag to verify when exactly the packet
+ // got unparked.
+ ASSERT_NO_THROW(
+ HooksManager::park<std::string>("hookpt_one", "foo",
+ [&unparked] {
+ unparked = true;
+ })
+ );
+
+ // Call both installed callouts.
+ HooksManager::callCallouts(hookpt_one_index_, *handle);
+
+ // We have two callouts which should have returned pointers to the
+ // functions which we can call to simulate completion of asynchronous
+ // tasks.
+ std::function<void()> unpark_trigger_func1;
+ handle->getArgument("unpark_trigger1", unpark_trigger_func1);
+ // Call the first function. It should cause the hook library to call the
+ // "unpark" function. However, the object should not be unparked yet,
+ // because the other callout hasn't completed its scheduled asynchronous
+ // operation (keeps a reference on the parked object).
+ unpark_trigger_func1();
+ EXPECT_FALSE(unparked);
+
+ // Call the second function. This should decrease the reference count to
+ // 0 and the packet should be unparked.
+ std::function<void()> unpark_trigger_func2;
+ handle->getArgument("unpark_trigger2", unpark_trigger_func2);
+ unpark_trigger_func2();
+ EXPECT_TRUE(unparked);
+
+ // Resetting the handle makes return from test body to crash.
+
+ // Try unloading the libraries.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ // Handle is still active.
+ EXPECT_FALSE(status);
+}
+
+// This test verifies that the server can also unpark the packet.
+TEST_F(HooksManagerTest, ServerUnpark) {
+ // Load the same library twice. Both installed callouts will trigger
+ // asynchronous operation.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ // Load libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+
+ // We could be parked any object. Typically it will be a pointer to the
+ // packet. In this case, however, it is simpler to just use a string.
+ std::string parked_object = "foo";
+ handle->setArgument("parked_object", parked_object);
+
+ // This boolean value will be set to true when the packet gets unparked.
+ bool unparked = false;
+
+ // The callouts instruct us to park the object. We associated the callback
+ // function with the parked object, which sets "unparked" flag to true. We
+ // can later test the value of this flag to verify when exactly the packet
+ // got unparked.
+ HooksManager::park<std::string>("hookpt_one", "foo",
+ [&unparked] {
+ unparked = true;
+ });
+
+ // It should be possible for the server to increase reference counter.
+ ASSERT_NO_THROW(HooksManager::reference<std::string>("hookpt_one", "foo"));
+
+ // Call installed callout.
+ HooksManager::callCallouts(hookpt_one_index_, *handle);
+
+ // Server can force unparking the object.
+ EXPECT_TRUE(HooksManager::unpark<std::string>("hookpt_one", "foo"));
+
+ EXPECT_TRUE(unparked);
+
+ // Reset the handle to allow unload.
+ handle.reset();
+
+ // Try unloading the libraries.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+}
+
+// This test verifies that the server can drop parked packet.
+TEST_F(HooksManagerTest, ServerDropParked) {
+ // Load library.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+
+ // We could be parked any object. Typically it will be a pointer to the
+ // packet. In this case, however, it is simpler to just use a string.
+ std::string parked_object = "foo";
+ handle->setArgument("parked_object", parked_object);
+
+ // This boolean value will be set to true when the packet gets unparked.
+ bool unparked = false;
+
+ // The callouts instruct us to park the object. We associated the callback
+ // function with the parked object, which sets "unparked" flag to true. We
+ // can later test the value of this flag to verify when exactly the packet
+ // got unparked.
+ HooksManager::park<std::string>("hookpt_one", "foo",
+ [&unparked] {
+ unparked = true;
+ });
+
+ // It should be possible for the server to increase reference counter.
+ ASSERT_NO_THROW(HooksManager::reference<std::string>("hookpt_one", "foo"));
+
+ // Call installed callout.
+ HooksManager::callCallouts(hookpt_one_index_, *handle);
+
+ // Drop the parked packet. The callback should not be called.
+ EXPECT_TRUE(HooksManager::drop<std::string>("hookpt_one", "foo"));
+
+ EXPECT_FALSE(unparked);
+
+ // An attempt to unpark the packet should return false, as this packet
+ // is not parked anymore.
+ EXPECT_FALSE(HooksManager::unpark<std::string>("hookpt_one", "foo"));
+
+ // Reset the handle to allow unload.
+ handle.reset();
+
+ // Try unloading the libraries.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+}
+
+// This test verifies that parked objects are removed when libraries are
+// unloaded.
+TEST_F(HooksManagerTest, UnloadBeforeUnpark) {
+ // Load the same library twice. Both installed callouts will trigger
+ // asynchronous operation.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(ASYNC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ // Load libraries.
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ CalloutHandlePtr handle = HooksManager::createCalloutHandle();
+
+ // We could be parked any object. Typically it will be a pointer to the
+ // packet. In this case, however, it is simpler to just use a string.
+ std::string parked_object = "foo";
+ handle->setArgument("parked_object", parked_object);
+
+ // This boolean value will be set to true when the packet gets unparked.
+ bool unparked = false;
+
+ // The callouts instruct us to park the object. We associated the callback
+ // function with the parked object, which sets "unparked" flag to true. We
+ // can later test the value of this flag to verify when exactly the packet
+ // got unparked.
+ HooksManager::park<std::string>("hookpt_one", "foo",
+ [&unparked] {
+ unparked = true;
+ });
+
+ // Call installed callout.
+ HooksManager::callCallouts(hookpt_one_index_, *handle);
+
+ // Reset the handle to allow a reload.
+ handle.reset();
+
+ // Try reloading the libraries.
+ EXPECT_NO_THROW(HooksManager::prepareUnloadLibraries());
+ bool status = false;
+ EXPECT_NO_THROW(status = HooksManager::unloadLibraries());
+ EXPECT_TRUE(status);
+ EXPECT_TRUE(HooksManager::loadLibraries(library_names));
+
+ // Parked object should have been removed.
+ EXPECT_FALSE(HooksManager::unpark<std::string>("hookpt_one", "foo"));
+
+ // Callback should not be called.
+ EXPECT_FALSE(unparked);
+}
+
+
+} // 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..f4d7850
--- /dev/null
+++ b/src/lib/hooks/tests/incorrect_version_library.cc
@@ -0,0 +1,26 @@
+// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief 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 <config.h>
+#include <hooks/hooks.h>
+
+extern "C" {
+
+int version() {
+ return (KEA_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..9b30cb3
--- /dev/null
+++ b/src/lib/hooks/tests/library_manager_collection_unittest.cc
@@ -0,0 +1,314 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <hooks/callout_handle.h>
+#include <hooks/callout_manager.h>
+#include <hooks/library_manager.h>
+#include <hooks/library_manager_collection.h>
+#include <hooks/libinfo.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 {
+private:
+
+ /// To avoid unused variable errors
+ std::string dummy(int i) {
+ if (i == 0) {
+ return (MARKER_FILE);
+ } else if (i > 0) {
+ return (LOAD_CALLOUT_LIBRARY);
+ } else {
+ return (LOAD_ERROR_CALLOUT_LIBRARY);
+ }
+ }
+};
+
+} // namespace
+
+namespace isc {
+namespace hooks {
+/// @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 LibraryManagerCollection {
+public:
+ /// @brief Constructor
+ ///
+ /// @param List of libraries that this collection will manage. The order
+ /// of the libraries is important.
+ PublicLibraryManagerCollection(const HookLibsCollection& libraries)
+ : LibraryManagerCollection(libraries) {
+ }
+
+ /// Public methods that call protected methods on the superclass.
+ using LibraryManagerCollection::unloadLibraries;
+};
+
+} // namespace hooks
+} // namespace isc
+
+namespace {
+// 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.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Set up the library manager collection and get the callout manager we'll
+ // be using.
+ PublicLibraryManagerCollection lm_collection(library_names);
+
+ // Load the libraries.
+ EXPECT_TRUE(lm_collection.loadLibraries());
+ EXPECT_EQ(2, lm_collection.getLoadedLibraryCount());
+
+ // 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
+ boost::shared_ptr<CalloutManager> manager =
+ lm_collection.getCalloutManager();
+ {
+ 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());
+ EXPECT_EQ(0, lm_collection.getLoadedLibraryCount());
+
+ // 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 no libraries will be loaded.
+
+TEST_F(LibraryManagerCollectionTest, LoadLibrariesWithError) {
+
+ // Set up the list of libraries to be loaded.
+ HookLibsCollection library_names;
+ library_names.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(INCORRECT_VERSION_LIBRARY),
+ data::ConstElementPtr()));
+ library_names.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Set up the library manager collection and get the callout manager we'll
+ // 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());
+
+ // Expect no libraries were loaded.
+ EXPECT_EQ(0, lm_collection.getLoadedLibraryCount());
+}
+
+// Check that everything works even with no libraries loaded.
+
+TEST_F(LibraryManagerCollectionTest, NoLibrariesLoaded) {
+ // Set up the list of libraries to be loaded.
+ HookLibsCollection 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());
+ EXPECT_EQ(0, lm_collection.getLoadedLibraryCount());
+ boost::shared_ptr<CalloutManager> manager =
+ lm_collection.getCalloutManager();
+
+ // Execute 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);
+}
+
+// Check that we can get the names of the libraries.
+
+TEST_F(LibraryManagerCollectionTest, LibraryNames) {
+
+ // Set up the list of libraries to be loaded.
+ HookLibsCollection libraries;
+ libraries.push_back(make_pair(std::string(FULL_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+ libraries.push_back(make_pair(std::string(BASIC_CALLOUT_LIBRARY),
+ data::ConstElementPtr()));
+
+ // Set up the library manager collection and get the callout manager we'll
+ // be using.
+ PublicLibraryManagerCollection lm_collection(libraries);
+
+ // Check the names before the libraries are loaded.
+ std::vector<std::string> collection_names = lm_collection.getLibraryNames();
+ EXPECT_TRUE(extractNames(libraries) == collection_names);
+
+ // Load the libraries and check the names again.
+ EXPECT_TRUE(lm_collection.loadLibraries());
+ EXPECT_EQ(2, lm_collection.getLoadedLibraryCount());
+ collection_names = lm_collection.getLibraryNames();
+ EXPECT_TRUE(extractNames(libraries) == collection_names);
+}
+
+// Test the library validation function.
+
+TEST_F(LibraryManagerCollectionTest, validateLibraries) {
+ // Vector of libraries that failed validation
+ std::vector<std::string> failed;
+
+ // Test different vectors of libraries.
+
+ // No libraries should return a success.
+ std::vector<std::string> libraries;
+
+ failed = LibraryManagerCollection::validateLibraries(libraries);
+ EXPECT_TRUE(failed.empty());
+
+ // Single valid library should validate.
+ libraries.clear();
+ libraries.push_back(BASIC_CALLOUT_LIBRARY);
+
+ failed = LibraryManagerCollection::validateLibraries(libraries);
+ EXPECT_TRUE(failed.empty());
+
+ // Multiple valid libraries should succeed.
+ libraries.clear();
+ libraries.push_back(BASIC_CALLOUT_LIBRARY);
+ libraries.push_back(FULL_CALLOUT_LIBRARY);
+ libraries.push_back(UNLOAD_CALLOUT_LIBRARY);
+ libraries.push_back(CALLOUT_PARAMS_LIBRARY);
+
+ failed = LibraryManagerCollection::validateLibraries(libraries);
+ EXPECT_TRUE(failed.empty());
+
+ // Single invalid library should fail.
+ libraries.clear();
+ libraries.push_back(NOT_PRESENT_LIBRARY);
+
+ failed = LibraryManagerCollection::validateLibraries(libraries);
+ EXPECT_TRUE(failed == libraries);
+
+ // Multiple invalid libraries should fail.
+ libraries.clear();
+ libraries.push_back(INCORRECT_VERSION_LIBRARY);
+ libraries.push_back(NO_VERSION_LIBRARY);
+ libraries.push_back(FRAMEWORK_EXCEPTION_LIBRARY);
+
+ failed = LibraryManagerCollection::validateLibraries(libraries);
+ EXPECT_TRUE(failed == libraries);
+
+ // Combination of valid and invalid (first one valid) should fail.
+ libraries.clear();
+ libraries.push_back(FULL_CALLOUT_LIBRARY);
+ libraries.push_back(INCORRECT_VERSION_LIBRARY);
+ libraries.push_back(NO_VERSION_LIBRARY);
+
+ std::vector<std::string> expected_failures;
+ expected_failures.push_back(INCORRECT_VERSION_LIBRARY);
+ expected_failures.push_back(NO_VERSION_LIBRARY);
+
+ failed = LibraryManagerCollection::validateLibraries(libraries);
+ EXPECT_TRUE(failed == expected_failures);
+
+ // Combination of valid and invalid (first one invalid) should fail.
+ libraries.clear();
+ libraries.push_back(NO_VERSION_LIBRARY);
+ libraries.push_back(FULL_CALLOUT_LIBRARY);
+ libraries.push_back(INCORRECT_VERSION_LIBRARY);
+
+ expected_failures.clear();
+ expected_failures.push_back(NO_VERSION_LIBRARY);
+ expected_failures.push_back(INCORRECT_VERSION_LIBRARY);
+
+ failed = LibraryManagerCollection::validateLibraries(libraries);
+ EXPECT_TRUE(failed == expected_failures);
+}
+
+// This test verifies if getLibraryNames and getLibraryInfo are returning
+// expected values if there are no libraries configured.
+TEST_F(LibraryManagerCollectionTest, libraryGetEmpty) {
+
+ HookLibsCollection empty;
+ boost::shared_ptr<LibraryManagerCollection> mgr;
+
+ // Instantiate library manager collection with no libraries
+ EXPECT_NO_THROW(mgr.reset(new LibraryManagerCollection(empty)));
+
+ // Check that getLibraryInfo returns empty list properly.
+ HookLibsCollection returned = mgr->getLibraryInfo();
+ EXPECT_TRUE(returned.empty());
+
+ // Check that getLibraryNames return empty list, too.
+ vector<string> names(3, "rubbish"); // just put something in it.
+ EXPECT_NO_THROW(names = mgr->getLibraryNames());
+ EXPECT_TRUE(names.empty());
+}
+
+// This test verifies if getLibraryNames and getLibraryInfo are returning
+// expected values when there are libraries configured.
+TEST_F(LibraryManagerCollectionTest, libraryGet) {
+ using namespace data;
+
+ HookLibsCollection libs;
+ ElementPtr param1(Element::fromJSON("{ \"param1\": \"foo\" }"));
+ ElementPtr param2(Element::fromJSON("{ \"param2\": \"bar\" }"));
+ libs.push_back(make_pair("libone", param1));
+ libs.push_back(make_pair("libtwo", param2));
+
+ boost::shared_ptr<LibraryManagerCollection> mgr;
+ EXPECT_NO_THROW(mgr.reset(new LibraryManagerCollection(libs)));
+ EXPECT_TRUE(libs == mgr->getLibraryInfo());
+
+ vector<string> exp_names;
+ exp_names.push_back("libone");
+ exp_names.push_back("libtwo");
+
+ EXPECT_TRUE(exp_names == mgr->getLibraryNames());
+}
+
+} // Anonymous namespace
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..c0174a4
--- /dev/null
+++ b/src/lib/hooks/tests/library_manager_unittest.cc
@@ -0,0 +1,741 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#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 <log/message_dictionary.h>
+#include <log/message_initializer.h>
+
+#include <util/multi_threading_mgr.h>
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <fstream>
+#include <string>
+
+#include <unistd.h>
+
+
+using namespace isc;
+using namespace isc::hooks;
+using namespace isc::log;
+using namespace isc::util;
+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>(remove(MARKER_FILE));
+
+ // Disable multi-threading.
+ MultiThreadingMgr::instance().setMode(false);
+ }
+
+ /// @brief Destructor
+ ///
+ /// Ensures a marker file is removed after each test.
+ ~LibraryManagerTest() {
+ static_cast<void>(remove(MARKER_FILE));
+
+ // Disable multi-threading.
+ MultiThreadingMgr::instance().setMode(false);
+ }
+
+ /// @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);
+ }
+
+ /// @brief Call command handlers test.
+ ///
+ /// A wrapper around the method of the same name in the HooksCommonTestClass
+ /// object, this passes this class's CalloutManager to that method.
+ ///
+ /// @param r1..r2, d1..d2 Values and intermediate values expected.
+ void executeCallCommandHandlers(int d1, int r1, int d2, int r2) {
+ HooksCommonTestClass::executeCallCommandHandlers(callout_manager_,
+ d1, r1, d2, r2);
+ }
+
+ /// 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::unloadLibrary;
+ using LibraryManager::openLibrary;
+ using LibraryManager::closeLibrary;
+ using LibraryManager::checkVersion;
+ using LibraryManager::checkMultiThreadingCompatible;
+ using LibraryManager::registerStandardCallouts;
+ using LibraryManager::runLoad;
+ using LibraryManager::prepareUnloadLibrary;
+};
+
+
+// Check that LibraryManager constructor requires a not null manager
+
+TEST_F(LibraryManagerTest, NullManager) {
+ boost::shared_ptr<CalloutManager> null_manager;
+ EXPECT_THROW(PublicLibraryManager(std::string("foo"), 0, null_manager),
+ NoCalloutManager);
+}
+
+// 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 that the code handles the case of a library with no
+// multi_threading_compatible function.
+
+TEST_F(LibraryManagerTest, NoMultiThreadingCompatible) {
+ PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+
+ // Open should succeed.
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Not multi-threading compatible: does not matter without MT.
+ EXPECT_TRUE(lib_manager.checkMultiThreadingCompatible());
+
+ // Not multi-threading compatible: does matter with MT.
+ MultiThreadingMgr::instance().setMode(true);
+ EXPECT_FALSE(lib_manager.checkMultiThreadingCompatible());
+
+ // Tidy up.
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Checks that the code handles the case of a library with a
+// multi_threading_compatible function returning 0 (not compatible).
+
+TEST_F(LibraryManagerTest, multiThreadingNotCompatible) {
+ PublicLibraryManager lib_manager(std::string(LOAD_ERROR_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+
+ // Open should succeed.
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Not multi-threading compatible: does not matter without MT.
+ EXPECT_TRUE(lib_manager.checkMultiThreadingCompatible());
+
+ // Not multi-threading compatible: does matter with MT.
+ MultiThreadingMgr::instance().setMode(true);
+ EXPECT_FALSE(lib_manager.checkMultiThreadingCompatible());
+
+ // Tidy up.
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Checks that the code handles the case of a library with a
+// multi_threading_compatible function returning 1 (compatible)
+
+TEST_F(LibraryManagerTest, multiThreadingCompatible) {
+ PublicLibraryManager lib_manager(std::string(FULL_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+
+ // Open should succeed.
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Multi-threading compatible: does not matter without MT.
+ EXPECT_TRUE(lib_manager.checkMultiThreadingCompatible());
+
+ // Multi-threading compatible: does matter with MT.
+ MultiThreadingMgr::instance().setMode(true);
+ EXPECT_TRUE(lib_manager.checkMultiThreadingCompatible());
+
+ // Tidy up.
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Checks that the code handles the case of a library with a
+// multi_threading_compatible function returning 1 (compatible)
+
+TEST_F(LibraryManagerTest, multiThreadingCompatibleException) {
+ PublicLibraryManager lib_manager(std::string(FRAMEWORK_EXCEPTION_LIBRARY),
+ 0, callout_manager_);
+
+ // Open should succeed.
+ EXPECT_TRUE(lib_manager.openLibrary());
+
+ // Throw exception: does not matter without MT.
+ EXPECT_TRUE(lib_manager.checkMultiThreadingCompatible());
+
+ // Throw exception: does matter with MT.
+ MultiThreadingMgr::instance().setMode(true);
+ EXPECT_FALSE(lib_manager.checkMultiThreadingCompatible());
+
+ // 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_->commandHandlersPresent("command-one"));
+ EXPECT_FALSE(callout_manager_->commandHandlersPresent("command-two"));
+ 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_TRUE(callout_manager_->commandHandlersPresent("command-one"));
+ EXPECT_TRUE(callout_manager_->commandHandlersPresent("command-two"));
+ 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);
+
+ // Execute command handlers for 'command-one' and 'command-two'.
+ //
+ // r2 = d1 * d2 * 10;
+ executeCallCommandHandlers(5, 5, 7, 350);
+
+ // 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.prepareUnloadLibrary());
+
+ // 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.prepareUnloadLibrary());
+
+ // 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.prepareUnloadLibrary());
+
+ // Tidy up
+ EXPECT_TRUE(lib_manager.closeLibrary());
+}
+
+// Check that the case of the library's unload() function returning a
+// success is handled correctly.
+
+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.prepareUnloadLibrary());
+
+ // 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(LOAD_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_));
+ EXPECT_FALSE(callout_manager_->commandHandlersPresent("command-one"));
+ EXPECT_FALSE(callout_manager_->commandHandlersPresent("command-two"));
+
+ // 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_));
+ EXPECT_FALSE(callout_manager_->commandHandlersPresent("command-one"));
+ EXPECT_FALSE(callout_manager_->commandHandlersPresent("command-two"));
+
+ // 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_));
+ EXPECT_TRUE(callout_manager_->commandHandlersPresent("command-one"));
+ EXPECT_TRUE(callout_manager_->commandHandlersPresent("command-two"));
+
+ // 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_));
+ EXPECT_FALSE(callout_manager_->commandHandlersPresent("command-one"));
+ EXPECT_FALSE(callout_manager_->commandHandlersPresent("command-two"));
+}
+
+// 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) {
+ PublicLibraryManager 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.
+ PublicLibraryManager 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.
+ PublicLibraryManager 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.
+ PublicLibraryManager 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.
+ PublicLibraryManager 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());
+}
+
+// Check that libraries can be validated.
+
+TEST_F(LibraryManagerTest, validateLibraries) {
+ EXPECT_TRUE(LibraryManager::validateLibrary(BASIC_CALLOUT_LIBRARY));
+ EXPECT_TRUE(LibraryManager::validateLibrary(FULL_CALLOUT_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(FRAMEWORK_EXCEPTION_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(INCORRECT_VERSION_LIBRARY));
+ EXPECT_TRUE(LibraryManager::validateLibrary(LOAD_CALLOUT_LIBRARY));
+ EXPECT_TRUE(LibraryManager::validateLibrary(LOAD_ERROR_CALLOUT_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(NOT_PRESENT_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(NO_VERSION_LIBRARY));
+ EXPECT_TRUE(LibraryManager::validateLibrary(UNLOAD_CALLOUT_LIBRARY));
+ EXPECT_TRUE(LibraryManager::validateLibrary(CALLOUT_PARAMS_LIBRARY));
+
+ MultiThreadingMgr::instance().setMode(true);
+
+ EXPECT_FALSE(LibraryManager::validateLibrary(BASIC_CALLOUT_LIBRARY));
+ EXPECT_TRUE(LibraryManager::validateLibrary(FULL_CALLOUT_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(FRAMEWORK_EXCEPTION_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(INCORRECT_VERSION_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(LOAD_CALLOUT_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(LOAD_ERROR_CALLOUT_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(NOT_PRESENT_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(NO_VERSION_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(UNLOAD_CALLOUT_LIBRARY));
+ EXPECT_FALSE(LibraryManager::validateLibrary(CALLOUT_PARAMS_LIBRARY));
+}
+
+// Check that log messages are properly registered and unregistered.
+
+TEST_F(LibraryManagerTest, libraryLoggerSetup) {
+ // Load a library with all framework functions.
+ PublicLibraryManager lib_manager(std::string(BASIC_CALLOUT_LIBRARY),
+ 0, callout_manager_);
+ EXPECT_TRUE(lib_manager.loadLibrary());
+
+ // After loading the library, the global logging dictionary should
+ // contain log messages registered for this library.
+ const MessageDictionaryPtr& dict = MessageDictionary::globalDictionary();
+ EXPECT_EQ("basic callout load %1", dict->getText("BCL_LOAD_START"));
+ EXPECT_EQ("basic callout load end", dict->getText("BCL_LOAD_END"));
+ // Some of the messages defined by the hook library are duplicates. But,
+ // the loadLibrary function should have logged the duplicates and clear
+ // the duplicates list. By checking that the list of duplicates is empty
+ // we test that the LibraryManager handles the duplicates (logs and
+ // clears them).
+ EXPECT_TRUE(MessageInitializer::getDuplicates().empty());
+
+ // After unloading the library, the messages should be unregistered.
+ EXPECT_TRUE(lib_manager.unloadLibrary());
+ // The musl libc does not implement dlclose
+#ifndef LIBC_MUSL
+ EXPECT_TRUE(dict->getText("BCL_LOAD_START").empty());
+ EXPECT_TRUE(dict->getText("BCL_LOAD_END").empty());
+#endif
+}
+
+} // 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..613ec2c
--- /dev/null
+++ b/src/lib/hooks/tests/load_callout_library.cc
@@ -0,0 +1,152 @@
+// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief 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 <config.h>
+#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);
+}
+
+// Third 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);
+}
+
+// First command handler assigns data to a result.
+
+static int
+command_handler_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 command handler multiples the result by data by 10.
+
+static int
+command_handler_two(CalloutHandle& handle) {
+ int data;
+ handle.getArgument("data_2", data);
+
+ int result;
+ handle.getArgument("result", result);
+
+ result *= data * 10;
+ handle.setArgument("result", result);
+
+ return (0);
+}
+
+// Framework functions
+
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+int load(LibraryHandle& handle) {
+ // Initialize the user library if the main image was statically linked
+#ifdef USE_STATIC_LINK
+ hooksStaticLinkInit();
+#endif
+ // Register the non-standard functions
+ handle.registerCallout("hookpt_two", hook_nonstandard_two);
+ handle.registerCallout("hookpt_three", hook_nonstandard_three);
+
+ // Register command_handler_one as control command handler.
+ handle.registerCommandCallout("command-one", command_handler_one);
+ handle.registerCommandCallout("command-two", command_handler_two);
+
+ 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..66b1359
--- /dev/null
+++ b/src/lib/hooks/tests/load_error_callout_library.cc
@@ -0,0 +1,47 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief 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 <config.h>
+#include <hooks/hooks.h>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Framework functions
+
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+int
+load(LibraryHandle&) {
+ return (1);
+}
+
+int
+unload() {
+ return (1);
+}
+
+int
+multi_threading_compatible() {
+ return (0);
+}
+
+};
+
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..368ad37
--- /dev/null
+++ b/src/lib/hooks/tests/marker_file.h.in
@@ -0,0 +1,19 @@
+// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef 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..a4c4bb5
--- /dev/null
+++ b/src/lib/hooks/tests/no_version_library.cc
@@ -0,0 +1,24 @@
+// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+/// @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/parking_lots_unittest.cc b/src/lib/hooks/tests/parking_lots_unittest.cc
new file mode 100644
index 0000000..96f8814
--- /dev/null
+++ b/src/lib/hooks/tests/parking_lots_unittest.cc
@@ -0,0 +1,339 @@
+// Copyright (C) 2018-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <hooks/parking_lots.h>
+#include <boost/weak_ptr.hpp>
+#include <testutils/gtest_utils.h>
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace isc;
+using namespace isc::hooks;
+
+namespace {
+
+// Defines a pointer to a string. The tests use shared pointers
+// as parked objects to ensure key matching works correctly with
+// them. We're doing this because real-world use parked objects
+// are typically pointers to packets.
+typedef boost::shared_ptr<std::string> StringPtr;
+
+// Test that it is possible to create and retrieve parking lots for
+// specified hook points.
+TEST(ParkingLotsTest, createGetParkingLot) {
+ ParkingLots parking_lots;
+
+ ParkingLotPtr parking_lot0 = parking_lots.getParkingLotPtr(1);
+ ParkingLotPtr parking_lot1 = parking_lots.getParkingLotPtr(2);
+ ParkingLotPtr parking_lot2 = parking_lots.getParkingLotPtr(1);
+
+ ASSERT_TRUE(parking_lot0);
+ ASSERT_TRUE(parking_lot1);
+ ASSERT_TRUE(parking_lot2);
+
+ EXPECT_EQ(0, parking_lot0->size());
+ EXPECT_EQ(0, parking_lot1->size());
+ EXPECT_EQ(0, parking_lot2->size());
+
+ EXPECT_FALSE(parking_lot0 == parking_lot1);
+ EXPECT_TRUE(parking_lot0 == parking_lot2);
+
+ ASSERT_NO_THROW(parking_lots.clear());
+
+ ParkingLotPtr parking_lot3 = parking_lots.getParkingLotPtr(1);
+ ASSERT_TRUE(parking_lot3);
+
+ EXPECT_FALSE(parking_lot3 == parking_lot0);
+}
+
+// Verify that an object can be parked.
+TEST(ParkingLotsTest, park) {
+ ParkingLot parking_lot;
+
+ // Create object to park.
+ StringPtr parked_object(new std::string("foo"));
+
+ // Verify that we can park an object that has not been parked.
+ ASSERT_NO_THROW(parking_lot.park(parked_object, [] {}));
+
+ EXPECT_EQ(1, parking_lot.size());
+
+ // Verify that we cannot park an object that has been parked
+ EXPECT_THROW(parking_lot.park(parked_object, [] {}),
+ InvalidOperation);
+}
+
+// Verify that an object can be referenced.
+TEST(ParkingLotsTest, reference) {
+ ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
+ ParkingLotHandlePtr parking_lot_handle =
+ boost::make_shared<ParkingLotHandle>(parking_lot);
+
+ // Create an object.
+ StringPtr parked_object(new std::string("foo"));
+
+ // Cannot reference an object that has not been parked.
+ ASSERT_THROW(parking_lot_handle->reference(parked_object),
+ InvalidOperation);
+
+ // Park the object.
+ ASSERT_NO_THROW(parking_lot->park(parked_object, [] {}));
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Reference the object. Reference count should one.
+ int ref_count = 0;
+ ASSERT_NO_THROW(ref_count = parking_lot_handle->reference(parked_object));
+ ASSERT_EQ(1, ref_count);
+
+ // Reference the object again. Reference count should two.
+ ASSERT_NO_THROW(ref_count = parking_lot_handle->reference(parked_object));
+ ASSERT_EQ(2, ref_count);
+
+ EXPECT_EQ(1, parking_lot->size());
+}
+
+// Test that object can be parked and then unparked.
+TEST(ParkingLotsTest, unpark) {
+ ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
+ ParkingLotHandlePtr parking_lot_handle =
+ boost::make_shared<ParkingLotHandle>(parking_lot);
+
+ StringPtr parked_object(new std::string("foo"));
+
+ // Unparking should return false if the object isn't parked.
+ EXPECT_FALSE(parking_lot->unpark(parked_object));
+
+ EXPECT_EQ(0, parking_lot->size());
+
+ // This flag will indicate if the callback has been called.
+ bool unparked = false;
+
+ ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
+ unparked = true;
+ }));
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Reference the parked object twice because we're going to test that
+ // reference counting works fine.
+ ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
+ ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Try to unpark the object. It should decrease the reference count, but not
+ // unpark the packet yet.
+ EXPECT_TRUE(parking_lot_handle->unpark(parked_object));
+ EXPECT_FALSE(unparked);
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Try to unpark the object. This time it should be successful, because the
+ // reference count goes to 0.
+ EXPECT_TRUE(parking_lot_handle->unpark(parked_object));
+ EXPECT_TRUE(unparked);
+
+ EXPECT_EQ(0, parking_lot->size());
+
+ // Calling unpark again should return false to indicate that the object is
+ // not parked.
+ EXPECT_FALSE(parking_lot_handle->unpark(parked_object));
+
+ EXPECT_EQ(0, parking_lot->size());
+}
+
+// Test that parked object can be dropped.
+TEST(ParkingLotsTest, drop) {
+ ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
+ ParkingLotHandlePtr parking_lot_handle =
+ boost::make_shared<ParkingLotHandle>(parking_lot);
+
+ StringPtr parked_object(new std::string("foo"));
+
+ // This flag will indicate if the callback has been called.
+ bool unparked = false;
+ ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
+ unparked = true;
+ }));
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Reference object twice to test that dropping the packet ignores
+ // reference counting.
+ ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
+ ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
+
+ // Drop parked object. The callback should not be invoked.
+ EXPECT_TRUE(parking_lot_handle->drop(parked_object));
+ EXPECT_FALSE(unparked);
+
+ EXPECT_EQ(0, parking_lot->size());
+
+ // Expect that an attempt to unpark return false, as the object
+ // has been dropped.
+ EXPECT_FALSE(parking_lot_handle->unpark(parked_object));
+}
+
+// Test that parked lots can be cleared.
+TEST(ParkingLotsTest, clear) {
+ ParkingLotsPtr parking_lots = boost::make_shared<ParkingLots>();
+ ParkingLotPtr parking_lot = parking_lots->getParkingLotPtr(1234);
+ ASSERT_TRUE(parking_lot);
+ ParkingLotHandlePtr parking_lot_handle =
+ boost::make_shared<ParkingLotHandle>(parking_lot);
+
+ boost::shared_ptr<std::string> parked_object =
+ boost::make_shared<std::string>("foo");
+ boost::weak_ptr<std::string> weak_parked_object(parked_object);
+
+ // This flag will indicate if the callback has been called.
+ bool unparked = false;
+ ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
+ unparked = true;
+ }));
+
+ // Reference object twice to test that clearing the parking lots
+ // ignores reference counting.
+ ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
+ ASSERT_NO_THROW(parking_lot_handle->reference(parked_object));
+
+ // Drop reference on objects.
+ parking_lot.reset();
+ parking_lot_handle.reset();
+ parked_object.reset();
+
+ // The parked object is still alive.
+ EXPECT_FALSE(weak_parked_object.expired());
+
+ // Clear the parking lots.
+ ASSERT_NO_THROW(parking_lots->clear());
+
+ // The callback should not be invoked.
+ EXPECT_FALSE(unparked);
+
+ // The parked object was destroyed.
+ EXPECT_TRUE(weak_parked_object.expired());
+}
+
+// Verify that an object can be dereferenced.
+TEST(ParkingLotsTest, dereference) {
+ ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
+ ParkingLotHandlePtr parking_lot_handle =
+ boost::make_shared<ParkingLotHandle>(parking_lot);
+
+ // Create an object.
+ StringPtr parked_object(new std::string("foo"));
+
+ // Verify that an object that hasn't been parked, cannot be
+ // dereferenced.
+ ASSERT_THROW(parking_lot_handle->dereference(parked_object),
+ InvalidOperation);
+
+ // Park the object.
+ // This flag will indicate if the callback has been called.
+ bool unparked = false;
+ ASSERT_NO_THROW(parking_lot->park(parked_object, [&unparked] {
+ unparked = true;
+ }));
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Reference the parked object twice.
+ int ref_count = 0;
+ ASSERT_NO_THROW(ref_count = parking_lot_handle->reference(parked_object));
+ ASSERT_EQ(1, ref_count);
+ ASSERT_NO_THROW(ref_count = parking_lot_handle->reference(parked_object));
+ ASSERT_EQ(2, ref_count);
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Try to dereference the object. It should decrease the reference count,
+ // but not unpark the packet or invoke the callback.
+ ASSERT_NO_THROW(ref_count = parking_lot_handle->dereference(parked_object));
+ ASSERT_EQ(1, ref_count);
+ EXPECT_FALSE(unparked);
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Try to dereference the object. It should decrease the reference count,
+ // but not unpark the packet or invoke the callback.
+ ASSERT_NO_THROW(ref_count = parking_lot_handle->dereference(parked_object));
+ ASSERT_EQ(0, ref_count);
+ EXPECT_FALSE(unparked);
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Try to dereference the object. It should decrement to -1
+ // but not unpark the packet or invoke the callback.
+ ASSERT_NO_THROW(ref_count = parking_lot_handle->dereference(parked_object));
+ EXPECT_EQ(-1, ref_count);
+ EXPECT_FALSE(unparked);
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Calling unpark should invoke the callback.
+ ASSERT_TRUE(parking_lot_handle->unpark(parked_object));
+ EXPECT_TRUE(unparked);
+
+ EXPECT_EQ(0, parking_lot->size());
+}
+
+// Verify that parked objects are correctly distinguished from
+// one another.
+TEST(ParkingLotsTest, multipleObjects) {
+ ParkingLotPtr parking_lot = boost::make_shared<ParkingLot>();
+ ParkingLotHandlePtr parking_lot_handle =
+ boost::make_shared<ParkingLotHandle>(parking_lot);
+
+ // Create an object and park it.
+ StringPtr object_one(new std::string("one"));
+ int unparked_one = 0;
+ ASSERT_NO_THROW(parking_lot->park(object_one, [&unparked_one] {
+ ++unparked_one;
+ }));
+
+ // Create a second object and park it.
+ StringPtr object_two(new std::string("two"));
+ int unparked_two = 0;
+ ASSERT_NO_THROW(parking_lot->park(object_two, [&unparked_two] {
+ ++unparked_two;
+ }));
+
+ EXPECT_EQ(2, parking_lot->size());
+
+ // Create a third object but don't park it.
+ StringPtr object_three(new std::string("three"));
+
+ // Try to unpark object_three. It should fail, and no callbacks
+ // should get invoked.
+ EXPECT_FALSE(parking_lot_handle->unpark(object_three));
+ EXPECT_EQ(unparked_one, 0);
+ EXPECT_EQ(unparked_two, 0);
+
+ EXPECT_EQ(2, parking_lot->size());
+
+ // Unpark object one. It should succeed and its callback should
+ // get invoked.
+ EXPECT_TRUE(parking_lot_handle->unpark(object_one));
+ EXPECT_EQ(unparked_one, 1);
+ EXPECT_EQ(unparked_two, 0);
+
+ EXPECT_EQ(1, parking_lot->size());
+
+ // Unpark object two. It should succeed and its callback should
+ // get invoked.
+ EXPECT_TRUE(parking_lot_handle->unpark(object_two));
+ EXPECT_EQ(unparked_one, 1);
+ EXPECT_EQ(unparked_two, 1);
+
+ EXPECT_EQ(0, parking_lot->size());
+}
+
+}
diff --git a/src/lib/hooks/tests/run_unittests.cc b/src/lib/hooks/tests/run_unittests.cc
new file mode 100644
index 0000000..cd6fc2b
--- /dev/null
+++ b/src/lib/hooks/tests/run_unittests.cc
@@ -0,0 +1,19 @@
+// Copyright (C) 2009-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#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..f9b20cb
--- /dev/null
+++ b/src/lib/hooks/tests/server_hooks_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#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(0, hooks.findIndex("context_create"));
+ EXPECT_EQ(1, hooks.getIndex("context_destroy"));
+ EXPECT_EQ(1, hooks.findIndex("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.
+// This test has been updated. See #5251 for details. The old
+// code is retained in case we decide to get back to it.
+TEST(ServerHooksTest, DISABLED_OldDuplicateHooks) {
+ 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);
+}
+
+// Check that duplicate names are handled properly. The code used to throw,
+// but it now returns the existing index. See #5251 for details.
+TEST(ServerHooksTest, NewDuplicateHooks) {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+
+ int index = hooks.getIndex("context_create");
+
+ // Ensure we can duplicate one of the existing names.
+ // Instead of throwing, we just check that a reasonable
+ // index has been returned.
+ EXPECT_EQ(index, hooks.registerHook("context_create"));
+
+ // Check that mutiple attempts to register the same hook will return
+ // existing index.
+ int gamma = hooks.registerHook("gamma");
+ EXPECT_EQ(2, gamma);
+ EXPECT_EQ(gamma, hooks.registerHook("gamma"));
+ EXPECT_EQ(gamma, hooks.registerHook("gamma"));
+ EXPECT_EQ(gamma, hooks.registerHook("gamma"));
+ EXPECT_EQ(gamma, hooks.registerHook("gamma"));
+}
+
+// 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 (size_t 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");
+
+ 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 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);
+ EXPECT_EQ(-1, hooks.findIndex("unknown"));
+}
+
+// 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());
+}
+
+// Check that the hook name is correctly generated for a control command name
+// and vice versa.
+
+TEST(ServerHooksTest, CommandToHookName) {
+ EXPECT_EQ("$x_y_z", ServerHooks::commandToHookName("x-y-z"));
+ EXPECT_EQ("$foo_bar_foo", ServerHooks::commandToHookName("foo-bar_foo"));
+ EXPECT_EQ("$", ServerHooks::commandToHookName(""));
+}
+
+TEST(ServerHooksTest, HookToCommandName) {
+ // Underscores replaced by hyphens.
+ EXPECT_EQ("x-y-z", ServerHooks::hookToCommandName("$x_y_z"));
+ EXPECT_EQ("foo-bar-foo", ServerHooks::hookToCommandName("$foo_bar-foo"));
+ // Single dollar is converted to empty string.
+ EXPECT_TRUE(ServerHooks::hookToCommandName("$").empty());
+ // If no dollar, it is not a hook name. Return empty string.
+ EXPECT_TRUE(ServerHooks::hookToCommandName("abc").empty());
+}
+
+TEST(ServerHooksTest, getParkingLots) {
+ ServerHooks& hooks = ServerHooks::getServerHooks();
+ hooks.reset();
+ int alpha_hook = hooks.registerHook("alpha");
+
+ ASSERT_TRUE(hooks.getParkingLotsPtr());
+ ASSERT_TRUE(hooks.getParkingLotPtr(alpha_hook));
+ ASSERT_TRUE(hooks.getParkingLotPtr("alpha"));
+}
+
+} // 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..afad62f
--- /dev/null
+++ b/src/lib/hooks/tests/test_libraries.h.in
@@ -0,0 +1,61 @@
+// Copyright (C) 2013-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef TEST_LIBRARIES_H
+#define TEST_LIBRARIES_H
+
+#include <config.h>
+
+namespace {
+
+// 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.so";
+
+// 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.so";
+
+// Library where the all framework functions throw an exception
+static const char* FRAMEWORK_EXCEPTION_LIBRARY = "@abs_builddir@/.libs/libfxl.so";
+
+// Library where the version() function returns an incorrect result.
+static const char* INCORRECT_VERSION_LIBRARY = "@abs_builddir@/.libs/libivl.so";
+
+// Library where some of the callout registration is done with the load()
+// function.
+static const char* LOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/liblcl.so";
+
+// Library where the load() function returns an error.
+static const char* LOAD_ERROR_CALLOUT_LIBRARY =
+ "@abs_builddir@/.libs/liblecl.so";
+
+// Name of a library which is not present.
+static const char* NOT_PRESENT_LIBRARY = "@abs_builddir@/.libs/libnothere.so";
+
+// Library that does not include a version function.
+static const char* NO_VERSION_LIBRARY = "@abs_builddir@/.libs/libnvl.so";
+
+// Library where there is an unload() function.
+static const char* UNLOAD_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libucl.so";
+
+// Library where parameters are checked.
+static const char* CALLOUT_PARAMS_LIBRARY = "@abs_builddir@/.libs/libpcl.so";
+
+// Library which tests objects parking.
+// Used only by hooks_manager_unittest.cc.
+#ifdef TEST_ASYNC_CALLOUT
+static const char* ASYNC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libacl.so";
+#endif
+
+} // 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..a15b870
--- /dev/null
+++ b/src/lib/hooks/tests/unload_callout_library.cc
@@ -0,0 +1,46 @@
+// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/// @file
+/// @brief 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 <config.h>
+
+#include <hooks/hooks.h>
+#include <hooks/tests/marker_file.h>
+
+#include <fstream>
+
+using namespace isc::hooks;
+
+extern "C" {
+
+// Framework functions
+
+int
+version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+int
+unload() {
+ // Create the marker file.
+ std::fstream marker;
+ marker.open(MARKER_FILE, std::fstream::out);
+ marker.close();
+
+ return (0);
+}
+
+};