diff --git a/doc/tutorials/plugins.md b/doc/tutorials/plugins.md index e2ccdac0abc..c8344eb4fc5 100644 --- a/doc/tutorials/plugins.md +++ b/doc/tutorials/plugins.md @@ -23,6 +23,7 @@ Additionally, there is one more function called [ELEKTRA_PLUGIN_EXPORT](http://doc.libelektra.org/api/current/html/group__plugin.html#gabe78724d2d477eef39997fd9b85bff16), where once again `Plugin` should be replaced with the name of the plug-in, this time in lower-case. So for my line plugin this function would be `ELEKTRA_PLUGIN_EXPORT(line)`. +The developer may define elektraPluginCheckConf() if configuration validation at mount time is desired. The KDB relies on the first five functions for interacting with configuration files stored in the key database. Calls for kdbGet() and kdbClose() will call the functions elektraPluginGet() and elektraPluginClose() respectively for the @@ -322,6 +323,38 @@ The `elektraPluginOpen` and `elektraPluginClose` functions are not commonly used reviewing. `elektraPluginOpen` function runs before `elektraPluginGet` and is useful to do initialization if necessary for the plug-in. On the other hand `elektraPluginClose` is run after other functions of the plug-in and can be useful for freeing up resources. +### elektraPluginCheckConf ### + +The `elektraPluginCheckConf` function may be used for validation of the plugin configuration during mount time. The signature of the function is: + + int elektraLineCheckConfig (Key * errorKey, KeySet * conf) + +The configuration of the plugin is provided as `conf`. The function may report an error or warnings using the `errorKey` and the return value. + +The following convention was established for the return value of `elektraPluginCheckConf`: + +- 0: The configuration was OK and has not been changed +- 1: The configuration has been changed and now it is OK +- -1: The configuration was not OK and could not be fixed. An error has to be set to errorKey. + +The following example demonstrates how to limit the length of the values within the plugin configuration to 3 charaters. + + int elektraLineCheckConfig (Key * errorKey, KeySet * conf) + { + Key * cur; + ksRewind (conf); + while ((cur = ksNext (conf)) != 0) + { + const char * value = keyString (cur); + if (strlen (value) > 3) + { + ELEKTRA_SET_ERRORF (ELEKTRA_ERROR_VALUE_LENGTH, errorKey, "value %s is more than 3 characters long", value); + return -1; // The configuration was not OK and could not be fixed + } + } + return 0; // The configuration was OK and has not been changed + } + ### ELEKTRA_PLUGIN_EXPORT ### The last function, one that is always needed in a plug-in, is `ELEKTRA_PLUGIN_EXPORT`. This functions is responsible for letting Elektra know that diff --git a/src/libs/tools/include/backendbuilder.hpp b/src/libs/tools/include/backendbuilder.hpp index f71ed4f0c1d..c461eaf12c2 100644 --- a/src/libs/tools/include/backendbuilder.hpp +++ b/src/libs/tools/include/backendbuilder.hpp @@ -94,6 +94,9 @@ class BackendBuilder : public BackendInterface void removeProvided (std::vector & needs) const; void removeMetadata (std::set & needsMetadata) const; +protected: + KeySet backendConf; + public: explicit BackendBuilder (BackendBuilderInit const & bbi = BackendBuilderInit ()); @@ -146,6 +149,9 @@ class BackendBuilder : public BackendInterface void recommendPlugin (std::string provider); void fillPlugins (BackendInterface & b) const; + + void setBackendConfig (KeySet const & ks); + KeySet getBackendConfig (); }; /** @@ -167,8 +173,13 @@ class GlobalPluginsBuilder : public BackendBuilder class MountBackendBuilder : public MountBackendInterface, public BackendBuilder { Key mountpoint; - KeySet backendConf; + + /** + * Contains the keys of system/elektra/mountpoints. + * It is needed to detect if a mountpoint already exists. + */ KeySet mountConf; + std::string configfile; public: @@ -183,10 +194,6 @@ class MountBackendBuilder : public MountBackendInterface, public BackendBuilder std::string getMountpoint () const; void setBackendConfig (KeySet const & ks); - KeySet getBackendConfig () - { - return backendConf; - } void useConfigFile (std::string file); std::string getConfigFile () const; diff --git a/src/libs/tools/include/plugindatabase.hpp b/src/libs/tools/include/plugindatabase.hpp index f80657a10a6..b6bc9a57122 100644 --- a/src/libs/tools/include/plugindatabase.hpp +++ b/src/libs/tools/include/plugindatabase.hpp @@ -30,6 +30,9 @@ namespace tools */ class PluginDatabase { +protected: + typedef void (*func_t) (); + public: /** * @brief list all plugins @@ -68,6 +71,16 @@ class PluginDatabase */ virtual std::string lookupInfo (PluginSpec const & whichplugin, std::string const & which) const = 0; + /** + * @brief get exported plugin symbol + * + * @param whichplugin from which plugin? + * @param which which symbol would you like to look up? + * + * @return the function pointer to the exported symbol or NULL if the symbol was not found + */ + virtual func_t getSymbol (PluginSpec const & whichplugin, std::string const & which) const = 0; + /** * @brief lookup which plugin handles meta data * @@ -118,6 +131,7 @@ class ModulesPluginDatabase : public PluginDatabase std::vector listAllPlugins () const; PluginDatabase::Status status (PluginSpec const & whichplugin) const; std::string lookupInfo (PluginSpec const & spec, std::string const & which) const; + func_t getSymbol (PluginSpec const & whichplugin, std::string const & which) const; PluginSpec lookupMetadata (std::string const & which) const; PluginSpec lookupProvides (std::string const & provides) const; }; @@ -128,6 +142,8 @@ class ModulesPluginDatabase : public PluginDatabase class MockPluginDatabase : public ModulesPluginDatabase { public: + typedef int (*checkConfPtr) (ckdb::Key *, ckdb::KeySet *); + /// only data from here will be returned /// @note that it is ordered by name, i.e., different ref-names cannot be distinguished mutable std::unordered_map, PluginSpecHash, PluginSpecName> data; @@ -135,6 +151,11 @@ class MockPluginDatabase : public ModulesPluginDatabase std::vector listAllPlugins () const; PluginDatabase::Status status (PluginSpec const & whichplugin) const; std::string lookupInfo (PluginSpec const & spec, std::string const & which) const; + func_t getSymbol (PluginSpec const & whichplugin, std::string const & which) const; + void setCheckconfFunction (checkConfPtr const newCheckconf); + +private: + checkConfPtr checkconf = NULL; }; } } diff --git a/src/libs/tools/include/pluginspec.hpp b/src/libs/tools/include/pluginspec.hpp index 18c07d5743c..23569de28a8 100644 --- a/src/libs/tools/include/pluginspec.hpp +++ b/src/libs/tools/include/pluginspec.hpp @@ -51,6 +51,7 @@ class PluginSpec void setName (std::string const & name); void appendConfig (KeySet config); + void setConfig (KeySet config); void validate (std::string const & str) const; diff --git a/src/libs/tools/include/toolexcept.hpp b/src/libs/tools/include/toolexcept.hpp index 8c167883eed..b69bf6f6ae0 100644 --- a/src/libs/tools/include/toolexcept.hpp +++ b/src/libs/tools/include/toolexcept.hpp @@ -157,6 +157,35 @@ struct PluginAlreadyInserted : public PluginCheckException std::string m_str; }; +struct PluginConfigInvalid : public PluginCheckException +{ + explicit PluginConfigInvalid (Key key) : m_key (key), m_str () + { + } + + explicit PluginConfigInvalid (std::string const & message) : m_str (message) + { + } + + virtual const char * what () const throw () override + { + if (m_str.empty ()) + { + std::stringstream ss; + ss << "The provided plugin configuration is not valid!\n"; + ss << "Errors/Warnings during the check were:\n"; + printError (ss, m_key); + printWarnings (ss, m_key); + m_str = ss.str (); + } + return m_str.c_str (); + } + +private: + Key m_key; + mutable std::string m_str; +}; + struct BadPluginName : public PluginCheckException { explicit BadPluginName (std::string name) diff --git a/src/libs/tools/src/backendbuilder.cpp b/src/libs/tools/src/backendbuilder.cpp index 2837f762830..5c974172e4c 100644 --- a/src/libs/tools/src/backendbuilder.cpp +++ b/src/libs/tools/src/backendbuilder.cpp @@ -384,12 +384,18 @@ void BackendBuilder::recommendPlugin (std::string name) * @pre Needs to be a unique new name (use refname if you want to add the same module multiple times) * * Will automatically resolve virtual plugins to actual plugins. + * + * Also calls the checkconf function if provided by the plugin. The checkconf function has the + * following signature: int checkconf (Key * errorKey, KeySet * config) and allows a plugin to + * verify its configuration at mount time. * * @see resolveNeeds() * @param plugin */ void BackendBuilder::addPlugin (PluginSpec const & plugin) { + typedef int (*checkConfPtr) (ckdb::Key *, ckdb::KeySet *); + for (auto & p : toAdd) { if (p.getFullName () == plugin.getFullName ()) @@ -409,6 +415,46 @@ void BackendBuilder::addPlugin (PluginSpec const & plugin) newPlugin.appendConfig (provides.getConfig ()); } + // call plugin's checkconf function (if provided) + // this enables a plugin to verify its configuration at mount time + checkConfPtr checkConfFunction = reinterpret_cast (pluginDatabase->getSymbol (newPlugin, "checkconf")); + if (checkConfFunction) + { + ckdb::Key * errorKey = ckdb::keyNew (0); + + // merge plugin config and backend config together + ckdb::KeySet * pluginConfig = newPlugin.getConfig ().dup (); + ckdb::ksAppend (pluginConfig, backendConf.getKeySet ()); + + // call the plugin's checkconf function + int checkResult = checkConfFunction (errorKey, pluginConfig); + if (checkResult == -1) + { + ckdb::ksDel (pluginConfig); + throw PluginConfigInvalid (errorKey); + } + else if (checkResult == 1) + { + // separate plugin config from the backend config + ckdb::Key * backendParent = ckdb::keyNew ("system/", KEY_END); + ckdb::KeySet * newBackendConfig = ckdb::ksCut (pluginConfig, backendParent); + + // take over the new configuration + KeySet modifiedPluginConfig = KeySet (pluginConfig); + KeySet modifiedBackendConfig = KeySet (newBackendConfig); + + newPlugin.setConfig (modifiedPluginConfig); + setBackendConfig (modifiedBackendConfig); + + ckdb::keyDel (backendParent); + } + else + { + ckdb::ksDel (pluginConfig); + } + ckdb::keyDel (errorKey); + } + toAdd.push_back (newPlugin); sort (); } @@ -428,6 +474,16 @@ void BackendBuilder::fillPlugins (BackendInterface & b) const } } +void BackendBuilder::setBackendConfig (KeySet const & ks) +{ + backendConf = ks; +} + +KeySet BackendBuilder::getBackendConfig () +{ + return backendConf; +} + GlobalPluginsBuilder::GlobalPluginsBuilder (BackendBuilderInit const & bbi) : BackendBuilder (bbi) { } @@ -489,7 +545,7 @@ std::string MountBackendBuilder::getMountpoint () const void MountBackendBuilder::setBackendConfig (KeySet const & ks) { - backendConf = ks; + BackendBuilder::setBackendConfig (ks); } void MountBackendBuilder::useConfigFile (std::string file) diff --git a/src/libs/tools/src/plugindatabase.cpp b/src/libs/tools/src/plugindatabase.cpp index f17b5fb1d2f..1d205dac048 100644 --- a/src/libs/tools/src/plugindatabase.cpp +++ b/src/libs/tools/src/plugindatabase.cpp @@ -210,17 +210,21 @@ PluginDatabase::Status ModulesPluginDatabase::status (PluginSpec const & spec) c std::string ModulesPluginDatabase::lookupInfo (PluginSpec const & spec, std::string const & which) const { - PluginPtr plugin; + PluginPtr plugin = impl->modules.load (spec.getName (), spec.getConfig ()); + return plugin->lookupInfo (which); +} + +PluginDatabase::func_t ModulesPluginDatabase::getSymbol (PluginSpec const & spec, std::string const & which) const +{ try { - plugin = impl->modules.load (spec.getName (), spec.getConfig ()); + PluginPtr plugin = impl->modules.load (spec.getName (), spec.getConfig ()); + return plugin->getSymbol (which); } catch (...) { - throw; + return NULL; } - - return plugin->lookupInfo (which); } PluginSpec ModulesPluginDatabase::lookupMetadata (std::string const & which) const @@ -367,5 +371,19 @@ std::string MockPluginDatabase::lookupInfo (PluginSpec const & spec, std::string return ""; } + +PluginDatabase::func_t MockPluginDatabase::getSymbol (PluginSpec const & spec ELEKTRA_UNUSED, std::string const & which) const +{ + if (which == "checkconf") + { + return reinterpret_cast (checkconf); + } + return NULL; +} + +void MockPluginDatabase::setCheckconfFunction (const MockPluginDatabase::checkConfPtr newCheckconf) +{ + checkconf = newCheckconf; +} } } diff --git a/src/libs/tools/src/pluginspec.cpp b/src/libs/tools/src/pluginspec.cpp index 138f138533f..ccb17e169d3 100644 --- a/src/libs/tools/src/pluginspec.cpp +++ b/src/libs/tools/src/pluginspec.cpp @@ -195,6 +195,17 @@ void PluginSpec::appendConfig (KeySet c) config.append (c); } +/** + * @brief Set plugin config + * + * @param c new config to be used as plugin config + */ +void PluginSpec::setConfig (KeySet c) +{ + config.clear (); + config.append (c); +} + /** * @brief Check if str starts with a-z and then only has chars a-z, 0-9 or underscore (_) * diff --git a/src/libs/tools/tests/testtool_backendbuilder.cpp b/src/libs/tools/tests/testtool_backendbuilder.cpp index 6f49195ecc9..67f76cdaf28 100644 --- a/src/libs/tools/tests/testtool_backendbuilder.cpp +++ b/src/libs/tools/tests/testtool_backendbuilder.cpp @@ -546,3 +546,144 @@ TEST (BackendBuilder, resolveDoubleRecommends) EXPECT_EQ (bb.cbegin ()[1], PluginSpec ("a")); EXPECT_EQ (bb.cbegin ()[2], PluginSpec ("c")); } + +static int checkconfLookup (ckdb::Key * errorKey, ckdb::KeySet * config) +{ + ckdb::Key * k = ckdb::ksLookupByName (config, "/a", 0); + if (k) + { + return 0; + } + return -1; +} + +TEST (BackendBuilder, checkconfOkNoChange) +{ + using namespace kdb; + using namespace kdb::tools; + std::shared_ptr mpd = std::make_shared (); + mpd->data[PluginSpec ("checkconf1")]["provides"] = "test123"; + mpd->setCheckconfFunction (checkconfLookup); + BackendBuilderInit bbi (mpd); + BackendBuilder bb (bbi); + PluginSpec spec ("checkconf1"); + KeySet pluginConfig; + Key a; + a.setName ("user/a"); + a.setString ("abc"); + pluginConfig.append (a); + spec.appendConfig (pluginConfig); + bb.addPlugin (spec); +} + +TEST (BackendBuilder, checkconfNotOKmissing) +{ + using namespace kdb; + using namespace kdb::tools; + std::shared_ptr mpd = std::make_shared (); + mpd->data[PluginSpec ("checkconf3")]["c"] = "something"; + mpd->setCheckconfFunction (checkconfLookup); + BackendBuilderInit bbi (mpd); + BackendBuilder bb (bbi); + EXPECT_THROW (bb.addPlugin (PluginSpec ("checkconf3")), PluginConfigInvalid); +} + +static int checkconfAppend (ckdb::Key * errorKey, ckdb::KeySet * config) +{ + ckdb::ksAppendKey (config, ckdb::keyNew ("user/b", KEY_VALUE, "test", KEY_END)); + return 1; +} + +TEST (BackendBuilder, checkconfOkChanged) +{ + using namespace kdb; + using namespace kdb::tools; + std::shared_ptr mpd = std::make_shared (); + mpd->data[PluginSpec ("checkconf1")]["provides"] = "test123"; + mpd->setCheckconfFunction (checkconfAppend); + BackendBuilderInit bbi (mpd); + BackendBuilder bb (bbi); + PluginSpec spec ("checkconf1"); + KeySet pluginConfig; + spec.appendConfig (pluginConfig); + bb.addPlugin (spec); + // we expect b to be added now + spec = *bb.begin (); + EXPECT_EQ (spec.getConfig ().get ("user/b"), "test"); +} + +static int checkconfDelete (ckdb::Key * errorKey, ckdb::KeySet * config) +{ + ckdb::ksCopy (config, NULL); + return 1; +} + +TEST (BackendBuilder, checkconfOkRemovedPluginConfig) +{ + using namespace kdb; + using namespace kdb::tools; + std::shared_ptr mpd = std::make_shared (); + mpd->data[PluginSpec ("checkconf1")]["provides"] = "test123"; + mpd->setCheckconfFunction (checkconfDelete); + BackendBuilderInit bbi (mpd); + BackendBuilder bb (bbi); + PluginSpec spec ("checkconf1"); + KeySet pluginConfig; + Key a; + a.setName ("user/a"); + a.setString ("abc"); + pluginConfig.append (a); + spec.appendConfig (pluginConfig); + bb.addPlugin (spec); + // we expect a to be removed now + spec = *bb.begin (); + EXPECT_THROW (spec.getConfig ().get ("user/a"), KeyNotFoundException); +} + +TEST (BackendBuilder, checkconfOkRemovedBackendConfig) +{ + using namespace kdb; + using namespace kdb::tools; + std::shared_ptr mpd = std::make_shared (); + mpd->data[PluginSpec ("checkconf1")]["provides"] = "test123"; + mpd->setCheckconfFunction (checkconfDelete); + BackendBuilderInit bbi (mpd); + BackendBuilder bb (bbi); + PluginSpec spec ("checkconf1"); + KeySet pluginConfig; + spec.appendConfig (pluginConfig); + KeySet backendConfig; + Key b; + b.setName ("system/b"); + b.setString ("xyz"); + backendConfig.append (b); + bb.setBackendConfig (backendConfig); + bb.addPlugin (spec); + // we expect b to be removed now + spec = *bb.begin (); + EXPECT_THROW (bb.getBackendConfig ().get ("system/b"), KeyNotFoundException); +} + +static int checkconfAppendBackendConf (ckdb::Key * errorKey, ckdb::KeySet * config) +{ + ckdb::ksAppendKey (config, ckdb::keyNew ("system/a", KEY_VALUE, "abc", KEY_END)); + return 1; +} + +TEST (BackendBuilder, checkconfOkAppendBackendConfig) +{ + using namespace kdb; + using namespace kdb::tools; + std::shared_ptr mpd = std::make_shared (); + mpd->data[PluginSpec ("checkconf1")]["provides"] = "test123"; + mpd->setCheckconfFunction (checkconfAppendBackendConf); + BackendBuilderInit bbi (mpd); + BackendBuilder bb (bbi); + PluginSpec spec ("checkconf1"); + KeySet pluginConfig; + spec.appendConfig (pluginConfig); + bb.addPlugin (spec); + // we expect b to be added now + spec = *bb.begin (); + EXPECT_EQ (bb.getBackendConfig ().get ("system/a"), "abc"); +} diff --git a/src/plugins/doc/doc.c b/src/plugins/doc/doc.c index 7402086f585..48a3e954753 100644 --- a/src/plugins/doc/doc.c +++ b/src/plugins/doc/doc.c @@ -107,6 +107,7 @@ int elektraDocGet (Plugin * plugin ELEKTRA_UNUSED, KeySet * returned, Key * pare keyNew ("system/elektra/modules/doc/exports/get", KEY_FUNC, elektraDocGet, KEY_END), keyNew ("system/elektra/modules/doc/exports/set", KEY_FUNC, elektraDocSet, KEY_END), keyNew ("system/elektra/modules/doc/exports/error", KEY_FUNC, elektraDocError, KEY_END), + keyNew ("system/elektra/modules/doc/exports/checkconf", KEY_FUNC, elektraDocCheckConf, KEY_END), #include ELEKTRA_README (doc) keyNew ("system/elektra/modules/doc/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END); ksAppend (returned, contract); @@ -183,6 +184,19 @@ static void saveToDisc (Key * k ELEKTRA_UNUSED) { } +//![validate configuration] +int elektraDocCheckConf (Key * errorKey ELEKTRA_UNUSED, KeySet * conf ELEKTRA_UNUSED) +{ + /* validate plugin configuration */ + + // the return codes have the following meaning: + // 0: The configuration was OK and has not been changed + // 1: The configuration has been changed and now it is OK + // -1: The configuration was not OK and could not be fixed. An error has to be set to errorKey. + return 0; +} +//![validate configuration] + //![set full] static void usercode (KDB * handle, KeySet * keyset, Key * key) { diff --git a/src/plugins/doc/doc.h b/src/plugins/doc/doc.h index 34911c99d5d..b2f3441104b 100644 --- a/src/plugins/doc/doc.h +++ b/src/plugins/doc/doc.h @@ -494,6 +494,28 @@ int elektraDocSet (Plugin * handle, KeySet * returned, Key * parentKey); */ int elektraDocError (Plugin * handle, KeySet * returned, Key * parentKey); +/** + * @brief Validate plugin configuration at mount time. + * + * During the mount phase the BackendBuilder calls this method, + * if it is provided by the plugin. + * + * In this method the plugin configuration can be checked for validity or + * integrity. Missing items can be added to complete the configuration. + * + * @param errorKey is used to propagate error messages to the caller + * @param conf contains the plugin configuration to be validated + * + * @retval 1 on success: the configuration was OK and has not been changed. + * @retval 0 on success: the configuration has been changed and now it is OK. + * @retval -1 on failure: the configuration was not OK and could not be fixed. + * Set an error using #ELEKTRA_SET_ERROR to inform the user what went wrong. + * Additionally you can add any number of warnings with + * #ELEKTRA_ADD_WARNING. + * + * @ingroup plugin + */ +int elektraDocCheckConf (Key * errorKey, KeySet * conf); /** * @} diff --git a/src/plugins/template/template.c b/src/plugins/template/template.c index 0d1758673ed..ae7825ec817 100644 --- a/src/plugins/template/template.c +++ b/src/plugins/template/template.c @@ -15,6 +15,7 @@ int elektraTemplateOpen (Plugin * handle ELEKTRA_UNUSED, Key * errorKey ELEKTRA_UNUSED) { // plugin initialization logic + // this function is optional return 1; // success } @@ -22,6 +23,7 @@ int elektraTemplateOpen (Plugin * handle ELEKTRA_UNUSED, Key * errorKey ELEKTRA_ int elektraTemplateClose (Plugin * handle ELEKTRA_UNUSED, Key * errorKey ELEKTRA_UNUSED) { // free all plugin resources and shut it down + // this function is optional return 1; // success } @@ -38,6 +40,7 @@ int elektraTemplateGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTR keyNew ("system/elektra/modules/template/exports/get", KEY_FUNC, elektraTemplateGet, KEY_END), keyNew ("system/elektra/modules/template/exports/set", KEY_FUNC, elektraTemplateSet, KEY_END), keyNew ("system/elektra/modules/template/exports/error", KEY_FUNC, elektraTemplateError, KEY_END), + keyNew ("system/elektra/modules/template/exports/checkconf", KEY_FUNC, elektraTemplateCheckConfig, KEY_END), #include ELEKTRA_README (template) keyNew ("system/elektra/modules/template/infos/version", KEY_VALUE, PLUGINVERSION, KEY_END), KS_END); ksAppend (returned, contract); @@ -53,6 +56,7 @@ int elektraTemplateGet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTR int elektraTemplateSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED) { // get all keys + // this function is optional return 1; // success } @@ -60,10 +64,23 @@ int elektraTemplateSet (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTR int elektraTemplateError (Plugin * handle ELEKTRA_UNUSED, KeySet * returned ELEKTRA_UNUSED, Key * parentKey ELEKTRA_UNUSED) { // set all keys + // this function is optional return 1; // success } +int elektraTemplateCheckConfig (Key * errorKey ELEKTRA_UNUSED, KeySet * conf ELEKTRA_UNUSED) +{ + // validate plugin configuration + // this function is optional + + // the return codes have the following meaning: + // 0: The configuration was OK and has not been changed + // 1: The configuration has been changed and now it is OK + // -1: The configuration was not OK and could not be fixed. An error has to be set to errorKey. + return 0; +} + Plugin * ELEKTRA_PLUGIN_EXPORT (template) { // clang-format off diff --git a/src/plugins/template/template.h b/src/plugins/template/template.h index e3c16d5f3ac..82d83fdb8e4 100644 --- a/src/plugins/template/template.h +++ b/src/plugins/template/template.h @@ -18,6 +18,7 @@ int elektraTemplateClose (Plugin * handle, Key * errorKey); int elektraTemplateGet (Plugin * handle, KeySet * ks, Key * parentKey); int elektraTemplateSet (Plugin * handle, KeySet * ks, Key * parentKey); int elektraTemplateError (Plugin * handle, KeySet * ks, Key * parentKey); +int elektraTemplateCheckConfig (Key * errorKey, KeySet * conf); Plugin * ELEKTRA_PLUGIN_EXPORT (template);