Coverage Report

Created: 2026-05-16 07:13

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