39#if defined(__linux__) && !defined(__ANDROID__)
55#include <crashlytics.h>
62#include <wx/hashset.h>
63#include <wx/filename.h>
65#include <wx/tokenzr.h>
67#include <wx/process.h>
86#include "std_filesystem.h"
89#include "androidUTIL.h"
92static const std::vector<std::string> SYSTEM_PLUGINS = {
93 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
97 const ArrayOfPlugIns& plugin_array) {
98 for (
const auto& p : plugin_array) {
105static bool IsSystemPluginPath(
const std::string& path) {
106 static const std::vector<std::string> kPlugins = {
107 "chartdldr_pi",
"wmm_pi",
"dashboard_pi",
"grib_pi",
"demo_pi"};
110 for (
const auto& p : kPlugins)
111 if (lc_path.find(p) != std::string::npos) return true;
116static bool IsSystemPluginName(
const std::string& name) {
117 static const std::vector<std::string> kPlugins = {
118 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
119 auto found = std::find(kPlugins.begin(), kPlugins.end(),
ocpn::tolower(name));
120 return found != kPlugins.end();
124static std::string GetInstalledVersion(
const PlugInData& pd) {
126 if (path ==
"" || !wxFileName::IsFileReadable(path)) {
127 auto loader = PluginLoader::GetInstance();
128 auto pic = GetContainer(pd, *loader->GetPlugInArray());
129 if (!pic || !pic->m_pplugin) {
132 int v_major = pic->m_pplugin->GetPlugInVersionMajor();
133 int v_minor = pic->m_pplugin->GetPlugInVersionMinor();
136 std::ifstream stream;
138 stream.open(path, std::ifstream::in);
145 auto catalogHdlr = CatalogHandler::GetInstance();
149 SemanticVersion orphanVersion(pic->m_version_major, pic->m_version_minor);
150 mdata.version = orphanVersion.to_string();
151 mdata.summary = pic->m_short_description;
152 mdata.description = pic->m_long_description;
154 mdata.target =
"all";
155 mdata.is_orphan =
true;
161static fs::path LoadStampPath(
const std::string& file_path) {
163 path = path /
"load_stamps";
168 return path.parent_path() / path.stem();
172static void CreateLoadStamp(
const std::string& filename) {
173 std::ofstream(LoadStampPath(filename).
string());
183static bool HasLoadStamp(
const std::string& filename) {
184 return exists(LoadStampPath(filename));
191static void ClearLoadStamp(
const std::string& filename) {
192 if (filename.empty())
return;
193 auto path = LoadStampPath(filename);
196 MESSAGE_LOG <<
" Cannot remove load stamp file: " << path;
202 ClearLoadStamp(library_path);
207 std::function<
const PluginMetadata(
const std::string&)> get_metadata) {
208 auto loader = PluginLoader::GetInstance();
209 auto pic = GetContainer(pd, *loader->GetPlugInArray());
215 metadata = pic->m_managed_metadata;
216 if (metadata.version ==
"")
218 std::string detail_suffix(metadata.is_imported ? _(
" [Imported]") :
"");
219 if (metadata.is_orphan) detail_suffix = _(
" [Orphan]");
223 if (pic->m_pplugin) {
231 v_major, v_minor, p->GetPlugInVersionPatch(), p->GetPlugInVersionPost(),
232 p->GetPlugInVersionPre(), p->GetPlugInVersionBuild());
233 return sv.to_string() + detail_suffix;
235 if (!metadata.is_orphan) {
236 std::string version = GetInstalledVersion(pd);
237 return version + detail_suffix;
239 return metadata.version + detail_suffix;
243PlugInContainer::PlugInContainer()
244 :
PlugInData(), m_pplugin(nullptr), m_library(), m_destroy_fn(nullptr) {}
247 : m_has_setup_options(false),
250 m_toolbox_panel(false),
260 m_version_major = v.major;
261 m_version_minor = v.minor;
262 m_managed_metadata = md;
263 m_status = PluginStatus::ManagedInstallAvailable;
287static void setLoadPath() {
290 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
292 if (osSystemId & wxOS_UNIX_LINUX) {
295 if (wxGetEnv(
"LD_LIBRARY_PATH", &envPath)) {
296 path = path +
":" + envPath.ToStdString();
298 wxLogMessage(
"Using LD_LIBRARY_PATH: %s", path.c_str());
299 wxSetEnv(
"LD_LIBRARY_PATH", path.c_str());
300 }
else if (osSystemId & wxOS_WINDOWS) {
304 if (wxGetEnv(
"PATH", &envPath)) {
305 path = path +
";" + envPath.ToStdString();
307 wxLogMessage(
"Using PATH: %s", path);
308 wxSetEnv(
"PATH", path);
309 }
else if (osSystemId & wxOS_MAC) {
312 if (wxGetEnv(
"DYLD_LIBRARY_PATH", &envPath)) {
313 path = path +
":" + envPath.ToStdString();
315 wxLogMessage(
"Using DYLD_LIBRARY_PATH: %s", path.c_str());
316 wxSetEnv(
"DYLD_LIBRARY_PATH", path.c_str());
318 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
319 if (os_name.Contains(
"wxQT")) {
320 wxLogMessage(
"setLoadPath() using Android library path");
322 wxLogWarning(
"SetLoadPath: Unsupported platform.");
324 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
328 wxGetEnv(
"PATH", &envPath);
329 path = path +
":" + envPath.ToStdString();
330 wxLogMessage(
"Using PATH: %s", path);
331 wxSetEnv(
"PATH", path);
337 wxString msg(
"PluginLoader: Calling LateInit PlugIn: ");
342 if (ppi) ppi->LateInit();
353PluginLoader::PluginLoader()
354 : m_blacklist(blacklist_factory()),
355 m_default_plugin_icon(nullptr),
357 m_found_wxwidgets(false),
363 for (
auto* pic : plugin_array) {
364 if (pic && pic->m_enabled && (pic->
m_common_name == commonName))
372 auto loader = PluginLoader::GetInstance();
373 auto pic = GetContainer(pd, *loader->GetPlugInArray());
377void PluginLoader::NotifySetupOptionsPlugin(
const PlugInData* pd) {
382 if (pic->m_enabled && pic->m_init_state) {
384 switch (pic->m_api_version) {
395 if (pic->m_pplugin) {
399 auto loader = PluginLoader::GetInstance();
413 for (
auto* pic : plugin_array) {
415 pic->m_enabled = enabled;
422 for (
auto* pic : plugin_array) {
424 pic->m_toolbox_panel = value;
428 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
433 for (
auto* pic : plugin_array) {
439 wxLogMessage(
"Atttempt to update setup options on non-existing plugin " +
443const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
444 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
445 return m_default_plugin_icon;
448void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
449 delete m_default_plugin_icon;
450 m_default_plugin_icon = bitmap;
454 auto pic = GetContainer(pd, plugin_array);
456 wxLogMessage(
"Attempt to remove non-existing plugin %s",
460 plugin_array.Remove(pic);
464 return (*p1)->Key().compare((*p2)->Key());
469 plugin_array.Sort(ComparePlugins);
475 static const wxString sep = wxFileName::GetPathSeparator();
477 wxLogMessage(
"PluginLoader: loading plugins from %s",
ocpn::join(dirs,
';'));
479 bool any_dir_loaded =
false;
480 for (
const auto& dir : dirs) {
482 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
483 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
493 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
497 return any_dir_loaded;
500bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
502 wxString plugin_file = wxFileName(file_name).GetFullName();
503 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
505 wxString plugin_loadstamp = wxFileName(file_name).GetName();
506 if (!IsSystemPluginPath(plugin_file.ToStdString())) {
507 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
508 MESSAGE_LOG <<
"Refusing to load " << file_name
509 <<
" failed at last attempt";
512 CreateLoadStamp(plugin_loadstamp.ToStdString());
514 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
515 wxLog::FlushActive();
518 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
519 file_name.ToStdString().c_str());
528 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
537 plugin_array.Remove(pic_test);
541 pic_test->m_destroy_fn(pic_test->m_pplugin);
546 loaded_pic = pic_test;
551 loaded_pic = pic_test;
558 ClearLoadStamp(plugin_loadstamp.ToStdString());
563 wxFileName fn_plugin_file(file_name);
564 wxString plugin_file_path =
565 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
567 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
568 base_plugin_path += wxFileName::GetPathSeparator();
572 if (!g_allow_arb_system_plugin) {
574 if (base_plugin_path.IsSameAs(plugin_file_path)) {
575 if (!IsSystemPluginPath(file_name.ToStdString())) {
576 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
579 ClearLoadStamp(plugin_loadstamp.ToStdString());
586 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
587 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
588 ClearLoadStamp(plugin_loadstamp.ToStdString());
593 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
594 wxLogMessage(msg.c_str());
595 wxLog::FlushActive();
601 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
602 wxLogMessage(msg.c_str());
603 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
604 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
605 load_errors.push_back(le);
615 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
617 if (pic && load_enabled && !enabled.Get(
true)) {
618 pic->m_destroy_fn(pic->m_pplugin);
620 wxLogMessage(
"Skipping not enabled candidate.");
621 ClearLoadStamp(plugin_loadstamp.ToStdString());
626 if (pic->m_pplugin) {
627 plugin_array.Add(pic);
634 pic->m_enabled = enabled.Get(
false);
636 if (safe_mode::get_mode()) {
637 pic->m_enabled =
false;
640 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
642 if (pic->m_enabled) {
644 pic->m_init_state =
true;
648 wxLog::FlushActive();
650 std::string found_version;
653 found_version = p.readonly ?
"" : p.version;
662 m_on_activate_cb(pic);
666 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
668 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
669 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
671 if (!pic->m_enabled && pic->m_destroy_fn) {
672 pic->m_destroy_fn(pic->m_pplugin);
673 pic->m_destroy_fn =
nullptr;
674 pic->m_pplugin =
nullptr;
675 pic->m_init_state =
false;
676 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
681 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
683 bool is_system = found != SYSTEM_PLUGINS.end();
689 available.begin(), available.end(),
692 if (it == available.end()) {
697 auto oprhan_metadata = CreateMetadata(pic);
698 auto catalogHdlr = CatalogHandler::GetInstance();
699 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
705 " PluginLoader: Unloading invalid PlugIn, API version %d ",
707 pic->m_destroy_fn(pic->m_pplugin);
709 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
711 load_errors.push_back(le);
717 ClearLoadStamp(plugin_loadstamp.ToStdString());
722bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
725 m_plugin_location = plugin_dir;
727 wxString msg(
"PluginLoader searching for PlugIns in location ");
728 msg += m_plugin_location;
732 wxString pispec =
"*_pi.dll";
733#elif defined(__WXOSX__)
734 wxString pispec =
"*_pi.dylib";
736 wxString pispec =
"*_pi.so";
739 if (!::wxDirExists(m_plugin_location)) {
740 msg = m_plugin_location;
741 msg.Prepend(
" Directory ");
742 msg.Append(
" does not exist.");
747 if (!
g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
749 wxArrayString file_list;
751 int get_flags = wxDIR_FILES | wxDIR_DIRS;
754 get_flags = wxDIR_FILES;
759 get_flags = wxDIR_FILES;
765 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
767 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
768 for (
auto& file_name : file_list) {
769 wxLog::FlushActive();
771 LoadPluginCandidate(file_name, load_enabled);
778 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
780 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
785 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
787 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
794 while ((i >= 0) && (i < plugin_array.GetCount())) {
796 if (pict->m_status == PluginStatus::PendingListRemoval) {
797 plugin_array.RemoveAt(i);
809 for (
const auto& pic : plugin_array) {
812 if (pic->m_pplugin) {
815 pic->m_pplugin =
nullptr;
816 pic->m_init_state =
false;
821 if (!pic->m_pplugin) {
822 if (pic->m_enabled) {
826 pic->m_status = stat;
827 pic->m_enabled =
true;
833 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
834 wxString msg(
"PluginLoader: Initializing PlugIn: ");
841 pic->m_init_state =
true;
842 ProcessLateInit(pic);
848 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
849 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
850 m_on_activate_cb(pic);
852 }
else if (!pic->m_enabled && pic->m_init_state) {
855 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
856 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
859 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
860 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
861 pic->m_pplugin =
nullptr;
862 pic->m_init_state =
false;
871 if (!pic)
return false;
872 if (pic->m_init_state) {
873 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
875 m_on_deactivate_cb(pic);
876 pic->m_init_state =
false;
883 auto pic = GetContainer(pd, plugin_array);
885 wxLogError(
"Attempt to deactivate non-existing plugin %s",
893 if (ix >= plugin_array.GetCount()) {
894 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
901 if (pic->m_pplugin) {
902 pic->m_destroy_fn(pic->m_pplugin);
906 plugin_array.RemoveAt(ix);
910static std::string VersionFromManifest(
const std::string& plugin_name) {
913 if (!path.empty() && wxFileName::IsFileReadable(path)) {
914 std::ifstream stream;
915 stream.open(path, std::ifstream::in);
924 if (name.empty())
return {};
927 if (isRegularFile(import_path.c_str())) {
928 std::ifstream f(import_path.c_str());
929 std::stringstream ss;
932 ParsePlugin(ss.str(), pd);
933 pd.is_imported =
true;
938 vector<PluginMetadata> matches;
939 copy_if(available.begin(), available.end(), back_inserter(matches),
941 if (matches.size() == 0)
return {};
942 if (matches.size() == 1)
return matches[0];
944 auto version = VersionFromManifest(name);
946 return version == md.version;
948 auto found = find_if(matches.begin(), matches.end(), predicate);
949 return found != matches.end() ? *found : matches[0];
955 if (name.empty())
return {};
958 vector<PluginMetadata> matches;
959 copy_if(available.begin(), available.end(), back_inserter(matches),
961 if (matches.size() == 0)
return {};
962 if (matches.size() == 1)
return matches[0];
966 auto rv = matches[0];
967 for (
auto p : matches) {
969 if (catVersion > version) {
970 version = catVersion;
981 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
983 bool is_system = found != SYSTEM_PLUGINS.end();
985 std::string installed = VersionFromManifest(md.name);
992 else if (plugin->m_status == PluginStatus::Imported)
994 else if (installedVersion < metaVersion)
995 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
996 else if (installedVersion == metaVersion)
997 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
999 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
1003 plugin->m_managed_metadata = md;
1007 std::vector<PlugInContainer*> loaded_plugins;
1008 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
1011 for (
auto& p : loaded_plugins) {
1012 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1013 p->m_common_name.Lower().ToStdString());
1014 bool is_system = found != SYSTEM_PLUGINS.end();
1017 if (!keep_orphans) {
1024 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1028 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1029 loaded_plugins.erase(end, loaded_plugins.end());
1033 for (
auto& plugin : loaded_plugins) {
1036 if (!md.name.empty()) {
1038 md.is_imported = isRegularFile(import_path.c_str());
1039 if (md.is_imported) {
1040 plugin->m_status = PluginStatus::Imported;
1044 }
else if (IsSystemPluginName(md.name)) {
1046 }
else if (md.is_orphan) {
1048 }
else if (plugin->m_api_version) {
1051 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1052 plugin->m_managed_metadata = md;
1055 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1060 plugin_array.Clear();
1061 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1067 while (plugin_array.GetCount()) {
1076 for (
auto* pic : plugin_array) {
1084DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1086 PIMAGE_SECTION_HEADER pSeh;
1091 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1092 if (rva >= pSeh->VirtualAddress &&
1093 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1098 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1105 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1106 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1109 uint64_t type_magic;
1110 DependencyMap dependencies;
1114bool ReadModuleInfoFromELF(
const wxString& file,
1115 const ModuleInfo::DependencySet& dependencies,
1117 static bool b_libelf_initialized =
false;
1118 static bool b_libelf_usable =
false;
1120 if (b_libelf_usable) {
1122 }
else if (b_libelf_initialized) {
1124 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1125 b_libelf_initialized =
true;
1126 b_libelf_usable =
false;
1127 wxLogError(
"LibELF is outdated.");
1130 b_libelf_initialized =
true;
1131 b_libelf_usable =
true;
1135 Elf* elf_handle =
nullptr;
1136 GElf_Ehdr elf_file_header;
1137 Elf_Scn* elf_section_handle =
nullptr;
1139 file_handle = open(file, O_RDONLY);
1140 if (file_handle == -1) {
1141 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1143 goto FailureEpilogue;
1146 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1147 if (elf_handle ==
nullptr) {
1148 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1149 goto FailureEpilogue;
1152 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1153 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1154 goto FailureEpilogue;
1157 switch (elf_file_header.e_type) {
1162 wxLogMessage(wxString::Format(
1163 "Module \"%s\" is not an executable or shared library.", file));
1164 goto FailureEpilogue;
1168 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1170 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1172 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1174 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1176 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1180 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1182 GElf_Shdr elf_section_header;
1183 Elf_Data* elf_section_data =
nullptr;
1184 size_t elf_section_entry_count = 0;
1186 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1187 &elf_section_header) {
1188 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1189 goto FailureEpilogue;
1190 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1194 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1195 if (elf_section_data ==
nullptr) {
1196 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1197 goto FailureEpilogue;
1200 if ((elf_section_data->d_size == 0) ||
1201 (elf_section_header.sh_entsize == 0)) {
1202 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1203 goto FailureEpilogue;
1206 elf_section_entry_count =
1207 elf_section_data->d_size / elf_section_header.sh_entsize;
1208 for (
size_t elf_section_entry_index = 0;
1209 elf_section_entry_index < elf_section_entry_count;
1210 ++elf_section_entry_index) {
1211 GElf_Dyn elf_dynamic_entry;
1212 const char* elf_dynamic_entry_name =
nullptr;
1213 if (gelf_getdyn(elf_section_data,
1214 static_cast<int>(elf_section_entry_index),
1215 &elf_dynamic_entry) != &elf_dynamic_entry) {
1216 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1218 goto FailureEpilogue;
1219 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1222 elf_dynamic_entry_name = elf_strptr(
1223 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1224 if (elf_dynamic_entry_name ==
nullptr) {
1225 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1226 "string entry", file));
1227 goto FailureEpilogue;
1229 wxString name_full(elf_dynamic_entry_name);
1230 wxString name_part(elf_dynamic_entry_name,
1231 strcspn(elf_dynamic_entry_name,
"-."));
1232 if (dependencies.find(name_part) != dependencies.end()) {
1233 info.dependencies.insert(
1234 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1239 goto SuccessEpilogue;
1242 elf_end(elf_handle);
1247 if (elf_handle !=
nullptr) elf_end(elf_handle);
1248 if (file_handle >= 0) close(file_handle);
1249 wxLog::FlushActive();
1255 bool b_compat =
false;
1271 if (!m_found_wxwidgets) {
1272 DWORD myPid = GetCurrentProcessId();
1274 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1275 if (hProcess == NULL) {
1276 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1277 plugin_file.c_str()));
1281 HMODULE hMods[1024];
1283 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1284 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1285 TCHAR szModName[MAX_PATH];
1286 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1287 sizeof(szModName) /
sizeof(TCHAR))) {
1288 m_module_name = szModName;
1289 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1290 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1291 m_found_wxwidgets =
true;
1292 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1293 m_module_name.c_str()));
1300 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1301 plugin_file.c_str()));
1303 if (hProcess) CloseHandle(hProcess);
1306 if (!m_found_wxwidgets) {
1307 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1308 plugin_file.c_str()));
1310 LPCWSTR fName = plugin_file.wc_str();
1311 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1312 FILE_ATTRIBUTE_NORMAL, 0);
1313 DWORD byteread, size = GetFileSize(handle, NULL);
1314 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1315 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1316 CloseHandle(handle);
1317 PIMAGE_NT_HEADERS ntheaders =
1318 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1319 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1320 PIMAGE_SECTION_HEADER pSech =
1321 IMAGE_FIRST_SECTION(ntheaders);
1322 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1323 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1328 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1330 ntheaders->OptionalHeader
1332 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1339 while (pImportDescriptor->Name != 0) {
1342 (PCHAR)((DWORD_PTR)virtualpointer +
1343 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1346 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1349 wxLogMessage(wxString::Format(
1350 "Compatible wxWidgets plugin library found for %s: %s",
1351 plugin_file.c_str(), libname[i]));
1354 pImportDescriptor++;
1359 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1361 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1364#if defined(__WXGTK__) || defined(__WXQT__)
1365#if defined(USE_LIBELF)
1367 static bool b_own_info_queried =
false;
1368 static bool b_own_info_usable =
false;
1370 static ModuleInfo::DependencySet dependencies;
1372 if (!b_own_info_queried) {
1373 dependencies.insert(
"libwx_baseu");
1375 char exe_buf[100] = {0};
1376 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1378 exe_buf[len] =
'\0';
1379 wxString app_path(exe_buf);
1380 wxLogMessage(
"Executable path: %s", exe_buf);
1382 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1383 if (!b_own_info_usable) {
1384 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1387 wxLogMessage(
"Cannot get own executable path.");
1389 b_own_info_queried =
true;
1392 if (b_own_info_usable) {
1393 bool b_pi_info_usable =
false;
1396 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1397 if (b_pi_info_usable) {
1398 b_compat = (pi_info.type_magic == own_info.type_magic);
1401 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1406 pi_info.dependencies.clear();
1408 wxString::Format(
" Plugin \"%s\" is of another binary "
1409 "flavor than the main module.",
1411 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1412 own_info.type_magic, pi_info.type_magic);
1414 for (
const auto& own_dependency : own_info.dependencies) {
1415 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1416 pi_info.dependencies.find(own_dependency.first);
1417 if ((pi_dependency != pi_info.dependencies.end()) &&
1418 (pi_dependency->second != own_dependency.second)) {
1421 " Plugin \"%s\" depends on library \"%s\", but the main "
1422 "module was built for \"%s\".",
1423 plugin_file, pi_dependency->second, own_dependency.second);
1430 wxString::Format(
" Plugin \"%s\" could not be reliably "
1431 "checked for compatibility.",
1439 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1440 b_compat ?
"true" :
"false");
1442 wxLog::FlushActive();
1457 FILE* f = fopen(plugin_file,
"r");
1460 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1464#
if defined(__WXGTK3__)
1465 "libwx_gtk3u_core-%i.%i"
1466#elif defined(__WXGTK20__)
1467 "libwx_gtk2u_core-%i.%i"
1468#elif defined(__WXQT__)
1469 "libwx_qtu_core-%i.%i"
1471#error undefined plugin platform
1474 wxMAJOR_VERSION, wxMINOR_VERSION);
1478 size_t len(strlen(strver));
1480 while ((c = fgetc(f)) != EOF) {
1481 if (c == strver[pos]) {
1492 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1508 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1510 if (plugin_file.empty()) {
1511 wxLogMessage(
"Ignoring loading of empty path");
1515 if (!wxIsReadable(plugin_file)) {
1516 wxLogMessage(
"Ignoring unreadable plugin %s",
1517 plugin_file.ToStdString().c_str());
1518 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1519 load_errors.push_back(le);
1526 pic->m_version_major, pic->m_version_minor);
1527 if (sts != plug_status::unblocked) {
1528 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1532 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1533 if (!data.name.empty()) {
1534 wxLogDebug(
"Refusing to load blacklisted library: %s",
1535 plugin_file.ToStdString().c_str());
1543 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1544 pic->m_library.Load(plugin_file);
1546 if (!pic->m_library.IsLoaded()) {
1549 wxFileName fn(plugin_file);
1550 std::string name = fn.GetName().ToStdString();
1551 auto found = m_blacklist->get_library_data(name);
1552 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1553 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1554 if (!found.name.empty()) {
1556 LoadError le(LoadError::Type::Unloadable, name, v);
1557 load_errors.push_back(le);
1559 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1560 load_errors.push_back(le);
1563 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1569 const char*
const FIX_LOADING =
1570 _(
"\n Install/uninstall plugin or remove file to mute message");
1571 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1572 if (
nullptr == create_plugin) {
1573 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1574 wxLogMessage(msg + plugin_file);
1575 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1576 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1577 load_errors.push_back(le);
1582 destroy_t* destroy_plugin =
1583 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1584 pic->m_destroy_fn = destroy_plugin;
1585 if (
nullptr == destroy_plugin) {
1586 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1588 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1589 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1590 load_errors.push_back(le);
1600 int api_ver = (api_major * 100) + api_minor;
1601 pic->m_api_version = api_ver;
1609 wxLogDebug(
"blacklist: Get status for %s %d %d",
1610 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1612 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1613 if (status != plug_status::unblocked) {
1614 wxLogDebug(
"Ignoring blacklisted plugin.");
1615 if (status != plug_status::unloadable) {
1617 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1618 load_errors.push_back(le);
1701 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1702 p->GetPlugInVersionBuild());
1705 if (!pic->m_pplugin) {
1706 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1707 INFO_LOG << _(
" API Version detected: ");
1708 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1709 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1710 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1712 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1714 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.
Global variables stored in configuration file.
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 blacklist for plugins which can or should not be loaded.
Downloaded plugins cache.
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.
Safe mode non-gui handling.
Semantic version encode/decode object.
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.