File indexing completed on 2025-01-18 10:17:35
0001
0002
0003
0004
0005
0006 #include "JPluginLoader.h"
0007 #include "JComponentManager.h"
0008 #include "JParameterManager.h"
0009 #include <JANA/JVersion.h>
0010
0011 #include <cstdlib>
0012 #include <dlfcn.h>
0013 #include <iostream>
0014 #include <sstream>
0015 #include <unistd.h>
0016 #include <set>
0017 #include <memory>
0018
0019 class JApplication;
0020
0021 void JPluginLoader::Init() {
0022
0023 m_params->SetDefaultParameter("plugins", m_plugins_from_parameter, "Comma-separated list of plugins to load.");
0024 m_params->SetDefaultParameter("plugins_to_ignore", m_plugins_to_exclude, "Comma-separated list of plugins to NOT load, even if they are specified in 'plugins'.");
0025 m_params->SetDefaultParameter("jana:plugin_path", m_plugin_paths_str, "Colon-separated list of paths to search for plugins");
0026 m_params->SetDefaultParameter("jana:debug_plugin_loading", m_verbose, "Trace the plugin search path and display any loading errors");
0027
0028 if (m_verbose) {
0029
0030
0031 GetLogger().level = JLogger::Level::TRACE;
0032 }
0033 }
0034
0035
0036
0037 void JPluginLoader::add_plugin(std::string plugin_name) {
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050 m_all_plugins_requested.push(plugin_name);
0051 }
0052
0053 void JPluginLoader::resolve_plugin_paths() {
0054
0055
0056
0057 std::stringstream param_ss(m_plugin_paths_str);
0058 std::string path;
0059 while (getline(param_ss, path, ':')) add_plugin_path(path);
0060
0061
0062 if (const char* jpp = getenv("JANA_PLUGIN_PATH")) {
0063 std::stringstream envvar_ss(jpp);
0064 while (getline(envvar_ss, path, ':')) add_plugin_path(path);
0065 }
0066
0067
0068 add_plugin_path(JVersion::GetInstallDir() + "/lib/JANA/plugins");
0069 }
0070
0071
0072 void JPluginLoader::add_plugin_path(std::string path) {
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087 for (std::string& n : m_plugin_paths) {
0088 if (n == path) {
0089 return;
0090 }
0091 }
0092 m_plugin_paths.push_back(path);
0093 }
0094
0095
0096 void JPluginLoader::attach_plugins(JComponentManager* jcm) {
0097
0098
0099
0100
0101
0102 resolve_plugin_paths();
0103
0104
0105
0106 std::set<std::string> exclusions(m_plugins_to_exclude.begin(), m_plugins_to_exclude.end());
0107
0108
0109
0110
0111
0112 for (const auto& plugin_name : m_plugins_from_parameter) {
0113 m_all_plugins_requested.push(plugin_name);
0114 }
0115
0116 while (!m_all_plugins_requested.empty()) {
0117
0118 const std::string name = m_all_plugins_requested.front();
0119 m_all_plugins_requested.pop();
0120
0121 std::ostringstream paths_checked;
0122
0123 if (!is_valid_plugin_name(name)) {
0124 throw JException("Invalid plugin name: '%s'. Should not be a path or have an extension.", name.c_str());
0125 }
0126
0127 if (exclusions.find(name) != exclusions.end()) {
0128 LOG_INFO(m_logger) << "Excluding plugin `" << name << "`" << LOG_END;
0129 continue;
0130 }
0131 if (m_plugin_index.find(name) != m_plugin_index.end()) {
0132
0133 LOG_DEBUG(m_logger) << "Ignoring already-loaded plugin `" << name << "`" << LOG_END;
0134 continue;
0135 }
0136
0137 std::string path = find_first_valid_path(name, paths_checked);
0138
0139
0140
0141 if (path.empty()) {
0142 LOG_ERROR(m_logger)
0143 << "Couldn't find plugin '" << name << "'\n"
0144 << " Make sure that the plugin search path is correctly set using the 'jana:plugin_path' parameter or the JANA_PLUGIN_PATH environment variable.\n"
0145 << " Paths checked:\n" << paths_checked.str() << LOG_END;
0146 throw JException("Couldn't find plugin '%s'", name.c_str());
0147 }
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159 jcm->next_plugin(name);
0160 attach_plugin(name, path);
0161 }
0162 }
0163
0164
0165 void JPluginLoader::attach_plugin(std::string name, std::string path) {
0166
0167
0168
0169
0170
0171
0172
0173
0174 dlerror();
0175 void* handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL | RTLD_NODELETE);
0176 if (!handle) {
0177 std::string err = dlerror();
0178 LOG_ERROR(m_logger) << "Plugin \"" << name << "\" dlopen() failed: " << err << LOG_END;
0179 throw JException("Plugin '%s' dlopen() failed: %s", name.c_str(), err.c_str());
0180 }
0181
0182
0183 typedef void InitPlugin_t(JApplication* app);
0184 InitPlugin_t* initialize_proc = (InitPlugin_t*) dlsym(handle, "InitPlugin");
0185 if (!initialize_proc) {
0186 dlclose(handle);
0187 LOG_ERROR(m_logger) << "Plugin \"" << name << "\" is missing 'InitPlugin' symbol" << LOG_END;
0188 throw JException("Plugin '%s' is missing 'InitPlugin' symbol", name.c_str());
0189 }
0190
0191
0192 LOG_WARN(m_logger) << "Loading plugin '" << name << "' from '" << path << "'" << LOG_END;
0193
0194 try {
0195 (*initialize_proc)(GetApplication());
0196 }
0197 catch (JException& ex) {
0198 if (ex.function_name.empty()) ex.function_name = "attach_plugin";
0199 if (ex.type_name.empty()) ex.type_name = "JPluginLoader";
0200 if (ex.instance_name.empty()) ex.instance_name = m_prefix;
0201 if (ex.plugin_name.empty()) ex.plugin_name = name;
0202 throw ex;
0203 }
0204 catch (std::exception& e) {
0205 auto ex = JException(e.what());
0206 ex.exception_type = JTypeInfo::demangle_current_exception_type();
0207 ex.nested_exception = std::current_exception();
0208 ex.function_name = "attach_plugin";
0209 ex.type_name = "JPluginLoader";
0210 ex.instance_name = m_prefix;
0211 ex.plugin_name = name;
0212 throw ex;
0213 }
0214 catch (...) {
0215 auto ex = JException("Unknown exception");
0216 ex.exception_type = JTypeInfo::demangle_current_exception_type();
0217 ex.nested_exception = std::current_exception();
0218 ex.function_name = "attach_plugin";
0219 ex.type_name = "JPluginLoader";
0220 ex.instance_name = m_prefix;
0221 ex.plugin_name = name;
0222 throw ex;
0223 }
0224
0225
0226 auto plugin = std::make_unique<JPlugin>(name, path);
0227 plugin->m_app = GetApplication();
0228 plugin->m_logger = m_logger;
0229 plugin->m_handle = handle;
0230 m_plugin_index[name] = plugin.get();
0231 m_plugins.push_front(std::move(plugin));
0232 }
0233
0234
0235 JPlugin::~JPlugin() {
0236
0237
0238 typedef void FinalizePlugin_t(JApplication* app);
0239 FinalizePlugin_t* finalize_proc = (FinalizePlugin_t*) dlsym(m_handle, "FinalizePlugin");
0240 if (finalize_proc) {
0241 LOG_INFO(m_logger) << "Finalizing plugin \"" << m_name << "\"" << LOG_END;
0242 (*finalize_proc)(m_app);
0243 }
0244
0245
0246 dlclose(m_handle);
0247 LOG_DEBUG(m_logger) << "Unloaded plugin \"" << m_name << "\"" << LOG_END;
0248 }
0249
0250 bool JPluginLoader::is_valid_plugin_name(const std::string& plugin_name) const {
0251
0252 if (plugin_name.size() > 2 && plugin_name.substr(plugin_name.size() - 3) == ".so") return false;
0253 if (plugin_name.size() > 5 && plugin_name.substr(plugin_name.size() - 6) == ".dylib") return false;
0254 if (plugin_name.find('/') != std::string::npos) return false;
0255 return true;
0256 }
0257
0258 bool ends_with(const std::string& s, char c) {
0259
0260 if (s.empty()) return false;
0261 return s.back() == c;
0262 }
0263
0264 std::string JPluginLoader::make_path_from_name(const std::string& name, const std::string& path_prefix) const {
0265 std::ostringstream oss;
0266 oss << path_prefix;
0267 if (!ends_with(path_prefix, '/')) {
0268 oss << "/";
0269 }
0270 oss << name;
0271 oss << ".so";
0272 return oss.str();
0273 }
0274
0275 std::string JPluginLoader::find_first_valid_path(const std::string& name, std::ostringstream& debug_log) const {
0276
0277 for (const std::string& path_prefix : m_plugin_paths) {
0278 auto path = make_path_from_name(name, path_prefix);
0279
0280 if (is_valid_path(path)) {
0281 debug_log << " " << path << " => Found" << std::endl;
0282 return path;
0283 }
0284 else {
0285 debug_log << " " << path << " => Not found" << std::endl;
0286 }
0287 }
0288 return "";
0289 }
0290
0291
0292 bool JPluginLoader::is_valid_path(const std::string& path) const {
0293 return (access(path.c_str(), F_OK) != -1);
0294 }
0295
0296