callout_handle.h 14.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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.

#ifndef CALLOUT_HANDLE_H
#define CALLOUT_HANDLE_H

#include <exceptions/exceptions.h>
19
#include <hooks/library_handle.h>
20
21
22
23
24
25
26
27
28

#include <boost/any.hpp>
#include <boost/shared_ptr.hpp>

#include <map>
#include <string>
#include <vector>

namespace isc {
29
namespace hooks {
30
31
32

/// @brief No such argument
///
33
34
/// Thrown if an attempt is made access an argument that does not exist.

35
36
37
38
39
40
class NoSuchArgument : public Exception {
public:
    NoSuchArgument(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

41
42
43
/// @brief No such callout context item
///
/// Thrown if an attempt is made to get an item of data from this callout's
44
45
46
/// context and either the context or an item in the context with that name
/// does not exist.

47
48
49
50
51
52
53
class NoSuchCalloutContext : public Exception {
public:
    NoSuchCalloutContext(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) {}
};

// Forward declaration of the library handle and related collection classes.
54

Stephen Morris's avatar
Stephen Morris committed
55
class CalloutManager;
56
class LibraryHandle;
57
class LibraryManagerCollection;
58

59
/// @brief Per-packet callout handle
60
61
///
/// An object of this class is associated with every packet (or request)
62
63
/// processed by the server.  It forms the principle means of passing data
/// between the server and the user-library callouts.
64
65
66
67
68
69
///
/// The class allows access to the following information:
///
/// - Arguments.  When the callouts associated with a hook are called, they
///   are passed information by the server (and can return information to it)
///   through name/value pairs.  Each of these pairs is an argument and the
70
///   information is accessed through the {get,set}Argument() methods.
71
72
///
/// - Per-packet context.  Each packet has a context associated with it, this
73
///   context being  on a per-library basis.  In other words, As a packet passes
74
75
76
///   through the callouts associated with a given library, the callouts can
///   associate and retrieve information with the packet.  The per-library
///   nature of the context means that the callouts within a given library can
77
///   pass packet-specific information between one another, but they cannot pass
78
79
80
81
82
///   information to callous within another library.  Typically such context
///   is created in the "context_create" callout and destroyed in the
///   "context_destroy" callout.  The information is accessed through the
///   {get,set}Context() methods.
///
83
84
85
86
87
/// - Per-library handle (LibraryHandle). The library handle allows the
///   callout to dynamically register and deregister callouts. In the latter
///   case, only functions registered by functions in the same library as the
///   callout doing the deregistration can be removed: callouts registered by
///   other libraries cannot be modified.
88
89
90
91

class CalloutHandle {
public:

92
93
94
95
96
97
98
99
    /// Typedef to allow abbreviation of iterator specification in methods.
    /// The std::string is the argument name and the "boost::any" is the
    /// corresponding value associated with it.
    typedef std::map<std::string, boost::any> ElementCollection;

    /// Typedef to allow abbreviations in specifications when accessing
    /// context.  The ElementCollection is the name/value collection for
    /// a particular context.  The "int" corresponds to the index of an
100
101
    /// associated library - there is a 1:1 correspondence between libraries
    /// and a name.value collection.
102
103
    ///
    /// The collection of contexts is stored in a map, as not every library
104
    /// will require creation of a context associated with each packet.  In
105
106
107
108
    /// addition, the structure is more flexible in that the size does not
    /// need to be set when the CalloutHandle is constructed.
    typedef std::map<int, ElementCollection> ContextCollection;

109
110
    /// @brief Constructor
    ///
111
112
113
    /// Creates the object and calls the callouts on the "context_create"
    /// hook.
    ///
114
115
116
117
118
119
120
121
122
123
    /// Of the two arguments passed, only the pointer to the callout manager is
    /// actively used.  The second argument, the pointer to the library manager
    /// collection, is used for lifetime control: after use, the callout handle
    /// may contain pointers to memory allocated by the loaded libraries.  The
    /// used of a shared pointer to the collection of library managers means
    /// that the libraries that could have allocated memory in a callout handle
    /// will not be unloaded until all such handles have been destroyed.  This
    /// issue is discussed in more detail in the documentation for
    /// isc::hooks::LibraryManager.
    ///
Stephen Morris's avatar
Stephen Morris committed
124
    /// @param manager Pointer to the callout manager object.
125
126
127
128
129
    /// @param lmcoll Pointer to the library manager collection.  This has a
    ///        null default for testing purposes.
    CalloutHandle(const boost::shared_ptr<CalloutManager>& manager,
                  const boost::shared_ptr<LibraryManagerCollection>& lmcoll =
                        boost::shared_ptr<LibraryManagerCollection>());
130
131
132
133

    /// @brief Destructor
    ///
    /// Calls the context_destroy callback to release any per-packet context.
134
    /// It also clears stored data to avoid problems during member destruction.
135
    ~CalloutHandle();
136
137
138

    /// @brief Set argument
    ///
139
140
    /// Sets the value of an argument.  The argument is created if it does not
    /// already exist.
141
    ///
142
    /// @param name Name of the argument.
143
    /// @param value Value to set.  That can be of any data type.
144
145
146
147
148
149
150
    template <typename T>
    void setArgument(const std::string& name, T value) {
        arguments_[name] = value;
    }

    /// @brief Get argument
    ///
151
    /// Gets the value of an argument.
152
    ///
153
    /// @param name Name of the element in the argument list to get.
154
155
156
    /// @param value [out] Value to set.  The type of "value" is important:
    ///        it must match the type of the value set.
    ///
157
158
159
160
    /// @throw NoSuchArgument No argument with the given name is present.
    /// @throw boost::bad_any_cast An argument with the given name is present,
    ///        but the data type of the value is not the same as the type of
    ///        the variable provided to receive the value.
161
162
    template <typename T>
    void getArgument(const std::string& name, T& value) const {
163
        ElementCollection::const_iterator element_ptr = arguments_.find(name);
164
165
166
167
168
169
170
171
172
173
174
175
176
        if (element_ptr == arguments_.end()) {
            isc_throw(NoSuchArgument, "unable to find argument with name " <<
                      name);
        }

        value = boost::any_cast<T>(element_ptr->second);
    }
    
    /// @brief Get argument names
    ///
    /// Returns a vector holding the names of arguments in the argument
    /// vector.
    ///
177
    /// @return Vector of strings reflecting argument names.
178
    std::vector<std::string> getArgumentNames() const;
179
180
181
182
183
184

    /// @brief Delete argument
    ///
    /// Deletes an argument of the given name.  If an argument of that name
    /// does not exist, the method is a no-op.
    ///
185
186
    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
    /// by this method.
187
    ///
188
189
190
191
192
193
194
195
    /// @param name Name of the element in the argument list to set.
    void deleteArgument(const std::string& name) {
        static_cast<void>(arguments_.erase(name));
    }

    /// @brief Delete all arguments
    ///
    /// Deletes all arguments associated with this context.
196
197
198
    ///
    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
    /// deleted by this method.
199
200
201
202
203
204
205
    void deleteAllArguments() {
        arguments_.clear();
    }

    /// @brief Set skip flag
    ///
    /// Sets the "skip" variable in the callout handle.  This variable is
206
207
    /// interrogated by the server to see if the remaining callouts associated
    /// with the current hook should be bypassed.
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
    ///
    /// @param skip New value of the "skip" flag.
    void setSkip(bool skip) {
        skip_ = skip;
    }

    /// @brief Get skip flag
    ///
    /// Gets the current value of the "skip" flag.
    ///
    /// @return Current value of the skip flag.
    bool getSkip() const {
        return (skip_);
    }

223
224
225
226
227
    /// @brief Access current library handle
    ///
    /// Returns a reference to the current library handle.  This function is
    /// only available when called by a callout (which in turn is called
    /// through the "callCallouts" method), as it is only then that the current
228
229
    /// library index is valid.  A callout uses the library handle to
    /// dynamically register or deregister callouts.
230
    ///
231
    /// @return Reference to the library handle.
232
    ///
233
234
235
    /// @throw InvalidIndex thrown if this method is called when the current
    ///        library index is invalid (typically if it is called outside of
    ///        the active callout).
236
237
238
239
240
241
242
    LibraryHandle& getLibraryHandle() const;

    /// @brief Set context
    ///
    /// Sets an element in the context associated with the current library.  If
    /// an element of the name is already present, it is replaced.
    ///
243
244
    /// @param name Name of the element in the context to set.
    /// @param value Value to set.
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
    template <typename T>
    void setContext(const std::string& name, T value) {
        getContextForLibrary()[name] = value;
    }

    /// @brief Get context
    ///
    /// Gets an element from the context associated with the current library.
    ///
    /// @param name Name of the element in the context to get.
    /// @param value [out] Value to set.  The type of "value" is important:
    ///        it must match the type of the value set.
    ///
    /// @throw NoSuchCalloutContext Thrown if no context element with the name
    ///        "name" is present.
260
261
262
    /// @throw boost::bad_any_cast Thrown if the context element is present
    ///        but the type of the data is not the same as the type of the
    ///        variable provided to receive its value.
263
264
265
266
267
268
269
270
    template <typename T>
    void getContext(const std::string& name, T& value) const {
        const ElementCollection& lib_context = getContextForLibrary();

        ElementCollection::const_iterator element_ptr = lib_context.find(name);
        if (element_ptr == lib_context.end()) {
            isc_throw(NoSuchCalloutContext, "unable to find callout context "
                      "item " << name << " in the context associated with "
271
                      "current library");
272
273
274
275
276
277
278
279
280
281
        }

        value = boost::any_cast<T>(element_ptr->second);
    }
    
    /// @brief Get context names
    ///
    /// Returns a vector holding the names of items in the context associated
    /// with the current library.
    ///
282
283
    /// @return Vector of strings reflecting the names of items in the callout
    ///         context associated with the current library.
284
285
286
287
288
289
290
291
292
293
294
    std::vector<std::string> getContextNames() const;

    /// @brief Delete context element
    ///
    /// Deletes an item of the given name from the context associated with the
    /// current library.  If an item  of that name does not exist, the method is
    /// a no-op.
    ///
    /// N.B. If the element is a raw pointer, the pointed-to data is NOT deleted
    /// by this.
    ///
295
    /// @param name Name of the context item to delete.
296
297
298
299
300
301
302
303
304
305
306
307
308
309
    void deleteContext(const std::string& name) {
        static_cast<void>(getContextForLibrary().erase(name));
    }

    /// @brief Delete all context items
    ///
    /// Deletes all items from the context associated with the current library.
    ///
    /// N.B. If any elements are raw pointers, the pointed-to data is NOT
    /// deleted by this.
    void deleteAllContext() {
        getContextForLibrary().clear();
    }

Stephen Morris's avatar
Stephen Morris committed
310
311
312
313
314
315
316
317
    /// @brief Get hook name
    ///
    /// Get the name of the hook to which the current callout is attached.
    /// This can be the null string if the CalloutHandle is being accessed
    /// outside of the CalloutManager's "callCallouts" method.
    ///
    /// @return Name of the current hook or the empty string if none.
    std::string getHookName() const;
318
319

private:
320
321
322
323
324
325
    /// @brief Check index
    ///
    /// Gets the current library index, throwing an exception if it is not set
    /// or is invalid for the current library collection.
    ///
    /// @return Current library index, valid for this library collection.
326
327
328
    ///
    /// @throw InvalidIndex current library index is not valid for the library
    ///        handle collection.
329
330
331
332
    int getLibraryIndex() const;

    /// @brief Return reference to context for current library
    ///
333
334
335
    /// Called by all context-setting functions, this returns a reference to
    /// the callout context for the current library, creating a context if it
    /// does not exist.
336
337
338
    ///
    /// @return Reference to the collection of name/value pairs associated
    ///         with the current library.
339
340
341
    ///
    /// @throw InvalidIndex current library index is not valid for the library
    ///        handle collection.
342
343
344
345
    ElementCollection& getContextForLibrary();

    /// @brief Return reference to context for current library (const version)
    ///
346
347
348
    /// Called by all context-accessing functions, this a reference to the
    /// callout context for the current library.  An exception is thrown if
    /// it does not exist.
349
350
351
352
353
354
355
356
357
    ///
    /// @return Reference to the collection of name/value pairs associated
    ///         with the current library.
    ///
    /// @throw NoSuchCalloutContext Thrown if there is no ElementCollection
    ///        associated with the current library.
    const ElementCollection& getContextForLibrary() const;

    // Member variables
358
359
360
361
    
    /// Pointer to the collection of libraries for which this handle has been
    /// created.
    boost::shared_ptr<LibraryManagerCollection> lm_collection_;
362

363
    /// Collection of arguments passed to the callouts
364
365
366
367
    ElementCollection arguments_;

    /// Context collection - there is one entry per library context.
    ContextCollection context_collection_;
368

Stephen Morris's avatar
Stephen Morris committed
369
370
    /// Callout manager.
    boost::shared_ptr<CalloutManager> manager_;
371
372
373
374
375

    /// "Skip" flag, indicating if the caller should bypass remaining callouts.
    bool skip_;
};

376
377
378
/// A shared pointer to a CalloutHandle object.
typedef boost::shared_ptr<CalloutHandle> CalloutHandlePtr;

379
} // namespace hooks
380
381
382
383
} // namespace isc


#endif // CALLOUT_HANDLE_H