diff options
Diffstat (limited to 'src/lib/hooks/hooks_manager.h')
-rw-r--r-- | src/lib/hooks/hooks_manager.h | 496 |
1 files changed, 496 insertions, 0 deletions
diff --git a/src/lib/hooks/hooks_manager.h b/src/lib/hooks/hooks_manager.h new file mode 100644 index 0000000..c3957e8 --- /dev/null +++ b/src/lib/hooks/hooks_manager.h @@ -0,0 +1,496 @@ +// Copyright (C) 2013-2023 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. + /// @param multi_threading_enabled The flag which indicates if MT is enabled + /// (used to check hook libraries compatibility with MT). + /// + /// @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, + bool multi_threading_enabled = false); + + /// @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. + /// @param multi_threading_enabled The flag which indicates if MT is enabled + /// (used to check hook libraries compatibility with MT). + /// + /// @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, + bool multi_threading_enabled = false); + + /// 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 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. + /// @param multi_threading_enabled The flag which indicates if MT is enabled + /// (used to check hook libraries compatibility with MT). + /// + /// @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, + bool multi_threading_enabled); + + /// @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 |