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 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
942 bool is_system = found != SYSTEM_PLUGINS.end();
944 std::string installed = VersionFromManifest(md.name);
951 else if (plugin->m_status == PluginStatus::Imported)
953 else if (installedVersion < metaVersion)
954 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
955 else if (installedVersion == metaVersion)
956 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
958 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
962 plugin->m_managed_metadata = md;
966 std::vector<PlugInContainer*> loaded_plugins;
967 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
970 for (
auto& p : loaded_plugins) {
971 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
972 p->m_common_name.Lower().ToStdString());
973 bool is_system = found != SYSTEM_PLUGINS.end();
983 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
987 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
988 loaded_plugins.erase(end, loaded_plugins.end());
992 for (
auto& plugin : loaded_plugins) {
994 if (!md.name.empty()) {
996 md.is_imported = isRegularFile(import_path.c_str());
997 if (md.is_imported) {
998 plugin->m_status = PluginStatus::Imported;
1002 }
else if (IsSystemPluginName(md.name)) {
1004 }
else if (md.is_orphan) {
1006 }
else if (plugin->m_api_version) {
1009 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1010 plugin->m_managed_metadata = md;
1013 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1018 plugin_array.Clear();
1019 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1025 while (plugin_array.GetCount()) {
1034 for (
auto* pic : plugin_array) {
1042DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1044 PIMAGE_SECTION_HEADER pSeh;
1049 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1050 if (rva >= pSeh->VirtualAddress &&
1051 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1056 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1063 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1064 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1067 uint64_t type_magic;
1068 DependencyMap dependencies;
1072bool ReadModuleInfoFromELF(
const wxString& file,
1073 const ModuleInfo::DependencySet& dependencies,
1075 static bool b_libelf_initialized =
false;
1076 static bool b_libelf_usable =
false;
1078 if (b_libelf_usable) {
1080 }
else if (b_libelf_initialized) {
1082 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1083 b_libelf_initialized =
true;
1084 b_libelf_usable =
false;
1085 wxLogError(
"LibELF is outdated.");
1088 b_libelf_initialized =
true;
1089 b_libelf_usable =
true;
1093 Elf* elf_handle =
nullptr;
1094 GElf_Ehdr elf_file_header;
1095 Elf_Scn* elf_section_handle =
nullptr;
1097 file_handle = open(file, O_RDONLY);
1098 if (file_handle == -1) {
1099 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1101 goto FailureEpilogue;
1104 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1105 if (elf_handle ==
nullptr) {
1106 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1107 goto FailureEpilogue;
1110 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1111 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1112 goto FailureEpilogue;
1115 switch (elf_file_header.e_type) {
1120 wxLogMessage(wxString::Format(
1121 "Module \"%s\" is not an executable or shared library.", file));
1122 goto FailureEpilogue;
1126 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1128 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1130 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1132 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1134 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1138 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1140 GElf_Shdr elf_section_header;
1141 Elf_Data* elf_section_data =
nullptr;
1142 size_t elf_section_entry_count = 0;
1144 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1145 &elf_section_header) {
1146 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1147 goto FailureEpilogue;
1148 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1152 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1153 if (elf_section_data ==
nullptr) {
1154 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1155 goto FailureEpilogue;
1158 if ((elf_section_data->d_size == 0) ||
1159 (elf_section_header.sh_entsize == 0)) {
1160 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1161 goto FailureEpilogue;
1164 elf_section_entry_count =
1165 elf_section_data->d_size / elf_section_header.sh_entsize;
1166 for (
size_t elf_section_entry_index = 0;
1167 elf_section_entry_index < elf_section_entry_count;
1168 ++elf_section_entry_index) {
1169 GElf_Dyn elf_dynamic_entry;
1170 const char* elf_dynamic_entry_name =
nullptr;
1171 if (gelf_getdyn(elf_section_data,
1172 static_cast<int>(elf_section_entry_index),
1173 &elf_dynamic_entry) != &elf_dynamic_entry) {
1174 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1176 goto FailureEpilogue;
1177 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1180 elf_dynamic_entry_name = elf_strptr(
1181 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1182 if (elf_dynamic_entry_name ==
nullptr) {
1183 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1184 "string entry", file));
1185 goto FailureEpilogue;
1187 wxString name_full(elf_dynamic_entry_name);
1188 wxString name_part(elf_dynamic_entry_name,
1189 strcspn(elf_dynamic_entry_name,
"-."));
1190 if (dependencies.find(name_part) != dependencies.end()) {
1191 info.dependencies.insert(
1192 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1197 goto SuccessEpilogue;
1200 elf_end(elf_handle);
1205 if (elf_handle !=
nullptr) elf_end(elf_handle);
1206 if (file_handle >= 0) close(file_handle);
1207 wxLog::FlushActive();
1213 bool b_compat =
false;
1229 if (!m_found_wxwidgets) {
1230 DWORD myPid = GetCurrentProcessId();
1232 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1233 if (hProcess == NULL) {
1234 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1235 plugin_file.c_str()));
1239 HMODULE hMods[1024];
1241 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1242 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1243 TCHAR szModName[MAX_PATH];
1244 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1245 sizeof(szModName) /
sizeof(TCHAR))) {
1246 m_module_name = szModName;
1247 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1248 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1249 m_found_wxwidgets =
true;
1250 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1251 m_module_name.c_str()));
1258 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1259 plugin_file.c_str()));
1261 if (hProcess) CloseHandle(hProcess);
1264 if (!m_found_wxwidgets) {
1265 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1266 plugin_file.c_str()));
1268 LPCWSTR fName = plugin_file.wc_str();
1269 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1270 FILE_ATTRIBUTE_NORMAL, 0);
1271 DWORD byteread, size = GetFileSize(handle, NULL);
1272 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1273 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1274 CloseHandle(handle);
1275 PIMAGE_NT_HEADERS ntheaders =
1276 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1277 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1278 PIMAGE_SECTION_HEADER pSech =
1279 IMAGE_FIRST_SECTION(ntheaders);
1280 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1281 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1286 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1288 ntheaders->OptionalHeader
1290 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1297 while (pImportDescriptor->Name != 0) {
1300 (PCHAR)((DWORD_PTR)virtualpointer +
1301 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1304 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1307 wxLogMessage(wxString::Format(
1308 "Compatible wxWidgets plugin library found for %s: %s",
1309 plugin_file.c_str(), libname[i]));
1312 pImportDescriptor++;
1317 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1319 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1322#if defined(__WXGTK__) || defined(__WXQT__)
1323#if defined(USE_LIBELF)
1325 static bool b_own_info_queried =
false;
1326 static bool b_own_info_usable =
false;
1328 static ModuleInfo::DependencySet dependencies;
1330 if (!b_own_info_queried) {
1331 dependencies.insert(
"libwx_baseu");
1333 char exe_buf[100] = {0};
1334 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1336 exe_buf[len] =
'\0';
1337 wxString app_path(exe_buf);
1338 wxLogMessage(
"Executable path: %s", exe_buf);
1340 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1341 if (!b_own_info_usable) {
1342 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1345 wxLogMessage(
"Cannot get own executable path.");
1347 b_own_info_queried =
true;
1350 if (b_own_info_usable) {
1351 bool b_pi_info_usable =
false;
1354 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1355 if (b_pi_info_usable) {
1356 b_compat = (pi_info.type_magic == own_info.type_magic);
1359 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1364 pi_info.dependencies.clear();
1366 wxString::Format(
" Plugin \"%s\" is of another binary "
1367 "flavor than the main module.",
1369 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1370 own_info.type_magic, pi_info.type_magic);
1372 for (
const auto& own_dependency : own_info.dependencies) {
1373 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1374 pi_info.dependencies.find(own_dependency.first);
1375 if ((pi_dependency != pi_info.dependencies.end()) &&
1376 (pi_dependency->second != own_dependency.second)) {
1379 " Plugin \"%s\" depends on library \"%s\", but the main "
1380 "module was built for \"%s\".",
1381 plugin_file, pi_dependency->second, own_dependency.second);
1388 wxString::Format(
" Plugin \"%s\" could not be reliably "
1389 "checked for compatibility.",
1397 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1398 b_compat ?
"true" :
"false");
1400 wxLog::FlushActive();
1415 FILE* f = fopen(plugin_file,
"r");
1418 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1422#
if defined(__WXGTK3__)
1423 "libwx_gtk3u_core-%i.%i"
1424#elif defined(__WXGTK20__)
1425 "libwx_gtk2u_core-%i.%i"
1426#elif defined(__WXQT__)
1427 "libwx_qtu_core-%i.%i"
1429#error undefined plugin platform
1432 wxMAJOR_VERSION, wxMINOR_VERSION);
1436 size_t len(strlen(strver));
1438 while ((c = fgetc(f)) != EOF) {
1439 if (c == strver[pos]) {
1450 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1466 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1468 if (plugin_file.empty()) {
1469 wxLogMessage(
"Ignoring loading of empty path");
1473 if (!wxIsReadable(plugin_file)) {
1474 wxLogMessage(
"Ignoring unreadable plugin %s",
1475 plugin_file.ToStdString().c_str());
1476 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1477 load_errors.push_back(le);
1484 pic->m_version_major, pic->m_version_minor);
1485 if (sts != plug_status::unblocked) {
1486 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1490 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1491 if (!data.name.empty()) {
1492 wxLogDebug(
"Refusing to load blacklisted library: %s",
1493 plugin_file.ToStdString().c_str());
1501 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1502 pic->m_library.Load(plugin_file);
1504 if (!pic->m_library.IsLoaded()) {
1507 wxFileName fn(plugin_file);
1508 std::string name = fn.GetName().ToStdString();
1509 auto found = m_blacklist->get_library_data(name);
1510 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1511 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1512 if (!found.name.empty()) {
1514 LoadError le(LoadError::Type::Unloadable, name, v);
1515 load_errors.push_back(le);
1517 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1518 load_errors.push_back(le);
1521 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1527 const char*
const FIX_LOADING =
1528 _(
"\n Install/uninstall plugin or remove file to mute message");
1529 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1530 if (
nullptr == create_plugin) {
1531 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1532 wxLogMessage(msg + plugin_file);
1533 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1534 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1535 load_errors.push_back(le);
1540 destroy_t* destroy_plugin =
1541 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1542 pic->m_destroy_fn = destroy_plugin;
1543 if (
nullptr == destroy_plugin) {
1544 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1546 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1547 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1548 load_errors.push_back(le);
1558 int api_ver = (api_major * 100) + api_minor;
1559 pic->m_api_version = api_ver;
1567 wxLogDebug(
"blacklist: Get status for %s %d %d",
1568 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1570 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1571 if (status != plug_status::unblocked) {
1572 wxLogDebug(
"Ignoring blacklisted plugin.");
1573 if (status != plug_status::unloadable) {
1575 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1576 load_errors.push_back(le);
1655 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1656 p->GetPlugInVersionBuild());
1659 if (!pic->m_pplugin) {
1660 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1661 INFO_LOG << _(
" API Version detected: ");
1662 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1663 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1664 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1666 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1668 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.
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.