Kea  1.5.0
library_manager.cc
Go to the documentation of this file.
1 // Copyright (C) 2013-2017 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>
20 
21 #include <string>
22 #include <vector>
23 
24 #include <dlfcn.h>
25 
26 using namespace std;
27 
28 namespace isc {
29 namespace hooks {
30 
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 (manager_) {
59  // LibraryManager instantiated to load a library, so ensure that
60  // it is unloaded before exiting.
61  static_cast<void>(unloadLibrary());
62  } else {
63  // LibraryManager instantiated to validate a library, so just ensure
64  // that it is closed before exiting.
65  static_cast<void>(closeLibrary());
66  }
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  }
99  }
100 
101  return (status == 0);
102 }
103 
104 // Check the version of the library
105 
106 bool
108 
109  // Get the pointer to the "version" function.
110  PointerConverter pc(dlsym(dl_handle_, VERSION_FUNCTION_NAME));
111  if (pc.versionPtr() != NULL) {
112  int version = KEA_HOOKS_VERSION - 1; // This is an invalid value
113  try {
114  version = (*pc.versionPtr())();
115  } catch (...) {
116  LOG_ERROR(hooks_logger, HOOKS_VERSION_EXCEPTION).arg(library_name_);
117  return (false);
118  }
119 
120  if (version == KEA_HOOKS_VERSION) {
121  // All OK, version checks out
122  LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_LIBRARY_VERSION)
123  .arg(library_name_).arg(version);
124  return (true);
125 
126  } else {
127  LOG_ERROR(hooks_logger, HOOKS_INCORRECT_VERSION).arg(library_name_)
128  .arg(version).arg(KEA_HOOKS_VERSION);
129  }
130  } else {
131  LOG_ERROR(hooks_logger, HOOKS_NO_VERSION).arg(library_name_);
132  }
133 
134  return (false);
135 }
136 
137 // Register the standard callouts
138 
139 void
141  // Set the library index for doing the registration. This is picked up
142  // when the library handle is created.
143  manager_->setLibraryIndex(index_);
144 
145  // Iterate through the list of known hooks
146  vector<string> hook_names = ServerHooks::getServerHooks().getHookNames();
147  for (size_t i = 0; i < hook_names.size(); ++i) {
148 
149  // Look up the symbol
150  void* dlsym_ptr = dlsym(dl_handle_, hook_names[i].c_str());
151  PointerConverter pc(dlsym_ptr);
152  if (pc.calloutPtr() != NULL) {
153  // Found a symbol, so register it.
154  manager_->getLibraryHandle().registerCallout(hook_names[i],
155  pc.calloutPtr());
157  HOOKS_STD_CALLOUT_REGISTERED).arg(library_name_)
158  .arg(hook_names[i]).arg(dlsym_ptr);
159 
160  }
161  }
162 }
163 
164 // Run the "load" function if present.
165 
166 bool
168 
169  // Get the pointer to the "load" function.
170  PointerConverter pc(dlsym(dl_handle_, LOAD_FUNCTION_NAME));
171  if (pc.loadPtr() != NULL) {
172 
173  // Call the load() function with the library handle. We need to set
174  // the CalloutManager's index appropriately. We'll invalidate it
175  // afterwards.
176 
177  int status = -1;
178  try {
179  manager_->setLibraryIndex(index_);
180  status = (*pc.loadPtr())(manager_->getLibraryHandle());
181  } catch (const isc::Exception& ex) {
182  LOG_ERROR(hooks_logger, HOOKS_LOAD_FRAMEWORK_EXCEPTION)
183  .arg(library_name_).arg(ex.what());
184  return (false);
185  } catch (...) {
186  LOG_ERROR(hooks_logger, HOOKS_LOAD_EXCEPTION).arg(library_name_);
187  return (false);
188  }
189 
190  if (status != 0) {
191  LOG_ERROR(hooks_logger, HOOKS_LOAD_ERROR).arg(library_name_)
192  .arg(status);
193  return (false);
194  } else {
195  LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LOAD_SUCCESS)
196  .arg(library_name_);
197  }
198 
199  } else {
200  LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_NO_LOAD)
201  .arg(library_name_);
202  }
203 
204  return (true);
205 }
206 
207 
208 // Run the "unload" function if present.
209 
210 bool
212 
213  // Get the pointer to the "load" function.
214  PointerConverter pc(dlsym(dl_handle_, UNLOAD_FUNCTION_NAME));
215  if (pc.unloadPtr() != NULL) {
216 
217  // Call the load() function with the library handle. We need to set
218  // the CalloutManager's index appropriately. We'll invalidate it
219  // afterwards.
220  int status = -1;
221  try {
222  status = (*pc.unloadPtr())();
223  } catch (const isc::Exception& ex) {
224  LOG_ERROR(hooks_logger, HOOKS_UNLOAD_FRAMEWORK_EXCEPTION)
225  .arg(library_name_).arg(ex.what());
226  return (false);
227  } catch (...) {
228  // Exception generated. Note a warning as the unload will occur
229  // anyway.
230  LOG_WARN(hooks_logger, HOOKS_UNLOAD_EXCEPTION).arg(library_name_);
231  return (false);
232  }
233 
234  if (status != 0) {
235  LOG_ERROR(hooks_logger, HOOKS_UNLOAD_ERROR).arg(library_name_)
236  .arg(status);
237  return (false);
238  } else {
239  LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_UNLOAD_SUCCESS)
240  .arg(library_name_);
241  }
242  } else {
243  LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_NO_UNLOAD)
244  .arg(library_name_);
245  }
246 
247  return (true);
248 }
249 
250 // The main library loading function.
251 
252 bool
254  LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LIBRARY_LOADING)
255  .arg(library_name_);
256 
257  // In the following, if a method such as openLibrary() fails, it will
258  // have issued an error message so there is no need to issue another one
259  // here.
260 
261  // Open the library (which is a check that it exists and is accessible).
262  if (openLibrary()) {
263 
264  // The hook libraries provide their own log messages and logger
265  // instances. This step is required to register log messages for
266  // the library being loaded in the global dictionary. Ideally, this
267  // should be called after all libraries have been loaded but we're
268  // going to call the version() and load() functions here and these
269  // functions may already contain logging statements.
271 
272  // The log messages registered by the new hook library may duplicate
273  // some of the existing messages. Log warning for each duplicated
274  // message now.
276 
277  // Library opened OK, see if a version function is present and if so,
278  // check what value it returns.
279  if (checkVersion()) {
280 
281  // Version OK, so now register the standard callouts and call the
282  // library's load() function if present.
284  if (runLoad()) {
285 
286  // Success - the library has been successfully loaded.
287  LOG_INFO(hooks_logger, HOOKS_LIBRARY_LOADED).arg(library_name_);
288  return (true);
289 
290  } else {
291 
292  // The load function failed, so back out. We can't just close
293  // the library as (a) we need to call the library's "unload"
294  // function (if present) in case "load" allocated resources that
295  // need to be freed and (b) we need to remove any callouts that
296  // have been installed.
297  static_cast<void>(unloadLibrary());
298  }
299  }
300 
301  // Either the version check or call to load() failed, so close the
302  // library and free up resources. Ignore the status return here - we
303  // already know there's an error and will have output a message.
304  static_cast<void>(closeLibrary());
305  }
306 
307  return (false);
308 }
309 
310 // The library unloading function. Call the unload() function (if present),
311 // remove callouts from the callout manager, then close the library. This is
312 // only run if the library is still loaded and is a no-op if the library is
313 // not open.
314 
315 bool
317  bool result = true;
318  if (dl_handle_ != NULL) {
319  LOG_DEBUG(hooks_logger, HOOKS_DBG_TRACE, HOOKS_LIBRARY_UNLOADING)
320  .arg(library_name_);
321 
322  // Call the unload() function if present. Note that this is done first
323  // - operations take place in the reverse order to which they were done
324  // when the library was loaded.
325  result = runUnload();
326 
327  // Regardless of status, remove all callouts associated with this
328  // library on all hooks.
329  vector<string> hooks = ServerHooks::getServerHooks().getHookNames();
330  manager_->setLibraryIndex(index_);
331  for (size_t i = 0; i < hooks.size(); ++i) {
332  bool removed = manager_->deregisterAllCallouts(hooks[i]);
333  if (removed) {
334  LOG_DEBUG(hooks_logger, HOOKS_DBG_CALLS, HOOKS_CALLOUTS_REMOVED)
335  .arg(hooks[i]).arg(library_name_);
336  }
337  }
338 
339  // ... and close the library.
340  result = closeLibrary() && result;
341  if (result) {
342 
343  // Issue the informational message only if the library was unloaded
344  // with no problems. If there was an issue, an error message would
345  // have been issued.
346  LOG_INFO(hooks_logger, HOOKS_LIBRARY_UNLOADED).arg(library_name_);
347  }
348  }
349  return (result);
350 }
351 
352 // Validate the library. We must be able to open it, and the version function
353 // must both exist and return the right number. Note that this is a static
354 // method.
355 
356 bool
357 LibraryManager::validateLibrary(const std::string& name) {
358  // Instantiate a library manager for the validation. We use the private
359  // constructor as we don't supply a CalloutManager.
360  LibraryManager manager(name);
361 
362  // Try to open it and, if we succeed, check the version.
363  bool validated = manager.openLibrary() && manager.checkVersion();
364 
365  // Regardless of whether the version checked out, close the library. (This
366  // is a no-op if the library failed to open.)
367  static_cast<void>(manager.closeLibrary());
368 
369  return (validated);
370 }
371 
372 // @note Moved from its own hooks.cc file to avoid undefined reference
373 // with static link.
376  isc::log::initLogger(std::string("userlib"));
377  }
378 }
379 
380 } // namespace hooks
381 } // namespace isc
isc::log::LoggerManager::logDuplicatedMessages
static void logDuplicatedMessages()
List duplicated log messages.
Definition: logger_manager.cc:130
isc::hooks::LibraryManager::closeLibrary
bool closeLibrary()
Close library.
Definition: library_manager.cc:88
logger_manager.h
hooks_log.h
LOG_ERROR
#define LOG_ERROR(LOGGER, MESSAGE)
Macro to conveniently test error output and log it.
Definition: macros.h:32
isc::hooks::ServerHooks::getServerHooks
static ServerHooks & getServerHooks()
Return ServerHooks object.
Definition: server_hooks.cc:174
version
int version()
returns Kea hooks version.
Definition: high_availability/version.cc:13
hooks.h
isc::hooks::PointerConverter::versionPtr
version_function_ptr versionPtr() const
Return pointer to version function.
Definition: pointer_converter.h:91
isc::hooks::LibraryManager::validateLibrary
static bool validateLibrary(const std::string &name)
Validate library.
Definition: library_manager.cc:357
isc::hooks::LibraryManager::~LibraryManager
~LibraryManager()
Destructor.
Definition: library_manager.cc:57
isc::hooks::ServerHooks::getHookNames
std::vector< std::string > getHookNames() const
Get hook names.
Definition: server_hooks.cc:160
isc::hooks::hooks_logger
isc::log::Logger hooks_logger("hooks")
Hooks Logger.
Definition: hooks_log.h:37
isc::hooks::HOOKS_DBG_CALLS
const int HOOKS_DBG_CALLS
Definition: hooks_log.h:25
library_handle.h
isc::hooks::LibraryManager::openLibrary
bool openLibrary()
Open library.
Definition: library_manager.cc:72
pointer_converter.h
isc::Exception
This is a base class for exceptions thrown from the DNS library module.
Definition: exceptions/exceptions.h:23
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::Exception::what
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
Definition: exceptions/exceptions.cc:32
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
LOG_DEBUG
#define LOG_DEBUG(LOGGER, LEVEL, MESSAGE)
Macro to conveniently test debug output and log it.
Definition: macros.h:14
callout_manager.h
isc::hooks::LibraryManager::runUnload
bool runUnload()
Run the unload function if present.
Definition: library_manager.cc:211
isc::hooks::LibraryManager
Library manager.
Definition: library_manager.h:72
library_manager.h
isc::hooks::LibraryManager::loadLibrary
bool loadLibrary()
Loads a library.
Definition: library_manager.cc:253
message_initializer.h
isc::hooks::HOOKS_DBG_TRACE
const int HOOKS_DBG_TRACE
Hooks debug Logging levels.
Definition: hooks_log.h:22
isc::hooks::PointerConverter::unloadPtr
unload_function_ptr unloadPtr() const
Return pointer to unload function.
Definition: pointer_converter.h:84
isc::hooks::LibraryManager::checkVersion
bool checkVersion() const
Check library version.
Definition: library_manager.cc:107
isc::hooks::PointerConverter::calloutPtr
CalloutPtr calloutPtr() const
Return pointer to callout function.
Definition: pointer_converter.h:70
isc::log::MessageInitializer::loadDictionary
static void loadDictionary(bool ignore_duplicates=false)
Run-Time Initialization.
Definition: message_initializer.cc:102
isc::log::initLogger
void initLogger(const string &root, isc::log::Severity severity, int dbglevel, const char *file, bool buffer)
Run-time initialization.
Definition: logger_support.cc:43
LOG_WARN
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
isc::hooks::PointerConverter
Local class for conversion of void pointers to function pointers.
Definition: pointer_converter.h:25
isc::hooks::LibraryManager::registerStandardCallouts
void registerStandardCallouts()
Register standard callouts.
Definition: library_manager.cc:140
isc::hooks::hooksStaticLinkInit
void hooksStaticLinkInit()
User-Library Initialization for Statically-Linked Kea.
Definition: library_manager.cc:374
isc::hooks::LibraryManager::unloadLibrary
bool unloadLibrary()
Unloads a library.
Definition: library_manager.cc:316
isc::hooks::PointerConverter::loadPtr
load_function_ptr loadPtr() const
Return pointer to load function.
Definition: pointer_converter.h:77
server_hooks.h
isc::log::isLoggingInitialized
bool isLoggingInitialized()
Is logging initialized?
Definition: logger_support.cc:28
exceptions.h
isc::hooks::LibraryManager::LibraryManager
LibraryManager(const std::string &name, int index, const boost::shared_ptr< CalloutManager > &manager)
Constructor.
Definition: library_manager.cc:33
isc::hooks::ServerHooks
Server hook collection.
Definition: server_hooks.h:62
isc::hooks::NoCalloutManager
No Callout Manager.
Definition: library_manager.h:23
LOG_INFO
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
isc::hooks::LibraryManager::runLoad
bool runLoad()
Run the load function if present.
Definition: library_manager.cc:167
logger_support.h
Logging initialization functions.