Commit 19c93b08 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[2995] Merge branch 'trac2980' into trac2995

 - Also code updated to match changes from 2980

Conflicts:
	src/lib/hooks/callout_handle.h
parents bdeeb21d 43ea555f
......@@ -1402,8 +1402,8 @@ AC_OUTPUT([doc/version.ent
src/lib/cc/session_config.h.pre
src/lib/cc/tests/session_unittests_config.h
src/lib/datasrc/datasrc_config.h.pre
src/lib/hooks/tests/library_manager_unittest.cc
src/lib/hooks/tests/marker_file.h
src/lib/hooks/tests/test_libraries.h
src/lib/log/tests/console_test.sh
src/lib/log/tests/destination_test.sh
src/lib/log/tests/init_logger_test.sh
......
......@@ -679,6 +679,7 @@ INPUT = ../src/lib/exceptions \
../src/lib/cache \
../src/lib/server_common/ \
../src/bin/sockcreator/ \
../src/lib/hooks/ \
../src/lib/util/ \
../src/lib/util/io/ \
../src/lib/util/threads/ \
......
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
/**
*
* @mainpage BIND10 Developer's Guide
*
* Welcome to BIND10 Developer's Guide. This documentation is addressed
* at existing and prospecting developers and programmers, who would like
* to gain insight into internal workings of BIND 10. It could also be useful
* for existing and prospective contributors.
* at existing and prospecting developers and programmers and provides
* information needed to both extend and maintain BIND 10.
*
* If you wish to write "hook" code - code that is loaded by BIND 10 at
* run-time and modifies its behavior you should read the section
* @ref hookDevelopersGuide.
*
* BIND 10 maintanenace information is divided into a number of sections
* depending on focus. DNS-specific issues are covered in the
* @ref dnsMaintenanceGuide while information on DHCP-specific topics can
* be found in the @ref dhcpMaintenanceGuide. General BIND 10 topics, not
* specific to any protocol, are discussed in @ref miscellaneousTopics.
*
* If you are a user or system administrator, rather than software engineer,
* you should read <a href="http://bind10.isc.org/docs/bind10-guide.html">BIND10
......@@ -13,13 +35,15 @@
*
* Regardless of your field of expertise, you are encouraged to visit
* <a href="http://bind10.isc.org/">BIND10 webpage (http://bind10.isc.org)</a>
* @section hooksFramework Hooks Framework
* - @subpage hooksComponentDeveloperGuide
*
* @section DNS
* @section dnsMaintenanceGuide DNS Maintenance Guide
* - Authoritative DNS (todo)
* - Recursive resolver (todo)
* - @subpage DataScrubbing
*
* @section DHCP
* @section dhcpMaintenanceGuide DHCP Maintenance Guide
* - @subpage dhcp4
* - @subpage dhcpv4Session
* - @subpage dhcpv4ConfigParser
......@@ -40,7 +64,7 @@
* - @subpage dhcpDatabaseBackends
* - @subpage perfdhcpInternals
*
* @section misc Miscellaneous topics
* @section miscellaneousTopics Miscellaneous topics
* - @subpage LoggingApi
* - @subpage LoggingApiOverview
* - @subpage LoggingApiLoggerNames
......@@ -48,7 +72,10 @@
* - @subpage SocketSessionUtility
* - <a href="./doxygen-error.log">Documentation warnings and errors</a>
*
* @todo: Move this logo to the right (and possibly up). Not sure what
* is the best way to do it in Doxygen, without using CSS hacks.
* @image html isc-logo.png
*/
/*
* @todo: Move the logo to the right (and possibly up). Not sure what
* is the best way to do it in Doxygen, without using CSS hacks.
*/
......@@ -37,7 +37,6 @@
#include <util/io_utilities.h>
#include <util/range_utilities.h>
#include <util/encode/hex.h>
#include <hooks/server_hooks.h>
#include <hooks/hooks_manager.h>
#include <hooks/callout_handle.h>
......@@ -113,13 +112,13 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
alloc_engine_.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100));
// Register hook points
hook_index_pkt6_receive_ = ServerHooks::getServerHooks().registerHook("pkt6_receive");
hook_index_subnet6_select_ = ServerHooks::getServerHooks().registerHook("subnet6_select");
hook_index_pkt6_send_ = ServerHooks::getServerHooks().registerHook("pkt6_send");
hook_index_pkt6_receive_ = HooksManager::registerHook("pkt6_receive");
hook_index_subnet6_select_ = HooksManager::registerHook("subnet6_select");
hook_index_pkt6_send_ = HooksManager::registerHook("pkt6_send");
/// @todo call loadLibraries() when handling configuration changes
vector<string> libraries; // no libraries at this time
HooksManager::getHooksManager().loadLibraries(libraries);
HooksManager::loadLibraries(libraries);
} catch (const std::exception &e) {
LOG_ERROR(dhcp6_logger, DHCP6_SRV_CONSTRUCT_ERROR).arg(e.what());
......
......@@ -36,7 +36,6 @@
#include <util/range_utilities.h>
#include <hooks/server_hooks.h>
#include <hooks/callout_manager.h>
#include <hooks/hooks_manager.h>
#include <boost/scoped_ptr.hpp>
......@@ -1912,9 +1911,6 @@ public:
// clear static buffers
resetCalloutBuffers();
// Let's pretent we're the library 0
EXPECT_NO_THROW(HooksManager::getCalloutManager()->setLibraryIndex(0));
}
~HooksDhcpv6SrvTest() {
......@@ -2116,8 +2112,8 @@ vector<string> HooksDhcpv6SrvTest::callback_argument_names_;
TEST_F(HooksDhcpv6SrvTest, simple_pkt6_receive) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("pkt6_receive",
pkt6_receive_callout));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"pkt6_receive", pkt6_receive_callout));
// Let's create a simple SOLICIT
Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
......@@ -2149,8 +2145,8 @@ TEST_F(HooksDhcpv6SrvTest, simple_pkt6_receive) {
TEST_F(HooksDhcpv6SrvTest, valueChange_pkt6_receive) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("pkt6_receive",
pkt6_receive_change_clientid));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"pkt6_receive", pkt6_receive_change_clientid));
// Let's create a simple SOLICIT
Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
......@@ -2185,8 +2181,8 @@ TEST_F(HooksDhcpv6SrvTest, valueChange_pkt6_receive) {
TEST_F(HooksDhcpv6SrvTest, deleteClientId_pkt6_receive) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("pkt6_receive",
pkt6_receive_delete_clientid));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"pkt6_receive", pkt6_receive_delete_clientid));
// Let's create a simple SOLICIT
Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
......@@ -2209,8 +2205,8 @@ TEST_F(HooksDhcpv6SrvTest, deleteClientId_pkt6_receive) {
TEST_F(HooksDhcpv6SrvTest, skip_pkt6_receive) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("pkt6_receive",
pkt6_receive_skip));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"pkt6_receive", pkt6_receive_skip));
// Let's create a simple SOLICIT
Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
......@@ -2234,8 +2230,8 @@ TEST_F(HooksDhcpv6SrvTest, skip_pkt6_receive) {
TEST_F(HooksDhcpv6SrvTest, simple_pkt6_send) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("pkt6_send",
pkt6_send_callout));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"pkt6_send", pkt6_send_callout));
// Let's create a simple SOLICIT
Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
......@@ -2270,8 +2266,8 @@ TEST_F(HooksDhcpv6SrvTest, simple_pkt6_send) {
TEST_F(HooksDhcpv6SrvTest, valueChange_pkt6_send) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("pkt6_send",
pkt6_send_change_serverid));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"pkt6_send", pkt6_send_change_serverid));
// Let's create a simple SOLICIT
Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
......@@ -2307,8 +2303,8 @@ TEST_F(HooksDhcpv6SrvTest, valueChange_pkt6_send) {
TEST_F(HooksDhcpv6SrvTest, deleteServerId_pkt6_send) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("pkt6_send",
pkt6_send_delete_serverid));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"pkt6_send", pkt6_send_delete_serverid));
// Let's create a simple SOLICIT
Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
......@@ -2338,8 +2334,8 @@ TEST_F(HooksDhcpv6SrvTest, deleteServerId_pkt6_send) {
TEST_F(HooksDhcpv6SrvTest, skip_pkt6_send) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("pkt6_send",
pkt6_send_skip));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"pkt6_send", pkt6_send_skip));
// Let's create a simple REQUEST
Pkt6Ptr sol = Pkt6Ptr(captureSimpleSolicit());
......@@ -2362,8 +2358,8 @@ TEST_F(HooksDhcpv6SrvTest, skip_pkt6_send) {
TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("subnet6_select",
subnet6_select_callout));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"subnet6_select", subnet6_select_callout));
// Configure 2 subnets, both directly reachable over local interface
// (let's not complicate the matter with relays)
......@@ -2430,8 +2426,8 @@ TEST_F(HooksDhcpv6SrvTest, subnet6_select) {
TEST_F(HooksDhcpv6SrvTest, subnet_select_change) {
// Install pkt6_receive_callout
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("subnet6_select",
subnet6_select_different_subnet_callout));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"subnet6_select", subnet6_select_different_subnet_callout));
// Configure 2 subnets, both directly reachable over local interface
// (let's not complicate the matter with relays)
......
......@@ -1154,10 +1154,8 @@ TEST_F(HookAllocEngine6Test, lease6_select) {
HooksManager::getHooksManager().loadLibraries(libraries);
// Install pkt6_receive_callout
ASSERT_TRUE(HooksManager::getCalloutManager());
EXPECT_NO_THROW(HooksManager::getCalloutManager()->setLibraryIndex(0));
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("lease6_select",
lease6_select_callout));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"lease6_select", lease6_select_callout));
CalloutHandlePtr callout_handle = HooksManager::getHooksManager().createCalloutHandle();
......@@ -1217,10 +1215,8 @@ TEST_F(HookAllocEngine6Test, change_lease6_select) {
HooksManager::getHooksManager().loadLibraries(libraries);
// Install a callout
ASSERT_TRUE(HooksManager::getCalloutManager());
EXPECT_NO_THROW(HooksManager::getCalloutManager()->setLibraryIndex(0));
EXPECT_NO_THROW(HooksManager::getCalloutManager()->registerCallout("lease6_select",
lease6_select_different_callout));
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"lease6_select", lease6_select_different_callout));
// Normally, dhcpv6_srv would passed the handle when calling allocateAddress6,
// but in tests we need to create it on our own.
......
......@@ -31,10 +31,11 @@ libb10_hooks_la_SOURCES += callout_handle.cc callout_handle.h
libb10_hooks_la_SOURCES += callout_manager.cc callout_manager.h
libb10_hooks_la_SOURCES += hooks.h
libb10_hooks_la_SOURCES += hooks_log.cc hooks_log.h
libb10_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
libb10_hooks_la_SOURCES += library_handle.cc library_handle.h
libb10_hooks_la_SOURCES += library_manager.cc library_manager.h
libb10_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h
libb10_hooks_la_SOURCES += server_hooks.cc server_hooks.h
libb10_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
nodist_libb10_hooks_la_SOURCES = hooks_messages.cc hooks_messages.h
......
......@@ -27,8 +27,10 @@ namespace isc {
namespace hooks {
// Constructor.
CalloutHandle::CalloutHandle(const boost::shared_ptr<CalloutManager>& manager)
: arguments_(), context_collection_(), manager_(manager), skip_(false) {
CalloutHandle::CalloutHandle(const boost::shared_ptr<CalloutManager>& manager,
const boost::shared_ptr<LibraryManagerCollection>& lmcoll)
: lm_collection_(lmcoll), arguments_(), context_collection_(),
manager_(manager), skip_(false) {
// Call the "context_create" hook. We should be OK doing this - although
// the constructor has not finished running, all the member variables
......@@ -43,6 +45,24 @@ CalloutHandle::~CalloutHandle() {
// 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.
......@@ -130,8 +150,9 @@ CalloutHandle::getHookName() const {
try {
hook = ServerHooks::getServerHooks().getName(index);
} catch (const NoSuchHook&) {
// Hook index is invalid, so probably called outside of a callout.
// This is a no-op.
// 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);
......
......@@ -54,6 +54,7 @@ public:
class CalloutManager;
class LibraryHandle;
class LibraryManagerCollection;
/// @brief Per-packet callout handle
///
......@@ -79,11 +80,11 @@ class LibraryHandle;
/// "context_destroy" callout. The information is accessed through the
/// {get,set}Context() methods.
///
/// - Per-library handle. Allows the callout to dynamically register and
/// deregister callouts. (In the latter case, only functions registered by
/// functions in the same library as the callout doing the deregistration
/// can be removed: callouts registered by other libraries cannot be
/// modified.)
/// - Per-library handle (LibraryHandle). The library handle allows the
/// callout to dynamically register and deregister callouts. In the latter
/// case, only functions registered by functions in the same library as the
/// callout doing the deregistration can be removed: callouts registered by
/// other libraries cannot be modified.
class CalloutHandle {
public:
......@@ -110,12 +111,27 @@ public:
/// 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.
CalloutHandle(const boost::shared_ptr<CalloutManager>& manager);
/// @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
......@@ -152,7 +168,7 @@ public:
value = boost::any_cast<T>(element_ptr->second);
}
/// @brief Get argument names
///
/// Returns a vector holding the names of arguments in the argument
......@@ -257,7 +273,7 @@ public:
value = boost::any_cast<T>(element_ptr->second);
}
/// @brief Get context names
///
/// Returns a vector holding the names of items in the context associated
......@@ -340,6 +356,10 @@ private:
// 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_;
......@@ -353,10 +373,10 @@ private:
bool skip_;
};
/// a shared pointer to CalloutHandle object
/// A shared pointer to a CalloutHandle object.
typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;
} // namespace util
} // namespace hooks
} // namespace isc
......
......@@ -19,6 +19,7 @@
#include <boost/static_assert.hpp>
#include <algorithm>
#include <climits>
#include <functional>
#include <utility>
......@@ -27,12 +28,41 @@ using namespace std;
namespace isc {
namespace hooks {
// Check that the index of a library is valid. It can range from 1 - n
// (n is the number of libraries), 0 (pre-user library callouts), or INT_MAX
// (post-user library callouts). It can also be -1 to indicate an invalid
// value.
void
CalloutManager::checkLibraryIndex(int library_index) const {
if (((library_index >= -1) && (library_index <= num_libraries_)) ||
(library_index == INT_MAX)) {
return;
}
isc_throw(NoSuchLibrary, "library index " << library_index <<
" is not valid for the number of loaded libraries (" <<
num_libraries_ << ")");
}
// Set the number of libraries handled by the CalloutManager.
void
CalloutManager::setNumLibraries(int num_libraries) {
if (num_libraries < 0) {
isc_throw(isc::BadValue, "number of libraries passed to the "
"CalloutManager must be >= 0");
}
num_libraries_ = num_libraries;
}
// Register a callout for the current library.
void
CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
// Note the registration.
LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_REGISTER_CALLOUT)
LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUT_REGISTRATION)
.arg(current_library_).arg(name);
// Sanity check that the current library index is set to a valid value.
......@@ -108,15 +138,24 @@ CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
current_library_ = i->first;
// Call the callout
// @todo Log the return status if non-zero
int status = (*i->second)(callout_handle);
if (status == 0) {
LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS, HOOKS_CALLOUT)
.arg(current_library_)
.arg(ServerHooks::getServerHooks().getName(current_hook_))
.arg(reinterpret_cast<void*>(i->second));
} else {
LOG_WARN(hooks_logger, HOOKS_CALLOUT_ERROR)
try {
int status = (*i->second)(callout_handle);
if (status == 0) {
LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
HOOKS_CALLOUT_CALLED).arg(current_library_)
.arg(ServerHooks::getServerHooks()
.getName(current_hook_))
.arg(reinterpret_cast<void*>(i->second));
} else {
LOG_ERROR(hooks_logger, HOOKS_CALLOUT_ERROR)
.arg(current_library_)
.arg(ServerHooks::getServerHooks()
.getName(current_hook_))
.arg(reinterpret_cast<void*>(i->second));
}
} catch (...) {
// Any exception, not just ones based on isc::Exception
LOG_ERROR(hooks_logger, HOOKS_CALLOUT_EXCEPTION)
.arg(current_library_)
.arg(ServerHooks::getServerHooks().getName(current_hook_))
.arg(reinterpret_cast<void*>(i->second));
......@@ -170,7 +209,7 @@ CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
bool removed = initial_size != hook_vector_[hook_index].size();
if (removed) {
LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
HOOKS_DEREGISTER_CALLOUT).arg(current_library_).arg(name);
HOOKS_CALLOUT_DEREGISTERED).arg(current_library_).arg(name);
}
return (removed);
......@@ -205,7 +244,7 @@ CalloutManager::deregisterAllCallouts(const std::string& name) {
bool removed = initial_size != hook_vector_[hook_index].size();
if (removed) {
LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
HOOKS_DEREGISTER_ALL_CALLOUTS).arg(current_library_)
HOOKS_ALL_CALLOUTS_DEREGISTERED).arg(current_library_)
.arg(name);
}
......
......@@ -21,6 +21,7 @@
#include <boost/shared_ptr.hpp>
#include <climits>
#include <map>
#include <string>
......@@ -37,11 +38,11 @@ public:
isc::Exception(file, line, what) {}
};
/// @brief Callout Manager
///
/// This class manages the registration, deregistration and execution of the
/// library callouts.
/// library callouts. It is part of the hooks framework used by the BIND 10
/// server, and is not for use by user-written code in a hooks library.
///
/// In operation, the class needs to know two items of data:
///
......@@ -59,14 +60,51 @@ public:
/// deregister callouts in the same library (including themselves): they
/// cannot affect callouts registered by another library. When calling a
/// callout, the callout manager maintains the idea of a "current library
/// index": if the callout calls one of the callout registration functions
/// index": if the callout calls one of the callout registration functions
/// (indirectly via the @ref LibraryHandle object), the registration
/// functions use the "current library index" in their processing.
///
/// These two items of data are supplied when an object of this class is
/// constructed.
/// constructed. The latter (number of libraries) can be updated after the
/// class is constructed. (Such an update is used during library loading where
/// the CalloutManager has to be constructed before the libraries are loaded,
/// but one of the libraries subsequently fails to load.)
///
/// The library index is important because it determines in what order callouts
/// on a particular hook are called. For each hook, the CalloutManager
/// maintains a vector of callouts ordered by library index. When a callout
/// is added to the list, it is added at the end of the callouts associated
/// with the current library. To clarify this further, suppose that three
/// libraries are loaded, A (assigned an index 1), B (assigned an index 2) and
/// C (assigned an index 3). Suppose A registers two callouts on a given hook,
/// A1 and A2 (in that order) B registers B1 and B2 (in that order) and C
/// registers C1 and C2 (in that order). Internally, the callouts are stored
/// in the order A1, A2, B1, B2, C1, and C2: this is also the order in which
/// the are called. If B now registers another callout (B3), it is added to
/// the vector after the list of callouts associated with B: the new order is
/// therefore A1, A2, B1, B2, B3, C1 and C2.
///
/// Indexes range between 1 and n (where n is the number of the libraries
/// loaded) and are assigned to libraries based on the order the libraries
/// presented to the hooks framework for loading (something that occurs in the
/// isc::util::HooksManager) class. However, two other indexes are recognised,
/// 0 and INT_MAX. These are used when the server itself registers callouts -
/// the server is able to register callouts that get called before any
/// user-library callouts, and ones that get called after user-library callouts.
/// In other words, assuming the callouts on a hook are A1, A2, B1, B2, B3, C2,
/// C2 as before, and that the server registers S1 (to run before the
/// user-registered callouts) and S2 (to run after them), the callouts are
/// stored (and executed) in the order S1, A1, A2, B1, B2, B3, C2, C2, S2. In
/// summary, the recognised index values are:
///
/// - < 0: invalid.
/// - 0: used for server-registered callouts that are called before
/// user-registered callouts.
/// - 1 - n: callouts from user libraries.
/// - INT_MAX: used for server-registered callouts called after
/// user-registered callouts.
///
/// Note that the callout function do not access the library manager: instead,
/// 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.
......@@ -96,17 +134,16 @@ public:
///
/// @throw isc::BadValue if the number of libraries is less than or equal
/// to 0, or if the pointer to the server hooks object is empty.
CalloutManager(int num_libraries)
: current_hook_(-1), current_library_(-1), hook_vector_(),
library_handle_(this), num_libraries_(num_libraries)
CalloutManager(int num_libraries = 0)
: current_hook_(-1), current_library_(-1),
hook_vector_(ServerHooks::getServerHooks().getCount()),
library_handle_(this), pre_library_handle_(this, 0),
post_library_handle_(this, INT_MAX), num_libraries_(num_libraries)
{
if (num_libraries <= 0) {