HAL  v4.5.0-124-g47ab54673
The Hardware Analyzer - a comprehensive reverse engineering and manipulation framework for gate-level netlists.
plugin_manager.cpp
Go to the documentation of this file.
2 
11 #include "hal_core/utilities/log.h"
13 
14 #include <vector>
15 
16 #ifdef _WIN32
17 #include <tchar.h>
18 #include <windows.h>
19 #else
20 #include <dirent.h>
21 #include <sys/stat.h>
22 #endif
23 
24 namespace hal
25 {
26  namespace plugin_manager
27  {
28  namespace
29  {
36  struct LoadedPlugin
37  {
38  std::unique_ptr<BasePluginInterface> instance;
39  std::unique_ptr<RuntimeLibrary> library;
40 
53  ~LoadedPlugin()
54  {
55  (void)library.release();
56  }
57 
58  LoadedPlugin() = default;
59  LoadedPlugin(LoadedPlugin&&) = default;
60  LoadedPlugin& operator=(LoadedPlugin&&) = default;
61  LoadedPlugin(const LoadedPlugin&) = delete;
62  LoadedPlugin& operator=(const LoadedPlugin&) = delete;
63  };
64 
65  // stores library and factory identified by plugin name)
66  std::unordered_map<std::string, LoadedPlugin> m_loaded_plugins;
67 
68  // stores special features offered by plugin
69  std::unordered_map<std::string, std::vector<PluginFeature>> m_plugin_features;
70 
71  // stores the CLI parser option for plugins [0=base_plugin] [1=UI_plugin]
72  std::unordered_map<std::string, std::string> m_cli_option_to_plugin_name[2];
73 
74  // stores the GUI callback
75  CallbackHook<void(bool, std::string const&, std::string const&)> m_hook;
76 
77  // stores the generic option parser
78  ProgramOptions m_existing_options("existing options");
79 
80  // stores the plugin option description
81  ProgramOptions m_plugin_options("plugin options");
82 
83  // stores the plugin folder
84  std::vector<std::filesystem::path> m_plugin_folders = utils::get_plugin_directories();
85 
86  // stores name of plugin while loading
87  std::string m_current_loading;
88 
89  bool solve_dependencies(std::string plugin_name, std::set<std::string> dep_file_name)
90  {
91  if (plugin_name.empty())
92  {
93  log_error("core", "parameter 'plugin_name' is empty");
94  return false;
95  }
96  if (dep_file_name.empty())
97  {
98  log_debug("core", "no dependency for plugin '{}'", plugin_name);
99  return true;
100  }
101  for (const auto& file_name : dep_file_name)
102  {
103  auto dep_plugin_name = std::filesystem::path(file_name).stem().string();
104 
105  // dependency already loaded
106  if (m_loaded_plugins.find(dep_plugin_name) != m_loaded_plugins.end())
107  {
108  continue;
109  }
110 
111  // search in plugin directories
112  std::filesystem::path file_path = get_plugin_path(dep_plugin_name);
113  if (file_path.empty() || !load(dep_plugin_name, file_path))
114  {
115  log_error("core", "cannot solve dependency '{}' for plugin '{}'", dep_plugin_name, plugin_name);
116  return false;
117  }
118 
119  log_debug("core", "solved dependency '{}' for plugin '{}'", dep_plugin_name, plugin_name);
120  }
121  log_debug("core", "solved {} dependencies for plugin '{}'", dep_file_name.size(), plugin_name);
122  return true;
123  }
124 
125  } // namespace
126 
127  std::filesystem::path get_plugin_path(std::string plugin_name)
128  {
129  std::filesystem::path retval;
130  if (plugin_name.empty()) return retval;
131  std::string file_name = plugin_name + "." + LIBRARY_FILE_EXTENSION;
132  retval = utils::get_file(file_name, m_plugin_folders);
133  if (!retval.empty() || !strlen(ALTERNATE_LIBRARY_FILE_EXTENSION)) return retval;
134  file_name = plugin_name + "." + ALTERNATE_LIBRARY_FILE_EXTENSION;
135  return utils::get_file(file_name, m_plugin_folders);
136  }
137 
138  bool has_valid_file_extension(std::filesystem::path file_name)
139  {
140 #if defined(__APPLE__) && defined(__MACH__)
141  if (utils::ends_with(file_name.string(), std::string(".so")))
142  return true;
143  if (file_name.string().find(".so") != std::string::npos)
144  return true;
145  if (utils::ends_with(file_name.string(), std::string(".icloud")))
146  return false;
147 #endif
148  // file has regular shared library extension
149  if (utils::ends_with(file_name.string(), std::string(".") + std::string(LIBRARY_FILE_EXTENSION))) return true;
150 
151  // there is no alternate extension
152  if (!strlen(ALTERNATE_LIBRARY_FILE_EXTENSION)) return false;
153 
154  // test whether file has alternate extension
155  return (utils::ends_with(file_name.string(), std::string(".") + std::string(ALTERNATE_LIBRARY_FILE_EXTENSION)));
156  }
157 
158  std::set<std::string> get_plugin_names()
159  {
160  std::set<std::string> names;
161  for (const auto& it : m_loaded_plugins)
162  {
163  names.insert(it.first);
164  }
165  return names;
166  }
167 
168  std::vector<PluginFeature> get_plugin_features(std::string name)
169  {
170  auto it = m_plugin_features.find(name);
171  if (it == m_plugin_features.end()) return std::vector<PluginFeature>();
172  return it->second;
173  }
174 
175  std::unordered_map<std::string, std::string> get_cli_plugin_flags()
176  {
177  return m_cli_option_to_plugin_name[0];
178  }
179 
180  std::unordered_map<std::string, std::string> get_ui_plugin_flags()
181  {
182  return m_cli_option_to_plugin_name[1];
183  }
184 
186  {
187  return m_plugin_options;
188  }
189 
190  bool load_all_plugins(const std::vector<std::filesystem::path>& directory_names)
191  {
192  auto directories = (!directory_names.empty()) ? directory_names : m_plugin_folders;
193  for (const auto& directory : directories)
194  {
195  if (!std::filesystem::exists(directory))
196  {
197  continue;
198  }
199  u32 num_of_loaded_plugins = 0;
200  for (const auto& file : utils::DirectoryRange(directory))
201  {
202  if (!has_valid_file_extension(file) || std::filesystem::is_directory(file))
203  continue;
204 
205  auto plugin_name = file.path().stem().string();
206  if (load(plugin_name, file.path()))
207  {
208  num_of_loaded_plugins++;
209  }
210  }
211  log_debug("core", "loaded {} plugins from '{}'", num_of_loaded_plugins, directory.string());
212  }
213  return true;
214  }
215 
216  bool load(const std::string& plugin_name, const std::filesystem::path& file_path_or_empty)
217  {
218  if (plugin_name.empty())
219  {
220  log_error("core", "parameter 'plugin_name' is empty");
221  return false;
222  }
223 
224  std::filesystem::path file_path = file_path_or_empty;
225  if (file_path.empty())
226  {
227  // file name not provided, search plugin folders
228  file_path = get_plugin_path(plugin_name);
229 
230  if (file_path.empty())
231  {
232  log_error("core", "path for plugin '{}' not found", plugin_name);
233  return false;
234  }
235  }
236  else
237  log_info("core", "loading plugin '{}'...", file_path.string());
238 
239  if (m_loaded_plugins.find(plugin_name) != m_loaded_plugins.end())
240  {
241  log_debug("core", "plugin '{}' is already loaded", plugin_name);
242  return true;
243  }
244 
245  /* load library */
246  auto lib = std::make_unique<RuntimeLibrary>();
247  if (!lib->load_library(file_path.string()))
248  {
249  return false;
250  }
251 
252  /* get factory of library */
253  auto factory = (instantiate_plugin_function)lib->get_function("create_plugin_instance");
254  if (factory == nullptr)
255  {
256  log_error("core", "file does not seem to be a HAL plugin since it does not contain a factory function 'create_plugin_instance'.");
257  return false;
258  }
259  auto instance = factory();
260  if (instance == nullptr)
261  {
262  log_error("core", "factory constructor for plugin '{}' returned a nullptr", plugin_name);
263  return false;
264  }
265 
266  /* solve dependencies */
267  std::set<std::string> dep_file_name = instance->get_dependencies();
268  if (!solve_dependencies(plugin_name, dep_file_name))
269  {
270  return false;
271  }
272 
273  /* add cli options */
274  CliExtensionInterface* ceif = instance->get_first_extension<CliExtensionInterface>();
275  if (ceif)
276  {
277  BasePluginInterface* plugin = instance.get();
278  if (plugin == nullptr)
279  {
280  return false;
281  }
282 
283  auto cli_options = ceif->get_cli_options();
284  UIPluginInterface* ui_plugin = dynamic_cast<UIPluginInterface*>(plugin);
285  for (const auto& cli_option : cli_options.get_options())
286  {
287  for (const auto& flag : std::get<0>(cli_option))
288  {
289  if (m_existing_options.is_registered(flag))
290  {
291  log_error("core", "command line option '{}' is already used by generic program options -- use another one.", flag);
292  return false;
293  }
294 
295  for (int iplugType = 0; iplugType<2; iplugType++)
296  if (m_cli_option_to_plugin_name[iplugType].find(flag) != m_cli_option_to_plugin_name[iplugType].end())
297  {
298  log_error("core", "command line option '{}' is already used by plugin '{}' -- use another option in plugin '{}'.", flag, m_cli_option_to_plugin_name[iplugType][flag], plugin_name);
299  return false;
300  }
301  m_cli_option_to_plugin_name[ui_plugin?1:0][flag] = plugin_name;
302  log_debug("core", "registered command line option '{}' for plugin '{}'.", flag, plugin->get_name());
303  }
304  }
305  m_plugin_options.add(cli_options);
306  }
307 
308  instance->initialize_logging();
309 
310  m_current_loading = plugin_name;
311  instance->on_load();
312 
313  for (AbstractExtensionInterface* aeif : instance->get_extensions())
314  {
315  FacExtensionInterface* feif = dynamic_cast<FacExtensionInterface*>(aeif);
316  if (!feif) continue;
318  m_plugin_features[plugin_name].push_back({feif->get_feature(),
320  feif->get_description()});
321  switch (feif->get_feature())
322  {
324  {
327  break;
328  }
330  {
333  break;
334  }
336  {
339  break;
340  }
342  {
345  break;
346  }
347  default:
348  break;
349  }
350  }
351  m_current_loading.clear();
352 
353  m_loaded_plugins[plugin_name] = LoadedPlugin{std::move(instance), std::move(lib)};
354 
355  /* notify callback that a plugin was loaded*/
356  m_hook(true, plugin_name, file_path.string());
357 
358  log_debug("core", "loaded plugin '{}'.", plugin_name);
359  return true;
360  }
361 
363  {
364  if (m_loaded_plugins.size() == 0)
365  {
366  log_debug("core", "no plugins to unload");
367  return true;
368  }
369  auto loaded_plugin_names = get_plugin_names();
370  for (const auto& name : loaded_plugin_names)
371  {
372  if (!unload(name))
373  {
374  log_error("core", "could not unload plugin '{}'", name);
375  }
376  }
377  log_info("core", "unloaded all {} plugins", loaded_plugin_names.size());
378  return true;
379  }
380 
381  bool unload(const std::string& plugin_name)
382  {
383  auto loaded_it = m_loaded_plugins.find(plugin_name);
384  if (loaded_it == m_loaded_plugins.end())
385  {
386  log_debug("core", "cannot find plugin '{}' to unload it", plugin_name);
387  return true;
388  }
389 
390  log_info("core", "unloading plugin '{}'...", plugin_name);
391 
392 
393  auto rt_library = std::move(loaded_it->second.library);
394  auto plugin_inst = std::move(loaded_it->second.instance);
395 
396  {
397  auto iplugType = dynamic_cast<UIPluginInterface*>(plugin_inst.get()) ? 1 : 0;
398  auto it = m_cli_option_to_plugin_name[iplugType].begin();
399  while (it != m_cli_option_to_plugin_name[iplugType].end())
400  {
401  auto flag = it->first;
402  auto name = it->second;
403  if (name == plugin_name)
404  {
405  m_plugin_options.remove(flag);
406  it = m_cli_option_to_plugin_name[iplugType].erase(it);
407  }
408  else
409  {
410  ++it;
411  }
412  }
413  }
414 
415  m_loaded_plugins.erase(loaded_it);
416 
417  for (AbstractExtensionInterface* aeif : plugin_inst->get_extensions())
418  {
419  FacExtensionInterface* feif = dynamic_cast<FacExtensionInterface*>(aeif);
420  if (!feif) continue;
421  switch (feif->get_feature())
422  {
425  break;
428  break;
431  break;
434  break;
435  default:
436  break;
437  }
438  }
439 
440  auto file_name = rt_library->get_file_name();
441  plugin_inst->on_unload();
442 
443  // actual unloading
444  // order of unloading is important, so we do it manually here:
445  // first destroy the plugin instance, then destroy the runtime library
446  plugin_inst.reset();
447  rt_library.reset();
448 
449  /* notify callback that a plugin was unloaded */
450  m_hook(false, plugin_name, file_name);
451 
452  log_debug("core", "unloaded plugin '{}'", plugin_name);
453  return true;
454  }
455 
456  BasePluginInterface* get_plugin_instance(const std::string& plugin_name, bool initialize, bool silent)
457  {
458  auto it = m_loaded_plugins.find(plugin_name);
459  if (it == m_loaded_plugins.end())
460  {
461  if (!silent)
462  log_error("core", "plugin '{}' is not loaded", plugin_name);
463  return nullptr;
464  }
465 
466  auto instance = it->second.instance.get();
467  if (instance != nullptr && initialize)
468  {
469  instance->initialize();
470  }
471  return instance;
472  }
473 
475  {
476  for (const auto& [_, plugin] : m_loaded_plugins)
477  {
478  if (auto* ui_plugin = dynamic_cast<UIPluginInterface*>(plugin.instance.get()); ui_plugin != nullptr)
479  {
480  return ui_plugin;
481  }
482  }
483  return nullptr;
484  }
485 
486  u64 add_model_changed_callback(std::function<void(bool, std::string const&, std::string const&)> callback)
487  {
488  if (callback == nullptr)
489  {
490  log_error("core", "parameter 'callback' is nullptr");
491  return 0;
492  }
493  return m_hook.add_callback(callback);
494  }
495 
497  {
498  m_hook.remove_callback(id);
499  }
500 
501  void add_existing_options_description(const ProgramOptions& existing_options)
502  {
503  m_existing_options.add(existing_options);
504  }
505  } // namespace plugin_manager
506 } // namespace hal
#define LIBRARY_FILE_EXTENSION
Definition: arch_linux.h:34
#define ALTERNATE_LIBRARY_FILE_EXTENSION
Definition: arch_linux.h:36
virtual std::string get_name() const =0
virtual ProgramOptions get_cli_options() const =0
AbstractFactoryProvider * factory_provider
std::vector< std::string > get_supported_file_extensions() const
std::function< std::unique_ptr< T >)> m_factory
uint64_t u64
Definition: defines.h:42
uint32_t u32
Definition: defines.h:41
#define log_error(channel,...)
Definition: log.h:78
#define log_debug(channel,...)
Definition: log.h:74
#define log_info(channel,...)
Definition: log.h:70
void register_parser(const std::string &name, const ParserFactory &parser_factory, const std::vector< std::string > &supported_file_extensions)
void unregister_parser(const std::string &name)
void unregister_writer(const std::string &name)
void register_writer(const std::string &name, const WriterFactory &writer_factory, const std::vector< std::string > &supported_file_extensions)
void register_parser(const std::string &name, const ParserFactory &parser_factory, const std::vector< std::string > &supported_file_extensions)
void unregister_parser(const std::string &name)
void unregister_writer(const std::string &name)
void register_writer(const std::string &name, const WriterFactory &writer_factory, const std::vector< std::string > &supported_file_extensions)
void add_existing_options_description(const ProgramOptions &existing_options)
u64 add_model_changed_callback(std::function< void(bool, std::string const &, std::string const &)> callback)
bool has_valid_file_extension(std::filesystem::path file_name)
UIPluginInterface * get_ui_plugin()
bool unload(const std::string &plugin_name)
bool load_all_plugins(const std::vector< std::filesystem::path > &directory_names={})
bool load(const std::string &plugin_name, const std::filesystem::path &file_path=std::filesystem::path())
std::set< std::string > get_plugin_names()
std::unordered_map< std::string, std::string > get_cli_plugin_flags()
std::unordered_map< std::string, std::string > get_ui_plugin_flags()
std::filesystem::path get_plugin_path(std::string plugin_name)
ProgramOptions get_cli_plugin_options()
void remove_model_changed_callback(u64 id)
BasePluginInterface * get_plugin_instance(const std::string &plugin_name, bool initialize=true, bool silent=false)
std::vector< PluginFeature > get_plugin_features(std::string name)
std::filesystem::path get_file(std::string file_name, std::vector< std::filesystem::path > path_hints)
Definition: utils.cpp:220
bool ends_with(const T &s, const T &end)
Definition: utils.h:149
std::vector< std::filesystem::path > get_plugin_directories()
Definition: utils.cpp:195
Definition: defines.h:45
void initialize()
Definition: event_log.cpp:302
std::unique_ptr< BasePluginInterface >(*)() instantiate_plugin_function
std::string name
std::unique_ptr< RuntimeLibrary > library
std::unique_ptr< BasePluginInterface > instance