Commit d2b10758 authored by Stephen Morris's avatar Stephen Morris
Browse files

[master] Merge branch 'trac2974'

parents a2a6bcce 3c7a0170
......@@ -37,6 +37,10 @@ libb10_util_la_SOURCES += encode/base32hex_from_binary.h
libb10_util_la_SOURCES += encode/base_n.cc encode/hex.h
libb10_util_la_SOURCES += encode/binary_from_base32hex.h
libb10_util_la_SOURCES += encode/binary_from_base16.h
libb10_util_la_SOURCES += hooks/callout_manager.h hooks/callout_manager.cc
libb10_util_la_SOURCES += hooks/callout_handle.h hooks/callout_handle.cc
libb10_util_la_SOURCES += hooks/library_handle.h hooks/library_handle.cc
libb10_util_la_SOURCES += hooks/server_hooks.h hooks/server_hooks.cc
libb10_util_la_SOURCES += random/qid_gen.h random/qid_gen.cc
libb10_util_la_SOURCES += random/random_number_generator.h
......
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/hooks/callout_handle.h>
#include <util/hooks/callout_manager.h>
#include <util/hooks/library_handle.h>
#include <util/hooks/server_hooks.h>
#include <string>
#include <utility>
#include <vector>
using namespace std;
using namespace isc::util;
namespace isc {
namespace util {
// Constructor.
CalloutHandle::CalloutHandle(const boost::shared_ptr<CalloutManager>& manager)
: arguments_(), context_collection_(), manager_(manager), skip_(false) {
// Call the "context_create" hook. We should be OK doing this - although
// the constructor has not finished running, all the member variables
// have been created.
manager_->callCallouts(ServerHooks::CONTEXT_CREATE, *this);
}
// Destructor
CalloutHandle::~CalloutHandle() {
// Call the "context_destroy" hook. We should be OK doing this - although
// the destructor is being called, all the member variables are still in
// existence.
manager_->callCallouts(ServerHooks::CONTEXT_DESTROY, *this);
}
// Return the name of all argument items.
vector<string>
CalloutHandle::getArgumentNames() const {
vector<string> names;
for (ElementCollection::const_iterator i = arguments_.begin();
i != arguments_.end(); ++i) {
names.push_back(i->first);
}
return (names);
}
// Return the library handle allowing the callout to access the CalloutManager
// registration/deregistration functions.
LibraryHandle&
CalloutHandle::getLibraryHandle() const {
return (manager_->getLibraryHandle());
}
// Return the context for the currently pointed-to library. This version is
// used by the "setContext()" method and creates a context for the current
// library if it does not exist.
CalloutHandle::ElementCollection&
CalloutHandle::getContextForLibrary() {
int libindex = manager_->getLibraryIndex();
// Access a reference to the element collection for the given index,
// creating a new element collection if necessary, and return it.
return (context_collection_[libindex]);
}
// The "const" version of the above, used by the "getContext()" method. If
// the context for the current library doesn't exist, throw an exception.
const CalloutHandle::ElementCollection&
CalloutHandle::getContextForLibrary() const {
int libindex = manager_->getLibraryIndex();
ContextCollection::const_iterator libcontext =
context_collection_.find(libindex);
if (libcontext == context_collection_.end()) {
isc_throw(NoSuchCalloutContext, "unable to find callout context "
"associated with the current library index (" << libindex <<
")");
}
// Return a reference to the context's element collection.
return (libcontext->second);
}
// Return the name of all items in the context associated with the current]
// library.
vector<string>
CalloutHandle::getContextNames() const {
vector<string> names;
const ElementCollection& elements = getContextForLibrary();
for (ElementCollection::const_iterator i = elements.begin();
i != elements.end(); ++i) {
names.push_back(i->first);
}
return (names);
}
} // namespace util
} // namespace isc
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef CALLOUT_HANDLE_H
#define CALLOUT_HANDLE_H
#include <exceptions/exceptions.h>
#include <util/hooks/library_handle.h>
#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>
#include <map>
#include <string>
#include <vector>
namespace isc {
namespace util {
/// @brief No such argument
///
/// Thrown if an attempt is made access an argument that does not exist.
class NoSuchArgument : public Exception {
public:
NoSuchArgument(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
/// @brief No such callout context item
///
/// Thrown if an attempt is made to get an item of data from this callout's
/// context and either the context or an item in the context with that name
/// does not exist.
class NoSuchCalloutContext : public Exception {
public:
NoSuchCalloutContext(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
};
// Forward declaration of the library handle and related collection classes.
class CalloutManager;
class LibraryHandle;
/// @brief Per-packet callout handle
///
/// An object of this class is associated with every packet (or request)
/// processed by the server. It forms the principle means of passing data
/// between the server and the user-library callouts.
///
/// The class allows access to the following information:
///
/// - Arguments. When the callouts associated with a hook are called, they
/// are passed information by the server (and can return information to it)
/// through name/value pairs. Each of these pairs is an argument and the
/// information is accessed through the {get,set}Argument() methods.
///
/// - Per-packet context. Each packet has a context associated with it, this
/// context being on a per-library basis. In other words, As a packet passes
/// through the callouts associated with a given library, the callouts can
/// associate and retrieve information with the packet. The per-library
/// nature of the context means that the callouts within a given library can
/// pass packet-specific information between one another, but they cannot pass
/// information to callous within another library. Typically such context
/// is created in the "context_create" callout and destroyed in the
/// "context_destroy" callout. The information is accessed through the
/// {get,set}Context() methods.
///
/// - Per-library handle. Allows the callout to dynamically register and
/// deregister callouts. (In the latter case, only functions registered by
/// functions in the same library as the callout doing the deregistration
/// can be removed: callouts registered by other libraries cannot be
/// modified.)
class CalloutHandle {
public:
/// Typedef to allow abbreviation of iterator specification in methods.
/// The std::string is the argument name and the "boost::any" is the
/// corresponding value associated with it.
typedef std::map<std::string, boost::any> ElementCollection;
/// Typedef to allow abbreviations in specifications when accessing
/// context. The ElementCollection is the name/value collection for
/// a particular context. The "int" corresponds to the index of an
/// associated library - there is a 1:1 correspondence between libraries
/// and a name.value collection.
///
/// The collection of contexts is stored in a map, as not every library
/// will require creation of a context associated with each packet. In
/// addition, the structure is more flexible in that the size does not
/// need to be set when the CalloutHandle is constructed.
typedef std::map<int, ElementCollection> ContextCollection;
/// @brief Constructor
///
/// Creates the object and calls the callouts on the "context_create"
/// hook.
///
/// @param manager Pointer to the callout manager object.
CalloutHandle(const boost::shared_ptr<CalloutManager>& manager);
/// @brief Destructor
///
/// Calls the context_destroy callback to release any per-packet context.
~CalloutHandle();
/// @brief Set argument
///
/// Sets the value of an argument. The argument is created if it does not
/// already exist.
///
/// @param name Name of the argument.
/// @param value Value to set. That can be of any data type.
template <typename T>
void setArgument(const std::string& name, T value) {
arguments_[name] = value;
}
/// @brief Get argument
///
/// Gets the value of an argument.
///
/// @param name Name of the element in the argument list to get.
/// @param value [out] Value to set. The type of "value" is important:
/// it must match the type of the value set.
///
/// @throw NoSuchArgument No argument with the given name is present.
/// @throw boost::bad_any_cast An argument with the given name is present,
/// but the data type of the value is not the same as the type of
/// the variable provided to receive the value.
template <typename T>
void getArgument(const std::string& name, T& value) const {
ElementCollection::const_iterator element_ptr = arguments_.find(name);
if (element_ptr == arguments_.end()) {
isc_throw(NoSuchArgument, "unable to find argument with name " <<
name);
}
value = boost::any_cast<T>(element_ptr->second);
}
/// @brief Get argument names
///
/// Returns a vector holding the names of arguments in the argument
/// vector.
///
/// @return Vector of strings reflecting argument names.
std::vector<std::string> getArgumentNames() const;
/// @brief Delete argument
///
/// Deletes an argument of the given name. If an argument of that name
/// does not exist, the method is a no-op.
///
/// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
/// by this method.
///
/// @param name Name of the element in the argument list to set.
void deleteArgument(const std::string& name) {
static_cast<void>(arguments_.erase(name));
}
/// @brief Delete all arguments
///
/// Deletes all arguments associated with this context.
///
/// N.B. If any elements are raw pointers, the pointed-to data is NOT
/// deleted by this method.
void deleteAllArguments() {
arguments_.clear();
}
/// @brief Set skip flag
///
/// Sets the "skip" variable in the callout handle. This variable is
/// interrogated by the server to see if the remaining callouts associated
/// with the current hook should be bypassed.
///
/// @param skip New value of the "skip" flag.
void setSkip(bool skip) {
skip_ = skip;
}
/// @brief Get skip flag
///
/// Gets the current value of the "skip" flag.
///
/// @return Current value of the skip flag.
bool getSkip() const {
return (skip_);
}
/// @brief Access current library handle
///
/// Returns a reference to the current library handle. This function is
/// only available when called by a callout (which in turn is called
/// through the "callCallouts" method), as it is only then that the current
/// library index is valid. A callout uses the library handle to
/// dynamically register or deregister callouts.
///
/// @return Reference to the library handle.
///
/// @throw InvalidIndex thrown if this method is called when the current
/// library index is invalid (typically if it is called outside of
/// the active callout).
LibraryHandle& getLibraryHandle() const;
/// @brief Set context
///
/// Sets an element in the context associated with the current library. If
/// an element of the name is already present, it is replaced.
///
/// @param name Name of the element in the context to set.
/// @param value Value to set.
template <typename T>
void setContext(const std::string& name, T value) {
getContextForLibrary()[name] = value;
}
/// @brief Get context
///
/// Gets an element from the context associated with the current library.
///
/// @param name Name of the element in the context to get.
/// @param value [out] Value to set. The type of "value" is important:
/// it must match the type of the value set.
///
/// @throw NoSuchCalloutContext Thrown if no context element with the name
/// "name" is present.
/// @throw boost::bad_any_cast Thrown if the context element is present
/// but the type of the data is not the same as the type of the
/// variable provided to receive its value.
template <typename T>
void getContext(const std::string& name, T& value) const {
const ElementCollection& lib_context = getContextForLibrary();
ElementCollection::const_iterator element_ptr = lib_context.find(name);
if (element_ptr == lib_context.end()) {
isc_throw(NoSuchCalloutContext, "unable to find callout context "
"item " << name << " in the context associated with "
"current library");
}
value = boost::any_cast<T>(element_ptr->second);
}
/// @brief Get context names
///
/// Returns a vector holding the names of items in the context associated
/// with the current library.
///
/// @return Vector of strings reflecting the names of items in the callout
/// context associated with the current library.
std::vector<std::string> getContextNames() const;
/// @brief Delete context element
///
/// Deletes an item of the given name from the context associated with the
/// current library. If an item of that name does not exist, the method is
/// a no-op.
///
/// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
/// by this.
///
/// @param name Name of the context item to delete.
void deleteContext(const std::string& name) {
static_cast<void>(getContextForLibrary().erase(name));
}
/// @brief Delete all context items
///
/// Deletes all items from the context associated with the current library.
///
/// N.B. If any elements are raw pointers, the pointed-to data is NOT
/// deleted by this.
void deleteAllContext() {
getContextForLibrary().clear();
}
private:
/// @brief Check index
///
/// Gets the current library index, throwing an exception if it is not set
/// or is invalid for the current library collection.
///
/// @return Current library index, valid for this library collection.
///
/// @throw InvalidIndex current library index is not valid for the library
/// handle collection.
int getLibraryIndex() const;
/// @brief Return reference to context for current library
///
/// Called by all context-setting functions, this returns a reference to
/// the callout context for the current library, creating a context if it
/// does not exist.
///
/// @return Reference to the collection of name/value pairs associated
/// with the current library.
///
/// @throw InvalidIndex current library index is not valid for the library
/// handle collection.
ElementCollection& getContextForLibrary();
/// @brief Return reference to context for current library (const version)
///
/// Called by all context-accessing functions, this a reference to the
/// callout context for the current library. An exception is thrown if
/// it does not exist.
///
/// @return Reference to the collection of name/value pairs associated
/// with the current library.
///
/// @throw NoSuchCalloutContext Thrown if there is no ElementCollection
/// associated with the current library.
const ElementCollection& getContextForLibrary() const;
// Member variables
/// Collection of arguments passed to the callouts
ElementCollection arguments_;
/// Context collection - there is one entry per library context.
ContextCollection context_collection_;
/// Callout manager.
boost::shared_ptr<CalloutManager> manager_;
/// "Skip" flag, indicating if the caller should bypass remaining callouts.
bool skip_;
};
} // namespace util
} // namespace isc
#endif // CALLOUT_HANDLE_H
// Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <util/hooks/callout_handle.h>
#include <util/hooks/callout_manager.h>
#include <boost/static_assert.hpp>
#include <algorithm>
#include <functional>
#include <utility>
using namespace std;
using namespace isc::util;
namespace isc {
namespace util {
// Register a callout for the current library.
void
CalloutManager::registerCallout(const std::string& name, CalloutPtr callout) {
// Sanity check that the current library index is set to a valid value.
checkLibraryIndex(current_library_);
// Get the index associated with this hook (validating the name in the
// process).
int hook_index = hooks_->getIndex(name);
// Iterate through the callout vector for the hook from start to end,
// looking for the first entry where the library index is greater than
// the present index.
for (CalloutVector::iterator i = hook_vector_[hook_index].begin();
i != hook_vector_[hook_index].end(); ++i) {
if (i->first > current_library_) {
// Found an element whose library index number is greater than the
// current index, so insert the new element ahead of this one.
hook_vector_[hook_index].insert(i, make_pair(current_library_,
callout));
return;
}
}
// Reached the end of the vector, so there is no element in the (possibly
// empty) set of callouts with a library index greater than the current
// library index. Inset the callout at the end of the list.
hook_vector_[hook_index].push_back(make_pair(current_library_, callout));
}
// Check if callouts are present for a given hook index.
bool
CalloutManager::calloutsPresent(int hook_index) const {
// Validate the hook index.
if ((hook_index < 0) || (hook_index >= hook_vector_.size())) {
isc_throw(NoSuchHook, "hook index " << hook_index <<
" is not valid for the list of registered hooks");
}
// Valid, so are there any callouts associated with that hook?
return (!hook_vector_[hook_index].empty());
}
// Call all the callouts for a given hook.
void
CalloutManager::callCallouts(int hook_index, CalloutHandle& callout_handle) {
// Only initialize and iterate if there are callouts present. This check
// also catches the case of an invalid index.
if (calloutsPresent(hook_index)) {
// Clear the "skip" flag so we don't carry state from a previous call.
callout_handle.setSkip(false);
// Duplicate the callout vector for this hook and work through that.
// This step is needed because we allow dynamic registration and
// deregistration of callouts. If a callout attached to a hook modified
// the list of callouts on that hook, the underlying CalloutVector would
// change and potentially affect the iteration through that vector.
CalloutVector callouts(hook_vector_[hook_index]);
// Call all the callouts.
for (CalloutVector::const_iterator i = callouts.begin();
i != callouts.end(); ++i) {
// In case the callout tries to register or deregister a callout,
// set the current library index to the index associated with the
// library that registered the callout being called.
current_library_ = i->first;
// Call the callout
// @todo Log the return status if non-zero
static_cast<void>((*i->second)(callout_handle));
}
// Reset the current library index to an invalid value to catch any
// programming errors.
current_library_ = -1;
}
}
// Deregister a callout registered by the current library on a particular hook.
bool
CalloutManager::deregisterCallout(const std::string& name, CalloutPtr callout) {
// Sanity check that the current library index is set to a valid value.
checkLibraryIndex(current_library_);
// Get the index associated with this hook (validating the name in the
// process).
int hook_index = hooks_->getIndex(name);
/// Construct a CalloutEntry matching the current library and the callout
/// we want to remove.
CalloutEntry target(current_library_, callout);
/// To decide if any entries were removed, we'll record the initial size
/// of the callout vector for the hook, and compare it with the size after
/// the removal.
size_t initial_size = hook_vector_[hook_index].size();
// The next bit is standard STL (see "Item 33" in "Effective STL" by