# python stuff
*.pyc
+
+# tag stuff
+src/tags
+
+# backup stuff
+*~
collectd without the need to start a heavy interpreter every interval.
See collectd-python(5) for details.
+ - redis
+ The redis plugin gathers information from a redis server, including:
+ uptime, used memory, total connections etc.
+
- routeros
Query interface and wireless registration statistics from RouterOS.
* libclntsh (optional)
Used by the `oracle' plugin.
+ * libcredis (optional)
+ Used by the redis plugin. Please note that you require a 0.2.2 version
+ or higher. <http://code.google.com/p/credis/>
+
* libcurl (optional)
If you want to use the `apache', `ascent', `curl', `nginx', or `write_http'
plugin.
fi
AM_CONDITIONAL(BUILD_WITH_LIBKVM_OPENFILES, test "x$with_kvm_openfiles" = "xyes")
+# --with-libcredis {{{
+AC_ARG_WITH(libcredis, [AS_HELP_STRING([--with-libcredis@<:@=PREFIX@:>@], [Path to libcredis.])],
+[
+ if test "x$withval" = "xyes"
+ then
+ with_libcredis="yes"
+ else if test "x$withval" = "xno"
+ then
+ with_libcredis="no"
+ else
+ with_libcredis="yes"
+ LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS -I$withval/include"
+ LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS -L$withval/lib"
+ fi; fi
+],
+[with_libcredis="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBCREDIS_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBCREDIS_LDFLAGS"
+
+if test "x$with_libcredis" = "xyes"
+then
+ if test "x$LIBCREDIS_CPPFLAGS" != "x"
+ then
+ AC_MSG_NOTICE([libcredis CPPFLAGS: $LIBCREDIS_CPPFLAGS])
+ fi
+ AC_CHECK_HEADERS(credis.h,
+ [with_libcredis="yes"],
+ [with_libcredis="no ('credis.h' not found)"])
+fi
+if test "x$with_libcredis" = "xyes"
+then
+ if test "x$LIBCREDIS_LDFLAGS" != "x"
+ then
+ AC_MSG_NOTICE([libcredis LDFLAGS: $LIBCREDIS_LDFLAGS])
+ fi
+ AC_CHECK_LIB(credis, credis_info,
+ [with_libcredis="yes"],
+ [with_libcredis="no (symbol 'credis_info' not found)"])
+
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libcredis" = "xyes"
+then
+ BUILD_WITH_LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS"
+ BUILD_WITH_LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS"
+ AC_SUBST(BUILD_WITH_LIBCREDIS_CPPFLAGS)
+ AC_SUBST(BUILD_WITH_LIBCREDIS_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBCREDIS, test "x$with_libcredis" = "xyes")
+# }}}
+
# --with-libcurl {{{
with_curl_config="curl-config"
with_curl_cflags=""
AC_PLUGIN([processes], [$plugin_processes], [Process statistics])
AC_PLUGIN([protocols], [$plugin_protocols], [Protocol (IP, TCP, ...) statistics])
AC_PLUGIN([python], [$with_python], [Embed a Python interpreter])
+AC_PLUGIN([redis], [$with_libcredis], [Redis plugin])
AC_PLUGIN([routeros], [$with_librouteros], [RouterOS plugin])
AC_PLUGIN([rrdcached], [$librrd_rrdc_update], [RRDTool output plugin])
AC_PLUGIN([rrdtool], [$with_librrd], [RRDTool output plugin])
AC_PLUGIN([vserver], [$plugin_vserver], [Linux VServer statistics])
AC_PLUGIN([wireless], [$plugin_wireless], [Wireless statistics])
AC_PLUGIN([write_http], [$with_libcurl], [HTTP output plugin])
+AC_PLUGIN([write_redis], [$with_libcredis], [Redis output plugin])
AC_PLUGIN([xmms], [$with_libxmms], [XMMS statistics])
AC_PLUGIN([zfs_arc], [$plugin_zfs_arc], [ZFS ARC statistics])
Libraries:
libcurl . . . . . . . $with_libcurl
libdbi . . . . . . . $with_libdbi
+ libcredis . . . . . . $with_libcredis
libesmtp . . . . . . $with_libesmtp
libganglia . . . . . $with_libganglia
libgcrypt . . . . . . $with_libgcrypt
processes . . . . . . $enable_processes
protocols . . . . . . $enable_protocols
python . . . . . . . $enable_python
+ redis . . . . . . . . $enable_redis
routeros . . . . . . $enable_routeros
rrdcached . . . . . . $enable_rrdcached
rrdtool . . . . . . . $enable_rrdtool
vserver . . . . . . . $enable_vserver
wireless . . . . . . $enable_wireless
write_http . . . . . $enable_write_http
+ write_redis . . . . . $enable_write_redis
xmms . . . . . . . . $enable_xmms
zfs_arc . . . . . . . $enable_zfs_arc
collectd_DEPENDENCIES += protocols.la
endif
+if BUILD_PLUGIN_REDIS
+pkglib_LTLIBRARIES += redis.la
+redis_la_SOURCES = redis.c
+redis_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBCREDIS_LDFLAGS)
+redis_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCREDIS_CPPFLAGS)
+redis_la_LIBADD = -lcredis
+collectd_LDADD += "-dlopen" redis.la
+collectd_DEPENDENCIES += redis.la
+endif
+
if BUILD_PLUGIN_ROUTEROS
pkglib_LTLIBRARIES += routeros.la
routeros_la_SOURCES = routeros.c
collectd_DEPENDENCIES += write_http.la
endif
+if BUILD_PLUGIN_WRITE_REDIS
+pkglib_LTLIBRARIES += write_redis.la
+write_redis_la_SOURCES = write_redis.c
+write_redis_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBCREDIS_LDFLAGS)
+write_redis_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBCREDIS_CPPFLAGS)
+write_redis_la_LIBADD = -lcredis
+collectd_LDADD += "-dlopen" write_redis.la
+collectd_DEPENDENCIES += write_redis.la
+endif
+
if BUILD_PLUGIN_XMMS
pkglib_LTLIBRARIES += xmms.la
xmms_la_SOURCES = xmms.c
# include "config.h"
#endif
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
+
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200112L
+#endif
+
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
+
#if !defined(__GNUC__) || !__GNUC__
# define __attribute__(x) /**/
#endif
#include <string.h>
#include <strings.h>
#include <assert.h>
-
-#include <sys/socket.h>
-#include <sys/un.h>
+#include <math.h>
#include "libcollectdclient/collectd/client.h"
-/*
- * This is copied directly from collectd.h. Make changes there!
- */
-#if NAN_STATIC_DEFAULT
-# include <math.h>
-/* #endif NAN_STATIC_DEFAULT*/
-#elif NAN_STATIC_ISOC
-# ifndef __USE_ISOC99
-# 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 */
-/* #endif NAN_STATIC_ISOC */
-#elif NAN_ZERO_ZERO
-# include <math.h>
-# ifdef NAN
-# undef NAN
-# endif
-# define NAN (0.0 / 0.0)
-# ifndef isnan
-# define isnan(f) ((f) != (f))
-# endif /* !defined(isnan) */
-#endif /* NAN_ZERO_ZERO */
-
#define RET_OKAY 0
#define RET_WARNING 1
#define RET_CRITICAL 2
=head1 SYNOPSIS
- LoadPlugin perl
+ <LoadPlugin perl>
+ Globals true
+ </LoadPlugin>
# ...
<Plugin perl>
IncludeDir "/path/to/perl/plugins"
Perl-script every time you want to read a value with the C<exec plugin> (see
L<collectd-exec(5)>) and provides a lot more functionality, too.
+When loading the C<perl plugin>, the B<Globals> option should be enabled.
+Else, the perl plugin will fail to load any Perl modules implemented in C,
+which includes, amongst many others, the B<threads> module used by the plugin
+itself. See the documentation of the B<Globals> option in L<collectd.conf(5)>
+for details.
+
=head1 CONFIGURATION
=over 4
#@BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes
#@BUILD_PLUGIN_PROTOCOLS_TRUE@LoadPlugin protocols
#@BUILD_PLUGIN_PYTHON_TRUE@LoadPlugin python
+#@BUILD_PLUGIN_REDIS_TRUE@LoadPlugin redis
#@BUILD_PLUGIN_ROUTEROS_TRUE@LoadPlugin routeros
#@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached
@LOAD_PLUGIN_RRDTOOL@LoadPlugin rrdtool
#@BUILD_PLUGIN_VSERVER_TRUE@LoadPlugin vserver
#@BUILD_PLUGIN_WIRELESS_TRUE@LoadPlugin wireless
#@BUILD_PLUGIN_WRITE_HTTP_TRUE@LoadPlugin write_http
+#@BUILD_PLUGIN_WRITE_REDIS_TRUE@LoadPlugin write_redis
#@BUILD_PLUGIN_XMMS_TRUE@LoadPlugin xmms
#@BUILD_PLUGIN_ZFS_ARC_TRUE@LoadPlugin zfs_arc
# </Module>
#</Plugin>
+#<Plugin redis>
+# <Node example>
+# Host "redis.example.com"
+# Port "6379"
+# Timeout 2000
+# </Node>
+#</Plugin>
+
#<Plugin routeros>
# <Router>
# Host "router.example.com"
# </URL>
#</Plugin>
+#<Plugin write_redis>
+# <Node "example">
+# Host "localhost"
+# Port "6379"
+# Timeout 1000
+# </Node>
+#</Plugin>
+
##############################################################################
# Filter configuration #
#----------------------------------------------------------------------------#
Loads the plugin I<Plugin>. There must be at least one such line or B<collectd>
will be mostly useless.
+Starting with collectd 4.9, this may also be a block in which further options
+affecting the behavior of B<LoadPlugin> may be specified. The following
+options are allowed inside a B<LoadPlugin> block:
+
+ <LoadPlugin perl>
+ Globals true
+ </LoadPlugin>
+
+=over 4
+
+=item B<Globals> B<true|false>
+
+If enabled, collectd will export all global symbols of the plugin (and of all
+libraries loaded as dependencies of the plugin) and, thus, makes those symbols
+available for resolving unresolved symbols in subsequently loaded plugins if
+that is supported by your system. By default, this is disabled.
+
+This is useful (or possibly even required), e.E<nbsp>g., when loading a plugin
+that embeds some scripting language into the daemon (e.E<nbsp>g. the C<perl>
+or C<python> plugins). Scripting languages usually provide means to load
+extensions written in C. Those extensions require symbols provided by the
+interpreter, which is loaded as a dependency of the respective collectd
+plugin. See the documentation of those plugins (e.E<nbsp>g.,
+L<collectd-perl(5)> or L<collectd-python(5)>) for details.
+
+=back
+
=item B<Include> I<Path>
If I<Path> points to a file, includes that file. If I<Path> points to a
=back
+=head2 Plugin C<redis>
+
+The I<Redis plugin> connects to one or more Redis servers and gathers
+information about each server's state. For each server there is a I<Node> block
+which configures the connection parameters for this node.
+
+ <Plugin redis>
+ <Node "example">
+ Host "localhost"
+ Port "6379"
+ Timeout 2000
+ </Node>
+ </Plugin>
+
+The information shown in the synopsis above is the I<default configuration>
+which is used by the plugin if no configuration is present.
+
+=over 4
+
+=item B<Node> I<Nodename>
+
+The B<Node> block identifies a new Redis node, that is a new Redis instance
+running in an specified host and port. The name for node is a canonical
+identifier which is used as I<plugin instance>. It is limited to
+64E<nbsp>characters in length.
+
+=item B<Host> I<Hostname>
+
+The B<Host> option is the hostname or IP-address where the Redis instance is
+running on.
+
+=item B<Port> I<Port>
+
+The B<Port> option is the TCP port on which the Redis instance accepts
+connections. Either a service name of a port number may be given. Please note
+that numerical port numbers must be given as a string, too.
+
+=item B<Timeout> I<Timeout in miliseconds>
+
+The B<Timeout> option set the socket timeout for node response. Since the Redis
+read function is blocking, you should keep this value as low as possible. Keep
+in mind that the sum of all B<Timeout> values for all B<Nodes> should be lower
+than B<Interval> defined globally.
+
+=back
+
=head2 Plugin C<rrdcached>
The C<rrdcached> plugin uses the RRDtool accelerator daemon, L<rrdcached(1)>,
=item B<CounterSet>
-The matched number is a counter. Simply sets the internal counter to this
-value.
+=item B<DeriveSet>
+
+=item B<AbsoluteSet>
+
+The matched number is a counter. Simply I<sets> the internal counter to this
+value. Variants exist for C<COUNTER>, C<DERIVE>, and C<ABSOLUTE> data sources.
=item B<CounterAdd>
-Add the matched value to the internal counter.
+=item B<DeriveAdd>
+
+Add the matched value to the internal counter. In case of B<DeriveAdd>, the
+matched number may be negative, which will effectively subtract from the
+internal counter.
=item B<CounterInc>
-Increase the internal counter by one. This B<DSType> is the only one that does
-not use the matched subexpression, but simply counts the number of matched
+=item B<DeriveInc>
+
+Increase the internal counter by one. These B<DSType> are the only ones that do
+not use the matched subexpression, but simply count the number of matched
lines. Thus, you may use a regular expression without submatch in this case.
=back
As you'd expect the B<Gauge*> types interpret the submatch as a floating point
-number, using L<strtod(3)>. The B<CounterSet> and B<CounterAdd> interpret the
-submatch as an integer using L<strtoll(3)>. B<CounterInc> does not use the
-submatch at all and it may be omitted in this case.
+number, using L<strtod(3)>. The B<Counter*> and B<AbsoluteSet> types interpret
+the submatch as an unsigned integer using L<strtoull(3)>. The B<Derive*> types
+interpret the submatch as a signed integer using L<strtoll(3)>. B<CounterInc>
+and B<DeriveInc> do not use the submatch at all and it may be omitted in this
+case.
=item B<Type> I<Type>
# include "config.h"
#endif
-#include "libcollectdclient/collectd/client.h"
+#ifndef _ISOC99_SOURCE
+# define _ISOC99_SOURCE
+#endif
-#include <assert.h>
+#ifndef _POSIX_C_SOURCE
+# define _POSIX_C_SOURCE 200112L
+#endif
-#include <errno.h>
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
-#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <assert.h>
+#include <errno.h>
#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include "libcollectdclient/collectd/client.h"
-#include <unistd.h>
#define DEFAULT_SOCK LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock"
}
}
else if (strcasecmp (key, "plugin") == 0) {
- status = array_grow ((void **)&plugins, &plugins_num,
+ status = array_grow ((void *)&plugins, &plugins_num,
sizeof (*plugins));
if (status != 0)
BAIL_OUT (status);
plugins[plugins_num - 1] = value;
}
else if (strcasecmp (key, "identifier") == 0) {
- status = array_grow ((void **)&identifiers, &identifiers_num,
+ status = array_grow ((void *)&identifiers, &identifiers_num,
sizeof (*identifiers));
if (status != 0)
BAIL_OUT (status);
}
if (plugins_num == 0) {
- status = array_grow ((void **)&plugins, &plugins_num, sizeof (*plugins));
+ status = array_grow ((void *)&plugins, &plugins_num, sizeof (*plugins));
if (status != 0)
BAIL_OUT (status);
vl.time = strtol (argv[i], &endptr, 0);
- if (endptr == value) {
+ if (endptr == argv[i]) {
fprintf (stderr, "ERROR: Failed to parse time as number: %s.\n",
argv[i]);
return (-1);
if (status != 0)
{
char errbuf[1024];
- ERROR ("configfile: stat (%s) failed: %s",
+ WARNING ("configfile: stat (%s) failed: %s",
path_ptr,
sstrerror (errno, errbuf, sizeof (errbuf)));
- oconfig_free (root);
- return (NULL);
+ continue;
}
if (S_ISREG (statbuf.st_mode))
temp = cf_read_dir (path_ptr, depth);
else
{
- ERROR ("configfile: %s is neither a file nor a "
+ WARNING ("configfile: %s is neither a file nor a "
"directory.", path);
continue;
}
wordfree (&we);
+ if (root->children == NULL)
+ {
+ oconfig_free (root);
+ return (NULL);
+ }
+
return (root);
} /* oconfig_item_t *cf_read_generic */
/* #endif HAVE_WORDEXP_H */
/**
* collectd - src/curl_json.c
* Copyright (C) 2009 Doug MacEachern
- * Copyright (C) 2006-2009 Florian octo Forster
+ * Copyright (C) 2006-2010 Florian octo Forster
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
}
/* yajl callbacks */
-static int cj_cb_integer (void *ctx, long val)
+#define CJ_CB_ABORT 0
+#define CJ_CB_CONTINUE 1
+
+/* "number" may not be null terminated, so copy it into a buffer before
+ * parsing. */
+static int cj_cb_number (void *ctx,
+ const char *number, unsigned int number_len)
{
+ char buffer[number_len + 1];
+
cj_t *db = (cj_t *)ctx;
cj_key_t *key = db->state[db->depth].key;
+ char *endptr;
+ value_t vt;
+ int type;
- if (key != NULL)
- {
- value_t vt;
- int type;
+ if (key == NULL)
+ return (CJ_CB_CONTINUE);
- type = cj_get_type (key);
- if (type == DS_TYPE_COUNTER)
- vt.counter = (counter_t) val;
- else if (type == DS_TYPE_GAUGE)
- vt.gauge = (gauge_t) val;
- else if (type == DS_TYPE_DERIVE)
- vt.derive = (derive_t) val;
- else if (type == DS_TYPE_ABSOLUTE)
- vt.absolute = (absolute_t) val;
- else
- return 0;
+ memcpy (buffer, number, number_len);
+ buffer[sizeof (buffer) - 1] = 0;
- cj_submit (db, key, &vt);
- }
- return 1;
-}
+ type = cj_get_type (key);
-static int cj_cb_double (void *ctx, double val)
-{
- cj_t *db = (cj_t *)ctx;
- cj_key_t *key = db->state[db->depth].key;
+ endptr = NULL;
+ errno = 0;
- if (key != NULL)
+ if (type == DS_TYPE_COUNTER)
+ vt.counter = (counter_t) strtoull (buffer, &endptr, /* base = */ 0);
+ else if (type == DS_TYPE_GAUGE)
+ vt.gauge = (gauge_t) strtod (buffer, &endptr);
+ else if (type == DS_TYPE_DERIVE)
+ vt.derive = (derive_t) strtoll (buffer, &endptr, /* base = */ 0);
+ else if (type == DS_TYPE_ABSOLUTE)
+ vt.absolute = (absolute_t) strtoull (buffer, &endptr, /* base = */ 0);
+ else
{
- value_t vt;
- int type;
-
- type = cj_get_type (key);
- if (type == DS_TYPE_COUNTER)
- vt.counter = (counter_t) val;
- else if (type == DS_TYPE_GAUGE)
- vt.gauge = (gauge_t) val;
- else if (type == DS_TYPE_DERIVE)
- vt.derive = (derive_t) val;
- else if (type == DS_TYPE_ABSOLUTE)
- vt.absolute = (absolute_t) val;
- else
- return 0;
+ ERROR ("curl_json plugin: Unknown data source type: \"%s\"", key->type);
+ return (CJ_CB_ABORT);
+ }
- cj_submit (db, key, &vt);
+ if ((endptr == &buffer[0]) || (errno != 0))
+ {
+ NOTICE ("curl_json plugin: Overflow while parsing number. "
+ "Ignoring this value.");
+ return (CJ_CB_CONTINUE);
}
- return 1;
-}
+
+ cj_submit (db, key, &vt);
+ return (CJ_CB_CONTINUE);
+} /* int cj_cb_number */
static int cj_cb_map_key (void *ctx, const unsigned char *val,
unsigned int len)
db->state[db->depth].key = NULL;
}
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_string (void *ctx, const unsigned char *val,
char *ptr;
if (db->depth != 1) /* e.g. _all_dbs */
- return 1;
+ return (CJ_CB_CONTINUE);
cj_cb_map_key (ctx, val, len); /* same logic */
cj_curl_perform (db, curl);
curl_easy_cleanup (curl);
}
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_start (void *ctx)
if (++db->depth >= YAJL_MAX_DEPTH)
{
ERROR ("curl_json plugin: %s depth exceeds max, aborting.", db->url);
- return 0;
+ return (CJ_CB_ABORT);
}
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_end (void *ctx)
cj_t *db = (cj_t *)ctx;
db->state[db->depth].tree = NULL;
--db->depth;
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_start_map (void *ctx)
static yajl_callbacks ycallbacks = {
NULL, /* null */
NULL, /* boolean */
- cj_cb_integer,
- cj_cb_double,
- NULL, /* number */
+ NULL, /* integer */
+ NULL, /* double */
+ cj_cb_number,
cj_cb_string,
cj_cb_start_map,
cj_cb_map_key,
curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
- if (rc != 200)
+ /* The response code is zero if a non-HTTP transport was used. */
+ if ((rc != 0) && (rc != 200))
{
ERROR ("curl_json plugin: curl_easy_perform failed with response code %ld (%s)",
rc, url);
if (c_avl_get (data_sets, vl->type, (void *) &ds) != 0)
{
- INFO ("plugin_dispatch_values: Dataset not found: %s", vl->type);
+ char ident[6 * DATA_MAX_NAME_LEN];
+
+ FORMAT_VL (ident, sizeof (ident), vl);
+ INFO ("plugin_dispatch_values: Dataset not found: %s "
+ "(from \"%s\"), check your types.db!",
+ vl->type, ident);
return (-1);
}
--- /dev/null
+/**
+ * collectd - src/redis.c, based on src/memcached.c
+ * Copyright (C) 2010 Andrés J. DÃaz <ajdiaz@connectical.com>
+ *
+ * This program 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Andrés J. DÃaz <ajdiaz@connectical.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <pthread.h>
+#include <credis.h>
+
+#define REDIS_DEF_HOST "localhost"
+#define REDIS_DEF_PORT 6379
+#define REDIS_DEF_TIMEOUT 2000
+#define MAX_REDIS_NODE_NAME 64
+
+/* Redis plugin configuration example:
+ *
+ * <Plugin redis>
+ * <Node "mynode">
+ * Host "localhost"
+ * Port "6379"
+ * Timeout 2000
+ * </Node>
+ * </Plugin>
+ */
+
+struct redis_node_s;
+typedef struct redis_node_s redis_node_t;
+struct redis_node_s
+{
+ char name[MAX_REDIS_NODE_NAME];
+ char host[HOST_NAME_MAX];
+ int port;
+ int timeout;
+
+ redis_node_t *next;
+};
+
+static redis_node_t *nodes_head = NULL;
+
+static int redis_node_add (const redis_node_t *rn) /* {{{ */
+{
+ redis_node_t *rn_copy;
+ redis_node_t *rn_ptr;
+
+ /* Check for duplicates first */
+ for (rn_ptr = nodes_head; rn_ptr != NULL; rn_ptr = rn_ptr->next)
+ if (strcmp (rn->name, rn_ptr->name) == 0)
+ break;
+
+ if (rn_ptr != NULL)
+ {
+ ERROR ("redis plugin: A node with the name `%s' already exists.",
+ rn->name);
+ return (-1);
+ }
+
+ rn_copy = malloc (sizeof (*rn_copy));
+ if (rn_copy == NULL)
+ {
+ ERROR ("redis plugin: malloc failed adding redis_node to the tree.");
+ return (-1);
+ }
+
+ memcpy (rn_copy, rn, sizeof (*rn_copy));
+ rn_copy->next = NULL;
+
+ DEBUG ("redis plugin: Adding node \"%s\".", rn->name);
+
+ if (nodes_head == NULL)
+ nodes_head = rn_copy;
+ else
+ {
+ rn_ptr = nodes_head;
+ while (rn_ptr->next != NULL)
+ rn_ptr = rn_ptr->next;
+ rn_ptr->next = rn_copy;
+ }
+
+ return (0);
+} /* }}} */
+
+static int redis_config_node (oconfig_item_t *ci) /* {{{ */
+{
+ redis_node_t rn;
+ int i;
+ int status;
+
+ memset (&rn, 0, sizeof (rn));
+ sstrncpy (rn.host, REDIS_DEF_HOST, sizeof (rn.host));
+ rn.port = REDIS_DEF_PORT;
+ rn.timeout = REDIS_DEF_TIMEOUT;
+
+ status = cf_util_get_string_buffer (ci, rn.name, sizeof (rn.name));
+ if (status != 0)
+ return (status);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("Host", option->key) == 0)
+ status = cf_util_get_string_buffer (option, rn.host, sizeof (rn.host));
+ else if (strcasecmp ("Port", option->key) == 0)
+ {
+ status = cf_util_get_port_number (option);
+ if (status > 0)
+ {
+ rn.port = status;
+ status = 0;
+ }
+ }
+ else if (strcasecmp ("Timeout", option->key) == 0)
+ status = cf_util_get_int (option, &rn.timeout);
+ else
+ WARNING ("redis plugin: Option `%s' not allowed inside a `Node' "
+ "block. I'll ignore this option.", option->key);
+
+ if (status != 0)
+ break;
+ }
+
+ if (status != 0)
+ return (status);
+
+ return (redis_node_add (&rn));
+} /* }}} int redis_config_node */
+
+static int redis_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("Node", option->key) == 0)
+ redis_config_node (option);
+ else
+ WARNING ("redis plugin: Option `%s' not allowed in redis"
+ " configuration. It will be ignored.", option->key);
+ }
+
+ if (nodes_head == NULL)
+ {
+ ERROR ("redis plugin: No valid node configuration could be found.");
+ return (ENOENT);
+ }
+
+ return (0);
+} /* }}} */
+
+ __attribute__ ((nonnull(2)))
+static void redis_submit_g (char *plugin_instance,
+ const char *type, const char *type_instance,
+ gauge_t value) /* {{{ */
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].gauge = value;
+
+ vl.values = values;
+ vl.values_len = 1;
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "redis", sizeof (vl.plugin));
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance,
+ sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance,
+ sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+} /* }}} */
+
+ __attribute__ ((nonnull(2)))
+static void redis_submit_c (char *plugin_instance,
+ const char *type, const char *type_instance,
+ counter_t value) /* {{{ */
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].counter = value;
+
+ vl.values = values;
+ vl.values_len = 1;
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "redis", sizeof (vl.plugin));
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance,
+ sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance,
+ sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+} /* }}} */
+
+static int redis_init (void) /* {{{ */
+{
+ redis_node_t rn = { "default", REDIS_DEF_HOST, REDIS_DEF_PORT,
+ REDIS_DEF_TIMEOUT, /* next = */ NULL };
+
+ if (nodes_head == NULL)
+ redis_node_add (&rn);
+
+ return (0);
+} /* }}} int redis_init */
+
+static int redis_read (void) /* {{{ */
+{
+ redis_node_t *rn;
+
+ for (rn = nodes_head; rn != NULL; rn = rn->next)
+ {
+ REDIS rh;
+ REDIS_INFO info;
+
+ int status;
+
+ DEBUG ("redis plugin: querying info from node `%s' (%s:%d).", rn->name, rn->host, rn->port);
+
+ rh = credis_connect (rn->host, rn->port, rn->timeout);
+ if (rh == NULL)
+ {
+ ERROR ("redis plugin: unable to connect to node `%s' (%s:%d).", rn->name, rn->host, rn->port);
+ continue;
+ }
+
+ memset (&info, 0, sizeof (info));
+ status = credis_info (rh, &info);
+ if (status != 0)
+ {
+ WARNING ("redis plugin: unable to get info from node `%s'.", rn->name);
+ credis_close (rh);
+ continue;
+ }
+
+ /* typedef struct _cr_info {
+ * char redis_version[CREDIS_VERSION_STRING_SIZE];
+ * int bgsave_in_progress;
+ * int connected_clients;
+ * int connected_slaves;
+ * unsigned int used_memory;
+ * long long changes_since_last_save;
+ * int last_save_time;
+ * long long total_connections_received;
+ * long long total_commands_processed;
+ * int uptime_in_seconds;
+ * int uptime_in_days;
+ * int role;
+ * } REDIS_INFO; */
+
+ DEBUG ("redis plugin: received info from node `%s': connected_clients = %d; "
+ "connected_slaves = %d; used_memory = %lu; changes_since_last_save = %lld; "
+ "bgsave_in_progress = %d; total_connections_received = %lld; "
+ "total_commands_processed = %lld; uptime_in_seconds = %ld", rn->name,
+ info.connected_clients, info.connected_slaves, info.used_memory,
+ info.changes_since_last_save, info.bgsave_in_progress,
+ info.total_connections_received, info.total_commands_processed,
+ info.uptime_in_seconds);
+
+ redis_submit_g (rn->name, "current_connections", "clients", info.connected_clients);
+ redis_submit_g (rn->name, "current_connections", "slaves", info.connected_slaves);
+ redis_submit_g (rn->name, "memory", "used", info.used_memory);
+ redis_submit_g (rn->name, "volatile_changes", NULL, info.changes_since_last_save);
+ redis_submit_c (rn->name, "total_connections", NULL, info.total_connections_received);
+ redis_submit_c (rn->name, "total_operations", NULL, info.total_commands_processed);
+
+ credis_close (rh);
+ }
+
+ return 0;
+}
+/* }}} */
+
+void module_register (void) /* {{{ */
+{
+ plugin_register_complex_config ("redis", redis_config);
+ plugin_register_init ("redis", redis_init);
+ plugin_register_read ("redis", redis_read);
+ /* TODO: plugin_register_write: one redis list per value id with
+ * X elements */
+}
+/* }}} */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
cpufreq value:GAUGE:0:U
cpu value:COUNTER:0:4294967295
current value:GAUGE:U:U
+current_connections value:GAUGE:0:U
delay seconds:GAUGE:-1000000:1000000
derive value:DERIVE:0:U
df used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623
timeleft timeleft:GAUGE:0:3600
time_offset seconds:GAUGE:-1000000:1000000
total_bytes value:DERIVE:0:U
+total_connections value:DERIVE:0:U
total_operations value:DERIVE:0:U
total_requests value:DERIVE:0:U
total_sessions value:DERIVE:0:U
vmpage_faults minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807
vmpage_io in:COUNTER:0:4294967295, out:COUNTER:0:4294967295
vmpage_number value:GAUGE:0:4294967295
+volatile_changes value:GAUGE:0:U
voltage_threshold value:GAUGE:U:U, threshold:GAUGE:U:U
voltage value:GAUGE:U:U
vs_memory value:GAUGE:0:9223372036854775807
return;
if (dir == DIR_UP)
- reheap (h, root / 2, dir);
+ reheap (h, (root - 1) / 2, dir);
else if (dir == DIR_DOWN)
reheap (h, min, dir);
} /* void reheap */
int c_heap_insert (c_heap_t *h, void *ptr)
{
+ size_t index;
+
if ((h == NULL) || (ptr == NULL))
return (-EINVAL);
}
/* Insert the new node as a leaf. */
- h->list[h->list_len] = ptr;
+ index = h->list_len;
+ h->list[index] = ptr;
h->list_len++;
/* Reorganize the heap from bottom up. */
- reheap (h, /* parent of this node = */ (h->list_len - 1) / 2, DIR_UP);
+ reheap (h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
pthread_mutex_unlock (&h->lock);
return (0);
if (matches_num < 2)
return (-1);
- value = strtod (matches[1], &endptr);
+ value = (gauge_t) strtod (matches[1], &endptr);
if (matches[1] == endptr)
return (-1);
if (matches_num < 2)
return (-1);
- value = strtoll (matches[1], &endptr, 0);
+ value = (counter_t) strtoull (matches[1], &endptr, 0);
if (matches[1] == endptr)
return (-1);
if (matches_num < 2)
return (-1);
- value = strtoll (matches[1], &endptr, 0);
+ value = (derive_t) strtoll (matches[1], &endptr, 0);
if (matches[1] == endptr)
return (-1);
if (matches_num < 2)
return (-1);
- value = strtoll (matches[1], &endptr, 0);
+ value = (absolute_t) strtoull (matches[1], &endptr, 0);
if (matches[1] == endptr)
return (-1);
/*
* Defines
*/
-#define UTILS_MATCH_DS_TYPE_GAUGE 0x10
-#define UTILS_MATCH_DS_TYPE_COUNTER 0x20
-#define UTILS_MATCH_DS_TYPE_DERIVE 0x30
-#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x40
+#define UTILS_MATCH_DS_TYPE_GAUGE 0x10
+#define UTILS_MATCH_DS_TYPE_COUNTER 0x20
+#define UTILS_MATCH_DS_TYPE_DERIVE 0x40
+#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x80
#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
#define UTILS_MATCH_CF_GAUGE_MIN 0x02
--- /dev/null
+/**
+ * collectd - src/write_redis.c
+ * Copyright (C) 2010 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <ff at octo.it>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+
+#include <pthread.h>
+#include <credis.h>
+
+struct wr_node_s
+{
+ char name[DATA_MAX_NAME_LEN];
+
+ char *host;
+ int port;
+ int timeout;
+
+ REDIS conn;
+ pthread_mutex_t lock;
+};
+typedef struct wr_node_s wr_node_t;
+
+/*
+ * Functions
+ */
+static int wr_write (const data_set_t *ds, /* {{{ */
+ const value_list_t *vl,
+ user_data_t *ud)
+{
+ wr_node_t *node = ud->data;
+ char ident[512];
+ char key[512];
+ char value[512];
+ size_t value_size;
+ char *value_ptr;
+ int status;
+ int i;
+
+ status = FORMAT_VL (ident, sizeof (ident), vl);
+ if (status != 0)
+ return (status);
+ ssnprintf (key, sizeof (key), "collectd/%s", ident);
+
+ memset (value, 0, sizeof (value));
+ value_size = sizeof (value);
+ value_ptr = &value[0];
+
+#define APPEND(...) do { \
+ status = snprintf (value_ptr, value_size, __VA_ARGS__); \
+ if (((size_t) status) > value_size) \
+ { \
+ value_ptr += value_size; \
+ value_size = 0; \
+ } \
+ else \
+ { \
+ value_ptr += status; \
+ value_size -= status; \
+ } \
+} while (0)
+
+ APPEND ("%lu", (unsigned long) vl->time);
+ for (i = 0; i < ds->ds_num; i++)
+ {
+ if (ds->ds[i].type == DS_TYPE_COUNTER)
+ APPEND ("%llu", vl->values[i].counter);
+ else if (ds->ds[i].type == DS_TYPE_GAUGE)
+ APPEND ("%g", vl->values[i].gauge);
+ else if (ds->ds[i].type == DS_TYPE_DERIVE)
+ APPEND ("%"PRIi64, vl->values[i].derive);
+ else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+ APPEND ("%"PRIu64, vl->values[i].absolute);
+ else
+ assert (23 == 42);
+ }
+
+#undef APPEND
+
+ pthread_mutex_lock (&node->lock);
+
+ if (node->conn == NULL)
+ {
+ node->conn = credis_connect (node->host, node->port, node->timeout);
+ if (node->conn == NULL)
+ {
+ ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed.",
+ (node->host != NULL) ? node->host : "localhost",
+ (node->port != 0) ? node->port : 6379);
+ pthread_mutex_unlock (&node->lock);
+ return (-1);
+ }
+ }
+
+ /* "credis_zadd" doesn't handle a NULL pointer gracefully, so I'd rather
+ * have a meaningful assertion message than a normal segmentation fault. */
+ assert (node->conn != NULL);
+ status = credis_zadd (node->conn, key, (double) vl->time, value);
+
+ credis_sadd (node->conn, "collectd/values", ident);
+
+ pthread_mutex_unlock (&node->lock);
+
+ return (0);
+} /* }}} int wr_write */
+
+static void wr_config_free (void *ptr) /* {{{ */
+{
+ wr_node_t *node = ptr;
+
+ if (node == NULL)
+ return;
+
+ if (node->conn != NULL)
+ {
+ credis_close (node->conn);
+ node->conn = NULL;
+ }
+
+ sfree (node->host);
+ sfree (node);
+} /* }}} void wr_config_free */
+
+static int wr_config_node (oconfig_item_t *ci) /* {{{ */
+{
+ wr_node_t *node;
+ int status;
+ int i;
+
+ node = malloc (sizeof (*node));
+ if (node == NULL)
+ return (ENOMEM);
+ memset (node, 0, sizeof (*node));
+ node->host = NULL;
+ node->port = 0;
+ node->timeout = 1000;
+ node->conn = NULL;
+ pthread_mutex_init (&node->lock, /* attr = */ NULL);
+
+ status = cf_util_get_string_buffer (ci, node->name, sizeof (node->name));
+ if (status != 0)
+ {
+ sfree (node);
+ return (status);
+ }
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Host", child->key) == 0)
+ status = cf_util_get_string (child, &node->host);
+ else if (strcasecmp ("Port", child->key) == 0)
+ {
+ status = cf_util_get_port_number (child);
+ if (status > 0)
+ {
+ node->port = status;
+ status = 0;
+ }
+ }
+ else if (strcasecmp ("Timeout", child->key) == 0)
+ status = cf_util_get_int (child, &node->timeout);
+ else
+ WARNING ("write_redis plugin: Ignoring unknown config option \"%s\".",
+ child->key);
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ if (status == 0)
+ {
+ char cb_name[DATA_MAX_NAME_LEN];
+ user_data_t ud;
+
+ ssnprintf (cb_name, sizeof (cb_name), "write_redis/%s", node->name);
+
+ ud.data = node;
+ ud.free_func = wr_config_free;
+
+ status = plugin_register_write (cb_name, wr_write, &ud);
+ }
+
+ if (status != 0)
+ wr_config_free (node);
+
+ return (status);
+} /* }}} int wr_config_node */
+
+static int wr_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Node", child->key) == 0)
+ wr_config_node (child);
+ else
+ WARNING ("write_redis plugin: Ignoring unknown "
+ "configuration option \"%s\" at top level.", child->key);
+ }
+
+ return (0);
+} /* }}} int wr_config */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("write_redis", wr_config);
+}
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */