From c91e935c62b8e254b9daadf37b915c983518bff4 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Fri, 18 Apr 2025 16:25:35 +0000
Subject: [PATCH] add dynamic module load, more escape seq
---
src/Modules/ModuleManager.hpp | 134 ++++++++++++++++++++++++++++++++++++++------
1 files changed, 114 insertions(+), 20 deletions(-)
diff --git a/src/Modules/ModuleManager.hpp b/src/Modules/ModuleManager.hpp
index c694493..03b0a12 100644
--- a/src/Modules/ModuleManager.hpp
+++ b/src/Modules/ModuleManager.hpp
@@ -2,14 +2,25 @@
#ifndef MODULES_MODULEMANAGER_HPP
#define MODULES_MODULEMANAGER_HPP
-#include <memory>
-#include <vector>
-#include "BaseModule.hpp"
+#include <filesystem>
#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
#include <unordered_map>
+#include <vector>
+
+#include "BaseModule.hpp"
#include "Symbols/Value.hpp"
+#ifndef _WIN32
+# include <dlfcn.h>
+#else
+# include <windows.h>
+#endif
+
namespace Modules {
+using CallbackFunction = std::function<Symbols::Value(const std::vector<Symbols::Value> &)>;
/**
* @brief Manager for registering and invoking modules.
@@ -19,7 +30,7 @@
/**
* @brief Get singleton instance of ModuleManager.
*/
- static ModuleManager &instance() {
+ static ModuleManager & instance() {
static ModuleManager mgr;
return mgr;
}
@@ -28,55 +39,138 @@
* @brief Add a module to the manager.
* @param module Unique pointer to a BaseModule.
*/
- void addModule(std::unique_ptr<BaseModule> module) {
- modules_.push_back(std::move(module));
- }
+ void addModule(std::unique_ptr<BaseModule> module) { modules_.push_back(std::move(module)); }
/**
* @brief Invoke all registered modules to register their symbols.
*/
void registerAll() {
- for (const auto &module : modules_) {
+ for (const auto & module : modules_) {
module->registerModule();
}
}
private:
ModuleManager() = default;
- std::vector<std::unique_ptr<BaseModule>> modules_;
+ std::vector<std::unique_ptr<BaseModule>> modules_;
// Built-in function callbacks: name -> function
- std::unordered_map<std::string,
- std::function<Symbols::Value(const std::vector<Symbols::Value>&)>> callbacks_;
+ std::unordered_map<std::string, std::function<Symbols::Value(const std::vector<Symbols::Value> &)>> callbacks_;
+ // Plugin handles for dynamically loaded modules
+ std::vector<void *> pluginHandles_;
public:
/**
* @brief Register a built-in function callback.
* @param name Name of the function.
* @param cb Callable taking argument values and returning a Value.
*/
- void registerFunction(const std::string &name,
- std::function<Symbols::Value(const std::vector<Symbols::Value>&)> cb) {
+ void registerFunction(const std::string & name,
+ std::function<Symbols::Value(const std::vector<Symbols::Value> &)> cb) {
callbacks_[name] = std::move(cb);
}
/**
* @brief Check if a built-in function is registered.
*/
- bool hasFunction(const std::string &name) const {
- return callbacks_.find(name) != callbacks_.end();
- }
+ bool hasFunction(const std::string & name) const { return callbacks_.find(name) != callbacks_.end(); }
/**
* @brief Call a built-in function callback.
*/
- Symbols::Value callFunction(const std::string &name,
- const std::vector<Symbols::Value> &args) const {
+ Symbols::Value callFunction(const std::string & name, const std::vector<Symbols::Value> & args) const {
auto it = callbacks_.find(name);
if (it == callbacks_.end()) {
throw std::runtime_error("Built-in function callback not found: " + name);
}
return it->second(args);
}
+
+ /**
+ * @brief Load all plugin modules from specified directory.
+ * @param directory Path to directory containing plugin shared libraries.
+ */
+ void loadPlugins(const std::string & directory) {
+ namespace fs = std::filesystem;
+ if (!fs::exists(directory) || !fs::is_directory(directory)) {
+ return;
+ }
+ // Recursively search for plugin shared libraries
+ for (const auto & entry : fs::recursive_directory_iterator(directory)) {
+ if (!entry.is_regular_file()) {
+ continue;
+ }
+#ifdef _WIN32
+ if (entry.path().extension() == ".dll") {
+#else
+ if (entry.path().extension() == ".so") {
+#endif
+ loadPlugin(entry.path().string());
+ }
+ }
+ }
+
+ /**
+ * @brief Load a single plugin module from shared library.
+ * @param path Filesystem path to the shared library.
+ */
+ void loadPlugin(const std::string & path) {
+#ifndef _WIN32
+ void * handle = dlopen(path.c_str(), RTLD_NOW);
+ if (!handle) {
+ throw std::runtime_error("Failed to load module: " + path + ": " + dlerror());
+ }
+#else
+ HMODULE handle = LoadLibraryA(path.c_str());
+ if (!handle) {
+ throw std::runtime_error("Failed to load module: " + path);
+ }
+#endif
+ pluginHandles_.push_back(handle);
+
+#ifndef _WIN32
+ dlerror(); // clear any existing error
+ using PluginInitFunc = void (*)();
+ auto initFunc = reinterpret_cast<PluginInitFunc>(dlsym(handle, "plugin_init"));
+ const char * dlsymError = dlerror();
+ if (dlsymError) {
+ dlclose(handle);
+ pluginHandles_.pop_back();
+ throw std::runtime_error("Cannot find symbol 'plugin_init' in " + path + ": " + dlsymError);
+ }
+ initFunc();
+#else
+ using PluginInitFunc = void(__cdecl *)();
+ auto initFunc = reinterpret_cast<PluginInitFunc>(GetProcAddress(handle, "plugin_init"));
+ if (!initFunc) {
+ FreeLibrary(handle);
+ pluginHandles_.pop_back();
+ throw std::runtime_error("Cannot find symbol 'plugin_init' in " + path);
+ }
+ initFunc();
+#endif
+ }
+
+ /**
+ * @brief Destructor unloads modules and plugin libraries in safe order.
+ * Modules (and their callback functions) are destroyed before the libraries are unloaded,
+ * ensuring that destructors (in plugin code) run while the libraries are still mapped.
+ */
+ ~ModuleManager() {
+ // Destroy module instances (may call code in plugin libraries)
+ modules_.clear();
+ // Clear callback functions (may refer to plugin code)
+ callbacks_.clear();
+ // Unload all dynamically loaded plugin libraries
+ for (auto handle : pluginHandles_) {
+#ifndef _WIN32
+ dlclose(handle);
+#else
+ FreeLibrary((HMODULE) handle);
+#endif
+ }
+ // Clear handles
+ pluginHandles_.clear();
+ }
};
-} // namespace Modules
-#endif // MODULES_MODULEMANAGER_HPP
\ No newline at end of file
+} // namespace Modules
+#endif // MODULES_MODULEMANAGER_HPP
--
Gitblit v1.9.3