| plugin(3elektra) | Elektra | plugin(3elektra) | 
NAME¶
plugin - Elektra plugin framework.
SYNOPSIS¶
Macros¶
#define ELEKTRA_PLUGIN_FUNCTION(module, function)
    libelektra_##module##_LTX_elektraPlugin##function
  
  Declare a plugin's function name suitable for compilation variants (see
    doc/tutorials). #define ELEKTRA_README(module) ELEKTRA_README2
    (module)
  
  The filename for inclusion of the readme for compilation variants (see
    doc/tutorials). #define ELEKTRA_SET_ERROR(number, key, text)
  
  Sets the error in the keys metadata. #define ELEKTRA_SET_ERRORF(number,
    key, formatstring, ...)
  
  Sets the error in the keys metadata. #define
    ELEKTRA_ADD_WARNINGF(number, key, formatstring, ...)
  
  Adds a warning in the keys metadata. #define
    ELEKTRA_ADD_WARNING(number, key, text)
  
  Adds a warning in the keys metadata. #define
    ELEKTRA_SET_ERROR_GET(parentKey)
  
  Set error in kdbGet() when opening the file failed. #define
    ELEKTRA_SET_ERROR_SET(parentKey)
  
  Set error in kdbSet() when opening the file failed.
  
Functions¶
Plugin * elektraPluginExport (const char *pluginName,...)
  
  Allows one to Export Methods for a Plugin. KeySet *
    elektraPluginGetConfig (Plugin *handle)
  
  Returns the configuration of that plugin. void elektraPluginSetData
    (Plugin *plugin, void *data)
  
  Store a pointer to any plugin related data. void * elektraPluginGetData
    (Plugin *plugin)
  
  Get a pointer to any plugin related data stored before. int
    elektraDocOpen (Plugin *handle, Key *warningsKey)
  
  Initialize data for the plugin. int elektraDocClose (Plugin *handle,
    Key *warningsKey)
  
  Finalize the plugin. int elektraDocGet (Plugin *handle, KeySet
    *returned, Key *parentKey)
  
  Get data from storage to application. int elektraDocSet (Plugin
    *handle, KeySet *returned, Key *parentKey)
  
  Set data from application to storage. int elektraDocError (Plugin
    *handle, KeySet *returned, Key *parentKey)
  
  Rollback in case of errors. int elektraDocCheckConf (Key *errorKey,
    KeySet *conf)
  
  Validate plugin configuration at mount time.
  
Detailed Description¶
Elektra plugin framework.
Since:
version 0.7.0 Elektra can have multiple backends, mounted at any place in the key database.
version 0.8.0 Elektra backends are composed out of multiple plugins.
To get started with writing plugins, first read our Plugin Tutorial.
A plugin can implement any functionality related to configuration. There are 6 possible entry points for a plugin.
- elektraDocGet() will be called when configuration or the plugin's contract is retrieved from the key database
 - elektraDocSet() will be called when configuration is written to the key database
 - elektraDocOpen() will be called before any other method of the plugin is called
 - elektraDocClose() will be called as last method
 - elektraDocError() will be called when kdbSet() failed (to give the plugin a chance to recover/undo its actions)
 - elektraPluginExport() exports all methods for the plugin.
 
Additionally, make sure that you write a contract in the README.md. It is used by the build system and the mounting tools.
The names described here contain 'Doc' within the method's name just because the plugin described in this document is called doc (the doxygen source was generated from src/plugins/doc/doc.h). Always replace Doc with the name of the plugin you are going to implement or use ELEKTRA_PLUGIN_FUNCTION.
Overview
- A storage plugin gets an empty keyset in elektraDocGet() and
      constructs the information out from a file. In elektraDocSet() the
      keyset is written to a file.
    
Other persistent storage then a file is not handled within this document because it involves many other issues. For files the resolver plugin already takes care of transactions and rollback. So the storage plugin is the source and dump as known from pipes and filters. - A filter plugin is a plugin which operates on existing keys. It may process or change the keyset. Or it may reject specific keysets which do not meet some criteria.
 
Use following include to have the functions that are not implement by you available:
#include <kdbplugin.h>
Error and Warnings
number:60 description:Invalid Line encountered severity:error ingroup:plugin macro:NOEOF module:simpleini
  
   Use following include to have the macros for setting the error and adding the
    warnings available:
// using namespace ckdb; // for C++ #include <kdberrors.h>
  
   and then you can use:
ELEKTRA_SET_ERROR (ELEKTRA_ERROR_NOEOF, parentKey, "not at the end of file");
  
   Note that you also need to return -1 in the case of error. See individual
    description of entry points to implement below.
Further help
Macro Definition Documentation¶
#define ELEKTRA_ADD_WARNING(number, key, text)¶
Adds a warning in the keys metadata. Include kdberrors.h to make it work:
// using namespace ckdb; // for C++ #include <kdberrors.h>
Parameters:
key to write the error to
text additional text for the user
#define ELEKTRA_ADD_WARNINGF(number, key, formatstring, ...)¶
Adds a warning in the keys metadata. Include kdberrors.h to make it work:
// using namespace ckdb; // for C++ #include <kdberrors.h>
Parameters:
key to write the error to
formatstring a format string as in printf
... further arguments as in printf
#define ELEKTRA_PLUGIN_FUNCTION(module, function) libelektra_##module##_LTX_elektraPlugin##function¶
Declare a plugin's function name suitable for compilation variants (see doc/tutorials). It can be used in the same way as elektraPluginExport().
See also:
Parameters:
function which function it is (open, close, get, set, error)
#define ELEKTRA_README(module) ELEKTRA_README2 (module)¶
The filename for inclusion of the readme for compilation variants (see doc/tutorials).
Parameters:
#define ELEKTRA_SET_ERROR(number, key, text)¶
Sets the error in the keys metadata. Include kdberrors.h to make it work:
// using namespace ckdb; // for C++ #include <kdberrors.h>
Parameters:
key to write the error to
text additional text for the user
#define ELEKTRA_SET_ERROR_GET(parentKey)¶
Set error in kdbGet() when opening the file failed. Assumes that error reason is in errno.
Parameters:
To use it include:
#include <kdbplugin.h>
// using namespace ckdb; // for C++ #include <kdberrors.h>
Returns:
#define ELEKTRA_SET_ERROR_SET(parentKey)¶
Set error in kdbSet() when opening the file failed. Assumes that error reason is in errno.
Parameters:
To use it include:
#include <kdbplugin.h>
// using namespace ckdb; // for C++ #include <kdberrors.h>
Returns:
#define ELEKTRA_SET_ERRORF(number, key, formatstring, ...)¶
Sets the error in the keys metadata. Include kdberrors.h to make it work:
// using namespace ckdb; // for C++ #include <kdberrors.h>
Parameters:
key to write the error to
formatstring a format string as in printf
... further arguments as in printf
Function Documentation¶
int elektraDocCheckConf (Key * errorKey, KeySet * conf)¶
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.
Parameters:
conf contains the plugin configuration to be validated
Return values:
1 on success: the configuration has been changed and now it is OK.
-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.
int elektraDocClose (Plugin * handle, Key * warningsKey)¶
Finalize the plugin. Called prior to unloading the plugin dynamic module. After this function is called, it is ensured that no functions from your plugin will ever be accessed again.
Make sure to free all memory that your plugin requested at runtime. Also make sure to free what you stored by elektraPluginSetData() before.
So for the Doc plugin we need to:
int elektraDocClose (Plugin * handle, Key * warningsKey ELEKTRA_UNUSED)
{
        elektraFree (elektraPluginGetData (handle));
        return 0; /* success */
}
  
   After this call, libelektra.so will unload the plugin library, so this is the
    point to shutdown any affairs with the storage.
Parameters:
warningsKey can be used to to add warnings using ELEKTRA_ADD_WARNING (Do not add errors!)
Return values:
-1 on problems (only use ELEKTRA_ADD_WARNING, but never set an error).
See also:
elektraPluginGetData(), elektraPluginSetData() and elektraPluginGetConfig()
int elektraDocError (Plugin * handle, KeySet * returned, Key * parentKey)¶
Rollback in case of errors. First for all plugins elektraDocSet() will be called. If any plugin had problems before the commit (done by the resolver plugin), we can safely rollback our changes.
This method is rarely used by plugins, it is mainly used for resolvers (to implement rollback) or by logging plugins. It is not needed for storage plugins, because they only operate on temporary files created by the resolver.
Parameters:
returned contains a keyset with relevant keys
parentKey contains the information where to set the keys. can be used to add warnings with the macro ELEKTRA_ADD_WARNING, but do not add errors!
Return values:
0 on success with no action
-1 on failure (you can add warnings, but we are already in an error state, so do not set the error).
int elektraDocGet (Plugin * handle, KeySet * returned, Key * parentKey)¶
Get data from storage to application. Retrieve information from a permanent storage to construct a keyset.
Introduction¶
The elektraDocGet() function handle everything related to receiving keys.
Contract Handling¶
The contract is a keyset that needs to be returned if the parentKey is system/elektra/modules/yourpluginname.
Which keys and their meaning is specified in doc/CONTRACT.ini
Here is an example for our doc plugin:
int elektraDocGet (Plugin * plugin ELEKTRA_UNUSED, KeySet * returned, Key * parentKey)
{
        if (!strcmp (keyName (parentKey), "system/elektra/modules/doc"))
        {
                KeySet * contract =
                        ksNew (30, keyNew ("system/elektra/modules/doc", KEY_VALUE, "doc plugin waits for your orders", KEY_END),
                               keyNew ("system/elektra/modules/doc/exports", KEY_END),
                               keyNew ("system/elektra/modules/doc/exports/open", KEY_FUNC, elektraDocOpen, KEY_END),
                               keyNew ("system/elektra/modules/doc/exports/close", KEY_FUNC, elektraDocClose, KEY_END),
                               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);
                ksDel (contract);
                return 1; /* success */
        }
  
   Some clauses of the contract, especially the description of the plugin can be
    done more conveniently directly in a README.md that is included by
    ELEKTRA_README.
Storage Plugins¶
For storage plugins the filename is written in the value of the parentKey. So the first task of the plugin is to open that file. Then it should parse its content and construct a keyset with all information of that file.
You need to be able to reconstruct the same file with the information of the keyset. So be sure to copy all comments, whitespaces and so on into some metadata of the keys. Otherwise the information is lost after writing the file the next time.
Now lets look at an example how the typical elektraDocGet() might be implemented. To explain we introduce some pseudo functions which do all the work with the storage (which is of course 90% of the work for a real plugin):
- •
 - parse_key will parse a key and a value from an open file handle
 
The typical loop for a storage plugin will be like:
FILE * fp = fopen (keyString (parentKey), "r");
char * key = 0;
char * value = 0;
while (parseKey (fp, &key, &value) >= 1)
{
Key * read = keyNew (keyName (parentKey), KEY_END);
if (keyAddName (read, key) == -1)
{
ELEKTRA_ADD_WARNING (ELEKTRA_WARNING_INVALID_KEY, parentKey, key);
keyDel (read);
continue;
}
keySetString (read, value);
ksAppendKey (returned, read);
}
if (feof (fp) == 0)
{
fclose (fp);
ELEKTRA_SET_ERROR (ELEKTRA_ERROR_NOEOF, parentKey, "not at the end of file");
return -1;
}
fclose (fp);
  
   When opening files, make sure to use the macros ELEKTRA_SET_ERROR_SET
    and ELEKTRA_SET_ERROR_GET for errors, as shown here:
FILE * fp = fopen (keyString (parentKey), "w");
if (!fp)
{
ELEKTRA_SET_ERROR_SET (parentKey);
return -1;
}
Filter Plugins¶
For filter plugins the actual task is rather unspecified. You basically can do anything with the keyset. To get roundtrip properties you might want to undo any changes you did in elektraDocSet().
The pseudo functions (which do the real work) are:
- •
 - do_action() which processes every key in this filter
 
Key * k;
ksRewind (returned);
while ((k = ksNext (returned)) != 0)
{
doAction (k);
}
return 1; // success }
Precondition:
- is a valid key (means that it is a system or user key).
 - is your mountpoint and that your plugin is responsible for it.
 
and that the returned:
- is a valid keyset.
 - your plugin is only called when needed (e.g. only if file was modified)
 - has all keys related to your plugin.
 - contains only valid keys below (see keyIsBelow()) your parentKey.
 - is in a sorted order (given implicit by KeySet)
 
and that the handle:
- is valid for your plugin.
 - that elektraPluginGetData() contains the same handle for lifetime of your plugin.
 
The caller kdbGet() will make sure that afterwards you were called, that:
- other plugins below your plugin will be called again recursively.
 - that all keys are merged to one keyset the user gets
 - that all keys (that should not be removed) are passed to kdbSet() if writing to disc is needed.
 
Invariant:
Postcondition:
Updating
See also:
Parameters:
returned contains a keyset where the function need to append the keys got from the storage. There might be also some keys inside it, see conditions. You may use them to support efficient updating of keys, see updating.
parentKey contains the information below which key the keys should be gotten.
Return values:
0 when nothing was to do
-1 on failure, the current key in returned shows the position. use ELEKTRA_SET_ERROR of kdberrors.h to define the error code. You additionally can add as many warnings as you would like to add.
int elektraDocOpen (Plugin * handle, Key * warningsKey)¶
Initialize data for the plugin. This is the first method called after dynamically loading this plugin. It is guaranteed, that this method will be called before any other method.
This method is responsible for:
- plugin's specific configuration gathering
 - initialization of all plugin's internal structs
 - initial setup of all I/O details such as opening a file, connecting to a database, setup connection to a server, iff this cannot be done per invocation in elektraDocGet() and elektraDocSet().
 
You may also read the configuration you can get with elektraPluginGetConfig() and transform it into other structures used by your plugin.
Note:
Instead of global variables the methods elektraPluginGetData() and elektraPluginSetData() exist to store and get any information related to your plugin.
The correct substitute for global variables will be:
typedef struct
{
        int global;
} GlobalData;
  
   and then initialize it using:
int elektraDocOpen (Plugin * handle, Key * warningsKey ELEKTRA_UNUSED)
{
        GlobalData * data;
        KeySet * config = elektraPluginGetConfig (handle);
        Key * kg = ksLookupByName (config, "/global", 0);
        data = elektraMalloc (sizeof (GlobalData));
        data->global = 0;
        if (kg) data->global = atoi (keyString (kg));
        elektraPluginSetData (handle, data);
  
   Make sure to free everything you allocate within
  elektraDocClose().
If your plugin has no useful way to startup without config, the module loader would not be able to load the module. We need, however, to still load the plugin to get the contract.
To solve that problem the module loader adds the configuration key /module. Even if your plugin is basically not able to startup successfully, it should still provide a fallback when /module is present, so that elektraDocGet() on system/elektra/modules can be called successfully later on.
if (ksLookupByName (config, "/module", 0))
{
return 0;
}
// do some setup that will fail without configuration
  
   Note that for plugins where the contract will be altered based on
    configuration this specific configuration should be considered. In fact the
    idea of /module is to get the correct contract.
Return values:
1 on success
Parameters:
warningsKey can be used to add warnings with the macro ELEKTRA_ADD_WARNING (Do not add errors!)
See also:
elektraDocClose()
int elektraDocSet (Plugin * handle, KeySet * returned, Key * parentKey)¶
Set data from application to storage. This function does everything related to set and remove keys in a plugin. There is only one function for that purpose to make implementation of file based plugins much easier.
The keyset returned was filled in with information from the application using elektra and the task of this function is to store it in a permanent way so that a subsequent call of elektraPluginGet() can rebuild the keyset as it was before. See the live cycle to understand:
static void usercode (KDB * handle, KeySet * keyset, Key * key)
{
        // some more user code
        keySetString (key, "mycomment"); // the user changes the key
        ksAppendKey (keyset, key);       // append the key to the keyset
        kdbSet (handle, keyset, 0);      // and syncs it to disc
}
// so now kdbSet is called
int elektraKdbSet (KDB * handle, KeySet * keyset, Key * parentKey)
{
        int ret = 0;
        // find appropriate plugin and then call it:
        Plugin * plugin = findPlugin (handle);
        ret = elektraDocSet (plugin, keyset, parentKey);
        // the keyset with the key (and others for this plugin)
        // will be passed to this function
        return ret;
}
// so now elektraPluginSet(), which is the function described here,
// is called:
int elektraPluginSet (Plugin * plugin ELEKTRA_UNUSED, KeySet * returned, Key * parentKey ELEKTRA_UNUSED)
{
        // the task of elektraPluginSet is now to store the keys
        Key * k;
        ksRewind (returned);
        while ((k = ksNext (returned)) != 0)
        {
                saveToDisc (k);
        }
        return 1; /* success */
}
  
   Of course all information of every key in the keyset returned need to be
    stored permanently. So this specification needs to give an exhaustive list
    of information present in a key.
Precondition:
The parentKey is the key which is the ancestor for all other keys in the keyset. The first key of the keyset returned has the same keyname. The name of the parentKey marks the mountpoint. The string of the parentKey is the filename to write to.
Make sure to set all keys, all directories and also all hidden keys. If some of them are not wished, the caller kdbSet() and plugins will sort them out.
Invariant:
Postcondition:
See also:
Parameters:
returned contains a keyset with relevant keys
parentKey contains the information where to set the keys (name is mountpoint your plugin is mounted, string is the file to write to)
Returns:
Return values:
0 on success with no changed key in database
-1 on failure. The cause of the error needs to be added in parentKey You also have to make sure that ksGetCursor() shows to the position where the error appeared. 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.
Plugin* elektraPluginExport (const char * pluginName, ...)¶
Allows one to Export Methods for a Plugin. This function must be called within ELEKTRA_PLUGIN_EXPORT. It define the plugin's methods that will be exported.
All KDB methods implemented by the plugin basically could have random names (convention is elektraName*), except ELEKTRA_PLUGIN_EXPORT.
This is the single symbol that will be looked up when loading the plugin, and the first method of the backend implementation that will be called.
You need to use a macro so that both dynamic and static loading of the plugin works. For example for the doc plugin:
Plugin * ELEKTRA_PLUGIN_EXPORT (doc)
{
        // clang-format off
        return elektraPluginExport(DOC_PLUGIN_NAME,
                ELEKTRA_PLUGIN_OPEN,    &elektraDocOpen,
                ELEKTRA_PLUGIN_CLOSE,   &elektraDocClose,
                ELEKTRA_PLUGIN_GET,     &elektraDocGet,
                ELEKTRA_PLUGIN_SET,     &elektraDocSet,
                ELEKTRA_PLUGIN_ERROR,   &elektraDocError,
                ELEKTRA_PLUGIN_END);
}
  
   The first parameter is the name of the plugin. Then every plugin should have:
    ELEKTRA_PLUGIN_OPEN, ELEKTRA_PLUGIN_CLOSE, ELEKTRA_PLUGIN_GET,
    ELEKTRA_PLUGIN_SET and optionally ELEKTRA_PLUGIN_ERROR.
The list is terminated with ELEKTRA_PLUGIN_END.
You must use static 'char arrays' in a read only segment. Don't allocate storage, it won't be freed.
Parameters:
Returns:
KeySet* elektraPluginGetConfig (Plugin * handle)¶
Returns the configuration of that plugin.
- The user/ config holds plugin specific configuration
 - The system/ config holds backend specific configuration
 
So prefer cascading lookups to honor both.
Parameters:
Returns:
void* elektraPluginGetData (Plugin * plugin)¶
Get a pointer to any plugin related data stored before. If elektraPluginSetData() was not called earlier, NULL will be returned.
See also:
Parameters:
Returns:
void elektraPluginSetData (Plugin * plugin, void * data)¶
Store a pointer to any plugin related data.
See also:
Parameters:
data the pointer to the data
Author¶
Generated automatically by Doxygen for Elektra from the source code.
| Mon Jan 15 2018 | Version 0.8.20 |