1 // Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this 5 // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 #ifndef HOOKS_MANAGER_H 8 #define HOOKS_MANAGER_H 9 10 #include <hooks/server_hooks.h> 11 #include <hooks/libinfo.h> 12 13 #include <boost/noncopyable.hpp> 14 #include <boost/shared_ptr.hpp> 15 16 #include <string> 17 #include <vector> 18 19 namespace isc { 20 namespace hooks { 21 22 /// @brief Libraries still opened. 23 /// 24 /// Thrown if an attempt is made to load libraries when some are still 25 /// in memory likely because they were not unloaded (logic error in Kea) 26 /// or they have some visible dangling pointers (logic error in a hook 27 /// library). 28 class LibrariesStillOpened : public Exception { 29 public: LibrariesStillOpened(const char * file,size_t line,const char * what)30 LibrariesStillOpened(const char* file, size_t line, const char* what) : 31 isc::Exception(file, line, what) {} 32 }; 33 34 // Forward declarations 35 class CalloutHandle; 36 class CalloutManager; 37 class LibraryHandle; 38 class LibraryManagerCollection; 39 40 /// @brief Hooks Manager 41 /// 42 /// This is the overall manager of the hooks framework and is the main class 43 /// used by a Kea module when handling hooks. It is responsible for the 44 /// loading and unloading of user libraries, and for calling the callouts on 45 /// each hook point. 46 /// 47 /// The class is a singleton, the single instance of the object being accessed 48 /// through the static getHooksManager() method. 49 50 class HooksManager : boost::noncopyable { 51 public: 52 53 /// @brief Load and reload libraries 54 /// 55 /// Loads the list of libraries into the server address space. For each 56 /// library, the "standard" functions (ones with the same names as the 57 /// hook points) are configured and the libraries' "load" function 58 /// called. 59 /// 60 /// @note this method now requires the libraries are unloaded before 61 /// being called. 62 /// 63 /// If any library fails to load, an error message will be logged. The 64 /// remaining libraries will be loaded if possible. 65 /// 66 /// @param libraries List of libraries to be loaded. The order is 67 /// important, as it determines the order that callouts on the same 68 /// hook will be called. 69 /// 70 /// @return true if all libraries loaded without a problem, false if one or 71 /// more libraries failed to load. In the latter case, message will 72 /// be logged that give the reason. 73 /// @throw LibrariesStillOpened when some libraries are already loaded. 74 static bool loadLibraries(const HookLibsCollection& libraries); 75 76 /// @brief Unload libraries 77 /// 78 /// Unloads the loaded libraries and leaves the hooks subsystem in the 79 /// state it was after construction but before loadLibraries() is called. 80 /// 81 /// @note: This method should be called after @ref prepareUnloadLibraries 82 /// in order to destroy appropriate objects. See notes for 83 /// the class LibraryManager for pitfalls. 84 /// @note: if even after @ref prepareUnloadLibraries there are still 85 /// visible pointers (i.e. callout handles owning the 86 /// library manager collection) the method will fail to close 87 /// libraries and returns false. It is a fatal error as there 88 /// is no possible recovery. It is a logic error in the hook 89 /// code too so the solution is to fix it and to restart 90 /// the server with a correct hook library binary. 91 /// 92 /// @return true if all libraries unloaded successfully, false if they 93 /// are still in memory. 94 static bool unloadLibraries(); 95 96 /// @brief Prepare the unloading of libraries 97 /// 98 /// Calls the unload functions when they exist and removes callouts. 99 /// 100 /// @note: after the call to this method there should be no visible 101 /// dangling pointers (i.e. callout handles owning the library 102 /// manager collection) nor invisible dangling pointers. 103 /// In the first case it will be impossible to close libraries 104 /// so they will remain in memory, in the second case a crash 105 /// is possible in particular at exit time during global 106 /// object finalization. In both cases the hook library code 107 /// causing the problem is incorrect and must be fixed. 108 /// @note: it is a logic error to not call this method before 109 /// @ref unloadLibraries even it hurts only with particular 110 /// hooks libraries. 111 static void prepareUnloadLibraries(); 112 113 /// @brief Are callouts present? 114 /// 115 /// Checks loaded libraries and returns true if at lease one callout 116 /// has been registered by them for the given hook. 117 /// 118 /// @param index Hooks index for which callouts are checked. 119 /// 120 /// @return true if callouts are present, false if not. 121 /// @throw NoSuchHook Given index does not correspond to a valid hook. 122 static bool calloutsPresent(int index); 123 124 /// @brief Checks if control command handlers are present for the 125 /// specified command. 126 /// 127 /// @param command_name Command name for which handlers' presence should 128 /// be checked. 129 /// 130 /// @return true if there is a hook point associated with the specified 131 /// command and callouts/command handlers are installed for this hook 132 /// point, false otherwise. 133 static bool commandHandlersPresent(const std::string& command_name); 134 135 /// @brief Calls the callouts for a given hook 136 /// 137 /// Iterates through the library handles and calls the callouts associated 138 /// with the given hook index. 139 /// 140 /// @note This method invalidates the current library index set with 141 /// setLibraryIndex(). 142 /// 143 /// @param index Index of the hook to call. 144 /// @param handle Reference to the CalloutHandle object for the current 145 /// object being processed. 146 static void callCallouts(int index, CalloutHandle& handle); 147 148 /// @brief Calls the callouts/command handlers for a given command name. 149 /// 150 /// Iterates through the library handles and calls the command handlers 151 /// associated with the given command. It expects that the hook point 152 /// for this command exists (with a name being a command_name prefixed 153 /// with a dollar sign and with hyphens replaced with underscores). 154 /// 155 /// @param command_name Command name for which handlers should be called. 156 /// @param handle Reference to the CalloutHandle object for the current 157 /// object being processed. 158 /// 159 /// @throw NoSuchHook if the hook point for the specified command does 160 /// not exist. 161 static void callCommandHandlers(const std::string& command_name, 162 CalloutHandle& handle); 163 164 /// @brief Return pre-callouts library handle 165 /// 166 /// Returns a library handle that can be used by the server to register 167 /// callouts on a hook that are called _before_ any callouts belonging 168 /// to a library. 169 /// 170 /// @note Both the reference returned and the callouts registered with 171 /// this handle only remain valid until the next loadLibraries() or 172 /// unloadLibraries() call. If the callouts are to remain registered 173 /// after this time, a new handle will need to be obtained and the 174 /// callouts re-registered. 175 /// 176 /// @return Reference to library handle associated with pre-library callout 177 /// registration. 178 static LibraryHandle& preCalloutsLibraryHandle(); 179 180 /// @brief Return post-callouts library handle 181 /// 182 /// Returns a library handle that can be used by the server to register 183 /// callouts on a hook that are called _after any callouts belonging 184 /// to a library. 185 /// 186 /// @note Both the reference returned and the callouts registered with 187 /// this handle only remain valid until the next loadLibraries() or 188 /// unloadLibraries() call. If the callouts are to remain registered 189 /// after this time, a new handle will need to be obtained and the 190 /// callouts re-registered. 191 /// 192 /// @return Reference to library handle associated with post-library callout 193 /// registration. 194 static LibraryHandle& postCalloutsLibraryHandle(); 195 196 /// @brief Return callout handle 197 /// 198 /// Returns a callout handle to be associated with a request passed round 199 /// the system. 200 /// 201 /// @note This handle is valid only after a loadLibraries() call and then 202 /// only up to the next loadLibraries() call. 203 /// 204 /// @return Shared pointer to a CalloutHandle object. 205 static boost::shared_ptr<CalloutHandle> createCalloutHandle(); 206 207 /// @brief Register Hook 208 /// 209 /// This is just a convenience shell around the ServerHooks::registerHook() 210 /// method. It - along with the definitions of the two hook indexes for 211 /// the context_create and context_destroy methods - means that server 212 /// authors only need to deal with HooksManager and CalloutHandle, and not 213 /// include any other hooks framework classes. 214 /// 215 /// @param name Name of the hook 216 /// 217 /// @return Index of the hook, to be used in subsequent hook-related calls. 218 /// This will be greater than or equal to zero (so allowing a 219 /// negative value to indicate an invalid index). 220 /// 221 /// @throws DuplicateHook A hook with the same name has already been 222 /// registered. 223 static int registerHook(const std::string& name); 224 225 /// @brief Return list of loaded libraries 226 /// 227 /// Returns the names of the loaded libraries. 228 /// 229 /// @return List of loaded library names. 230 static std::vector<std::string> getLibraryNames(); 231 232 /// @brief Return list of loaded libraries with its parameters. 233 /// 234 /// Returns the names of the loaded libraries and their parameters. 235 /// 236 /// @return List of loaded libraries (names + parameters) 237 static HookLibsCollection getLibraryInfo(); 238 239 /// @brief Validate library list 240 /// 241 /// For each library passed to it, checks that the library can be opened 242 /// and that the "version" function is present and gives the right answer. 243 /// Each library is closed afterwards. 244 /// 245 /// This is used during the configuration parsing - when the list of hooks 246 /// libraries is changed, each of the new libraries is checked before the 247 /// change is committed. 248 /// 249 /// @param libraries List of libraries to be validated. 250 /// 251 /// @return An empty vector if all libraries validated. Otherwise it 252 /// holds the names of the libraries that failed validation. 253 static std::vector<std::string> validateLibraries( 254 const std::vector<std::string>& libraries); 255 256 /// Index numbers for pre-defined hooks. 257 static const int CONTEXT_CREATE = ServerHooks::CONTEXT_CREATE; 258 static const int CONTEXT_DESTROY = ServerHooks::CONTEXT_DESTROY; 259 260 /// @brief Park an object (packet). 261 /// 262 /// The typical use case for parking an object is when the server needs to 263 /// suspend processing of a packet to perform an asynchronous operation, 264 /// before the response is sent to a client. In this case, the object type 265 /// is a pointer to the processed packet. Therefore, further in this 266 /// description we're going to refer to the parked objects as "parked 267 /// packets". However, any other object can be parked if necessary. 268 /// 269 /// The following is the typical flow when packets are parked. The callouts 270 /// responsible for performing an asynchronous operation signal this need 271 /// to the server by returning the status @c NEXT_STEP_PARK, which instructs 272 /// the server to call this function. This function stops processing the 273 /// packet and puts it in, so called, parking lot. In order to be able to 274 /// resume the packet processing when instructed by the hooks library, the 275 /// parked packet is associated with the callback which, when called, will 276 /// resume packet processing. 277 /// 278 /// The hook library must increase a reference count on the parked object 279 /// by calling @c ParkingLotHandle::reference prior to returning the 280 /// @c NEXT_STEP_PARK status. This is important when multiple callouts 281 /// are installed on the same hook point and each of them schedules an 282 /// asynchronous operation. In this case, the packet must not be unparked 283 /// until all hook libraries call @c ParkingLotHandle::unpark to mark 284 /// that respective asynchronous operations are completed. 285 /// 286 /// @tparam Type of the parked object. 287 /// @param hook_name name of the hook point for which the packet is parked. 288 /// @param parked_object packet to be parked. 289 /// @param unpark_callback callback invoked when the packet is unparked. 290 template<typename T> park(const std::string & hook_name,T parked_object,std::function<void ()> unpark_callback)291 static void park(const std::string& hook_name, T parked_object, 292 std::function<void()> unpark_callback) { 293 ServerHooks::getServerHooks(). 294 getParkingLotPtr(hook_name)->park(parked_object, unpark_callback); 295 } 296 297 /// @brief Forces unparking the object (packet). 298 /// 299 /// This method unparks the object regardless of the reference counting 300 /// value. This is used in the situations when the callouts fail to unpark 301 /// the packet for some reason. 302 /// 303 /// @tparam T type of the parked object. 304 /// @param hook_name name of the hook point for which the packet is parked. 305 /// @param parked_object parked object to be unparked. 306 /// @return true if the specified object has been found, false otherwise. 307 template<typename T> unpark(const std::string & hook_name,T parked_object)308 static bool unpark(const std::string& hook_name, T parked_object) { 309 return (ServerHooks::getServerHooks(). 310 getParkingLotPtr(hook_name)->unpark(parked_object, true)); 311 } 312 313 /// @brief Removes parked object without calling a callback. 314 /// 315 /// @tparam T type of the parked object. 316 /// @param hook_name name of the hook point for which the packet is parked. 317 /// @param parked_object parked object to be removed. 318 /// @return true if the specified object has been found false otherwise. 319 template<typename T> drop(const std::string & hook_name,T parked_object)320 static bool drop(const std::string& hook_name, T parked_object) { 321 return (ServerHooks::getServerHooks(). 322 getParkingLotPtr(hook_name)->drop(parked_object)); 323 } 324 325 /// @brief Increases reference counter for the parked object. 326 /// 327 /// Reference counter must be increased at least to 1 before the @c park() 328 /// method can be called. 329 /// 330 /// @tparam Type of the parked object. 331 /// @param hook_name name of the hook point for which the packet is parked. 332 /// @param parked_object parked object for which reference counter should 333 /// be increased. 334 template<typename T> reference(const std::string & hook_name,T parked_object)335 static void reference(const std::string& hook_name, T parked_object) { 336 ServerHooks::getServerHooks(). 337 getParkingLotPtr(hook_name)->reference(parked_object); 338 } 339 340 /// @brief Clears any parking packets. 341 /// 342 /// This method should be called during reconfiguration to ensure there 343 /// are no dangling pointers that could possibly prevent the library 344 /// from being unloaded. clearParkingLots()345 static void clearParkingLots() { 346 ServerHooks::getServerHooks().getParkingLotsPtr()->clear(); 347 } 348 349 /// @brief Set test mode 350 /// 351 /// If enabled by unit tests will permit to register callouts before calling 352 /// @ref loadLibraries which will return immediately without changing 353 /// current internal state. 354 /// 355 /// @param mode the test mode flag which enables or disabled the 356 /// functionality. 357 static void setTestMode(bool mode); 358 359 /// @brief Get test mode 360 /// 361 /// @return the test mode flag. 362 static bool getTestMode(); 363 364 private: 365 366 /// @brief Constructor 367 /// 368 /// This is private as the object is a singleton and can only be addressed 369 /// through the getHooksManager() static method. 370 HooksManager(); 371 372 /// @brief Get singleton hooks manager 373 /// 374 /// @return Reference to the singleton hooks manager. 375 static HooksManager& getHooksManager(); 376 377 //@{ 378 /// The following methods correspond to similarly-named static methods, 379 /// but actually do the work on the singleton instance of the HooksManager. 380 /// See the descriptions of the static methods for more details. 381 382 /// @brief Validate library list 383 /// 384 /// @param List of libraries to be validated. 385 /// 386 /// @return An empty string if all libraries validated. Otherwise it is 387 /// the name of the first library that failed validation. The 388 /// configuration code can return this to bindctl as an indication 389 /// of the problem. 390 std::string validateLibrariesInternal( 391 const std::vector<std::string>& libraries) const; 392 393 /// @brief Load and reload libraries 394 /// 395 /// @param libraries List of libraries to be loaded. The order is 396 /// important, as it determines the order that callouts on the same 397 /// hook will be called. 398 /// 399 /// @return true if all libraries loaded without a problem, false if one or 400 /// more libraries failed to load. In the latter case, message will 401 /// be logged that give the reason. 402 bool loadLibrariesInternal(const HookLibsCollection& libraries); 403 404 /// @brief Unload libraries 405 /// 406 /// @return true if all libraries unloaded successfully, false on an error. 407 /// In the latter case, an error message will have been output. 408 bool unloadLibrariesInternal(); 409 410 /// @brief Prepare the unloading of libraries 411 void prepareUnloadLibrariesInternal(); 412 413 /// @brief Are callouts present? 414 /// 415 /// @param index Hooks index for which callouts are checked. 416 /// 417 /// @return true if callouts are present, false if not. 418 /// @throw NoSuchHook Given index does not correspond to a valid hook. 419 bool calloutsPresentInternal(int index); 420 421 /// @brief Checks if control command handlers are present for the 422 /// specified command. 423 /// 424 /// @param command_name Command name for which handlers' presence should 425 /// be checked. 426 /// 427 /// @return true if there is a hook point associated with the specified 428 /// command and callouts/command handlers are installed for this hook 429 /// point, false otherwise. 430 bool commandHandlersPresentInternal(const std::string& command_name); 431 432 /// @brief Calls the callouts for a given hook 433 /// 434 /// @param index Index of the hook to call. 435 /// @param handle Reference to the CalloutHandle object for the current 436 /// object being processed. 437 void callCalloutsInternal(int index, CalloutHandle& handle); 438 439 /// @brief Calls the callouts/command handlers for a given command name. 440 /// 441 /// @param command_name Command name for which handlers should be called. 442 /// @param handle Reference to the CalloutHandle object for the current 443 /// object being processed. 444 /// 445 /// @throw NoSuchHook if the hook point for the specified command does 446 /// not exist. 447 void callCommandHandlersInternal(const std::string& command_name, 448 CalloutHandle& handle); 449 450 /// @brief Return callout handle 451 /// 452 /// @return Shared pointer to a CalloutHandle object. 453 boost::shared_ptr<CalloutHandle> createCalloutHandleInternal(); 454 455 /// @brief Return pre-callouts library handle 456 /// 457 /// @return Reference to library handle associated with pre-library callout 458 /// registration. 459 LibraryHandle& preCalloutsLibraryHandleInternal(); 460 461 /// @brief Return post-callouts library handle 462 /// 463 /// @return Reference to library handle associated with post-library callout 464 /// registration. 465 LibraryHandle& postCalloutsLibraryHandleInternal(); 466 467 /// @brief Return list of loaded libraries 468 /// 469 /// @return List of loaded library names. 470 std::vector<std::string> getLibraryNamesInternal() const; 471 472 /// @brief Return a collection of library names with parameters. 473 HookLibsCollection getLibraryInfoInternal() const; 474 475 //@} 476 477 // Members 478 479 /// Set of library managers. 480 /// 481 /// @note: This should never be null. 482 boost::shared_ptr<LibraryManagerCollection> lm_collection_; 483 484 /// Callout manager for the set of library managers. 485 /// 486 /// @note: This should never be null. 487 boost::shared_ptr<CalloutManager> callout_manager_; 488 489 /// Test flag to keep @ref callout_manager_ when calling @ref loadLibraries 490 /// from unit tests (called by @ref configureDhcp[46]Server). 491 /// 492 /// @note: This will effectively make @ref loadLibraries return immediately. 493 bool test_mode_; 494 }; 495 496 } // namespace util 497 } // namespace hooks 498 499 #endif // HOOKS_MANAGER_H 500