Kea  2.3.1-git
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>
12 #include <hooks/callout_manager.h>
13 #include <hooks/library_handle.h>
14 #include <hooks/library_manager.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 
27 using namespace std;
28 
29 namespace isc {
30 namespace hooks {
31 
32 // Constructor (used by external agency)
33 LibraryManager::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.
52 LibraryManager::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 
71 bool
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 
87 bool
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 
108 bool
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 (...) {
118  LOG_ERROR(hooks_logger, HOOKS_VERSION_EXCEPTION).arg(library_name_);
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 {
129  LOG_ERROR(hooks_logger, HOOKS_INCORRECT_VERSION).arg(library_name_)
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 
141 bool
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;
152  if (pc.multiThreadingCompatiblePtr()) {
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 
175 void
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 
202 bool
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 
246 bool
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 
316 bool
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 
378 bool
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 
409 bool
410 LibraryManager::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
const isc::log::MessageID HOOKS_LOAD_ERROR
std::vector< std::string > getHookNames() const
Get hook names.
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
static void logDuplicatedMessages()
List duplicated log messages.
bool unloadLibrary()
Unloads a library.
multi_threading_compatible_function_ptr multiThreadingCompatiblePtr() const
Return pointer to multi_threading_compatible function.
void hooksStaticLinkInit()
User-Library Initialization for Statically-Linked Kea.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
static MultiThreadingMgr & instance()
Returns a single instance of Multi Threading Manager.
const isc::log::MessageID HOOKS_LIBRARY_LOADING
const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_NOT_COMPATIBLE
static ServerHooks & getServerHooks()
Return ServerHooks object.
const isc::log::MessageID HOOKS_LIBRARY_LOADED
bool loadLibrary()
Loads a library.
const isc::log::MessageID HOOKS_LOAD_FRAMEWORK_EXCEPTION
version_function_ptr versionPtr() const
Return pointer to version function.
const isc::log::MessageID HOOKS_UNLOAD_FRAMEWORK_EXCEPTION
const isc::log::MessageID HOOKS_LIBRARY_MULTI_THREADING_COMPATIBLE
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
const isc::log::MessageID HOOKS_LIBRARY_UNLOADED
STL namespace.
static bool validateLibrary(const std::string &name)
Validate library.
const isc::log::MessageID HOOKS_LIBRARY_CLOSED
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Local class for conversion of void pointers to function pointers.
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.
bool checkMultiThreadingCompatible() const
Check multi-threading compatibility.
const isc::log::MessageID HOOKS_UNLOAD_EXCEPTION
bool checkVersion() const
Check library version.
const isc::log::MessageID HOOKS_LIBRARY_VERSION
bool isLoggingInitialized()
Is logging initialized?
int version()
returns Kea hooks version.
const isc::log::MessageID HOOKS_VERSION_EXCEPTION
const isc::log::MessageID HOOKS_LOAD_SUCCESS
const isc::log::MessageID HOOKS_OPEN_ERROR
void registerStandardCallouts()
Register standard callouts.
const isc::log::MessageID HOOKS_NO_VERSION
const int HOOKS_DBG_CALLS
Definition: hooks_log.h:25
const isc::log::MessageID HOOKS_LOAD_EXCEPTION
const isc::log::MessageID HOOKS_CALLOUTS_REMOVED
load_function_ptr loadPtr() const
Return pointer to load function.
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-lfc.
const isc::log::MessageID HOOKS_NO_LOAD
const isc::log::MessageID HOOKS_UNLOAD_SUCCESS
Logging initialization functions.
isc::log::Logger hooks_logger("hooks")
Hooks Logger.
Definition: hooks_log.h:37
bool prepareUnloadLibrary()
Prepares library unloading.
unload_function_ptr unloadPtr() const
Return pointer to unload function.
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
bool runLoad()
Run the load function if present.
CalloutPtr calloutPtr() const
Return pointer to callout function.
bool openLibrary()
Open library.
void initLogger(const string &root, isc::log::Severity severity, int dbglevel, const char *file, bool buffer)
Run-time initialization.
No Callout Manager.
const isc::log::MessageID HOOKS_INCORRECT_VERSION
bool closeLibrary()
Close library.
Server hook collection.
Definition: server_hooks.h:62
const isc::log::MessageID HOOKS_UNLOAD_ERROR
const isc::log::MessageID HOOKS_NO_UNLOAD
LibraryManager(const std::string &name, int index, const boost::shared_ptr< CalloutManager > &manager)
Constructor.
const isc::log::MessageID HOOKS_MULTI_THREADING_COMPATIBLE_EXCEPTION
const int HOOKS_DBG_TRACE
Hooks debug Logging levels.
Definition: hooks_log.h:22
const isc::log::MessageID HOOKS_STD_CALLOUT_REGISTERED
const isc::log::MessageID HOOKS_LIBRARY_UNLOADING
const isc::log::MessageID HOOKS_CLOSE_ERROR