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>
69#include "observable/configvar.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 auto loader = PluginLoader::GetInstance();
210 auto pic = GetContainer(pd, *loader->GetPlugInArray());
216 metadata = pic->m_managed_metadata;
217 if (metadata.version ==
"")
219 std::string detail_suffix(metadata.is_imported ? _(
" [Imported]") :
"");
220 if (metadata.is_orphan) detail_suffix = _(
" [Orphan]");
224 if (pic->m_pplugin) {
232 v_major, v_minor, p->GetPlugInVersionPatch(), p->GetPlugInVersionPost(),
233 p->GetPlugInVersionPre(), p->GetPlugInVersionBuild());
234 return sv.to_string() + detail_suffix;
236 if (!metadata.is_orphan) {
237 std::string version = GetInstalledVersion(pd);
238 return version + detail_suffix;
240 return metadata.version + detail_suffix;
244PlugInContainer::PlugInContainer()
245 :
PlugInData(), m_pplugin(nullptr), m_library(), m_destroy_fn(nullptr) {}
248 : m_has_setup_options(false),
251 m_toolbox_panel(false),
261 m_version_major = v.major;
262 m_version_minor = v.minor;
263 m_managed_metadata = md;
264 m_status = PluginStatus::ManagedInstallAvailable;
288static void setLoadPath() {
291 auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
293 if (osSystemId & wxOS_UNIX_LINUX) {
296 if (wxGetEnv(
"LD_LIBRARY_PATH", &envPath)) {
297 path = path +
":" + envPath.ToStdString();
299 wxLogMessage(
"Using LD_LIBRARY_PATH: %s", path.c_str());
300 wxSetEnv(
"LD_LIBRARY_PATH", path.c_str());
301 }
else if (osSystemId & wxOS_WINDOWS) {
305 if (wxGetEnv(
"PATH", &envPath)) {
306 path = path +
";" + envPath.ToStdString();
308 wxLogMessage(
"Using PATH: %s", path);
309 wxSetEnv(
"PATH", path);
310 }
else if (osSystemId & wxOS_MAC) {
313 if (wxGetEnv(
"DYLD_LIBRARY_PATH", &envPath)) {
314 path = path +
":" + envPath.ToStdString();
316 wxLogMessage(
"Using DYLD_LIBRARY_PATH: %s", path.c_str());
317 wxSetEnv(
"DYLD_LIBRARY_PATH", path.c_str());
319 wxString os_name = wxPlatformInfo::Get().GetPortIdName();
320 if (os_name.Contains(
"wxQT")) {
321 wxLogMessage(
"setLoadPath() using Android library path");
323 wxLogWarning(
"SetLoadPath: Unsupported platform.");
325 if (osSystemId & wxOS_MAC || osSystemId & wxOS_UNIX_LINUX) {
329 wxGetEnv(
"PATH", &envPath);
330 path = path +
":" + envPath.ToStdString();
331 wxLogMessage(
"Using PATH: %s", path);
332 wxSetEnv(
"PATH", path);
338 wxString msg(
"PluginLoader: Calling LateInit PlugIn: ");
343 if (ppi) ppi->LateInit();
354PluginLoader::PluginLoader()
355 : m_blacklist(blacklist_factory()),
356 m_default_plugin_icon(nullptr),
358 m_found_wxwidgets(false),
364 for (
auto* pic : plugin_array) {
365 if (pic && pic->m_enabled && (pic->
m_common_name == commonName))
373 auto loader = PluginLoader::GetInstance();
374 auto pic = GetContainer(pd, *loader->GetPlugInArray());
378void PluginLoader::NotifySetupOptionsPlugin(
const PlugInData* pd) {
383 if (pic->m_enabled && pic->m_init_state) {
385 switch (pic->m_api_version) {
399 if (pic->m_pplugin) {
403 auto loader = PluginLoader::GetInstance();
417 for (
auto* pic : plugin_array) {
419 pic->m_enabled = enabled;
426 for (
auto* pic : plugin_array) {
428 pic->m_toolbox_panel = value;
432 wxLogMessage(
"Atttempt to update toolbox panel on non-existing plugin " +
437 for (
auto* pic : plugin_array) {
443 wxLogMessage(
"Atttempt to update setup options on non-existing plugin " +
447const wxBitmap* PluginLoader::GetPluginDefaultIcon() {
448 if (!m_default_plugin_icon) m_default_plugin_icon =
new wxBitmap(32, 32);
449 return m_default_plugin_icon;
452void PluginLoader::SetPluginDefaultIcon(
const wxBitmap* bitmap) {
453 delete m_default_plugin_icon;
454 m_default_plugin_icon = bitmap;
458 auto pic = GetContainer(pd, plugin_array);
460 wxLogMessage(
"Attempt to remove non-existing plugin %s",
464 plugin_array.Remove(pic);
468 return (*p1)->Key().compare((*p2)->Key());
473 plugin_array.Sort(ComparePlugins);
479 static const wxString sep = wxFileName::GetPathSeparator();
481 wxLogMessage(
"PluginLoader: loading plugins from %s",
ocpn::join(dirs,
';'));
483 bool any_dir_loaded =
false;
484 for (
const auto& dir : dirs) {
486 wxLogMessage(
"Loading plugins from dir: %s", wxdir.mb_str().data());
487 if (LoadPlugInDirectory(wxdir, load_enabled)) any_dir_loaded =
true;
497 auto errors = std::make_shared<std::vector<LoadError>>(load_errors);
500 return any_dir_loaded;
503bool PluginLoader::LoadPluginCandidate(
const wxString& file_name,
505 wxString plugin_file = wxFileName(file_name).GetFullName();
506 wxLogMessage(
"Checking plugin candidate: %s", file_name.mb_str().data());
508 wxString plugin_loadstamp = wxFileName(file_name).GetName();
509 if (!IsSystemPluginPath(plugin_file.ToStdString())) {
510 if (HasLoadStamp(plugin_loadstamp.ToStdString())) {
511 MESSAGE_LOG <<
"Refusing to load " << file_name
512 <<
" failed at last attempt";
515 CreateLoadStamp(plugin_loadstamp.ToStdString());
517 wxDateTime plugin_modification = wxFileName(file_name).GetModificationTime();
518 wxLog::FlushActive();
521 firebase::crashlytics::SetCustomKey(
"LoadPluginCandidate",
522 file_name.ToStdString().c_str());
531 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
540 plugin_array.Remove(pic_test);
544 pic_test->m_destroy_fn(pic_test->m_pplugin);
549 loaded_pic = pic_test;
554 loaded_pic = pic_test;
561 ClearLoadStamp(plugin_loadstamp.ToStdString());
566 wxFileName fn_plugin_file(file_name);
567 wxString plugin_file_path =
568 fn_plugin_file.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
570 if (!base_plugin_path.EndsWith(wxFileName::GetPathSeparator()))
571 base_plugin_path += wxFileName::GetPathSeparator();
575 if (!g_allow_arb_system_plugin) {
577 if (base_plugin_path.IsSameAs(plugin_file_path)) {
578 if (!IsSystemPluginPath(file_name.ToStdString())) {
579 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in "
582 ClearLoadStamp(plugin_loadstamp.ToStdString());
590 DEBUG_LOG <<
"Skipping plugin " << file_name <<
" in safe mode";
591 ClearLoadStamp(plugin_loadstamp.ToStdString());
596 std::string(
"Checking plugin compatibility: ") + file_name.ToStdString();
597 wxLogMessage(msg.c_str());
598 wxLog::FlushActive();
604 std::string(
"Incompatible plugin detected: ") + file_name.ToStdString();
605 wxLogMessage(msg.c_str());
606 if (m_blacklist->mark_unloadable(file_name.ToStdString())) {
607 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
608 load_errors.push_back(le);
618 const auto path = std::string(
"/PlugIns/") + plugin_file.ToStdString();
619 obs::ConfigVar<bool> enabled(path,
"bEnabled", TheBaseConfig());
620 if (pic && load_enabled && !enabled.Get(
true)) {
621 pic->m_destroy_fn(pic->m_pplugin);
623 wxLogMessage(
"Skipping not enabled candidate.");
624 ClearLoadStamp(plugin_loadstamp.ToStdString());
629 if (pic->m_pplugin) {
630 plugin_array.Add(pic);
637 pic->m_enabled = enabled.Get(
false);
640 !IsSystemPluginPath(file_name.ToStdString())) {
641 pic->m_enabled =
false;
644 if (
dynamic_cast<wxApp*
>(wxAppConsole::GetInstance())) {
646 if (pic->m_enabled) {
648 pic->m_init_state =
true;
652 wxLog::FlushActive();
654 std::string found_version;
657 found_version = p.readonly ?
"" : p.version;
666 m_on_activate_cb(pic);
670 pbm0 = (wxBitmap*)GetPluginDefaultIcon();
672 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
673 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
675 if (!pic->m_enabled && pic->m_destroy_fn) {
676 pic->m_destroy_fn(pic->m_pplugin);
677 pic->m_destroy_fn =
nullptr;
678 pic->m_pplugin =
nullptr;
679 pic->m_init_state =
false;
680 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
685 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
687 bool is_system = found != SYSTEM_PLUGINS.end();
693 available.begin(), available.end(),
696 if (it == available.end()) {
701 auto oprhan_metadata = CreateMetadata(pic);
702 auto catalogHdlr = CatalogHandler::GetInstance();
703 catalogHdlr->AddMetadataToActiveContext(oprhan_metadata);
709 " PluginLoader: Unloading invalid PlugIn, API version %d ",
711 pic->m_destroy_fn(pic->m_pplugin);
713 LoadError le(LoadError::Type::Unloadable, file_name.ToStdString());
715 load_errors.push_back(le);
721 ClearLoadStamp(plugin_loadstamp.ToStdString());
726bool PluginLoader::LoadPlugInDirectory(
const wxString& plugin_dir,
729 m_plugin_location = plugin_dir;
731 wxString msg(
"PluginLoader searching for PlugIns in location ");
732 msg += m_plugin_location;
736 wxString pispec =
"*_pi.dll";
737#elif defined(__WXOSX__)
738 wxString pispec =
"*_pi.dylib";
740 wxString pispec =
"*_pi.so";
743 if (!::wxDirExists(m_plugin_location)) {
744 msg = m_plugin_location;
745 msg.Prepend(
" Directory ");
746 msg.Append(
" does not exist.");
751 if (!
g_BasePlatform->isPlatformCapable(PLATFORM_CAP_PLUGINS))
return false;
753 wxArrayString file_list;
755 int get_flags = wxDIR_FILES | wxDIR_DIRS;
758 get_flags = wxDIR_FILES;
763 get_flags = wxDIR_FILES;
769 wxDir::GetAllFiles(m_plugin_location, &file_list, pispec, get_flags);
771 wxLogMessage(
"Found %d candidates", (
int)file_list.GetCount());
772 for (
auto& file_name : file_list) {
773 wxLog::FlushActive();
775 LoadPluginCandidate(file_name, load_enabled);
782 for (
unsigned int i = 0; i < plugin_array.GetCount(); i++) {
784 for (
unsigned int j = i + 1; j < plugin_array.GetCount(); j++) {
789 plugin_array.Item(i)->m_status = PluginStatus::PendingListRemoval;
791 plugin_array.Item(j)->m_status = PluginStatus::PendingListRemoval;
798 while ((i >= 0) && (i < plugin_array.GetCount())) {
800 if (pict->m_status == PluginStatus::PendingListRemoval) {
801 plugin_array.RemoveAt(i);
813 for (
const auto& pic : plugin_array) {
816 if (pic->m_pplugin) {
819 pic->m_pplugin =
nullptr;
820 pic->m_init_state =
false;
825 if (!pic->m_pplugin) {
826 if (pic->m_enabled) {
830 pic->m_status = stat;
831 pic->m_enabled =
true;
837 if (pic->m_enabled && !pic->m_init_state && pic->m_pplugin) {
838 wxString msg(
"PluginLoader: Initializing PlugIn: ");
845 pic->m_init_state =
true;
846 ProcessLateInit(pic);
852 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
853 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
854 m_on_activate_cb(pic);
856 }
else if (!pic->m_enabled && pic->m_init_state) {
859 pic->m_bitmap = wxBitmap(pbm0->GetSubBitmap(
860 wxRect(0, 0, pbm0->GetWidth(), pbm0->GetHeight())));
863 if (pic->m_pplugin) pic->m_destroy_fn(pic->m_pplugin);
864 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
865 pic->m_pplugin =
nullptr;
866 pic->m_init_state =
false;
875 if (!pic)
return false;
876 if (pic->m_init_state) {
877 wxString msg(
"PluginLoader: Deactivating PlugIn: ");
879 m_on_deactivate_cb(pic);
880 pic->m_init_state =
false;
889 auto pic = GetContainer(pd, plugin_array);
891 wxLogError(
"Attempt to deactivate non-existing plugin %s",
899 if (ix >= plugin_array.GetCount()) {
900 wxLogWarning(
"Attempt to remove non-existing plugin %d", ix);
907 if (pic->m_pplugin) {
908 pic->m_destroy_fn(pic->m_pplugin);
912 plugin_array.RemoveAt(ix);
916static std::string VersionFromManifest(
const std::string& plugin_name) {
919 if (!path.empty() && wxFileName::IsFileReadable(path)) {
920 std::ifstream stream;
921 stream.open(path, std::ifstream::in);
930 if (name.empty())
return {};
933 if (isRegularFile(import_path.c_str())) {
934 std::ifstream f(import_path.c_str());
935 std::stringstream ss;
938 ParsePlugin(ss.str(), pd);
939 pd.is_imported =
true;
944 vector<PluginMetadata> matches;
945 copy_if(available.begin(), available.end(), back_inserter(matches),
947 if (matches.size() == 0)
return {};
948 if (matches.size() == 1)
return matches[0];
950 auto version = VersionFromManifest(name);
952 return version == md.version;
954 auto found = find_if(matches.begin(), matches.end(), predicate);
955 return found != matches.end() ? *found : matches[0];
961 if (name.empty())
return {};
964 vector<PluginMetadata> matches;
965 copy_if(available.begin(), available.end(), back_inserter(matches),
967 if (matches.size() == 0)
return {};
968 if (matches.size() == 1)
return matches[0];
972 auto rv = matches[0];
973 for (
auto p : matches) {
975 if (catVersion > version) {
976 version = catVersion;
987 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
989 bool is_system = found != SYSTEM_PLUGINS.end();
991 std::string installed = VersionFromManifest(md.name);
998 else if (plugin->m_status == PluginStatus::Imported)
1000 else if (installedVersion < metaVersion)
1001 plugin->m_status = PluginStatus::ManagedInstalledUpdateAvailable;
1002 else if (installedVersion == metaVersion)
1003 plugin->m_status = PluginStatus::ManagedInstalledCurrentVersion;
1005 plugin->m_status = PluginStatus::ManagedInstalledDowngradeAvailable;
1009 plugin->m_managed_metadata = md;
1013 std::vector<PlugInContainer*> loaded_plugins;
1014 for (
auto& p : plugin_array) loaded_plugins.push_back(p);
1017 for (
auto& p : loaded_plugins) {
1018 auto found = std::find(SYSTEM_PLUGINS.begin(), SYSTEM_PLUGINS.end(),
1019 p->m_common_name.Lower().ToStdString());
1020 bool is_system = found != SYSTEM_PLUGINS.end();
1023 if (!keep_orphans) {
1030 return md.name.empty() && !md.is_imported && !pd->m_pplugin &&
1034 std::remove_if(loaded_plugins.begin(), loaded_plugins.end(), predicate);
1035 loaded_plugins.erase(end, loaded_plugins.end());
1039 for (
auto& plugin : loaded_plugins) {
1042 if (!md.name.empty()) {
1044 md.is_imported = isRegularFile(import_path.c_str());
1045 if (md.is_imported) {
1046 plugin->m_status = PluginStatus::Imported;
1050 }
else if (IsSystemPluginName(md.name)) {
1052 }
else if (md.is_orphan) {
1054 }
else if (plugin->m_api_version) {
1057 plugin->m_status = PluginStatus::LegacyUpdateAvailable;
1058 plugin->m_managed_metadata = md;
1061 plugin->m_status = PluginStatus::ManagedInstallAvailable;
1066 plugin_array.Clear();
1067 for (
const auto& p : loaded_plugins) plugin_array.Add(p);
1073 while (plugin_array.GetCount()) {
1082 for (
auto* pic : plugin_array) {
1090DWORD Rva2Offset(DWORD rva, PIMAGE_SECTION_HEADER psh, PIMAGE_NT_HEADERS pnt) {
1092 PIMAGE_SECTION_HEADER pSeh;
1097 for (i = 0; i < pnt->FileHeader.NumberOfSections; i++) {
1098 if (rva >= pSeh->VirtualAddress &&
1099 rva < pSeh->VirtualAddress + pSeh->Misc.VirtualSize) {
1104 return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
1111 WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual, DependencySet);
1112 WX_DECLARE_HASH_MAP(wxString, wxString, wxStringHash, wxStringEqual,
1115 uint64_t type_magic;
1116 DependencyMap dependencies;
1120bool ReadModuleInfoFromELF(
const wxString& file,
1121 const ModuleInfo::DependencySet& dependencies,
1123 static bool b_libelf_initialized =
false;
1124 static bool b_libelf_usable =
false;
1126 if (b_libelf_usable) {
1128 }
else if (b_libelf_initialized) {
1130 }
else if (elf_version(EV_CURRENT) == EV_NONE) {
1131 b_libelf_initialized =
true;
1132 b_libelf_usable =
false;
1133 wxLogError(
"LibELF is outdated.");
1136 b_libelf_initialized =
true;
1137 b_libelf_usable =
true;
1141 Elf* elf_handle =
nullptr;
1142 GElf_Ehdr elf_file_header;
1143 Elf_Scn* elf_section_handle =
nullptr;
1145 file_handle = open(file, O_RDONLY);
1146 if (file_handle == -1) {
1147 wxLogMessage(
"Could not open file \"%s\" for reading: %s", file,
1149 goto FailureEpilogue;
1152 elf_handle = elf_begin(file_handle, ELF_C_READ,
nullptr);
1153 if (elf_handle ==
nullptr) {
1154 wxLogMessage(
"Could not get ELF structures from \"%s\".", file);
1155 goto FailureEpilogue;
1158 if (gelf_getehdr(elf_handle, &elf_file_header) != &elf_file_header) {
1159 wxLogMessage(
"Could not get ELF file header from \"%s\".", file);
1160 goto FailureEpilogue;
1163 switch (elf_file_header.e_type) {
1168 wxLogMessage(wxString::Format(
1169 "Module \"%s\" is not an executable or shared library.", file));
1170 goto FailureEpilogue;
1174 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_CLASS])
1176 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_DATA])
1178 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_OSABI])
1180 (
static_cast<uint64_t
>(elf_file_header.e_ident[EI_ABIVERSION])
1182 (
static_cast<uint64_t
>(elf_file_header.e_machine)
1186 while ((elf_section_handle = elf_nextscn(elf_handle, elf_section_handle)) !=
1188 GElf_Shdr elf_section_header;
1189 Elf_Data* elf_section_data =
nullptr;
1190 size_t elf_section_entry_count = 0;
1192 if (gelf_getshdr(elf_section_handle, &elf_section_header) !=
1193 &elf_section_header) {
1194 wxLogMessage(
"Could not get ELF section header from \"%s\".", file);
1195 goto FailureEpilogue;
1196 }
else if (elf_section_header.sh_type != SHT_DYNAMIC) {
1200 elf_section_data = elf_getdata(elf_section_handle,
nullptr);
1201 if (elf_section_data ==
nullptr) {
1202 wxLogMessage(
"Could not get ELF section data from \"%s\".", file);
1203 goto FailureEpilogue;
1206 if ((elf_section_data->d_size == 0) ||
1207 (elf_section_header.sh_entsize == 0)) {
1208 wxLogMessage(
"Got malformed ELF section metadata from \"%s\".", file);
1209 goto FailureEpilogue;
1212 elf_section_entry_count =
1213 elf_section_data->d_size / elf_section_header.sh_entsize;
1214 for (
size_t elf_section_entry_index = 0;
1215 elf_section_entry_index < elf_section_entry_count;
1216 ++elf_section_entry_index) {
1217 GElf_Dyn elf_dynamic_entry;
1218 const char* elf_dynamic_entry_name =
nullptr;
1219 if (gelf_getdyn(elf_section_data,
1220 static_cast<int>(elf_section_entry_index),
1221 &elf_dynamic_entry) != &elf_dynamic_entry) {
1222 wxLogMessage(
"Could not get ELF dynamic_section entry from \"%s\".",
1224 goto FailureEpilogue;
1225 }
else if (elf_dynamic_entry.d_tag != DT_NEEDED) {
1228 elf_dynamic_entry_name = elf_strptr(
1229 elf_handle, elf_section_header.sh_link, elf_dynamic_entry.d_un.d_val);
1230 if (elf_dynamic_entry_name ==
nullptr) {
1231 wxLogMessage(wxString::Format(
"Could not get %s %s from \"%s\".",
"ELF",
1232 "string entry", file));
1233 goto FailureEpilogue;
1235 wxString name_full(elf_dynamic_entry_name);
1236 wxString name_part(elf_dynamic_entry_name,
1237 strcspn(elf_dynamic_entry_name,
"-."));
1238 if (dependencies.find(name_part) != dependencies.end()) {
1239 info.dependencies.insert(
1240 ModuleInfo::DependencyMap::value_type(name_part, name_full));
1245 goto SuccessEpilogue;
1248 elf_end(elf_handle);
1253 if (elf_handle !=
nullptr) elf_end(elf_handle);
1254 if (file_handle >= 0) close(file_handle);
1255 wxLog::FlushActive();
1261 bool b_compat =
false;
1277 if (!m_found_wxwidgets) {
1278 DWORD myPid = GetCurrentProcessId();
1280 OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, myPid);
1281 if (hProcess == NULL) {
1282 wxLogMessage(wxString::Format(
"Cannot identify running process for %s",
1283 plugin_file.c_str()));
1287 HMODULE hMods[1024];
1289 if (EnumProcessModules(hProcess, hMods,
sizeof(hMods), &cbNeeded)) {
1290 for (
int i = 0; i < (cbNeeded /
sizeof(HMODULE)); i++) {
1291 TCHAR szModName[MAX_PATH];
1292 if (GetModuleFileNameEx(hProcess, hMods[i], szModName,
1293 sizeof(szModName) /
sizeof(TCHAR))) {
1294 m_module_name = szModName;
1295 if (m_module_name.Find(
"wxmsw") != wxNOT_FOUND) {
1296 if (m_module_name.Find(
"_core_") != wxNOT_FOUND) {
1297 m_found_wxwidgets =
true;
1298 wxLogMessage(wxString::Format(
"Found wxWidgets core DLL: %s",
1299 m_module_name.c_str()));
1306 wxLogMessage(wxString::Format(
"Cannot enumerate process modules for %s",
1307 plugin_file.c_str()));
1309 if (hProcess) CloseHandle(hProcess);
1312 if (!m_found_wxwidgets) {
1313 wxLogMessage(wxString::Format(
"Cannot identify wxWidgets core DLL for %s",
1314 plugin_file.c_str()));
1316 LPCWSTR fName = plugin_file.wc_str();
1317 HANDLE handle = CreateFile(fName, GENERIC_READ, 0, 0, OPEN_EXISTING,
1318 FILE_ATTRIBUTE_NORMAL, 0);
1319 DWORD byteread, size = GetFileSize(handle, NULL);
1320 PVOID virtualpointer = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
1321 bool status = ReadFile(handle, virtualpointer, size, &byteread, NULL);
1322 CloseHandle(handle);
1323 PIMAGE_NT_HEADERS ntheaders =
1324 (PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) +
1325 PIMAGE_DOS_HEADER(virtualpointer)->e_lfanew);
1326 PIMAGE_SECTION_HEADER pSech =
1327 IMAGE_FIRST_SECTION(ntheaders);
1328 PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor;
1329 if (ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
1334 (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +
1336 ntheaders->OptionalHeader
1338 [IMAGE_DIRECTORY_ENTRY_IMPORT]
1345 while (pImportDescriptor->Name != 0) {
1348 (PCHAR)((DWORD_PTR)virtualpointer +
1349 Rva2Offset(pImportDescriptor->Name, pSech, ntheaders));
1352 if (m_module_name.Find(libname[i]) != wxNOT_FOUND) {
1355 wxLogMessage(wxString::Format(
1356 "Compatible wxWidgets plugin library found for %s: %s",
1357 plugin_file.c_str(), libname[i]));
1360 pImportDescriptor++;
1365 wxString::Format(
"No Import Table! in %s", plugin_file.c_str()));
1367 if (virtualpointer) VirtualFree(virtualpointer, size, MEM_DECOMMIT);
1370#if defined(__WXGTK__) || defined(__WXQT__)
1371#if defined(USE_LIBELF)
1373 static bool b_own_info_queried =
false;
1374 static bool b_own_info_usable =
false;
1376 static ModuleInfo::DependencySet dependencies;
1378 if (!b_own_info_queried) {
1379 dependencies.insert(
"libwx_baseu");
1381 char exe_buf[100] = {0};
1382 ssize_t len = readlink(
"/proc/self/exe", exe_buf, 99);
1384 exe_buf[len] =
'\0';
1385 wxString app_path(exe_buf);
1386 wxLogMessage(
"Executable path: %s", exe_buf);
1388 ReadModuleInfoFromELF(app_path, dependencies, own_info);
1389 if (!b_own_info_usable) {
1390 wxLogMessage(
"Cannot get own info from: %s", exe_buf);
1393 wxLogMessage(
"Cannot get own executable path.");
1395 b_own_info_queried =
true;
1398 if (b_own_info_usable) {
1399 bool b_pi_info_usable =
false;
1402 ReadModuleInfoFromELF(plugin_file, dependencies, pi_info);
1403 if (b_pi_info_usable) {
1404 b_compat = (pi_info.type_magic == own_info.type_magic);
1407 if ((pi_info.type_magic ^ own_info.type_magic) == 0x00030000) {
1412 pi_info.dependencies.clear();
1414 wxString::Format(
" Plugin \"%s\" is of another binary "
1415 "flavor than the main module.",
1417 wxLogMessage(
"host magic: %.8x, plugin magic: %.8x",
1418 own_info.type_magic, pi_info.type_magic);
1420 for (
const auto& own_dependency : own_info.dependencies) {
1421 ModuleInfo::DependencyMap::const_iterator pi_dependency =
1422 pi_info.dependencies.find(own_dependency.first);
1423 if ((pi_dependency != pi_info.dependencies.end()) &&
1424 (pi_dependency->second != own_dependency.second)) {
1427 " Plugin \"%s\" depends on library \"%s\", but the main "
1428 "module was built for \"%s\".",
1429 plugin_file, pi_dependency->second, own_dependency.second);
1436 wxString::Format(
" Plugin \"%s\" could not be reliably "
1437 "checked for compatibility.",
1445 wxLogMessage(
"Plugin is compatible by elf library scan: %s",
1446 b_compat ?
"true" :
"false");
1448 wxLog::FlushActive();
1463 FILE* f = fopen(plugin_file,
"r");
1466 wxLogMessage(
"Plugin %s can't be opened", plugin_file);
1470#
if defined(__WXGTK3__)
1471 "libwx_gtk3u_core-%i.%i"
1472#elif defined(__WXGTK20__)
1473 "libwx_gtk2u_core-%i.%i"
1474#elif defined(__WXQT__)
1475 "libwx_qtu_core-%i.%i"
1477#error undefined plugin platform
1480 wxMAJOR_VERSION, wxMINOR_VERSION);
1484 size_t len(strlen(strver));
1486 while ((c = fgetc(f)) != EOF) {
1487 if (c == strver[pos]) {
1498 wxLogMessage(
"Plugin is compatible: %s", b_compat ?
"true" :
"false");
1514 wxLogMessage(wxString(
"PluginLoader: Loading PlugIn: ") + plugin_file);
1516 if (plugin_file.empty()) {
1517 wxLogMessage(
"Ignoring loading of empty path");
1521 if (!wxIsReadable(plugin_file)) {
1522 wxLogMessage(
"Ignoring unreadable plugin %s",
1523 plugin_file.ToStdString().c_str());
1524 LoadError le(LoadError::Type::Unreadable, plugin_file.ToStdString());
1525 load_errors.push_back(le);
1532 pic->m_version_major, pic->m_version_minor);
1533 if (sts != plug_status::unblocked) {
1534 wxLogDebug(
"Refusing to load blacklisted plugin: %s",
1538 auto data = m_blacklist->get_library_data(plugin_file.ToStdString());
1539 if (!data.name.empty()) {
1540 wxLogDebug(
"Refusing to load blacklisted library: %s",
1541 plugin_file.ToStdString().c_str());
1549 if (pic->m_library.IsLoaded()) pic->m_library.Unload();
1550 pic->m_library.Load(plugin_file);
1552 if (!pic->m_library.IsLoaded()) {
1555 wxFileName fn(plugin_file);
1556 std::string name = fn.GetName().ToStdString();
1557 auto found = m_blacklist->get_library_data(name);
1558 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1559 wxLogMessage(
"Ignoring blacklisted plugin %s", name.c_str());
1560 if (!found.name.empty()) {
1562 LoadError le(LoadError::Type::Unloadable, name, v);
1563 load_errors.push_back(le);
1565 LoadError le(LoadError::Type::Unloadable, plugin_file.ToStdString());
1566 load_errors.push_back(le);
1569 wxLogMessage(wxString(
" PluginLoader: Cannot load library: ") +
1575 const char*
const FIX_LOADING =
1576 _(
"\n Install/uninstall plugin or remove file to mute message");
1577 create_t* create_plugin = (create_t*)pic->m_library.GetSymbol(
"create_pi");
1578 if (
nullptr == create_plugin) {
1579 std::string msg(_(
" PluginLoader: Cannot load symbol create_pi: "));
1580 wxLogMessage(msg + plugin_file);
1581 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1582 LoadError le(LoadError::Type::NoCreate, plugin_file.ToStdString());
1583 load_errors.push_back(le);
1588 destroy_t* destroy_plugin =
1589 (destroy_t*)pic->m_library.GetSymbol(
"destroy_pi");
1590 pic->m_destroy_fn = destroy_plugin;
1591 if (
nullptr == destroy_plugin) {
1592 wxLogMessage(
" PluginLoader: Cannot load symbol destroy_pi: " +
1594 if (m_blacklist->mark_unloadable(plugin_file.ToStdString())) {
1595 LoadError le(LoadError::Type::NoDestroy, plugin_file.ToStdString());
1596 load_errors.push_back(le);
1606 int api_ver = (api_major * 100) + api_minor;
1607 pic->m_api_version = api_ver;
1615 wxLogDebug(
"blacklist: Get status for %s %d %d",
1616 pi_name.ToStdString().c_str(), pi_major, pi_minor);
1618 m_blacklist->get_status(pi_name.ToStdString(), pi_major, pi_minor);
1619 if (status != plug_status::unblocked) {
1620 wxLogDebug(
"Ignoring blacklisted plugin.");
1621 if (status != plug_status::unloadable) {
1623 LoadError le(LoadError::Type::Blacklisted, pi_name.ToStdString(), v);
1624 load_errors.push_back(le);
1711 p->GetPlugInVersionPost(), p->GetPlugInVersionPre(),
1712 p->GetPlugInVersionBuild());
1715 if (!pic->m_pplugin) {
1716 INFO_LOG << _(
"Incompatible plugin detected: ") << plugin_file <<
"\n";
1717 INFO_LOG << _(
" API Version detected: ");
1718 INFO_LOG << api_major <<
"." << api_minor <<
"\n";
1719 INFO_LOG << _(
" PlugIn Version detected: ") << pi_ver <<
"\n";
1720 if (m_blacklist->mark_unloadable(pi_name.ToStdString(), pi_ver.major,
1722 LoadError le(LoadError::Type::Incompatible, pi_name.ToStdString(),
1724 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.
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)
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.