From: Florian Forster Date: Thu, 19 Aug 2010 13:03:22 +0000 (+0200) Subject: Merge branch 'master' into ff/netlib X-Git-Tag: collectd-5.2.0~18^2~1 X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=6624c959628fa1188f8cfa4789da1eccccc5f5e4;hp=d866effbc50aef27811aac113ef2619da7fc6b43;p=collectd.git Merge branch 'master' into ff/netlib Conflicts: src/collectdctl.c --- diff --git a/.gitignore b/.gitignore index e8f9af66..cbdd62f8 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,9 @@ bindings/java/org/collectd/java/*.class # python stuff *.pyc + +# tag stuff +src/tags + +# backup stuff +*~ diff --git a/README b/README index 8d4d275d..0c7a4221 100644 --- a/README +++ b/README @@ -235,6 +235,10 @@ Features 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. @@ -507,6 +511,10 @@ Prerequisites * 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. + * libcurl (optional) If you want to use the `apache', `ascent', `curl', `nginx', or `write_http' plugin. diff --git a/configure.in b/configure.in index 3ddfee12..ba11dc3c 100644 --- a/configure.in +++ b/configure.in @@ -1279,6 +1279,64 @@ then 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="" @@ -4472,6 +4530,7 @@ AC_PLUGIN([powerdns], [yes], [PowerDNS statistics]) 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]) @@ -4502,6 +4561,7 @@ AC_PLUGIN([vmem], [$plugin_vmem], [Virtual memory statistics]) 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]) @@ -4676,6 +4736,7 @@ Configuration: Libraries: libcurl . . . . . . . $with_libcurl libdbi . . . . . . . $with_libdbi + libcredis . . . . . . $with_libcredis libesmtp . . . . . . $with_libesmtp libganglia . . . . . $with_libganglia libgcrypt . . . . . . $with_libgcrypt @@ -4791,6 +4852,7 @@ Configuration: processes . . . . . . $enable_processes protocols . . . . . . $enable_protocols python . . . . . . . $enable_python + redis . . . . . . . . $enable_redis routeros . . . . . . $enable_routeros rrdcached . . . . . . $enable_rrdcached rrdtool . . . . . . . $enable_rrdtool @@ -4821,6 +4883,7 @@ Configuration: vserver . . . . . . . $enable_vserver wireless . . . . . . $enable_wireless write_http . . . . . $enable_write_http + write_redis . . . . . $enable_write_redis xmms . . . . . . . . $enable_xmms zfs_arc . . . . . . . $enable_zfs_arc diff --git a/src/Makefile.am b/src/Makefile.am index d5fdc1e1..025476af 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -910,6 +910,16 @@ collectd_LDADD += "-dlopen" protocols.la 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 @@ -1225,6 +1235,16 @@ endif 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 diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c index b805c762..2d59ea77 100644 --- a/src/collectd-nagios.c +++ b/src/collectd-nagios.c @@ -23,6 +23,18 @@ # 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 @@ -34,40 +46,10 @@ #include #include #include - -#include -#include +#include #include "libcollectdclient/collectd/client.h" -/* - * This is copied directly from collectd.h. Make changes there! - */ -#if NAN_STATIC_DEFAULT -# include -/* #endif NAN_STATIC_DEFAULT*/ -#elif NAN_STATIC_ISOC -# ifndef __USE_ISOC99 -# define DISABLE_ISOC99 1 -# define __USE_ISOC99 1 -# endif /* !defined(__USE_ISOC99) */ -# include -# if DISABLE_ISOC99 -# undef DISABLE_ISOC99 -# undef __USE_ISOC99 -# endif /* DISABLE_ISOC99 */ -/* #endif NAN_STATIC_ISOC */ -#elif NAN_ZERO_ZERO -# include -# 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 diff --git a/src/collectd-perl.pod b/src/collectd-perl.pod index 56370533..5c11b652 100644 --- a/src/collectd-perl.pod +++ b/src/collectd-perl.pod @@ -4,7 +4,9 @@ collectd-perl - Documentation of collectd's C =head1 SYNOPSIS - LoadPlugin perl + + Globals true + # ... IncludeDir "/path/to/perl/plugins" @@ -25,6 +27,12 @@ for collectd in Perl. This is a lot more efficient than executing a Perl-script every time you want to read a value with the C (see L) and provides a lot more functionality, too. +When loading the C, the B 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 module used by the plugin +itself. See the documentation of the B option in L +for details. + =head1 CONFIGURATION =over 4 diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 9cdecc02..cc125ddf 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -114,6 +114,7 @@ #@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 @@ -138,6 +139,7 @@ #@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 @@ -739,6 +741,14 @@ # # +# +# +# Host "redis.example.com" +# Port "6379" +# Timeout 2000 +# +# + # # # Host "router.example.com" @@ -929,6 +939,14 @@ # # +# +# +# Host "localhost" +# Port "6379" +# Timeout 1000 +# +# + ############################################################################## # Filter configuration # #----------------------------------------------------------------------------# diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 936088eb..af07cdf2 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -57,6 +57,33 @@ directory for the daemon. Loads the plugin I. There must be at least one such line or B will be mostly useless. +Starting with collectd 4.9, this may also be a block in which further options +affecting the behavior of B may be specified. The following +options are allowed inside a B block: + + + Globals true + + +=over 4 + +=item B B + +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.Eg., when loading a plugin +that embeds some scripting language into the daemon (e.Eg. the C +or C 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.Eg., +L or L) for details. + +=back + =item B I If I points to a file, includes that file. If I points to a @@ -3724,6 +3751,52 @@ Defaults to B. =back +=head2 Plugin C + +The I connects to one or more Redis servers and gathers +information about each server's state. For each server there is a I block +which configures the connection parameters for this node. + + + + Host "localhost" + Port "6379" + Timeout 2000 + + + +The information shown in the synopsis above is the I +which is used by the plugin if no configuration is present. + +=over 4 + +=item B I + +The B 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. It is limited to +64Echaracters in length. + +=item B I + +The B option is the hostname or IP-address where the Redis instance is +running on. + +=item B I + +The B 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 I + +The B 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 values for all B should be lower +than B defined globally. + +=back + =head2 Plugin C The C plugin uses the RRDtool accelerator daemon, L, @@ -4120,25 +4193,37 @@ Use the last number found. =item B -The matched number is a counter. Simply sets the internal counter to this -value. +=item B + +=item B + +The matched number is a counter. Simply I the internal counter to this +value. Variants exist for C, C, and C data sources. =item B -Add the matched value to the internal counter. +=item B + +Add the matched value to the internal counter. In case of B, the +matched number may be negative, which will effectively subtract from the +internal counter. =item B -Increase the internal counter by one. This B is the only one that does -not use the matched subexpression, but simply counts the number of matched +=item B + +Increase the internal counter by one. These B 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 types interpret the submatch as a floating point -number, using L. The B and B interpret the -submatch as an integer using L. B does not use the -submatch at all and it may be omitted in this case. +number, using L. The B and B types interpret +the submatch as an unsigned integer using L. The B types +interpret the submatch as a signed integer using L. B +and B do not use the submatch at all and it may be omitted in this +case. =item B I diff --git a/src/collectdctl.c b/src/collectdctl.c index 81ed7b19..f16a70dd 100644 --- a/src/collectdctl.c +++ b/src/collectdctl.c @@ -25,21 +25,30 @@ # include "config.h" #endif -#include "libcollectdclient/collectd/client.h" +#ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE +#endif -#include +#ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112L +#endif -#include +#ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 600 +#endif -#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include -#include +#include "libcollectdclient/collectd/client.h" -#include #define DEFAULT_SOCK LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock" @@ -254,7 +263,7 @@ static int flush (lcc_connection_t *c, int argc, char **argv) } } 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); @@ -262,7 +271,7 @@ static int flush (lcc_connection_t *c, int argc, char **argv) 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); @@ -280,7 +289,7 @@ static int flush (lcc_connection_t *c, int argc, char **argv) } 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); @@ -443,7 +452,7 @@ static int putval (lcc_connection_t *c, int argc, char **argv) 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); diff --git a/src/configfile.c b/src/configfile.c index 46624dc9..99dded93 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -701,11 +701,10 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth) 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)) @@ -714,7 +713,7 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth) 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; } @@ -731,6 +730,12 @@ static oconfig_item_t *cf_read_generic (const char *path, int depth) wordfree (&we); + if (root->children == NULL) + { + oconfig_free (root); + return (NULL); + } + return (root); } /* oconfig_item_t *cf_read_generic */ /* #endif HAVE_WORDEXP_H */ diff --git a/src/curl_json.c b/src/curl_json.c index 03ef6a34..21deed61 100644 --- a/src/curl_json.c +++ b/src/curl_json.c @@ -1,7 +1,7 @@ /** * 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 @@ -130,59 +130,57 @@ static int cj_get_type (cj_key_t *key) } /* 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) @@ -209,7 +207,7 @@ static int cj_cb_map_key (void *ctx, const unsigned char *val, db->state[db->depth].key = NULL; } - return 1; + return (CJ_CB_CONTINUE); } static int cj_cb_string (void *ctx, const unsigned char *val, @@ -220,7 +218,7 @@ 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 */ @@ -242,7 +240,7 @@ static int cj_cb_string (void *ctx, const unsigned char *val, cj_curl_perform (db, curl); curl_easy_cleanup (curl); } - return 1; + return (CJ_CB_CONTINUE); } static int cj_cb_start (void *ctx) @@ -251,9 +249,9 @@ 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) @@ -261,7 +259,7 @@ 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) @@ -287,9 +285,9 @@ static int cj_cb_end_array (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, @@ -777,7 +775,8 @@ static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */ 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); diff --git a/src/plugin.c b/src/plugin.c index 4a3c9171..af894d54 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -1374,7 +1374,12 @@ int plugin_dispatch_values (value_list_t *vl) 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); } diff --git a/src/redis.c b/src/redis.c new file mode 100644 index 00000000..30bd8da3 --- /dev/null +++ b/src/redis.c @@ -0,0 +1,311 @@ +/** + * collectd - src/redis.c, based on src/memcached.c + * Copyright (C) 2010 Andrés J. Díaz + * + * 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 + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" + +#include +#include + +#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: + * + * + * + * Host "localhost" + * Port "6379" + * Timeout 2000 + * + * + */ + +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 : */ diff --git a/src/types.db b/src/types.db index 7a962f0b..f75e6007 100644 --- a/src/types.db +++ b/src/types.db @@ -27,6 +27,7 @@ counter value:COUNTER:U:U 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 @@ -157,6 +158,7 @@ time_dispersion seconds:GAUGE:-1000000:1000000 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 @@ -171,6 +173,7 @@ vmpage_action value:COUNTER:0:4294967295 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 diff --git a/src/utils_heap.c b/src/utils_heap.c index 086649a1..f8f74058 100644 --- a/src/utils_heap.c +++ b/src/utils_heap.c @@ -96,7 +96,7 @@ static void reheap (c_heap_t *h, size_t root, enum reheap_direction dir) 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 */ @@ -140,6 +140,8 @@ void c_heap_destroy (c_heap_t *h) int c_heap_insert (c_heap_t *h, void *ptr) { + size_t index; + if ((h == NULL) || (ptr == NULL)) return (-EINVAL); @@ -162,11 +164,12 @@ int c_heap_insert (c_heap_t *h, void *ptr) } /* 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); diff --git a/src/utils_match.c b/src/utils_match.c index 0f87bc0c..4d4b57d0 100644 --- a/src/utils_match.c +++ b/src/utils_match.c @@ -83,7 +83,7 @@ static int default_callback (const char __attribute__((unused)) *str, if (matches_num < 2) return (-1); - value = strtod (matches[1], &endptr); + value = (gauge_t) strtod (matches[1], &endptr); if (matches[1] == endptr) return (-1); @@ -131,7 +131,7 @@ static int default_callback (const char __attribute__((unused)) *str, 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); @@ -162,7 +162,7 @@ static int default_callback (const char __attribute__((unused)) *str, 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); @@ -186,7 +186,7 @@ static int default_callback (const char __attribute__((unused)) *str, 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); diff --git a/src/utils_match.h b/src/utils_match.h index 9e47d5c5..36abe30c 100644 --- a/src/utils_match.h +++ b/src/utils_match.h @@ -28,10 +28,10 @@ /* * 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 diff --git a/src/write_redis.c b/src/write_redis.c new file mode 100644 index 00000000..58f2cae3 --- /dev/null +++ b/src/write_redis.c @@ -0,0 +1,238 @@ +/** + * 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 + **/ + +#include "collectd.h" +#include "plugin.h" +#include "common.h" +#include "configfile.h" + +#include +#include + +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 : */