40#if defined(__linux__) && !defined(__ANDROID__)
52#include <wx/hashset.h>
53#include <wx/filename.h>
55#include <wx/tokenzr.h>
57#include <wx/process.h>
59#include "model/base_platform.h"
62#include "model/config_vars.h"
64#include "model/config_vars.h"
66#include "model/ocpn_utils.h"
67#include "model/plugin_blacklist.h"
68#include "model/plugin_cache.h"
69#include "model/plugin_handler.h"
70#include "model/plugin_loader.h"
71#include "model/plugin_paths.h"
72#include "model/safe_mode.h"
73#include "model/semantic_vers.h"
74#include "observable_confvar.h"
77#include "androidUTIL.h"
79#include "crashlytics.h"
86static const std::vector<std::string> SYSTEM_PLUGINS = {
87 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
91 const ArrayOfPlugIns& plugin_array) {
92 for (
size_t i = 0; i < plugin_array.GetCount(); i++) {
93 const auto& p = plugin_array.Item(i);
100static bool IsSystemPluginPath(
const std::string& path) {
101 static const std::vector<std::string> kPlugins = {
102 "chartdldr_pi",
"wmm_pi",
"dashboard_pi",
"grib_pi",
"demo_pi"};
104 const std::string lc_path = ocpn::tolower(path);
105 for (
const auto& p : kPlugins)
106 if (lc_path.find(p) != std::string::npos) return true;
111static bool IsSystemPluginName(
const std::string& name) {
112 static const std::vector<std::string> kPlugins = {
113 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
114 auto found = std::find(kPlugins.begin(), kPlugins.end(), ocpn::tolower(name));
115 return found != kPlugins.end();
119static std::string GetInstalledVersion(
const PlugInData& pd) {
121 if (path ==
"" || !wxFileName::IsFileReadable(path)) {
122 auto loader = PluginLoader::getInstance();
123 auto pic = GetContainer(pd, *loader->GetPlugInArray());
124 if (!pic || !pic->m_pplugin) {
127 int v_major = pic->m_pplugin->GetPlugInVersionMajor();
128 int v_minor = pic->m_pplugin->GetPlugInVersionMinor();
131 std::ifstream stream;
133 stream.open(path, std::ifstream::in);
139 auto catalogHdlr = CatalogHandler::getInstance();
143 SemanticVersion orphanVersion(pic->m_version_major, pic->m_version_minor);
144 mdata.version = orphanVersion.to_string();
145 mdata.summary = pic->m_short_description;
146 mdata.description = pic->m_long_description;
148 mdata.target =
"all";
149 mdata.is_orphan =
true;
156 std::function<
const PluginMetadata(
const std::string&)> get_metadata) {
157 auto loader = PluginLoader::getInstance();
158 auto pic = GetContainer(pd, *loader->GetPlugInArray());
164 metadata = pic->m_managed_metadata;
165 if (metadata.version ==
"")
167 std::string detail_suffix(metadata.is_imported ? _(
" [Imported]") :
"");
168 if (metadata.is_orphan) detail_suffix = _(
" [Orphan]");
172 if (pic->m_pplugin) {
173 v_major = pic->m_pplugin->GetPlugInVersionMajor();
174 v_minor = pic->m_pplugin->GetPlugInVersionMinor();
180 v_major, v_minor, p->GetPlugInVersionPatch(), p->GetPlugInVersionPost(),
181 p->GetPlugInVersionPre(), p->GetPlugInVersionBuild());
182 return sv.to_string() + detail_suffix;
184 if (!metadata.is_orphan) {
185 std::string version = GetInstalledVersion(pd);
186 return version + detail_suffix;
188 return metadata.version + detail_suffix;
192PlugInContainer::PlugInContainer()
193 :
PlugInData(), m_pplugin(nullptr), m_library(), m_destroy_fn(nullptr) {}
196 : m_has_setup_options(false),
199 m_toolbox_panel(false),
204 m_status(PluginStatus::Unknown) {}
209 m_version_major = v.major;
210 m_version_minor = v.minor;
211 m_managed_metadata = md;
212 m_status = PluginStatus::ManagedInstallAvailable;
217 return std::string(m_status == PluginStatus::Managed ?
"1" :
"0") +
236static void setLoadPath() {
239 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
241 if (osSystemId & wxOS_UNIX_LINUX) {
242 string path = ocpn::join(dirs,
':');
244 if (wxGetEnv(
"LD_LIBRARY_PATH", &envPath)) {
245 path = path +
":" + envPath.ToStdString();
247 wxLogMessage(
"Using LD_LIBRARY_PATH: %s", path.c_str());
248 wxSetEnv(
"LD_LIBRARY_PATH", path.c_str());
249 }
else if (osSystemId & wxOS_WINDOWS) {
251 string path = ocpn::join(dirs,
';');
253 if (wxGetEnv(
"PATH", &envPath)) {
254 path = path +
";" + envPath.ToStdString();
256 wxLogMessage(
"Using PATH: %s", path);
257 wxSetEnv(
"PATH", path);
258 }
else if (osSystemId & wxOS_MAC) {
259 string path = ocpn::join(dirs,
':');
261 if (wxGetEnv(
"DYLD_LIBRARY_PATH", &envPath)) {
262 path = path +
":" + envPath.ToStdString();
264 wxLogMessage(
"Using DYLD_LIBRARY_PATH: %s", path.c_str());
265 wxSetEnv(
"DYLD_LIBRARY_PATH", path.c_str());
267 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
268 if (os_name.Contains(
"wxQT")) {
269 wxLogMessage(
"setLoadPath() using Android library path");
271 wxLogWarning(
"SetLoadPath: Unsupported platform.");
273 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
275 string path = ocpn::join(dirs,
':');
277 wxGetEnv(
"PATH", &envPath);
278 path = path +
":" + envPath.ToStdString();
279 wxLogMessage(
"Using PATH: %s", path);
280 wxSetEnv(
"PATH", path);
286 wxString msg(
"PluginLoader: Calling LateInit PlugIn: ");
291 if (ppi) ppi->LateInit();
302PluginLoader::PluginLoader()
303 : m_blacklist(blacklist_factory()),
304 m_default_plugin_icon(nullptr),
306 m_found_wxwidgets(false),
311bool PluginLoader::IsPlugInAvailable(
const wxString& commonName) {
312 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
314 if (pic && pic->m_enabled && (pic->
m_common_name == commonName))
322 auto loader = PluginLoader::getInstance();
323 auto pic = GetContainer(pd, *loader->GetPlugInArray());
324 if (pic) pic->m_pplugin->ShowPreferencesDialog(parent);
327void PluginLoader::NotifySetupOptionsPlugin(
const PlugInData* pd) {
328 auto pic = GetContainer(*pd, *GetPlugInArray());
332 if (pic->m_enabled && pic->m_init_state) {
334 switch (pic->m_api_version) {
345 if (pic->m_pplugin) {
348 ppi->OnSetupOptions();
349 auto loader = PluginLoader::getInstance();
363 for (
size_t i = 0; i < plugin_array.GetCount(); i++) {
366 pic->m_enabled = enabled;
373 for (
size_t i = 0; i < plugin_array.GetCount(); i++) {
376 pic->m_toolbox_panel = value;
380 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
385 for (
size_t i = 0; i < plugin_array.GetCount(); i++) {
392 wxLogMessage(
"Atttempt to update setup options on non-existing plugin " +
396const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
397 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
398 return m_default_plugin_icon;
401void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
402 delete m_default_plugin_icon;
403 m_default_plugin_icon = bitmap;
407 auto pic = GetContainer(pd, plugin_array);
409 wxLogMessage(
"Attempt to remove non-existing plugin %s",
413 plugin_array.Remove(pic);
417 return (*p1)->Key().compare((*p2)->Key());
422 plugin_array.Sort(ComparePlugins);
428 static const wxString sep = wxFileName::GetPathSeparator();
430 wxLogMessage(
"PluginLoader: loading plugins from %s", ocpn::join(dirs,
';'));
432 bool any_dir_loaded =
false;
433 for (
const auto& dir : dirs) {
435 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
436 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
442 if (!load_enabled) UpdateManagedPlugins(keep_orphans);
445 evt_update_chart_types.
Notify();
446 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
450 return any_dir_loaded;
453bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
455 wxString plugin_file = wxFileName(file_name).GetFullName();
456 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
457 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
458 wxLog::FlushActive();
461 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
462 file_name.ToStdString().c_str());
471 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
481 plugin_array.Remove(pic_test);
484 DeactivatePlugIn(pic_test);
485 pic_test->m_destroy_fn(pic_test->m_pplugin);
490 loaded_pic = pic_test;
495 loaded_pic = pic_test;
501 if (loaded)
return true;
503 wxFileName fn_plugin_file(file_name);
504 wxString plugin_file_path =
505 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
506 wxString base_plugin_path = g_BasePlatform->
GetPluginDir();
507 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
508 base_plugin_path += wxFileName::GetPathSeparator();
511 if (base_plugin_path.IsSameAs(plugin_file_path)) {
512 if (!IsSystemPluginPath(file_name.ToStdString())) {
513 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
520 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
521 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
526 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
527 wxLogMessage(msg.c_str());
528 wxLog::FlushActive();
530 bool b_compat = CheckPluginCompatibility(file_name);
534 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
535 wxLogMessage(msg.c_str());
536 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
537 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
538 load_errors.push_back(le);
548 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
550 if (pic && load_enabled && !enabled.Get(
true)) {
551 pic->m_destroy_fn(pic->m_pplugin);
553 wxLogMessage(
"Skipping not enabled candidate.");
558 if (pic->m_pplugin) {
559 plugin_array.Add(pic);
566 pic->m_enabled = enabled.Get(
false);
568 if (safe_mode::get_mode()) {
569 pic->m_enabled =
false;
572 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
574 if (pic->m_enabled) {
576 pic->m_init_state =
true;
579 evt_load_plugin.
Notify(pic);
580 wxLog::FlushActive();
582 std::string found_version;
583 for (
const auto& p :
PluginHandler::getInstance()->getInstalled()) {
585 found_version = p.readonly ?
"" : p.version;
590 pic->m_short_description = pic->m_pplugin->GetShortDescription();
591 pic->m_long_description = pic->m_pplugin->GetLongDescription();
592 pic->m_version_major = pic->m_pplugin->GetPlugInVersionMajor();
593 pic->m_version_minor = pic->m_pplugin->GetPlugInVersionMinor();
597 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
599 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
600 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
602 if (!pic->m_enabled && pic->m_destroy_fn) {
603 pic->m_destroy_fn(pic->m_pplugin);
604 pic->m_destroy_fn =
nullptr;
605 pic->m_pplugin =
nullptr;
606 pic->m_init_state =
false;
607 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
612 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
614 bool is_system = found != SYSTEM_PLUGINS.end();
620 available.begin(), available.end(),
623 if (it == available.end()) {
628 auto oprhan_metadata = CreateMetadata(pic);
629 auto catalogHdlr = CatalogHandler::getInstance();
630 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
636 " PluginLoader: Unloading invalid PlugIn, API version %d ",
638 pic->m_destroy_fn(pic->m_pplugin);
640 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
642 load_errors.push_back(le);
652bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
654 evt_load_directory.
Notify();
655 m_plugin_location = plugin_dir;
657 wxString msg(
"PluginLoader searching for PlugIns in location ");
658 msg += m_plugin_location;
662 wxString pispec =
"*_pi.dll";
663#elif defined(__WXOSX__)
664 wxString pispec =
"*_pi.dylib";
666 wxString pispec =
"*_pi.so";
669 if (!::wxDirExists(m_plugin_location)) {
670 msg = m_plugin_location;
671 msg.Prepend(
" Directory ");
672 msg.Append(
" does not exist.");
677 if (!g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
679 wxArrayString file_list;
681 int get_flags = wxDIR_FILES | wxDIR_DIRS;
684 get_flags = wxDIR_FILES;
689 get_flags = wxDIR_FILES;
695 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
697 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
698 for (
unsigned int i = 0; i < file_list.GetCount(); i++) {
699 wxLog::FlushActive();
701 wxString file_name = file_list[i];
703 LoadPluginCandidate(file_name, load_enabled);
710 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
712 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
717 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
719 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
726 while ((i >= 0) && (i < plugin_array.GetCount())) {
728 if (pict->m_status == PluginStatus::PendingListRemoval) {
729 plugin_array.RemoveAt(i);
738bool PluginLoader::UpdatePlugIns() {
741 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
746 if (pic->m_pplugin) {
749 pic->m_pplugin =
nullptr;
750 pic->m_init_state =
false;
755 if (!pic->m_pplugin) {
756 if (pic->m_enabled) {
757 PluginStatus stat = pic->m_status;
760 pic->m_status = stat;
761 pic->m_enabled =
true;
767 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
768 wxString msg(
"PluginLoader: Initializing PlugIn: ");
774 pic->m_pplugin->SetDefaults();
775 pic->m_init_state =
true;
776 ProcessLateInit(pic);
777 pic->m_short_description = pic->m_pplugin->GetShortDescription();
778 pic->m_long_description = pic->m_pplugin->GetLongDescription();
779 pic->m_version_major = pic->m_pplugin->GetPlugInVersionMajor();
780 pic->m_version_minor = pic->m_pplugin->GetPlugInVersionMinor();
782 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
783 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
785 }
else if (!pic->m_enabled && pic->m_init_state) {
788 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
789 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
791 bret = DeactivatePlugIn(pic);
792 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
793 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
794 pic->m_pplugin =
nullptr;
795 pic->m_init_state =
false;
798 evt_update_chart_types.
Notify();
803 if (!pic)
return false;
804 if (pic->m_init_state) {
805 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
807 m_on_deactivate_cb(pic);
808 pic->m_init_state =
false;
809 pic->m_pplugin->DeInit();
814bool PluginLoader::DeactivatePlugIn(
const PlugInData& pd) {
815 auto pic = GetContainer(pd, plugin_array);
817 wxLogError(
"Attempt to deactivate non-existing plugin %s",
821 return DeactivatePlugIn(pic);
825 if (ix >= plugin_array.GetCount()) {
826 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
830 if (!DeactivatePlugIn(pic)) {
833 if (pic->m_pplugin) {
834 pic->m_destroy_fn(pic->m_pplugin);
838 plugin_array.RemoveAt(ix);
842static std::string VersionFromManifest(
const std::string& plugin_name) {
845 if (!path.empty() && wxFileName::IsFileReadable(path)) {
846 std::ifstream stream;
847 stream.open(path, std::ifstream::in);
856 if (name.empty())
return {};
859 if (isRegularFile(import_path.c_str())) {
860 std::ifstream f(import_path.c_str());
861 std::stringstream ss;
864 ParsePlugin(ss.str(), pd);
865 pd.is_imported =
true;
870 vector<PluginMetadata> matches;
871 copy_if(available.begin(), available.end(), back_inserter(matches),
873 if (matches.size() == 0)
return {};
874 if (matches.size() == 1)
return matches[0];
876 auto version = VersionFromManifest(name);
878 return version == md.version;
880 auto found = find_if(matches.begin(), matches.end(), predicate);
881 return found != matches.end() ? *found : matches[0];
887 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
889 bool is_system = found != SYSTEM_PLUGINS.end();
891 std::string installed = VersionFromManifest(md.name);
897 plugin->m_status = PluginStatus::System;
898 else if (plugin->m_status == PluginStatus::Imported)
900 else if (installedVersion < metaVersion)
901 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
902 else if (installedVersion == metaVersion)
903 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
905 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
907 if (!is_system && md.is_orphan) plugin->m_status = PluginStatus::Unmanaged;
909 plugin->m_managed_metadata = md;
912void PluginLoader::UpdateManagedPlugins(
bool keep_orphans) {
913 std::vector<PlugInContainer*> loaded_plugins;
914 for (
size_t i = 0; i < plugin_array.GetCount(); i++)
915 loaded_plugins.push_back(plugin_array.Item(i));
918 for (
auto& p : loaded_plugins) {
919 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
920 p->m_common_name.Lower().ToStdString());
921 bool is_system = found != SYSTEM_PLUGINS.end();
922 p->m_status = is_system ? PluginStatus::System : PluginStatus::Unmanaged;
931 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
935 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
936 loaded_plugins.erase(end, loaded_plugins.end());
940 for (
auto& plugin : loaded_plugins) {
942 if (!md.name.empty()) {
944 md.is_imported = isRegularFile(import_path.c_str());
945 if (md.is_imported) {
946 plugin->m_status = PluginStatus::Imported;
950 }
else if (IsSystemPluginName(md.name)) {
951 plugin->m_status = PluginStatus::System;
952 }
else if (md.is_orphan) {
953 plugin->m_status = PluginStatus::Unmanaged;
954 }
else if (plugin->m_api_version) {
957 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
958 plugin->m_managed_metadata = md;
961 plugin->m_status = PluginStatus::ManagedInstallAvailable;
966 plugin_array.Clear();
967 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
968 evt_pluglist_change.
Notify();
971bool PluginLoader::UnLoadAllPlugIns() {
973 while (plugin_array.GetCount()) {
981bool PluginLoader::DeactivateAllPlugIns() {
982 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
984 if (pic && pic->m_enabled && pic->m_init_state) DeactivatePlugIn(pic);
991DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
993 PIMAGE_SECTION_HEADER pSeh;
998 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
999 if (rva >= pSeh->VirtualAddress &&
1000 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1005 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1012 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1013 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1016 uint64_t type_magic;
1017 DependencyMap dependencies;
1021bool ReadModuleInfoFromELF(
const wxString& file,
1022 const ModuleInfo::DependencySet& dependencies,
1024 static bool b_libelf_initialized =
false;
1025 static bool b_libelf_usable =
false;
1027 if (b_libelf_usable) {
1029 }
else if (b_libelf_initialized) {
1031 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1032 b_libelf_initialized =
true;
1033 b_libelf_usable =
false;
1034 wxLogError(
"LibELF is outdated.");
1037 b_libelf_initialized =
true;
1038 b_libelf_usable =
true;
1042 Elf* elf_handle =
nullptr;
1043 GElf_Ehdr elf_file_header;
1044 Elf_Scn* elf_section_handle =
nullptr;
1046 file_handle = open(file, O_RDONLY);
1047 if (file_handle == -1) {
1048 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1050 goto FailureEpilogue;
1053 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1054 if (elf_handle ==
nullptr) {
1055 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1056 goto FailureEpilogue;
1059 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1060 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1061 goto FailureEpilogue;
1064 switch (elf_file_header.e_type) {
1069 wxLogMessage(wxString::Format(
1070 "Module \"%s\" is not an executable or shared library.", file));
1071 goto FailureEpilogue;
1075 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1077 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1079 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1081 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1083 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1087 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1089 GElf_Shdr elf_section_header;
1090 Elf_Data* elf_section_data =
nullptr;
1091 size_t elf_section_entry_count = 0;
1093 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1094 &elf_section_header) {
1095 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1096 goto FailureEpilogue;
1097 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1101 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1102 if (elf_section_data ==
nullptr) {
1103 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1104 goto FailureEpilogue;
1107 if ((elf_section_data->d_size == 0) ||
1108 (elf_section_header.sh_entsize == 0)) {
1109 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1110 goto FailureEpilogue;
1113 elf_section_entry_count =
1114 elf_section_data->d_size / elf_section_header.sh_entsize;
1115 for (
size_t elf_section_entry_index = 0;
1116 elf_section_entry_index < elf_section_entry_count;
1117 ++elf_section_entry_index) {
1118 GElf_Dyn elf_dynamic_entry;
1119 const char* elf_dynamic_entry_name =
nullptr;
1120 if (gelf_getdyn(elf_section_data,
1121 static_cast<int>(elf_section_entry_index),
1122 &elf_dynamic_entry) != &elf_dynamic_entry) {
1123 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1125 goto FailureEpilogue;
1126 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1129 elf_dynamic_entry_name = elf_strptr(
1130 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1131 if (elf_dynamic_entry_name ==
nullptr) {
1132 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1133 "string entry", file));
1134 goto FailureEpilogue;
1136 wxString name_full(elf_dynamic_entry_name);
1137 wxString name_part(elf_dynamic_entry_name,
1138 strcspn(elf_dynamic_entry_name,
"-."));
1139 if (dependencies.find(name_part) != dependencies.end()) {
1140 info.dependencies.insert(
1141 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1146 goto SuccessEpilogue;
1149 elf_end(elf_handle);
1154 if (elf_handle !=
nullptr) elf_end(elf_handle);
1155 if (file_handle >= 0) close(file_handle);
1156 wxLog::FlushActive();
1161bool PluginLoader::CheckPluginCompatibility(
const wxString& plugin_file) {
1162 bool b_compat =
false;
1178 if (!m_found_wxwidgets) {
1179 DWORD myPid = GetCurrentProcessId();
1181 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1182 if (hProcess == NULL) {
1183 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1184 plugin_file.c_str()));
1188 HMODULE hMods[1024];
1190 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1191 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1192 TCHAR szModName[MAX_PATH];
1193 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1194 sizeof(szModName) /
sizeof(TCHAR))) {
1195 m_module_name = szModName;
1196 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1197 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1198 m_found_wxwidgets =
true;
1199 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1200 m_module_name.c_str()));
1207 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1208 plugin_file.c_str()));
1210 if (hProcess) CloseHandle(hProcess);
1213 if (!m_found_wxwidgets) {
1214 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1215 plugin_file.c_str()));
1217 LPCWSTR fName = plugin_file.wc_str();
1218 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1219 FILE_ATTRIBUTE_NORMAL, 0);
1220 DWORD byteread, size = GetFileSize(handle, NULL);
1221 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1222 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1223 CloseHandle(handle);
1224 PIMAGE_NT_HEADERS ntheaders =
1225 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1226 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1227 PIMAGE_SECTION_HEADER pSech =
1228 IMAGE_FIRST_SECTION(ntheaders);
1229 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1230 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1235 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1237 ntheaders->OptionalHeader
1239 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1246 while (pImportDescriptor->Name != 0) {
1249 (PCHAR)((DWORD_PTR)virtualpointer +
1250 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1253 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1256 wxLogMessage(wxString::Format(
1257 "Compatible wxWidgets plugin library found for %s: %s",
1258 plugin_file.c_str(), libname[i]));
1261 pImportDescriptor++;
1266 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1268 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1271#if defined(__WXGTK__) || defined(__WXQT__)
1272#if defined(USE_LIBELF)
1274 static bool b_own_info_queried =
false;
1275 static bool b_own_info_usable =
false;
1277 static ModuleInfo::DependencySet dependencies;
1279 if (!b_own_info_queried) {
1280 dependencies.insert(
"libwx_baseu");
1282 char exe_buf[100] = {0};
1283 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1285 exe_buf[len] =
'\0';
1286 wxString app_path(exe_buf);
1287 wxLogMessage(
"Executable path: %s", exe_buf);
1289 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1290 if (!b_own_info_usable) {
1291 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1294 wxLogMessage(
"Cannot get own executable path.");
1296 b_own_info_queried =
true;
1299 if (b_own_info_usable) {
1300 bool b_pi_info_usable =
false;
1303 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1304 if (b_pi_info_usable) {
1305 b_compat = (pi_info.type_magic == own_info.type_magic);
1308 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1313 pi_info.dependencies.clear();
1315 wxString::Format(
" Plugin \"%s\" is of another binary "
1316 "flavor than the main module.",
1318 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1319 own_info.type_magic, pi_info.type_magic);
1321 for (
const auto& own_dependency : own_info.dependencies) {
1322 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1323 pi_info.dependencies.find(own_dependency.first);
1324 if ((pi_dependency != pi_info.dependencies.end()) &&
1325 (pi_dependency->second != own_dependency.second)) {
1328 " Plugin \"%s\" depends on library \"%s\", but the main "
1329 "module was built for \"%s\".",
1330 plugin_file, pi_dependency->second, own_dependency.second);
1337 wxString::Format(
" Plugin \"%s\" could not be reliably "
1338 "checked for compatibility.",
1346 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1347 b_compat ?
"true" :
"false");
1349 wxLog::FlushActive();
1364 FILE* f = fopen(plugin_file,
"r");
1367 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1371#
if defined(__WXGTK3__)
1372 "libwx_gtk3u_core-%i.%i"
1373#elif defined(__WXGTK20__)
1374 "libwx_gtk2u_core-%i.%i"
1375#elif defined(__WXQT__)
1376 "libwx_qtu_core-%i.%i"
1378#error undefined plugin platform
1381 wxMAJOR_VERSION, wxMINOR_VERSION);
1385 size_t len(strlen(strver));
1387 while ((c = fgetc(f)) != EOF) {
1388 if (c == strver[pos]) {
1399 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1403PlugInContainer* PluginLoader::LoadPlugIn(
const wxString& plugin_file) {
1405 if (!LoadPlugIn(plugin_file, pic)) {
1415 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1417 if (plugin_file.empty()) {
1418 wxLogMessage(
"Ignoring loading of empty path");
1422 if (!wxIsReadable(plugin_file)) {
1423 wxLogMessage(
"Ignoring unreadable plugin %s",
1424 plugin_file.ToStdString().c_str());
1425 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1426 load_errors.push_back(le);
1433 pic->m_version_major, pic->m_version_minor);
1434 if (sts != plug_status::unblocked) {
1435 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1439 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1440 if (!data.name.empty()) {
1441 wxLogDebug(
"Refusing to load blacklisted library: %s",
1442 plugin_file.ToStdString().c_str());
1447 PluginStatus::Unmanaged;
1450 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1451 pic->m_library.Load(plugin_file);
1453 if (!pic->m_library.IsLoaded()) {
1456 wxFileName fn(plugin_file);
1457 std::string name = fn.GetName().ToStdString();
1458 auto found = m_blacklist->get_library_data(name);
1459 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1460 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1461 if (!found.name.empty()) {
1463 LoadError le(LoadError::Type::Unloadable, name, v);
1464 load_errors.push_back(le);
1466 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1467 load_errors.push_back(le);
1470 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1476 const char*
const FIX_LOADING =
1477 _(
"\n Install/uninstall plugin or remove file to mute message");
1478 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1479 if (
nullptr == create_plugin) {
1480 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1481 wxLogMessage(msg + plugin_file);
1482 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1483 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1484 load_errors.push_back(le);
1489 destroy_t* destroy_plugin =
1490 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1491 pic->m_destroy_fn = destroy_plugin;
1492 if (
nullptr == destroy_plugin) {
1493 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1495 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1496 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1497 load_errors.push_back(le);
1505 int api_major = plug_in->GetAPIVersionMajor();
1506 int api_minor = plug_in->GetAPIVersionMinor();
1507 int api_ver = (api_major * 100) + api_minor;
1508 pic->m_api_version = api_ver;
1510 int pi_major = plug_in->GetPlugInVersionMajor();
1511 int pi_minor = plug_in->GetPlugInVersionMinor();
1514 wxString pi_name = plug_in->GetCommonName();
1516 wxLogDebug(
"blacklist: Get status for %s %d %d",
1517 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1519 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1520 if (status != plug_status::unblocked) {
1521 wxLogDebug(
"Ignoring blacklisted plugin.");
1522 if (status != plug_status::unloadable) {
1524 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1525 load_errors.push_back(le);
1600 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1601 p->GetPlugInVersionBuild());
1604 if (!pic->m_pplugin) {
1605 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1606 INFO_LOG << _(
" API Version detected: ");
1607 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1608 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1609 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1611 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1613 load_errors.push_back(le);
Plugin catalog management: Build the runtime catalog, handling downloads as required.
Datatypes and methods to parse ocpn-plugins.xml XML data, either complete catalog or a single plugin.
Wrapper for configuration variables which lives in a wxBaseConfig object.
const void Notify()
Notify all listeners, no data supplied.
Data for a loaded plugin, including dl-loaded library.
Basic data for a loaded plugin, trivially copyable.
wxString m_plugin_filename
The short file path.
wxString m_plugin_file
The full file path.
int m_cap_flag
PlugIn Capabilities descriptor.
PlugInData(const PluginMetadata &md)
Create a container with applicable fields defined from metadata.
wxString m_common_name
A common name string for the plugin.
bool m_has_setup_options
Has run NotifySetupOptionsPlugin()
std::string Key() const
sort key.
std::string m_manifest_version
As detected from manifest.
wxDateTime m_plugin_modification
used to detect upgraded plugins
wxString m_version_str
Complete version as of semantic_vers.
std::vector< PluginMetadata > getCompatiblePlugins()
Return list of available, unique and compatible plugins from configured XML catalog.
static std::string ImportedMetadataPath(std::string name)
Return path to imported metadata for given plugin.
static std::string fileListPath(std::string name)
Return path to installation manifest for given plugin.
static std::string versionPath(std::string name)
Return path to file containing version for given plugin.
PluginLoader is a backend module without any direct GUI functionality.
bool LoadAllPlugIns(bool enabled_plugins, bool keep_orphans=false)
Update catalog with imported metadata and load all plugin library files.
EventVar evt_plugin_loadall_finalize
Emitted after all plugins are loaded.
static std::string GetPluginVersion(const PlugInData pd, std::function< const PluginMetadata(const std::string &)> get_metadata)
Return version string for a plugin, possibly with an "Imported" suffix.
bool UnLoadPlugIn(size_t ix)
Unload, delete and remove item ix in GetPlugInArray().
void SortPlugins(int(*cmp_func)(PlugInContainer **, PlugInContainer **))
Sort GetPluginArray().
static void UpdatePlugin(PlugInContainer *plugin, const PluginMetadata &md)
Update PlugInContainer status using data from PluginMetadata and manifest.
void SetSetupOptions(const wxString &common_name, bool value)
Update m_has_setup_options state for plugin with given name.
void SetEnabled(const wxString &common_name, bool enabled)
Update enabled/disabled state for plugin with given name.
void SetToolboxPanel(const wxString &common_name, bool value)
Update m_toolbox_panel state for plugin with given name.
static PluginMetadata MetadataByName(const std::string &name)
Find metadata for given plugin.
void RemovePlugin(const PlugInData &pd)
Remove a plugin from *GetPluginArray().
void ShowPreferencesDialog(const PlugInData &pd, wxWindow *parent)
Display the preferences dialog for a plugin.
std::vector< std::string > Libdirs()
List of directories from which we load plugins.
std::vector< std::string > Bindirs()
'List of directories for plugin binary helpers.
static PluginPaths * getInstance()
Return the singleton instance.
virtual wxBitmap * GetPlugInBitmap()
FIXME static wxBitmap* LoadSVG(const wxString filename, unsigned int width, ...
Global variables reflecting command line options and arguments.
Enhanced logging interface on top of wx/log.h.
#define WANTS_LATE_INIT
Delay full plugin initialization until system is ready.
#define INSTALLS_TOOLBOX_PAGE
Plugin will add pages to the toolbox/settings dialog.
Versions uses a modified semantic versioning scheme: major.minor.revision.post-tag+build.
static SemanticVersion parse(std::string s)
Parse a version string, sets major == -1 on errors.
std::string to_string()
Return printable representation.