39#if defined(__linux__) && !defined(__ANDROID__)
51#include <wx/hashset.h>
52#include <wx/filename.h>
54#include <wx/tokenzr.h>
56#include <wx/process.h>
58#include "model/base_platform.h"
61#include "model/config_vars.h"
63#include "model/config_vars.h"
66#include "model/plugin_blacklist.h"
67#include "model/plugin_cache.h"
68#include "model/plugin_handler.h"
70#include "model/plugin_paths.h"
71#include "model/safe_mode.h"
72#include "model/semantic_vers.h"
73#include "observable_confvar.h"
74#include "std_filesystem.h"
77#include "androidUTIL.h"
79#include "crashlytics.h"
86static const std::vector<std::string> SYSTEM_PLUGINS = {
87 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
91 const ArrayOfPlugIns& plugin_array) {
92 for (
const auto& p : plugin_array) {
99static bool IsSystemPluginPath(
const std::string& path) {
100 static const std::vector<std::string> kPlugins = {
101 "chartdldr_pi",
"wmm_pi",
"dashboard_pi",
"grib_pi",
"demo_pi"};
104 for (
const auto& p : kPlugins)
105 if (lc_path.find(p) != std::string::npos) return true;
110static bool IsSystemPluginName(
const std::string& name) {
111 static const std::vector<std::string> kPlugins = {
112 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
113 auto found = std::find(kPlugins.begin(), kPlugins.end(),
ocpn::tolower(name));
114 return found != kPlugins.end();
118static std::string GetInstalledVersion(
const PlugInData& pd) {
120 if (path ==
"" || !wxFileName::IsFileReadable(path)) {
121 auto loader = PluginLoader::GetInstance();
122 auto pic = GetContainer(pd, *loader->GetPlugInArray());
123 if (!pic || !pic->m_pplugin) {
126 int v_major = pic->m_pplugin->GetPlugInVersionMajor();
127 int v_minor = pic->m_pplugin->GetPlugInVersionMinor();
130 std::ifstream stream;
132 stream.open(path, std::ifstream::in);
139 auto catalogHdlr = CatalogHandler::getInstance();
143 SemanticVersion orphanVersion(pic->m_version_major, pic->m_version_minor);
144 mdata.version = orphanVersion.to_string();
145 mdata.summary = pic->m_short_description;
146 mdata.description = pic->m_long_description;
148 mdata.target =
"all";
149 mdata.is_orphan =
true;
155static fs::path LoadStampPath(
const std::string& file_path) {
157 path = path /
"load_stamps";
162 return path.parent_path() / path.stem();
165static void CreateLoadStamp(
const std::string& filename) {
166 std::ofstream(LoadStampPath(filename).
string());
169static bool HasLoadStamp(
const std::string& filename) {
170 return exists(LoadStampPath(filename));
173static void ClearLoadStamp(
const std::string& filename) {
174 if (filename.empty())
return;
175 auto path = LoadStampPath(filename);
178 MESSAGE_LOG <<
" Cannot remove load stamp file: " << path;
184 ClearLoadStamp(library_path);
189 std::function<
const PluginMetadata(
const std::string&)> get_metadata) {
190 auto loader = PluginLoader::GetInstance();
191 auto pic = GetContainer(pd, *loader->GetPlugInArray());
197 metadata = pic->m_managed_metadata;
198 if (metadata.version ==
"")
200 std::string detail_suffix(metadata.is_imported ? _(
" [Imported]") :
"");
201 if (metadata.is_orphan) detail_suffix = _(
" [Orphan]");
205 if (pic->m_pplugin) {
213 v_major, v_minor, p->GetPlugInVersionPatch(), p->GetPlugInVersionPost(),
214 p->GetPlugInVersionPre(), p->GetPlugInVersionBuild());
215 return sv.to_string() + detail_suffix;
217 if (!metadata.is_orphan) {
218 std::string version = GetInstalledVersion(pd);
219 return version + detail_suffix;
221 return metadata.version + detail_suffix;
225PlugInContainer::PlugInContainer()
226 :
PlugInData(), m_pplugin(nullptr), m_library(), m_destroy_fn(nullptr) {}
229 : m_has_setup_options(false),
232 m_toolbox_panel(false),
242 m_version_major = v.major;
243 m_version_minor = v.minor;
244 m_managed_metadata = md;
245 m_status = PluginStatus::ManagedInstallAvailable;
269static void setLoadPath() {
272 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
274 if (osSystemId & wxOS_UNIX_LINUX) {
277 if (wxGetEnv(
"LD_LIBRARY_PATH", &envPath)) {
278 path = path +
":" + envPath.ToStdString();
280 wxLogMessage(
"Using LD_LIBRARY_PATH: %s", path.c_str());
281 wxSetEnv(
"LD_LIBRARY_PATH", path.c_str());
282 }
else if (osSystemId & wxOS_WINDOWS) {
286 if (wxGetEnv(
"PATH", &envPath)) {
287 path = path +
";" + envPath.ToStdString();
289 wxLogMessage(
"Using PATH: %s", path);
290 wxSetEnv(
"PATH", path);
291 }
else if (osSystemId & wxOS_MAC) {
294 if (wxGetEnv(
"DYLD_LIBRARY_PATH", &envPath)) {
295 path = path +
":" + envPath.ToStdString();
297 wxLogMessage(
"Using DYLD_LIBRARY_PATH: %s", path.c_str());
298 wxSetEnv(
"DYLD_LIBRARY_PATH", path.c_str());
300 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
301 if (os_name.Contains(
"wxQT")) {
302 wxLogMessage(
"setLoadPath() using Android library path");
304 wxLogWarning(
"SetLoadPath: Unsupported platform.");
306 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
310 wxGetEnv(
"PATH", &envPath);
311 path = path +
":" + envPath.ToStdString();
312 wxLogMessage(
"Using PATH: %s", path);
313 wxSetEnv(
"PATH", path);
319 wxString msg(
"PluginLoader: Calling LateInit PlugIn: ");
324 if (ppi) ppi->LateInit();
335PluginLoader::PluginLoader()
336 : m_blacklist(blacklist_factory()),
337 m_default_plugin_icon(nullptr),
339 m_found_wxwidgets(false),
344bool PluginLoader::IsPlugInAvailable(
const wxString& commonName) {
345 for (
auto* pic : plugin_array) {
346 if (pic && pic->m_enabled && (pic->
m_common_name == commonName))
354 auto loader = PluginLoader::GetInstance();
355 auto pic = GetContainer(pd, *loader->GetPlugInArray());
359void PluginLoader::NotifySetupOptionsPlugin(
const PlugInData* pd) {
360 auto pic = GetContainer(*pd, *GetPlugInArray());
364 if (pic->m_enabled && pic->m_init_state) {
366 switch (pic->m_api_version) {
377 if (pic->m_pplugin) {
381 auto loader = PluginLoader::GetInstance();
395 for (
auto* pic : plugin_array) {
397 pic->m_enabled = enabled;
404 for (
auto* pic : plugin_array) {
406 pic->m_toolbox_panel = value;
410 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
415 for (
auto* pic : plugin_array) {
421 wxLogMessage(
"Atttempt to update setup options on non-existing plugin " +
425const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
426 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
427 return m_default_plugin_icon;
430void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
431 delete m_default_plugin_icon;
432 m_default_plugin_icon = bitmap;
436 auto pic = GetContainer(pd, plugin_array);
438 wxLogMessage(
"Attempt to remove non-existing plugin %s",
442 plugin_array.Remove(pic);
446 return (*p1)->Key().compare((*p2)->Key());
451 plugin_array.Sort(ComparePlugins);
457 static const wxString sep = wxFileName::GetPathSeparator();
459 wxLogMessage(
"PluginLoader: loading plugins from %s",
ocpn::join(dirs,
';'));
461 bool any_dir_loaded =
false;
462 for (
const auto& dir : dirs) {
464 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
465 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
471 if (!load_enabled) UpdateManagedPlugins(keep_orphans);
475 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
479 return any_dir_loaded;
482bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
484 wxString plugin_file = wxFileName(file_name).GetFullName();
485 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
487 wxString plugin_loadstamp = wxFileName(file_name).GetName();
488 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
489 MESSAGE_LOG <<
"Refusing to load " << file_name
490 <<
" failed at last attempt";
493 CreateLoadStamp(plugin_loadstamp.ToStdString());
494 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
495 wxLog::FlushActive();
498 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
499 file_name.ToStdString().c_str());
508 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
517 plugin_array.Remove(pic_test);
520 DeactivatePlugIn(pic_test);
521 pic_test->m_destroy_fn(pic_test->m_pplugin);
526 loaded_pic = pic_test;
531 loaded_pic = pic_test;
538 ClearLoadStamp(plugin_loadstamp.ToStdString());
543 wxFileName fn_plugin_file(file_name);
544 wxString plugin_file_path =
545 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
546 wxString base_plugin_path = g_BasePlatform->
GetPluginDir();
547 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
548 base_plugin_path += wxFileName::GetPathSeparator();
551 if (base_plugin_path.IsSameAs(plugin_file_path)) {
552 if (!IsSystemPluginPath(file_name.ToStdString())) {
553 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
556 ClearLoadStamp(plugin_loadstamp.ToStdString());
562 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
563 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
564 ClearLoadStamp(plugin_loadstamp.ToStdString());
569 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
570 wxLogMessage(msg.c_str());
571 wxLog::FlushActive();
573 bool b_compat = CheckPluginCompatibility(file_name);
577 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
578 wxLogMessage(msg.c_str());
579 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
580 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
581 load_errors.push_back(le);
591 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
593 if (pic && load_enabled && !enabled.Get(
true)) {
594 pic->m_destroy_fn(pic->m_pplugin);
596 wxLogMessage(
"Skipping not enabled candidate.");
597 ClearLoadStamp(plugin_loadstamp.ToStdString());
602 if (pic->m_pplugin) {
603 plugin_array.Add(pic);
610 pic->m_enabled = enabled.Get(
false);
612 if (safe_mode::get_mode()) {
613 pic->m_enabled =
false;
616 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
618 if (pic->m_enabled) {
620 pic->m_init_state =
true;
623 evt_load_plugin.
Notify(pic);
624 wxLog::FlushActive();
626 std::string found_version;
627 for (
const auto& p :
PluginHandler::getInstance()->getInstalled()) {
629 found_version = p.readonly ?
"" : p.version;
641 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
643 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
644 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
646 if (!pic->m_enabled && pic->m_destroy_fn) {
647 pic->m_destroy_fn(pic->m_pplugin);
648 pic->m_destroy_fn =
nullptr;
649 pic->m_pplugin =
nullptr;
650 pic->m_init_state =
false;
651 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
656 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
658 bool is_system = found != SYSTEM_PLUGINS.end();
664 available.begin(), available.end(),
667 if (it == available.end()) {
672 auto oprhan_metadata = CreateMetadata(pic);
673 auto catalogHdlr = CatalogHandler::getInstance();
674 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
680 " PluginLoader: Unloading invalid PlugIn, API version %d ",
682 pic->m_destroy_fn(pic->m_pplugin);
684 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
686 load_errors.push_back(le);
692 ClearLoadStamp(plugin_loadstamp.ToStdString());
697bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
699 evt_load_directory.
Notify();
700 m_plugin_location = plugin_dir;
702 wxString msg(
"PluginLoader searching for PlugIns in location ");
703 msg += m_plugin_location;
707 wxString pispec =
"*_pi.dll";
708#elif defined(__WXOSX__)
709 wxString pispec =
"*_pi.dylib";
711 wxString pispec =
"*_pi.so";
714 if (!::wxDirExists(m_plugin_location)) {
715 msg = m_plugin_location;
716 msg.Prepend(
" Directory ");
717 msg.Append(
" does not exist.");
722 if (!g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
724 wxArrayString file_list;
726 int get_flags = wxDIR_FILES | wxDIR_DIRS;
729 get_flags = wxDIR_FILES;
734 get_flags = wxDIR_FILES;
740 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
742 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
743 for (
auto& file_name : file_list) {
744 wxLog::FlushActive();
746 LoadPluginCandidate(file_name, load_enabled);
753 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
755 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
760 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
762 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
769 while ((i >= 0) && (i < plugin_array.GetCount())) {
771 if (pict->m_status == PluginStatus::PendingListRemoval) {
772 plugin_array.RemoveAt(i);
781bool PluginLoader::UpdatePlugIns() {
784 for (
const auto& pic : plugin_array) {
787 if (pic->m_pplugin) {
790 pic->m_pplugin =
nullptr;
791 pic->m_init_state =
false;
796 if (!pic->m_pplugin) {
797 if (pic->m_enabled) {
801 pic->m_status = stat;
802 pic->m_enabled =
true;
808 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
809 wxString msg(
"PluginLoader: Initializing PlugIn: ");
816 pic->m_init_state =
true;
817 ProcessLateInit(pic);
823 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
824 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
826 }
else if (!pic->m_enabled && pic->m_init_state) {
829 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
830 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
832 bret = DeactivatePlugIn(pic);
833 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
834 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
835 pic->m_pplugin =
nullptr;
836 pic->m_init_state =
false;
844 if (!pic)
return false;
845 if (pic->m_init_state) {
846 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
848 m_on_deactivate_cb(pic);
849 pic->m_init_state =
false;
855bool PluginLoader::DeactivatePlugIn(
const PlugInData& pd) {
856 auto pic = GetContainer(pd, plugin_array);
858 wxLogError(
"Attempt to deactivate non-existing plugin %s",
862 return DeactivatePlugIn(pic);
866 if (ix >= plugin_array.GetCount()) {
867 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
871 if (!DeactivatePlugIn(pic)) {
874 if (pic->m_pplugin) {
875 pic->m_destroy_fn(pic->m_pplugin);
879 plugin_array.RemoveAt(ix);
883static std::string VersionFromManifest(
const std::string& plugin_name) {
886 if (!path.empty() && wxFileName::IsFileReadable(path)) {
887 std::ifstream stream;
888 stream.open(path, std::ifstream::in);
897 if (name.empty())
return {};
900 if (isRegularFile(import_path.c_str())) {
901 std::ifstream f(import_path.c_str());
902 std::stringstream ss;
905 ParsePlugin(ss.str(), pd);
906 pd.is_imported =
true;
911 vector<PluginMetadata> matches;
912 copy_if(available.begin(), available.end(), back_inserter(matches),
914 if (matches.size() == 0)
return {};
915 if (matches.size() == 1)
return matches[0];
917 auto version = VersionFromManifest(name);
919 return version == md.version;
921 auto found = find_if(matches.begin(), matches.end(), predicate);
922 return found != matches.end() ? *found : matches[0];
928 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
930 bool is_system = found != SYSTEM_PLUGINS.end();
932 std::string installed = VersionFromManifest(md.name);
939 else if (plugin->m_status == PluginStatus::Imported)
941 else if (installedVersion < metaVersion)
942 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
943 else if (installedVersion == metaVersion)
944 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
946 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
950 plugin->m_managed_metadata = md;
953void PluginLoader::UpdateManagedPlugins(
bool keep_orphans) {
954 std::vector<PlugInContainer*> loaded_plugins;
955 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
958 for (
auto& p : loaded_plugins) {
959 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
960 p->m_common_name.Lower().ToStdString());
961 bool is_system = found != SYSTEM_PLUGINS.end();
971 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
975 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
976 loaded_plugins.erase(end, loaded_plugins.end());
980 for (
auto& plugin : loaded_plugins) {
982 if (!md.name.empty()) {
984 md.is_imported = isRegularFile(import_path.c_str());
985 if (md.is_imported) {
986 plugin->m_status = PluginStatus::Imported;
990 }
else if (IsSystemPluginName(md.name)) {
992 }
else if (md.is_orphan) {
994 }
else if (plugin->m_api_version) {
997 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
998 plugin->m_managed_metadata = md;
1001 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1006 plugin_array.Clear();
1007 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1008 evt_pluglist_change.
Notify();
1011bool PluginLoader::UnLoadAllPlugIns() {
1013 while (plugin_array.GetCount()) {
1021bool PluginLoader::DeactivateAllPlugIns() {
1022 for (
auto* pic : plugin_array) {
1023 if (pic && pic->m_enabled && pic->m_init_state) DeactivatePlugIn(pic);
1030DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1032 PIMAGE_SECTION_HEADER pSeh;
1037 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1038 if (rva >= pSeh->VirtualAddress &&
1039 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1044 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1051 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1052 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1055 uint64_t type_magic;
1056 DependencyMap dependencies;
1060bool ReadModuleInfoFromELF(
const wxString& file,
1061 const ModuleInfo::DependencySet& dependencies,
1063 static bool b_libelf_initialized =
false;
1064 static bool b_libelf_usable =
false;
1066 if (b_libelf_usable) {
1068 }
else if (b_libelf_initialized) {
1070 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1071 b_libelf_initialized =
true;
1072 b_libelf_usable =
false;
1073 wxLogError(
"LibELF is outdated.");
1076 b_libelf_initialized =
true;
1077 b_libelf_usable =
true;
1081 Elf* elf_handle =
nullptr;
1082 GElf_Ehdr elf_file_header;
1083 Elf_Scn* elf_section_handle =
nullptr;
1085 file_handle = open(file, O_RDONLY);
1086 if (file_handle == -1) {
1087 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1089 goto FailureEpilogue;
1092 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1093 if (elf_handle ==
nullptr) {
1094 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1095 goto FailureEpilogue;
1098 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1099 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1100 goto FailureEpilogue;
1103 switch (elf_file_header.e_type) {
1108 wxLogMessage(wxString::Format(
1109 "Module \"%s\" is not an executable or shared library.", file));
1110 goto FailureEpilogue;
1114 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1116 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1118 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1120 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1122 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1126 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1128 GElf_Shdr elf_section_header;
1129 Elf_Data* elf_section_data =
nullptr;
1130 size_t elf_section_entry_count = 0;
1132 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1133 &elf_section_header) {
1134 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1135 goto FailureEpilogue;
1136 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1140 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1141 if (elf_section_data ==
nullptr) {
1142 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1143 goto FailureEpilogue;
1146 if ((elf_section_data->d_size == 0) ||
1147 (elf_section_header.sh_entsize == 0)) {
1148 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1149 goto FailureEpilogue;
1152 elf_section_entry_count =
1153 elf_section_data->d_size / elf_section_header.sh_entsize;
1154 for (
size_t elf_section_entry_index = 0;
1155 elf_section_entry_index < elf_section_entry_count;
1156 ++elf_section_entry_index) {
1157 GElf_Dyn elf_dynamic_entry;
1158 const char* elf_dynamic_entry_name =
nullptr;
1159 if (gelf_getdyn(elf_section_data,
1160 static_cast<int>(elf_section_entry_index),
1161 &elf_dynamic_entry) != &elf_dynamic_entry) {
1162 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1164 goto FailureEpilogue;
1165 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1168 elf_dynamic_entry_name = elf_strptr(
1169 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1170 if (elf_dynamic_entry_name ==
nullptr) {
1171 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1172 "string entry", file));
1173 goto FailureEpilogue;
1175 wxString name_full(elf_dynamic_entry_name);
1176 wxString name_part(elf_dynamic_entry_name,
1177 strcspn(elf_dynamic_entry_name,
"-."));
1178 if (dependencies.find(name_part) != dependencies.end()) {
1179 info.dependencies.insert(
1180 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1185 goto SuccessEpilogue;
1188 elf_end(elf_handle);
1193 if (elf_handle !=
nullptr) elf_end(elf_handle);
1194 if (file_handle >= 0) close(file_handle);
1195 wxLog::FlushActive();
1200bool PluginLoader::CheckPluginCompatibility(
const wxString& plugin_file) {
1201 bool b_compat =
false;
1217 if (!m_found_wxwidgets) {
1218 DWORD myPid = GetCurrentProcessId();
1220 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1221 if (hProcess == NULL) {
1222 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1223 plugin_file.c_str()));
1227 HMODULE hMods[1024];
1229 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1230 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1231 TCHAR szModName[MAX_PATH];
1232 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1233 sizeof(szModName) /
sizeof(TCHAR))) {
1234 m_module_name = szModName;
1235 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1236 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1237 m_found_wxwidgets =
true;
1238 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1239 m_module_name.c_str()));
1246 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1247 plugin_file.c_str()));
1249 if (hProcess) CloseHandle(hProcess);
1252 if (!m_found_wxwidgets) {
1253 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1254 plugin_file.c_str()));
1256 LPCWSTR fName = plugin_file.wc_str();
1257 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1258 FILE_ATTRIBUTE_NORMAL, 0);
1259 DWORD byteread, size = GetFileSize(handle, NULL);
1260 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1261 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1262 CloseHandle(handle);
1263 PIMAGE_NT_HEADERS ntheaders =
1264 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1265 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1266 PIMAGE_SECTION_HEADER pSech =
1267 IMAGE_FIRST_SECTION(ntheaders);
1268 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1269 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1274 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1276 ntheaders->OptionalHeader
1278 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1285 while (pImportDescriptor->Name != 0) {
1288 (PCHAR)((DWORD_PTR)virtualpointer +
1289 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1292 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1295 wxLogMessage(wxString::Format(
1296 "Compatible wxWidgets plugin library found for %s: %s",
1297 plugin_file.c_str(), libname[i]));
1300 pImportDescriptor++;
1305 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1307 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1310#if defined(__WXGTK__) || defined(__WXQT__)
1311#if defined(USE_LIBELF)
1313 static bool b_own_info_queried =
false;
1314 static bool b_own_info_usable =
false;
1316 static ModuleInfo::DependencySet dependencies;
1318 if (!b_own_info_queried) {
1319 dependencies.insert(
"libwx_baseu");
1321 char exe_buf[100] = {0};
1322 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1324 exe_buf[len] =
'\0';
1325 wxString app_path(exe_buf);
1326 wxLogMessage(
"Executable path: %s", exe_buf);
1328 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1329 if (!b_own_info_usable) {
1330 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1333 wxLogMessage(
"Cannot get own executable path.");
1335 b_own_info_queried =
true;
1338 if (b_own_info_usable) {
1339 bool b_pi_info_usable =
false;
1342 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1343 if (b_pi_info_usable) {
1344 b_compat = (pi_info.type_magic == own_info.type_magic);
1347 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1352 pi_info.dependencies.clear();
1354 wxString::Format(
" Plugin \"%s\" is of another binary "
1355 "flavor than the main module.",
1357 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1358 own_info.type_magic, pi_info.type_magic);
1360 for (
const auto& own_dependency : own_info.dependencies) {
1361 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1362 pi_info.dependencies.find(own_dependency.first);
1363 if ((pi_dependency != pi_info.dependencies.end()) &&
1364 (pi_dependency->second != own_dependency.second)) {
1367 " Plugin \"%s\" depends on library \"%s\", but the main "
1368 "module was built for \"%s\".",
1369 plugin_file, pi_dependency->second, own_dependency.second);
1376 wxString::Format(
" Plugin \"%s\" could not be reliably "
1377 "checked for compatibility.",
1385 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1386 b_compat ?
"true" :
"false");
1388 wxLog::FlushActive();
1403 FILE* f = fopen(plugin_file,
"r");
1406 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1410#
if defined(__WXGTK3__)
1411 "libwx_gtk3u_core-%i.%i"
1412#elif defined(__WXGTK20__)
1413 "libwx_gtk2u_core-%i.%i"
1414#elif defined(__WXQT__)
1415 "libwx_qtu_core-%i.%i"
1417#error undefined plugin platform
1420 wxMAJOR_VERSION, wxMINOR_VERSION);
1424 size_t len(strlen(strver));
1426 while ((c = fgetc(f)) != EOF) {
1427 if (c == strver[pos]) {
1438 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1442PlugInContainer* PluginLoader::LoadPlugIn(
const wxString& plugin_file) {
1444 if (!LoadPlugIn(plugin_file, pic)) {
1454 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1456 if (plugin_file.empty()) {
1457 wxLogMessage(
"Ignoring loading of empty path");
1461 if (!wxIsReadable(plugin_file)) {
1462 wxLogMessage(
"Ignoring unreadable plugin %s",
1463 plugin_file.ToStdString().c_str());
1464 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1465 load_errors.push_back(le);
1472 pic->m_version_major, pic->m_version_minor);
1473 if (sts != plug_status::unblocked) {
1474 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1478 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1479 if (!data.name.empty()) {
1480 wxLogDebug(
"Refusing to load blacklisted library: %s",
1481 plugin_file.ToStdString().c_str());
1489 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1490 pic->m_library.Load(plugin_file);
1492 if (!pic->m_library.IsLoaded()) {
1495 wxFileName fn(plugin_file);
1496 std::string name = fn.GetName().ToStdString();
1497 auto found = m_blacklist->get_library_data(name);
1498 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1499 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1500 if (!found.name.empty()) {
1502 LoadError le(LoadError::Type::Unloadable, name, v);
1503 load_errors.push_back(le);
1505 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1506 load_errors.push_back(le);
1509 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1515 const char*
const FIX_LOADING =
1516 _(
"\n Install/uninstall plugin or remove file to mute message");
1517 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1518 if (
nullptr == create_plugin) {
1519 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1520 wxLogMessage(msg + plugin_file);
1521 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1522 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1523 load_errors.push_back(le);
1528 destroy_t* destroy_plugin =
1529 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1530 pic->m_destroy_fn = destroy_plugin;
1531 if (
nullptr == destroy_plugin) {
1532 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1534 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1535 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1536 load_errors.push_back(le);
1546 int api_ver = (api_major * 100) + api_minor;
1547 pic->m_api_version = api_ver;
1555 wxLogDebug(
"blacklist: Get status for %s %d %d",
1556 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1558 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1559 if (status != plug_status::unblocked) {
1560 wxLogDebug(
"Ignoring blacklisted plugin.");
1561 if (status != plug_status::unloadable) {
1563 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1564 load_errors.push_back(le);
1643 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1644 p->GetPlugInVersionBuild());
1647 if (!pic->m_pplugin) {
1648 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1649 INFO_LOG << _(
" API Version detected: ");
1650 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1651 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1652 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1654 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1656 load_errors.push_back(le);
Plugin catalog management: Build the runtime catalog, handling downloads as required.
Datatypes and methods to parse ocpn-plugins.xml XML data, either complete catalog or a single plugin.
Wrapper for configuration variables which lives in a wxBaseConfig object.
const void Notify()
Notify all listeners, no data supplied.
Error condition when loading a plugin.
Data for a loaded plugin, including dl-loaded library.
Basic data for a loaded plugin, trivially copyable.
wxString m_plugin_filename
The short file path.
wxString m_plugin_file
The full file path.
int m_cap_flag
PlugIn Capabilities descriptor.
PlugInData(const PluginMetadata &md)
Create a container with applicable fields defined from metadata.
wxString m_common_name
A common name string for the plugin.
bool m_has_setup_options
Has run NotifySetupOptionsPlugin()
std::string Key() const
sort key.
std::string m_manifest_version
As detected from manifest.
wxDateTime m_plugin_modification
used to detect upgraded plugins
wxString m_version_str
Complete version as of semantic_vers.
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 fileListPath(std::string name)
Return path to installation manifest for given plugin.
static std::string versionPath(std::string name)
Return path to file containing version for given plugin.
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.
static void MarkAsLoadable(const std::string &library_path)
Mark a library file (complete path) as loadable i.
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.
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.
void RemovePlugin(const PlugInData &pd)
Remove a plugin from *GetPluginArray().
void ShowPreferencesDialog(const PlugInData &pd, wxWindow *parent)
Display the preferences dialog for a plugin.
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.
std::vector< std::string > Bindirs()
'List of directories for plugin binary helpers.
static PluginPaths * getInstance()
Return the singleton instance.
virtual void OnSetupOptions(void)
Allows plugin to add custom setup options.
Base class for OpenCPN plugins.
virtual void ShowPreferencesDialog(wxWindow *parent)
Shows the plugin preferences dialog.
virtual wxBitmap * GetPlugInBitmap()
Get the plugin's icon bitmap.
virtual int Init(void)
Initialize the plugin and declare its capabilities.
virtual bool DeInit(void)
Clean up plugin resources.
virtual void SetDefaults(void)
Sets plugin default options.
virtual wxString GetShortDescription()
Get a brief description of the plugin.
virtual wxString GetCommonName()
Get the plugin's common (short) name.
virtual int GetPlugInVersionMajor()
Returns the major version number of the plugin itself.
virtual int GetAPIVersionMinor()
Returns the minor version number of the plugin API that this plugin supports.
virtual int GetAPIVersionMajor()
Returns the major version number of the plugin API that this plugin supports.
virtual wxString GetLongDescription()
Get detailed plugin information.
virtual int GetPlugInVersionMinor()
Returns the minor version number of the plugin itself.
Global variables reflecting command line options and arguments.
Enhanced logging interface on top of wx/log.h.
std::string tolower(const std::string &input)
Return copy of s with all characters converted to lower case.
bool exists(const std::string &name)
std::string join(std::vector< std::string > v, char c)
Return a single string being the concatenation of all elements in v with character c in between.
void mkdir(const std::string path)
#define WANTS_LATE_INIT
Delay full plugin initialization until system is ready.
#define INSTALLS_TOOLBOX_PAGE
Plugin will add pages to the toolbox/settings dialog.
Miscellaneous utilities, many of which string related.
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.
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.