40 #if defined(__linux__) && !defined(__ANDROID__)
49 #include <wx/bitmap.h>
52 #include <wx/hashset.h>
53 #include <wx/filename.h>
54 #include <wx/string.h>
55 #include <wx/tokenzr.h>
56 #include <wx/window.h>
57 #include <wx/process.h>
59 #include "model/base_platform.h"
60 #include "model/catalog_handler.h"
61 #include "model/catalog_parser.h"
62 #include "model/config_vars.h"
64 #include "model/config_vars.h"
65 #include "model/logger.h"
66 #include "model/ocpn_utils.h"
67 #include "model/plugin_blacklist.h"
68 #include "model/plugin_cache.h"
69 #include "model/plugin_handler.h"
70 #include "model/plugin_loader.h"
71 #include "model/plugin_paths.h"
72 #include "model/safe_mode.h"
73 #include "observable_confvar.h"
77 #include "androidUTIL.h"
85 static const std::vector<std::string> SYSTEM_PLUGINS = {
86 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
90 const ArrayOfPlugIns& plugin_array) {
91 for (
size_t i = 0; i < plugin_array.GetCount(); i++) {
92 const auto& p = plugin_array.Item(i);
99 static 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"};
103 const std::string lc_path = ocpn::tolower(path);
104 for (
const auto& p : kPlugins)
105 if (lc_path.find(p) != std::string::npos)
return true;
110 static bool IsSystemPluginName(
const std::string& name) {
111 static const std::vector<std::string> kPlugins = {
112 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
114 std::find(kPlugins.begin(), kPlugins.end(), ocpn::tolower(name));
115 return found != kPlugins.end();
119 static std::string GetInstalledVersion(
const PlugInData& pd) {
122 if (path ==
"" || !wxFileName::IsFileReadable(path)) {
123 auto loader = PluginLoader::getInstance();
124 auto pic = GetContainer(pd, *loader->GetPlugInArray());
125 if (!pic || !pic->m_pplugin) {
128 int v_major = pic->m_pplugin->GetPlugInVersionMajor();
129 int v_minor = pic->m_pplugin->GetPlugInVersionMinor();
132 std::ifstream stream;
134 stream.open(path, std::ifstream::in);
140 auto catalogHdlr = CatalogHandler::getInstance();
144 SemanticVersion orphanVersion(pic->m_version_major, pic->m_version_minor);
145 mdata.version = orphanVersion.to_string();
146 mdata.summary = pic->m_short_description;
147 mdata.description = pic->m_long_description;
149 mdata.target =
"all";
150 mdata.is_orphan =
true;
157 std::function<
const PluginMetadata(
const std::string&)> get_metadata) {
158 auto loader = PluginLoader::getInstance();
159 auto pic = GetContainer(pd, *loader->GetPlugInArray());
165 metadata = pic->m_managed_metadata;
166 if (metadata.version ==
"")
168 std::string detail_suffix(metadata.is_imported ? _(
" [Imported]") :
"");
169 if (metadata.is_orphan)
170 detail_suffix = _(
" [Orphan]");
174 if (pic->m_pplugin) {
175 v_major = pic->m_pplugin->GetPlugInVersionMajor();
176 v_minor = pic->m_pplugin->GetPlugInVersionMinor();
182 v_major, v_minor, p->GetPlugInVersionPatch(), p->GetPlugInVersionPost(),
183 p->GetPlugInVersionPre(), p->GetPlugInVersionBuild());
184 return sv.to_string() + detail_suffix;
186 if (!metadata.is_orphan) {
187 std::string version = GetInstalledVersion(pd);
188 return version + detail_suffix;
191 return metadata.version + detail_suffix;
195 PlugInContainer::PlugInContainer()
196 :
PlugInData(), m_pplugin(nullptr), m_library(), m_destroy_fn(nullptr) {}
199 : m_has_setup_options(false),
202 m_toolbox_panel(false),
207 m_status(PluginStatus::Unknown) {}
212 m_version_major = v.major;
213 m_version_minor = v.minor;
214 m_managed_metadata = md;
215 m_status = PluginStatus::ManagedInstallAvailable;
220 return std::string(m_status == PluginStatus::Managed ?
"1" :
"0") +
239 static void setLoadPath() {
242 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
244 if (osSystemId & wxOS_UNIX_LINUX) {
245 string path = ocpn::join(dirs,
':');
247 if (wxGetEnv(
"LD_LIBRARY_PATH", &envPath)) {
248 path = path +
":" + envPath.ToStdString();
250 wxLogMessage(
"Using LD_LIBRARY_PATH: %s", path.c_str());
251 wxSetEnv(
"LD_LIBRARY_PATH", path.c_str());
252 }
else if (osSystemId & wxOS_WINDOWS) {
254 string path = ocpn::join(dirs,
';');
256 if (wxGetEnv(
"PATH", &envPath)) {
257 path = path +
";" + envPath.ToStdString();
259 wxLogMessage(
"Using PATH: %s", path);
260 wxSetEnv(
"PATH", path);
261 }
else if (osSystemId & wxOS_MAC) {
262 string path = ocpn::join(dirs,
':');
264 if (wxGetEnv(
"DYLD_LIBRARY_PATH", &envPath)) {
265 path = path +
":" + envPath.ToStdString();
267 wxLogMessage(
"Using DYLD_LIBRARY_PATH: %s", path.c_str());
268 wxSetEnv(
"DYLD_LIBRARY_PATH", path.c_str());
270 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
271 if (os_name.Contains(
"wxQT")) {
272 wxLogMessage(
"setLoadPath() using Android library path");
274 wxLogWarning(
"SetLoadPath: Unsupported platform.");
276 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
278 string path = ocpn::join(dirs,
':');
280 wxGetEnv(
"PATH", &envPath);
281 path = path +
":" + envPath.ToStdString();
282 wxLogMessage(
"Using PATH: %s", path);
283 wxSetEnv(
"PATH", path);
289 wxString msg(
"PluginLoader: Calling LateInit PlugIn: ");
294 if (ppi) ppi->LateInit();
305 PluginLoader::PluginLoader()
306 : m_blacklist(blacklist_factory()),
307 m_default_plugin_icon(nullptr),
309 m_found_wxwidgets(false),
313 bool PluginLoader::IsPlugInAvailable(
const wxString& commonName) {
314 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
316 if (pic && pic->m_enabled && (pic->
m_common_name == commonName))
324 auto loader = PluginLoader::getInstance();
325 auto pic = GetContainer(pd, *loader->GetPlugInArray());
326 if (pic) pic->m_pplugin->ShowPreferencesDialog(parent);
329 void PluginLoader::NotifySetupOptionsPlugin(
const PlugInData* pd) {
330 auto pic = GetContainer(*pd, *GetPlugInArray());
334 if (pic->m_enabled && pic->m_init_state) {
335 if (pic->
m_cap_flag & INSTALLS_TOOLBOX_PAGE) {
336 switch (pic->m_api_version) {
347 if (pic->m_pplugin) {
350 ppi->OnSetupOptions();
351 auto loader = PluginLoader::getInstance();
365 for (
size_t i = 0; i < plugin_array.GetCount(); i++) {
368 pic->m_enabled = enabled;
375 for (
size_t i = 0; i < plugin_array.GetCount(); i++) {
378 pic->m_toolbox_panel = value;
382 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
386 const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
387 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
388 return m_default_plugin_icon;
391 void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
392 delete m_default_plugin_icon;
393 m_default_plugin_icon = bitmap;
397 auto pic = GetContainer(pd, plugin_array);
399 wxLogMessage(
"Attempt to remove non-existing plugin %s",
403 plugin_array.Remove(pic);
407 return (*p1)->Key().compare((*p2)->Key());
412 plugin_array.Sort(ComparePlugins);
418 static const wxString sep = wxFileName::GetPathSeparator();
420 wxLogMessage(
"PluginLoader: loading plugins from %s", ocpn::join(dirs,
';'));
422 bool any_dir_loaded =
false;
423 for (
const auto& dir : dirs) {
425 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
426 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
432 if (!load_enabled) UpdateManagedPlugins(keep_orphans);
435 evt_update_chart_types.Notify();
436 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
440 return any_dir_loaded;
443 bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
445 wxString plugin_file = wxFileName(file_name).GetFullName();
446 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
447 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
448 wxLog::FlushActive();
456 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
466 plugin_array.Remove(pic_test);
469 DeactivatePlugIn(pic_test);
470 pic_test->m_destroy_fn(pic_test->m_pplugin);
475 loaded_pic = pic_test;
480 loaded_pic = pic_test;
486 if (loaded)
return true;
488 wxFileName fn_plugin_file(file_name);
489 wxString plugin_file_path =
490 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
491 wxString base_plugin_path = g_BasePlatform->
GetPluginDir();
492 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
493 base_plugin_path += wxFileName::GetPathSeparator();
496 if (base_plugin_path.IsSameAs(plugin_file_path)) {
497 if (!IsSystemPluginPath(file_name.ToStdString())) {
498 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
505 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
506 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
511 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
512 wxLogMessage(msg.c_str());
513 wxLog::FlushActive();
515 bool b_compat = CheckPluginCompatibility(file_name);
519 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
520 wxLogMessage(msg.c_str());
521 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
522 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
523 load_errors.push_back(le);
533 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
535 if (load_enabled && !enabled.Get(
true)) {
536 pic->m_destroy_fn(pic->m_pplugin);
538 wxLogMessage(
"Skipping not enabled candidate.");
543 if (pic->m_pplugin) {
544 plugin_array.Add(pic);
551 pic->m_enabled = enabled.Get(
false);
553 if (safe_mode::get_mode()) {
554 pic->m_enabled =
false;
557 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
559 if (pic->m_enabled) {
561 pic->m_init_state =
true;
564 evt_load_plugin.
Notify(pic);
565 wxLog::FlushActive();
567 std::string found_version;
568 for (
const auto& p : PluginHandler::getInstance()->getInstalled()) {
570 found_version = p.readonly ?
"" : p.version;
575 pic->m_short_description = pic->m_pplugin->GetShortDescription();
576 pic->m_long_description = pic->m_pplugin->GetLongDescription();
577 pic->m_version_major = pic->m_pplugin->GetPlugInVersionMajor();
578 pic->m_version_minor = pic->m_pplugin->GetPlugInVersionMinor();
582 pbm0 = (wxBitmap *)GetPluginDefaultIcon();
584 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
585 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
587 if (!pic->m_enabled && pic->m_destroy_fn) {
588 pic->m_destroy_fn(pic->m_pplugin);
589 pic->m_destroy_fn =
nullptr;
590 pic->m_pplugin =
nullptr;
591 pic->m_init_state =
false;
592 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
597 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
599 bool is_system = found != SYSTEM_PLUGINS.end();
604 auto it = find_if(available.begin(), available.end(),
607 if (it == available.end()) {
612 auto oprhan_metadata = CreateMetadata(pic);
613 auto catalogHdlr = CatalogHandler::getInstance();
614 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
620 " PluginLoader: Unloading invalid PlugIn, API version %d ",
622 pic->m_destroy_fn(pic->m_pplugin);
624 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
626 load_errors.push_back(le);
636 bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
638 evt_load_directory.
Notify();
639 m_plugin_location = plugin_dir;
641 wxString msg(
"PluginLoader searching for PlugIns in location ");
642 msg += m_plugin_location;
646 wxString pispec =
"*_pi.dll";
647 #elif defined(__WXOSX__)
648 wxString pispec =
"*_pi.dylib";
650 wxString pispec =
"*_pi.so";
653 if (!::wxDirExists(m_plugin_location)) {
654 msg = m_plugin_location;
655 msg.Prepend(
" Directory ");
656 msg.Append(
" does not exist.");
661 if (!g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
663 wxArrayString file_list;
665 int get_flags = wxDIR_FILES | wxDIR_DIRS;
668 get_flags = wxDIR_FILES;
673 get_flags = wxDIR_FILES;
679 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
681 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
682 for (
unsigned int i = 0; i < file_list.GetCount(); i++) {
683 wxLog::FlushActive();
685 wxString file_name = file_list[i];
687 LoadPluginCandidate(file_name, load_enabled);
694 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
696 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
701 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
703 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
710 while ((i >= 0) && (i < plugin_array.GetCount())) {
712 if (pict->m_status == PluginStatus::PendingListRemoval) {
713 plugin_array.RemoveAt(i);
722 bool PluginLoader::UpdatePlugIns() {
725 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
730 if (pic->m_pplugin) {
733 pic->m_pplugin =
nullptr;
734 pic->m_init_state =
false;
739 if (!pic->m_pplugin) {
740 if (pic->m_enabled) {
741 PluginStatus stat = pic->m_status;
744 pic->m_status = stat;
745 pic->m_enabled =
true;
751 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
752 wxString msg(
"PluginLoader: Initializing PlugIn: ");
758 pic->m_pplugin->SetDefaults();
759 pic->m_init_state =
true;
760 ProcessLateInit(pic);
761 pic->m_short_description = pic->m_pplugin->GetShortDescription();
762 pic->m_long_description = pic->m_pplugin->GetLongDescription();
763 pic->m_version_major = pic->m_pplugin->GetPlugInVersionMajor();
764 pic->m_version_minor = pic->m_pplugin->GetPlugInVersionMinor();
766 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
767 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
769 }
else if (!pic->m_enabled && pic->m_init_state) {
772 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
773 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
775 bret = DeactivatePlugIn(pic);
776 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
777 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
778 pic->m_pplugin =
nullptr;
779 pic->m_init_state =
false;
782 evt_update_chart_types.
Notify();
787 if (!pic)
return false;
788 if (pic->m_init_state) {
789 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
791 m_on_deactivate_cb(pic);
792 pic->m_init_state =
false;
793 pic->m_pplugin->DeInit();
798 bool PluginLoader::DeactivatePlugIn(
const PlugInData& pd) {
799 auto pic = GetContainer(pd, plugin_array);
801 wxLogError(
"Attempt to deactivate non-existing plugin %s",
805 return DeactivatePlugIn(pic);
809 if (ix >= plugin_array.GetCount()) {
810 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
814 if (!DeactivatePlugIn(pic)) {
817 if (pic->m_pplugin) {
818 pic->m_destroy_fn(pic->m_pplugin);
822 plugin_array.RemoveAt(ix);
826 static std::string VersionFromManifest(
const std::string& plugin_name) {
829 if (!path.empty() && wxFileName::IsFileReadable(path)) {
830 std::ifstream stream;
831 stream.open(path, std::ifstream::in);
840 if (name.empty())
return {};
843 if (isRegularFile(import_path.c_str())) {
844 std::ifstream f(import_path.c_str());
845 std::stringstream ss;
848 ParsePlugin(ss.str(), pd);
852 vector<PluginMetadata> matches;
853 copy_if(available.begin(), available.end(), back_inserter(matches),
855 if (matches.size() == 0)
return {};
856 if (matches.size() == 1)
return matches[0];
858 auto version = VersionFromManifest(name);
860 [version](
const PluginMetadata& md) {
return version == md.version; };
861 auto found = find_if(matches.begin(), matches.end(), predicate);
862 return found != matches.end() ? *found : matches[0];
868 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
870 bool is_system = found != SYSTEM_PLUGINS.end();
872 std::string installed = VersionFromManifest(md.name);
878 plugin->m_status = PluginStatus::System;
879 else if (installedVersion < metaVersion)
880 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
881 else if (installedVersion == metaVersion)
882 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
884 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
886 if (!is_system && md.is_orphan)
887 plugin->m_status = PluginStatus::Unmanaged;
890 plugin->m_managed_metadata = md;
893 void PluginLoader::UpdateManagedPlugins(
bool keep_orphans) {
894 std::vector<PlugInContainer*> loaded_plugins;
895 for (
size_t i = 0; i < plugin_array.GetCount(); i++)
896 loaded_plugins.push_back(plugin_array.Item(i));
899 for (
auto& p : loaded_plugins) {
900 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
901 p->m_common_name.Lower().ToStdString());
902 bool is_system = found != SYSTEM_PLUGINS.end();
903 p->m_status = is_system ? PluginStatus::System : PluginStatus::Unmanaged;
912 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
916 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
917 loaded_plugins.erase(end, loaded_plugins.end());
921 for (
auto& plugin : loaded_plugins) {
923 if (!md.name.empty()) {
925 md.is_imported = isRegularFile(import_path.c_str());
929 }
else if (IsSystemPluginName(md.name)) {
930 plugin->m_status = PluginStatus::System;
931 }
else if (md.is_orphan) {
932 plugin->m_status = PluginStatus::Unmanaged;
933 }
else if (plugin->m_api_version) {
936 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
937 plugin->m_managed_metadata = md;
940 plugin->m_status = PluginStatus::ManagedInstallAvailable;
945 plugin_array.Clear();
946 for (
const auto& p : loaded_plugins)
948 evt_pluglist_change.
Notify();
951 bool PluginLoader::UnLoadAllPlugIns() {
953 while (plugin_array.GetCount()) {
961 bool PluginLoader::DeactivateAllPlugIns() {
962 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
964 if (pic && pic->m_enabled && pic->m_init_state) DeactivatePlugIn(pic);
971 DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
973 PIMAGE_SECTION_HEADER pSeh;
978 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
979 if (rva >= pSeh->VirtualAddress &&
980 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
985 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
992 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
993 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
997 DependencyMap dependencies;
1001 bool ReadModuleInfoFromELF(
const wxString& file,
1002 const ModuleInfo::DependencySet& dependencies,
1004 static bool b_libelf_initialized =
false;
1005 static bool b_libelf_usable =
false;
1007 if (b_libelf_usable) {
1009 }
else if (b_libelf_initialized) {
1011 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1012 b_libelf_initialized =
true;
1013 b_libelf_usable =
false;
1014 wxLogError(
"LibELF is outdated.");
1017 b_libelf_initialized =
true;
1018 b_libelf_usable =
true;
1022 Elf* elf_handle =
nullptr;
1023 GElf_Ehdr elf_file_header;
1024 Elf_Scn* elf_section_handle =
nullptr;
1026 file_handle = open(file, O_RDONLY);
1027 if (file_handle == -1) {
1028 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1030 goto FailureEpilogue;
1033 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1034 if (elf_handle ==
nullptr) {
1035 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1036 goto FailureEpilogue;
1039 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1040 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1041 goto FailureEpilogue;
1044 switch (elf_file_header.e_type) {
1049 wxLogMessage(wxString::Format(
1050 "Module \"%s\" is not an executable or shared library.", file));
1051 goto FailureEpilogue;
1055 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1057 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1059 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1061 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1063 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1067 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1069 GElf_Shdr elf_section_header;
1070 Elf_Data* elf_section_data =
nullptr;
1071 size_t elf_section_entry_count = 0;
1073 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1074 &elf_section_header) {
1075 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1076 goto FailureEpilogue;
1077 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1081 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1082 if (elf_section_data ==
nullptr) {
1083 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1084 goto FailureEpilogue;
1087 if ((elf_section_data->d_size == 0) ||
1088 (elf_section_header.sh_entsize == 0)) {
1089 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1090 goto FailureEpilogue;
1093 elf_section_entry_count =
1094 elf_section_data->d_size / elf_section_header.sh_entsize;
1095 for (
size_t elf_section_entry_index = 0;
1096 elf_section_entry_index < elf_section_entry_count;
1097 ++elf_section_entry_index) {
1098 GElf_Dyn elf_dynamic_entry;
1099 const char* elf_dynamic_entry_name =
nullptr;
1100 if (gelf_getdyn(elf_section_data,
1101 static_cast<int>(elf_section_entry_index),
1102 &elf_dynamic_entry) != &elf_dynamic_entry) {
1103 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1105 goto FailureEpilogue;
1106 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1109 elf_dynamic_entry_name = elf_strptr(
1110 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1111 if (elf_dynamic_entry_name ==
nullptr) {
1112 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1113 "string entry", file));
1114 goto FailureEpilogue;
1116 wxString name_full(elf_dynamic_entry_name);
1117 wxString name_part(elf_dynamic_entry_name,
1118 strcspn(elf_dynamic_entry_name,
"-."));
1119 if (dependencies.find(name_part) != dependencies.end()) {
1120 info.dependencies.insert(
1121 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1126 goto SuccessEpilogue;
1129 elf_end(elf_handle);
1134 if (elf_handle !=
nullptr) elf_end(elf_handle);
1135 if (file_handle >= 0) close(file_handle);
1136 wxLog::FlushActive();
1141 bool PluginLoader::CheckPluginCompatibility(
const wxString& plugin_file) {
1142 bool b_compat =
false;
1156 if (!m_found_wxwidgets) {
1157 DWORD myPid = GetCurrentProcessId();
1159 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1160 if (hProcess == NULL) {
1161 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1162 plugin_file.c_str()));
1166 HMODULE hMods[1024];
1168 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1169 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1170 TCHAR szModName[MAX_PATH];
1171 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1172 sizeof(szModName) /
sizeof(TCHAR))) {
1173 m_module_name = szModName;
1174 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1175 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1176 m_found_wxwidgets =
true;
1177 wxLogMessage(wxString::Format(
1178 "Found wxWidgets core DLL: %s",
1179 m_module_name.c_str()));
1186 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1187 plugin_file.c_str()));
1189 if (hProcess) CloseHandle(hProcess);
1192 if (!m_found_wxwidgets) {
1193 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1194 plugin_file.c_str()));
1196 LPCWSTR fName = plugin_file.wc_str();
1197 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1198 FILE_ATTRIBUTE_NORMAL, 0);
1199 DWORD byteread, size = GetFileSize(handle, NULL);
1200 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1201 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1202 CloseHandle(handle);
1203 PIMAGE_NT_HEADERS ntheaders =
1204 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1205 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1206 PIMAGE_SECTION_HEADER pSech =
1207 IMAGE_FIRST_SECTION(ntheaders);
1208 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1209 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1214 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1216 ntheaders->OptionalHeader
1218 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1224 while (pImportDescriptor->Name != 0) {
1227 (PCHAR)((DWORD_PTR)virtualpointer +
1228 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1230 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1234 wxString::Format(
"Compatible wxWidgets plugin library found for %s: %s",
1235 plugin_file.c_str(), libname[i]));
1238 pImportDescriptor++;
1243 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1245 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1248 #if defined(__WXGTK__) || defined(__WXQT__)
1249 #if defined(USE_LIBELF)
1251 static bool b_own_info_queried =
false;
1252 static bool b_own_info_usable =
false;
1254 static ModuleInfo::DependencySet dependencies;
1256 if (!b_own_info_queried) {
1257 dependencies.insert(
"libwx_baseu");
1259 char exe_buf[100] = {0};
1260 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1262 exe_buf[len] =
'\0';
1263 wxString app_path(exe_buf);
1264 wxLogMessage(
"Executable path: %s", exe_buf);
1266 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1267 if (!b_own_info_usable) {
1268 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1271 wxLogMessage(
"Cannot get own executable path.");
1273 b_own_info_queried =
true;
1276 if (b_own_info_usable) {
1277 bool b_pi_info_usable =
false;
1280 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1281 if (b_pi_info_usable) {
1282 b_compat = (pi_info.type_magic == own_info.type_magic);
1285 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1290 pi_info.dependencies.clear();
1292 wxString::Format(
" Plugin \"%s\" is of another binary "
1293 "flavor than the main module.",
1295 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1296 own_info.type_magic, pi_info.type_magic);
1298 for (
const auto& own_dependency : own_info.dependencies) {
1299 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1300 pi_info.dependencies.find(own_dependency.first);
1301 if ((pi_dependency != pi_info.dependencies.end()) &&
1302 (pi_dependency->second != own_dependency.second)) {
1305 " Plugin \"%s\" depends on library \"%s\", but the main "
1306 "module was built for \"%s\".",
1307 plugin_file, pi_dependency->second, own_dependency.second);
1314 wxString::Format(
" Plugin \"%s\" could not be reliably "
1315 "checked for compatibility.",
1323 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1324 b_compat ?
"true" :
"false");
1326 wxLog::FlushActive();
1341 FILE* f = fopen(plugin_file,
"r");
1344 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1348 #
if defined(__WXGTK3__)
1349 "libwx_gtk3u_core-%i.%i"
1350 #elif defined(__WXGTK20__)
1351 "libwx_gtk2u_core-%i.%i"
1352 #elif defined(__WXQT__)
1353 "libwx_qtu_core-%i.%i"
1355 #error undefined plugin platform
1358 wxMAJOR_VERSION, wxMINOR_VERSION);
1362 size_t len(strlen(strver));
1364 while ((c = fgetc(f)) != EOF) {
1365 if (c == strver[pos]) {
1376 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1380 PlugInContainer* PluginLoader::LoadPlugIn(
const wxString& plugin_file) {
1382 if (!LoadPlugIn(plugin_file, pic)) {
1390 PlugInContainer* PluginLoader::LoadPlugIn(
const wxString& plugin_file,
1392 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1394 if (plugin_file.empty()) {
1395 wxLogMessage(
"Ignoring loading of empty path");
1399 if (!wxIsReadable(plugin_file)) {
1400 wxLogMessage(
"Ignoring unreadable plugin %s",
1401 plugin_file.ToStdString().c_str());
1402 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1403 load_errors.push_back(le);
1410 pic->m_version_major, pic->m_version_minor);
1411 if (sts != plug_status::unblocked) {
1412 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1416 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1417 if (!data.name.empty()) {
1418 wxLogDebug(
"Refusing to load blacklisted library: %s",
1419 plugin_file.ToStdString().c_str());
1424 PluginStatus::Unmanaged;
1427 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1428 pic->m_library.Load(plugin_file);
1430 if (!pic->m_library.IsLoaded()) {
1433 wxFileName fn(plugin_file);
1434 std::string name = fn.GetName().ToStdString();
1435 auto found = m_blacklist->get_library_data(name);
1436 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1437 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1438 if (!found.name.empty()) {
1440 LoadError le(LoadError::Type::Unloadable, name, v);
1441 load_errors.push_back(le);
1443 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1444 load_errors.push_back(le);
1447 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1453 const char*
const FIX_LOADING =
1454 _(
"\n Install/uninstall plugin or remove file to mute message");
1455 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1456 if (
nullptr == create_plugin) {
1457 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1458 wxLogMessage(msg + plugin_file);
1459 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1460 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1461 load_errors.push_back(le);
1466 destroy_t* destroy_plugin =
1467 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1468 pic->m_destroy_fn = destroy_plugin;
1469 if (
nullptr == destroy_plugin) {
1470 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1472 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1473 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1474 load_errors.push_back(le);
1482 int api_major = plug_in->GetAPIVersionMajor();
1483 int api_minor = plug_in->GetAPIVersionMinor();
1484 int api_ver = (api_major * 100) + api_minor;
1485 pic->m_api_version = api_ver;
1487 int pi_major = plug_in->GetPlugInVersionMajor();
1488 int pi_minor = plug_in->GetPlugInVersionMinor();
1491 wxString pi_name = plug_in->GetCommonName();
1493 wxLogDebug(
"blacklist: Get status for %s %d %d",
1494 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1496 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1497 if (status != plug_status::unblocked) {
1498 wxLogDebug(
"Ignoring blacklisted plugin.");
1499 if (status != plug_status::unloadable) {
1501 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1502 load_errors.push_back(le);
1561 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1562 p->GetPlugInVersionBuild());
1571 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1572 p->GetPlugInVersionBuild());
1580 if (!pic->m_pplugin) {
1581 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1582 INFO_LOG << _(
" API Version detected: ");
1583 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1584 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1585 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1587 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1589 load_errors.push_back(le);
Wrapper for configuration variables which lives in a wxBaseConfig object.
const void Notify()
Notify all listeners, no data supplied.
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.
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 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.
std::vector< std::string > Bindirs()
'List of directories for plugin binary helpers.
std::vector< std::string > Libdirs()
List of directories from which we load plugins.
static PluginPaths * getInstance()
Return the singleton instance.
virtual wxBitmap * GetPlugInBitmap()
FIXME static wxBitmap* LoadSVG(const wxString filename, unsigned int width, unsigned int height) { if...
Global variables reflecting command line options and arguments.
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.