Kea 2.2.0
library_manager.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2020 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#include <config.h>
8
10#include <hooks/hooks.h>
11#include <hooks/hooks_log.h>
16#include <hooks/server_hooks.h>
17#include <log/logger_manager.h>
18#include <log/logger_support.h>
21
22#include <string>
23#include <vector>
24
25#include <dlfcn.h>
26
27using namespace std;
28
29namespace isc {
30namespace hooks {
31
32// Constructor (used by external agency)
33LibraryManager::LibraryManager(const std::string& name, int index,
34 const boost::shared_ptr<CalloutManager>& manager)
35 : dl_handle_(NULL), index_(index), manager_(manager),
36 library_name_(name),
37 server_hooks_(ServerHooks::getServerHooksPtr())
38{
39 if (!manager) {
40 isc_throw(NoCalloutManager, "must specify a CalloutManager when "
41 "instantiating a LibraryManager object");
42 }
43}
44
45// Constructor (used by "validate" for library validation). Note that this
46// sets "manager_" to not point to anything, which means that methods such as
47// registerStandardCallout() will fail, probably with a segmentation fault.
48// There are no checks for this condition in those methods: this constructor
49// is declared "private", so can only be executed by a method in this class.
50// The only method to do so is "validateLibrary", which takes care not to call
51// methods requiring a non-NULL manager.
52LibraryManager::LibraryManager(const std::string& name)
53 : dl_handle_(NULL), index_(-1), manager_(), library_name_(name)
54{}
55
56// Destructor.
58 if (index_ >= 0) {
59 // LibraryManager instantiated to load a library, so ensure that
60 // it is unloaded before exiting.
61 static_cast<void>(prepareUnloadLibrary());
62 }
63
64 // LibraryManager instantiated to validate a library, so just ensure
65 // that it is closed before exiting.
66 static_cast<void>(closeLibrary());
67}
68
69// Open the library
70
71bool
73
74 // Open the library. We'll resolve names now, so that if there are any
75 // issues we don't bugcheck in the middle of apparently unrelated code.
76 dl_handle_ = dlopen(library_name_.c_str(), RTLD_NOW | RTLD_LOCAL);
77 if (dl_handle_ == NULL) {
78 LOG_ERROR(hooks_logger, HOOKS_OPEN_ERROR).arg(library_name_)
79 .arg(dlerror());
80 }
81
82 return (dl_handle_ != NULL);
83}
84
85// Close the library if not already open
86
87bool
89
90 // Close the library if it is open. (If not, this is a no-op.)
91 int status = 0;
92 if (dl_handle_ != NULL) {
93 status = dlclose(dl_handle_);
94 dl_handle_ = NULL;
95 if (status != 0) {
96 LOG_ERROR(hooks_logger, HOOKS_CLOSE_ERROR).arg(library_name_)
97 .arg(dlerror());
98 } else {
99 LOG_INFO(hooks_logger, HOOKS_LIBRARY_CLOSED).arg(library_name_);
100 }
101 }
102
103 return (status == 0);
104}
105
106// Check the version of the library
107
108bool
110
111 // Get the pointer to the "version" function.
112 PointerConverter pc(dlsym(dl_handle_, VERSION_FUNCTION_NAME));
113 if (pc.versionPtr() != NULL) {
114 int version = KEA_HOOKS_VERSION - 1; // This is an invalid value
115 try {
116 version = (*pc.versionPtr())();
117 } catch (...) {
119 return (false);
120 }
121
122 if (version == KEA_HOOKS_VERSION) {
123 // All OK, version checks out
125 .arg(library_name_).arg(version);
126 return (true);
127
128 } else {
130 .arg(version).arg(KEA_HOOKS_VERSION);
131 }
132 } else {
133 LOG_ERROR(hooks_logger, HOOKS_NO_VERSION).arg(library_name_);
134 }
135
136 return (false);
137}
138
139// Check the multi-threading compatibility of the library
140
141bool
143
144 // Compatible with single-threaded.
145 if (!util::MultiThreadingMgr::instance().getMode()) {
146 return (true);
147 }
148
149 // Get the pointer to the "multi_threading_compatible" function.
150 PointerConverter pc(dlsym(dl_handle_, MULTI_THREADING_COMPATIBLE_FUNCTION_NAME));
151 int compatible = 0;
153 try {
154 compatible = (*pc.multiThreadingCompatiblePtr())();
155 } catch (...) {
157 .arg(library_name_);
158 return (false);
159 }
160
163 .arg(library_name_)
164 .arg(compatible);
165 }
166 if (compatible == 0) {
168 .arg(library_name_);
169 }
170 return (compatible != 0);
171}
172
173// Register the standard callouts
174
175void
177 // Set the library index for doing the registration. This is picked up
178 // when the library handle is created.
179 manager_->setLibraryIndex(index_);
180
181 // Iterate through the list of known hooks
182 vector<string> hook_names = ServerHooks::getServerHooks().getHookNames();
183 for (size_t i = 0; i < hook_names.size(); ++i) {
184
185 // Look up the symbol
186 void* dlsym_ptr = dlsym(dl_handle_, hook_names[i].c_str());
187 PointerConverter pc(dlsym_ptr);
188 if (pc.calloutPtr() != NULL) {
189 // Found a symbol, so register it.
190 manager_->getLibraryHandle().registerCallout(hook_names[i],
191 pc.calloutPtr());
193 HOOKS_STD_CALLOUT_REGISTERED).arg(library_name_)
194 .arg(hook_names[i]).arg(dlsym_ptr);
195
196 }
197 }
198}
199
200// Run the "load" function if present.
201
202bool
204
205 // Get the pointer to the "load" function.
206 PointerConverter pc(dlsym(dl_handle_, LOAD_FUNCTION_NAME));
207 if (pc.loadPtr() != NULL) {
208
209 // Call the load() function with the library handle. We need to set
210 // the CalloutManager's index appropriately. We'll invalidate it
211 // afterwards.
212
213 int status = -1;
214 try {
215 manager_->setLibraryIndex(index_);
216 status = (*pc.loadPtr())(manager_->getLibraryHandle());
217 } catch (const isc::Exception& ex) {
219 .arg(library_name_).arg(ex.what());
220 return (false);
221 } catch (...) {
222 LOG_ERROR(hooks_logger, HOOKS_LOAD_EXCEPTION).arg(library_name_);
223 return (false);
224 }
225
226 if (status != 0) {
227 LOG_ERROR(hooks_logger, HOOKS_LOAD_ERROR).arg(library_name_)
228 .arg(status);
229 return (false);
230 } else {
232 .arg(library_name_);
233 }
234
235 } else {
237 .arg(library_name_);
238 }
239
240 return (true);
241}
242
243
244// Run the "unload" function if present.
245
246bool
248
249 // Nothing to do.
250 if (dl_handle_ == NULL) {
251 return (true);
252 }
253
254 // Call once.
255 if (index_ < 0) {
256 return (true);
257 }
258
259 // Get the pointer to the "load" function.
260 bool result = false;
261 PointerConverter pc(dlsym(dl_handle_, UNLOAD_FUNCTION_NAME));
262 if (pc.unloadPtr() != NULL) {
263
264 // Call the load() function with the library handle. We need to set
265 // the CalloutManager's index appropriately. We'll invalidate it
266 // afterwards.
267 int status = -1;
268 try {
269 status = (*pc.unloadPtr())();
270 result = true;
271 } catch (const isc::Exception& ex) {
273 .arg(library_name_).arg(ex.what());
274 } catch (...) {
275 // Exception generated. Note a warning as the unload will occur
276 // anyway.
277 LOG_WARN(hooks_logger, HOOKS_UNLOAD_EXCEPTION).arg(library_name_);
278 }
279
280 if (result) {
281 if (status != 0) {
282 LOG_ERROR(hooks_logger, HOOKS_UNLOAD_ERROR).arg(library_name_)
283 .arg(status);
284 result = false;
285 } else {
287 .arg(library_name_);
288 }
289 }
290 } else {
292 .arg(library_name_);
293 result = true;
294 }
295
296 // Regardless of status, remove all callouts associated with this
297 // library on all hooks.
298 vector<string> hooks = ServerHooks::getServerHooks().getHookNames();
299 manager_->setLibraryIndex(index_);
300 for (size_t i = 0; i < hooks.size(); ++i) {
301 bool removed = manager_->deregisterAllCallouts(hooks[i], index_);
302 if (removed) {
304 .arg(hooks[i]).arg(library_name_);
305 }
306 }
307
308 // Mark as unload() ran.
309 index_ = -1;
310
311 return (result);
312}
313
314// The main library loading function.
315
316bool
319 .arg(library_name_);
320
321 // In the following, if a method such as openLibrary() fails, it will
322 // have issued an error message so there is no need to issue another one
323 // here.
324
325 // Open the library (which is a check that it exists and is accessible).
326 if (openLibrary()) {
327
328 // The hook libraries provide their own log messages and logger
329 // instances. This step is required to register log messages for
330 // the library being loaded in the global dictionary. Ideally, this
331 // should be called after all libraries have been loaded but we're
332 // going to call the version() and load() functions here and these
333 // functions may already contain logging statements.
335
336 // The log messages registered by the new hook library may duplicate
337 // some of the existing messages. Log warning for each duplicated
338 // message now.
340
341 // Library opened OK, see if a version function is present and if so,
342 // check what value it returns. Check multi-threading compatibility.
344 // Version OK, so now register the standard callouts and call the
345 // library's load() function if present.
347 if (runLoad()) {
348
349 // Success - the library has been successfully loaded.
350 LOG_INFO(hooks_logger, HOOKS_LIBRARY_LOADED).arg(library_name_);
351 return (true);
352
353 } else {
354
355 // The load function failed, so back out. We can't just close
356 // the library as (a) we need to call the library's "unload"
357 // function (if present) in case "load" allocated resources that
358 // need to be freed and (b) we need to remove any callouts that
359 // have been installed.
360 static_cast<void>(prepareUnloadLibrary());
361 }
362 }
363
364 // Either the version check or call to load() failed, so close the
365 // library and free up resources. Ignore the status return here - we
366 // already know there's an error and will have output a message.
367 static_cast<void>(closeLibrary());
368 }
369
370 return (false);
371}
372
373// The library unloading function. Call the unload() function (if present),
374// remove callouts from the callout manager, then close the library. This is
375// only run if the library is still loaded and is a no-op if the library is
376// not open.
377
378bool
380 bool result = true;
381 if (dl_handle_ != NULL) {
383 .arg(library_name_);
384
385 // Call the unload() function if present. Note that this is done first
386 // - operations take place in the reverse order to which they were done
387 // when the library was loaded.
388 if (index_ >= 0) {
389 result = prepareUnloadLibrary();
390 }
391
392 // ... and close the library.
393 result = closeLibrary() && result;
394 if (result) {
395
396 // Issue the informational message only if the library was unloaded
397 // with no problems. If there was an issue, an error message would
398 // have been issued.
399 LOG_INFO(hooks_logger, HOOKS_LIBRARY_UNLOADED).arg(library_name_);
400 }
401 }
402 return (result);
403}
404
405// Validate the library. We must be able to open it, and the version function
406// must both exist and return the right number. Note that this is a static
407// method.
408
409bool
410LibraryManager::validateLibrary(const std::string& name) {
411 // Instantiate a library manager for the validation. We use the private
412 // constructor as we don't supply a CalloutManager.
413 LibraryManager manager(name);
414
415 // Try to open it and, if we succeed, check the version.
416 bool validated = manager.openLibrary() && manager.checkVersion() &&
418
419 // Regardless of whether the version checked out, close the library. (This
420 // is a no-op if the library failed to open.)
421 static_cast<void>(manager.closeLibrary());
422
423 return (validated);
424}
425
426// @note Moved from its own hooks.cc file to avoid undefined reference
427// with static link.
430 isc::log::initLogger(std::string("userlib"));
431 }
432}
433
434} // namespace hooks
435} // namespace isc
int version()
returns Kea hooks version.
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
LibraryManager(const std::string &name, int index, const boost::shared_ptr< CalloutManager > &manager)
Constructor.
bool unloadLibrary()
Unloads a library.
bool loadLibrary()
Loads a library.
bool openLibrary()
Open library.
bool closeLibrary()
Close library.
static bool validateLibrary(const std::string &name)
Validate library.
bool checkVersion() const
Check library version.
void registerStandardCallouts()
Register standard callouts.
bool runLoad()
Run the load function if present.
bool prepareUnloadLibrary()
Prepares library unloading.
bool checkMultiThreadingCompatible() const
Check multi-threading compatibility.
Local class for conversion of void pointers to function pointers.
multi_threading_compatible_function_ptr multiThreadingCompatiblePtr() const
Return pointer to multi_threading_compatible function.
unload_function_ptr unloadPtr() const
Return pointer to unload function.
version_function_ptr versionPtr() const
Return pointer to version function.
CalloutPtr calloutPtr() const
Return pointer to callout function.
load_function_ptr loadPtr() const
Return pointer to load function.
Server hook collection.
Definition: server_hooks.h:62
static ServerHooks & getServerHooks()
Return ServerHooks object.
std::vector< std::string > getHookNames() const
Get hook names.
static void logDuplicatedMessages()
List duplicated log messages.
static void loadDictionary(bool ignore_duplicates=false)
Run-Time Initialization.
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Logging initialization functions.
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
void hooksStaticLinkInit()
User-Library Initialization for Statically-Linked Kea.
const isc::log::MessageID HOOKS_LIBRARY_UNLOADED
const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE
const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE
const isc::log::MessageID HOOKS_LOAD_FRAMEWORK_EXCEPTION
const isc::log::MessageID HOOKS_UNLOAD_SUCCESS
const isc::log::MessageID HOOKS_LIBRARY_VERSION
const isc::log::MessageID HOOKS_NO_UNLOAD
const isc::log::MessageID HOOKS_VERSION_EXCEPTION
const isc::log::MessageID HOOKS_STD_CALLOUT_REGISTERED
isc::log::Logger hooks_logger("hooks")
Hooks Logger.
Definition: hooks_log.h:37
const isc::log::MessageID HOOKS_LIBRARY_UNLOADING
const isc::log::MessageID HOOKS_LIBRARY_LOADED
const isc::log::MessageID HOOKS_INCORRECT_VERSION
const isc::log::MessageID HOOKS_LIBRARY_LOADING
const isc::log::MessageID HOOKS_UNLOAD_EXCEPTION
const int HOOKS_DBG_CALLS
Definition: hooks_log.h:25
const isc::log::MessageID HOOKS_LOAD_ERROR
const int HOOKS_DBG_TRACE
Hooks debug Logging levels.
Definition: hooks_log.h:22
const isc::log::MessageID HOOKS_LIBRARY_CLOSED
const isc::log::MessageID HOOKS_LOAD_EXCEPTION
const isc::log::MessageID HOOKS_OPEN_ERROR
const isc::log::MessageID HOOKS_UNLOAD_FRAMEWORK_EXCEPTION
const isc::log::MessageID HOOKS_UNLOAD_ERROR
const isc::log::MessageID HOOKS_LOAD_SUCCESS
const isc::log::MessageID HOOKS_CLOSE_ERROR
const isc::log::MessageID HOOKS_NO_VERSION
const isc::log::MessageID HOOKS_NO_LOAD
const isc::log::MessageID HOOKS_CALLOUTS_REMOVED
const isc::log::MessageID HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION
void initLogger(const string &root, isc::log::Severity severity, int dbglevel, const char *file, bool buffer)
Run-time initialization.
bool isLoggingInitialized()
Is logging initialized?
Defines the logger used by the top-level component of kea-lfc.