summaryrefslogtreecommitdiffstats
path: root/src/lib/hooks/library_manager.h
blob: 84c5d82dc95dfb2fd446723dd8a3497dd14b81b3 (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
// 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 LIBRARY_MANAGER_H
#define LIBRARY_MANAGER_H

#include <exceptions/exceptions.h>
#include <hooks/server_hooks.h>
#include <boost/shared_ptr.hpp>

#include <string>

namespace isc {
namespace hooks {

/// @brief No Callout Manager
///
/// Thrown if a library manager is instantiated by an external agency without
/// specifying a CalloutManager object.
class NoCalloutManager : public Exception {
public:
    NoCalloutManager(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

class CalloutManager;
class LibraryHandle;
class LibraryManager;

/// @brief Library manager
///
/// This class handles the loading and unloading of a specific library.  It also
/// provides a static method for checking that a library is valid (this is used
/// in configuration parsing).
///
/// On loading, it opens the library using dlopen and checks the version (set
/// with the "version" method.  If all is OK, it iterates through the list of
/// known hooks and locates their symbols, registering each callout as it does
/// so.  Finally it locates the "load" function (if present) and calls it.
///
/// On unload, it clears the callouts on all hooks, calls the "unload"
/// method if present, clears the callouts on all hooks, and
/// closes the library.
///
/// @note Caution needs to be exercised when using the close method. During
///       normal use, data will pass between the server and the library.  In
///       this process, the library may allocate memory and pass it back to the
///       server.  This could happen by the server setting arguments or context
///       in the CalloutHandle object, or by the library modifying the content
///       of pointed-to data. If the library is closed, this memory may lie
///       in the virtual address space deleted in that process. (The word "may"
///       is used, as this could be operating-system specific.) Should this
///       happen, any reference to the memory will cause a segmentation fault.
///       This can occur in a quite obscure place, for example in the middle of
///       a destructor of an STL class when it is deleting memory allocated
///       when the data structure was extended by a function in the library.
///
/// @note The only safe way to run the "close" function is to ensure that all
///       possible references to it are removed first.  This means that all
///       CalloutHandles must be destroyed, as must any data items that were
///       passed to the callouts.  In practice, it could mean that a server
///       suspends processing of new requests until all existing ones have
///       been serviced and all packet/context structures destroyed before
///       reloading the libraries.
///
/// When validating a library, only the fact that the library can be opened and
/// version() exists and returns the correct number is checked.  The library
/// is closed after the validation.

class LibraryManager {
public:
    /// @brief Constructor
    ///
    /// This constructor is used by external agencies (i.e. the
    /// LibraryManagerCollection) when instantiating a LibraryManager.  It
    /// stores the library name - the actual actual loading is done in
    /// loadLibrary().
    ///
    /// @param name Name of the library to load.  This should be an absolute
    ///        path name.
    /// @param index Index of this library
    /// @param manager CalloutManager object
    ///
    /// @throw NoCalloutManager Thrown if the manager argument is NULL.
    LibraryManager(const std::string& name, int index,
                   const boost::shared_ptr<CalloutManager>& manager);

    /// @brief Destructor
    ///
    /// If the library is open, closes it.  This is principally a safety
    /// feature to ensure closure in the case of an exception destroying this
    /// object.  However, see the caveat in the class header about when it is
    /// safe to close libraries.
    ~LibraryManager();

    /// @brief Validate library
    ///
    /// A static method that is used to validate a library.  Validation checks
    /// that the library can be opened, that "version" exists, and that it
    /// returns the right number, and the multi-threading compatibility.
    ///
    /// @param name Name of the library to validate
    /// @param multi_threading_enabled The flag which indicates if MT is enabled
    ///        (used to check hook libraries compatibility with MT).
    ///
    /// @return true if the library validated, false if not.  If the library
    /// fails to validate, the reason for the failure is logged.
    static bool validateLibrary(const std::string& name,
                                bool multi_threading_enabled = false);

    /// @brief Loads a library
    ///
    /// Open the library, check the version and the multi-threading
    /// compatibility.  If all is OK, load all standard symbols then
    /// call "load" if present.
    ///
    /// It also calls the @c isc::log::MessageInitializer::loadDictionary,
    /// prior to invoking the @c version function of the library, to
    /// update the global logging dictionary with the log messages
    /// registered by the loaded library.
    ///
    /// @param multi_threading_enabled The flag which indicates if MT is enabled
    ///        (used to check hook libraries compatibility with MT).
    ///
    /// @return true if the library loaded successfully, false otherwise.
    /// In the latter case, the library will be unloaded if possible.
    bool loadLibrary(bool multi_threading_enabled = false);

    /// @brief Prepares library unloading
    ///
    /// Searches for the "unload" framework function and, if present, runs it.
    /// Regardless of status, remove all callouts associated with this
    /// library on all hooks.
    ///
    /// @return bool true if not found or found and run successfully,
    ///         false on an error.  In this case, an error message will
    ///         have been output.
    bool prepareUnloadLibrary();

    /// @brief Return library name
    ///
    /// @return Name of this library
    std::string getName() const {
        return (library_name_);
    }

protected:
    // The following methods are protected as they are accessed in testing.

    /// @brief Unloads a library
    ///
    /// Calls the libraries "unload" function if present, the closes the
    /// library.
    ///
    /// However, see the caveat in the class header about when it is safe to
    /// unload libraries.
    ///
    /// @return true if the library unloaded successfully, false if an error
    ///         occurred in the process (most likely the unload() function
    ///         (if present) returned an error).  Even if an error did occur,
    ///         the library is closed if possible.
    bool unloadLibrary();

    /// @brief Open library
    ///
    /// Opens the library associated with this LibraryManager.  A message is
    /// logged on an error.
    ///
    /// @return true if the library opened successfully, false otherwise.
    bool openLibrary();

    /// @brief Close library
    ///
    /// Closes the library associated with this LibraryManager.  A message is
    /// logged on an error.
    ///
    /// @return true if the library closed successfully, false otherwise. "true"
    ///         is also returned if the library were already closed when this
    ///         method was called.
    bool closeLibrary();

    /// @brief Check library version
    ///
    /// With the library open, accesses the "version()" function and, if
    /// present, checks the returned value against the hooks version symbol
    /// for the currently running Kea.  The "version()" function is
    /// mandatory and must be present (and return the correct value) for the
    /// library to load.
    ///
    /// If there is no version() function, or if there is a mismatch in
    /// version number, a message logged.
    ///
    /// @return bool true if the check succeeded
    bool checkVersion() const;

    /// @brief Check multi-threading compatibility
    ///
    /// If the multi-threading mode is disabled returns true, else with
    /// the library open, accesses the "multi_threading_compatible()"
    /// function and returns false if not exists or has value 0, returns
    /// true otherwise.
    ///
    /// @param multi_threading_enabled The flag which indicates if MT is enabled
    ///        (used to check hook libraries compatibility with MT).
    ///
    /// @return bool true if the check succeeded
    bool checkMultiThreadingCompatible(bool multi_threading_enabled) const;

    /// @brief Register standard callouts
    ///
    /// Loops through the list of hook names and searches the library for
    /// functions with those names.  Any that are found are registered as
    /// callouts for that hook.
    void registerStandardCallouts();

    /// @brief Run the load function if present
    ///
    /// Searches for the "load" framework function and, if present, runs it.
    ///
    /// @return bool true if not found or found and run successfully,
    ///         false on an error.  In this case, an error message will
    ///         have been output.
    bool runLoad();

private:
    /// @brief Validating constructor
    ///
    /// Constructor used when the LibraryManager is instantiated to validate
    /// a library (i.e. by the "validateLibrary" static method).
    ///
    /// @param name Name of the library to load.  This should be an absolute
    ///        path name.
    LibraryManager(const std::string& name);

    // Member variables

    void*       dl_handle_;     ///< Handle returned by dlopen
    int         index_;         ///< Index associated with this library
    boost::shared_ptr<CalloutManager> manager_;
                                ///< Callout manager for registration
    std::string library_name_;  ///< Name of the library

    ServerHooksPtr server_hooks_; ///< Stores a pointer to ServerHooks.

};

} // namespace hooks
} // namespace isc

#endif  // LIBRARY_MANAGER_H