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>
87#include "std_filesystem.h"
90#include "androidUTIL.h"
93static const std::vector<std::string> SYSTEM_PLUGINS = {
94 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
98 const ArrayOfPlugIns& plugin_array) {
99 for (
const auto& p : plugin_array) {
106static bool IsSystemPluginPath(
const std::string& path) {
107 static const std::vector<std::string> kPlugins = {
108 "chartdldr_pi",
"wmm_pi",
"dashboard_pi",
"grib_pi",
"demo_pi"};
111 for (
const auto& p : kPlugins)
112 if (lc_path.find(p) != std::string::npos) return true;
117static bool IsSystemPluginName(
const std::string& name) {
118 static const std::vector<std::string> kPlugins = {
119 "chartdownloader",
"wmm",
"dashboard",
"grib",
"demo"};
120 auto found = std::find(kPlugins.begin(), kPlugins.end(),
ocpn::tolower(name));
121 return found != kPlugins.end();
125static std::string GetInstalledVersion(
const PlugInData& pd) {
127 if (path ==
"" || !wxFileName::IsFileReadable(path)) {
128 auto loader = PluginLoader::GetInstance();
129 auto pic = GetContainer(pd, *loader->GetPlugInArray());
130 if (!pic || !pic->m_pplugin) {
133 int v_major = pic->m_pplugin->GetPlugInVersionMajor();
134 int v_minor = pic->m_pplugin->GetPlugInVersionMinor();
137 std::ifstream stream;
139 stream.open(path, std::ifstream::in);
146 auto catalogHdlr = CatalogHandler::GetInstance();
150 SemanticVersion orphanVersion(pic->m_version_major, pic->m_version_minor);
151 mdata.version = orphanVersion.to_string();
152 mdata.summary = pic->m_short_description;
153 mdata.description = pic->m_long_description;
155 mdata.target =
"all";
156 mdata.is_orphan =
true;
162static fs::path LoadStampPath(
const std::string& file_path) {
164 path = path /
"load_stamps";
169 return path.parent_path() / path.stem();
173static void CreateLoadStamp(
const std::string& filename) {
174 std::ofstream(LoadStampPath(filename).
string());
184static bool HasLoadStamp(
const std::string& filename) {
185 return exists(LoadStampPath(filename));
192static void ClearLoadStamp(
const std::string& filename) {
193 if (filename.empty())
return;
194 auto path = LoadStampPath(filename);
197 MESSAGE_LOG <<
" Cannot remove load stamp file: " << path;
203 ClearLoadStamp(library_path);
208 std::function<
const PluginMetadata(
const std::string&)> get_metadata) {
209 if (IsSystemPluginName(pd.
m_common_name.ToStdString()))
return VERSION_FULL;
210 auto loader = PluginLoader::GetInstance();
211 auto pic = GetContainer(pd, *loader->GetPlugInArray());
217 metadata = pic->m_managed_metadata;
218 if (metadata.version ==
"")
220 std::string detail_suffix(metadata.is_imported ? _(
" [Imported]") :
"");
221 if (metadata.is_orphan) detail_suffix = _(
" [Orphan]");
225 if (pic->m_pplugin) {
233 v_major, v_minor, p->GetPlugInVersionPatch(), p->GetPlugInVersionPost(),
234 p->GetPlugInVersionPre(), p->GetPlugInVersionBuild());
235 return sv.to_string() + detail_suffix;
237 if (!metadata.is_orphan) {
238 std::string version = GetInstalledVersion(pd);
239 return version + detail_suffix;
241 return metadata.version + detail_suffix;
245PlugInContainer::PlugInContainer()
246 :
PlugInData(), m_pplugin(nullptr), m_library(), m_destroy_fn(nullptr) {}
249 : m_has_setup_options(false),
252 m_toolbox_panel(false),
262 m_version_major = v.major;
263 m_version_minor = v.minor;
264 m_managed_metadata = md;
265 m_status = PluginStatus::ManagedInstallAvailable;
289static void setLoadPath() {
292 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
294 if (osSystemId & wxOS_UNIX_LINUX) {
297 if (wxGetEnv(
"LD_LIBRARY_PATH", &envPath)) {
298 path = path +
":" + envPath.ToStdString();
300 wxLogMessage(
"Using LD_LIBRARY_PATH: %s", path.c_str());
301 wxSetEnv(
"LD_LIBRARY_PATH", path.c_str());
302 }
else if (osSystemId & wxOS_WINDOWS) {
306 if (wxGetEnv(
"PATH", &envPath)) {
307 path = path +
";" + envPath.ToStdString();
309 wxLogMessage(
"Using PATH: %s", path);
310 wxSetEnv(
"PATH", path);
311 }
else if (osSystemId & wxOS_MAC) {
314 if (wxGetEnv(
"DYLD_LIBRARY_PATH", &envPath)) {
315 path = path +
":" + envPath.ToStdString();
317 wxLogMessage(
"Using DYLD_LIBRARY_PATH: %s", path.c_str());
318 wxSetEnv(
"DYLD_LIBRARY_PATH", path.c_str());
320 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
321 if (os_name.Contains(
"wxQT")) {
322 wxLogMessage(
"setLoadPath() using Android library path");
324 wxLogWarning(
"SetLoadPath: Unsupported platform.");
326 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
330 wxGetEnv(
"PATH", &envPath);
331 path = path +
":" + envPath.ToStdString();
332 wxLogMessage(
"Using PATH: %s", path);
333 wxSetEnv(
"PATH", path);
339 wxString msg(
"PluginLoader: Calling LateInit PlugIn: ");
344 if (ppi) ppi->LateInit();
355PluginLoader::PluginLoader()
356 : m_blacklist(blacklist_factory()),
357 m_default_plugin_icon(nullptr),
359 m_found_wxwidgets(false),
365 for (
auto* pic : plugin_array) {
366 if (pic && pic->m_enabled && (pic->
m_common_name == commonName))
374 auto loader = PluginLoader::GetInstance();
375 auto pic = GetContainer(pd, *loader->GetPlugInArray());
379void PluginLoader::NotifySetupOptionsPlugin(
const PlugInData* pd) {
384 if (pic->m_enabled && pic->m_init_state) {
386 switch (pic->m_api_version) {
400 if (pic->m_pplugin) {
404 auto loader = PluginLoader::GetInstance();
418 for (
auto* pic : plugin_array) {
420 pic->m_enabled = enabled;
427 for (
auto* pic : plugin_array) {
429 pic->m_toolbox_panel = value;
433 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
438 for (
auto* pic : plugin_array) {
444 wxLogMessage(
"Atttempt to update setup options on non-existing plugin " +
448const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
449 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
450 return m_default_plugin_icon;
453void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
454 delete m_default_plugin_icon;
455 m_default_plugin_icon = bitmap;
459 auto pic = GetContainer(pd, plugin_array);
461 wxLogMessage(
"Attempt to remove non-existing plugin %s",
465 plugin_array.Remove(pic);
469 return (*p1)->Key().compare((*p2)->Key());
474 plugin_array.Sort(ComparePlugins);
480 static const wxString sep = wxFileName::GetPathSeparator();
482 wxLogMessage(
"PluginLoader: loading plugins from %s",
ocpn::join(dirs,
';'));
484 bool any_dir_loaded =
false;
485 for (
const auto& dir : dirs) {
487 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
488 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
498 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
501 return any_dir_loaded;
504bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
506 wxString plugin_file = wxFileName(file_name).GetFullName();
507 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
509 wxString plugin_loadstamp = wxFileName(file_name).GetName();
510 if (!IsSystemPluginPath(plugin_file.ToStdString())) {
511 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
512 MESSAGE_LOG <<
"Refusing to load " << file_name
513 <<
" failed at last attempt";
516 CreateLoadStamp(plugin_loadstamp.ToStdString());
518 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
519 wxLog::FlushActive();
522 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
523 file_name.ToStdString().c_str());
532 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
541 plugin_array.Remove(pic_test);
545 pic_test->m_destroy_fn(pic_test->m_pplugin);
550 loaded_pic = pic_test;
555 loaded_pic = pic_test;
562 ClearLoadStamp(plugin_loadstamp.ToStdString());
567 wxFileName fn_plugin_file(file_name);
568 wxString plugin_file_path =
569 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
571 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
572 base_plugin_path += wxFileName::GetPathSeparator();
576 if (!g_allow_arb_system_plugin) {
578 if (base_plugin_path.IsSameAs(plugin_file_path)) {
579 if (!IsSystemPluginPath(file_name.ToStdString())) {
580 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
583 ClearLoadStamp(plugin_loadstamp.ToStdString());
591 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
592 ClearLoadStamp(plugin_loadstamp.ToStdString());
597 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
598 wxLogMessage(msg.c_str());
599 wxLog::FlushActive();
605 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
606 wxLogMessage(msg.c_str());
607 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
608 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
609 load_errors.push_back(le);
619 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
621 if (pic && load_enabled && !enabled.Get(
true)) {
622 pic->m_destroy_fn(pic->m_pplugin);
624 wxLogMessage(
"Skipping not enabled candidate.");
625 ClearLoadStamp(plugin_loadstamp.ToStdString());
630 if (pic->m_pplugin) {
631 plugin_array.Add(pic);
638 pic->m_enabled = enabled.Get(
false);
641 !IsSystemPluginPath(file_name.ToStdString())) {
642 pic->m_enabled =
false;
645 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
647 if (pic->m_enabled) {
649 pic->m_init_state =
true;
653 wxLog::FlushActive();
655 std::string found_version;
658 found_version = p.readonly ?
"" : p.version;
667 m_on_activate_cb(pic);
671 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
673 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
674 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
676 if (!pic->m_enabled && pic->m_destroy_fn) {
677 pic->m_destroy_fn(pic->m_pplugin);
678 pic->m_destroy_fn =
nullptr;
679 pic->m_pplugin =
nullptr;
680 pic->m_init_state =
false;
681 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
686 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
688 bool is_system = found != SYSTEM_PLUGINS.end();
694 available.begin(), available.end(),
697 if (it == available.end()) {
702 auto oprhan_metadata = CreateMetadata(pic);
703 auto catalogHdlr = CatalogHandler::GetInstance();
704 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
710 " PluginLoader: Unloading invalid PlugIn, API version %d ",
712 pic->m_destroy_fn(pic->m_pplugin);
714 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
716 load_errors.push_back(le);
722 ClearLoadStamp(plugin_loadstamp.ToStdString());
727bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
730 m_plugin_location = plugin_dir;
732 wxString msg(
"PluginLoader searching for PlugIns in location ");
733 msg += m_plugin_location;
737 wxString pispec =
"*_pi.dll";
738#elif defined(__WXOSX__)
739 wxString pispec =
"*_pi.dylib";
741 wxString pispec =
"*_pi.so";
744 if (!::wxDirExists(m_plugin_location)) {
745 msg = m_plugin_location;
746 msg.Prepend(
" Directory ");
747 msg.Append(
" does not exist.");
752 if (!
g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
754 wxArrayString file_list;
756 int get_flags = wxDIR_FILES | wxDIR_DIRS;
759 get_flags = wxDIR_FILES;
764 get_flags = wxDIR_FILES;
770 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
772 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
773 for (
auto& file_name : file_list) {
774 wxLog::FlushActive();
776 LoadPluginCandidate(file_name, load_enabled);
783 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
785 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
790 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
792 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
799 while ((i >= 0) && (i < plugin_array.GetCount())) {
801 if (pict->m_status == PluginStatus::PendingListRemoval) {
802 plugin_array.RemoveAt(i);
814 for (
const auto& pic : plugin_array) {
817 if (pic->m_pplugin) {
820 pic->m_pplugin =
nullptr;
821 pic->m_init_state =
false;
826 if (!pic->m_pplugin) {
827 if (pic->m_enabled) {
831 pic->m_status = stat;
832 pic->m_enabled =
true;
838 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
839 wxString msg(
"PluginLoader: Initializing PlugIn: ");
846 pic->m_init_state =
true;
847 ProcessLateInit(pic);
853 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
854 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
855 m_on_activate_cb(pic);
857 }
else if (!pic->m_enabled && pic->m_init_state) {
860 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
861 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
864 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
865 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
866 pic->m_pplugin =
nullptr;
867 pic->m_init_state =
false;
876 if (!pic)
return false;
877 if (pic->m_init_state) {
878 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
880 m_on_deactivate_cb(pic);
881 pic->m_init_state =
false;
890 auto pic = GetContainer(pd, plugin_array);
892 wxLogError(
"Attempt to deactivate non-existing plugin %s",
900 if (ix >= plugin_array.GetCount()) {
901 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
908 if (pic->m_pplugin) {
909 pic->m_destroy_fn(pic->m_pplugin);
913 plugin_array.RemoveAt(ix);
917static std::string VersionFromManifest(
const std::string& plugin_name) {
920 if (!path.empty() && wxFileName::IsFileReadable(path)) {
921 std::ifstream stream;
922 stream.open(path, std::ifstream::in);
931 if (name.empty())
return {};
934 if (isRegularFile(import_path.c_str())) {
935 std::ifstream f(import_path.c_str());
936 std::stringstream ss;
939 ParsePlugin(ss.str(), pd);
940 pd.is_imported =
true;
945 vector<PluginMetadata> matches;
946 copy_if(available.begin(), available.end(), back_inserter(matches),
948 if (matches.size() == 0)
return {};
949 if (matches.size() == 1)
return matches[0];
951 auto version = VersionFromManifest(name);
953 return version == md.version;
955 auto found = find_if(matches.begin(), matches.end(), predicate);
956 return found != matches.end() ? *found : matches[0];
962 if (name.empty())
return {};
965 vector<PluginMetadata> matches;
966 copy_if(available.begin(), available.end(), back_inserter(matches),
968 if (matches.size() == 0)
return {};
969 if (matches.size() == 1)
return matches[0];
973 auto rv = matches[0];
974 for (
auto p : matches) {
976 if (catVersion > version) {
977 version = catVersion;
988 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
990 bool is_system = found != SYSTEM_PLUGINS.end();
992 std::string installed = VersionFromManifest(md.name);
999 else if (plugin->m_status == PluginStatus::Imported)
1001 else if (installedVersion < metaVersion)
1002 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
1003 else if (installedVersion == metaVersion)
1004 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
1006 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
1010 plugin->m_managed_metadata = md;
1014 std::vector<PlugInContainer*> loaded_plugins;
1015 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
1018 for (
auto& p : loaded_plugins) {
1019 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1020 p->m_common_name.Lower().ToStdString());
1021 bool is_system = found != SYSTEM_PLUGINS.end();
1024 if (!keep_orphans) {
1031 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1035 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1036 loaded_plugins.erase(end, loaded_plugins.end());
1040 for (
auto& plugin : loaded_plugins) {
1043 if (!md.name.empty()) {
1045 md.is_imported = isRegularFile(import_path.c_str());
1046 if (md.is_imported) {
1047 plugin->m_status = PluginStatus::Imported;
1051 }
else if (IsSystemPluginName(md.name)) {
1053 }
else if (md.is_orphan) {
1055 }
else if (plugin->m_api_version) {
1058 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1059 plugin->m_managed_metadata = md;
1062 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1067 plugin_array.Clear();
1068 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1074 while (plugin_array.GetCount()) {
1083 for (
auto* pic : plugin_array) {
1091DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1093 PIMAGE_SECTION_HEADER pSeh;
1098 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1099 if (rva >= pSeh->VirtualAddress &&
1100 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1105 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1112 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1113 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1116 uint64_t type_magic;
1117 DependencyMap dependencies;
1121bool ReadModuleInfoFromELF(
const wxString& file,
1122 const ModuleInfo::DependencySet& dependencies,
1124 static bool b_libelf_initialized =
false;
1125 static bool b_libelf_usable =
false;
1127 if (b_libelf_usable) {
1129 }
else if (b_libelf_initialized) {
1131 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1132 b_libelf_initialized =
true;
1133 b_libelf_usable =
false;
1134 wxLogError(
"LibELF is outdated.");
1137 b_libelf_initialized =
true;
1138 b_libelf_usable =
true;
1142 Elf* elf_handle =
nullptr;
1143 GElf_Ehdr elf_file_header;
1144 Elf_Scn* elf_section_handle =
nullptr;
1146 file_handle = open(file, O_RDONLY);
1147 if (file_handle == -1) {
1148 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1150 goto FailureEpilogue;
1153 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1154 if (elf_handle ==
nullptr) {
1155 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1156 goto FailureEpilogue;
1159 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1160 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1161 goto FailureEpilogue;
1164 switch (elf_file_header.e_type) {
1169 wxLogMessage(wxString::Format(
1170 "Module \"%s\" is not an executable or shared library.", file));
1171 goto FailureEpilogue;
1175 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1177 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1179 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1181 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1183 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1187 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1189 GElf_Shdr elf_section_header;
1190 Elf_Data* elf_section_data =
nullptr;
1191 size_t elf_section_entry_count = 0;
1193 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1194 &elf_section_header) {
1195 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1196 goto FailureEpilogue;
1197 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1201 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1202 if (elf_section_data ==
nullptr) {
1203 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1204 goto FailureEpilogue;
1207 if ((elf_section_data->d_size == 0) ||
1208 (elf_section_header.sh_entsize == 0)) {
1209 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1210 goto FailureEpilogue;
1213 elf_section_entry_count =
1214 elf_section_data->d_size / elf_section_header.sh_entsize;
1215 for (
size_t elf_section_entry_index = 0;
1216 elf_section_entry_index < elf_section_entry_count;
1217 ++elf_section_entry_index) {
1218 GElf_Dyn elf_dynamic_entry;
1219 const char* elf_dynamic_entry_name =
nullptr;
1220 if (gelf_getdyn(elf_section_data,
1221 static_cast<int>(elf_section_entry_index),
1222 &elf_dynamic_entry) != &elf_dynamic_entry) {
1223 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1225 goto FailureEpilogue;
1226 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1229 elf_dynamic_entry_name = elf_strptr(
1230 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1231 if (elf_dynamic_entry_name ==
nullptr) {
1232 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1233 "string entry", file));
1234 goto FailureEpilogue;
1236 wxString name_full(elf_dynamic_entry_name);
1237 wxString name_part(elf_dynamic_entry_name,
1238 strcspn(elf_dynamic_entry_name,
"-."));
1239 if (dependencies.find(name_part) != dependencies.end()) {
1240 info.dependencies.insert(
1241 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1246 goto SuccessEpilogue;
1249 elf_end(elf_handle);
1254 if (elf_handle !=
nullptr) elf_end(elf_handle);
1255 if (file_handle >= 0) close(file_handle);
1256 wxLog::FlushActive();
1262 bool b_compat =
false;
1278 if (!m_found_wxwidgets) {
1279 DWORD myPid = GetCurrentProcessId();
1281 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1282 if (hProcess == NULL) {
1283 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1284 plugin_file.c_str()));
1288 HMODULE hMods[1024];
1290 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1291 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1292 TCHAR szModName[MAX_PATH];
1293 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1294 sizeof(szModName) /
sizeof(TCHAR))) {
1295 m_module_name = szModName;
1296 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1297 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1298 m_found_wxwidgets =
true;
1299 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1300 m_module_name.c_str()));
1307 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1308 plugin_file.c_str()));
1310 if (hProcess) CloseHandle(hProcess);
1313 if (!m_found_wxwidgets) {
1314 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1315 plugin_file.c_str()));
1317 LPCWSTR fName = plugin_file.wc_str();
1318 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1319 FILE_ATTRIBUTE_NORMAL, 0);
1320 DWORD byteread, size = GetFileSize(handle, NULL);
1321 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1322 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1323 CloseHandle(handle);
1324 PIMAGE_NT_HEADERS ntheaders =
1325 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1326 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1327 PIMAGE_SECTION_HEADER pSech =
1328 IMAGE_FIRST_SECTION(ntheaders);
1329 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1330 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1335 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1337 ntheaders->OptionalHeader
1339 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1346 while (pImportDescriptor->Name != 0) {
1349 (PCHAR)((DWORD_PTR)virtualpointer +
1350 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1353 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1356 wxLogMessage(wxString::Format(
1357 "Compatible wxWidgets plugin library found for %s: %s",
1358 plugin_file.c_str(), libname[i]));
1361 pImportDescriptor++;
1366 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1368 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1371#if defined(__WXGTK__) || defined(__WXQT__)
1372#if defined(USE_LIBELF)
1374 static bool b_own_info_queried =
false;
1375 static bool b_own_info_usable =
false;
1377 static ModuleInfo::DependencySet dependencies;
1379 if (!b_own_info_queried) {
1380 dependencies.insert(
"libwx_baseu");
1382 char exe_buf[100] = {0};
1383 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1385 exe_buf[len] =
'\0';
1386 wxString app_path(exe_buf);
1387 wxLogMessage(
"Executable path: %s", exe_buf);
1389 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1390 if (!b_own_info_usable) {
1391 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1394 wxLogMessage(
"Cannot get own executable path.");
1396 b_own_info_queried =
true;
1399 if (b_own_info_usable) {
1400 bool b_pi_info_usable =
false;
1403 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1404 if (b_pi_info_usable) {
1405 b_compat = (pi_info.type_magic == own_info.type_magic);
1408 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1413 pi_info.dependencies.clear();
1415 wxString::Format(
" Plugin \"%s\" is of another binary "
1416 "flavor than the main module.",
1418 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1419 own_info.type_magic, pi_info.type_magic);
1421 for (
const auto& own_dependency : own_info.dependencies) {
1422 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1423 pi_info.dependencies.find(own_dependency.first);
1424 if ((pi_dependency != pi_info.dependencies.end()) &&
1425 (pi_dependency->second != own_dependency.second)) {
1428 " Plugin \"%s\" depends on library \"%s\", but the main "
1429 "module was built for \"%s\".",
1430 plugin_file, pi_dependency->second, own_dependency.second);
1437 wxString::Format(
" Plugin \"%s\" could not be reliably "
1438 "checked for compatibility.",
1446 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1447 b_compat ?
"true" :
"false");
1449 wxLog::FlushActive();
1464 FILE* f = fopen(plugin_file,
"r");
1467 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1471#
if defined(__WXGTK3__)
1472 "libwx_gtk3u_core-%i.%i"
1473#elif defined(__WXGTK20__)
1474 "libwx_gtk2u_core-%i.%i"
1475#elif defined(__WXQT__)
1476 "libwx_qtu_core-%i.%i"
1478#error undefined plugin platform
1481 wxMAJOR_VERSION, wxMINOR_VERSION);
1485 size_t len(strlen(strver));
1487 while ((c = fgetc(f)) != EOF) {
1488 if (c == strver[pos]) {
1499 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1515 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1517 if (plugin_file.empty()) {
1518 wxLogMessage(
"Ignoring loading of empty path");
1522 if (!wxIsReadable(plugin_file)) {
1523 wxLogMessage(
"Ignoring unreadable plugin %s",
1524 plugin_file.ToStdString().c_str());
1525 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1526 load_errors.push_back(le);
1533 pic->m_version_major, pic->m_version_minor);
1534 if (sts != plug_status::unblocked) {
1535 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1539 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1540 if (!data.name.empty()) {
1541 wxLogDebug(
"Refusing to load blacklisted library: %s",
1542 plugin_file.ToStdString().c_str());
1550 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1551 pic->m_library.Load(plugin_file);
1553 if (!pic->m_library.IsLoaded()) {
1556 wxFileName fn(plugin_file);
1557 std::string name = fn.GetName().ToStdString();
1558 auto found = m_blacklist->get_library_data(name);
1559 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1560 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1561 if (!found.name.empty()) {
1563 LoadError le(LoadError::Type::Unloadable, name, v);
1564 load_errors.push_back(le);
1566 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1567 load_errors.push_back(le);
1570 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1576 const char*
const FIX_LOADING =
1577 _(
"\n Install/uninstall plugin or remove file to mute message");
1578 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1579 if (
nullptr == create_plugin) {
1580 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1581 wxLogMessage(msg + plugin_file);
1582 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1583 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1584 load_errors.push_back(le);
1589 destroy_t* destroy_plugin =
1590 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1591 pic->m_destroy_fn = destroy_plugin;
1592 if (
nullptr == destroy_plugin) {
1593 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1595 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1596 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1597 load_errors.push_back(le);
1607 int api_ver = (api_major * 100) + api_minor;
1608 pic->m_api_version = api_ver;
1616 wxLogDebug(
"blacklist: Get status for %s %d %d",
1617 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1619 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1620 if (status != plug_status::unblocked) {
1621 wxLogDebug(
"Ignoring blacklisted plugin.");
1622 if (status != plug_status::unloadable) {
1624 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1625 load_errors.push_back(le);
1712 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1713 p->GetPlugInVersionBuild());
1716 if (!pic->m_pplugin) {
1717 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1718 INFO_LOG << _(
" API Version detected: ");
1719 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1720 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1721 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1723 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1725 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.
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.
obs::EventVar evt_load_plugin
Notified with a PlugInContainer* pointer when a plugin is loaded.
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.
obs::EventVar evt_deactivate_plugin
Notified with plugin name when it's deactivated.
void UpdateManagedPlugins(bool keep_orphans)
Update all managed plugins i.
obs::EventVar evt_update_chart_types
Notified without data after all plugins loaded ot updated.
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.
obs::EventVar evt_plugin_loadall_finalize
Emitted after all plugins are loaded.
void SetSetupOptions(const wxString &common_name, bool value)
Update m_has_setup_options state for plugin with given name.
obs::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.
obs::EventVar evt_pluglist_change
Notified without data when the GetPlugInArray() list is changed.
bool DeactivateAllPlugIns()
Deactivate all plugins.
PlugInContainer * LoadPlugIn(const wxString &plugin_file)
Load given plugin file from disk into GetPlugInArray() list.
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.
Wrapper for configuration variables which lives in a wxBaseConfig object.
void Notify() override
Notify all listeners, no data supplied.
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.
Notify()/Listen() configuration variable wrapper.
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)
bool GetMode()
Return true if we are running in safe mode.
#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.