summaryrefslogtreecommitdiffstats
path: root/src/lib/hooks/hooks_manager.h
blob: 7cda194bbb6bee29346914e3cae2e1f39ece1676 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
// Copyright (C) 2013-2020 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.
    ///
    /// @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);

    /// @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.
    ///
    /// @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);

    /// 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 Validate library list
    ///
    /// @param List of libraries to be validated.
    ///
    /// @return An empty string if all libraries validated.  Otherwise it is
    ///         the name of the first library that failed validation.  The
    ///         configuration code can return this to bindctl as an indication
    ///         of the problem.
    std::string validateLibrariesInternal(
                       const std::vector<std::string>& libraries) const;

    /// @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.
    ///
    /// @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);

    /// @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