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