39#if defined(__linux__) && !defined(__ANDROID__)
55#include <crashlytics.h>
62#include <wx/hashset.h>
63#include <wx/filename.h>
65#include <wx/tokenzr.h>
67#include <wx/process.h>
86#include "std_filesystem.h"
89#include "androidUTIL.h"
92static const std::vector<std::string> SYSTEM_PLUGINS = {
93 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
97 const ArrayOfPlugIns& plugin_array) {
98 for (
const auto& p : plugin_array) {
105static bool IsSystemPluginPath(
const std::string& path) {
106 static const std::vector<std::string> kPlugins = {
107 "chartdldr_pi",
"wmm_pi",
"dashboard_pi",
"grib_pi",
"demo_pi"};
110 for (
const auto& p : kPlugins)
111 if (lc_path.find(p) != std::string::npos) return true;
116static bool IsSystemPluginName(
const std::string& name) {
117 static const std::vector<std::string> kPlugins = {
118 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
119 auto found = std::find(kPlugins.begin(), kPlugins.end(),
ocpn::tolower(name));
120 return found != kPlugins.end();
124static std::string GetInstalledVersion(
const PlugInData& pd) {
126 if (path ==
"" || !wxFileName::IsFileReadable(path)) {
127 auto loader = PluginLoader::GetInstance();
128 auto pic = GetContainer(pd, *loader->GetPlugInArray());
129 if (!pic || !pic->m_pplugin) {
132 int v_major = pic->m_pplugin->GetPlugInVersionMajor();
133 int v_minor = pic->m_pplugin->GetPlugInVersionMinor();
136 std::ifstream stream;
138 stream.open(path, std::ifstream::in);
145 auto catalogHdlr = CatalogHandler::GetInstance();
149 SemanticVersion orphanVersion(pic->m_version_major, pic->m_version_minor);
150 mdata.version = orphanVersion.to_string();
151 mdata.summary = pic->m_short_description;
152 mdata.description = pic->m_long_description;
154 mdata.target =
"all";
155 mdata.is_orphan =
true;
161static fs::path LoadStampPath(
const std::string& file_path) {
163 path = path /
"load_stamps";
168 return path.parent_path() / path.stem();
172static void CreateLoadStamp(
const std::string& filename) {
173 std::ofstream(LoadStampPath(filename).
string());
183static bool HasLoadStamp(
const std::string& filename) {
184 return exists(LoadStampPath(filename));
191static void ClearLoadStamp(
const std::string& filename) {
192 if (filename.empty())
return;
193 auto path = LoadStampPath(filename);
196 MESSAGE_LOG <<
" Cannot remove load stamp file: " << path;
202 ClearLoadStamp(library_path);
207 std::function<
const PluginMetadata(
const std::string&)> get_metadata) {
208 auto loader = PluginLoader::GetInstance();
209 auto pic = GetContainer(pd, *loader->GetPlugInArray());
215 metadata = pic->m_managed_metadata;
216 if (metadata.version ==
"")
218 std::string detail_suffix(metadata.is_imported ? _(
" [Imported]") :
"");
219 if (metadata.is_orphan) detail_suffix = _(
" [Orphan]");
223 if (pic->m_pplugin) {
231 v_major, v_minor, p->GetPlugInVersionPatch(), p->GetPlugInVersionPost(),
232 p->GetPlugInVersionPre(), p->GetPlugInVersionBuild());
233 return sv.to_string() + detail_suffix;
235 if (!metadata.is_orphan) {
236 std::string version = GetInstalledVersion(pd);
237 return version + detail_suffix;
239 return metadata.version + detail_suffix;
243PlugInContainer::PlugInContainer()
244 :
PlugInData(), m_pplugin(nullptr), m_library(), m_destroy_fn(nullptr) {}
247 : m_has_setup_options(false),
250 m_toolbox_panel(false),
260 m_version_major = v.major;
261 m_version_minor = v.minor;
262 m_managed_metadata = md;
263 m_status = PluginStatus::ManagedInstallAvailable;
287static void setLoadPath() {
290 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
292 if (osSystemId & wxOS_UNIX_LINUX) {
295 if (wxGetEnv(
"LD_LIBRARY_PATH", &envPath)) {
296 path = path +
":" + envPath.ToStdString();
298 wxLogMessage(
"Using LD_LIBRARY_PATH: %s", path.c_str());
299 wxSetEnv(
"LD_LIBRARY_PATH", path.c_str());
300 }
else if (osSystemId & wxOS_WINDOWS) {
304 if (wxGetEnv(
"PATH", &envPath)) {
305 path = path +
";" + envPath.ToStdString();
307 wxLogMessage(
"Using PATH: %s", path);
308 wxSetEnv(
"PATH", path);
309 }
else if (osSystemId & wxOS_MAC) {
312 if (wxGetEnv(
"DYLD_LIBRARY_PATH", &envPath)) {
313 path = path +
":" + envPath.ToStdString();
315 wxLogMessage(
"Using DYLD_LIBRARY_PATH: %s", path.c_str());
316 wxSetEnv(
"DYLD_LIBRARY_PATH", path.c_str());
318 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
319 if (os_name.Contains(
"wxQT")) {
320 wxLogMessage(
"setLoadPath() using Android library path");
322 wxLogWarning(
"SetLoadPath: Unsupported platform.");
324 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
328 wxGetEnv(
"PATH", &envPath);
329 path = path +
":" + envPath.ToStdString();
330 wxLogMessage(
"Using PATH: %s", path);
331 wxSetEnv(
"PATH", path);
337 wxString msg(
"PluginLoader: Calling LateInit PlugIn: ");
342 if (ppi) ppi->LateInit();
353PluginLoader::PluginLoader()
354 : m_blacklist(blacklist_factory()),
355 m_default_plugin_icon(nullptr),
357 m_found_wxwidgets(false),
363 for (
auto* pic : plugin_array) {
364 if (pic && pic->m_enabled && (pic->
m_common_name == commonName))
372 auto loader = PluginLoader::GetInstance();
373 auto pic = GetContainer(pd, *loader->GetPlugInArray());
377void PluginLoader::NotifySetupOptionsPlugin(
const PlugInData* pd) {
382 if (pic->m_enabled && pic->m_init_state) {
384 switch (pic->m_api_version) {
398 if (pic->m_pplugin) {
402 auto loader = PluginLoader::GetInstance();
416 for (
auto* pic : plugin_array) {
418 pic->m_enabled = enabled;
425 for (
auto* pic : plugin_array) {
427 pic->m_toolbox_panel = value;
431 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
436 for (
auto* pic : plugin_array) {
442 wxLogMessage(
"Atttempt to update setup options on non-existing plugin " +
446const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
447 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
448 return m_default_plugin_icon;
451void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
452 delete m_default_plugin_icon;
453 m_default_plugin_icon = bitmap;
457 auto pic = GetContainer(pd, plugin_array);
459 wxLogMessage(
"Attempt to remove non-existing plugin %s",
463 plugin_array.Remove(pic);
467 return (*p1)->Key().compare((*p2)->Key());
472 plugin_array.Sort(ComparePlugins);
478 static const wxString sep = wxFileName::GetPathSeparator();
480 wxLogMessage(
"PluginLoader: loading plugins from %s",
ocpn::join(dirs,
';'));
482 bool any_dir_loaded =
false;
483 for (
const auto& dir : dirs) {
485 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
486 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
496 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
499 return any_dir_loaded;
502bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
504 wxString plugin_file = wxFileName(file_name).GetFullName();
505 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
507 wxString plugin_loadstamp = wxFileName(file_name).GetName();
508 if (!IsSystemPluginPath(plugin_file.ToStdString())) {
509 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
510 MESSAGE_LOG <<
"Refusing to load " << file_name
511 <<
" failed at last attempt";
514 CreateLoadStamp(plugin_loadstamp.ToStdString());
516 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
517 wxLog::FlushActive();
520 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
521 file_name.ToStdString().c_str());
530 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
539 plugin_array.Remove(pic_test);
543 pic_test->m_destroy_fn(pic_test->m_pplugin);
548 loaded_pic = pic_test;
553 loaded_pic = pic_test;
560 ClearLoadStamp(plugin_loadstamp.ToStdString());
565 wxFileName fn_plugin_file(file_name);
566 wxString plugin_file_path =
567 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
569 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
570 base_plugin_path += wxFileName::GetPathSeparator();
574 if (!g_allow_arb_system_plugin) {
576 if (base_plugin_path.IsSameAs(plugin_file_path)) {
577 if (!IsSystemPluginPath(file_name.ToStdString())) {
578 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
581 ClearLoadStamp(plugin_loadstamp.ToStdString());
588 if (!IsSystemPluginPath(file_name.ToStdString()) && safe_mode::get_mode()) {
589 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
590 ClearLoadStamp(plugin_loadstamp.ToStdString());
595 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
596 wxLogMessage(msg.c_str());
597 wxLog::FlushActive();
603 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
604 wxLogMessage(msg.c_str());
605 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
606 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
607 load_errors.push_back(le);
617 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
619 if (pic && load_enabled && !enabled.Get(
true)) {
620 pic->m_destroy_fn(pic->m_pplugin);
622 wxLogMessage(
"Skipping not enabled candidate.");
623 ClearLoadStamp(plugin_loadstamp.ToStdString());
628 if (pic->m_pplugin) {
629 plugin_array.Add(pic);
636 pic->m_enabled = enabled.Get(
false);
638 if (safe_mode::get_mode() &&
639 !IsSystemPluginPath(file_name.ToStdString())) {
640 pic->m_enabled =
false;
643 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
645 if (pic->m_enabled) {
647 pic->m_init_state =
true;
651 wxLog::FlushActive();
653 std::string found_version;
656 found_version = p.readonly ?
"" : p.version;
665 m_on_activate_cb(pic);
669 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
671 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
672 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
674 if (!pic->m_enabled && pic->m_destroy_fn) {
675 pic->m_destroy_fn(pic->m_pplugin);
676 pic->m_destroy_fn =
nullptr;
677 pic->m_pplugin =
nullptr;
678 pic->m_init_state =
false;
679 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
684 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
686 bool is_system = found != SYSTEM_PLUGINS.end();
692 available.begin(), available.end(),
695 if (it == available.end()) {
700 auto oprhan_metadata = CreateMetadata(pic);
701 auto catalogHdlr = CatalogHandler::GetInstance();
702 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
708 " PluginLoader: Unloading invalid PlugIn, API version %d ",
710 pic->m_destroy_fn(pic->m_pplugin);
712 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
714 load_errors.push_back(le);
720 ClearLoadStamp(plugin_loadstamp.ToStdString());
725bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
728 m_plugin_location = plugin_dir;
730 wxString msg(
"PluginLoader searching for PlugIns in location ");
731 msg += m_plugin_location;
735 wxString pispec =
"*_pi.dll";
736#elif defined(__WXOSX__)
737 wxString pispec =
"*_pi.dylib";
739 wxString pispec =
"*_pi.so";
742 if (!::wxDirExists(m_plugin_location)) {
743 msg = m_plugin_location;
744 msg.Prepend(
" Directory ");
745 msg.Append(
" does not exist.");
750 if (!
g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
752 wxArrayString file_list;
754 int get_flags = wxDIR_FILES | wxDIR_DIRS;
757 get_flags = wxDIR_FILES;
762 get_flags = wxDIR_FILES;
768 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
770 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
771 for (
auto& file_name : file_list) {
772 wxLog::FlushActive();
774 LoadPluginCandidate(file_name, load_enabled);
781 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
783 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
788 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
790 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
797 while ((i >= 0) && (i < plugin_array.GetCount())) {
799 if (pict->m_status == PluginStatus::PendingListRemoval) {
800 plugin_array.RemoveAt(i);
812 for (
const auto& pic : plugin_array) {
815 if (pic->m_pplugin) {
818 pic->m_pplugin =
nullptr;
819 pic->m_init_state =
false;
824 if (!pic->m_pplugin) {
825 if (pic->m_enabled) {
829 pic->m_status = stat;
830 pic->m_enabled =
true;
836 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
837 wxString msg(
"PluginLoader: Initializing PlugIn: ");
844 pic->m_init_state =
true;
845 ProcessLateInit(pic);
851 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
852 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
853 m_on_activate_cb(pic);
855 }
else if (!pic->m_enabled && pic->m_init_state) {
858 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
859 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
862 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
863 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
864 pic->m_pplugin =
nullptr;
865 pic->m_init_state =
false;
874 if (!pic)
return false;
875 if (pic->m_init_state) {
876 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
878 m_on_deactivate_cb(pic);
879 pic->m_init_state =
false;
886 auto pic = GetContainer(pd, plugin_array);
888 wxLogError(
"Attempt to deactivate non-existing plugin %s",
896 if (ix >= plugin_array.GetCount()) {
897 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
904 if (pic->m_pplugin) {
905 pic->m_destroy_fn(pic->m_pplugin);
909 plugin_array.RemoveAt(ix);
913static std::string VersionFromManifest(
const std::string& plugin_name) {
916 if (!path.empty() && wxFileName::IsFileReadable(path)) {
917 std::ifstream stream;
918 stream.open(path, std::ifstream::in);
927 if (name.empty())
return {};
930 if (isRegularFile(import_path.c_str())) {
931 std::ifstream f(import_path.c_str());
932 std::stringstream ss;
935 ParsePlugin(ss.str(), pd);
936 pd.is_imported =
true;
941 vector<PluginMetadata> matches;
942 copy_if(available.begin(), available.end(), back_inserter(matches),
944 if (matches.size() == 0)
return {};
945 if (matches.size() == 1)
return matches[0];
947 auto version = VersionFromManifest(name);
949 return version == md.version;
951 auto found = find_if(matches.begin(), matches.end(), predicate);
952 return found != matches.end() ? *found : matches[0];
958 if (name.empty())
return {};
961 vector<PluginMetadata> matches;
962 copy_if(available.begin(), available.end(), back_inserter(matches),
964 if (matches.size() == 0)
return {};
965 if (matches.size() == 1)
return matches[0];
969 auto rv = matches[0];
970 for (
auto p : matches) {
972 if (catVersion > version) {
973 version = catVersion;
984 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
986 bool is_system = found != SYSTEM_PLUGINS.end();
988 std::string installed = VersionFromManifest(md.name);
995 else if (plugin->m_status == PluginStatus::Imported)
997 else if (installedVersion < metaVersion)
998 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
999 else if (installedVersion == metaVersion)
1000 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
1002 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
1006 plugin->m_managed_metadata = md;
1010 std::vector<PlugInContainer*> loaded_plugins;
1011 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
1014 for (
auto& p : loaded_plugins) {
1015 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1016 p->m_common_name.Lower().ToStdString());
1017 bool is_system = found != SYSTEM_PLUGINS.end();
1020 if (!keep_orphans) {
1027 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1031 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1032 loaded_plugins.erase(end, loaded_plugins.end());
1036 for (
auto& plugin : loaded_plugins) {
1039 if (!md.name.empty()) {
1041 md.is_imported = isRegularFile(import_path.c_str());
1042 if (md.is_imported) {
1043 plugin->m_status = PluginStatus::Imported;
1047 }
else if (IsSystemPluginName(md.name)) {
1049 }
else if (md.is_orphan) {
1051 }
else if (plugin->m_api_version) {
1054 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1055 plugin->m_managed_metadata = md;
1058 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1063 plugin_array.Clear();
1064 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1070 while (plugin_array.GetCount()) {
1079 for (
auto* pic : plugin_array) {
1087DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1089 PIMAGE_SECTION_HEADER pSeh;
1094 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1095 if (rva >= pSeh->VirtualAddress &&
1096 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1101 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1108 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1109 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1112 uint64_t type_magic;
1113 DependencyMap dependencies;
1117bool ReadModuleInfoFromELF(
const wxString& file,
1118 const ModuleInfo::DependencySet& dependencies,
1120 static bool b_libelf_initialized =
false;
1121 static bool b_libelf_usable =
false;
1123 if (b_libelf_usable) {
1125 }
else if (b_libelf_initialized) {
1127 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1128 b_libelf_initialized =
true;
1129 b_libelf_usable =
false;
1130 wxLogError(
"LibELF is outdated.");
1133 b_libelf_initialized =
true;
1134 b_libelf_usable =
true;
1138 Elf* elf_handle =
nullptr;
1139 GElf_Ehdr elf_file_header;
1140 Elf_Scn* elf_section_handle =
nullptr;
1142 file_handle = open(file, O_RDONLY);
1143 if (file_handle == -1) {
1144 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1146 goto FailureEpilogue;
1149 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1150 if (elf_handle ==
nullptr) {
1151 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1152 goto FailureEpilogue;
1155 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1156 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1157 goto FailureEpilogue;
1160 switch (elf_file_header.e_type) {
1165 wxLogMessage(wxString::Format(
1166 "Module \"%s\" is not an executable or shared library.", file));
1167 goto FailureEpilogue;
1171 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1173 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1175 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1177 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1179 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1183 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1185 GElf_Shdr elf_section_header;
1186 Elf_Data* elf_section_data =
nullptr;
1187 size_t elf_section_entry_count = 0;
1189 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1190 &elf_section_header) {
1191 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1192 goto FailureEpilogue;
1193 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1197 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1198 if (elf_section_data ==
nullptr) {
1199 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1200 goto FailureEpilogue;
1203 if ((elf_section_data->d_size == 0) ||
1204 (elf_section_header.sh_entsize == 0)) {
1205 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1206 goto FailureEpilogue;
1209 elf_section_entry_count =
1210 elf_section_data->d_size / elf_section_header.sh_entsize;
1211 for (
size_t elf_section_entry_index = 0;
1212 elf_section_entry_index < elf_section_entry_count;
1213 ++elf_section_entry_index) {
1214 GElf_Dyn elf_dynamic_entry;
1215 const char* elf_dynamic_entry_name =
nullptr;
1216 if (gelf_getdyn(elf_section_data,
1217 static_cast<int>(elf_section_entry_index),
1218 &elf_dynamic_entry) != &elf_dynamic_entry) {
1219 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1221 goto FailureEpilogue;
1222 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1225 elf_dynamic_entry_name = elf_strptr(
1226 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1227 if (elf_dynamic_entry_name ==
nullptr) {
1228 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1229 "string entry", file));
1230 goto FailureEpilogue;
1232 wxString name_full(elf_dynamic_entry_name);
1233 wxString name_part(elf_dynamic_entry_name,
1234 strcspn(elf_dynamic_entry_name,
"-."));
1235 if (dependencies.find(name_part) != dependencies.end()) {
1236 info.dependencies.insert(
1237 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1242 goto SuccessEpilogue;
1245 elf_end(elf_handle);
1250 if (elf_handle !=
nullptr) elf_end(elf_handle);
1251 if (file_handle >= 0) close(file_handle);
1252 wxLog::FlushActive();
1258 bool b_compat =
false;
1274 if (!m_found_wxwidgets) {
1275 DWORD myPid = GetCurrentProcessId();
1277 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1278 if (hProcess == NULL) {
1279 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1280 plugin_file.c_str()));
1284 HMODULE hMods[1024];
1286 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1287 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1288 TCHAR szModName[MAX_PATH];
1289 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1290 sizeof(szModName) /
sizeof(TCHAR))) {
1291 m_module_name = szModName;
1292 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1293 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1294 m_found_wxwidgets =
true;
1295 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1296 m_module_name.c_str()));
1303 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1304 plugin_file.c_str()));
1306 if (hProcess) CloseHandle(hProcess);
1309 if (!m_found_wxwidgets) {
1310 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1311 plugin_file.c_str()));
1313 LPCWSTR fName = plugin_file.wc_str();
1314 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1315 FILE_ATTRIBUTE_NORMAL, 0);
1316 DWORD byteread, size = GetFileSize(handle, NULL);
1317 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1318 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1319 CloseHandle(handle);
1320 PIMAGE_NT_HEADERS ntheaders =
1321 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1322 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1323 PIMAGE_SECTION_HEADER pSech =
1324 IMAGE_FIRST_SECTION(ntheaders);
1325 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1326 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1331 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1333 ntheaders->OptionalHeader
1335 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1342 while (pImportDescriptor->Name != 0) {
1345 (PCHAR)((DWORD_PTR)virtualpointer +
1346 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1349 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1352 wxLogMessage(wxString::Format(
1353 "Compatible wxWidgets plugin library found for %s: %s",
1354 plugin_file.c_str(), libname[i]));
1357 pImportDescriptor++;
1362 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1364 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1367#if defined(__WXGTK__) || defined(__WXQT__)
1368#if defined(USE_LIBELF)
1370 static bool b_own_info_queried =
false;
1371 static bool b_own_info_usable =
false;
1373 static ModuleInfo::DependencySet dependencies;
1375 if (!b_own_info_queried) {
1376 dependencies.insert(
"libwx_baseu");
1378 char exe_buf[100] = {0};
1379 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1381 exe_buf[len] =
'\0';
1382 wxString app_path(exe_buf);
1383 wxLogMessage(
"Executable path: %s", exe_buf);
1385 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1386 if (!b_own_info_usable) {
1387 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1390 wxLogMessage(
"Cannot get own executable path.");
1392 b_own_info_queried =
true;
1395 if (b_own_info_usable) {
1396 bool b_pi_info_usable =
false;
1399 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1400 if (b_pi_info_usable) {
1401 b_compat = (pi_info.type_magic == own_info.type_magic);
1404 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1409 pi_info.dependencies.clear();
1411 wxString::Format(
" Plugin \"%s\" is of another binary "
1412 "flavor than the main module.",
1414 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1415 own_info.type_magic, pi_info.type_magic);
1417 for (
const auto& own_dependency : own_info.dependencies) {
1418 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1419 pi_info.dependencies.find(own_dependency.first);
1420 if ((pi_dependency != pi_info.dependencies.end()) &&
1421 (pi_dependency->second != own_dependency.second)) {
1424 " Plugin \"%s\" depends on library \"%s\", but the main "
1425 "module was built for \"%s\".",
1426 plugin_file, pi_dependency->second, own_dependency.second);
1433 wxString::Format(
" Plugin \"%s\" could not be reliably "
1434 "checked for compatibility.",
1442 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1443 b_compat ?
"true" :
"false");
1445 wxLog::FlushActive();
1460 FILE* f = fopen(plugin_file,
"r");
1463 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1467#
if defined(__WXGTK3__)
1468 "libwx_gtk3u_core-%i.%i"
1469#elif defined(__WXGTK20__)
1470 "libwx_gtk2u_core-%i.%i"
1471#elif defined(__WXQT__)
1472 "libwx_qtu_core-%i.%i"
1474#error undefined plugin platform
1477 wxMAJOR_VERSION, wxMINOR_VERSION);
1481 size_t len(strlen(strver));
1483 while ((c = fgetc(f)) != EOF) {
1484 if (c == strver[pos]) {
1495 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1511 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1513 if (plugin_file.empty()) {
1514 wxLogMessage(
"Ignoring loading of empty path");
1518 if (!wxIsReadable(plugin_file)) {
1519 wxLogMessage(
"Ignoring unreadable plugin %s",
1520 plugin_file.ToStdString().c_str());
1521 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1522 load_errors.push_back(le);
1529 pic->m_version_major, pic->m_version_minor);
1530 if (sts != plug_status::unblocked) {
1531 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1535 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1536 if (!data.name.empty()) {
1537 wxLogDebug(
"Refusing to load blacklisted library: %s",
1538 plugin_file.ToStdString().c_str());
1546 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1547 pic->m_library.Load(plugin_file);
1549 if (!pic->m_library.IsLoaded()) {
1552 wxFileName fn(plugin_file);
1553 std::string name = fn.GetName().ToStdString();
1554 auto found = m_blacklist->get_library_data(name);
1555 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1556 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1557 if (!found.name.empty()) {
1559 LoadError le(LoadError::Type::Unloadable, name, v);
1560 load_errors.push_back(le);
1562 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1563 load_errors.push_back(le);
1566 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1572 const char*
const FIX_LOADING =
1573 _(
"\n Install/uninstall plugin or remove file to mute message");
1574 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1575 if (
nullptr == create_plugin) {
1576 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1577 wxLogMessage(msg + plugin_file);
1578 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1579 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1580 load_errors.push_back(le);
1585 destroy_t* destroy_plugin =
1586 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1587 pic->m_destroy_fn = destroy_plugin;
1588 if (
nullptr == destroy_plugin) {
1589 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1591 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1592 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1593 load_errors.push_back(le);
1603 int api_ver = (api_major * 100) + api_minor;
1604 pic->m_api_version = api_ver;
1612 wxLogDebug(
"blacklist: Get status for %s %d %d",
1613 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1615 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1616 if (status != plug_status::unblocked) {
1617 wxLogDebug(
"Ignoring blacklisted plugin.");
1618 if (status != plug_status::unloadable) {
1620 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1621 load_errors.push_back(le);
1704 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1705 p->GetPlugInVersionBuild());
1708 if (!pic->m_pplugin) {
1709 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1710 INFO_LOG << _(
" API Version detected: ");
1711 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1712 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1713 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1715 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1717 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.