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();
571 if (base_plugin_path.IsSameAs(plugin_file_path)) {
572 if (!IsSystemPluginPath(file_name.ToStdString())) {
573 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
576 ClearLoadStamp(plugin_loadstamp.ToStdString());
582 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
583 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
584 ClearLoadStamp(plugin_loadstamp.ToStdString());
589 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
590 wxLogMessage(msg.c_str());
591 wxLog::FlushActive();
597 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
598 wxLogMessage(msg.c_str());
599 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
600 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
601 load_errors.push_back(le);
611 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
613 if (pic && load_enabled && !enabled.Get(
true)) {
614 pic->m_destroy_fn(pic->m_pplugin);
616 wxLogMessage(
"Skipping not enabled candidate.");
617 ClearLoadStamp(plugin_loadstamp.ToStdString());
622 if (pic->m_pplugin) {
623 plugin_array.Add(pic);
630 pic->m_enabled = enabled.Get(
false);
632 if (safe_mode::get_mode()) {
633 pic->m_enabled =
false;
636 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
638 if (pic->m_enabled) {
640 pic->m_init_state =
true;
644 wxLog::FlushActive();
646 std::string found_version;
649 found_version = p.readonly ?
"" : p.version;
658 m_on_activate_cb(pic);
662 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
664 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
665 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
667 if (!pic->m_enabled && pic->m_destroy_fn) {
668 pic->m_destroy_fn(pic->m_pplugin);
669 pic->m_destroy_fn =
nullptr;
670 pic->m_pplugin =
nullptr;
671 pic->m_init_state =
false;
672 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
677 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
679 bool is_system = found != SYSTEM_PLUGINS.end();
685 available.begin(), available.end(),
688 if (it == available.end()) {
693 auto oprhan_metadata = CreateMetadata(pic);
694 auto catalogHdlr = CatalogHandler::GetInstance();
695 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
701 " PluginLoader: Unloading invalid PlugIn, API version %d ",
703 pic->m_destroy_fn(pic->m_pplugin);
705 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
707 load_errors.push_back(le);
713 ClearLoadStamp(plugin_loadstamp.ToStdString());
718bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
721 m_plugin_location = plugin_dir;
723 wxString msg(
"PluginLoader searching for PlugIns in location ");
724 msg += m_plugin_location;
728 wxString pispec =
"*_pi.dll";
729#elif defined(__WXOSX__)
730 wxString pispec =
"*_pi.dylib";
732 wxString pispec =
"*_pi.so";
735 if (!::wxDirExists(m_plugin_location)) {
736 msg = m_plugin_location;
737 msg.Prepend(
" Directory ");
738 msg.Append(
" does not exist.");
743 if (!
g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
745 wxArrayString file_list;
747 int get_flags = wxDIR_FILES | wxDIR_DIRS;
750 get_flags = wxDIR_FILES;
755 get_flags = wxDIR_FILES;
761 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
763 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
764 for (
auto& file_name : file_list) {
765 wxLog::FlushActive();
767 LoadPluginCandidate(file_name, load_enabled);
774 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
776 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
781 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
783 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
790 while ((i >= 0) && (i < plugin_array.GetCount())) {
792 if (pict->m_status == PluginStatus::PendingListRemoval) {
793 plugin_array.RemoveAt(i);
805 for (
const auto& pic : plugin_array) {
808 if (pic->m_pplugin) {
811 pic->m_pplugin =
nullptr;
812 pic->m_init_state =
false;
817 if (!pic->m_pplugin) {
818 if (pic->m_enabled) {
822 pic->m_status = stat;
823 pic->m_enabled =
true;
829 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
830 wxString msg(
"PluginLoader: Initializing PlugIn: ");
837 pic->m_init_state =
true;
838 ProcessLateInit(pic);
844 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
845 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
846 m_on_activate_cb(pic);
848 }
else if (!pic->m_enabled && pic->m_init_state) {
851 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
852 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
855 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
856 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
857 pic->m_pplugin =
nullptr;
858 pic->m_init_state =
false;
867 if (!pic)
return false;
868 if (pic->m_init_state) {
869 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
871 m_on_deactivate_cb(pic);
872 pic->m_init_state =
false;
879 auto pic = GetContainer(pd, plugin_array);
881 wxLogError(
"Attempt to deactivate non-existing plugin %s",
889 if (ix >= plugin_array.GetCount()) {
890 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
897 if (pic->m_pplugin) {
898 pic->m_destroy_fn(pic->m_pplugin);
902 plugin_array.RemoveAt(ix);
906static std::string VersionFromManifest(
const std::string& plugin_name) {
909 if (!path.empty() && wxFileName::IsFileReadable(path)) {
910 std::ifstream stream;
911 stream.open(path, std::ifstream::in);
920 if (name.empty())
return {};
923 if (isRegularFile(import_path.c_str())) {
924 std::ifstream f(import_path.c_str());
925 std::stringstream ss;
928 ParsePlugin(ss.str(), pd);
929 pd.is_imported =
true;
934 vector<PluginMetadata> matches;
935 copy_if(available.begin(), available.end(), back_inserter(matches),
937 if (matches.size() == 0)
return {};
938 if (matches.size() == 1)
return matches[0];
940 auto version = VersionFromManifest(name);
942 return version == md.version;
944 auto found = find_if(matches.begin(), matches.end(), predicate);
945 return found != matches.end() ? *found : matches[0];
951 if (name.empty())
return {};
954 vector<PluginMetadata> matches;
955 copy_if(available.begin(), available.end(), back_inserter(matches),
957 if (matches.size() == 0)
return {};
958 if (matches.size() == 1)
return matches[0];
962 auto rv = matches[0];
963 for (
auto p : matches) {
965 if (catVersion > version) {
966 version = catVersion;
977 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
979 bool is_system = found != SYSTEM_PLUGINS.end();
981 std::string installed = VersionFromManifest(md.name);
988 else if (plugin->m_status == PluginStatus::Imported)
990 else if (installedVersion < metaVersion)
991 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
992 else if (installedVersion == metaVersion)
993 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
995 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
999 plugin->m_managed_metadata = md;
1003 std::vector<PlugInContainer*> loaded_plugins;
1004 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
1007 for (
auto& p : loaded_plugins) {
1008 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1009 p->m_common_name.Lower().ToStdString());
1010 bool is_system = found != SYSTEM_PLUGINS.end();
1013 if (!keep_orphans) {
1020 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1024 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1025 loaded_plugins.erase(end, loaded_plugins.end());
1029 for (
auto& plugin : loaded_plugins) {
1032 if (!md.name.empty()) {
1034 md.is_imported = isRegularFile(import_path.c_str());
1035 if (md.is_imported) {
1036 plugin->m_status = PluginStatus::Imported;
1040 }
else if (IsSystemPluginName(md.name)) {
1042 }
else if (md.is_orphan) {
1044 }
else if (plugin->m_api_version) {
1047 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1048 plugin->m_managed_metadata = md;
1051 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1056 plugin_array.Clear();
1057 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1063 while (plugin_array.GetCount()) {
1072 for (
auto* pic : plugin_array) {
1080DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1082 PIMAGE_SECTION_HEADER pSeh;
1087 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1088 if (rva >= pSeh->VirtualAddress &&
1089 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1094 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1101 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1102 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1105 uint64_t type_magic;
1106 DependencyMap dependencies;
1110bool ReadModuleInfoFromELF(
const wxString& file,
1111 const ModuleInfo::DependencySet& dependencies,
1113 static bool b_libelf_initialized =
false;
1114 static bool b_libelf_usable =
false;
1116 if (b_libelf_usable) {
1118 }
else if (b_libelf_initialized) {
1120 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1121 b_libelf_initialized =
true;
1122 b_libelf_usable =
false;
1123 wxLogError(
"LibELF is outdated.");
1126 b_libelf_initialized =
true;
1127 b_libelf_usable =
true;
1131 Elf* elf_handle =
nullptr;
1132 GElf_Ehdr elf_file_header;
1133 Elf_Scn* elf_section_handle =
nullptr;
1135 file_handle = open(file, O_RDONLY);
1136 if (file_handle == -1) {
1137 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1139 goto FailureEpilogue;
1142 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1143 if (elf_handle ==
nullptr) {
1144 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1145 goto FailureEpilogue;
1148 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1149 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1150 goto FailureEpilogue;
1153 switch (elf_file_header.e_type) {
1158 wxLogMessage(wxString::Format(
1159 "Module \"%s\" is not an executable or shared library.", file));
1160 goto FailureEpilogue;
1164 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1166 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1168 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1170 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1172 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1176 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1178 GElf_Shdr elf_section_header;
1179 Elf_Data* elf_section_data =
nullptr;
1180 size_t elf_section_entry_count = 0;
1182 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1183 &elf_section_header) {
1184 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1185 goto FailureEpilogue;
1186 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1190 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1191 if (elf_section_data ==
nullptr) {
1192 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1193 goto FailureEpilogue;
1196 if ((elf_section_data->d_size == 0) ||
1197 (elf_section_header.sh_entsize == 0)) {
1198 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1199 goto FailureEpilogue;
1202 elf_section_entry_count =
1203 elf_section_data->d_size / elf_section_header.sh_entsize;
1204 for (
size_t elf_section_entry_index = 0;
1205 elf_section_entry_index < elf_section_entry_count;
1206 ++elf_section_entry_index) {
1207 GElf_Dyn elf_dynamic_entry;
1208 const char* elf_dynamic_entry_name =
nullptr;
1209 if (gelf_getdyn(elf_section_data,
1210 static_cast<int>(elf_section_entry_index),
1211 &elf_dynamic_entry) != &elf_dynamic_entry) {
1212 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1214 goto FailureEpilogue;
1215 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1218 elf_dynamic_entry_name = elf_strptr(
1219 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1220 if (elf_dynamic_entry_name ==
nullptr) {
1221 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1222 "string entry", file));
1223 goto FailureEpilogue;
1225 wxString name_full(elf_dynamic_entry_name);
1226 wxString name_part(elf_dynamic_entry_name,
1227 strcspn(elf_dynamic_entry_name,
"-."));
1228 if (dependencies.find(name_part) != dependencies.end()) {
1229 info.dependencies.insert(
1230 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1235 goto SuccessEpilogue;
1238 elf_end(elf_handle);
1243 if (elf_handle !=
nullptr) elf_end(elf_handle);
1244 if (file_handle >= 0) close(file_handle);
1245 wxLog::FlushActive();
1251 bool b_compat =
false;
1267 if (!m_found_wxwidgets) {
1268 DWORD myPid = GetCurrentProcessId();
1270 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1271 if (hProcess == NULL) {
1272 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1273 plugin_file.c_str()));
1277 HMODULE hMods[1024];
1279 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1280 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1281 TCHAR szModName[MAX_PATH];
1282 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1283 sizeof(szModName) /
sizeof(TCHAR))) {
1284 m_module_name = szModName;
1285 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1286 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1287 m_found_wxwidgets =
true;
1288 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1289 m_module_name.c_str()));
1296 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1297 plugin_file.c_str()));
1299 if (hProcess) CloseHandle(hProcess);
1302 if (!m_found_wxwidgets) {
1303 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1304 plugin_file.c_str()));
1306 LPCWSTR fName = plugin_file.wc_str();
1307 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1308 FILE_ATTRIBUTE_NORMAL, 0);
1309 DWORD byteread, size = GetFileSize(handle, NULL);
1310 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1311 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1312 CloseHandle(handle);
1313 PIMAGE_NT_HEADERS ntheaders =
1314 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1315 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1316 PIMAGE_SECTION_HEADER pSech =
1317 IMAGE_FIRST_SECTION(ntheaders);
1318 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1319 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1324 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1326 ntheaders->OptionalHeader
1328 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1335 while (pImportDescriptor->Name != 0) {
1338 (PCHAR)((DWORD_PTR)virtualpointer +
1339 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1342 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1345 wxLogMessage(wxString::Format(
1346 "Compatible wxWidgets plugin library found for %s: %s",
1347 plugin_file.c_str(), libname[i]));
1350 pImportDescriptor++;
1355 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1357 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1360#if defined(__WXGTK__) || defined(__WXQT__)
1361#if defined(USE_LIBELF)
1363 static bool b_own_info_queried =
false;
1364 static bool b_own_info_usable =
false;
1366 static ModuleInfo::DependencySet dependencies;
1368 if (!b_own_info_queried) {
1369 dependencies.insert(
"libwx_baseu");
1371 char exe_buf[100] = {0};
1372 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1374 exe_buf[len] =
'\0';
1375 wxString app_path(exe_buf);
1376 wxLogMessage(
"Executable path: %s", exe_buf);
1378 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1379 if (!b_own_info_usable) {
1380 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1383 wxLogMessage(
"Cannot get own executable path.");
1385 b_own_info_queried =
true;
1388 if (b_own_info_usable) {
1389 bool b_pi_info_usable =
false;
1392 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1393 if (b_pi_info_usable) {
1394 b_compat = (pi_info.type_magic == own_info.type_magic);
1397 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1402 pi_info.dependencies.clear();
1404 wxString::Format(
" Plugin \"%s\" is of another binary "
1405 "flavor than the main module.",
1407 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1408 own_info.type_magic, pi_info.type_magic);
1410 for (
const auto& own_dependency : own_info.dependencies) {
1411 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1412 pi_info.dependencies.find(own_dependency.first);
1413 if ((pi_dependency != pi_info.dependencies.end()) &&
1414 (pi_dependency->second != own_dependency.second)) {
1417 " Plugin \"%s\" depends on library \"%s\", but the main "
1418 "module was built for \"%s\".",
1419 plugin_file, pi_dependency->second, own_dependency.second);
1426 wxString::Format(
" Plugin \"%s\" could not be reliably "
1427 "checked for compatibility.",
1435 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1436 b_compat ?
"true" :
"false");
1438 wxLog::FlushActive();
1453 FILE* f = fopen(plugin_file,
"r");
1456 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1460#
if defined(__WXGTK3__)
1461 "libwx_gtk3u_core-%i.%i"
1462#elif defined(__WXGTK20__)
1463 "libwx_gtk2u_core-%i.%i"
1464#elif defined(__WXQT__)
1465 "libwx_qtu_core-%i.%i"
1467#error undefined plugin platform
1470 wxMAJOR_VERSION, wxMINOR_VERSION);
1474 size_t len(strlen(strver));
1476 while ((c = fgetc(f)) != EOF) {
1477 if (c == strver[pos]) {
1488 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1504 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1506 if (plugin_file.empty()) {
1507 wxLogMessage(
"Ignoring loading of empty path");
1511 if (!wxIsReadable(plugin_file)) {
1512 wxLogMessage(
"Ignoring unreadable plugin %s",
1513 plugin_file.ToStdString().c_str());
1514 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1515 load_errors.push_back(le);
1522 pic->m_version_major, pic->m_version_minor);
1523 if (sts != plug_status::unblocked) {
1524 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1528 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1529 if (!data.name.empty()) {
1530 wxLogDebug(
"Refusing to load blacklisted library: %s",
1531 plugin_file.ToStdString().c_str());
1539 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1540 pic->m_library.Load(plugin_file);
1542 if (!pic->m_library.IsLoaded()) {
1545 wxFileName fn(plugin_file);
1546 std::string name = fn.GetName().ToStdString();
1547 auto found = m_blacklist->get_library_data(name);
1548 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1549 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1550 if (!found.name.empty()) {
1552 LoadError le(LoadError::Type::Unloadable, name, v);
1553 load_errors.push_back(le);
1555 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1556 load_errors.push_back(le);
1559 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1565 const char*
const FIX_LOADING =
1566 _(
"\n Install/uninstall plugin or remove file to mute message");
1567 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1568 if (
nullptr == create_plugin) {
1569 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1570 wxLogMessage(msg + plugin_file);
1571 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1572 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1573 load_errors.push_back(le);
1578 destroy_t* destroy_plugin =
1579 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1580 pic->m_destroy_fn = destroy_plugin;
1581 if (
nullptr == destroy_plugin) {
1582 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1584 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1585 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1586 load_errors.push_back(le);
1596 int api_ver = (api_major * 100) + api_minor;
1597 pic->m_api_version = api_ver;
1605 wxLogDebug(
"blacklist: Get status for %s %d %d",
1606 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1608 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1609 if (status != plug_status::unblocked) {
1610 wxLogDebug(
"Ignoring blacklisted plugin.");
1611 if (status != plug_status::unloadable) {
1613 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1614 load_errors.push_back(le);
1697 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1698 p->GetPlugInVersionBuild());
1701 if (!pic->m_pplugin) {
1702 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1703 INFO_LOG << _(
" API Version detected: ");
1704 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1705 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1706 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1708 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1710 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.