callout_manager.h 17 KB
Newer Older
Stephen Morris's avatar
Stephen Morris committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
16
#ifndef CALLOUT_MANAGER_H
#define CALLOUT_MANAGER_H
Stephen Morris's avatar
Stephen Morris committed
17
18

#include <exceptions/exceptions.h>
19
20
#include <hooks/library_handle.h>
#include <hooks/server_hooks.h>
Stephen Morris's avatar
Stephen Morris committed
21
22
23

#include <boost/shared_ptr.hpp>

24
#include <climits>
Stephen Morris's avatar
Stephen Morris committed
25
26
27
28
#include <map>
#include <string>

namespace isc {
29
namespace hooks {
Stephen Morris's avatar
Stephen Morris committed
30

31
32
33
34
35
36
37
38
39
40
/// @brief No such library
///
/// Thrown if an attempt is made to set the current library index to a value
/// that is invalid for the number of loaded libraries.
class NoSuchLibrary : public Exception {
public:
    NoSuchLibrary(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

Stephen Morris's avatar
Stephen Morris committed
41
42
43
/// @brief Callout Manager
///
/// This class manages the registration, deregistration and execution of the
44
45
/// library callouts.  It is part of the hooks framework used by the BIND 10
/// server, and is not for use by user-written code in a hooks library.
Stephen Morris's avatar
Stephen Morris committed
46
///
47
/// In operation, the class needs to know two items of data:
Stephen Morris's avatar
Stephen Morris committed
48
///
49
/// - The list of server hooks, which is used in two ways.  Firstly, when a
50
51
52
///   callout registers or deregisters a hook, it does so by name: the
///   @ref isc::util::ServerHooks object supplies the names of registered
///   hooks.  Secondly, when the callouts associated with a hook are called by
53
54
///   the server, the server supplies the index of the relevant hook: this is
///   validated by reference to the list of hook.
55
56
57
58
59
///
/// - The number of loaded libraries.  Each callout registered by a user
///   library is associated with that library, the callout manager storing both
///   a pointer to the callout and the index of the library in the list of
///   loaded libraries.  Callouts are allowed to dynamically register and
60
61
62
63
64
65
///   deregister callouts in the same library (including themselves): they
///   cannot affect callouts registered by another library.  When calling a
///   callout, the callout manager maintains the idea of a "current library
///   index": if the callout calls one of the callout registration functions 
///   (indirectly via the @ref LibraryHandle object), the registration
///   functions use the "current library index" in their processing.
66
///
67
/// These two items of data are supplied when an object of this class is
68
69
70
71
72
73
74
/// constructed.  The latter (number of libraries) can be updated after the
/// class is constructed.  (Such an update is used during library loading where
/// the CalloutManager has to be constructed before the libraries are loaded,
/// but one of the libraries subsequently fails to load.)
///
/// The library index is important because it determines in what order callouts
/// on a particular hook are called.  For each hook, the CalloutManager
Stephen Morris's avatar
Stephen Morris committed
75
/// maintains a vector of callouts ordered by library index.  When a callout
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/// is added to the list, it is added at the end of the callouts associated
/// with the current library.  To clarify this further, suppose that three
/// libraries are loaded, A (assigned an index 1), B (assigned an index 2) and
/// C (assigned an index 3).  Suppose A registers two callouts on a given hook,
/// A1 and A2 (in that order) B registers B1 and B2 (in that order) and C
/// registers C1 and C2 (in that order).  Internally, the callouts are stored
/// in the order A1, A2, B1, B2, C1, and C2: this is also the order in which
/// the are called.  If B now registers another callout (B3), it is added to
/// the vector after the list of callouts associated with B: the new order is
/// therefore A1, A2, B1, B2, B3, C1 and C2.
///
/// Indexes range between 1 and n (where n is the number of the libraries
/// loaded) and are assigned to libraries based on the order the libraries
/// presented to the hooks framework for loading (something that occurs in the
/// isc::util::HooksManager) class.  However, two other indexes are recognised,
Stephen Morris's avatar
Stephen Morris committed
91
92
93
94
95
96
97
98
/// 0 and INT_MAX.  These are used when the server itself registers callouts -
/// the server is able to register callouts that get called before any
/// user-library callouts, and ones that get called after user-library callouts.
/// In other words, assuming the callouts on a hook are A1, A2, B1, B2, B3, C2,
/// C2 as before, and that the server registers S1 (to run before the
/// user-registered callouts) and S2 (to run after them), the callouts are
/// stored (and executed) in the order S1, A1, A2, B1, B2, B3, C2, C2, S2.  In
/// summary, the recognised index values are:
99
///
100
101
102
/// - < 0: invalid.
/// - 0: used for server-registered callouts that are called before
///   user-registered callouts.
Stephen Morris's avatar
Stephen Morris committed
103
/// - 1 - n: callouts from user libraries.
104
105
106
107
/// - INT_MAX: used for server-registered callouts called after
///   user-registered callouts.
///
/// Note that the callout functions do not access the CalloutManager: instead,
108
109
110
/// they use a LibraryHandle object.  This contains an internal pointer to
/// the CalloutManager, but provides a restricted interface.  In that way,
/// callouts are unable to affect callouts supplied by other libraries.
Stephen Morris's avatar
Stephen Morris committed
111
112
113
114
115
116
117

class CalloutManager {
private:

    // Private typedefs

    /// Element in the vector of callouts.  The elements in the pair are the
118
119
    /// index of the library from which this callout was registered, and a#
    /// pointer to the callout itself.
Stephen Morris's avatar
Stephen Morris committed
120
121
    typedef std::pair<int, CalloutPtr> CalloutEntry;

122
123
    /// An element in the hook vector. Each element is a vector of callouts
    /// associated with a given hook.
Stephen Morris's avatar
Stephen Morris committed
124
125
126
127
128
129
130
    typedef std::vector<CalloutEntry> CalloutVector;

public:

    /// @brief Constructor
    ///
    /// Initializes member variables, in particular sizing the hook vector
131
    /// (the vector of callout vectors) to the appropriate size.
Stephen Morris's avatar
Stephen Morris committed
132
    ///
133
    /// @param num_libraries Number of loaded libraries.
134
135
136
    ///
    /// @throw isc::BadValue if the number of libraries is less than or equal
    ///        to 0, or if the pointer to the server hooks object is empty.
137
138
139
    CalloutManager(int num_libraries = 0)
        : current_hook_(-1), current_library_(-1),
          hook_vector_(ServerHooks::getServerHooks().getCount()),
140
141
          library_handle_(this), pre_library_handle_(this, 0),
          post_library_handle_(this, INT_MAX), num_libraries_(num_libraries)
142
    {
143
144
145
146
        // Check that the number of libraries is OK.  (This does a redundant
        // set of the number of libraries, but it's only a single assignment
        // and avoids the need for a separate "check" method.
        setNumLibraries(num_libraries);
147
    }
Stephen Morris's avatar
Stephen Morris committed
148

149
    /// @brief Register a callout on a hook for the current library
Stephen Morris's avatar
Stephen Morris committed
150
    ///
151
152
153
154
    /// Registers a callout function for the current library with a given hook
    /// (the index of the "current library" being given by the current_library_
    /// member).  The callout is added to the end of the callouts for this
    /// library that are associated with that hook.
Stephen Morris's avatar
Stephen Morris committed
155
156
157
158
159
160
161
    ///
    /// @param name Name of the hook to which the callout is added.
    /// @param callout Pointer to the callout function to be registered.
    ///
    /// @throw NoSuchHook The hook name is unrecognised.
    /// @throw Unexpected The hook name is valid but an internal data structure
    ///        is of the wrong size.
162
    void registerCallout(const std::string& name, CalloutPtr callout);
Stephen Morris's avatar
Stephen Morris committed
163

164
    /// @brief De-Register a callout on a hook for the current library
Stephen Morris's avatar
Stephen Morris committed
165
    ///
166
167
168
169
    /// Searches through the functions registered by the the current library
    /// (the index of the "current library" being given by the current_library_
    /// member) with the named hook and removes all entries matching the
    /// callout.
Stephen Morris's avatar
Stephen Morris committed
170
171
172
173
174
175
176
177
178
    ///
    /// @param name Name of the hook from which the callout is removed.
    /// @param callout Pointer to the callout function to be removed.
    ///
    /// @return true if a one or more callouts were deregistered.
    ///
    /// @throw NoSuchHook The hook name is unrecognised.
    /// @throw Unexpected The hook name is valid but an internal data structure
    ///        is of the wrong size.
179
    bool deregisterCallout(const std::string& name, CalloutPtr callout);
Stephen Morris's avatar
Stephen Morris committed
180

181
    /// @brief Removes all callouts on a hook for the current library
Stephen Morris's avatar
Stephen Morris committed
182
183
    ///
    /// Removes all callouts associated with a given hook that were registered
184
185
    /// by the current library (the index of the "current library" being given
    /// by the current_library_ member).
Stephen Morris's avatar
Stephen Morris committed
186
187
188
189
190
191
    ///
    /// @param name Name of the hook from which the callouts are removed.
    ///
    /// @return true if one or more callouts were deregistered.
    ///
    /// @throw NoSuchHook Thrown if the hook name is unrecognised.
192
    bool deregisterAllCallouts(const std::string& name);
Stephen Morris's avatar
Stephen Morris committed
193
194
195
196
197
198

    /// @brief Checks if callouts are present on a hook
    ///
    /// Checks all loaded libraries and returns true if at least one callout
    /// has been registered by any of them for the given hook.
    ///
199
    /// @param hook_index Hook index for which callouts are checked.
Stephen Morris's avatar
Stephen Morris committed
200
201
202
203
    ///
    /// @return true if callouts are present, false if not.
    ///
    /// @throw NoSuchHook Given index does not correspond to a valid hook.
204
    bool calloutsPresent(int hook_index) const;
Stephen Morris's avatar
Stephen Morris committed
205
206
207
208
209
210

    /// @brief Calls the callouts for a given hook
    ///
    /// Iterates through the libray handles and calls the callouts associated
    /// with the given hook index.
    ///
211
212
213
    /// @note This method invalidates the current library index set with
    ///       setLibraryIndex().
    ///
214
    /// @param hook_index Index of the hook to call.
Stephen Morris's avatar
Stephen Morris committed
215
216
    /// @param callout_handle Reference to the CalloutHandle object for the
    ///        current object being processed.
217
    void callCallouts(int hook_index, CalloutHandle& callout_handle);
Stephen Morris's avatar
Stephen Morris committed
218

Stephen Morris's avatar
Stephen Morris committed
219
220
221
222
223
224
225
226
    /// @brief Get current hook index
    ///
    /// Made available during callCallouts, this is the index of the hook
    /// on which callouts are being called.
    int getHookIndex() const {
        return (current_hook_);
    }

227
228
229
230
231
232
    /// @brief Set number of libraries
    ///
    /// Sets the number of libraries.  Although the value is passed to the
    /// constructor, in some cases that is only an estimate and the number
    /// can only be determined after the CalloutManager is created.
    ///
233
234
235
    /// @note If the number if libraries is reset, it must be done *before*
    ///       any callouts are registered.
    ///
236
237
238
    /// @param num_libraries Number of libraries served by this CalloutManager.
    ///
    /// @throw BadValue Number of libraries must be >= 0.
239
240
    /// @throw LibraryCountChanged Number of libraries has been changed after
    ///        callouts have been registered.
241
242
    void setNumLibraries(int num_libraries);

243
244
245
246
247
    /// @brief Get number of libraries
    ///
    /// Returns the number of libraries that this CalloutManager is expected
    /// to serve.  This is the number passed to its constructor.
    ///
248
    /// @return Number of libraries served by this CalloutManager.
249
250
251
252
    int getNumLibraries() const {
        return (num_libraries_);
    }

253
254
255
256
257
258
    /// @brief Get current library index
    ///
    /// Returns the index of the "current" library.  This the index associated
    /// with the currently executing callout when callCallouts is executing.
    /// When callCallouts() is not executing (as is the case when the load()
    /// function in a user-library is called during the library load process),
259
260
261
262
    /// the index can be set by setLibraryIndex().
    ///
    /// @note The value set by this method is lost after a call to
    ///       callCallouts.
263
264
265
266
267
    ///
    /// @return Current library index.
    int getLibraryIndex() const {
        return (current_library_);
    }
Stephen Morris's avatar
Stephen Morris committed
268

269
270
    /// @brief Set current library index
    ///
271
272
273
274
275
276
277
278
    /// Sets the current library index.  This has the following valid values:
    ///
    /// - -1: invalidate current index.
    /// - 0: pre-user library callout.
    /// - 1 - numlib: user-library callout (where "numlib" is the number of
    ///   libraries loaded in the system, this figure being passed to this
    ///   object at construction time).
    /// - INT_MAX: post-user library callout.
Stephen Morris's avatar
Stephen Morris committed
279
    ///
280
281
282
283
284
285
286
287
    /// @param library_index New library index.
    ///
    /// @throw NoSuchLibrary if the index is not valid.
    void setLibraryIndex(int library_index) {
        checkLibraryIndex(library_index);
        current_library_ = library_index;
    }

288
289
290
291
292
293
294
295
296
297
298
299
300
    /// @defgroup calloutManagerLibraryHandles Callout manager library handles
    ///
    /// The CalloutManager offers three library handles:
    ///
    /// - a "standard" one, used to register and deregister callouts for
    ///   the library index that is marked as current in the CalloutManager.
    ///   When a callout is called, it is passed this one.
    /// - a pre-library callout handle, used by the server to register
    //    callouts to run prior to user-library callouts.
    /// - a post-library callout handle, used by the server to register
    ///   callouts to run after the user-library callouts.
    //@{

301
302
303
304
305
    /// @brief Return library handle
    ///
    /// The library handle is available to the user callout via the callout
    /// handle object.  It provides a cut-down view of the CalloutManager,
    /// allowing the callout to register and deregister callouts in the
306
307
    /// library of which it is part, whilst denying access to anything that
    /// may affect other libraries.
308
    ///
309
    /// @return Reference to library handle for this manager
310
311
312
    LibraryHandle& getLibraryHandle() {
        return (library_handle_);
    }
Stephen Morris's avatar
Stephen Morris committed
313

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
    /// @brief Return pre-user callouts library handle
    ///
    /// The LibraryHandle to affect callouts that will run before the
    /// user-library callouts.
    ///
    /// @return Reference to pre-user library handle for this manager
    LibraryHandle& getPreLibraryHandle() {
        return (pre_library_handle_);
    }

    /// @brief Return post-user callouts library handle
    ///
    /// The LibraryHandle to affect callouts that will run before the
    /// user-library callouts.
    ///
    /// @return Reference to post-user library handle for this manager
    LibraryHandle& getPostLibraryHandle() {
        return (post_library_handle_);
    }

    //@}

Stephen Morris's avatar
Stephen Morris committed
336
private:
337
338
339
340
341
342
    /// @brief Check library index
    ///
    /// Ensures that the current library index is valid.  This is called by
    /// the hook registration functions.
    ///
    /// @param library_index Value to check for validity as a library index.
343
344
    ///        Valid values are 0 - numlib+1 and -1: see @ref setLibraryIndex
    ///        for the meaning of the various values.
345
    ///
346
    /// @throw NoSuchLibrary Library index is not valid.
347
    void checkLibraryIndex(int library_index) const;
348

Stephen Morris's avatar
Stephen Morris committed
349
350
    /// @brief Compare two callout entries for library equality
    ///
351
352
353
    /// This is used in callout removal code when all callouts on a hook for a
    /// given library are being removed.  It checks whether two callout entries
    /// have the same library index.
Stephen Morris's avatar
Stephen Morris committed
354
355
356
357
358
    ///
    /// @param ent1 First callout entry to check
    /// @param ent2 Second callout entry to check
    ///
    /// @return bool true if the library entries are the same
359
360
    class CalloutLibraryEqual :
        public std::binary_function<CalloutEntry, CalloutEntry, bool> {
Stephen Morris's avatar
Stephen Morris committed
361
    public:
362
363
        bool operator()(const CalloutEntry& ent1,
                        const CalloutEntry& ent2) const {
Stephen Morris's avatar
Stephen Morris committed
364
365
366
367
            return (ent1.first == ent2.first);
        }
    };

Stephen Morris's avatar
Stephen Morris committed
368
369
370
371
372
    /// Current hook.  When a call is made to callCallouts, this holds the
    /// index of the current hook.  It is set to an invalid value (-1)
    /// otherwise.
    int current_hook_;

373
374
    /// Current library index.  When a call is made to any of the callout
    /// registration methods, this variable indicates the index of the user
375
    /// library that should be associated with the call.
376
377
    int current_library_;

Stephen Morris's avatar
Stephen Morris committed
378
    /// Vector of callout vectors.  There is one entry in this outer vector for
379
380
    /// each hook. Each element is itself a vector, with one entry for each
    /// callout registered for that hook.
381
    std::vector<CalloutVector> hook_vector_;
Stephen Morris's avatar
Stephen Morris committed
382

383
    /// LibraryHandle object user by the callout to access the callout
384
385
386
    /// registration methods on this CalloutManager object.  The object is set
    /// such that the index of the library associated with any operation is
    /// whatever is currently set in the CalloutManager.
387
    LibraryHandle library_handle_;
388
389
390
391
392
393
394
395
    
    /// LibraryHandle for callouts to be registered as being called before
    /// the user-registered callouts.
    LibraryHandle pre_library_handle_;
    
    /// LibraryHandle for callouts to be registered as being called after
    /// the user-registered callouts.
    LibraryHandle post_library_handle_;
396

397
    /// Number of libraries.
398
    int num_libraries_;
Stephen Morris's avatar
Stephen Morris committed
399
400
401
402
403
};

} // namespace util
} // namespace isc

404
#endif // CALLOUT_MANAGER_H