- Background about plugins framework
Many component systems pass information between the various components by
calling methods of each other. This is not the way Elektra´s plugin
system works. Instead, the core passes a KeySet
object in one direction
from plugin to plugin. So they form a so called pipes-and-filter. Each of the
plugins can modify the configuration or add any other information using
metakeys. While this approach is in general less flexible, this information
flow still allows powerful chaining. Because plugins do not have to bother to
call other plugins, the plugin development is also easier. The ordering of
plugins in backends is controlled using contracts.
Every plugin should provide a full contract to give information how it will work
with other plugins. Most parts of the contract are obligatory. Plugins cannot
be loaded without this information. For example, plugins must provide the
. It is vital so that the plugin loader knows which
version of Elektra the plugin was built for.
It is, however, up to the plugin not to have every clause of the contract. For
example, the plugin might not tell what it provides or needs. It can also
leave any description out. In this situation it is unclear what the plugin
will do. Such a plugin can add or remove keys, and changes values and metadata
regardless what other plugins expect. If only such plugins existed there would
be chaos. It would be impossible to determine the behaviour of a backend which
uses a multiple of such plugins.
To avoid this situation, every plugin exports a contract describing how the
plugin modifies the KeySet returned
. Most often it is enough to
state that it is a storage
plugin or that it will filter
The data structures, however, are already responsible for most of the pre- and
postconditions. Every condition the data structure guarantees, takes away a
concern for the plugins. All the parts that are already guaranteed by data
structures do not need to be stated in the contract.
Plugins should not be burdened to check too many postconditions. Instead,
plugins focus on their task. The plugin does not need to check the sync flag
of keys or if the keys are below the mount point. The core already guarantees
correct behaviour as described in algorithm
To sum up, contracts give the information how a plugin interacts with others. It
describes if, and how, the KeySet returned
is changed. Using
contracts, we get a predictable behaviour, but still support every kind of
As already stated, some parts of the contracts are obligatory. kdb mount
needs to know which symbols the plugin exports. Only the
symbol is mandatory - it is used to yield this
information. Elektra´s core also uses the functions
if available. Other
functions like serialise
implement special features can be supported, but are ignored by the core. For
the user of the library these functions can be very useful. These functions
shall either belong to the concern of the plugin or be implemented within the
plugin because of the dependences.
As described in infos/provides /doc/CONTRACT.ini
, the plugin can also
provide descriptive information, for example about the author and the licence.
Advanced plugins can also export plugin configuration for other plugins so
that the overall backend works properly. Last, but not least, as enumerated in
dependency and placement information
makes the system reliable and robust. With that information, plugins can be
placed into a backend in an automatic and secure way.
provides for every module the information
described above. The entry exists once a plugin of that module is loaded. For
each module a special module backend
is generated and mounted at
function generates this described contract on
For example, the ccode plugin, implements:
int elektraCcodeGet(Plugin *handle, KeySet *returned, Key *parentKey)
if (!strcmp (keyName(parentKey), "system/elektra/modules/ccode"))
KeySet *contract = ksNew (30,
ksAppend (returned, contract);
// implementation of elektraCcodeGet
We see in Listing above that the plugin generates and returns the contract if,
and only if, the name of the parentKey
. The user and the contract checker can
access the contract of ccode below the key system/elektra/modules/ccode
in the same way other configuration is accessed. Note that we also have to
at the end of the contract to not execute the regular
functionality of the plugin.
This configuration is static and contains the contract information. In theory,
the contract can be changed without any problems in ways that it provides more
and obligates less. But the problem is that it will not be checked if this is
the case because a recheck of the contracts of a backend is very expensive.
The contract checker doing this, only runs once during mount time. Changing
contracts in an incompatible way forces the user to remove all mount points
where the plugin is and mount it again. Such actions are only sustainable in a
development phase and not in a productive environment.
But the plugin´s implementation is allowed to change without being
remounted if it is a subtype of the earlier version. Only in this situation it
can be a drop-in placement. With a good testing framework the behaviour can be
checked to some extent.
We also see in Listing above that the code responsible for generating the
contract and the code for the implementation are next to each other. Plugins
need to satisfy those self-imposed obligations that are described in
contracts. They ensure that plugins interact in predictable ways. So the
process of writing individual plugins and composing them together can be
described as Component-Based Software Engineering.
Plugins can also be viewed as framework extensions. A component abstracts
plugins. But this term is misleading in our case, because components usually
can choose which interfaces they implement. Elektra´s plugins, however,
are restricted to implement one specific interface. Without contracts, plugins
could not interact as described in this chapter.
- elektra-contracts(7) elektra-contracts.md