Fawkes API Fawkes Development Version
loader.cpp
1
2/***************************************************************************
3 * loader.cpp - Loads plugins from .so shared objects
4 *
5 * Created: Wed Aug 23 15:23:36 2006
6 * Copyright 2006-2008 Tim Niemueller [www.niemueller.de]
7 *
8 ****************************************************************************/
9
10/* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version. A runtime exception applies to
14 * this software (see LICENSE.GPL_WRE file mentioned below for details).
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22 */
23
24#include <plugin/loader.h>
25#include <utils/system/dynamic_module/module.h>
26#include <utils/system/dynamic_module/module_manager.h>
27
28#include <map>
29
30#ifdef HAVE_LIBELF
31# include <cstdio>
32# include <cstring>
33# include <fcntl.h>
34# include <gelf.h>
35# include <libelf.h>
36# include <unistd.h>
37#endif
38
39namespace fawkes {
40
41/// @cond QA
42class PluginLoader::Data
43{
44public:
45 ModuleManager * mm;
46 std::map<Plugin *, Module *> plugin_module_map;
47 std::map<std::string, Plugin *> name_plugin_map;
48 std::map<Plugin *, std::string> plugin_name_map;
49};
50/// @endcond
51
52/** @class PluginLoadException <plugin/loader.h>
53 * This exception is thrown if the requested plugin could not be loaded.
54 */
55
56/** Constructor.
57 * @param plugin name of the plugin that caused the exception
58 * @param message message of exception
59 */
60PluginLoadException::PluginLoadException(const char *plugin, const char *message)
61: Exception(), plugin_name_(plugin)
62{
63 append("Plugin '%s' could not be loaded: %s", plugin, message);
64}
65
66/** Destructor. */
68{
69}
70
71/** Constructor.
72 * @param plugin name of the plugin that caused the exception
73 * @param message message of exception
74 * @param e exception to copy further messages from
75 */
76PluginLoadException::PluginLoadException(const char *plugin, const char *message, Exception &e)
77: Exception(), plugin_name_(plugin)
78{
79 append("Plugin '%s' could not be loaded: %s", plugin, message);
81}
82
83/** Get name of plugin which failed to load.
84 * @return plugin name
85 */
86std::string
88{
89 return plugin_name_;
90}
91
92/** @class PluginUnloadException <plugin/loader.h>
93 * This exception is thrown if the requested plugin could not be unloaded.
94 */
95
96/** Constructor.
97 * @param plugin_name name of the plugin
98 * @param add_msg additional message, reason for problem
99 */
100PluginUnloadException::PluginUnloadException(const char *plugin_name, const char *add_msg)
101: Exception()
102{
103 append("Plugin '%s' could not be unloaded", plugin_name);
104 append(add_msg);
105}
106
107/** @class PluginLoader <plugin/loader.h>
108 * This class manages plugins.
109 * With this class plugins can be loaded and unloaded. Information is
110 * kept about active plugins.
111 *
112 * @author Tim Niemueller
113 */
114
115/** Constructor
116 * @param plugin_base_dir The base directory where to search for the shared
117 * libraries which contain the plugins
118 * @param config Fawkes configuration
119 */
120PluginLoader::PluginLoader(const char *plugin_base_dir, Configuration *config)
121{
122 plugin_base_dir_ = plugin_base_dir;
123 d_ = new Data();
124 config_ = config;
125 d_->mm = new ModuleManager(plugin_base_dir);
126}
127
128/** Destructor */
130{
131 delete d_->mm;
132 delete d_;
133}
134
135/** Get module manager.
136 * This should be used rarely, but may be useful, for example, to pass specific
137 * module opening flags in some situations.
138 * @return internally used module manager
139 */
142{
143 return d_->mm;
144}
145
146Module *
147PluginLoader::open_module(const char *plugin_name)
148{
149 std::string module_name = std::string(plugin_name) + "." + d_->mm->get_module_file_extension();
150
151 try {
152 return d_->mm->open_module(module_name.c_str());
153 } catch (ModuleOpenException &e) {
154 throw PluginLoadException(plugin_name, "failed to open module", e);
155 }
156}
157
158Plugin *
159PluginLoader::create_instance(const char *plugin_name, Module *module)
160{
161 if (!module->has_symbol("plugin_factory")) {
162 throw PluginLoadException(plugin_name,
163 "Symbol 'plugin_factory' not found. Forgot EXPORT_PLUGIN?");
164 }
165 if (!module->has_symbol("plugin_description")) {
166 throw PluginLoadException(plugin_name,
167 "Symbol 'plugin_description' not found. Forgot PLUGIN_DESCRIPTION?");
168 }
169
170 PluginFactoryFunc pff = (PluginFactoryFunc)module->get_symbol("plugin_factory");
171 Plugin * p = NULL;
172
173 p = pff(config_);
174 if (p == NULL) {
175 throw PluginLoadException(plugin_name, "Plugin could not be instantiated");
176 } else {
177 p->set_name(plugin_name);
178 }
179
180 return p;
181}
182
183/** Load a specific plugin
184 * The plugin loader is clever and guarantees that every plugin is only
185 * loaded once (as long as you use only one instance of the PluginLoader,
186 * using multiple instances is discouraged. If you try to open a plugin
187 * a second time it will return the
188 * very same instance that it returned on previous load()s.
189 * @param plugin_name The name of the plugin to be loaded, the plugin name has to
190 * correspond to a plugin name and the name of the shared object that will
191 * be opened for this plugin (for instance on Linux systems opening the
192 * plugin test_plugin will look for plugin_base_dir/test_plugin.so)
193 * @return Returns a pointer to the opened plugin. Do not under any
194 * circumstances delete this object, use unload() instead! Since the delete
195 * operator could be overloaded this would result in memory chaos.
196 * @exception PluginLoadException thrown if plugin could not be loaded
197 * @exception ModuleOpenException passed along from module manager
198 */
199Plugin *
200PluginLoader::load(const char *plugin_name)
201{
202 std::string pn = plugin_name;
203
204 if (d_->name_plugin_map.find(pn) != d_->name_plugin_map.end()) {
205 return d_->name_plugin_map[pn];
206 }
207
208 try {
209 Module *module = open_module(plugin_name);
210 Plugin *p = create_instance(plugin_name, module);
211
212 d_->plugin_module_map[p] = module;
213 d_->name_plugin_map[pn] = p;
214 d_->plugin_name_map[p] = pn;
215
216 return p;
217 } catch (PluginLoadException &e) {
218 throw;
219 }
220}
221
222/** Get content of a string symbol of a plugin.
223 * @param plugin_name name of the plugin
224 * @param symbol_name name of the desired symbol
225 * @param section_name ELF section name to look for
226 * @return string symbol
227 * @throw Exception thrown if the symbol could not be found or file unreadable
228 */
229std::string
230PluginLoader::get_string_symbol(const char *plugin_name,
231 const char *symbol_name,
232 const char *section_name)
233{
234#ifdef HAVE_LIBELF
235 GElf_Ehdr elf_header;
236 Elf * elf;
237
238 std::string module_name =
239 plugin_base_dir_ + "/" + plugin_name + "." + d_->mm->get_module_file_extension();
240
241 if (elf_version(EV_CURRENT) == EV_NONE) {
242 throw Exception("libelf library ELF version too old");
243 }
244
245 int fd = open(module_name.c_str(), O_RDONLY);
246 if (fd == -1) {
247 throw Exception("Failed to open file of plugin '%s'", plugin_name);
248 }
249
250 elf = elf_begin(fd, ELF_C_READ, NULL);
251 if (!elf) {
252 throw Exception("Cannot read elf file: %s", elf_errmsg(elf_errno()));
253 }
254
255 if (gelf_getehdr(elf, &elf_header) == NULL) {
256 elf_end(elf);
257 throw Exception("Failed to read ELF header of plugin %s: %s",
258 plugin_name,
259 elf_errmsg(elf_errno()));
260 }
261
262 Elf_Scn *scn = NULL;
263 while ((scn = elf_nextscn(elf, scn)) != 0) {
264 GElf_Shdr shdr;
265 gelf_getshdr(scn, &shdr);
266
267 if (shdr.sh_type == SHT_SYMTAB) {
268 Elf_Data *edata = elf_getdata(scn, NULL);
269 size_t symbol_count = shdr.sh_size / shdr.sh_entsize;
270
271 for (size_t i = 0; i < symbol_count; ++i) {
272 GElf_Sym sym;
273 gelf_getsym(edata, i, &sym);
274
275 GElf_Shdr sym_shdr;
276 Elf_Scn * sym_scn = elf_getscn(elf, sym.st_shndx);
277 gelf_getshdr(sym_scn, &sym_shdr);
278
279 char *secname = elf_strptr(elf, elf_header.e_shstrndx, sym_shdr.sh_name);
280 char *symname = elf_strptr(elf, shdr.sh_link, sym.st_name);
281
282 if ((strcmp(secname, section_name) == 0) && (strcmp(symname, symbol_name) == 0)) {
283 // found it, extract string
284 Elf_Data * sym_data = elf_rawdata(sym_scn, NULL);
285 const char *start = (const char *)sym_data->d_buf + (sym.st_value - sym_shdr.sh_offset);
286 const char *const limit = start + sym.st_size;
287 const char * end = (const char *)memchr(start, '\0', limit - start);
288
289 if (end != NULL) {
290 close(fd);
291 std::string rv(start);
292 elf_end(elf);
293 return rv;
294 } else {
295 close(fd);
296 elf_end(elf);
297 throw Exception("Failed to retrieve string for symbol '%s' in section '%s'"
298 " of plugin '%s'",
299 symbol_name,
300 section_name,
301 plugin_name);
302 }
303 }
304 }
305 }
306 }
307 close(fd);
308 elf_end(elf);
309 throw Exception("Description for plugin %s not found. "
310 "Forgot PLUGIN_DESCRIPTION?",
311 plugin_name);
312#else
313 throw Exception("libelf not supported at compile time");
314#endif
315}
316
317/** Get plugin description.
318 * @param plugin_name name of the plugin
319 * @return plugin description tring
320 * @throw PluginLoadException thrown if opening the plugin fails
321 */
322std::string
323PluginLoader::get_description(const char *plugin_name)
324{
325#ifdef HAVE_LIBELF
326 return get_string_symbol(plugin_name, "_plugin_description");
327#else
328 Module *module = open_module(plugin_name);
329
330 if (!module->has_symbol("plugin_description")) {
331 throw PluginLoadException(plugin_name,
332 "Symbol 'plugin_description' not found. Forgot PLUGIN_DESCRIPTION?");
333 }
334
335 PluginDescriptionFunc pdf = (PluginDescriptionFunc)module->get_symbol("plugin_description");
336 std::string rv = pdf();
337 d_->mm->close_module(module);
338
339 return rv;
340#endif
341}
342
343/** Check if a plugin is loaded.
344 * @param plugin_name name of the plugin to chekc
345 * @return true if the plugin is loaded, false otherwise
346 */
347bool
348PluginLoader::is_loaded(const char *plugin_name)
349{
350 return (d_->name_plugin_map.find(plugin_name) != d_->name_plugin_map.end());
351}
352
353/** Unload the given plugin
354 * This will unload the given plugin. The plugin is destroyed with the
355 * proper destroy method from the shared object. The shared object is unloaded
356 * after the destruction of the plugin.
357 * Note that even though you may call load() multiple times per plugin you may
358 * only unload() it once! Every further access will lead to a segmentation
359 * fault.
360 * Make sure that you have closed any resources claimed by the plugin like
361 * threads, memory access etc.
362 * @param plugin The plugin that has to be unloaded
363 */
364void
366{
367 if (d_->plugin_module_map.find(plugin) != d_->plugin_module_map.end()) {
368 PluginDestroyFunc pdf =
369 (PluginDestroyFunc)d_->plugin_module_map[plugin]->get_symbol("plugin_destroy");
370 if (pdf != NULL) {
371 pdf(plugin);
372 }
373 d_->mm->close_module(d_->plugin_module_map[plugin]);
374 d_->plugin_module_map.erase(plugin);
375
376 d_->name_plugin_map.erase(d_->plugin_name_map[plugin]);
377 d_->plugin_name_map.erase(plugin);
378 }
379}
380
381} // end namespace fawkes
Plugin representation for JSON transfer.
Definition: Plugin.h:28
void set_name(const std::string &name)
Set name value.
Definition: Plugin.h:135
Interface for configuration handling.
Definition: config.h:68
Base class for exceptions in Fawkes.
Definition: exception.h:36
void copy_messages(const Exception &exc) noexcept
Copy messages from given exception.
Definition: exception.cpp:519
void append(const char *format,...) noexcept
Append messages to the message list.
Definition: exception.cpp:333
Dynamic module manager.
Dynamic module loader for Linux, FreeBSD, and MacOS X.
Definition: module.h:41
virtual void * get_symbol(const char *symbol_name)
Get a symbol from the module.
Definition: module.cpp:244
virtual bool has_symbol(const char *symbol_name)
Check if the module has the given symbol.
Definition: module.cpp:222
This exception is thrown if the requested plugin could not be loaded.
Definition: loader.h:39
~PluginLoadException() noexcept
Destructor.
Definition: loader.cpp:67
PluginLoadException(const char *plugin, const char *message)
Constructor.
Definition: loader.cpp:60
std::string plugin_name() const
Get name of plugin which failed to load.
Definition: loader.cpp:87
bool is_loaded(const char *plugin_name)
Check if a plugin is loaded.
Definition: loader.cpp:348
ModuleManager * get_module_manager() const
Get module manager.
Definition: loader.cpp:141
std::string get_description(const char *plugin_name)
Get plugin description.
Definition: loader.cpp:323
Plugin * load(const char *plugin_name)
Load a specific plugin The plugin loader is clever and guarantees that every plugin is only loaded on...
Definition: loader.cpp:200
void unload(Plugin *plugin)
Unload the given plugin This will unload the given plugin.
Definition: loader.cpp:365
~PluginLoader()
Destructor.
Definition: loader.cpp:129
PluginLoader(const char *plugin_base_dir, Configuration *config)
Constructor.
Definition: loader.cpp:120
PluginUnloadException(const char *plugin_type, const char *add_msg=NULL)
Constructor.
Definition: loader.cpp:100
Plugin interface class.
Definition: plugin.h:34
Fawkes library namespace.
const char *(* PluginDescriptionFunc)()
Plugin description function for the shared library.
Definition: plugin.h:81