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