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);
500 return any_dir_loaded;
503bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
505 wxString plugin_file = wxFileName(file_name).GetFullName();
506 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
508 wxString plugin_loadstamp = wxFileName(file_name).GetName();
509 if (!IsSystemPluginPath(plugin_file.ToStdString())) {
510 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
511 MESSAGE_LOG <<
"Refusing to load " << file_name
512 <<
" failed at last attempt";
515 CreateLoadStamp(plugin_loadstamp.ToStdString());
517 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
518 wxLog::FlushActive();
521 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
522 file_name.ToStdString().c_str());
531 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
540 plugin_array.Remove(pic_test);
544 pic_test->m_destroy_fn(pic_test->m_pplugin);
549 loaded_pic = pic_test;
554 loaded_pic = pic_test;
561 ClearLoadStamp(plugin_loadstamp.ToStdString());
566 wxFileName fn_plugin_file(file_name);
567 wxString plugin_file_path =
568 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
570 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
571 base_plugin_path += wxFileName::GetPathSeparator();
575 if (!g_allow_arb_system_plugin) {
577 if (base_plugin_path.IsSameAs(plugin_file_path)) {
578 if (!IsSystemPluginPath(file_name.ToStdString())) {
579 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
582 ClearLoadStamp(plugin_loadstamp.ToStdString());
589 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
590 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
591 ClearLoadStamp(plugin_loadstamp.ToStdString());
596 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
597 wxLogMessage(msg.c_str());
598 wxLog::FlushActive();
604 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
605 wxLogMessage(msg.c_str());
606 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
607 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
608 load_errors.push_back(le);
618 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
620 if (pic && load_enabled && !enabled.Get(
true)) {
621 pic->m_destroy_fn(pic->m_pplugin);
623 wxLogMessage(
"Skipping not enabled candidate.");
624 ClearLoadStamp(plugin_loadstamp.ToStdString());
629 if (pic->m_pplugin) {
630 plugin_array.Add(pic);
637 pic->m_enabled = enabled.Get(
false);
639 if (safe_mode::get_mode() &&
640 !IsSystemPluginPath(file_name.ToStdString())) {
641 pic->m_enabled =
false;
644 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
646 if (pic->m_enabled) {
648 pic->m_init_state =
true;
652 wxLog::FlushActive();
654 std::string found_version;
657 found_version = p.readonly ?
"" : p.version;
666 m_on_activate_cb(pic);
670 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
672 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
673 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
675 if (!pic->m_enabled && pic->m_destroy_fn) {
676 pic->m_destroy_fn(pic->m_pplugin);
677 pic->m_destroy_fn =
nullptr;
678 pic->m_pplugin =
nullptr;
679 pic->m_init_state =
false;
680 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
685 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
687 bool is_system = found != SYSTEM_PLUGINS.end();
693 available.begin(), available.end(),
696 if (it == available.end()) {
701 auto oprhan_metadata = CreateMetadata(pic);
702 auto catalogHdlr = CatalogHandler::GetInstance();
703 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
709 " PluginLoader: Unloading invalid PlugIn, API version %d ",
711 pic->m_destroy_fn(pic->m_pplugin);
713 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
715 load_errors.push_back(le);
721 ClearLoadStamp(plugin_loadstamp.ToStdString());
726bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
729 m_plugin_location = plugin_dir;
731 wxString msg(
"PluginLoader searching for PlugIns in location ");
732 msg += m_plugin_location;
736 wxString pispec =
"*_pi.dll";
737#elif defined(__WXOSX__)
738 wxString pispec =
"*_pi.dylib";
740 wxString pispec =
"*_pi.so";
743 if (!::wxDirExists(m_plugin_location)) {
744 msg = m_plugin_location;
745 msg.Prepend(
" Directory ");
746 msg.Append(
" does not exist.");
751 if (!
g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
753 wxArrayString file_list;
755 int get_flags = wxDIR_FILES | wxDIR_DIRS;
758 get_flags = wxDIR_FILES;
763 get_flags = wxDIR_FILES;
769 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
771 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
772 for (
auto& file_name : file_list) {
773 wxLog::FlushActive();
775 LoadPluginCandidate(file_name, load_enabled);
782 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
784 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
789 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
791 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
798 while ((i >= 0) && (i < plugin_array.GetCount())) {
800 if (pict->m_status == PluginStatus::PendingListRemoval) {
801 plugin_array.RemoveAt(i);
813 for (
const auto& pic : plugin_array) {
816 if (pic->m_pplugin) {
819 pic->m_pplugin =
nullptr;
820 pic->m_init_state =
false;
825 if (!pic->m_pplugin) {
826 if (pic->m_enabled) {
830 pic->m_status = stat;
831 pic->m_enabled =
true;
837 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
838 wxString msg(
"PluginLoader: Initializing PlugIn: ");
845 pic->m_init_state =
true;
846 ProcessLateInit(pic);
852 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
853 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
854 m_on_activate_cb(pic);
856 }
else if (!pic->m_enabled && pic->m_init_state) {
859 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
860 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
863 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
864 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
865 pic->m_pplugin =
nullptr;
866 pic->m_init_state =
false;
875 if (!pic)
return false;
876 if (pic->m_init_state) {
877 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
879 m_on_deactivate_cb(pic);
880 pic->m_init_state =
false;
887 auto pic = GetContainer(pd, plugin_array);
889 wxLogError(
"Attempt to deactivate non-existing plugin %s",
897 if (ix >= plugin_array.GetCount()) {
898 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
905 if (pic->m_pplugin) {
906 pic->m_destroy_fn(pic->m_pplugin);
910 plugin_array.RemoveAt(ix);
914static std::string VersionFromManifest(
const std::string& plugin_name) {
917 if (!path.empty() && wxFileName::IsFileReadable(path)) {
918 std::ifstream stream;
919 stream.open(path, std::ifstream::in);
928 if (name.empty())
return {};
931 if (isRegularFile(import_path.c_str())) {
932 std::ifstream f(import_path.c_str());
933 std::stringstream ss;
936 ParsePlugin(ss.str(), pd);
937 pd.is_imported =
true;
942 vector<PluginMetadata> matches;
943 copy_if(available.begin(), available.end(), back_inserter(matches),
945 if (matches.size() == 0)
return {};
946 if (matches.size() == 1)
return matches[0];
948 auto version = VersionFromManifest(name);
950 return version == md.version;
952 auto found = find_if(matches.begin(), matches.end(), predicate);
953 return found != matches.end() ? *found : matches[0];
959 if (name.empty())
return {};
962 vector<PluginMetadata> matches;
963 copy_if(available.begin(), available.end(), back_inserter(matches),
965 if (matches.size() == 0)
return {};
966 if (matches.size() == 1)
return matches[0];
970 auto rv = matches[0];
971 for (
auto p : matches) {
973 if (catVersion > version) {
974 version = catVersion;
985 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
987 bool is_system = found != SYSTEM_PLUGINS.end();
989 std::string installed = VersionFromManifest(md.name);
996 else if (plugin->m_status == PluginStatus::Imported)
998 else if (installedVersion < metaVersion)
999 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
1000 else if (installedVersion == metaVersion)
1001 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
1003 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
1007 plugin->m_managed_metadata = md;
1011 std::vector<PlugInContainer*> loaded_plugins;
1012 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
1015 for (
auto& p : loaded_plugins) {
1016 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1017 p->m_common_name.Lower().ToStdString());
1018 bool is_system = found != SYSTEM_PLUGINS.end();
1021 if (!keep_orphans) {
1028 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1032 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1033 loaded_plugins.erase(end, loaded_plugins.end());
1037 for (
auto& plugin : loaded_plugins) {
1040 if (!md.name.empty()) {
1042 md.is_imported = isRegularFile(import_path.c_str());
1043 if (md.is_imported) {
1044 plugin->m_status = PluginStatus::Imported;
1048 }
else if (IsSystemPluginName(md.name)) {
1050 }
else if (md.is_orphan) {
1052 }
else if (plugin->m_api_version) {
1055 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1056 plugin->m_managed_metadata = md;
1059 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1064 plugin_array.Clear();
1065 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1071 while (plugin_array.GetCount()) {
1080 for (
auto* pic : plugin_array) {
1088DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1090 PIMAGE_SECTION_HEADER pSeh;
1095 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1096 if (rva >= pSeh->VirtualAddress &&
1097 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1102 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1109 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1110 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1113 uint64_t type_magic;
1114 DependencyMap dependencies;
1118bool ReadModuleInfoFromELF(
const wxString& file,
1119 const ModuleInfo::DependencySet& dependencies,
1121 static bool b_libelf_initialized =
false;
1122 static bool b_libelf_usable =
false;
1124 if (b_libelf_usable) {
1126 }
else if (b_libelf_initialized) {
1128 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1129 b_libelf_initialized =
true;
1130 b_libelf_usable =
false;
1131 wxLogError(
"LibELF is outdated.");
1134 b_libelf_initialized =
true;
1135 b_libelf_usable =
true;
1139 Elf* elf_handle =
nullptr;
1140 GElf_Ehdr elf_file_header;
1141 Elf_Scn* elf_section_handle =
nullptr;
1143 file_handle = open(file, O_RDONLY);
1144 if (file_handle == -1) {
1145 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1147 goto FailureEpilogue;
1150 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1151 if (elf_handle ==
nullptr) {
1152 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1153 goto FailureEpilogue;
1156 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1157 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1158 goto FailureEpilogue;
1161 switch (elf_file_header.e_type) {
1166 wxLogMessage(wxString::Format(
1167 "Module \"%s\" is not an executable or shared library.", file));
1168 goto FailureEpilogue;
1172 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1174 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1176 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1178 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1180 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1184 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1186 GElf_Shdr elf_section_header;
1187 Elf_Data* elf_section_data =
nullptr;
1188 size_t elf_section_entry_count = 0;
1190 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1191 &elf_section_header) {
1192 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1193 goto FailureEpilogue;
1194 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1198 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1199 if (elf_section_data ==
nullptr) {
1200 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1201 goto FailureEpilogue;
1204 if ((elf_section_data->d_size == 0) ||
1205 (elf_section_header.sh_entsize == 0)) {
1206 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1207 goto FailureEpilogue;
1210 elf_section_entry_count =
1211 elf_section_data->d_size / elf_section_header.sh_entsize;
1212 for (
size_t elf_section_entry_index = 0;
1213 elf_section_entry_index < elf_section_entry_count;
1214 ++elf_section_entry_index) {
1215 GElf_Dyn elf_dynamic_entry;
1216 const char* elf_dynamic_entry_name =
nullptr;
1217 if (gelf_getdyn(elf_section_data,
1218 static_cast<int>(elf_section_entry_index),
1219 &elf_dynamic_entry) != &elf_dynamic_entry) {
1220 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1222 goto FailureEpilogue;
1223 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1226 elf_dynamic_entry_name = elf_strptr(
1227 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1228 if (elf_dynamic_entry_name ==
nullptr) {
1229 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1230 "string entry", file));
1231 goto FailureEpilogue;
1233 wxString name_full(elf_dynamic_entry_name);
1234 wxString name_part(elf_dynamic_entry_name,
1235 strcspn(elf_dynamic_entry_name,
"-."));
1236 if (dependencies.find(name_part) != dependencies.end()) {
1237 info.dependencies.insert(
1238 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1243 goto SuccessEpilogue;
1246 elf_end(elf_handle);
1251 if (elf_handle !=
nullptr) elf_end(elf_handle);
1252 if (file_handle >= 0) close(file_handle);
1253 wxLog::FlushActive();
1259 bool b_compat =
false;
1275 if (!m_found_wxwidgets) {
1276 DWORD myPid = GetCurrentProcessId();
1278 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1279 if (hProcess == NULL) {
1280 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1281 plugin_file.c_str()));
1285 HMODULE hMods[1024];
1287 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1288 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1289 TCHAR szModName[MAX_PATH];
1290 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1291 sizeof(szModName) /
sizeof(TCHAR))) {
1292 m_module_name = szModName;
1293 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1294 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1295 m_found_wxwidgets =
true;
1296 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1297 m_module_name.c_str()));
1304 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1305 plugin_file.c_str()));
1307 if (hProcess) CloseHandle(hProcess);
1310 if (!m_found_wxwidgets) {
1311 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1312 plugin_file.c_str()));
1314 LPCWSTR fName = plugin_file.wc_str();
1315 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1316 FILE_ATTRIBUTE_NORMAL, 0);
1317 DWORD byteread, size = GetFileSize(handle, NULL);
1318 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1319 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1320 CloseHandle(handle);
1321 PIMAGE_NT_HEADERS ntheaders =
1322 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1323 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1324 PIMAGE_SECTION_HEADER pSech =
1325 IMAGE_FIRST_SECTION(ntheaders);
1326 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1327 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1332 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1334 ntheaders->OptionalHeader
1336 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1343 while (pImportDescriptor->Name != 0) {
1346 (PCHAR)((DWORD_PTR)virtualpointer +
1347 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1350 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1353 wxLogMessage(wxString::Format(
1354 "Compatible wxWidgets plugin library found for %s: %s",
1355 plugin_file.c_str(), libname[i]));
1358 pImportDescriptor++;
1363 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1365 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1368#if defined(__WXGTK__) || defined(__WXQT__)
1369#if defined(USE_LIBELF)
1371 static bool b_own_info_queried =
false;
1372 static bool b_own_info_usable =
false;
1374 static ModuleInfo::DependencySet dependencies;
1376 if (!b_own_info_queried) {
1377 dependencies.insert(
"libwx_baseu");
1379 char exe_buf[100] = {0};
1380 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1382 exe_buf[len] =
'\0';
1383 wxString app_path(exe_buf);
1384 wxLogMessage(
"Executable path: %s", exe_buf);
1386 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1387 if (!b_own_info_usable) {
1388 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1391 wxLogMessage(
"Cannot get own executable path.");
1393 b_own_info_queried =
true;
1396 if (b_own_info_usable) {
1397 bool b_pi_info_usable =
false;
1400 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1401 if (b_pi_info_usable) {
1402 b_compat = (pi_info.type_magic == own_info.type_magic);
1405 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1410 pi_info.dependencies.clear();
1412 wxString::Format(
" Plugin \"%s\" is of another binary "
1413 "flavor than the main module.",
1415 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1416 own_info.type_magic, pi_info.type_magic);
1418 for (
const auto& own_dependency : own_info.dependencies) {
1419 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1420 pi_info.dependencies.find(own_dependency.first);
1421 if ((pi_dependency != pi_info.dependencies.end()) &&
1422 (pi_dependency->second != own_dependency.second)) {
1425 " Plugin \"%s\" depends on library \"%s\", but the main "
1426 "module was built for \"%s\".",
1427 plugin_file, pi_dependency->second, own_dependency.second);
1434 wxString::Format(
" Plugin \"%s\" could not be reliably "
1435 "checked for compatibility.",
1443 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1444 b_compat ?
"true" :
"false");
1446 wxLog::FlushActive();
1461 FILE* f = fopen(plugin_file,
"r");
1464 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1468#
if defined(__WXGTK3__)
1469 "libwx_gtk3u_core-%i.%i"
1470#elif defined(__WXGTK20__)
1471 "libwx_gtk2u_core-%i.%i"
1472#elif defined(__WXQT__)
1473 "libwx_qtu_core-%i.%i"
1475#error undefined plugin platform
1478 wxMAJOR_VERSION, wxMINOR_VERSION);
1482 size_t len(strlen(strver));
1484 while ((c = fgetc(f)) != EOF) {
1485 if (c == strver[pos]) {
1496 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1512 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1514 if (plugin_file.empty()) {
1515 wxLogMessage(
"Ignoring loading of empty path");
1519 if (!wxIsReadable(plugin_file)) {
1520 wxLogMessage(
"Ignoring unreadable plugin %s",
1521 plugin_file.ToStdString().c_str());
1522 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1523 load_errors.push_back(le);
1530 pic->m_version_major, pic->m_version_minor);
1531 if (sts != plug_status::unblocked) {
1532 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1536 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1537 if (!data.name.empty()) {
1538 wxLogDebug(
"Refusing to load blacklisted library: %s",
1539 plugin_file.ToStdString().c_str());
1547 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1548 pic->m_library.Load(plugin_file);
1550 if (!pic->m_library.IsLoaded()) {
1553 wxFileName fn(plugin_file);
1554 std::string name = fn.GetName().ToStdString();
1555 auto found = m_blacklist->get_library_data(name);
1556 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1557 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1558 if (!found.name.empty()) {
1560 LoadError le(LoadError::Type::Unloadable, name, v);
1561 load_errors.push_back(le);
1563 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1564 load_errors.push_back(le);
1567 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1573 const char*
const FIX_LOADING =
1574 _(
"\n Install/uninstall plugin or remove file to mute message");
1575 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1576 if (
nullptr == create_plugin) {
1577 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1578 wxLogMessage(msg + plugin_file);
1579 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1580 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1581 load_errors.push_back(le);
1586 destroy_t* destroy_plugin =
1587 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1588 pic->m_destroy_fn = destroy_plugin;
1589 if (
nullptr == destroy_plugin) {
1590 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1592 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1593 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1594 load_errors.push_back(le);
1604 int api_ver = (api_major * 100) + api_minor;
1605 pic->m_api_version = api_ver;
1613 wxLogDebug(
"blacklist: Get status for %s %d %d",
1614 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1616 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1617 if (status != plug_status::unblocked) {
1618 wxLogDebug(
"Ignoring blacklisted plugin.");
1619 if (status != plug_status::unloadable) {
1621 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1622 load_errors.push_back(le);
1705 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1706 p->GetPlugInVersionBuild());
1709 if (!pic->m_pplugin) {
1710 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1711 INFO_LOG << _(
" API Version detected: ");
1712 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1713 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1714 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1716 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1718 load_errors.push_back(le);
Plugin catalog management: Build the runtime catalog, handling downloads as required.
Datatypes and methods to parse ocpn-plugins.xml XML data, either complete catalog or a single plugin.
Wrapper for configuration variables which lives in a wxBaseConfig object.
void Notify() override
Notify all listeners, no data supplied.
Error condition when loading a plugin.
Data for a loaded plugin, including dl-loaded library.
Basic data for a loaded plugin, trivially copyable.
wxString m_plugin_filename
The short file path.
wxString m_plugin_file
The full file path.
int m_cap_flag
PlugIn Capabilities descriptor.
PlugInData(const PluginMetadata &md)
Create a container with applicable fields defined from metadata.
wxString m_common_name
A common name string for the plugin.
bool m_has_setup_options
Has run NotifySetupOptionsPlugin()
std::string Key() const
sort key.
std::string m_manifest_version
As detected from manifest.
wxDateTime m_plugin_modification
used to detect upgraded plugins
wxString m_version_str
Complete version as of semantic_vers.
Handle plugin install from remote repositories and local operations to Uninstall and list plugins.
std::vector< PluginMetadata > getCompatiblePlugins()
Return list of available, unique and compatible plugins from configured XML catalog.
static std::string ImportedMetadataPath(std::string name)
Return path to imported metadata for given plugin.
static std::string VersionPath(std::string name)
Return path to file containing version for given plugin.
static std::string FileListPath(std::string name)
Return path to installation manifest for given plugin.
static PluginHandler * GetInstance()
Singleton factory.
PluginLoader is a backend module without any direct GUI functionality.
bool LoadAllPlugIns(bool enabled_plugins, bool keep_orphans=false)
Update catalog with imported metadata and load all plugin library files.
bool IsPlugInAvailable(const wxString &commonName)
Return true if a plugin with given name exists in GetPlugInArray()
static void MarkAsLoadable(const std::string &library_path)
Mark a library file (complete path) as loadable i.
static PluginMetadata LatestMetadataByName(const std::string &name)
Find highest versioned metadata for given plugin.
EventVar evt_load_plugin
Notified with a PlugInContainer* pointer when a plugin is loaded.
void UpdateManagedPlugins(bool keep_orphans)
Update all managed plugins i.
EventVar evt_pluglist_change
Notified without data when the GetPlugInArray() list is changed.
EventVar evt_plugin_loadall_finalize
Emitted after all plugins are loaded.
static std::string GetPluginVersion(const PlugInData pd, std::function< const PluginMetadata(const std::string &)> get_metadata)
Return version string for a plugin, possibly with an "Imported" suffix.
bool UnLoadPlugIn(size_t ix)
Unload, delete and remove item ix in GetPlugInArray().
void SortPlugins(int(*cmp_func)(PlugInContainer **, PlugInContainer **))
Sort GetPluginArray().
static void UpdatePlugin(PlugInContainer *plugin, const PluginMetadata &md)
Update PlugInContainer status using data from PluginMetadata and manifest.
void SetSetupOptions(const wxString &common_name, bool value)
Update m_has_setup_options state for plugin with given name.
EventVar evt_load_directory
Notified without data when loader starts loading from a new directory.
void SetEnabled(const wxString &common_name, bool enabled)
Update enabled/disabled state for plugin with given name.
void SetToolboxPanel(const wxString &common_name, bool value)
Update m_toolbox_panel state for plugin with given name.
static PluginMetadata MetadataByName(const std::string &name)
Find metadata for given plugin.
bool DeactivatePlugIn(PlugInContainer *pic)
Deactivate given plugin.
bool UnLoadAllPlugIns()
Unload allplugins i.
void RemovePlugin(const PlugInData &pd)
Remove a plugin from *GetPluginArray().
bool UpdatePlugIns()
Update the GetPlugInArray() list by reloading all plugins from disk.
void ShowPreferencesDialog(const PlugInData &pd, wxWindow *parent)
Display the preferences dialog for a plugin.
bool CheckPluginCompatibility(const wxString &plugin_file)
Check plugin compatibiliet w r t library type.
const ArrayOfPlugIns * GetPlugInArray()
Return list of currently loaded plugins.
bool DeactivateAllPlugIns()
Deactivate all plugins.
PlugInContainer * LoadPlugIn(const wxString &plugin_file)
Load given plugin file from disk into GetPlugInArray() list.
EventVar evt_update_chart_types
Notified without data after all plugins loaded ot updated.
std::vector< std::string > Libdirs()
List of directories from which we load plugins.
static PluginPaths * GetInstance()
Return the singleton instance.
std::vector< std::string > Bindirs()
'List of directories for plugin binary helpers.
virtual void OnSetupOptions(void)
Allows plugin to add pages to global Options dialog.
Base class for OpenCPN plugins.
virtual void ShowPreferencesDialog(wxWindow *parent)
Shows the plugin preferences dialog.
virtual wxBitmap * GetPlugInBitmap()
Get the plugin's icon bitmap.
virtual int Init(void)
Initialize the plugin and declare its capabilities.
virtual bool DeInit(void)
Clean up plugin resources.
virtual void SetDefaults(void)
Sets plugin default options.
virtual wxString GetShortDescription()
Get a brief description of the plugin.
virtual wxString GetCommonName()
Get the plugin's common (short) name.
virtual int GetPlugInVersionMajor()
Returns the major version number of the plugin itself.
virtual int GetAPIVersionMinor()
Returns the minor version number of the plugin API that this plugin supports.
virtual int GetAPIVersionMajor()
Returns the major version number of the plugin API that this plugin supports.
virtual wxString GetLongDescription()
Get detailed plugin information.
virtual int GetPlugInVersionMinor()
Returns the minor version number of the plugin itself.
Global variables reflecting command line options and arguments.
Global variables stored in configuration file.
Enhanced logging interface on top of wx/log.h.
std::string tolower(const std::string &input)
Return copy of s with all characters converted to lower case.
bool exists(const std::string &name)
std::string join(std::vector< std::string > v, char c)
Return a single string being the concatenation of all elements in v with character c in between.
void mkdir(const std::string path)
Notify()/Listen() configuration variable wrapper.
#define WANTS_LATE_INIT
Delay full plugin initialization until system is ready.
#define INSTALLS_TOOLBOX_PAGE
Plugin will add pages to the toolbox/settings dialog.
Miscellaneous utilities, many of which string related.
Plugin blacklist for plugins which can or should not be loaded.
Downloaded plugins cache.
Plugin remote repositories installation and Uninstall/list operations.
Low level code to load plugins from disk, notably the PluginLoader class.
@ Unmanaged
Unmanaged, probably a package.
@ Managed
Managed by installer.
@ System
One of the four system plugins, unmanaged.
Plugin installation and data paths support.
std::vector< const PlugInData * > GetInstalled()
Return sorted list of all installed plugins.
Safe mode non-gui handling.
Semantic version encode/decode object.
Versions uses a modified semantic versioning scheme: major.minor.revision.post-tag+build.
static SemanticVersion parse(std::string s)
Parse a version string, sets major == -1 on errors.
std::string to_string()
Return printable representation.