39#if defined(__linux__) && !defined(__ANDROID__)
51#include <wx/hashset.h>
52#include <wx/filename.h>
54#include <wx/tokenzr.h>
56#include <wx/process.h>
58#include "model/base_platform.h"
61#include "model/config_vars.h"
63#include "model/config_vars.h"
66#include "model/plugin_blacklist.h"
67#include "model/plugin_cache.h"
71#include "model/safe_mode.h"
72#include "model/semantic_vers.h"
73#include "observable_confvar.h"
74#include "std_filesystem.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 (
const auto& p : plugin_array) {
99static bool IsSystemPluginPath(
const std::string& path) {
100 static const std::vector<std::string> kPlugins = {
101 "chartdldr_pi",
"wmm_pi",
"dashboard_pi",
"grib_pi",
"demo_pi"};
104 for (
const auto& p : kPlugins)
105 if (lc_path.find(p) != std::string::npos) return true;
110static bool IsSystemPluginName(
const std::string& name) {
111 static const std::vector<std::string> kPlugins = {
112 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
113 auto found = std::find(kPlugins.begin(), kPlugins.end(),
ocpn::tolower(name));
114 return found != kPlugins.end();
118static std::string GetInstalledVersion(
const PlugInData& pd) {
120 if (path ==
"" || !wxFileName::IsFileReadable(path)) {
121 auto loader = PluginLoader::GetInstance();
122 auto pic = GetContainer(pd, *loader->GetPlugInArray());
123 if (!pic || !pic->m_pplugin) {
126 int v_major = pic->m_pplugin->GetPlugInVersionMajor();
127 int v_minor = pic->m_pplugin->GetPlugInVersionMinor();
130 std::ifstream stream;
132 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;
155static fs::path LoadStampPath(
const std::string& file_path) {
157 path = path /
"load_stamps";
162 return path.parent_path() / path.stem();
166static void CreateLoadStamp(
const std::string& filename) {
167 std::ofstream(LoadStampPath(filename).
string());
177static bool HasLoadStamp(
const std::string& filename) {
178 return exists(LoadStampPath(filename));
185static void ClearLoadStamp(
const std::string& filename) {
186 if (filename.empty())
return;
187 auto path = LoadStampPath(filename);
190 MESSAGE_LOG <<
" Cannot remove load stamp file: " << path;
196 ClearLoadStamp(library_path);
201 std::function<
const PluginMetadata(
const std::string&)> get_metadata) {
202 auto loader = PluginLoader::GetInstance();
203 auto pic = GetContainer(pd, *loader->GetPlugInArray());
209 metadata = pic->m_managed_metadata;
210 if (metadata.version ==
"")
212 std::string detail_suffix(metadata.is_imported ? _(
" [Imported]") :
"");
213 if (metadata.is_orphan) detail_suffix = _(
" [Orphan]");
217 if (pic->m_pplugin) {
225 v_major, v_minor, p->GetPlugInVersionPatch(), p->GetPlugInVersionPost(),
226 p->GetPlugInVersionPre(), p->GetPlugInVersionBuild());
227 return sv.to_string() + detail_suffix;
229 if (!metadata.is_orphan) {
230 std::string version = GetInstalledVersion(pd);
231 return version + detail_suffix;
233 return metadata.version + detail_suffix;
237PlugInContainer::PlugInContainer()
238 :
PlugInData(), m_pplugin(nullptr), m_library(), m_destroy_fn(nullptr) {}
241 : m_has_setup_options(false),
244 m_toolbox_panel(false),
254 m_version_major = v.major;
255 m_version_minor = v.minor;
256 m_managed_metadata = md;
257 m_status = PluginStatus::ManagedInstallAvailable;
281static void setLoadPath() {
284 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
286 if (osSystemId & wxOS_UNIX_LINUX) {
289 if (wxGetEnv(
"LD_LIBRARY_PATH", &envPath)) {
290 path = path +
":" + envPath.ToStdString();
292 wxLogMessage(
"Using LD_LIBRARY_PATH: %s", path.c_str());
293 wxSetEnv(
"LD_LIBRARY_PATH", path.c_str());
294 }
else if (osSystemId & wxOS_WINDOWS) {
298 if (wxGetEnv(
"PATH", &envPath)) {
299 path = path +
";" + envPath.ToStdString();
301 wxLogMessage(
"Using PATH: %s", path);
302 wxSetEnv(
"PATH", path);
303 }
else if (osSystemId & wxOS_MAC) {
306 if (wxGetEnv(
"DYLD_LIBRARY_PATH", &envPath)) {
307 path = path +
":" + envPath.ToStdString();
309 wxLogMessage(
"Using DYLD_LIBRARY_PATH: %s", path.c_str());
310 wxSetEnv(
"DYLD_LIBRARY_PATH", path.c_str());
312 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
313 if (os_name.Contains(
"wxQT")) {
314 wxLogMessage(
"setLoadPath() using Android library path");
316 wxLogWarning(
"SetLoadPath: Unsupported platform.");
318 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
322 wxGetEnv(
"PATH", &envPath);
323 path = path +
":" + envPath.ToStdString();
324 wxLogMessage(
"Using PATH: %s", path);
325 wxSetEnv(
"PATH", path);
331 wxString msg(
"PluginLoader: Calling LateInit PlugIn: ");
336 if (ppi) ppi->LateInit();
347PluginLoader::PluginLoader()
348 : m_blacklist(blacklist_factory()),
349 m_default_plugin_icon(nullptr),
351 m_found_wxwidgets(false),
357 for (
auto* pic : plugin_array) {
358 if (pic && pic->m_enabled && (pic->
m_common_name == commonName))
366 auto loader = PluginLoader::GetInstance();
367 auto pic = GetContainer(pd, *loader->GetPlugInArray());
371void PluginLoader::NotifySetupOptionsPlugin(
const PlugInData* pd) {
376 if (pic->m_enabled && pic->m_init_state) {
378 switch (pic->m_api_version) {
389 if (pic->m_pplugin) {
393 auto loader = PluginLoader::GetInstance();
407 for (
auto* pic : plugin_array) {
409 pic->m_enabled = enabled;
416 for (
auto* pic : plugin_array) {
418 pic->m_toolbox_panel = value;
422 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
427 for (
auto* pic : plugin_array) {
433 wxLogMessage(
"Atttempt to update setup options on non-existing plugin " +
437const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
438 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
439 return m_default_plugin_icon;
442void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
443 delete m_default_plugin_icon;
444 m_default_plugin_icon = bitmap;
448 auto pic = GetContainer(pd, plugin_array);
450 wxLogMessage(
"Attempt to remove non-existing plugin %s",
454 plugin_array.Remove(pic);
458 return (*p1)->Key().compare((*p2)->Key());
463 plugin_array.Sort(ComparePlugins);
469 static const wxString sep = wxFileName::GetPathSeparator();
471 wxLogMessage(
"PluginLoader: loading plugins from %s",
ocpn::join(dirs,
';'));
473 bool any_dir_loaded =
false;
474 for (
const auto& dir : dirs) {
476 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
477 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
487 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
491 return any_dir_loaded;
494bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
496 wxString plugin_file = wxFileName(file_name).GetFullName();
497 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
499 wxString plugin_loadstamp = wxFileName(file_name).GetName();
500 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
501 MESSAGE_LOG <<
"Refusing to load " << file_name
502 <<
" failed at last attempt";
505 CreateLoadStamp(plugin_loadstamp.ToStdString());
506 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
507 wxLog::FlushActive();
510 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
511 file_name.ToStdString().c_str());
520 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
529 plugin_array.Remove(pic_test);
533 pic_test->m_destroy_fn(pic_test->m_pplugin);
538 loaded_pic = pic_test;
543 loaded_pic = pic_test;
550 ClearLoadStamp(plugin_loadstamp.ToStdString());
555 wxFileName fn_plugin_file(file_name);
556 wxString plugin_file_path =
557 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
558 wxString base_plugin_path = g_BasePlatform->
GetPluginDir();
559 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
560 base_plugin_path += wxFileName::GetPathSeparator();
563 if (base_plugin_path.IsSameAs(plugin_file_path)) {
564 if (!IsSystemPluginPath(file_name.ToStdString())) {
565 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
568 ClearLoadStamp(plugin_loadstamp.ToStdString());
574 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
575 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
576 ClearLoadStamp(plugin_loadstamp.ToStdString());
581 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
582 wxLogMessage(msg.c_str());
583 wxLog::FlushActive();
589 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
590 wxLogMessage(msg.c_str());
591 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
592 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
593 load_errors.push_back(le);
603 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
605 if (pic && load_enabled && !enabled.Get(
true)) {
606 pic->m_destroy_fn(pic->m_pplugin);
608 wxLogMessage(
"Skipping not enabled candidate.");
609 ClearLoadStamp(plugin_loadstamp.ToStdString());
614 if (pic->m_pplugin) {
615 plugin_array.Add(pic);
622 pic->m_enabled = enabled.Get(
false);
624 if (safe_mode::get_mode()) {
625 pic->m_enabled =
false;
628 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
630 if (pic->m_enabled) {
632 pic->m_init_state =
true;
636 wxLog::FlushActive();
638 std::string found_version;
639 for (
const auto& p :
PluginHandler::GetInstance()->GetInstalled()) {
641 found_version = p.readonly ?
"" : p.version;
653 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
655 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
656 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
658 if (!pic->m_enabled && pic->m_destroy_fn) {
659 pic->m_destroy_fn(pic->m_pplugin);
660 pic->m_destroy_fn =
nullptr;
661 pic->m_pplugin =
nullptr;
662 pic->m_init_state =
false;
663 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
668 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
670 bool is_system = found != SYSTEM_PLUGINS.end();
676 available.begin(), available.end(),
679 if (it == available.end()) {
684 auto oprhan_metadata = CreateMetadata(pic);
685 auto catalogHdlr = CatalogHandler::GetInstance();
686 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
692 " PluginLoader: Unloading invalid PlugIn, API version %d ",
694 pic->m_destroy_fn(pic->m_pplugin);
696 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
698 load_errors.push_back(le);
704 ClearLoadStamp(plugin_loadstamp.ToStdString());
709bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
712 m_plugin_location = plugin_dir;
714 wxString msg(
"PluginLoader searching for PlugIns in location ");
715 msg += m_plugin_location;
719 wxString pispec =
"*_pi.dll";
720#elif defined(__WXOSX__)
721 wxString pispec =
"*_pi.dylib";
723 wxString pispec =
"*_pi.so";
726 if (!::wxDirExists(m_plugin_location)) {
727 msg = m_plugin_location;
728 msg.Prepend(
" Directory ");
729 msg.Append(
" does not exist.");
734 if (!g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
736 wxArrayString file_list;
738 int get_flags = wxDIR_FILES | wxDIR_DIRS;
741 get_flags = wxDIR_FILES;
746 get_flags = wxDIR_FILES;
752 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
754 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
755 for (
auto& file_name : file_list) {
756 wxLog::FlushActive();
758 LoadPluginCandidate(file_name, load_enabled);
765 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
767 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
772 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
774 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
781 while ((i >= 0) && (i < plugin_array.GetCount())) {
783 if (pict->m_status == PluginStatus::PendingListRemoval) {
784 plugin_array.RemoveAt(i);
796 for (
const auto& pic : plugin_array) {
799 if (pic->m_pplugin) {
802 pic->m_pplugin =
nullptr;
803 pic->m_init_state =
false;
808 if (!pic->m_pplugin) {
809 if (pic->m_enabled) {
813 pic->m_status = stat;
814 pic->m_enabled =
true;
820 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
821 wxString msg(
"PluginLoader: Initializing PlugIn: ");
828 pic->m_init_state =
true;
829 ProcessLateInit(pic);
835 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
836 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
838 }
else if (!pic->m_enabled && pic->m_init_state) {
841 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
842 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
845 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
846 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
847 pic->m_pplugin =
nullptr;
848 pic->m_init_state =
false;
856 if (!pic)
return false;
857 if (pic->m_init_state) {
858 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
860 m_on_deactivate_cb(pic);
861 pic->m_init_state =
false;
868 auto pic = GetContainer(pd, plugin_array);
870 wxLogError(
"Attempt to deactivate non-existing plugin %s",
878 if (ix >= plugin_array.GetCount()) {
879 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
886 if (pic->m_pplugin) {
887 pic->m_destroy_fn(pic->m_pplugin);
891 plugin_array.RemoveAt(ix);
895static std::string VersionFromManifest(
const std::string& plugin_name) {
898 if (!path.empty() && wxFileName::IsFileReadable(path)) {
899 std::ifstream stream;
900 stream.open(path, std::ifstream::in);
909 if (name.empty())
return {};
912 if (isRegularFile(import_path.c_str())) {
913 std::ifstream f(import_path.c_str());
914 std::stringstream ss;
917 ParsePlugin(ss.str(), pd);
918 pd.is_imported =
true;
923 vector<PluginMetadata> matches;
924 copy_if(available.begin(), available.end(), back_inserter(matches),
926 if (matches.size() == 0)
return {};
927 if (matches.size() == 1)
return matches[0];
929 auto version = VersionFromManifest(name);
931 return version == md.version;
933 auto found = find_if(matches.begin(), matches.end(), predicate);
934 return found != matches.end() ? *found : matches[0];
940 if (name.empty())
return {};
943 vector<PluginMetadata> matches;
944 copy_if(available.begin(), available.end(), back_inserter(matches),
946 if (matches.size() == 0)
return {};
947 if (matches.size() == 1)
return matches[0];
951 auto rv = matches[0];
952 for (
auto p : matches) {
954 if (catVersion > version) {
955 version = catVersion;
966 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
968 bool is_system = found != SYSTEM_PLUGINS.end();
970 std::string installed = VersionFromManifest(md.name);
977 else if (plugin->m_status == PluginStatus::Imported)
979 else if (installedVersion < metaVersion)
980 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
981 else if (installedVersion == metaVersion)
982 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
984 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
988 plugin->m_managed_metadata = md;
992 std::vector<PlugInContainer*> loaded_plugins;
993 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
996 for (
auto& p : loaded_plugins) {
997 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
998 p->m_common_name.Lower().ToStdString());
999 bool is_system = found != SYSTEM_PLUGINS.end();
1002 if (!keep_orphans) {
1009 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1013 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1014 loaded_plugins.erase(end, loaded_plugins.end());
1018 for (
auto& plugin : loaded_plugins) {
1021 if (!md.name.empty()) {
1023 md.is_imported = isRegularFile(import_path.c_str());
1024 if (md.is_imported) {
1025 plugin->m_status = PluginStatus::Imported;
1029 }
else if (IsSystemPluginName(md.name)) {
1031 }
else if (md.is_orphan) {
1033 }
else if (plugin->m_api_version) {
1036 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1037 plugin->m_managed_metadata = md;
1040 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1045 plugin_array.Clear();
1046 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1052 while (plugin_array.GetCount()) {
1061 for (
auto* pic : plugin_array) {
1069DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1071 PIMAGE_SECTION_HEADER pSeh;
1076 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1077 if (rva >= pSeh->VirtualAddress &&
1078 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1083 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1090 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1091 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1094 uint64_t type_magic;
1095 DependencyMap dependencies;
1099bool ReadModuleInfoFromELF(
const wxString& file,
1100 const ModuleInfo::DependencySet& dependencies,
1102 static bool b_libelf_initialized =
false;
1103 static bool b_libelf_usable =
false;
1105 if (b_libelf_usable) {
1107 }
else if (b_libelf_initialized) {
1109 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1110 b_libelf_initialized =
true;
1111 b_libelf_usable =
false;
1112 wxLogError(
"LibELF is outdated.");
1115 b_libelf_initialized =
true;
1116 b_libelf_usable =
true;
1120 Elf* elf_handle =
nullptr;
1121 GElf_Ehdr elf_file_header;
1122 Elf_Scn* elf_section_handle =
nullptr;
1124 file_handle = open(file, O_RDONLY);
1125 if (file_handle == -1) {
1126 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1128 goto FailureEpilogue;
1131 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1132 if (elf_handle ==
nullptr) {
1133 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1134 goto FailureEpilogue;
1137 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1138 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1139 goto FailureEpilogue;
1142 switch (elf_file_header.e_type) {
1147 wxLogMessage(wxString::Format(
1148 "Module \"%s\" is not an executable or shared library.", file));
1149 goto FailureEpilogue;
1153 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1155 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1157 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1159 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1161 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1165 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1167 GElf_Shdr elf_section_header;
1168 Elf_Data* elf_section_data =
nullptr;
1169 size_t elf_section_entry_count = 0;
1171 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1172 &elf_section_header) {
1173 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1174 goto FailureEpilogue;
1175 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1179 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1180 if (elf_section_data ==
nullptr) {
1181 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1182 goto FailureEpilogue;
1185 if ((elf_section_data->d_size == 0) ||
1186 (elf_section_header.sh_entsize == 0)) {
1187 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1188 goto FailureEpilogue;
1191 elf_section_entry_count =
1192 elf_section_data->d_size / elf_section_header.sh_entsize;
1193 for (
size_t elf_section_entry_index = 0;
1194 elf_section_entry_index < elf_section_entry_count;
1195 ++elf_section_entry_index) {
1196 GElf_Dyn elf_dynamic_entry;
1197 const char* elf_dynamic_entry_name =
nullptr;
1198 if (gelf_getdyn(elf_section_data,
1199 static_cast<int>(elf_section_entry_index),
1200 &elf_dynamic_entry) != &elf_dynamic_entry) {
1201 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1203 goto FailureEpilogue;
1204 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1207 elf_dynamic_entry_name = elf_strptr(
1208 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1209 if (elf_dynamic_entry_name ==
nullptr) {
1210 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1211 "string entry", file));
1212 goto FailureEpilogue;
1214 wxString name_full(elf_dynamic_entry_name);
1215 wxString name_part(elf_dynamic_entry_name,
1216 strcspn(elf_dynamic_entry_name,
"-."));
1217 if (dependencies.find(name_part) != dependencies.end()) {
1218 info.dependencies.insert(
1219 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1224 goto SuccessEpilogue;
1227 elf_end(elf_handle);
1232 if (elf_handle !=
nullptr) elf_end(elf_handle);
1233 if (file_handle >= 0) close(file_handle);
1234 wxLog::FlushActive();
1240 bool b_compat =
false;
1256 if (!m_found_wxwidgets) {
1257 DWORD myPid = GetCurrentProcessId();
1259 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1260 if (hProcess == NULL) {
1261 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1262 plugin_file.c_str()));
1266 HMODULE hMods[1024];
1268 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1269 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1270 TCHAR szModName[MAX_PATH];
1271 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1272 sizeof(szModName) /
sizeof(TCHAR))) {
1273 m_module_name = szModName;
1274 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1275 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1276 m_found_wxwidgets =
true;
1277 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1278 m_module_name.c_str()));
1285 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1286 plugin_file.c_str()));
1288 if (hProcess) CloseHandle(hProcess);
1291 if (!m_found_wxwidgets) {
1292 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1293 plugin_file.c_str()));
1295 LPCWSTR fName = plugin_file.wc_str();
1296 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1297 FILE_ATTRIBUTE_NORMAL, 0);
1298 DWORD byteread, size = GetFileSize(handle, NULL);
1299 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1300 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1301 CloseHandle(handle);
1302 PIMAGE_NT_HEADERS ntheaders =
1303 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1304 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1305 PIMAGE_SECTION_HEADER pSech =
1306 IMAGE_FIRST_SECTION(ntheaders);
1307 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1308 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1313 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1315 ntheaders->OptionalHeader
1317 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1324 while (pImportDescriptor->Name != 0) {
1327 (PCHAR)((DWORD_PTR)virtualpointer +
1328 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1331 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1334 wxLogMessage(wxString::Format(
1335 "Compatible wxWidgets plugin library found for %s: %s",
1336 plugin_file.c_str(), libname[i]));
1339 pImportDescriptor++;
1344 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1346 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1349#if defined(__WXGTK__) || defined(__WXQT__)
1350#if defined(USE_LIBELF)
1352 static bool b_own_info_queried =
false;
1353 static bool b_own_info_usable =
false;
1355 static ModuleInfo::DependencySet dependencies;
1357 if (!b_own_info_queried) {
1358 dependencies.insert(
"libwx_baseu");
1360 char exe_buf[100] = {0};
1361 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1363 exe_buf[len] =
'\0';
1364 wxString app_path(exe_buf);
1365 wxLogMessage(
"Executable path: %s", exe_buf);
1367 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1368 if (!b_own_info_usable) {
1369 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1372 wxLogMessage(
"Cannot get own executable path.");
1374 b_own_info_queried =
true;
1377 if (b_own_info_usable) {
1378 bool b_pi_info_usable =
false;
1381 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1382 if (b_pi_info_usable) {
1383 b_compat = (pi_info.type_magic == own_info.type_magic);
1386 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1391 pi_info.dependencies.clear();
1393 wxString::Format(
" Plugin \"%s\" is of another binary "
1394 "flavor than the main module.",
1396 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1397 own_info.type_magic, pi_info.type_magic);
1399 for (
const auto& own_dependency : own_info.dependencies) {
1400 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1401 pi_info.dependencies.find(own_dependency.first);
1402 if ((pi_dependency != pi_info.dependencies.end()) &&
1403 (pi_dependency->second != own_dependency.second)) {
1406 " Plugin \"%s\" depends on library \"%s\", but the main "
1407 "module was built for \"%s\".",
1408 plugin_file, pi_dependency->second, own_dependency.second);
1415 wxString::Format(
" Plugin \"%s\" could not be reliably "
1416 "checked for compatibility.",
1424 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1425 b_compat ?
"true" :
"false");
1427 wxLog::FlushActive();
1442 FILE* f = fopen(plugin_file,
"r");
1445 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1449#
if defined(__WXGTK3__)
1450 "libwx_gtk3u_core-%i.%i"
1451#elif defined(__WXGTK20__)
1452 "libwx_gtk2u_core-%i.%i"
1453#elif defined(__WXQT__)
1454 "libwx_qtu_core-%i.%i"
1456#error undefined plugin platform
1459 wxMAJOR_VERSION, wxMINOR_VERSION);
1463 size_t len(strlen(strver));
1465 while ((c = fgetc(f)) != EOF) {
1466 if (c == strver[pos]) {
1477 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1493 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1495 if (plugin_file.empty()) {
1496 wxLogMessage(
"Ignoring loading of empty path");
1500 if (!wxIsReadable(plugin_file)) {
1501 wxLogMessage(
"Ignoring unreadable plugin %s",
1502 plugin_file.ToStdString().c_str());
1503 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1504 load_errors.push_back(le);
1511 pic->m_version_major, pic->m_version_minor);
1512 if (sts != plug_status::unblocked) {
1513 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1517 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1518 if (!data.name.empty()) {
1519 wxLogDebug(
"Refusing to load blacklisted library: %s",
1520 plugin_file.ToStdString().c_str());
1528 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1529 pic->m_library.Load(plugin_file);
1531 if (!pic->m_library.IsLoaded()) {
1534 wxFileName fn(plugin_file);
1535 std::string name = fn.GetName().ToStdString();
1536 auto found = m_blacklist->get_library_data(name);
1537 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1538 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1539 if (!found.name.empty()) {
1541 LoadError le(LoadError::Type::Unloadable, name, v);
1542 load_errors.push_back(le);
1544 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1545 load_errors.push_back(le);
1548 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1554 const char*
const FIX_LOADING =
1555 _(
"\n Install/uninstall plugin or remove file to mute message");
1556 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1557 if (
nullptr == create_plugin) {
1558 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1559 wxLogMessage(msg + plugin_file);
1560 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1561 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1562 load_errors.push_back(le);
1567 destroy_t* destroy_plugin =
1568 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1569 pic->m_destroy_fn = destroy_plugin;
1570 if (
nullptr == destroy_plugin) {
1571 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1573 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1574 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1575 load_errors.push_back(le);
1585 int api_ver = (api_major * 100) + api_minor;
1586 pic->m_api_version = api_ver;
1594 wxLogDebug(
"blacklist: Get status for %s %d %d",
1595 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1597 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1598 if (status != plug_status::unblocked) {
1599 wxLogDebug(
"Ignoring blacklisted plugin.");
1600 if (status != plug_status::unloadable) {
1602 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1603 load_errors.push_back(le);
1686 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1687 p->GetPlugInVersionBuild());
1690 if (!pic->m_pplugin) {
1691 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1692 INFO_LOG << _(
" API Version detected: ");
1693 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1694 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1695 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1697 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1699 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.
Error condition when loading a plugin.
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.
Handle plugin install from remote repositories and local operations to Uninstall and list plugins.
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 VersionPath(std::string name)
Return path to file containing version for given plugin.
static std::string FileListPath(std::string name)
Return path to installation manifest for given plugin.
static PluginHandler * GetInstance()
Singleton factory.
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.
bool IsPlugInAvailable(const wxString &commonName)
Return true if a plugin with given name exists in GetPlugInArray()
static void MarkAsLoadable(const std::string &library_path)
Mark a library file (complete path) as loadable i.
static PluginMetadata LatestMetadataByName(const std::string &name)
Find highest versioned metadata for given plugin.
EventVar evt_load_plugin
Notified with a PlugInContainer* pointer when a plugin is loaded.
void UpdateManagedPlugins(bool keep_orphans)
Update all managed plugins i.
EventVar evt_pluglist_change
Notified without data when the GetPlugInArray() list is changed.
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.
EventVar evt_load_directory
Notified without data when loader starts loading from a new directory.
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.
bool DeactivatePlugIn(PlugInContainer *pic)
Deactivate given plugin.
bool UnLoadAllPlugIns()
Unload allplugins i.
void RemovePlugin(const PlugInData &pd)
Remove a plugin from *GetPluginArray().
bool UpdatePlugIns()
Update the GetPlugInArray() list by reloading all plugins from disk.
void ShowPreferencesDialog(const PlugInData &pd, wxWindow *parent)
Display the preferences dialog for a plugin.
bool CheckPluginCompatibility(const wxString &plugin_file)
Check plugin compatibiliet w r t library type.
const ArrayOfPlugIns * GetPlugInArray()
Return list of currently loaded plugins.
bool DeactivateAllPlugIns()
Deactivate all plugins.
PlugInContainer * LoadPlugIn(const wxString &plugin_file)
Load given plugin file from disk into GetPlugInArray() list.
EventVar evt_update_chart_types
Notified without data after all plugins loaded ot updated.
std::vector< std::string > Libdirs()
List of directories from which we load plugins.
static PluginPaths * GetInstance()
Return the singleton instance.
std::vector< std::string > Bindirs()
'List of directories for plugin binary helpers.
virtual void OnSetupOptions(void)
Allows plugin to add pages to global Options dialog.
Base class for OpenCPN plugins.
virtual void ShowPreferencesDialog(wxWindow *parent)
Shows the plugin preferences dialog.
virtual wxBitmap * GetPlugInBitmap()
Get the plugin's icon bitmap.
virtual int Init(void)
Initialize the plugin and declare its capabilities.
virtual bool DeInit(void)
Clean up plugin resources.
virtual void SetDefaults(void)
Sets plugin default options.
virtual wxString GetShortDescription()
Get a brief description of the plugin.
virtual wxString GetCommonName()
Get the plugin's common (short) name.
virtual int GetPlugInVersionMajor()
Returns the major version number of the plugin itself.
virtual int GetAPIVersionMinor()
Returns the minor version number of the plugin API that this plugin supports.
virtual int GetAPIVersionMajor()
Returns the major version number of the plugin API that this plugin supports.
virtual wxString GetLongDescription()
Get detailed plugin information.
virtual int GetPlugInVersionMinor()
Returns the minor version number of the plugin itself.
Global variables reflecting command line options and arguments.
Enhanced logging interface on top of wx/log.h.
std::string tolower(const std::string &input)
Return copy of s with all characters converted to lower case.
bool exists(const std::string &name)
std::string join(std::vector< std::string > v, char c)
Return a single string being the concatenation of all elements in v with character c in between.
void mkdir(const std::string path)
#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.
Miscellaneous utilities, many of which string related.
PLugin remote repositories installation and Uninstall/list operations.
Low level code to load plugins from disk, notably the PluginLoader class.
@ Unmanaged
Unmanaged, probably a package.
@ Managed
Managed by installer.
@ System
One of the four system plugins, unmanaged.
Plugin installation and data paths support.
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.