collectd's plugin architecture
The plugin structure in itself is not very complicated, but it's
easy to forget something anyway. I recommend copying a simple plugin
(the load-plugin, for
example) and start from there. Please note that this document
describes the plugin architecture of version 4. The
plugin architecture that was used in version 3 is obsolete.
Here's a walk-through or a check-list of things a plugin needs.
Copyright notice
All *.c-files must include a copyright
notice and license information. The license must be compatible to
collectd's own license, the
GPL 2.
Unless you have a good reason to split up your plugin into multiple
files, please put everything into one .c-file.
Includes / Header files
Usually plugins include the following header-files:
#include "common.h" /* auxiliary functions */
#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
Callback functions
The initial step for a plugin is to register
one or more callback functions. After the shared object has
been loaded, the daemon will call the function
void module_register (void) that has to be
exported by the plugin. The purpose of this function is to register
callback functions with the daemon (and nothing else).
There are the following types of callback functions, that can be registered. Of course, they are all optional.
config-functions
If a plugin needs configuration it must provide a config-function.
The function is called once for each config-item found. It is
registered using the plugin_register_config function.
There's no garuantee that it's called only once for each config-key or
that it's called at all.
Prototype
The function needs to have the following prototype:
Return value
The return-value must be zero upon success, greater than zero if it
failed or less than zero if key has an invalid value.
init-functions
The init-function is used to set a plugin up before
using it. The init-function is called after the
configfile has been read (i. e. all calls to the
config-function have been made) and before any calls to
the read- and write-functions.
Prototype
Return value
The function has to return zero if successfull and non-zero otherwise. If non-zero is returned all functions the plugin registered will be unregistered.
read-functions
This function is called once every step seconds. It's
supposed to gather the values from somewhere and eventually call
plugin_dispatch_values. Several implementation of this
function may exist, but only one may be compiled. Use
#defines to decide which function to compile.
Prototype
Return value
The function has to return zero if successfull and non-zero
otherwise. If non-zero is returned this function will not be called
again for some time. The time between the calls of the function is
increased exponentially until one day (86400 seconds) is reached. As
soon as the function returns zero it will be called every
step seconds again.
write-functions
Plugins that implement writing of values to somewhere may register
a write-function. When a read-function calls
plugin_dispatch_values that value-list will be passed to
all write-functions together with a data-set, a
description of the values. See below for a description of these
data-types. Please note that this function needs to be
thread-safe.
Prototype
Return value
As expected: Zero indicates success, non-zero indicates failure.
log-functions
These callbacks are called whenever some plugin (or the daemon
itself) want to inform the world about something. Please note that
using one of the logging macros, such as ERROR from
within these functions will result in an endless loop.
Prototype
shutdown-functions
Many plugins wish to clean up after themselves. The
shutdown-functions can be used to implement that and
terminate threads, close files or similar tasks before exiting.
Prototype
Return value
As expected: Zero indicates success, non-zero indicates failure.
The module_register-function
The only function with a fixed name. It's called
once after the shared object has been loaded so the plugin can
register it's functions with the rest of the plugin architecture.
Typically thats one or more calls to plugin_register_*.
Prototype
Please note that this function may not be static and the name may not be changed.
Data types
There are two complex data types that plugins may encounter. The
first one, value_list_t, is used when passing values from
the plugin to the plugin_dispatch_values function from
the plugin interface. The second one, data_set_t occurs
in the prototype of write-functions and may come up when
dynamically loading data-set-definitions. All the following
declarations can be found in "plugin.h".
value_list_t
This is the container for values that are passed to the daemon or
from the daemon to write-functions. It is defined as
follows:
{
/* Array of values. */
value_t *values;
int values_len;
/* Time when the values were collected. */
time_t time;
/* Identity of the values (without type). */
char host[DATA_MAX_NAME_LEN];
char plugin[DATA_MAX_NAME_LEN];
/* These two may be empty: */
char plugin_instance[DATA_MAX_NAME_LEN];
char type_instance[DATA_MAX_NAME_LEN];
};
typedef struct value_list_s value_list_t;
Please note that the char-arrays have a fixed size of
DATA_MAX_NAME_LEN (currently 64). Don't let this buffer
overflow! Also, you can use the define VALUE_LIST_INIT to
initialize (static) instances of this type. It has proven handy to
implement a "submit" function with a static array of type
value_t and a static instance of this type.
data_set_t
The data_set_t type is registered once with the daemon
and used to know of which type each value_t in a
value_list_t is and which minimum/maximum values are
allowed. This is, of course, mostly relevant for
write-functions which get a structure of this type as the
first argument.
{
/* Name of the value/data source */
char name[DATA_MAX_NAME_LEN];
/* DS_TYPE_COUNTER or DS_TYPE_GAUGE */
int type;
/* Minimum/maximum value; NAN means undefined */
double min;
double max;
};
typedef struct data_source_s data_source_t;
struct data_set_s
{
/* Name of the data-set (== type) */
char type[DATA_MAX_NAME_LEN];
/* Array of values/data-sources */
data_source_t *ds;
int ds_num;
};
typedef struct data_set_s data_set_t;
Plugins can register such a data_set_t using
plugin_register_data_set. The structure will be copied,
i. e. it can be freed in the plugin after registering it. For
plugins that simply want to provide a read-function it is
recommended to include the data-set-definition in the
types.db rather than registering it from within the
plugin.
The interface's stability
Please be aware, that the plugin-interface is not
considered part of the backwards compatibility of new minor versions,
i. e. it may change pretty much at any time. It will certainly
change with the next major version.
It's not as if we change the interface with every other release, but
if you want to be really sure that your plugin works next year, please send it in.
We will then take care of it and adapt the interface should it
change.
