Kea 2.7.8
library_manager.cc
Go to the documentation of this file.
1// Copyright (C) 2013-2025 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
142LibraryManager::checkMultiThreadingCompatible(bool multi_threading_enabled) const {
143
144 // Compatible with single-threaded.
145 if (!multi_threading_enabled) {
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 int old_index = manager_->getLibraryHandle().getLibraryIndex();
215 try {
216 manager_->setLibraryIndex(index_);
217 manager_->getLibraryHandle().setLibraryIndex(index_);
218 status = (*pc.loadPtr())(manager_->getLibraryHandle());
219 } catch (const isc::Exception& ex) {
221 .arg(library_name_).arg(ex.what());
222 manager_->getLibraryHandle().setLibraryIndex(old_index);
223 return (false);
224 } catch (...) {
225 LOG_ERROR(hooks_logger, HOOKS_LOAD_EXCEPTION).arg(library_name_);
226 manager_->getLibraryHandle().setLibraryIndex(old_index);
227 return (false);
228 }
229
230 manager_->getLibraryHandle().setLibraryIndex(old_index);
231
232 if (status != 0) {
233 LOG_ERROR(hooks_logger, HOOKS_LOAD_ERROR).arg(library_name_)
234 .arg(status);
235 return (false);
236 } else {
238 .arg(library_name_);
239 }
240
241 } else {
243 .arg(library_name_);
244 }
245
246 return (true);
247}
248
249
250// Run the "unload" function if present.
251
252bool
254
255 // Nothing to do.
256 if (dl_handle_ == NULL) {
257 return (true);
258 }
259
260 // Call once.
261 if (index_ < 0) {
262 return (true);
263 }
264
265 // Get the pointer to the "load" function.
266 bool result = false;
267 PointerConverter pc(dlsym(dl_handle_, UNLOAD_FUNCTION_NAME));
268 if (pc.unloadPtr() != NULL) {
269
270 // Call the load() function with the library handle. We need to set
271 // the CalloutManager's index appropriately. We'll invalidate it
272 // afterwards.
273 int status = -1;
274 try {
275 status = (*pc.unloadPtr())();
276 result = true;
277 } catch (const isc::Exception& ex) {
279 .arg(library_name_).arg(ex.what());
280 } catch (...) {
281 // Exception generated. Note a warning as the unload will occur
282 // anyway.
283 LOG_WARN(hooks_logger, HOOKS_UNLOAD_EXCEPTION).arg(library_name_);
284 }
285
286 if (result) {
287 if (status != 0) {
288 LOG_ERROR(hooks_logger, HOOKS_UNLOAD_ERROR).arg(library_name_)
289 .arg(status);
290 result = false;
291 } else {
293 .arg(library_name_);
294 }
295 }
296 } else {
298 .arg(library_name_);
299 result = true;
300 }
301
302 // Regardless of status, remove all callouts associated with this
303 // library on all hooks.
304 vector<string> hooks = ServerHooks::getServerHooks().getHookNames();
305 manager_->setLibraryIndex(index_);
306 for (size_t i = 0; i < hooks.size(); ++i) {
307 bool removed = manager_->deregisterAllCallouts(hooks[i], index_);
308 if (removed) {
310 .arg(hooks[i]).arg(library_name_);
311 }
312 }
313
314 // Mark as unload() ran.
315 index_ = -1;
316
317 return (result);
318}
319
320// The main library loading function.
321
322bool
323LibraryManager::loadLibrary(bool multi_threading_enabled) {
325 .arg(library_name_);
326
327 // In the following, if a method such as openLibrary() fails, it will
328 // have issued an error message so there is no need to issue another one
329 // here.
330
331 // Open the library (which is a check that it exists and is accessible).
332 if (openLibrary()) {
333
334 // The hook libraries provide their own log messages and logger
335 // instances. This step is required to register log messages for
336 // the library being loaded in the global dictionary. Ideally, this
337 // should be called after all libraries have been loaded but we're
338 // going to call the version() and load() functions here and these
339 // functions may already contain logging statements.
341
342 // The log messages registered by the new hook library may duplicate
343 // some of the existing messages. Log warning for each duplicated
344 // message now.
346
347 // Library opened OK, see if a version function is present and if so,
348 // check what value it returns. Check multi-threading compatibility.
349 if (checkVersion() && checkMultiThreadingCompatible(multi_threading_enabled)) {
350 // Version OK, so now register the standard callouts and call the
351 // library's load() function if present.
353 if (runLoad()) {
354
355 // Success - the library has been successfully loaded.
356 LOG_INFO(hooks_logger, HOOKS_LIBRARY_LOADED).arg(library_name_);
357 return (true);
358
359 } else {
360
361 // The load function failed, so back out. We can't just close
362 // the library as (a) we need to call the library's "unload"
363 // function (if present) in case "load" allocated resources that
364 // need to be freed and (b) we need to remove any callouts that
365 // have been installed.
366 static_cast<void>(prepareUnloadLibrary());
367 }
368 }
369
370 // Either the version check or call to load() failed, so close the
371 // library and free up resources. Ignore the status return here - we
372 // already know there's an error and will have output a message.
373 static_cast<void>(closeLibrary());
374 }
375
376 return (false);
377}
378
379// The library unloading function. Call the unload() function (if present),
380// remove callouts from the callout manager, then close the library. This is
381// only run if the library is still loaded and is a no-op if the library is
382// not open.
383
384bool
386 bool result = true;
387 if (dl_handle_ != NULL) {
389 .arg(library_name_);
390
391 // Call the unload() function if present. Note that this is done first
392 // - operations take place in the reverse order to which they were done
393 // when the library was loaded.
394 if (index_ >= 0) {
395 result = prepareUnloadLibrary();
396 }
397
398 // ... and close the library.
399 result = closeLibrary() && result;
400 if (result) {
401
402 // Issue the informational message only if the library was unloaded
403 // with no problems. If there was an issue, an error message would
404 // have been issued.
405 LOG_INFO(hooks_logger, HOOKS_LIBRARY_UNLOADED).arg(library_name_);
406 }
407 }
408 return (result);
409}
410
411// Validate the library. We must be able to open it, and the version function
412// must both exist and return the right number. Note that this is a static
413// method.
414
415bool
416LibraryManager::validateLibrary(const std::string& name, bool multi_threading_enabled) {
417 // Instantiate a library manager for the validation. We use the private
418 // constructor as we don't supply a CalloutManager.
419 LibraryManager manager(name);
420
421 // Try to open it and, if we succeed, check the version.
422 bool validated = manager.openLibrary() && manager.checkVersion() &&
423 manager.checkMultiThreadingCompatible(multi_threading_enabled);
424
425 // Regardless of whether the version checked out, close the library. (This
426 // is a no-op if the library failed to open.)
427 static_cast<void>(manager.closeLibrary());
428
429 return (validated);
430}
431
432// @note Moved from its own hooks.cc file to avoid undefined reference
433// with static link.
436 isc::log::initLogger(std::string("userlib"));
437 }
438}
439
440} // namespace hooks
441} // 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.
static bool validateLibrary(const std::string &name, bool multi_threading_enabled=false)
Validate library.
bool openLibrary()
Open library.
bool closeLibrary()
Close library.
bool checkMultiThreadingCompatible(bool multi_threading_enabled) const
Check multi-threading compatibility.
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 loadLibrary(bool multi_threading_enabled=false)
Loads a library.
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.
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.
#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.