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) {
398 if (pic->m_pplugin) {
402 auto loader = PluginLoader::GetInstance();
416 for (
auto* pic : plugin_array) {
418 pic->m_enabled = enabled;
425 for (
auto* pic : plugin_array) {
427 pic->m_toolbox_panel = value;
431 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
436 for (
auto* pic : plugin_array) {
442 wxLogMessage(
"Atttempt to update setup options on non-existing plugin " +
446const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
447 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
448 return m_default_plugin_icon;
451void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
452 delete m_default_plugin_icon;
453 m_default_plugin_icon = bitmap;
457 auto pic = GetContainer(pd, plugin_array);
459 wxLogMessage(
"Attempt to remove non-existing plugin %s",
463 plugin_array.Remove(pic);
467 return (*p1)->Key().compare((*p2)->Key());
472 plugin_array.Sort(ComparePlugins);
478 static const wxString sep = wxFileName::GetPathSeparator();
480 wxLogMessage(
"PluginLoader: loading plugins from %s",
ocpn::join(dirs,
';'));
482 bool any_dir_loaded =
false;
483 for (
const auto& dir : dirs) {
485 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
486 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
496 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
499 return any_dir_loaded;
502bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
504 wxString plugin_file = wxFileName(file_name).GetFullName();
505 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
507 wxString plugin_loadstamp = wxFileName(file_name).GetName();
508 if (!IsSystemPluginPath(plugin_file.ToStdString())) {
509 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
510 MESSAGE_LOG <<
"Refusing to load " << file_name
511 <<
" failed at last attempt";
514 CreateLoadStamp(plugin_loadstamp.ToStdString());
516 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
517 wxLog::FlushActive();
520 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
521 file_name.ToStdString().c_str());
530 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
539 plugin_array.Remove(pic_test);
543 pic_test->m_destroy_fn(pic_test->m_pplugin);
548 loaded_pic = pic_test;
553 loaded_pic = pic_test;
560 ClearLoadStamp(plugin_loadstamp.ToStdString());
565 wxFileName fn_plugin_file(file_name);
566 wxString plugin_file_path =
567 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
569 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
570 base_plugin_path += wxFileName::GetPathSeparator();
574 if (!g_allow_arb_system_plugin) {
576 if (base_plugin_path.IsSameAs(plugin_file_path)) {
577 if (!IsSystemPluginPath(file_name.ToStdString())) {
578 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
581 ClearLoadStamp(plugin_loadstamp.ToStdString());
588 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
589 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
590 ClearLoadStamp(plugin_loadstamp.ToStdString());
595 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
596 wxLogMessage(msg.c_str());
597 wxLog::FlushActive();
603 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
604 wxLogMessage(msg.c_str());
605 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
606 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
607 load_errors.push_back(le);
617 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
619 if (pic && load_enabled && !enabled.Get(
true)) {
620 pic->m_destroy_fn(pic->m_pplugin);
622 wxLogMessage(
"Skipping not enabled candidate.");
623 ClearLoadStamp(plugin_loadstamp.ToStdString());
628 if (pic->m_pplugin) {
629 plugin_array.Add(pic);
636 pic->m_enabled = enabled.Get(
false);
638 if (safe_mode::get_mode() &&
639 !IsSystemPluginPath(file_name.ToStdString())) {
640 pic->m_enabled =
false;
643 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
645 if (pic->m_enabled) {
647 pic->m_init_state =
true;
651 wxLog::FlushActive();
653 std::string found_version;
656 found_version = p.readonly ?
"" : p.version;
665 m_on_activate_cb(pic);
669 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
671 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
672 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
674 if (!pic->m_enabled && pic->m_destroy_fn) {
675 pic->m_destroy_fn(pic->m_pplugin);
676 pic->m_destroy_fn =
nullptr;
677 pic->m_pplugin =
nullptr;
678 pic->m_init_state =
false;
679 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
684 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
686 bool is_system = found != SYSTEM_PLUGINS.end();
692 available.begin(), available.end(),
695 if (it == available.end()) {
700 auto oprhan_metadata = CreateMetadata(pic);
701 auto catalogHdlr = CatalogHandler::GetInstance();
702 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
708 " PluginLoader: Unloading invalid PlugIn, API version %d ",
710 pic->m_destroy_fn(pic->m_pplugin);
712 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
714 load_errors.push_back(le);
720 ClearLoadStamp(plugin_loadstamp.ToStdString());
725bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
728 m_plugin_location = plugin_dir;
730 wxString msg(
"PluginLoader searching for PlugIns in location ");
731 msg += m_plugin_location;
735 wxString pispec =
"*_pi.dll";
736#elif defined(__WXOSX__)
737 wxString pispec =
"*_pi.dylib";
739 wxString pispec =
"*_pi.so";
742 if (!::wxDirExists(m_plugin_location)) {
743 msg = m_plugin_location;
744 msg.Prepend(
" Directory ");
745 msg.Append(
" does not exist.");
750 if (!
g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
752 wxArrayString file_list;
754 int get_flags = wxDIR_FILES | wxDIR_DIRS;
757 get_flags = wxDIR_FILES;
762 get_flags = wxDIR_FILES;
768 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
770 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
771 for (
auto& file_name : file_list) {
772 wxLog::FlushActive();
774 LoadPluginCandidate(file_name, load_enabled);
781 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
783 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
788 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
790 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
797 while ((i >= 0) && (i < plugin_array.GetCount())) {
799 if (pict->m_status == PluginStatus::PendingListRemoval) {
800 plugin_array.RemoveAt(i);
812 for (
const auto& pic : plugin_array) {
815 if (pic->m_pplugin) {
818 pic->m_pplugin =
nullptr;
819 pic->m_init_state =
false;
824 if (!pic->m_pplugin) {
825 if (pic->m_enabled) {
829 pic->m_status = stat;
830 pic->m_enabled =
true;
836 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
837 wxString msg(
"PluginLoader: Initializing PlugIn: ");
844 pic->m_init_state =
true;
845 ProcessLateInit(pic);
851 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
852 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
853 m_on_activate_cb(pic);
855 }
else if (!pic->m_enabled && pic->m_init_state) {
858 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
859 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
862 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
863 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
864 pic->m_pplugin =
nullptr;
865 pic->m_init_state =
false;
874 if (!pic)
return false;
875 if (pic->m_init_state) {
876 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
878 m_on_deactivate_cb(pic);
879 pic->m_init_state =
false;
888 auto pic = GetContainer(pd, plugin_array);
890 wxLogError(
"Attempt to deactivate non-existing plugin %s",
898 if (ix >= plugin_array.GetCount()) {
899 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
906 if (pic->m_pplugin) {
907 pic->m_destroy_fn(pic->m_pplugin);
911 plugin_array.RemoveAt(ix);
915static std::string VersionFromManifest(
const std::string& plugin_name) {
918 if (!path.empty() && wxFileName::IsFileReadable(path)) {
919 std::ifstream stream;
920 stream.open(path, std::ifstream::in);
929 if (name.empty())
return {};
932 if (isRegularFile(import_path.c_str())) {
933 std::ifstream f(import_path.c_str());
934 std::stringstream ss;
937 ParsePlugin(ss.str(), pd);
938 pd.is_imported =
true;
943 vector<PluginMetadata> matches;
944 copy_if(available.begin(), available.end(), back_inserter(matches),
946 if (matches.size() == 0)
return {};
947 if (matches.size() == 1)
return matches[0];
949 auto version = VersionFromManifest(name);
951 return version == md.version;
953 auto found = find_if(matches.begin(), matches.end(), predicate);
954 return found != matches.end() ? *found : matches[0];
960 if (name.empty())
return {};
963 vector<PluginMetadata> matches;
964 copy_if(available.begin(), available.end(), back_inserter(matches),
966 if (matches.size() == 0)
return {};
967 if (matches.size() == 1)
return matches[0];
971 auto rv = matches[0];
972 for (
auto p : matches) {
974 if (catVersion > version) {
975 version = catVersion;
986 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
988 bool is_system = found != SYSTEM_PLUGINS.end();
990 std::string installed = VersionFromManifest(md.name);
997 else if (plugin->m_status == PluginStatus::Imported)
999 else if (installedVersion < metaVersion)
1000 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
1001 else if (installedVersion == metaVersion)
1002 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
1004 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
1008 plugin->m_managed_metadata = md;
1012 std::vector<PlugInContainer*> loaded_plugins;
1013 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
1016 for (
auto& p : loaded_plugins) {
1017 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1018 p->m_common_name.Lower().ToStdString());
1019 bool is_system = found != SYSTEM_PLUGINS.end();
1022 if (!keep_orphans) {
1029 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1033 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1034 loaded_plugins.erase(end, loaded_plugins.end());
1038 for (
auto& plugin : loaded_plugins) {
1041 if (!md.name.empty()) {
1043 md.is_imported = isRegularFile(import_path.c_str());
1044 if (md.is_imported) {
1045 plugin->m_status = PluginStatus::Imported;
1049 }
else if (IsSystemPluginName(md.name)) {
1051 }
else if (md.is_orphan) {
1053 }
else if (plugin->m_api_version) {
1056 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1057 plugin->m_managed_metadata = md;
1060 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1065 plugin_array.Clear();
1066 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1072 while (plugin_array.GetCount()) {
1081 for (
auto* pic : plugin_array) {
1089DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1091 PIMAGE_SECTION_HEADER pSeh;
1096 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1097 if (rva >= pSeh->VirtualAddress &&
1098 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1103 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1110 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1111 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1114 uint64_t type_magic;
1115 DependencyMap dependencies;
1119bool ReadModuleInfoFromELF(
const wxString& file,
1120 const ModuleInfo::DependencySet& dependencies,
1122 static bool b_libelf_initialized =
false;
1123 static bool b_libelf_usable =
false;
1125 if (b_libelf_usable) {
1127 }
else if (b_libelf_initialized) {
1129 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1130 b_libelf_initialized =
true;
1131 b_libelf_usable =
false;
1132 wxLogError(
"LibELF is outdated.");
1135 b_libelf_initialized =
true;
1136 b_libelf_usable =
true;
1140 Elf* elf_handle =
nullptr;
1141 GElf_Ehdr elf_file_header;
1142 Elf_Scn* elf_section_handle =
nullptr;
1144 file_handle = open(file, O_RDONLY);
1145 if (file_handle == -1) {
1146 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1148 goto FailureEpilogue;
1151 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1152 if (elf_handle ==
nullptr) {
1153 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1154 goto FailureEpilogue;
1157 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1158 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1159 goto FailureEpilogue;
1162 switch (elf_file_header.e_type) {
1167 wxLogMessage(wxString::Format(
1168 "Module \"%s\" is not an executable or shared library.", file));
1169 goto FailureEpilogue;
1173 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1175 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1177 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1179 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1181 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1185 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1187 GElf_Shdr elf_section_header;
1188 Elf_Data* elf_section_data =
nullptr;
1189 size_t elf_section_entry_count = 0;
1191 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1192 &elf_section_header) {
1193 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1194 goto FailureEpilogue;
1195 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1199 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1200 if (elf_section_data ==
nullptr) {
1201 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1202 goto FailureEpilogue;
1205 if ((elf_section_data->d_size == 0) ||
1206 (elf_section_header.sh_entsize == 0)) {
1207 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1208 goto FailureEpilogue;
1211 elf_section_entry_count =
1212 elf_section_data->d_size / elf_section_header.sh_entsize;
1213 for (
size_t elf_section_entry_index = 0;
1214 elf_section_entry_index < elf_section_entry_count;
1215 ++elf_section_entry_index) {
1216 GElf_Dyn elf_dynamic_entry;
1217 const char* elf_dynamic_entry_name =
nullptr;
1218 if (gelf_getdyn(elf_section_data,
1219 static_cast<int>(elf_section_entry_index),
1220 &elf_dynamic_entry) != &elf_dynamic_entry) {
1221 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1223 goto FailureEpilogue;
1224 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1227 elf_dynamic_entry_name = elf_strptr(
1228 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1229 if (elf_dynamic_entry_name ==
nullptr) {
1230 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1231 "string entry", file));
1232 goto FailureEpilogue;
1234 wxString name_full(elf_dynamic_entry_name);
1235 wxString name_part(elf_dynamic_entry_name,
1236 strcspn(elf_dynamic_entry_name,
"-."));
1237 if (dependencies.find(name_part) != dependencies.end()) {
1238 info.dependencies.insert(
1239 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1244 goto SuccessEpilogue;
1247 elf_end(elf_handle);
1252 if (elf_handle !=
nullptr) elf_end(elf_handle);
1253 if (file_handle >= 0) close(file_handle);
1254 wxLog::FlushActive();
1260 bool b_compat =
false;
1276 if (!m_found_wxwidgets) {
1277 DWORD myPid = GetCurrentProcessId();
1279 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1280 if (hProcess == NULL) {
1281 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1282 plugin_file.c_str()));
1286 HMODULE hMods[1024];
1288 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1289 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1290 TCHAR szModName[MAX_PATH];
1291 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1292 sizeof(szModName) /
sizeof(TCHAR))) {
1293 m_module_name = szModName;
1294 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1295 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1296 m_found_wxwidgets =
true;
1297 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1298 m_module_name.c_str()));
1305 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1306 plugin_file.c_str()));
1308 if (hProcess) CloseHandle(hProcess);
1311 if (!m_found_wxwidgets) {
1312 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1313 plugin_file.c_str()));
1315 LPCWSTR fName = plugin_file.wc_str();
1316 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1317 FILE_ATTRIBUTE_NORMAL, 0);
1318 DWORD byteread, size = GetFileSize(handle, NULL);
1319 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1320 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1321 CloseHandle(handle);
1322 PIMAGE_NT_HEADERS ntheaders =
1323 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1324 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1325 PIMAGE_SECTION_HEADER pSech =
1326 IMAGE_FIRST_SECTION(ntheaders);
1327 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1328 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1333 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1335 ntheaders->OptionalHeader
1337 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1344 while (pImportDescriptor->Name != 0) {
1347 (PCHAR)((DWORD_PTR)virtualpointer +
1348 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1351 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1354 wxLogMessage(wxString::Format(
1355 "Compatible wxWidgets plugin library found for %s: %s",
1356 plugin_file.c_str(), libname[i]));
1359 pImportDescriptor++;
1364 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1366 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1369#if defined(__WXGTK__) || defined(__WXQT__)
1370#if defined(USE_LIBELF)
1372 static bool b_own_info_queried =
false;
1373 static bool b_own_info_usable =
false;
1375 static ModuleInfo::DependencySet dependencies;
1377 if (!b_own_info_queried) {
1378 dependencies.insert(
"libwx_baseu");
1380 char exe_buf[100] = {0};
1381 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1383 exe_buf[len] =
'\0';
1384 wxString app_path(exe_buf);
1385 wxLogMessage(
"Executable path: %s", exe_buf);
1387 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1388 if (!b_own_info_usable) {
1389 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1392 wxLogMessage(
"Cannot get own executable path.");
1394 b_own_info_queried =
true;
1397 if (b_own_info_usable) {
1398 bool b_pi_info_usable =
false;
1401 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1402 if (b_pi_info_usable) {
1403 b_compat = (pi_info.type_magic == own_info.type_magic);
1406 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1411 pi_info.dependencies.clear();
1413 wxString::Format(
" Plugin \"%s\" is of another binary "
1414 "flavor than the main module.",
1416 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1417 own_info.type_magic, pi_info.type_magic);
1419 for (
const auto& own_dependency : own_info.dependencies) {
1420 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1421 pi_info.dependencies.find(own_dependency.first);
1422 if ((pi_dependency != pi_info.dependencies.end()) &&
1423 (pi_dependency->second != own_dependency.second)) {
1426 " Plugin \"%s\" depends on library \"%s\", but the main "
1427 "module was built for \"%s\".",
1428 plugin_file, pi_dependency->second, own_dependency.second);
1435 wxString::Format(
" Plugin \"%s\" could not be reliably "
1436 "checked for compatibility.",
1444 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1445 b_compat ?
"true" :
"false");
1447 wxLog::FlushActive();
1462 FILE* f = fopen(plugin_file,
"r");
1465 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1469#
if defined(__WXGTK3__)
1470 "libwx_gtk3u_core-%i.%i"
1471#elif defined(__WXGTK20__)
1472 "libwx_gtk2u_core-%i.%i"
1473#elif defined(__WXQT__)
1474 "libwx_qtu_core-%i.%i"
1476#error undefined plugin platform
1479 wxMAJOR_VERSION, wxMINOR_VERSION);
1483 size_t len(strlen(strver));
1485 while ((c = fgetc(f)) != EOF) {
1486 if (c == strver[pos]) {
1497 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1513 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1515 if (plugin_file.empty()) {
1516 wxLogMessage(
"Ignoring loading of empty path");
1520 if (!wxIsReadable(plugin_file)) {
1521 wxLogMessage(
"Ignoring unreadable plugin %s",
1522 plugin_file.ToStdString().c_str());
1523 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1524 load_errors.push_back(le);
1531 pic->m_version_major, pic->m_version_minor);
1532 if (sts != plug_status::unblocked) {
1533 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1537 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1538 if (!data.name.empty()) {
1539 wxLogDebug(
"Refusing to load blacklisted library: %s",
1540 plugin_file.ToStdString().c_str());
1548 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1549 pic->m_library.Load(plugin_file);
1551 if (!pic->m_library.IsLoaded()) {
1554 wxFileName fn(plugin_file);
1555 std::string name = fn.GetName().ToStdString();
1556 auto found = m_blacklist->get_library_data(name);
1557 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1558 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1559 if (!found.name.empty()) {
1561 LoadError le(LoadError::Type::Unloadable, name, v);
1562 load_errors.push_back(le);
1564 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1565 load_errors.push_back(le);
1568 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1574 const char*
const FIX_LOADING =
1575 _(
"\n Install/uninstall plugin or remove file to mute message");
1576 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1577 if (
nullptr == create_plugin) {
1578 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1579 wxLogMessage(msg + plugin_file);
1580 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1581 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1582 load_errors.push_back(le);
1587 destroy_t* destroy_plugin =
1588 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1589 pic->m_destroy_fn = destroy_plugin;
1590 if (
nullptr == destroy_plugin) {
1591 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1593 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1594 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1595 load_errors.push_back(le);
1605 int api_ver = (api_major * 100) + api_minor;
1606 pic->m_api_version = api_ver;
1614 wxLogDebug(
"blacklist: Get status for %s %d %d",
1615 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1617 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1618 if (status != plug_status::unblocked) {
1619 wxLogDebug(
"Ignoring blacklisted plugin.");
1620 if (status != plug_status::unloadable) {
1622 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1623 load_errors.push_back(le);
1710 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1711 p->GetPlugInVersionBuild());
1714 if (!pic->m_pplugin) {
1715 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1716 INFO_LOG << _(
" API Version detected: ");
1717 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1718 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1719 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1721 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1723 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.
EventVar evt_deactivate_plugin
Notified with plugin name when it's deactivated.
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.