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"
66#include "model/plugin_blacklist.h"
67#include "model/plugin_cache.h"
71#include "model/safe_mode.h"
72#include "model/semantic_vers.h"
74#include "std_filesystem.h"
77#include "androidUTIL.h"
79#include "crashlytics.h"
86static const std::vector<std::string> SYSTEM_PLUGINS = {
87 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
91 const ArrayOfPlugIns& plugin_array) {
92 for (
const auto& p : plugin_array) {
99static bool IsSystemPluginPath(
const std::string& path) {
100 static const std::vector<std::string> kPlugins = {
101 "chartdldr_pi",
"wmm_pi",
"dashboard_pi",
"grib_pi",
"demo_pi"};
104 for (
const auto& p : kPlugins)
105 if (lc_path.find(p) != std::string::npos) return true;
110static bool IsSystemPluginName(
const std::string& name) {
111 static const std::vector<std::string> kPlugins = {
112 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
113 auto found = std::find(kPlugins.begin(), kPlugins.end(),
ocpn::tolower(name));
114 return found != kPlugins.end();
118static std::string GetInstalledVersion(
const PlugInData& pd) {
120 if (path ==
"" || !wxFileName::IsFileReadable(path)) {
121 auto loader = PluginLoader::GetInstance();
122 auto pic = GetContainer(pd, *loader->GetPlugInArray());
123 if (!pic || !pic->m_pplugin) {
126 int v_major = pic->m_pplugin->GetPlugInVersionMajor();
127 int v_minor = pic->m_pplugin->GetPlugInVersionMinor();
130 std::ifstream stream;
132 stream.open(path, std::ifstream::in);
139 auto catalogHdlr = CatalogHandler::GetInstance();
143 SemanticVersion orphanVersion(pic->m_version_major, pic->m_version_minor);
144 mdata.version = orphanVersion.to_string();
145 mdata.summary = pic->m_short_description;
146 mdata.description = pic->m_long_description;
148 mdata.target =
"all";
149 mdata.is_orphan =
true;
155static fs::path LoadStampPath(
const std::string& file_path) {
157 path = path /
"load_stamps";
162 return path.parent_path() / path.stem();
166static void CreateLoadStamp(
const std::string& filename) {
167 std::ofstream(LoadStampPath(filename).
string());
177static bool HasLoadStamp(
const std::string& filename) {
178 return exists(LoadStampPath(filename));
185static void ClearLoadStamp(
const std::string& filename) {
186 if (filename.empty())
return;
187 auto path = LoadStampPath(filename);
190 MESSAGE_LOG <<
" Cannot remove load stamp file: " << path;
196 ClearLoadStamp(library_path);
201 std::function<
const PluginMetadata(
const std::string&)> get_metadata) {
202 auto loader = PluginLoader::GetInstance();
203 auto pic = GetContainer(pd, *loader->GetPlugInArray());
209 metadata = pic->m_managed_metadata;
210 if (metadata.version ==
"")
212 std::string detail_suffix(metadata.is_imported ? _(
" [Imported]") :
"");
213 if (metadata.is_orphan) detail_suffix = _(
" [Orphan]");
217 if (pic->m_pplugin) {
225 v_major, v_minor, p->GetPlugInVersionPatch(), p->GetPlugInVersionPost(),
226 p->GetPlugInVersionPre(), p->GetPlugInVersionBuild());
227 return sv.to_string() + detail_suffix;
229 if (!metadata.is_orphan) {
230 std::string version = GetInstalledVersion(pd);
231 return version + detail_suffix;
233 return metadata.version + detail_suffix;
237PlugInContainer::PlugInContainer()
238 :
PlugInData(), m_pplugin(nullptr), m_library(), m_destroy_fn(nullptr) {}
241 : m_has_setup_options(false),
244 m_toolbox_panel(false),
254 m_version_major = v.major;
255 m_version_minor = v.minor;
256 m_managed_metadata = md;
257 m_status = PluginStatus::ManagedInstallAvailable;
281static void setLoadPath() {
284 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
286 if (osSystemId & wxOS_UNIX_LINUX) {
289 if (wxGetEnv(
"LD_LIBRARY_PATH", &envPath)) {
290 path = path +
":" + envPath.ToStdString();
292 wxLogMessage(
"Using LD_LIBRARY_PATH: %s", path.c_str());
293 wxSetEnv(
"LD_LIBRARY_PATH", path.c_str());
294 }
else if (osSystemId & wxOS_WINDOWS) {
298 if (wxGetEnv(
"PATH", &envPath)) {
299 path = path +
";" + envPath.ToStdString();
301 wxLogMessage(
"Using PATH: %s", path);
302 wxSetEnv(
"PATH", path);
303 }
else if (osSystemId & wxOS_MAC) {
306 if (wxGetEnv(
"DYLD_LIBRARY_PATH", &envPath)) {
307 path = path +
":" + envPath.ToStdString();
309 wxLogMessage(
"Using DYLD_LIBRARY_PATH: %s", path.c_str());
310 wxSetEnv(
"DYLD_LIBRARY_PATH", path.c_str());
312 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
313 if (os_name.Contains(
"wxQT")) {
314 wxLogMessage(
"setLoadPath() using Android library path");
316 wxLogWarning(
"SetLoadPath: Unsupported platform.");
318 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
322 wxGetEnv(
"PATH", &envPath);
323 path = path +
":" + envPath.ToStdString();
324 wxLogMessage(
"Using PATH: %s", path);
325 wxSetEnv(
"PATH", path);
331 wxString msg(
"PluginLoader: Calling LateInit PlugIn: ");
336 if (ppi) ppi->LateInit();
347PluginLoader::PluginLoader()
348 : m_blacklist(blacklist_factory()),
349 m_default_plugin_icon(nullptr),
351 m_found_wxwidgets(false),
357 for (
auto* pic : plugin_array) {
358 if (pic && pic->m_enabled && (pic->
m_common_name == commonName))
366 auto loader = PluginLoader::GetInstance();
367 auto pic = GetContainer(pd, *loader->GetPlugInArray());
371void PluginLoader::NotifySetupOptionsPlugin(
const PlugInData* pd) {
376 if (pic->m_enabled && pic->m_init_state) {
378 switch (pic->m_api_version) {
389 if (pic->m_pplugin) {
393 auto loader = PluginLoader::GetInstance();
407 for (
auto* pic : plugin_array) {
409 pic->m_enabled = enabled;
416 for (
auto* pic : plugin_array) {
418 pic->m_toolbox_panel = value;
422 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
427 for (
auto* pic : plugin_array) {
433 wxLogMessage(
"Atttempt to update setup options on non-existing plugin " +
437const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
438 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
439 return m_default_plugin_icon;
442void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
443 delete m_default_plugin_icon;
444 m_default_plugin_icon = bitmap;
448 auto pic = GetContainer(pd, plugin_array);
450 wxLogMessage(
"Attempt to remove non-existing plugin %s",
454 plugin_array.Remove(pic);
458 return (*p1)->Key().compare((*p2)->Key());
463 plugin_array.Sort(ComparePlugins);
469 static const wxString sep = wxFileName::GetPathSeparator();
471 wxLogMessage(
"PluginLoader: loading plugins from %s",
ocpn::join(dirs,
';'));
473 bool any_dir_loaded =
false;
474 for (
const auto& dir : dirs) {
476 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
477 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
487 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
491 return any_dir_loaded;
494bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
496 wxString plugin_file = wxFileName(file_name).GetFullName();
497 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
499 wxString plugin_loadstamp = wxFileName(file_name).GetName();
500 if (!IsSystemPluginPath(plugin_file.ToStdString())) {
501 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
502 MESSAGE_LOG <<
"Refusing to load " << file_name
503 <<
" failed at last attempt";
506 CreateLoadStamp(plugin_loadstamp.ToStdString());
508 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
509 wxLog::FlushActive();
512 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
513 file_name.ToStdString().c_str());
522 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
531 plugin_array.Remove(pic_test);
535 pic_test->m_destroy_fn(pic_test->m_pplugin);
540 loaded_pic = pic_test;
545 loaded_pic = pic_test;
552 ClearLoadStamp(plugin_loadstamp.ToStdString());
557 wxFileName fn_plugin_file(file_name);
558 wxString plugin_file_path =
559 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
560 wxString base_plugin_path = g_BasePlatform->
GetPluginDir();
561 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
562 base_plugin_path += wxFileName::GetPathSeparator();
565 if (base_plugin_path.IsSameAs(plugin_file_path)) {
566 if (!IsSystemPluginPath(file_name.ToStdString())) {
567 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
570 ClearLoadStamp(plugin_loadstamp.ToStdString());
576 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
577 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
578 ClearLoadStamp(plugin_loadstamp.ToStdString());
583 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
584 wxLogMessage(msg.c_str());
585 wxLog::FlushActive();
591 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
592 wxLogMessage(msg.c_str());
593 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
594 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
595 load_errors.push_back(le);
605 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
607 if (pic && load_enabled && !enabled.Get(
true)) {
608 pic->m_destroy_fn(pic->m_pplugin);
610 wxLogMessage(
"Skipping not enabled candidate.");
611 ClearLoadStamp(plugin_loadstamp.ToStdString());
616 if (pic->m_pplugin) {
617 plugin_array.Add(pic);
624 pic->m_enabled = enabled.Get(
false);
626 if (safe_mode::get_mode()) {
627 pic->m_enabled =
false;
630 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
632 if (pic->m_enabled) {
634 pic->m_init_state =
true;
638 wxLog::FlushActive();
640 std::string found_version;
643 found_version = p.readonly ?
"" : p.version;
652 m_on_activate_cb(pic);
656 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
658 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
659 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
661 if (!pic->m_enabled && pic->m_destroy_fn) {
662 pic->m_destroy_fn(pic->m_pplugin);
663 pic->m_destroy_fn =
nullptr;
664 pic->m_pplugin =
nullptr;
665 pic->m_init_state =
false;
666 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
671 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
673 bool is_system = found != SYSTEM_PLUGINS.end();
679 available.begin(), available.end(),
682 if (it == available.end()) {
687 auto oprhan_metadata = CreateMetadata(pic);
688 auto catalogHdlr = CatalogHandler::GetInstance();
689 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
695 " PluginLoader: Unloading invalid PlugIn, API version %d ",
697 pic->m_destroy_fn(pic->m_pplugin);
699 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
701 load_errors.push_back(le);
707 ClearLoadStamp(plugin_loadstamp.ToStdString());
712bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
715 m_plugin_location = plugin_dir;
717 wxString msg(
"PluginLoader searching for PlugIns in location ");
718 msg += m_plugin_location;
722 wxString pispec =
"*_pi.dll";
723#elif defined(__WXOSX__)
724 wxString pispec =
"*_pi.dylib";
726 wxString pispec =
"*_pi.so";
729 if (!::wxDirExists(m_plugin_location)) {
730 msg = m_plugin_location;
731 msg.Prepend(
" Directory ");
732 msg.Append(
" does not exist.");
737 if (!g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
739 wxArrayString file_list;
741 int get_flags = wxDIR_FILES | wxDIR_DIRS;
744 get_flags = wxDIR_FILES;
749 get_flags = wxDIR_FILES;
755 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
757 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
758 for (
auto& file_name : file_list) {
759 wxLog::FlushActive();
761 LoadPluginCandidate(file_name, load_enabled);
768 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
770 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
775 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
777 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
784 while ((i >= 0) && (i < plugin_array.GetCount())) {
786 if (pict->m_status == PluginStatus::PendingListRemoval) {
787 plugin_array.RemoveAt(i);
799 for (
const auto& pic : plugin_array) {
802 if (pic->m_pplugin) {
805 pic->m_pplugin =
nullptr;
806 pic->m_init_state =
false;
811 if (!pic->m_pplugin) {
812 if (pic->m_enabled) {
816 pic->m_status = stat;
817 pic->m_enabled =
true;
823 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
824 wxString msg(
"PluginLoader: Initializing PlugIn: ");
831 pic->m_init_state =
true;
832 ProcessLateInit(pic);
838 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
839 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
840 m_on_activate_cb(pic);
842 }
else if (!pic->m_enabled && pic->m_init_state) {
845 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
846 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
849 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
850 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
851 pic->m_pplugin =
nullptr;
852 pic->m_init_state =
false;
861 if (!pic)
return false;
862 if (pic->m_init_state) {
863 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
865 m_on_deactivate_cb(pic);
866 pic->m_init_state =
false;
873 auto pic = GetContainer(pd, plugin_array);
875 wxLogError(
"Attempt to deactivate non-existing plugin %s",
883 if (ix >= plugin_array.GetCount()) {
884 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
891 if (pic->m_pplugin) {
892 pic->m_destroy_fn(pic->m_pplugin);
896 plugin_array.RemoveAt(ix);
900static std::string VersionFromManifest(
const std::string& plugin_name) {
903 if (!path.empty() && wxFileName::IsFileReadable(path)) {
904 std::ifstream stream;
905 stream.open(path, std::ifstream::in);
914 if (name.empty())
return {};
917 if (isRegularFile(import_path.c_str())) {
918 std::ifstream f(import_path.c_str());
919 std::stringstream ss;
922 ParsePlugin(ss.str(), pd);
923 pd.is_imported =
true;
928 vector<PluginMetadata> matches;
929 copy_if(available.begin(), available.end(), back_inserter(matches),
931 if (matches.size() == 0)
return {};
932 if (matches.size() == 1)
return matches[0];
934 auto version = VersionFromManifest(name);
936 return version == md.version;
938 auto found = find_if(matches.begin(), matches.end(), predicate);
939 return found != matches.end() ? *found : matches[0];
945 if (name.empty())
return {};
948 vector<PluginMetadata> matches;
949 copy_if(available.begin(), available.end(), back_inserter(matches),
951 if (matches.size() == 0)
return {};
952 if (matches.size() == 1)
return matches[0];
956 auto rv = matches[0];
957 for (
auto p : matches) {
959 if (catVersion > version) {
960 version = catVersion;
971 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
973 bool is_system = found != SYSTEM_PLUGINS.end();
975 std::string installed = VersionFromManifest(md.name);
982 else if (plugin->m_status == PluginStatus::Imported)
984 else if (installedVersion < metaVersion)
985 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
986 else if (installedVersion == metaVersion)
987 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
989 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
993 plugin->m_managed_metadata = md;
997 std::vector<PlugInContainer*> loaded_plugins;
998 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
1001 for (
auto& p : loaded_plugins) {
1002 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1003 p->m_common_name.Lower().ToStdString());
1004 bool is_system = found != SYSTEM_PLUGINS.end();
1007 if (!keep_orphans) {
1014 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1018 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1019 loaded_plugins.erase(end, loaded_plugins.end());
1023 for (
auto& plugin : loaded_plugins) {
1026 if (!md.name.empty()) {
1028 md.is_imported = isRegularFile(import_path.c_str());
1029 if (md.is_imported) {
1030 plugin->m_status = PluginStatus::Imported;
1034 }
else if (IsSystemPluginName(md.name)) {
1036 }
else if (md.is_orphan) {
1038 }
else if (plugin->m_api_version) {
1041 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1042 plugin->m_managed_metadata = md;
1045 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1050 plugin_array.Clear();
1051 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1057 while (plugin_array.GetCount()) {
1066 for (
auto* pic : plugin_array) {
1074DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1076 PIMAGE_SECTION_HEADER pSeh;
1081 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1082 if (rva >= pSeh->VirtualAddress &&
1083 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1088 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1095 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1096 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1099 uint64_t type_magic;
1100 DependencyMap dependencies;
1104bool ReadModuleInfoFromELF(
const wxString& file,
1105 const ModuleInfo::DependencySet& dependencies,
1107 static bool b_libelf_initialized =
false;
1108 static bool b_libelf_usable =
false;
1110 if (b_libelf_usable) {
1112 }
else if (b_libelf_initialized) {
1114 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1115 b_libelf_initialized =
true;
1116 b_libelf_usable =
false;
1117 wxLogError(
"LibELF is outdated.");
1120 b_libelf_initialized =
true;
1121 b_libelf_usable =
true;
1125 Elf* elf_handle =
nullptr;
1126 GElf_Ehdr elf_file_header;
1127 Elf_Scn* elf_section_handle =
nullptr;
1129 file_handle = open(file, O_RDONLY);
1130 if (file_handle == -1) {
1131 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1133 goto FailureEpilogue;
1136 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1137 if (elf_handle ==
nullptr) {
1138 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1139 goto FailureEpilogue;
1142 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1143 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1144 goto FailureEpilogue;
1147 switch (elf_file_header.e_type) {
1152 wxLogMessage(wxString::Format(
1153 "Module \"%s\" is not an executable or shared library.", file));
1154 goto FailureEpilogue;
1158 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1160 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1162 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1164 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1166 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1170 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1172 GElf_Shdr elf_section_header;
1173 Elf_Data* elf_section_data =
nullptr;
1174 size_t elf_section_entry_count = 0;
1176 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1177 &elf_section_header) {
1178 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1179 goto FailureEpilogue;
1180 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1184 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1185 if (elf_section_data ==
nullptr) {
1186 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1187 goto FailureEpilogue;
1190 if ((elf_section_data->d_size == 0) ||
1191 (elf_section_header.sh_entsize == 0)) {
1192 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1193 goto FailureEpilogue;
1196 elf_section_entry_count =
1197 elf_section_data->d_size / elf_section_header.sh_entsize;
1198 for (
size_t elf_section_entry_index = 0;
1199 elf_section_entry_index < elf_section_entry_count;
1200 ++elf_section_entry_index) {
1201 GElf_Dyn elf_dynamic_entry;
1202 const char* elf_dynamic_entry_name =
nullptr;
1203 if (gelf_getdyn(elf_section_data,
1204 static_cast<int>(elf_section_entry_index),
1205 &elf_dynamic_entry) != &elf_dynamic_entry) {
1206 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1208 goto FailureEpilogue;
1209 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1212 elf_dynamic_entry_name = elf_strptr(
1213 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1214 if (elf_dynamic_entry_name ==
nullptr) {
1215 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1216 "string entry", file));
1217 goto FailureEpilogue;
1219 wxString name_full(elf_dynamic_entry_name);
1220 wxString name_part(elf_dynamic_entry_name,
1221 strcspn(elf_dynamic_entry_name,
"-."));
1222 if (dependencies.find(name_part) != dependencies.end()) {
1223 info.dependencies.insert(
1224 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1229 goto SuccessEpilogue;
1232 elf_end(elf_handle);
1237 if (elf_handle !=
nullptr) elf_end(elf_handle);
1238 if (file_handle >= 0) close(file_handle);
1239 wxLog::FlushActive();
1245 bool b_compat =
false;
1261 if (!m_found_wxwidgets) {
1262 DWORD myPid = GetCurrentProcessId();
1264 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1265 if (hProcess == NULL) {
1266 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1267 plugin_file.c_str()));
1271 HMODULE hMods[1024];
1273 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1274 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1275 TCHAR szModName[MAX_PATH];
1276 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1277 sizeof(szModName) /
sizeof(TCHAR))) {
1278 m_module_name = szModName;
1279 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1280 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1281 m_found_wxwidgets =
true;
1282 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1283 m_module_name.c_str()));
1290 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1291 plugin_file.c_str()));
1293 if (hProcess) CloseHandle(hProcess);
1296 if (!m_found_wxwidgets) {
1297 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1298 plugin_file.c_str()));
1300 LPCWSTR fName = plugin_file.wc_str();
1301 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1302 FILE_ATTRIBUTE_NORMAL, 0);
1303 DWORD byteread, size = GetFileSize(handle, NULL);
1304 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1305 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1306 CloseHandle(handle);
1307 PIMAGE_NT_HEADERS ntheaders =
1308 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1309 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1310 PIMAGE_SECTION_HEADER pSech =
1311 IMAGE_FIRST_SECTION(ntheaders);
1312 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1313 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1318 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1320 ntheaders->OptionalHeader
1322 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1329 while (pImportDescriptor->Name != 0) {
1332 (PCHAR)((DWORD_PTR)virtualpointer +
1333 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1336 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1339 wxLogMessage(wxString::Format(
1340 "Compatible wxWidgets plugin library found for %s: %s",
1341 plugin_file.c_str(), libname[i]));
1344 pImportDescriptor++;
1349 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1351 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1354#if defined(__WXGTK__) || defined(__WXQT__)
1355#if defined(USE_LIBELF)
1357 static bool b_own_info_queried =
false;
1358 static bool b_own_info_usable =
false;
1360 static ModuleInfo::DependencySet dependencies;
1362 if (!b_own_info_queried) {
1363 dependencies.insert(
"libwx_baseu");
1365 char exe_buf[100] = {0};
1366 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1368 exe_buf[len] =
'\0';
1369 wxString app_path(exe_buf);
1370 wxLogMessage(
"Executable path: %s", exe_buf);
1372 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1373 if (!b_own_info_usable) {
1374 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1377 wxLogMessage(
"Cannot get own executable path.");
1379 b_own_info_queried =
true;
1382 if (b_own_info_usable) {
1383 bool b_pi_info_usable =
false;
1386 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1387 if (b_pi_info_usable) {
1388 b_compat = (pi_info.type_magic == own_info.type_magic);
1391 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1396 pi_info.dependencies.clear();
1398 wxString::Format(
" Plugin \"%s\" is of another binary "
1399 "flavor than the main module.",
1401 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1402 own_info.type_magic, pi_info.type_magic);
1404 for (
const auto& own_dependency : own_info.dependencies) {
1405 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1406 pi_info.dependencies.find(own_dependency.first);
1407 if ((pi_dependency != pi_info.dependencies.end()) &&
1408 (pi_dependency->second != own_dependency.second)) {
1411 " Plugin \"%s\" depends on library \"%s\", but the main "
1412 "module was built for \"%s\".",
1413 plugin_file, pi_dependency->second, own_dependency.second);
1420 wxString::Format(
" Plugin \"%s\" could not be reliably "
1421 "checked for compatibility.",
1429 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1430 b_compat ?
"true" :
"false");
1432 wxLog::FlushActive();
1447 FILE* f = fopen(plugin_file,
"r");
1450 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1454#
if defined(__WXGTK3__)
1455 "libwx_gtk3u_core-%i.%i"
1456#elif defined(__WXGTK20__)
1457 "libwx_gtk2u_core-%i.%i"
1458#elif defined(__WXQT__)
1459 "libwx_qtu_core-%i.%i"
1461#error undefined plugin platform
1464 wxMAJOR_VERSION, wxMINOR_VERSION);
1468 size_t len(strlen(strver));
1470 while ((c = fgetc(f)) != EOF) {
1471 if (c == strver[pos]) {
1482 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1498 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1500 if (plugin_file.empty()) {
1501 wxLogMessage(
"Ignoring loading of empty path");
1505 if (!wxIsReadable(plugin_file)) {
1506 wxLogMessage(
"Ignoring unreadable plugin %s",
1507 plugin_file.ToStdString().c_str());
1508 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1509 load_errors.push_back(le);
1516 pic->m_version_major, pic->m_version_minor);
1517 if (sts != plug_status::unblocked) {
1518 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1522 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1523 if (!data.name.empty()) {
1524 wxLogDebug(
"Refusing to load blacklisted library: %s",
1525 plugin_file.ToStdString().c_str());
1533 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1534 pic->m_library.Load(plugin_file);
1536 if (!pic->m_library.IsLoaded()) {
1539 wxFileName fn(plugin_file);
1540 std::string name = fn.GetName().ToStdString();
1541 auto found = m_blacklist->get_library_data(name);
1542 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1543 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1544 if (!found.name.empty()) {
1546 LoadError le(LoadError::Type::Unloadable, name, v);
1547 load_errors.push_back(le);
1549 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1550 load_errors.push_back(le);
1553 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1559 const char*
const FIX_LOADING =
1560 _(
"\n Install/uninstall plugin or remove file to mute message");
1561 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1562 if (
nullptr == create_plugin) {
1563 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1564 wxLogMessage(msg + plugin_file);
1565 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1566 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1567 load_errors.push_back(le);
1572 destroy_t* destroy_plugin =
1573 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1574 pic->m_destroy_fn = destroy_plugin;
1575 if (
nullptr == destroy_plugin) {
1576 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1578 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1579 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1580 load_errors.push_back(le);
1590 int api_ver = (api_major * 100) + api_minor;
1591 pic->m_api_version = api_ver;
1599 wxLogDebug(
"blacklist: Get status for %s %d %d",
1600 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1602 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1603 if (status != plug_status::unblocked) {
1604 wxLogDebug(
"Ignoring blacklisted plugin.");
1605 if (status != plug_status::unloadable) {
1607 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1608 load_errors.push_back(le);
1691 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1692 p->GetPlugInVersionBuild());
1695 if (!pic->m_pplugin) {
1696 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1697 INFO_LOG << _(
" API Version detected: ");
1698 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1699 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1700 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1702 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1704 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 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.
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.