# HG changeset patch # User David Demelier # Date 1472386352 -7200 # Node ID 11045c180db97c34c9d61a9c4975c00ecd842b62 # Parent e0f58cfbd45ac986a7291042422e2c4c08f345ce Irccd: load/open plugins using abstract loaders diff -r e0f58cfbd45a -r 11045c180db9 cmake/IrccdOptions.cmake --- a/cmake/IrccdOptions.cmake Wed Aug 24 22:04:11 2016 +0200 +++ b/cmake/IrccdOptions.cmake Sun Aug 28 14:12:32 2016 +0200 @@ -39,6 +39,7 @@ # # WITH_BINDIR Binary directory for irccd, irccdctl # WITH_PLUGINDIR Path where plugins must be installed +# WITH_NPLUGINDIR Path where native plugins must be installed # WITH_DOCDIR Path where to install documentation # WITH_MANDIR Path where to install manuals # WITH_CONFDIR Path where to search configuration files @@ -119,11 +120,13 @@ set(WITH_DATADIR "share" CACHE STRING "Data directory") set(WITH_CACHEDIR "var" CACHE STRING "Temporary files directory") set(WITH_PLUGINDIR "share/plugins" CACHE STRING "Module prefix where to install") + set(WITH_NPLUGINDIR "lib/irccd/plugins" CACHE STRING "Directory for native plugins") set(WITH_DOCDIR "share/doc" CACHE STRING "Documentation directory") else () set(WITH_DATADIR "share/irccd" CACHE STRING "Data directory") set(WITH_CACHEDIR "var/irccd" CACHE STRING "Temporary files directory") set(WITH_PLUGINDIR "share/irccd/plugins" CACHE STRING "Module prefix where to install") + set(WITH_NPLUGINDIR "lib/irccd/plugins" CACHE STRING "Directory for native plugins") set(WITH_DOCDIR "share/doc/irccd" CACHE STRING "Documentation directory") endif () diff -r e0f58cfbd45a -r 11045c180db9 cmake/internal/sysconfig.hpp.in --- a/cmake/internal/sysconfig.hpp.in Wed Aug 24 22:04:11 2016 +0200 +++ b/cmake/internal/sysconfig.hpp.in Sun Aug 28 14:12:32 2016 +0200 @@ -61,6 +61,7 @@ #define WITH_DATADIR "@WITH_DATADIR@" #define WITH_CONFDIR "@WITH_CONFDIR@" #define WITH_PLUGINDIR "@WITH_PLUGINDIR@" +#define WITH_NPLUGINDIR "@WITH_NPLUGINDIR@" #define WITH_CACHEDIR "@WITH_CACHEDIR@" #cmakedefine WITH_JS diff -r e0f58cfbd45a -r 11045c180db9 lib/irccd/path.cpp --- a/lib/irccd/path.cpp Wed Aug 24 22:04:11 2016 +0200 +++ b/lib/irccd/path.cpp Sun Aug 28 14:12:32 2016 +0200 @@ -77,7 +77,7 @@ * supported). */ -std::string base; +std::string base{"."}; #if defined(IRCCD_SYSTEM_WINDOWS) @@ -176,30 +176,27 @@ std::string systemConfig() { - assert(!base.empty()); - return base + WITH_CONFDIR; } std::string systemData() { - assert(!base.empty()); - return base + WITH_DATADIR; } std::string systemCache() { - assert(!base.empty()); - return base + WITH_CACHEDIR; } std::string systemPlugins() { - assert(!base.empty()); + return base + WITH_PLUGINDIR; +} - return base + WITH_PLUGINDIR; +std::string systemNativePlugins() +{ + return base + WITH_NPLUGINDIR; } /* @@ -372,7 +369,7 @@ base = executablePath(); } catch (const std::exception &) { /* - * If an exception is thrown, that means the operatin system supports a + * If an exception is thrown, that means the operating system supports a * function to get the executable path but it failed. * * TODO: show a waning @@ -447,7 +444,7 @@ std::string get(Path path, Owner owner) { - assert(path >= PathConfig && path <= PathPlugins); + assert(path >= PathConfig && path <= PathNativePlugins); assert(owner >= OwnerSystem && owner <= OwnerUser); std::string result; @@ -467,6 +464,9 @@ case PathPlugins: result = clean(systemPlugins()); break; + case PathNativePlugins: + result = clean(systemNativePlugins()); + break; default: break; } @@ -481,6 +481,7 @@ case PathData: result = clean(userData()); break; + case PathNativePlugins: case PathPlugins: result = clean(userPlugins()); break; @@ -496,7 +497,7 @@ std::vector list(Path path) { - assert(path >= PathConfig && path <= PathPlugins); + assert(path >= PathConfig && path <= PathNativePlugins); std::vector list; @@ -518,6 +519,9 @@ list.push_back(clean(userPlugins())); list.push_back(clean(systemPlugins())); break; + case PathNativePlugins: + list.push_back(clean(systemPlugins())); + break; default: break; } diff -r e0f58cfbd45a -r 11045c180db9 lib/irccd/path.hpp --- a/lib/irccd/path.hpp Wed Aug 24 22:04:11 2016 +0200 +++ b/lib/irccd/path.hpp Sun Aug 28 14:12:32 2016 +0200 @@ -49,7 +49,8 @@ PathConfig, //!< Configuration files PathData, //!< Data directory PathCache, //!< Cache files - PathPlugins //!< Path to the plugins + PathPlugins, //!< Path to the plugins + PathNativePlugins //!< Path to native plugins }; /** diff -r e0f58cfbd45a -r 11045c180db9 lib/irccd/plugin-dynlib.cpp --- a/lib/irccd/plugin-dynlib.cpp Wed Aug 24 22:04:11 2016 +0200 +++ b/lib/irccd/plugin-dynlib.cpp Sun Aug 28 14:12:32 2016 +0200 @@ -16,6 +16,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "fs.hpp" +#include "logger.hpp" +#include "path.hpp" #include "plugin-dynlib.hpp" namespace irccd { @@ -173,4 +176,35 @@ call(m_onWhois, irccd, ev); } +std::shared_ptr DynlibPluginLoader::open(const std::string &id, + const std::string &path) noexcept +{ + if (path.rfind(DYNLIB_SUFFIX) == std::string::npos) + return nullptr; + + try { + return std::make_shared(id, path); + } catch (const std::exception &ex) { + log::warning() << "plugin " << id << ": " << ex.what() << std::endl; + } + + return nullptr; +} + +std::shared_ptr DynlibPluginLoader::find(const std::string &id) noexcept +{ + for (const auto &dir : path::list(path::PathNativePlugins)) { + auto path = dir + id + DYNLIB_SUFFIX; + + if (!fs::isReadable(path)) + continue; + + log::info() << "plugin " << id << ": trying " << path << std::endl; + + return open(id, path); + } + + return nullptr; +} + } // !irccd diff -r e0f58cfbd45a -r 11045c180db9 lib/irccd/plugin-dynlib.hpp --- a/lib/irccd/plugin-dynlib.hpp Wed Aug 24 22:04:11 2016 +0200 +++ b/lib/irccd/plugin-dynlib.hpp Sun Aug 28 14:12:32 2016 +0200 @@ -200,6 +200,23 @@ IRCCD_EXPORT void onWhois(Irccd &irccd, const WhoisEvent &event) override; }; +/** + * \brief Implementation for searching native plugins. + */ +class DynlibPluginLoader : public PluginLoader { +public: + /** + * \copydoc PluginLoader::find + */ + std::shared_ptr open(const std::string &id, + const std::string &path) noexcept override; + + /** + * \copydoc PluginLoader::find + */ + std::shared_ptr find(const std::string &id) noexcept override; +}; + } // !irccd #endif // !IRCCD_PLUGIN_DYNLIB_HPP diff -r e0f58cfbd45a -r 11045c180db9 lib/irccd/plugin-js.cpp --- a/lib/irccd/plugin-js.cpp Wed Aug 24 22:04:11 2016 +0200 +++ b/lib/irccd/plugin-js.cpp Sun Aug 28 14:12:32 2016 +0200 @@ -433,4 +433,35 @@ call("onWhois", 2); } +std::shared_ptr JsPluginLoader::open(const std::string &id, + const std::string &path) noexcept +{ + if (path.rfind(".js") == std::string::npos) + return nullptr; + + try { + return std::make_shared(id, path); + } catch (const std::exception &ex) { + log::warning() << "plugin " << id << ": " << ex.what() << std::endl; + } + + return nullptr; +} + +std::shared_ptr JsPluginLoader::find(const std::string &id) noexcept +{ + for (const auto &dir : path::list(path::PathPlugins)) { + auto path = dir + id + ".js"; + + if (!fs::isReadable(path)) + continue; + + log::info() << "plugin " << id << ": trying " << path << std::endl; + + return open(id, path); + } + + return nullptr; +} + } // !irccd diff -r e0f58cfbd45a -r 11045c180db9 lib/irccd/plugin-js.hpp --- a/lib/irccd/plugin-js.hpp Wed Aug 24 22:04:11 2016 +0200 +++ b/lib/irccd/plugin-js.hpp Sun Aug 28 14:12:32 2016 +0200 @@ -27,7 +27,6 @@ #include "duktape.hpp" #include "path.hpp" #include "plugin.hpp" -#include "signals.hpp" namespace irccd { @@ -221,6 +220,23 @@ IRCCD_EXPORT void onWhois(Irccd &irccd, const WhoisEvent &event) override; }; +/** + * \brief Implementation for searching Javascript plugins. + */ +class JsPluginLoader : public PluginLoader { +public: + /** + * \copydoc PluginLoader::find + */ + std::shared_ptr open(const std::string &id, + const std::string &path) noexcept override; + + /** + * \copydoc PluginLoader::find + */ + std::shared_ptr find(const std::string &id) noexcept override; +}; + } // !irccd #endif // !IRCCD_PLUGIN_JS_HPP diff -r e0f58cfbd45a -r 11045c180db9 lib/irccd/plugin.hpp --- a/lib/irccd/plugin.hpp Wed Aug 24 22:04:11 2016 +0200 +++ b/lib/irccd/plugin.hpp Sun Aug 28 14:12:32 2016 +0200 @@ -461,6 +461,40 @@ } }; +/** + * \brief Abstract interface for searching plugins. + * + * This class is used to make loading of plugins extensible, the PluginService + * knows some predefined plugins loaders and use them to search for available + * plugins. + * + * This makes easier to implement new plugins or new ways of loading them. + * + * \see DynlibPluginLoader + * \see JsPluginLoader + */ +class PluginLoader { +public: + /** + * Try to open the plugin specified by path. + * + * The implementation must test if the plugin is suitable for opening, by + * testing extension for example. + * + * \param file the file + */ + virtual std::shared_ptr open(const std::string &id, + const std::string &file) noexcept = 0; + + /** + * Search for a plugin named by this id. + * + * \param id the plugin id + * \return the plugin + */ + virtual std::shared_ptr find(const std::string &id) noexcept = 0; +}; + } // !irccd #endif // !IRCCD_PLUGIN_HPP diff -r e0f58cfbd45a -r 11045c180db9 lib/irccd/service-plugin.cpp --- a/lib/irccd/service-plugin.cpp Wed Aug 24 22:04:11 2016 +0200 +++ b/lib/irccd/service-plugin.cpp Sun Aug 28 14:12:32 2016 +0200 @@ -34,45 +34,11 @@ namespace irccd { -namespace { - -std::shared_ptr find(std::string name) -{ - for (const auto &path : path::list(path::PathPlugins)) { - std::string jspath = path + name + ".js"; - std::string dynlibpath = path + name + DYNLIB_SUFFIX; - - if (fs::isReadable(jspath)) - return std::make_shared(std::move(name), std::move(jspath)); - if (fs::isReadable(dynlibpath)) - return std::make_shared(std::move(name), std::move(dynlibpath)); - } - - throw std::runtime_error("no suitable plugin found"); -} - -std::shared_ptr open(std::string name, std::string path) -{ - std::regex regex(".*(\\..*)$"); - std::smatch match; - std::shared_ptr plugin; - - if (std::regex_match(path, match, regex)) { - if (match[1] == DYNLIB_SUFFIX) - plugin = std::make_shared(name, path); - else - plugin = std::make_shared(name, path); - } else - throw std::runtime_error("could not deduce plugin type from {}"_format(path)); - - return plugin; -} - -} // !namespace - PluginService::PluginService(Irccd &irccd) noexcept : m_irccd(irccd) { + m_loaders.push_back(std::make_unique()); + m_loaders.push_back(std::make_unique()); } PluginService::~PluginService() @@ -145,13 +111,34 @@ return PluginFormats(); } +std::shared_ptr PluginService::open(const std::string &id, + const std::string &path) +{ + for (const auto &loader : m_loaders) { + auto plugin = loader->open(id, path); + + if (plugin) + return plugin; + } + + return nullptr; +} + +std::shared_ptr PluginService::find(const std::string &id) +{ + for (const auto &loader : m_loaders) { + auto plugin = loader->find(id); + + if (plugin) + return plugin; + } + + return nullptr; +} + void PluginService::load(std::string name, std::string path) { - auto it = std::find_if(m_plugins.begin(), m_plugins.end(), [&] (const auto &plugin) { - return plugin->name() == name; - }); - - if (it != m_plugins.end()) + if (has(name)) return; try { @@ -162,10 +149,13 @@ else plugin = open(name, std::move(path)); - plugin->setConfig(m_config[name]); - plugin->setFormats(m_formats[name]); - plugin->onLoad(m_irccd); - add(std::move(plugin)); + if (plugin) { + plugin->setConfig(m_config[name]); + plugin->setFormats(m_formats[name]); + plugin->onLoad(m_irccd); + + add(std::move(plugin)); + } } catch (const std::exception &ex) { log::warning("plugin {}: {}"_format(name, ex.what())); } diff -r e0f58cfbd45a -r 11045c180db9 lib/irccd/service-plugin.hpp --- a/lib/irccd/service-plugin.hpp Wed Aug 24 22:04:11 2016 +0200 +++ b/lib/irccd/service-plugin.hpp Sun Aug 28 14:12:32 2016 +0200 @@ -42,6 +42,7 @@ private: Irccd &m_irccd; std::vector> m_plugins; + std::vector> m_loaders; std::unordered_map m_config; std::unordered_map m_formats; @@ -137,6 +138,27 @@ IRCCD_EXPORT PluginFormats formats(const std::string &name) const; /** + * Generic function for opening the plugin at the given path. + * + * This function will search for every PluginLoader and call open() on it, + * the first one that success will be returned. + * + * \param id the plugin id + * \param path the path to the file + * \return the plugin or nullptr on failures + */ + IRCCD_EXPORT std::shared_ptr open(const std::string &id, + const std::string &path); + + /** + * Generic function for finding a plugin. + * + * \param id the plugin id + * \return the plugin or nullptr on failures + */ + IRCCD_EXPORT std::shared_ptr find(const std::string &id); + + /** * Convenient wrapper that loads a plugin, call onLoad and add it to the * registry. *