Commit 82c997a7 authored by Stephen Morris's avatar Stephen Morris
Browse files

[master] Merge branch 'trac2980'

parents 4ae6b8af 3430cf9a
......@@ -430,6 +430,7 @@ AC_SUBST(B10_CXXFLAGS)
AC_SEARCH_LIBS(inet_pton, [nsl])
AC_SEARCH_LIBS(recvfrom, [socket])
AC_SEARCH_LIBS(nanosleep, [rt])
AC_SEARCH_LIBS(dlsym, [dl])
# Checks for header files.
......@@ -469,6 +470,17 @@ AM_COND_IF([OS_BSD], [AC_DEFINE([OS_BSD], [1], [Running on BSD?])])
AM_CONDITIONAL(OS_SOLARIS, test $OS_TYPE = Solaris)
AM_COND_IF([OS_SOLARIS], [AC_DEFINE([OS_SOLARIS], [1], [Running on Solaris?])])
# Deal with variants
AM_CONDITIONAL(OS_FREEBSD, test $system = FreeBSD)
AM_COND_IF([OS_FREEBSD], [AC_DEFINE([OS_FREEBSD], [1], [Running on FreeBSD?])])
AM_CONDITIONAL(OS_NETBSD, test $system = NetBSD)
AM_COND_IF([OS_NETBSD], [AC_DEFINE([OS_NETBSD], [1], [Running on NetBSD?])])
AM_CONDITIONAL(OS_OPENBSD, test $system = OpenBSD)
AM_COND_IF([OS_OPENBSD], [AC_DEFINE([OS_OPENBSD], [1], [Running on OpenBSD?])])
AM_CONDITIONAL(OS_OSX, test $system = Darwin)
AM_COND_IF([OS_OSX], [AC_DEFINE([OS_OSX], [1], [Running on OSX?])])
AC_MSG_CHECKING(for sa_len in struct sockaddr)
AC_TRY_COMPILE([
#include <sys/types.h>
......@@ -1317,6 +1329,8 @@ AC_CONFIG_FILES([Makefile
src/lib/datasrc/tests/memory/testdata/Makefile
src/lib/xfr/Makefile
src/lib/xfr/tests/Makefile
src/lib/hooks/Makefile
src/lib/hooks/tests/Makefile
src/lib/log/Makefile
src/lib/log/interprocess/Makefile
src/lib/log/interprocess/tests/Makefile
......@@ -1423,6 +1437,8 @@ AC_OUTPUT([doc/version.ent
src/lib/cc/session_config.h.pre
src/lib/cc/tests/session_unittests_config.h
src/lib/datasrc/datasrc_config.h.pre
src/lib/hooks/tests/marker_file.h
src/lib/hooks/tests/test_libraries.h
src/lib/log/tests/console_test.sh
src/lib/log/tests/destination_test.sh
src/lib/log/tests/init_logger_test.sh
......
......@@ -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
......@@ -39,7 +63,7 @@
* - @subpage dhcpDatabaseBackends
* - @subpage perfdhcpInternals
*
* @section misc Miscellaneous topics
* @section miscellaneousTopics Miscellaneous topics
* - @subpage LoggingApi
* - @subpage LoggingApiOverview
* - @subpage LoggingApiLoggerNames
......@@ -47,7 +71,10 @@
* - @subpage SocketSessionUtility
* - <a href="./doxygen-error.log">Documentation warnings and errors</a>
*
* @todo: Move this logo to the right (and possibly up). Not sure what
* is the best way to do it in Doxygen, without using CSS hacks.
* @image html isc-logo.png
*/
/*
* @todo: Move the logo to the right (and possibly up). Not sure what
* is the best way to do it in Doxygen, without using CSS hacks.
*/
SUBDIRS = exceptions util log cryptolink dns cc config acl xfr bench \
SUBDIRS = exceptions util log hooks cryptolink dns cc config acl xfr bench \
asiolink asiodns nsas cache resolve testutils datasrc \
server_common python dhcp dhcpsrv statistics
SUBDIRS = . tests
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
AM_CPPFLAGS += $(BOOST_INCLUDES)
AM_CXXFLAGS = $(B10_CXXFLAGS)
# Some versions of GCC warn about some versions of Boost regarding
# missing initializer for members in its posix_time.
# https://svn.boost.org/trac/boost/ticket/3477
# But older GCC compilers don't have the flag.
AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
# Define rule to build logging source files from message file
hooks_messages.h hooks_messages.cc: hooks_messages.mes
$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/hooks/hooks_messages.mes
# Tell automake that the message files are built as part of the build process
# (so that they are built before the main library is built).
BUILT_SOURCES = hooks_messages.h hooks_messages.cc
# Ensure that the message file is included in the distribution
EXTRA_DIST = hooks_messages.mes
# Get rid of generated message files on a clean
CLEANFILES = *.gcno *.gcda hooks_messages.h hooks_messages.cc
lib_LTLIBRARIES = libb10-hooks.la
libb10_hooks_la_SOURCES =
libb10_hooks_la_SOURCES += callout_handle.cc callout_handle.h
libb10_hooks_la_SOURCES += callout_manager.cc callout_manager.h
libb10_hooks_la_SOURCES += hooks.h
libb10_hooks_la_SOURCES += hooks_log.cc hooks_log.h
libb10_hooks_la_SOURCES += hooks_manager.cc hooks_manager.h
libb10_hooks_la_SOURCES += library_handle.cc library_handle.h
libb10_hooks_la_SOURCES += library_manager.cc library_manager.h
libb10_hooks_la_SOURCES += library_manager_collection.cc library_manager_collection.h
libb10_hooks_la_SOURCES += pointer_converter.h
libb10_hooks_la_SOURCES += server_hooks.cc server_hooks.h
nodist_libb10_hooks_la_SOURCES = hooks_messages.cc hooks_messages.h
libb10_hooks_la_CXXFLAGS = $(AM_CXXFLAGS)
libb10_hooks_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
libb10_hooks_la_LDFLAGS = $(AM_LDFLAGS)
libb10_hooks_la_LIBADD =
libb10_hooks_la_LIBADD += $(top_builddir)/src/lib/log/libb10-log.la
libb10_hooks_la_LIBADD += $(top_builddir)/src/lib/util/libb10-util.la
libb10_hooks_la_LIBADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
if USE_CLANGPP
# Disable unused parameter warning caused by some of the
# Boost headers when compiling with clang.
libb10_hooks_la_CXXFLAGS += -Wno-unused-parameter
endif
......@@ -12,24 +12,25 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/hooks/callout_handle.h>
#include <util/hooks/callout_manager.h>
#include <util/hooks/library_handle.h>
#include <util/hooks/server_hooks.h>
#include <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;
using namespace isc::util;
namespace isc {
namespace util {
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
......@@ -44,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.
......@@ -117,5 +136,27 @@ CalloutHandle::getContextNames() const {
return (names);
}
// Return name of current hook (the hook to which the current callout is
// attached) or the empty string if not called within the context of a
// callout.
string
CalloutHandle::getHookName() const {
// Get the current hook index.
int index = manager_->getHookIndex();
// ... and look up the hook.
string hook = "";
try {
hook = ServerHooks::getServerHooks().getName(index);
} catch (const NoSuchHook&) {
// Hook index is invalid, so this methods probably called from outside
// a callout being executed via a call to CalloutManager::callCallouts.
// In this case, the empty string is returned.
}
return (hook);
}
} // namespace util
} // namespace isc
......@@ -16,7 +16,7 @@
#define CALLOUT_HANDLE_H
#include <exceptions/exceptions.h>
#include <util/hooks/library_handle.h>
#include <hooks/library_handle.h>
#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>
......@@ -26,7 +26,7 @@
#include <vector>
namespace isc {
namespace util {
namespace hooks {
/// @brief No such argument
///
......@@ -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:
......@@ -105,18 +106,32 @@ public:
/// 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.
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
......@@ -153,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
......@@ -258,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
......@@ -292,6 +307,14 @@ public:
getContextForLibrary().clear();
}
/// @brief Get hook name
///
/// Get the name of the hook to which the current callout is attached.
/// This can be the null string if the CalloutHandle is being accessed
/// outside of the CalloutManager's "callCallouts" method.
///
/// @return Name of the current hook or the empty string if none.
std::string getHookName() const;
private:
/// @brief Check index
......@@ -333,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_;
......@@ -346,7 +373,10 @@ private:
bool skip_;
};
} // namespace util
/// A shared pointer to a CalloutHandle object.
typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;
} // namespace hooks
} // namespace isc
......
......@@ -12,31 +12,66 @@
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/hooks/callout_handle.h>
#include <util/hooks/callout_manager.h>
#include <hooks/callout_handle.h>
#include <hooks/callout_manager.h>
#include <hooks/hooks_log.h>
#include <hooks/pointer_converter.h>
#include <boost/static_assert.hpp>
#include <algorithm>
#include <climits>
#include <functional>
#include <utility>
using namespace std;
using namespace isc::util;
namespace isc {
namespace util {
namespace hooks {
// Check that the index of a library is valid. It can range from 1 - n
// (n is the number of libraries), 0 (pre-user library callouts), or INT_MAX
// (post-user library callouts). It can also be -1 to indicate an invalid
// value.
void
CalloutManager::checkLibraryIndex(int library_index) const {
if (((library_index >= -1) && (library_index <= num_libraries_)) ||
(library_index == INT_MAX)) {
return;
}
isc_throw(NoSuchLibrary, "library index " << library_index <<
" is not valid for the number of loaded libraries (" <<
num_libraries_ << ")");
}
// Set the number of libraries handled by the CalloutManager.
void
CalloutManager::setNumLibraries(int num_libraries) {
if (num_libraries < 0) {
isc_throw(isc::BadValue, "number of libraries passed to the "
"CalloutManager must be >= 0");
}
num_libraries_ = num_libraries;
}
// Register a callout for the current library.
void
CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
// Note the registration.
LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUT_REGISTRATION)
.arg(current_library_).arg(name);
// Sanity check that the current library index is set to a valid value.
checkLibraryIndex(current_library_);
// Get the index associated with this hook (validating the name in the
// process).
int hook_index = hooks_->getIndex(name);
int hook_index = ServerHooks::getServerHooks().getIndex(name);
// Iterate through the callout vector for the hook from start to end,
// looking for the first entry where the library index is greater than
......@@ -84,6 +119,10 @@ CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
// Clear the "skip" flag so we don't carry state from a previous call.
callout_handle.setSkip(false);
// Set the current hook index. This is used should a callout wish to
// determine to what hook it is attached.
current_hook_ = hook_index;
// Duplicate the callout vector for this hook and work through that.
// This step is needed because we allow dynamic registration and
// deregistration of callouts. If a callout attached to a hook modified
......@@ -100,12 +139,34 @@ CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
current_library_ = i->first;
// Call the callout
// @todo Log the return status if non-zero
static_cast<void>((*i->second)(callout_handle));
try {
int status = (*i->second)(callout_handle);
if (status == 0) {
LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
HOOKS_CALLOUT_CALLED).arg(current_library_)
.arg(ServerHooks::getServerHooks()
.getName(current_hook_))
.arg(PointerConverter(i->second).dlsymPtr());
} else {
LOG_ERROR(hooks_logger, HOOKS_CALLOUT_ERROR)
.arg(current_library_)
.arg(ServerHooks::getServerHooks()
.getName(current_hook_))
.arg(PointerConverter(i->second).dlsymPtr());
}
} catch (...) {
// Any exception, not just ones based on isc::Exception
LOG_ERROR(hooks_logger, HOOKS_CALLOUT_EXCEPTION)
.arg(current_library_)
.arg(ServerHooks::getServerHooks().getName(current_hook_))
.arg(PointerConverter(i->second).dlsymPtr());
}
}
// Reset the current library index to an invalid value to catch any
// programming errors.
// Reset the current hook and library indexs to an invalid value to
// catch any programming errors.
current_hook_ = -1;
current_library_ = -1;
}
}
......@@ -119,7 +180,7 @@ CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
// Get the index associated with this hook (validating the name in the
// process).
int hook_index = hooks_->getIndex(name);
int hook_index = ServerHooks::getServerHooks().getIndex(name);
/// Construct a CalloutEntry matching the current library and the callout
/// we want to remove.
......@@ -146,7 +207,13 @@ CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
hook_vector_[hook_index].end());
// Return an indication of whether anything was removed.
return (initial_size != hook_vector_[hook_index].size());
bool removed = initial_size != hook_vector_[hook_index].size();
if (removed) {
LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
HOOKS_CALLOUT_DEREGISTERED).arg(current_library_).arg(name);
}
return (removed);
}
// Deregister all callouts on a given hook.
......@@ -156,7 +223,7 @@ CalloutManager::deregisterAllCallouts(const std::string& name) {
// Get the index associated with this hook (validating the name in the
// process).
int hook_index = hooks_->getIndex(name);
int hook_index = ServerHooks::getServerHooks().getIndex(name);
/// Construct a CalloutEntry matching the current library (the callout
/// pointer is NULL as we are not checking that).
......@@ -175,7 +242,14 @@ CalloutManager::deregisterAllCallouts(const std::string& name) {
hook_vector_[hook_index].end());
// Return an indication of whether anything was removed.
return (initial_size != hook_vector_[hook_index].size());
bool removed = initial_size != hook_vector_[hook_index].size();
if (removed) {
LOG_DEBUG(hooks_logger, HOOKS_DBG_EXTENDED_CALLS,
HOOKS_ALL_CALLOUTS_DEREGISTERED).arg(current_library_)
.arg(name);
}
return (removed);
}
} // namespace util
......
......@@ -16,16 +16,17 @@
#define CALLOUT_MANAGER_H
#include <exceptions/exceptions.h>
#include <util/hooks/library_handle.h>
#include <util/hooks/server_hooks.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 util {
namespace hooks {
/// @brief No such library
///
......@@ -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