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>
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
26using namespace std;
27
28namespace isc {
29namespace hooks {
30
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 (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
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 }
99 }
100
101 return (status == 0);
102}
103
104// Check the version of the library
105
106bool
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
139void
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
166bool
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
210bool
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
252bool
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
315bool
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
356bool
357LibraryManager::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
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 runUnload()
Run the unload function if present.
Local class for conversion of void pointers to function pointers.
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.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
int version()
returns Kea hooks version.
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.
isc::log::Logger hooks_logger("hooks")
Hooks Logger.
Definition: hooks_log.h:37
const int HOOKS_DBG_CALLS
Definition: hooks_log.h:25
const int HOOKS_DBG_TRACE
Hooks debug Logging levels.
Definition: hooks_log.h:22
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-dhcp-ddns.