From 2d8d501f021fff1657c923a3eae9172cc43a492d Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Fri, 10 Mar 2017 06:38:54 +0300 Subject: [PATCH] integrate plugin engine --- .../include/queued/QueuedAdvancedSettings.h | 4 +- .../include/queued/QueuedConfiguration.h | 6 +- sources/queued/include/queued/QueuedCore.h | 51 ++++++- .../include/queued/QueuedCoreInterface.h | 24 ++++ .../include/queued/QueuedPluginInterface.h | 8 ++ .../include/queued/QueuedPluginManager.h | 18 +++ .../queued/QueuedPluginManagerInterface.h | 6 +- sources/queued/src/QueuedAdvancedSettings.cpp | 2 +- sources/queued/src/QueuedCore.cpp | 127 +++++++++++++++++- sources/queued/src/QueuedCoreInterface.cpp | 25 ++++ sources/queued/src/QueuedPluginManager.cpp | 46 ++++++- 11 files changed, 299 insertions(+), 18 deletions(-) diff --git a/sources/queued/include/queued/QueuedAdvancedSettings.h b/sources/queued/include/queued/QueuedAdvancedSettings.h index 58668d5..668a119 100644 --- a/sources/queued/include/queued/QueuedAdvancedSettings.h +++ b/sources/queued/include/queued/QueuedAdvancedSettings.h @@ -106,12 +106,14 @@ public: signals: /** * @brief emits on each value update + * @param _id + * internal key id * @param _key * changed key * @param _value * changed value */ - void valueUpdated(const QueuedCfg::QueuedSettings _key, + void valueUpdated(const QueuedCfg::QueuedSettings _id, const QString &_key, const QVariant &_value); private: diff --git a/sources/queued/include/queued/QueuedConfiguration.h b/sources/queued/include/queued/QueuedConfiguration.h index 15cd354..036d83a 100644 --- a/sources/queued/include/queued/QueuedConfiguration.h +++ b/sources/queued/include/queued/QueuedConfiguration.h @@ -97,6 +97,8 @@ typedef struct { * internal field to control current database version * @var QueuedSettings::ProcessCommandLine * control process command line + * @var QueuedSettings::Plugins + * plugin list */ enum class QueuedSettings { Invalid = 1 << 0, @@ -108,6 +110,7 @@ enum class QueuedSettings { TokenExpiration = 1 << 6, DatabaseVersion = 1 << 7, ProcessCommandLine = 1 << 8, + Plugins = 1 << 9, }; /** * @ingroup QueuedCfg @@ -148,7 +151,8 @@ const QueuedSettingsDefaultMap QueuedSettingsDefaults = { {QueuedSettings::ProcessCommandLine, "systemd-run\x01--scope\x01--unit={name}\x01--uid={uid}\x01--gid={gid}" "\x01-p\x01CPUQuota={cpu}%\x01-p\x01MemoryHigh={memory}\x01{" - "application}"}}}; + "application}"}}, + {"Plugins", {QueuedSettings::Plugins, ""}}}; }; #endif /* QUEUEDCONFIGURATION_H */ diff --git a/sources/queued/include/queued/QueuedCore.h b/sources/queued/include/queued/QueuedCore.h index f194eff..ecaa87a 100644 --- a/sources/queued/include/queued/QueuedCore.h +++ b/sources/queued/include/queued/QueuedCore.h @@ -35,6 +35,7 @@ class QueuedAdvancedSettings; class QueuedDatabase; class QueuedDatabaseManager; +class QueuedPluginManager; class QueuedProcess; class QueuedProcessManager; class QueuedReportManager; @@ -58,6 +59,16 @@ public: * @brief QueuedCore class destructor */ virtual ~QueuedCore(); + /** + * @brief add plugin to autoload and load it now + * @param _plugin + * plugin name + * @param _auth + * user auth structure + * @return true on successfully addition + */ + bool addPlugin(const QString &_plugin, + const QueuedUserManager::QueuedUserAuthorization &_auth); /** * @brief add new task * @param _command @@ -175,6 +186,23 @@ public: * @return option value or empty QVariant */ QVariant option(const QString &_key); + /** + * @brief get plugin settings + * @param _plugin + * plugin name + * @return hash of plugin settings + */ + QVariantHash pluginSettings(const QString &_plugin); + /** + * @brief remove plugin from autoload and unload it now + * @param _plugin + * plugin name + * @param _auth + * user auth structure + * @return true on successful plugin removal + */ + bool removePlugin(const QString &_plugin, + const QueuedUserManager::QueuedUserAuthorization &_auth); /** * @brief force start task * @param _id @@ -226,13 +254,15 @@ public: private slots: /** * @brief notify clients about settings update + * @param _id + * updated key id * @param _key * updated key * @param _value * new value */ - void updateSettings(const QueuedCfg::QueuedSettings _key, - const QVariant &_value); + void updateSettings(const QueuedCfg::QueuedSettings _id, + const QString &_key, const QVariant &_value); /** * @brief update process time * @param _id @@ -266,6 +296,10 @@ private: * @brief pointer to database manager object */ QueuedDatabaseManager *m_databaseManager = nullptr; + /** + * @brief pointer to plugin manager + */ + QueuedPluginManager *m_plugins = nullptr; /** * @brief pointer to process manager */ @@ -302,6 +336,10 @@ private: * @throw QueuedDBusException */ void initDBus(); + /** + * @brief init plugins + */ + void initPlugins(); /** * @brief init processes */ @@ -364,6 +402,15 @@ private: * @return true on successful option edition */ bool editOptionPrivate(const QString &_key, const QVariant &_value); + /** + * @brief edit plugin list + * @param _plugin + * plugin name + * @param add + * true if it requires add plugin + * @return true on successful action + */ + bool editPluginPrivate(const QString &_plugin, const bool _add); /** * @brief edit task * @param _id diff --git a/sources/queued/include/queued/QueuedCoreInterface.h b/sources/queued/include/queued/QueuedCoreInterface.h index 74f03c1..a6a3d25 100644 --- a/sources/queued/include/queued/QueuedCoreInterface.h +++ b/sources/queued/include/queued/QueuedCoreInterface.h @@ -76,6 +76,30 @@ public slots: */ bool OptionEdit(const QString &key, const QDBusVariant &value, const QString &whoAmI, const QString &token); + /** + * @brief add plugin + * @param plugin + * plugin name + * @param whoAmI + * auth user name + * @param token + * auth user token + * @return true on successful plugin addition + */ + bool PluginAdd(const QString &plugin, const QString &whoAmI, + const QString &token); + /** + * @brief remove plugin + * @param plugin + * plugin name + * @param whoAmI + * auth user name + * @param token + * auth user token + * @return true on successful plugin removal + */ + bool PluginRemove(const QString &plugin, const QString &whoAmI, + const QString &token); /** * @brief add new task * @param command diff --git a/sources/queued/include/queued/QueuedPluginInterface.h b/sources/queued/include/queued/QueuedPluginInterface.h index 5ecb5b4..ee983a0 100644 --- a/sources/queued/include/queued/QueuedPluginInterface.h +++ b/sources/queued/include/queued/QueuedPluginInterface.h @@ -54,6 +54,14 @@ public: * @remark plugin settings will be stored as "plugin.name.Key" */ virtual void init(const QVariantHash &_settings) = 0; + /** + * @brief method which will be called on option update + * @param _key + * option key + * @param _value + * option value + */ + virtual void updateSettings(const QString &_key, const QVariant &_value); }; Q_DECLARE_INTERFACE(QueuedPluginInterface, PLUGIN_INTERFACE_NAME) diff --git a/sources/queued/include/queued/QueuedPluginManager.h b/sources/queued/include/queued/QueuedPluginManager.h index 7e077aa..a6b41c2 100644 --- a/sources/queued/include/queued/QueuedPluginManager.h +++ b/sources/queued/include/queued/QueuedPluginManager.h @@ -26,6 +26,7 @@ #include #include +#include class QueuedPluginInterface; @@ -55,6 +56,13 @@ public: * @brief QueuedPluginManager class destructor */ virtual ~QueuedPluginManager(); + /** + * @brief get option name from database format + * @param _key + * option key + * @return plugin name and option key in plugin format + */ + static QPair convertOptionName(const QString &_key); /** * @brief plugin manager interface * @return pointer to plugin manager interface @@ -82,6 +90,16 @@ public: */ bool unloadPlugin(const QString &_name); +public slots: + /** + * @brief notifies plugin about option changes + * @param _key + * database option key + * @param _value + * option value + */ + void optionChanged(const QString &_key, const QVariant &_value); + private: /** * @brief loaded plugins diff --git a/sources/queued/include/queued/QueuedPluginManagerInterface.h b/sources/queued/include/queued/QueuedPluginManagerInterface.h index 3bd10c1..0d58cc6 100644 --- a/sources/queued/include/queued/QueuedPluginManagerInterface.h +++ b/sources/queued/include/queued/QueuedPluginManagerInterface.h @@ -37,11 +37,9 @@ class QueuedPluginManagerInterface : public QObject public: /** * @brief QueuedPluginManagerInterface class constructor - * @param parent - * pointer to parent object */ - explicit QueuedPluginManagerInterface(QObject *parent) - : QObject(parent){}; + explicit QueuedPluginManagerInterface() + : QObject(nullptr){}; /** * @brief QueuedPluginManagerInterface class destructor */ diff --git a/sources/queued/src/QueuedAdvancedSettings.cpp b/sources/queued/src/QueuedAdvancedSettings.cpp index 5261072..ed19d74 100644 --- a/sources/queued/src/QueuedAdvancedSettings.cpp +++ b/sources/queued/src/QueuedAdvancedSettings.cpp @@ -148,7 +148,7 @@ void QueuedAdvancedSettings::set(const QString &_key, const QVariant &_value) m_values[_key.toLower()] = _value; auto id = QueuedCfg::QueuedSettingsDefaults[internalId(_key)].id; - emit(valueUpdated(id, _value)); + emit(valueUpdated(id, _key, _value)); } diff --git a/sources/queued/src/QueuedCore.cpp b/sources/queued/src/QueuedCore.cpp index 612dfdb..af8fce7 100644 --- a/sources/queued/src/QueuedCore.cpp +++ b/sources/queued/src/QueuedCore.cpp @@ -53,6 +53,25 @@ QueuedCore::~QueuedCore() } +/** + * @fn addPlugin + */ +bool QueuedCore::addPlugin( + const QString &_plugin, + const QueuedUserManager::QueuedUserAuthorization &_auth) +{ + qCDebug(LOG_LIB) << "Add plugin" << _plugin; + + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user << "not allowed to add plugin"; + return false; + } + + return editPluginPrivate(_plugin, true); +} + + /** * @addTask */ @@ -309,6 +328,46 @@ QVariant QueuedCore::option(const QString &_key) } +/** + * @fn pluginSettings + */ +QVariantHash QueuedCore::pluginSettings(const QString &_plugin) +{ + qCDebug(LOG_LIB) << "Get plugin settings for" << _plugin; + + auto dbSettings + = m_database->get(QueuedDB::SETTINGS_TABLE, + QString("WHERE key LIKE 'Plugin.%1.%'").arg(_plugin)); + QVariantHash settings; + std::for_each(dbSettings.cbegin(), dbSettings.cend(), + [&settings](const QVariantHash &value) { + settings[value["key"].toString()] = value["value"]; + }); + + return settings; +} + + +/** + * @fn removePlugin + */ +bool QueuedCore::removePlugin( + const QString &_plugin, + const QueuedUserManager::QueuedUserAuthorization &_auth) +{ + qCDebug(LOG_LIB) << "Remove plugin" << _plugin; + + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to remove plugin"; + return false; + } + + return editPluginPrivate(_plugin, false); +} + + /** * @fn startTask */ @@ -430,6 +489,8 @@ void QueuedCore::deinit() delete m_databaseManager; if (m_reports) delete m_reports; + if (m_plugins) + delete m_plugins; if (m_processes) delete m_processes; if (m_users) @@ -455,15 +516,17 @@ void QueuedCore::init(const QString &_configuration) // init parts initSettings(_configuration); + initPlugins(); initUsers(); initProcesses(); // settings update notifier m_connections += connect( m_advancedSettings, - SIGNAL(valueUpdated(const QueuedCfg::QueuedSettings, const QVariant &)), + SIGNAL(valueUpdated(const QueuedCfg::QueuedSettings, const QString &, + const QVariant &)), this, SLOT(updateSettings(const QueuedCfg::QueuedSettings, - const QVariant &))); + const QString &, const QVariant &))); // dbus session initDBus(); @@ -473,15 +536,19 @@ void QueuedCore::init(const QString &_configuration) /** * @fn updateSettings */ -void QueuedCore::updateSettings(const QueuedCfg::QueuedSettings _key, - const QVariant &_value) +void QueuedCore::updateSettings(const QueuedCfg::QueuedSettings _id, + const QString &_key, const QVariant &_value) { - qCDebug(LOG_LIB) << "Received update for" << static_cast(_key) + qCDebug(LOG_LIB) << "Received update for" << static_cast(_id) << _key << "with value" << _value; // FIXME propbably there is a better way to change settings - switch (_key) { + switch (_id) { case QueuedCfg::QueuedSettings::Invalid: + // check if it is plugin settings + if (_key.startsWith("Plugin.")) + m_plugins->optionChanged(_key, _value); + // do nothing otherwise break; case QueuedCfg::QueuedSettings::DatabaseInterval: m_databaseManager->setInterval(_value.toLongLong()); @@ -500,6 +567,9 @@ void QueuedCore::updateSettings(const QueuedCfg::QueuedSettings _key, m_processes->setOnExitAction( static_cast(_value.toInt())); break; + case QueuedCfg::QueuedSettings::Plugins: + // do nothing here + break; case QueuedCfg::QueuedSettings::ProcessCommandLine: m_processes->setProcessLine(_value.toString()); break; @@ -601,6 +671,22 @@ void QueuedCore::initDBus() } +/** + * @fn initPlugins + */ +void QueuedCore::initPlugins() +{ + QStringList pluginList + = m_advancedSettings->get(QueuedCfg::QueuedSettings::Plugins) + .toString() + .split('\x01'); + + m_plugins = new QueuedPluginManager(this); + for (auto &plugin : pluginList) + m_plugins->loadPlugin(plugin, pluginSettings(plugin)); +} + + /** * @fn initProcesses */ @@ -796,8 +882,35 @@ bool QueuedCore::editOptionPrivate(const QString &_key, const QVariant &_value) } // add to child object - if (status) + if (status) { m_advancedSettings->set(_key, _value); + // notify plugin if required + } + return status; +} + + +/** + * @fn editPluginPrivate + */ +bool QueuedCore::editPluginPrivate(const QString &_plugin, const bool _add) +{ + qCDebug(LOG_LIB) << "Edit plugin" << _plugin << "add" << _add; + + QStringList pluginList + = m_advancedSettings->get(QueuedCfg::QueuedSettings::Plugins) + .toString() + .split('\x01'); + + bool status = false; + if (_add && !pluginList.contains(_plugin)) + status = m_plugins->loadPlugin(_plugin, pluginSettings(_plugin)); + else if (!_add && pluginList.contains(_plugin)) + status = m_plugins->unloadPlugin(_plugin); + else + qCDebug(LOG_LIB) << "Plugin" << _plugin + << "not loaded or already loaded"; + return status; } diff --git a/sources/queued/src/QueuedCoreInterface.cpp b/sources/queued/src/QueuedCoreInterface.cpp index 1263bcb..1ebc105 100644 --- a/sources/queued/src/QueuedCoreInterface.cpp +++ b/sources/queued/src/QueuedCoreInterface.cpp @@ -74,6 +74,31 @@ bool QueuedCoreInterface::OptionEdit(const QString &key, } +/** + * @fn PluginAdd + */ +bool QueuedCoreInterface::PluginAdd(const QString &plugin, + const QString &whoAmI, const QString &token) +{ + qCDebug(LOG_DBUS) << "Add plugin" << plugin << "auth by" << whoAmI; + + return m_core->addPlugin(plugin, QueuedUserManager::auth(whoAmI, token)); +} + + +/** + * @fn PluginRemove + */ +bool QueuedCoreInterface::PluginRemove(const QString &plugin, + const QString &whoAmI, + const QString &token) +{ + qCDebug(LOG_DBUS) << "Remove plugin" << plugin << "auth by" << whoAmI; + + return m_core->removePlugin(plugin, QueuedUserManager::auth(whoAmI, token)); +} + + /** * @fn TaskAdd */ diff --git a/sources/queued/src/QueuedPluginManager.cpp b/sources/queued/src/QueuedPluginManager.cpp index c0fb48a..ef1646f 100644 --- a/sources/queued/src/QueuedPluginManager.cpp +++ b/sources/queued/src/QueuedPluginManager.cpp @@ -39,7 +39,7 @@ QueuedPluginManager::QueuedPluginManager(QObject *parent) { qCDebug(LOG_PL) << __PRETTY_FUNCTION__; - m_interface = new QueuedPluginManagerInterface(this); + m_interface = new QueuedPluginManagerInterface(); } @@ -58,6 +58,25 @@ QueuedPluginManager::~QueuedPluginManager() } +/** + * @fn convertOptionName + */ +QPair +QueuedPluginManager::convertOptionName(const QString &_key) +{ + qCDebug(LOG_PL) << "Convert option name" << _key; + + QStringList fields = _key.split('.'); + // Plugin. + fields.takeFirst(); + // plugin name + QString plugin = fields.takeFirst(); + QString option = fields.join('.'); + + return {plugin, option}; +} + + /** * @fn interface */ @@ -76,6 +95,10 @@ bool QueuedPluginManager::loadPlugin(const QString &_name, qCDebug(LOG_PL) << "Load plugin" << _name << "with settings" << _settings; QString libraryName = QString("lib%2.so").arg(_name); + // init plugin settings with valid keys + QVariantHash pluginSettings; + for (auto &key : _settings.keys()) + pluginSettings[convertOptionName(key).first] = _settings[key]; for (auto &dir : pluginLocations()) { if (!QDir(dir).entryList(QDir::Files).contains(libraryName)) @@ -93,7 +116,7 @@ bool QueuedPluginManager::loadPlugin(const QString &_name, << "error" << loader.errorString(); if (item) { m_plugins[_name] = item; - item->init(_settings); + item->init(pluginSettings); item->connect(interface()); } else { qCCritical(LOG_PL) << "Could not cast plugin" << _name; @@ -140,3 +163,22 @@ bool QueuedPluginManager::unloadPlugin(const QString &_name) return true; } + + +/** + * @fn optionChanged + */ +void QueuedPluginManager::optionChanged(const QString &_key, + const QVariant &_value) +{ + qCDebug(LOG_PL) << "Option" << _key << "changed to" << _value; + + auto option = convertOptionName(_key); + if (!m_plugins.contains(option.first)) { + qCWarning(LOG_PL) << "Plugin" << option.first << "not found for" + << _key; + return; + } + + return m_plugins[option.first]->updateSettings(option.second, _value); +}