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 
39 namespace fawkes {
40 
41 /// @cond QA
42 class PluginLoader::Data
43 {
44 public:
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  */
60 PluginLoadException::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  */
76 PluginLoadException::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);
80  copy_messages(e);
81 }
82 
83 /** Get name of plugin which failed to load.
84  * @return plugin name
85  */
86 std::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  */
100 PluginUnloadException::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  */
120 PluginLoader::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 
146 Module *
147 PluginLoader::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 
158 Plugin *
159 PluginLoader::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  */
199 Plugin *
200 PluginLoader::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  */
229 std::string
230 PluginLoader::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  */
322 std::string
323 PluginLoader::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  */
347 bool
348 PluginLoader::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  */
364 void
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
fawkes::PluginLoadException::~PluginLoadException
~PluginLoadException()
Destructor.
Definition: loader.cpp:73
fawkes::PluginLoader::load
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:206
fawkes::ModuleOpenException
Definition: module.h:41
Plugin::set_name
void set_name(const std::string &name)
Set name value.
Definition: Plugin.h:135
fawkes::PluginLoadException
Definition: loader.h:44
fawkes::Module::get_symbol
virtual void * get_symbol(const char *symbol_name)
Get a symbol from the module.
Definition: module.cpp:251
fawkes::Configuration
Definition: config.h:70
fawkes::Exception::append
void append(const char *format,...)
Append messages to the message list.
Definition: exception.cpp:333
fawkes::PluginLoader::~PluginLoader
~PluginLoader()
Destructor.
Definition: loader.cpp:135
fawkes::PluginLoadException::plugin_name
std::string plugin_name() const
Get name of plugin which failed to load.
Definition: loader.cpp:93
fawkes::PluginLoader::unload
void unload(Plugin *plugin)
Unload the given plugin This will unload the given plugin.
Definition: loader.cpp:371
fawkes::PluginLoader::get_description
std::string get_description(const char *plugin_name)
Get plugin description.
Definition: loader.cpp:329
fawkes::Module::has_symbol
virtual bool has_symbol(const char *symbol_name)
Check if the module has the given symbol.
Definition: module.cpp:229
fawkes::PluginLoader::is_loaded
bool is_loaded(const char *plugin_name)
Check if a plugin is loaded.
Definition: loader.cpp:354
fawkes::PluginLoadException::PluginLoadException
PluginLoadException(const char *plugin, const char *message)
Constructor.
Definition: loader.cpp:66
fawkes
fawkes::PluginLoader::PluginLoader
PluginLoader(const char *plugin_base_dir, Configuration *config)
Constructor.
Definition: loader.cpp:126
Plugin
Plugin representation for JSON transfer.
Definition: Plugin.h:26
fawkes::Exception::copy_messages
void copy_messages(const Exception &exc)
Copy messages from given exception.
Definition: exception.cpp:519
fawkes::PluginDescriptionFunc
const typedef char *(* PluginDescriptionFunc)()
Plugin description function for the shared library.
Definition: plugin.h:87
fawkes::PluginUnloadException::PluginUnloadException
PluginUnloadException(const char *plugin_type, const char *add_msg=NULL)
Constructor.
Definition: loader.cpp:106
fawkes::PluginLoader::get_module_manager
ModuleManager * get_module_manager() const
Get module manager.
Definition: loader.cpp:147
fawkes::ModuleManager
Definition: module_manager.h:43
fawkes::Plugin
Definition: plugin.h:39
fawkes::Module
Definition: module.h:47
fawkes::Exception
Definition: exception.h:41