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"
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 (!IsSystemPluginPath(plugin_file.ToStdString())) {
501 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
502 MESSAGE_LOG <<
"Refusing to load " << file_name
503 <<
" failed at last attempt";
506 CreateLoadStamp(plugin_loadstamp.ToStdString());
508 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
509 wxLog::FlushActive();
512 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
513 file_name.ToStdString().c_str());
522 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
531 plugin_array.Remove(pic_test);
535 pic_test->m_destroy_fn(pic_test->m_pplugin);
540 loaded_pic = pic_test;
545 loaded_pic = pic_test;
552 ClearLoadStamp(plugin_loadstamp.ToStdString());
557 wxFileName fn_plugin_file(file_name);
558 wxString plugin_file_path =
559 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
560 wxString base_plugin_path = g_BasePlatform->
GetPluginDir();
561 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
562 base_plugin_path += wxFileName::GetPathSeparator();
565 if (base_plugin_path.IsSameAs(plugin_file_path)) {
566 if (!IsSystemPluginPath(file_name.ToStdString())) {
567 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
570 ClearLoadStamp(plugin_loadstamp.ToStdString());
576 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
577 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
578 ClearLoadStamp(plugin_loadstamp.ToStdString());
583 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
584 wxLogMessage(msg.c_str());
585 wxLog::FlushActive();
591 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
592 wxLogMessage(msg.c_str());
593 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
594 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
595 load_errors.push_back(le);
605 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
607 if (pic && load_enabled && !enabled.Get(
true)) {
608 pic->m_destroy_fn(pic->m_pplugin);
610 wxLogMessage(
"Skipping not enabled candidate.");
611 ClearLoadStamp(plugin_loadstamp.ToStdString());
616 if (pic->m_pplugin) {
617 plugin_array.Add(pic);
624 pic->m_enabled = enabled.Get(
false);
626 if (safe_mode::get_mode()) {
627 pic->m_enabled =
false;
630 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
632 if (pic->m_enabled) {
634 pic->m_init_state =
true;
638 wxLog::FlushActive();
640 std::string found_version;
643 found_version = p.readonly ?
"" : p.version;
655 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
657 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
658 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
660 if (!pic->m_enabled && pic->m_destroy_fn) {
661 pic->m_destroy_fn(pic->m_pplugin);
662 pic->m_destroy_fn =
nullptr;
663 pic->m_pplugin =
nullptr;
664 pic->m_init_state =
false;
665 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
670 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
672 bool is_system = found != SYSTEM_PLUGINS.end();
678 available.begin(), available.end(),
681 if (it == available.end()) {
686 auto oprhan_metadata = CreateMetadata(pic);
687 auto catalogHdlr = CatalogHandler::GetInstance();
688 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
694 " PluginLoader: Unloading invalid PlugIn, API version %d ",
696 pic->m_destroy_fn(pic->m_pplugin);
698 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
700 load_errors.push_back(le);
706 ClearLoadStamp(plugin_loadstamp.ToStdString());
711bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
714 m_plugin_location = plugin_dir;
716 wxString msg(
"PluginLoader searching for PlugIns in location ");
717 msg += m_plugin_location;
721 wxString pispec =
"*_pi.dll";
722#elif defined(__WXOSX__)
723 wxString pispec =
"*_pi.dylib";
725 wxString pispec =
"*_pi.so";
728 if (!::wxDirExists(m_plugin_location)) {
729 msg = m_plugin_location;
730 msg.Prepend(
" Directory ");
731 msg.Append(
" does not exist.");
736 if (!g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
738 wxArrayString file_list;
740 int get_flags = wxDIR_FILES | wxDIR_DIRS;
743 get_flags = wxDIR_FILES;
748 get_flags = wxDIR_FILES;
754 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
756 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
757 for (
auto& file_name : file_list) {
758 wxLog::FlushActive();
760 LoadPluginCandidate(file_name, load_enabled);
767 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
769 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
774 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
776 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
783 while ((i >= 0) && (i < plugin_array.GetCount())) {
785 if (pict->m_status == PluginStatus::PendingListRemoval) {
786 plugin_array.RemoveAt(i);
798 for (
const auto& pic : plugin_array) {
801 if (pic->m_pplugin) {
804 pic->m_pplugin =
nullptr;
805 pic->m_init_state =
false;
810 if (!pic->m_pplugin) {
811 if (pic->m_enabled) {
815 pic->m_status = stat;
816 pic->m_enabled =
true;
822 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
823 wxString msg(
"PluginLoader: Initializing PlugIn: ");
830 pic->m_init_state =
true;
831 ProcessLateInit(pic);
837 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
838 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
840 }
else if (!pic->m_enabled && pic->m_init_state) {
843 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
844 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
847 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
848 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
849 pic->m_pplugin =
nullptr;
850 pic->m_init_state =
false;
858 if (!pic)
return false;
859 if (pic->m_init_state) {
860 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
862 m_on_deactivate_cb(pic);
863 pic->m_init_state =
false;
870 auto pic = GetContainer(pd, plugin_array);
872 wxLogError(
"Attempt to deactivate non-existing plugin %s",
880 if (ix >= plugin_array.GetCount()) {
881 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
888 if (pic->m_pplugin) {
889 pic->m_destroy_fn(pic->m_pplugin);
893 plugin_array.RemoveAt(ix);
897static std::string VersionFromManifest(
const std::string& plugin_name) {
900 if (!path.empty() && wxFileName::IsFileReadable(path)) {
901 std::ifstream stream;
902 stream.open(path, std::ifstream::in);
911 if (name.empty())
return {};
914 if (isRegularFile(import_path.c_str())) {
915 std::ifstream f(import_path.c_str());
916 std::stringstream ss;
919 ParsePlugin(ss.str(), pd);
920 pd.is_imported =
true;
925 vector<PluginMetadata> matches;
926 copy_if(available.begin(), available.end(), back_inserter(matches),
928 if (matches.size() == 0)
return {};
929 if (matches.size() == 1)
return matches[0];
931 auto version = VersionFromManifest(name);
933 return version == md.version;
935 auto found = find_if(matches.begin(), matches.end(), predicate);
936 return found != matches.end() ? *found : matches[0];
942 if (name.empty())
return {};
945 vector<PluginMetadata> matches;
946 copy_if(available.begin(), available.end(), back_inserter(matches),
948 if (matches.size() == 0)
return {};
949 if (matches.size() == 1)
return matches[0];
953 auto rv = matches[0];
954 for (
auto p : matches) {
956 if (catVersion > version) {
957 version = catVersion;
968 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
970 bool is_system = found != SYSTEM_PLUGINS.end();
972 std::string installed = VersionFromManifest(md.name);
979 else if (plugin->m_status == PluginStatus::Imported)
981 else if (installedVersion < metaVersion)
982 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
983 else if (installedVersion == metaVersion)
984 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
986 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
990 plugin->m_managed_metadata = md;
994 std::vector<PlugInContainer*> loaded_plugins;
995 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
998 for (
auto& p : loaded_plugins) {
999 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1000 p->m_common_name.Lower().ToStdString());
1001 bool is_system = found != SYSTEM_PLUGINS.end();
1004 if (!keep_orphans) {
1011 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1015 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1016 loaded_plugins.erase(end, loaded_plugins.end());
1020 for (
auto& plugin : loaded_plugins) {
1023 if (!md.name.empty()) {
1025 md.is_imported = isRegularFile(import_path.c_str());
1026 if (md.is_imported) {
1027 plugin->m_status = PluginStatus::Imported;
1031 }
else if (IsSystemPluginName(md.name)) {
1033 }
else if (md.is_orphan) {
1035 }
else if (plugin->m_api_version) {
1038 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1039 plugin->m_managed_metadata = md;
1042 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1047 plugin_array.Clear();
1048 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1054 while (plugin_array.GetCount()) {
1063 for (
auto* pic : plugin_array) {
1071DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1073 PIMAGE_SECTION_HEADER pSeh;
1078 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1079 if (rva >= pSeh->VirtualAddress &&
1080 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1085 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1092 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1093 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1096 uint64_t type_magic;
1097 DependencyMap dependencies;
1101bool ReadModuleInfoFromELF(
const wxString& file,
1102 const ModuleInfo::DependencySet& dependencies,
1104 static bool b_libelf_initialized =
false;
1105 static bool b_libelf_usable =
false;
1107 if (b_libelf_usable) {
1109 }
else if (b_libelf_initialized) {
1111 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1112 b_libelf_initialized =
true;
1113 b_libelf_usable =
false;
1114 wxLogError(
"LibELF is outdated.");
1117 b_libelf_initialized =
true;
1118 b_libelf_usable =
true;
1122 Elf* elf_handle =
nullptr;
1123 GElf_Ehdr elf_file_header;
1124 Elf_Scn* elf_section_handle =
nullptr;
1126 file_handle = open(file, O_RDONLY);
1127 if (file_handle == -1) {
1128 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1130 goto FailureEpilogue;
1133 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1134 if (elf_handle ==
nullptr) {
1135 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1136 goto FailureEpilogue;
1139 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1140 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1141 goto FailureEpilogue;
1144 switch (elf_file_header.e_type) {
1149 wxLogMessage(wxString::Format(
1150 "Module \"%s\" is not an executable or shared library.", file));
1151 goto FailureEpilogue;
1155 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1157 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1159 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1161 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1163 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1167 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1169 GElf_Shdr elf_section_header;
1170 Elf_Data* elf_section_data =
nullptr;
1171 size_t elf_section_entry_count = 0;
1173 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1174 &elf_section_header) {
1175 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1176 goto FailureEpilogue;
1177 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1181 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1182 if (elf_section_data ==
nullptr) {
1183 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1184 goto FailureEpilogue;
1187 if ((elf_section_data->d_size == 0) ||
1188 (elf_section_header.sh_entsize == 0)) {
1189 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1190 goto FailureEpilogue;
1193 elf_section_entry_count =
1194 elf_section_data->d_size / elf_section_header.sh_entsize;
1195 for (
size_t elf_section_entry_index = 0;
1196 elf_section_entry_index < elf_section_entry_count;
1197 ++elf_section_entry_index) {
1198 GElf_Dyn elf_dynamic_entry;
1199 const char* elf_dynamic_entry_name =
nullptr;
1200 if (gelf_getdyn(elf_section_data,
1201 static_cast<int>(elf_section_entry_index),
1202 &elf_dynamic_entry) != &elf_dynamic_entry) {
1203 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1205 goto FailureEpilogue;
1206 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1209 elf_dynamic_entry_name = elf_strptr(
1210 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1211 if (elf_dynamic_entry_name ==
nullptr) {
1212 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1213 "string entry", file));
1214 goto FailureEpilogue;
1216 wxString name_full(elf_dynamic_entry_name);
1217 wxString name_part(elf_dynamic_entry_name,
1218 strcspn(elf_dynamic_entry_name,
"-."));
1219 if (dependencies.find(name_part) != dependencies.end()) {
1220 info.dependencies.insert(
1221 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1226 goto SuccessEpilogue;
1229 elf_end(elf_handle);
1234 if (elf_handle !=
nullptr) elf_end(elf_handle);
1235 if (file_handle >= 0) close(file_handle);
1236 wxLog::FlushActive();
1242 bool b_compat =
false;
1258 if (!m_found_wxwidgets) {
1259 DWORD myPid = GetCurrentProcessId();
1261 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1262 if (hProcess == NULL) {
1263 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1264 plugin_file.c_str()));
1268 HMODULE hMods[1024];
1270 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1271 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1272 TCHAR szModName[MAX_PATH];
1273 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1274 sizeof(szModName) /
sizeof(TCHAR))) {
1275 m_module_name = szModName;
1276 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1277 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1278 m_found_wxwidgets =
true;
1279 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1280 m_module_name.c_str()));
1287 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1288 plugin_file.c_str()));
1290 if (hProcess) CloseHandle(hProcess);
1293 if (!m_found_wxwidgets) {
1294 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1295 plugin_file.c_str()));
1297 LPCWSTR fName = plugin_file.wc_str();
1298 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1299 FILE_ATTRIBUTE_NORMAL, 0);
1300 DWORD byteread, size = GetFileSize(handle, NULL);
1301 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1302 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1303 CloseHandle(handle);
1304 PIMAGE_NT_HEADERS ntheaders =
1305 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1306 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1307 PIMAGE_SECTION_HEADER pSech =
1308 IMAGE_FIRST_SECTION(ntheaders);
1309 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1310 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1315 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1317 ntheaders->OptionalHeader
1319 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1326 while (pImportDescriptor->Name != 0) {
1329 (PCHAR)((DWORD_PTR)virtualpointer +
1330 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1333 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1336 wxLogMessage(wxString::Format(
1337 "Compatible wxWidgets plugin library found for %s: %s",
1338 plugin_file.c_str(), libname[i]));
1341 pImportDescriptor++;
1346 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1348 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1351#if defined(__WXGTK__) || defined(__WXQT__)
1352#if defined(USE_LIBELF)
1354 static bool b_own_info_queried =
false;
1355 static bool b_own_info_usable =
false;
1357 static ModuleInfo::DependencySet dependencies;
1359 if (!b_own_info_queried) {
1360 dependencies.insert(
"libwx_baseu");
1362 char exe_buf[100] = {0};
1363 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1365 exe_buf[len] =
'\0';
1366 wxString app_path(exe_buf);
1367 wxLogMessage(
"Executable path: %s", exe_buf);
1369 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1370 if (!b_own_info_usable) {
1371 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1374 wxLogMessage(
"Cannot get own executable path.");
1376 b_own_info_queried =
true;
1379 if (b_own_info_usable) {
1380 bool b_pi_info_usable =
false;
1383 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1384 if (b_pi_info_usable) {
1385 b_compat = (pi_info.type_magic == own_info.type_magic);
1388 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1393 pi_info.dependencies.clear();
1395 wxString::Format(
" Plugin \"%s\" is of another binary "
1396 "flavor than the main module.",
1398 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1399 own_info.type_magic, pi_info.type_magic);
1401 for (
const auto& own_dependency : own_info.dependencies) {
1402 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1403 pi_info.dependencies.find(own_dependency.first);
1404 if ((pi_dependency != pi_info.dependencies.end()) &&
1405 (pi_dependency->second != own_dependency.second)) {
1408 " Plugin \"%s\" depends on library \"%s\", but the main "
1409 "module was built for \"%s\".",
1410 plugin_file, pi_dependency->second, own_dependency.second);
1417 wxString::Format(
" Plugin \"%s\" could not be reliably "
1418 "checked for compatibility.",
1426 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1427 b_compat ?
"true" :
"false");
1429 wxLog::FlushActive();
1444 FILE* f = fopen(plugin_file,
"r");
1447 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1451#
if defined(__WXGTK3__)
1452 "libwx_gtk3u_core-%i.%i"
1453#elif defined(__WXGTK20__)
1454 "libwx_gtk2u_core-%i.%i"
1455#elif defined(__WXQT__)
1456 "libwx_qtu_core-%i.%i"
1458#error undefined plugin platform
1461 wxMAJOR_VERSION, wxMINOR_VERSION);
1465 size_t len(strlen(strver));
1467 while ((c = fgetc(f)) != EOF) {
1468 if (c == strver[pos]) {
1479 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1495 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1497 if (plugin_file.empty()) {
1498 wxLogMessage(
"Ignoring loading of empty path");
1502 if (!wxIsReadable(plugin_file)) {
1503 wxLogMessage(
"Ignoring unreadable plugin %s",
1504 plugin_file.ToStdString().c_str());
1505 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1506 load_errors.push_back(le);
1513 pic->m_version_major, pic->m_version_minor);
1514 if (sts != plug_status::unblocked) {
1515 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1519 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1520 if (!data.name.empty()) {
1521 wxLogDebug(
"Refusing to load blacklisted library: %s",
1522 plugin_file.ToStdString().c_str());
1530 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1531 pic->m_library.Load(plugin_file);
1533 if (!pic->m_library.IsLoaded()) {
1536 wxFileName fn(plugin_file);
1537 std::string name = fn.GetName().ToStdString();
1538 auto found = m_blacklist->get_library_data(name);
1539 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1540 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1541 if (!found.name.empty()) {
1543 LoadError le(LoadError::Type::Unloadable, name, v);
1544 load_errors.push_back(le);
1546 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1547 load_errors.push_back(le);
1550 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1556 const char*
const FIX_LOADING =
1557 _(
"\n Install/uninstall plugin or remove file to mute message");
1558 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1559 if (
nullptr == create_plugin) {
1560 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1561 wxLogMessage(msg + plugin_file);
1562 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1563 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1564 load_errors.push_back(le);
1569 destroy_t* destroy_plugin =
1570 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1571 pic->m_destroy_fn = destroy_plugin;
1572 if (
nullptr == destroy_plugin) {
1573 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1575 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1576 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1577 load_errors.push_back(le);
1587 int api_ver = (api_major * 100) + api_minor;
1588 pic->m_api_version = api_ver;
1596 wxLogDebug(
"blacklist: Get status for %s %d %d",
1597 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1599 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1600 if (status != plug_status::unblocked) {
1601 wxLogDebug(
"Ignoring blacklisted plugin.");
1602 if (status != plug_status::unloadable) {
1604 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1605 load_errors.push_back(le);
1688 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1689 p->GetPlugInVersionBuild());
1692 if (!pic->m_pplugin) {
1693 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1694 INFO_LOG << _(
" API Version detected: ");
1695 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1696 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1697 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1699 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1701 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.
void Notify() override
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)
Notify()/Listen() configuration variable wrapper.
#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.
std::vector< const PlugInData * > GetInstalled()
Return sorted list of all installed plugins.
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.