* ipvs plugin: The new `ipvs' plugin collects IPVS connection statistics
(number of connections, octets and packets for each service and
destination).
+ * rrdtool plugin: Use the threadsafe RRD-library if available. Try to
+ be more threadsafe otherwise by locking calls to the library.
+
+yyyy-mm-dd, Version 4.1.3
+ * rrdtool plugin: Use the threadsafe RRD-library if available. Try to
+ be more threadsafe otherwise by locking calls to the library.
2007-09-28, Version 4.1.2
* apcups plugin: Fix reporting of the `load percent' data.
* When the `csv' or `rrdtool' plugins are loaded they'll write the values to
files. The usual place for these files is beneath `/var/lib/collectd'.
- * When using some of the plugins, collectd needs to run as user root, since only
- root can do certain things, such as craft ICMP packages needed to ping
+ * When using some of the plugins, collectd needs to run as user root, since
+ only root can do certain things, such as craft ICMP packages needed to ping
other hosts. collectd should NOT be installed setuid root since it can be
used to overwrite valuable files!
the values and read the rrdtool(1) manpage thoroughly.
+collectd and chkrootkit
+-----------------------
+
+ If you are using the `dns' plugin chkrootkit(1) will report collectd as a
+ packet sniffer ("<iface>: PACKET SNIFFER(/usr/sbin/collectd[<pid>])"). The
+ plugin captures all UDP packets on port 53 to analyze the DNS traffic. In
+ this case, collectd is a legitimate sniffer and the report should be
+ considered to be a false positive. However, you might want to check that
+ this really is collectd and not some other, illegitimate sniffer.
+
+
Prerequisites
-------------
Crosscompiling
--------------
- To compile correctly collectd needs to be able to initialize static
- variables to NAN (Not A Number). Some C libraries, especially the GNU
- libc, have a problem with that.
+ To compile correctly collectd needs to be able to initialize static
+ variables to NAN (Not A Number). Some C libraries, especially the GNU
+ libc, have a problem with that.
- Luckily, with GCC it's possible to work around that problem: One can define
- NAN as being (0.0 / 0.0) and `isnan' as `f != f'. However, to test this
- ``implementation'' the configure script needs to compile and run a short
- test program. Obviously running a test program when doing a cross-
- compilation is, well, challenging.
+ Luckily, with GCC it's possible to work around that problem: One can define
+ NAN as being (0.0 / 0.0) and `isnan' as `f != f'. However, to test this
+ ``implementation'' the configure script needs to compile and run a short
+ test program. Obviously running a test program when doing a cross-
+ compilation is, well, challenging.
- If you run into this problem, you can use the `--with-nan-emulation'
- configure option to force the use of this implementation. We can't promise
- that the compiled binary actually behaves as it should, but since NANs
- are likely never passed to the libm you have a good chance to be lucky.
+ If you run into this problem, you can use the `--with-nan-emulation'
+ configure option to force the use of this implementation. We can't promise
+ that the compiled binary actually behaves as it should, but since NANs
+ are likely never passed to the libm you have a good chance to be lucky.
Contact
--- /dev/null
+# /usr/share/doc/collectd/examples/MyPlugin.pm
+#
+# A Perl plugin template for collectd.
+#
+# Written by Sebastian Harl <sh@tokkee.org>
+#
+# This is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation; only version 2 of the License is applicable.
+
+# Notes:
+# - each of the functions below (and the corresponding plugin_register call)
+# is optional
+
+package Collectd::Plugin::MyPlugin;
+
+use strict;
+use warnings;
+
+# data set definition:
+# see section "DATA TYPES" in collectd-perl(5) for details
+my $dataset =
+[
+ {
+ name => 'my_ds',
+ type => Collectd::DS_TYPE_GAUGE,
+ min => 0,
+ max => 65535,
+ },
+];
+
+# This code is executed after loading the plugin to register it with collectd.
+Collectd::plugin_register (Collectd::TYPE_LOG, 'myplugin', \&my_log);
+Collectd::plugin_register (Collectd::TYPE_DATASET, 'myplugin', $dataset);
+Collectd::plugin_register (Collectd::TYPE_INIT, 'myplugin', \&my_init);
+Collectd::plugin_register (Collectd::TYPE_READ, 'myplugin', \&my_read);
+Collectd::plugin_register (Collectd::TYPE_WRITE, 'myplugin', \&my_write);
+Collectd::plugin_register (Collectd::TYPE_SHUTDOWN, 'myplugin', \&my_shutdown);
+
+# For each of the functions below see collectd-perl(5) for details about
+# arguments and the like.
+
+# This function is called once upon startup to initialize the plugin.
+sub my_init
+{
+ # open sockets, initialize data structures, ...
+
+ # A false return value indicates an error and causes the plugin to be
+ # disabled.
+ return 1;
+} # my_init ()
+
+# This function is called in regular intervals to collectd the data.
+sub my_read
+{
+ # value list to dispatch to collectd:
+ # see section "DATA TYPES" in collectd-perl(5) for details
+ my $vl = {};
+
+ # do the magic to read the data:
+ # the number of values has to match the number of data sources defined in
+ # the registered data set
+ $vl->{'values'} = [ rand(65535) ];
+ $vl->{'plugin'} = 'myplugin';
+ # any other elements are optional
+
+ # dispatch the values to collectd which passes them on to all registered
+ # write functions - the first argument is used to lookup the data set
+ # definition
+ Collectd::plugin_dispatch_values ('myplugin', $vl);
+
+ # A false return value indicates an error and the plugin will be skipped
+ # for an increasing amount of time.
+ return 1;
+} # my_read ()
+
+# This function is called after values have been dispatched to collectd.
+sub my_write
+{
+ my $type = shift;
+ my $ds = shift;
+ my $vl = shift;
+
+ if (scalar (@$ds) != scalar (@{$vl->{'values'}})) {
+ Collectd::plugin_log (Collectd::LOG_WARNING,
+ "DS number does not match values length");
+ return;
+ }
+
+ for (my $i = 0; $i < scalar (@$ds); ++$i) {
+ # do the magic to output the data
+ print "$vl->{'host'}: $vl->{'plugin'}: ";
+
+ if (defined $vl->{'plugin_instance'}) {
+ print "$vl->{'plugin_instance'}: ";
+ }
+
+ print "$type: ";
+
+ if (defined $vl->{'type_instance'}) {
+ print "$vl->{'type_instance'}: ";
+ }
+
+ print "$vl->{'values'}->[$i]\n";
+ }
+ return 1;
+} # my_write()
+
+# This function is called before shutting down collectd.
+sub my_shutdown
+{
+ # close sockets, ...
+ return 1;
+} # my_shutdown ()
+
+# This function is called when plugin_log () has been used.
+sub my_log
+{
+ my $level = shift;
+ my $msg = shift;
+
+ print "LOG: $level - $msg\n";
+ return 1;
+} # my_log ()
+
--- /dev/null
+/*
+ * /usr/share/doc/collectd/examples/myplugin.c
+ *
+ * A plugin template for collectd.
+ *
+ * Written by Sebastian Harl <sh@tokkee.org>
+ *
+ * This is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; only version 2 of the License is applicable.
+ */
+
+/*
+ * Notes:
+ * - plugins are executed in parallel, thus, thread-safe
+ * functions need to be used
+ * - each of the functions below (except module_register)
+ * is optional
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+
+#ifndef __USE_ISOC99 /* required for NAN */
+# define DISABLE_ISOC99 1
+# define __USE_ISOC99 1
+#endif /* !defined(__USE_ISOC99) */
+#include <math.h>
+#if DISABLE_ISOC99
+# undef DISABLE_ISOC99
+# undef __USE_ISOC99
+#endif /* DISABLE_ISOC99 */
+
+#include <collectd/collectd.h>
+#include <collectd/common.h>
+#include <collectd/plugin.h>
+
+/*
+ * data source definition:
+ * - name of the data source
+ * - type of the data source (DS_TYPE_GAUGE, DS_TYPE_COUNTER)
+ * - minimum allowed value
+ * - maximum allowed value
+ */
+static data_source_t dsrc[1] =
+{
+ { "my_ds", DS_TYPE_GAUGE, 0, NAN }
+};
+
+/*
+ * data set definition:
+ * - name of the data set
+ * - number of data sources
+ * - list of data sources
+ */
+static data_set_t ds =
+{
+ "myplugin", STATIC_ARRAY_SIZE (dsrc), dsrc
+};
+
+/*
+ * This function is called once upon startup to initialize the plugin.
+ */
+static int my_init (void)
+{
+ /* open sockets, initialize data structures, ... */
+
+ /* A return value != 0 indicates an error and causes the plugin to be
+ disabled. */
+ return 0;
+} /* static int my_init (void) */
+
+/*
+ * This function is called in regular intervalls to collect the data.
+ */
+static int my_read (void)
+{
+ value_t values[1]; /* the size of this list should equal the number of
+ data sources */
+ value_list_t vl = VALUE_LIST_INIT;
+
+ /* do the magic to read the data */
+ values[0].gauge = random ();
+
+ vl.values = values;
+ vl.values_len = 1;
+ vl.time = time (NULL);
+ strcpy (vl.host, hostname_g);
+ strcpy (vl.plugin, "myplugin");
+ /* optionally set vl.plugin_instance and vl.type_instance to reasonable
+ * values (default: "") */
+
+ /* dispatch the values to collectd which passes them on to all registered
+ * write functions - the first argument is used to lookup the data set
+ * definition */
+ plugin_dispatch_values ("myplugin", &vl);
+
+ /* A return value != 0 indicates an error and the plugin will be skipped
+ * for an increasing amount of time. */
+ return 0;
+} /* static int my_read (void) */
+
+/*
+ * This function is called after values have been dispatched to collectd.
+ */
+static int my_write (const data_set_t *ds, const value_list_t *vl)
+{
+ char name[1024] = "";
+ int i = 0;
+
+ if (ds->ds_num != vl->values_len) {
+ plugin_log (LOG_WARNING, "DS number does not match values length");
+ return -1;
+ }
+
+ /* get the default base filename for the output file - depending on the
+ * provided values this will be something like
+ * <host>/<plugin>[-<plugin_type>]/<instance>[-<instance_type>] */
+ if (0 != format_name (name, 1024, vl->host, vl->plugin,
+ vl->plugin_instance, ds->type, vl->type_instance))
+ return -1;
+
+ for (i = 0; i < ds->ds_num; ++i) {
+ /* do the magic to output the data */
+ printf ("%s (%s) at %i: ", name,
+ (ds->ds->type == DS_TYPE_GAUGE) ? "GAUGE" : "COUNTER",
+ (int)vl->time);
+
+ if (ds->ds->type == DS_TYPE_GAUGE)
+ printf ("%f\n", vl->values[i].gauge);
+ else
+ printf ("%lld\n", vl->values[i].counter);
+ }
+ return 0;
+} /* static int my_write (data_set_t *, value_list_t *) */
+
+/*
+ * This function is called when plugin_log () has been used.
+ */
+static void my_log (int severity, const char *msg)
+{
+ printf ("LOG: %i - %s\n", severity, msg);
+ return;
+} /* static void my_log (int, const char *) */
+
+/*
+ * This function is called before shutting down collectd.
+ */
+static int my_shutdown (void)
+{
+ /* close sockets, free data structures, ... */
+ return 0;
+} /* static int my_shutdown (void) */
+
+/*
+ * This function is called after loading the plugin to register it with
+ * collectd.
+ */
+void module_register (void)
+{
+ plugin_register_log ("myplugin", my_log);
+ plugin_register_data_set (&ds);
+ plugin_register_read ("myplugin", my_read);
+ plugin_register_init ("myplugin", my_init);
+ plugin_register_write ("myplugin", my_write);
+ plugin_register_shutdown ("myplugin", my_shutdown);
+ return;
+} /* void module_register (void) */
+
#<Plugin rrdtool>
# DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd"
-# StepSize 10
-# HeartBeat 20
-# RRARows 1200
-# XFF 0.1
# CacheTimeout 120
# CacheFlush 900
#</Plugin>
=item B<DataDir> I<Directory>
-Set the directory to store RRD-files under. Per default RRD-files are generated
+Set the directory to store CSV-files under. Per default CSV-files are generated
beneath the daemon's working directory, i.E<nbsp>e. the B<BaseDir>.
=back
The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to the
database when started and keeps the connection up as long as possible. When the
connection is interrupted for whatever reason it will try to re-connect. The
-plugin logs loud complaints in case anything goes wrong.
+plugin will complaint loudly in case anything goes wrong.
This plugin issues C<SHOW STATUS> and evaluates C<Bytes_{received,sent}>,
C<Com_*> and C<Handler_*> which correspond to F<mysql_octets.rrd>,
vl->host, strlen (vl->host)) != 0)
return (-1);
strcpy (vl_def->host, vl->host);
- DEBUG ("network plugin: add_to_buffer: host = %s", vl->host);
}
if (vl_def->time != vl->time)
(uint64_t) vl->time))
return (-1);
vl_def->time = vl->time;
- DEBUG ("network plugin: add_to_buffer: time = %u",
- (unsigned int) vl->time);
}
if (vl_def->interval != vl->interval)
(uint64_t) vl->interval))
return (-1);
vl_def->interval = vl->interval;
- DEBUG ("network plugin: add_to_buffer: interval = %i",
- (int) vl->interval);
}
if (strcmp (vl_def->plugin, vl->plugin) != 0)
vl->plugin, strlen (vl->plugin)) != 0)
return (-1);
strcpy (vl_def->plugin, vl->plugin);
- DEBUG ("network plugin: add_to_buffer: plugin = %s",
- vl->plugin);
}
if (strcmp (vl_def->plugin_instance, vl->plugin_instance) != 0)
strlen (vl->plugin_instance)) != 0)
return (-1);
strcpy (vl_def->plugin_instance, vl->plugin_instance);
- DEBUG ("network plugin: add_to_buffer: plugin_instance = %s",
- vl->plugin_instance);
}
if (strcmp (type_def, ds->type) != 0)
ds->type, strlen (ds->type)) != 0)
return (-1);
strcpy (type_def, ds->type);
- DEBUG ("network plugin: add_to_buffer: type = %s", ds->type);
}
if (strcmp (vl_def->type_instance, vl->type_instance) != 0)
strlen (vl->type_instance)) != 0)
return (-1);
strcpy (vl_def->type_instance, vl->type_instance);
- DEBUG ("network plugin: add_to_buffer: type_instance = %s",
- vl->type_instance);
}
if (write_part_values (&buffer, &buffer_size, ds, vl) != 0)
static int network_shutdown (void)
{
- DEBUG ("Shutting down.");
-
listen_loop++;
if (listen_thread != (pthread_t) 0)
listen_thread = (pthread_t) 0;
}
- listen_thread = 0;
+ if (send_buffer_fill > 0)
+ flush_buffer ();
if (cache_tree != NULL)
{
/* Leave the rest at zero if this is only a zombi */
if (ps->num_proc == 0)
{
- DEBUG ("This is only a zombi: pid = %i; name = %s;",
- pid, ps->name);
+ DEBUG ("processes plugin: This is only a zombi: pid = %i; "
+ "name = %s;", pid, ps->name);
return (0);
}
avl_iterator_t *iter;
int i;
- DEBUG ("Flushing cache, timeout = %i", timeout);
+ DEBUG ("rrdtool plugin: Flushing cache, timeout = %i", timeout);
now = time (NULL);
iter = avl_get_iterator (cache);
while (avl_iterator_next (iter, (void *) &key, (void *) &rc) == 0)
{
- DEBUG ("key = %s; age = %i;", key, now - rc->first_value);
-
if (rc->flags == FLAG_QUEUED)
continue;
else if ((now - rc->first_value) < timeout)
{
if (avl_remove (cache, keys[i], (void *) &key, (void *) &rc) != 0)
{
- DEBUG ("avl_remove (%s) failed.", keys[i]);
+ DEBUG ("rrdtool plugin: avl_remove (%s) failed.", keys[i]);
continue;
}
} /* for (i = 0..keys_num) */
free (keys);
- DEBUG ("Flushed %i value(s)", keys_num);
cache_flush_last = now;
} /* void rrd_cache_flush */
avl_insert (cache, cache_key, rc);
}
- DEBUG ("rrd_cache_insert (%s, %s, %u) = %p", filename, value,
- (unsigned int) value_time, (void *) rc);
+ DEBUG ("rrdtool plugin: rrd_cache_insert: file = %s; "
+ "values_num = %i; age = %u;",
+ filename, rc->values_num,
+ rc->last_value - rc->first_value);
if ((rc->last_value - rc->first_value) >= cache_timeout)
{
rrd_cache_flush (-1);
pthread_mutex_unlock (&cache_lock);
+ /* Wait for all the values to be written to disk before returning. */
+ if (queue_thread != 0)
+ {
+ pthread_join (queue_thread, NULL);
+ queue_thread = 0;
+ DEBUG ("rrdtool plugin: queue_thread exited.");
+ }
+
pthread_mutex_lock (&queue_lock);
do_shutdown = 1;
pthread_cond_signal (&queue_cond);
while (loop != 0)
{
- DEBUG ("Calling accept..");
+ DEBUG ("unixsock plugin: Calling accept..");
status = accept (sock_fd, NULL, NULL);
if (status < 0)
{
ignorelist_item_t *this;
ignorelist_item_t *next;
- DEBUG ("(il = 0x%p)", (void *) il);
-
if (il == NULL)
return;
return get_spec_by_x(UUID, uuid);
bad_uuid:
- DEBUG("Found an invalid UUID: %s", s);
+ DEBUG("utils_mount: Found an invalid UUID: %s", s);
return NULL;
}
}
else if (strncmp (optstr, "UUID=", 5) == 0)
{
- DEBUG ("TODO: check UUID= code!");
+ DEBUG ("utils_mount: TODO: check UUID= code!");
rc = get_spec_by_uuid (optstr + 5);
}
else if (strncmp (optstr, "LABEL=", 6) == 0)
{
- DEBUG ("TODO: check LABEL= code!");
+ DEBUG ("utils_mount: TODO: check LABEL= code!");
rc = get_spec_by_volume_label (optstr + 6);
}
else
if(!rc)
{
- DEBUG ("Error checking device name: optstr = %s", optstr);
+ DEBUG ("utils_mount: Error checking device name: optstr = %s", optstr);
}
return rc;
}
struct tabmntent *mntlist;
if(listmntent(&mntlist, COLLECTD_MNTTAB, NULL, NULL) < 0) {
char errbuf[1024];
- DEBUG("calling listmntent() failed: %s",
+ DEBUG("utils_mount: calling listmntent() failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
}
if(loop == NULL) { /* no loop= mount */
device = get_device_name(mnt->mnt_fsname);
if(device == NULL) {
- DEBUG("can't get devicename for fs (%s) %s (%s)"
+ DEBUG("utils_mount: can't get devicename for fs (%s) %s (%s)"
": ignored", mnt->mnt_type,
mnt->mnt_dir, mnt->mnt_fsname);
continue;
if ((bufsize = CMD_STATFS (NULL, 0, FLAGS_STATFS)) < 1)
{
char errbuf[1024];
- DEBUG ("getv?fsstat failed: %s",
+ DEBUG ("utils_mount: getv?fsstat failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
return (NULL);
}
if ((num = CMD_STATFS (buf, bufsize * sizeof (STRUCT_STATFS), FLAGS_STATFS)) < 1)
{
char errbuf[1024];
- DEBUG ("getv?fsstat failed: %s",
+ DEBUG ("utils_mount: getv?fsstat failed: %s",
sstrerror (errno, errbuf, sizeof (errbuf)));
free (buf);
return (NULL);
cu_mount_t *last = NULL;
cu_mount_t *new = NULL;
- DEBUG ("(void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
+ DEBUG ("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
if ((fp = fopen (COLLECTD_MNTTAB, "r")) == NULL)
{
cu_mount_t *last = NULL;
cu_mount_t *new = NULL;
- DEBUG ("(void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
+ DEBUG ("utils_mount: (void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
if ((fp = setmntent (COLLECTD_MNTTAB, "r")) == NULL)
{
new->device = get_device_name (new->options);
new->next = NULL;
- DEBUG ("new = {dir = %s, spec_device = %s, type = %s, options = %s, device = %s}",
+ DEBUG ("utils_mount: new = {dir = %s, spec_device = %s, type = %s, options = %s, device = %s}",
new->dir, new->spec_device, new->type, new->options, new->device);
/* Append to list */
endmntent (fp);
- DEBUG ("return (0x%p)", (void *) first);
+ DEBUG ("utils_mount: return (0x%p)", (void *) first);
return (first);
}
cu_mount_t *this;
cu_mount_t *next;
- DEBUG ("(list = 0x%p)", (void *) list);
-
for (this = list; this != NULL; this = next)
{
next = this->next;
xmms_remote_get_info (xmms_session, &rate, &freq, &nch);
if ((freq == 0) || (nch == 0))
- return (0);
+ return (-1);
cxmms_submit ("bitrate", rate);
cxmms_submit ("frequency", freq);