HAL  v4.5.0-83-g30c8f0afc
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  {
30  // stores library and factory identified by plugin name)
31  std::unordered_map<std::string, std::tuple<std::unique_ptr<BasePluginInterface>, std::unique_ptr<RuntimeLibrary>>> m_loaded_plugins;
32 
33  // stores special features offered by plugin
34  std::unordered_map<std::string, std::vector<PluginFeature>> m_plugin_features;
35 
36  // stores the CLI parser option for plugins [0=base_plugin] [1=UI_plugin]
37  std::unordered_map<std::string, std::string> m_cli_option_to_plugin_name[2];
38 
39  // stores the GUI callback
40  CallbackHook<void(bool, std::string const&, std::string const&)> m_hook;
41 
42  // stores the generic option parser
43  ProgramOptions m_existing_options("existing options");
44 
45  // stores the plugin option description
46  ProgramOptions m_plugin_options("plugin options");
47 
48  // stores the plugin folder
49  std::vector<std::filesystem::path> m_plugin_folders = utils::get_plugin_directories();
50 
51  // stores name of plugin while loading
52  std::string m_current_loading;
53 
54  bool solve_dependencies(std::string plugin_name, std::set<std::string> dep_file_name)
55  {
56  if (plugin_name.empty())
57  {
58  log_error("core", "parameter 'plugin_name' is empty");
59  return false;
60  }
61  if (dep_file_name.empty())
62  {
63  log_debug("core", "no dependency for plugin '{}'", plugin_name);
64  return true;
65  }
66  for (const auto& file_name : dep_file_name)
67  {
68  auto dep_plugin_name = std::filesystem::path(file_name).stem().string();
69 
70  // dependency already loaded
71  if (m_loaded_plugins.find(dep_plugin_name) != m_loaded_plugins.end())
72  {
73  continue;
74  }
75 
76  // search in plugin directories
77  std::filesystem::path file_path = get_plugin_path(dep_plugin_name);
78  if (file_path.empty() || !load(dep_plugin_name, file_path))
79  {
80  log_error("core", "cannot solve dependency '{}' for plugin '{}'", dep_plugin_name, plugin_name);
81  return false;
82  }
83 
84  log_debug("core", "solved dependency '{}' for plugin '{}'", dep_plugin_name, plugin_name);
85  }
86  log_debug("core", "solved {} dependencies for plugin '{}'", dep_file_name.size(), plugin_name);
87  return true;
88  }
89 
90  } // namespace
91 
92  std::filesystem::path get_plugin_path(std::string plugin_name)
93  {
94  std::filesystem::path retval;
95  if (plugin_name.empty()) return retval;
96  std::string file_name = plugin_name + "." + LIBRARY_FILE_EXTENSION;
97  retval = utils::get_file(file_name, m_plugin_folders);
98  if (!retval.empty() || !strlen(ALTERNATE_LIBRARY_FILE_EXTENSION)) return retval;
99  file_name = plugin_name + "." + ALTERNATE_LIBRARY_FILE_EXTENSION;
100  return utils::get_file(file_name, m_plugin_folders);
101  }
102 
103  bool has_valid_file_extension(std::filesystem::path file_name)
104  {
105 #if defined(__APPLE__) && defined(__MACH__)
106  if (utils::ends_with(file_name.string(), std::string(".so")))
107  return true;
108  if (file_name.string().find(".so") != std::string::npos)
109  return true;
110  if (utils::ends_with(file_name.string(), std::string(".icloud")))
111  return false;
112 #endif
113  // file has regular shared library extension
114  if (utils::ends_with(file_name.string(), std::string(".") + std::string(LIBRARY_FILE_EXTENSION))) return true;
115 
116  // there is no alternate extension
117  if (!strlen(ALTERNATE_LIBRARY_FILE_EXTENSION)) return false;
118 
119  // test whether file has alternate extension
120  return (utils::ends_with(file_name.string(), std::string(".") + std::string(ALTERNATE_LIBRARY_FILE_EXTENSION)));
121  }
122 
123  std::set<std::string> get_plugin_names()
124  {
125  std::set<std::string> names;
126  for (const auto& it : m_loaded_plugins)
127  {
128  names.insert(it.first);
129  }
130  return names;
131  }
132 
133  std::vector<PluginFeature> get_plugin_features(std::string name)
134  {
135  auto it = m_plugin_features.find(name);
136  if (it == m_plugin_features.end()) return std::vector<PluginFeature>();
137  return it->second;
138  }
139 
140  std::unordered_map<std::string, std::string> get_cli_plugin_flags()
141  {
142  return m_cli_option_to_plugin_name[0];
143  }
144 
145  std::unordered_map<std::string, std::string> get_ui_plugin_flags()
146  {
147  return m_cli_option_to_plugin_name[1];
148  }
149 
151  {
152  return m_plugin_options;
153  }
154 
155  bool load_all_plugins(const std::vector<std::filesystem::path>& directory_names)
156  {
157  auto directories = (!directory_names.empty()) ? directory_names : m_plugin_folders;
158  for (const auto& directory : directories)
159  {
160  if (!std::filesystem::exists(directory))
161  {
162  continue;
163  }
164  u32 num_of_loaded_plugins = 0;
165  for (const auto& file : utils::DirectoryRange(directory))
166  {
167  if (!has_valid_file_extension(file) || std::filesystem::is_directory(file))
168  continue;
169 
170  auto plugin_name = file.path().stem().string();
171  if (load(plugin_name, file.path()))
172  {
173  num_of_loaded_plugins++;
174  }
175  }
176  log_debug("core", "loaded {} plugins from '{}'", num_of_loaded_plugins, directory.string());
177  }
178  return true;
179  }
180 
181  bool load(const std::string& plugin_name, const std::filesystem::path& file_path_or_empty)
182  {
183  if (plugin_name.empty())
184  {
185  log_error("core", "parameter 'plugin_name' is empty");
186  return false;
187  }
188 
189  std::filesystem::path file_path = file_path_or_empty;
190  if (file_path.empty())
191  {
192  // file name not provided, search plugin folders
193  file_path = get_plugin_path(plugin_name);
194 
195  if (file_path.empty())
196  {
197  log_error("core", "path for plugin '{}' not found", plugin_name);
198  return false;
199  }
200  }
201  else
202  log_info("core", "loading plugin '{}'...", file_path.string());
203 
204  if (m_loaded_plugins.find(plugin_name) != m_loaded_plugins.end())
205  {
206  log_debug("core", "plugin '{}' is already loaded", plugin_name);
207  return true;
208  }
209 
210  /* load library */
211  auto lib = std::make_unique<RuntimeLibrary>();
212  if (!lib->load_library(file_path.string()))
213  {
214  return false;
215  }
216 
217  /* get factory of library */
218  auto factory = (instantiate_plugin_function)lib->get_function("create_plugin_instance");
219  if (factory == nullptr)
220  {
221  log_error("core", "file does not seem to be a HAL plugin since it does not contain a factory function 'create_plugin_instance'.");
222  return false;
223  }
224  auto instance = factory();
225  if (instance == nullptr)
226  {
227  log_error("core", "factory constructor for plugin '{}' returned a nullptr", plugin_name);
228  return false;
229  }
230 
231  /* solve dependencies */
232  std::set<std::string> dep_file_name = instance->get_dependencies();
233  if (!solve_dependencies(plugin_name, dep_file_name))
234  {
235  return false;
236  }
237 
238  /* add cli options */
239  CliExtensionInterface* ceif = instance->get_first_extension<CliExtensionInterface>();
240  if (ceif)
241  {
242  BasePluginInterface* plugin = instance.get();
243  if (plugin == nullptr)
244  {
245  return false;
246  }
247 
248  auto cli_options = ceif->get_cli_options();
249  UIPluginInterface* ui_plugin = dynamic_cast<UIPluginInterface*>(plugin);
250  for (const auto& cli_option : cli_options.get_options())
251  {
252  for (const auto& flag : std::get<0>(cli_option))
253  {
254  if (m_existing_options.is_registered(flag))
255  {
256  log_error("core", "command line option '{}' is already used by generic program options -- use another one.", flag);
257  return false;
258  }
259 
260  for (int iplugType = 0; iplugType<2; iplugType++)
261  if (m_cli_option_to_plugin_name[iplugType].find(flag) != m_cli_option_to_plugin_name[iplugType].end())
262  {
263  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);
264  return false;
265  }
266  m_cli_option_to_plugin_name[ui_plugin?1:0][flag] = plugin_name;
267  log_debug("core", "registered command line option '{}' for plugin '{}'.", flag, plugin->get_name());
268  }
269  }
270  m_plugin_options.add(cli_options);
271  }
272 
273  instance->initialize_logging();
274 
275  m_current_loading = plugin_name;
276  instance->on_load();
277 
278  for (AbstractExtensionInterface* aeif : instance->get_extensions())
279  {
280  FacExtensionInterface* feif = dynamic_cast<FacExtensionInterface*>(aeif);
281  if (!feif) continue;
283  m_plugin_features[plugin_name].push_back({feif->get_feature(),
285  feif->get_description()});
286  switch (feif->get_feature())
287  {
289  {
292  break;
293  }
295  {
298  break;
299  }
301  {
304  break;
305  }
307  {
310  break;
311  }
312  default:
313  break;
314  }
315  }
316  m_current_loading.clear();
317 
318  m_loaded_plugins[plugin_name] = std::make_tuple(std::move(instance), std::move(lib));
319 
320  /* notify callback that a plugin was loaded*/
321  m_hook(true, plugin_name, file_path.string());
322 
323  log_debug("core", "loaded plugin '{}'.", plugin_name);
324  return true;
325  }
326 
328  {
329  if (m_loaded_plugins.size() == 0)
330  {
331  log_debug("core", "no plugins to unload");
332  return true;
333  }
334  auto loaded_plugin_names = get_plugin_names();
335  for (const auto& name : loaded_plugin_names)
336  {
337  if (!unload(name))
338  {
339  log_error("core", "could not unload plugin '{}'", name);
340  }
341  }
342  log_info("core", "unloaded all {} plugins", loaded_plugin_names.size());
343  return true;
344  }
345 
346  bool unload(const std::string& plugin_name)
347  {
348  auto loaded_it = m_loaded_plugins.find(plugin_name);
349  if (loaded_it == m_loaded_plugins.end())
350  {
351  log_debug("core", "cannot find plugin '{}' to unload it", plugin_name);
352  return true;
353  }
354 
355  log_info("core", "unloading plugin '{}'...", plugin_name);
356 
357 
358  auto rt_library = std::move(std::get<1>(loaded_it->second));
359  auto plugin_inst = std::move(std::get<0>(loaded_it->second));
360 
361  {
362  auto iplugType = dynamic_cast<UIPluginInterface*>(plugin_inst.get()) ? 1 : 0;
363  auto it = m_cli_option_to_plugin_name[iplugType].begin();
364  while (it != m_cli_option_to_plugin_name[iplugType].end())
365  {
366  auto flag = it->first;
367  auto name = it->second;
368  if (name == plugin_name)
369  {
370  m_plugin_options.remove(flag);
371  it = m_cli_option_to_plugin_name[iplugType].erase(it);
372  }
373  else
374  {
375  ++it;
376  }
377  }
378  }
379 
380  m_loaded_plugins.erase(loaded_it);
381 
382  for (AbstractExtensionInterface* aeif : plugin_inst->get_extensions())
383  {
384  FacExtensionInterface* feif = dynamic_cast<FacExtensionInterface*>(aeif);
385  if (!feif) continue;
386  switch (feif->get_feature())
387  {
390  break;
393  break;
396  break;
399  break;
400  default:
401  break;
402  }
403  }
404 
405  auto file_name = rt_library->get_file_name();
406  plugin_inst->on_unload();
407 
408  // actual unloading
409  // order of unloading is important, so we do it manually here:
410  // first destroy the plugin instance, then destroy the runtime library
411  plugin_inst.reset();
412  rt_library.reset();
413 
414  /* notify callback that a plugin was unloaded */
415  m_hook(false, plugin_name, file_name);
416 
417  log_debug("core", "unloaded plugin '{}'", plugin_name);
418  return true;
419  }
420 
421  BasePluginInterface* get_plugin_instance(const std::string& plugin_name, bool initialize, bool silent)
422  {
423  auto it = m_loaded_plugins.find(plugin_name);
424  if (it == m_loaded_plugins.end())
425  {
426  if (!silent)
427  log_error("core", "plugin '{}' is not loaded", plugin_name);
428  return nullptr;
429  }
430 
431  auto instance = std::get<0>(it->second).get();
432  if (instance != nullptr && initialize)
433  {
434  instance->initialize();
435  }
436  return instance;
437  }
438 
439  u64 add_model_changed_callback(std::function<void(bool, std::string const&, std::string const&)> callback)
440  {
441  if (callback == nullptr)
442  {
443  log_error("core", "parameter 'callback' is nullptr");
444  return 0;
445  }
446  return m_hook.add_callback(callback);
447  }
448 
450  {
451  m_hook.remove_callback(id);
452  }
453 
454  void add_existing_options_description(const ProgramOptions& existing_options)
455  {
456  m_existing_options.add(existing_options);
457  }
458  } // namespace plugin_manager
459 } // 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)
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