From: Florian Forster Date: Sat, 6 Nov 2010 08:04:39 +0000 (+0100) Subject: Merge branch 'sp/amqp' X-Git-Tag: collectd-5.0.0-beta0~28 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=626041ce6fa004ce49c183f35147f762a63cf302;hp=5ff3a8f355e0b8aec99a8f251b17835af54e6515;p=collectd.git Merge branch 'sp/amqp' --- 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 4d83a7ab..436f1468 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. @@ -511,6 +515,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/bindings/perl/lib/Collectd.pm b/bindings/perl/lib/Collectd.pm index f1b5d859..ca3b5d23 100644 --- a/bindings/perl/lib/Collectd.pm +++ b/bindings/perl/lib/Collectd.pm @@ -465,35 +465,6 @@ sub plugin_flush { } } -sub plugin_flush_one { - my $timeout = shift; - my $name = shift; - - WARNING ("Collectd::plugin_flush_one is deprecated - " - . "use Collectd::plugin_flush instead."); - - if (! (defined ($timeout) && defined ($name))) { - ERROR ("Usage: Collectd::plugin_flush_one(timeout, name)"); - return; - } - - plugin_flush (plugins => $name, timeout => $timeout); -} - -sub plugin_flush_all { - my $timeout = shift; - - WARNING ("Collectd::plugin_flush_all is deprecated - " - . "use Collectd::plugin_flush instead."); - - if (! defined ($timeout)) { - ERROR ("Usage: Collectd::plugin_flush_all(timeout)"); - return; - } - - plugin_flush (timeout => $timeout); -} - sub fc_call { my $type = shift; my $name = shift; diff --git a/configure.in b/configure.in index 04caadff..6249f966 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="" @@ -4327,6 +4385,7 @@ then fi if test "x$have_sysctlbyname" = "xyes" then + plugin_contextswitch="yes" plugin_cpu="yes" plugin_memory="yes" plugin_tcpconns="yes" @@ -4524,6 +4583,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]) @@ -4554,6 +4614,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]) @@ -4728,6 +4789,7 @@ Configuration: Libraries: libcurl . . . . . . . $with_libcurl libdbi . . . . . . . $with_libdbi + libcredis . . . . . . $with_libcredis libesmtp . . . . . . $with_libesmtp libganglia . . . . . $with_libganglia libgcrypt . . . . . . $with_libgcrypt @@ -4845,6 +4907,7 @@ Configuration: processes . . . . . . $enable_processes protocols . . . . . . $enable_protocols python . . . . . . . $enable_python + redis . . . . . . . . $enable_redis routeros . . . . . . $enable_routeros rrdcached . . . . . . $enable_rrdcached rrdtool . . . . . . . $enable_rrdtool @@ -4875,6 +4938,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 d4d6a1ba..14b4c83d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@ AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"' AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"' sbin_PROGRAMS = collectd collectdmon -bin_PROGRAMS = collectd-nagios collectd-flush +bin_PROGRAMS = collectd-nagios collectdctl collectd_SOURCES = collectd.c collectd.h \ common.c common.h \ @@ -106,16 +106,16 @@ collectd_nagios_LDADD += libcollectdclient/libcollectdclient.la collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la -collectd_flush_SOURCES = collectd-flush.c -collectd_flush_LDADD = +collectdctl_SOURCES = collectdctl.c +collectdctl_LDADD = if BUILD_WITH_LIBSOCKET -collectd_flush_LDADD += -lsocket +collectdctl_LDADD += -lsocket endif if BUILD_AIX -collectd_flush_LDADD += -lm +collectdctl_LDADD += -lm endif -collectd_flush_LDADD += libcollectdclient/libcollectdclient.la -collectd_flush_DEPENDENCIES = libcollectdclient/libcollectdclient.la +collectdctl_LDADD += libcollectdclient/libcollectdclient.la +collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la pkglib_LTLIBRARIES = @@ -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 @@ -1249,6 +1269,7 @@ dist_man_MANS = collectd.1 \ collectd.conf.5 \ collectd-email.5 \ collectd-exec.5 \ + collectdctl.1 \ collectd-java.5 \ collectdmon.1 \ collectd-nagios.1 \ @@ -1265,6 +1286,7 @@ EXTRA_DIST = types.db pinba.proto EXTRA_DIST += collectd.conf.pod \ collectd-email.pod \ collectd-exec.pod \ + collectdctl.pod \ collectd-java.pod \ collectdmon.pod \ collectd-nagios.pod \ diff --git a/src/apache.c b/src/apache.c index 3d6d957c..506ba84e 100644 --- a/src/apache.c +++ b/src/apache.c @@ -1,6 +1,6 @@ /** * collectd - src/apache.c - * Copyright (C) 2006-2009 Florian octo Forster + * Copyright (C) 2006-2010 Florian octo Forster * Copyright (C) 2007 Florent EppO Monbillard * Copyright (C) 2009 Amit Gupta * @@ -144,6 +144,8 @@ static size_t apache_header_callback (void *buf, size_t size, size_t nmemb, st->server_type = APACHE; else if (strstr (buf, "lighttpd") != NULL) st->server_type = LIGHTTPD; + else if (strstr (buf, "IBM_HTTP_Server") != NULL) + st->server_type = APACHE; else { const char *hdr = buf; @@ -333,57 +335,22 @@ static int config (oconfig_item_t *ci) { int status = 0; int i; - oconfig_item_t *lci = NULL; /* legacy config */ for (i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; - if (strcasecmp ("Instance", child->key) == 0 && child->children_num > 0) + if (strcasecmp ("Instance", child->key) == 0) config_add (child); else - { - /* legacy mode - convert to config */ - if (lci == NULL) - { - lci = malloc (sizeof(*lci)); - if (lci == NULL) - { - ERROR ("apache plugin: malloc failed."); - return (-1); - } - memset (lci, '\0', sizeof (*lci)); - } - - lci->children_num++; - lci->children = - realloc (lci->children, - lci->children_num * sizeof (*child)); - if (lci->children == NULL) - { - ERROR ("apache plugin: realloc failed."); - return (-1); - } - memcpy (&lci->children[lci->children_num-1], child, sizeof (*child)); - } + WARNING ("apache plugin: The configuration option " + "\"%s\" is not allowed here. Did you " + "forget to add an block " + "around the configuration?", + child->key); } /* for (ci->children) */ - if (lci) - { - /* create a entry */ - lci->key = "Instance"; - lci->values_num = 1; - lci->values = (oconfig_value_t *) malloc (lci->values_num * sizeof (oconfig_value_t)); - lci->values[0].type = OCONFIG_TYPE_STRING; - lci->values[0].value.string = ""; - - status = config_add (lci); - sfree (lci->values); - sfree (lci->children); - sfree (lci); - } - - return status; + return (status); } /* int config */ /* initialize curl for each host */ @@ -420,6 +387,8 @@ static int init_host (apache_t *st) /* {{{ */ st->server_type = APACHE; else if (strcasecmp(st->server, "lighttpd") == 0) st->server_type = LIGHTTPD; + else if (strcasecmp(st->server, "ibm_http_server") == 0) + st->server_type = APACHE; else WARNING ("apache plugin: Unknown `Server' setting: %s", st->server); diff --git a/src/collectd-flush.c b/src/collectd-flush.c deleted file mode 100644 index 71457e41..00000000 --- a/src/collectd-flush.c +++ /dev/null @@ -1,176 +0,0 @@ -/** - * collectd-flush - src/collectd-flush.c - * Copyright (C) 2010 Håkon J Dugstad Johnsen - * - * 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; only version 2 of the License is applicable. - * - * 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: - * Håkon J Dugstad Johnsen - **/ - -#include -#include -#include -#include -#include -#include - -#include "libcollectdclient/client.h" - -extern char *optarg; - -static int flush ( - const char *address, - const char *plugin, - const char *ident_str, - int timeout) -{ - lcc_connection_t *connection; - lcc_identifier_t ident; - - /* Pointer which is passed to lcc_flush. - * Either a null pointer or it points to ident */ - lcc_identifier_t *identp; - int status; - - connection = NULL; - status = lcc_connect(address, &connection); - if (status != 0) { - fprintf (stderr, "ERROR: Connecting to daemon at %s failed: %s.\n", - address, strerror (errno)); - return 1; - } - - identp = NULL; - if (ident_str != NULL && *ident_str != '\0') { - status = lcc_string_to_identifier (connection, &ident, ident_str); - if (status != 0) { - fprintf (stderr, "ERROR: Creating and identifier failed: %s.\n", - lcc_strerror(connection)); - LCC_DESTROY (connection); - - return 1; - } - identp = &ident; - } - - status = lcc_flush (connection, plugin, identp, timeout); - if (status != 0) { - fprintf (stderr, "ERROR: Flushing failed: %s.\n", - lcc_strerror (connection)); - LCC_DESTROY (connection); - - return 1; - } - - LCC_DESTROY (connection); - - return 0; -} - -void usage (const char *name) { - fprintf (stderr, "Usage: %s [options]\n" - "\n" - "Valid options are:\n" - " -h, --help Display this help message.\n" - " -s, --socket= Path to collectd's UNIX socket. Default: /var/run/collectd-unixsock\n" - " -p, --plugin= Plugin to flush _to_ (not from). Example: rrdtool\n" - " -i, --identifier=\n" - " Only flush data specified by , which has the format: \n" - "\n" - " [/][-]/[-]\n" - "\n" - " Hostname defaults to the local hostname if omitted.\n" - " No error is returned if the specified identifier does not exist.\n" - " Examples: uptime/uptime\n" - " somehost/cpu-0/cpu-wait\n" - " -t, --timeout= Only flush values older than this timeout.\n", name); -} - -/* - * Count how many occurences there are of a char in a string. - */ -int charoccurences (const char *str, char chr) { - int count = 0; - while (*str != '\0') { - if (*str == chr) { - count++; - } - str++; - } - - return count; -} - -int main (int argc, char **argv) { - char address[1024] = "unix:/var/run/collectd-unixsock"; - char *plugin = NULL; - char ident_str[1024] = ""; - int timeout = -1; - char hostname[1024]; - char c; - - static struct option long_options[] = - { - {"help", no_argument, 0, 'h'}, - {"socket", required_argument, 0, 's'}, - {"plugin", required_argument, 0, 'p'}, - {"identifier", required_argument, 0, 'i'}, - {"timeout", required_argument, 0, 't'} - }; - int option_index = 0; - - - while ((c = getopt_long (argc, argv, "s:p:i:ht:", long_options, &option_index)) != -1) { - switch (c) { - case 's': - snprintf (address, sizeof (address), "unix:%s", optarg); - break; - case 'p': - plugin = optarg; - break; - case 'i': - if(charoccurences(optarg, '/') == 1) { - /* The user has omitted the hostname part of the identifier - * (there is only one '/' in the identifier) - * Let's add the local hostname */ - if(gethostname(hostname, sizeof(hostname)) != 0) { - fprintf (stderr, "Could not get local hostname: %s", strerror(errno)); - return 1; - } - /* Make sure hostname is zero-terminated */ - hostname[sizeof(hostname)-1] = '\0'; - snprintf (ident_str, sizeof (ident_str), "%s/%s", hostname, optarg); - /* Make sure ident_str is zero terminated */ - ident_str[sizeof(ident_str)-1] = '\0'; - } else { - strncpy(ident_str, optarg, sizeof (ident_str)); - /* Make sure identifier is zero terminated */ - ident_str[sizeof(ident_str)-1] = '\0'; - } - break; - case 't': - timeout = atoi (optarg); - break; - case 'h': - usage (argv[0]); - return 0; - default: - usage (argv[0]); - return 1; - } - } - - return flush(address, plugin, ident_str, timeout); -} diff --git a/src/collectd-flush.pod b/src/collectd-flush.pod deleted file mode 100644 index cf7b178b..00000000 --- a/src/collectd-flush.pod +++ /dev/null @@ -1,71 +0,0 @@ -=head1 NAME - -collectd-flush - Small command line utility to flush collectd - -=head1 SYNOPSIS - -collectd-flush I<[options]> - -=head1 DESCRIPTION - -This small command line utitilty uses C to flush collectd -through a socket from the L. Useful if you want to be sure -you have the latest values in your RRD files before graphing them or copying -them somewhere else. - -=head1 ARGUMENTS AND OPTIONS - -The following arguments and options are understood by collectd-flush. The order -of the arguments generally doesn't matter, as long as no argument is passed -more than once. - -=over 4 - -=item B<-h>, B<--help> - -Display information about the options. - -=item B<-s>, B<--socket=>I - -Path to the UNIX socket opened by collectd's C. -Default: /var/run/collectd-unixsock - -=item B<-p>, B<--plugin=>I - -Plugin to flush I. Example: B. - -=item B<-i>, B<--identifier=>I - -If this option is present, only the data specified by I will be flushed. -I has the following format: - -[I/]I[-I]/I[-I] - -Examples: - somehost/cpu-0/cpu-idle - uptime/uptime - otherhost/memory/memory-used - -Hostname defaults to the local hostname if omitted. No error is returned if the -specified identifier does not exist (this is a limitation in the -C library).You can only specify one identifier each time you -run this program (even though L supports multiple -identifiers). - -=item B<-t>, B<--timeout=>I - -Only flush values older than I. - -=back - -=head1 SEE ALSO - -L -L -L - -=head1 AUTHOR - -Håkon J Dugstad Johnsen Ehakon-dugstad.johnsenEatEtelenor.comE - -=cut diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c index b190d6ee..b3c1855a 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/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..6b44722c 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 @@ -368,11 +376,6 @@ is found (and the number of values matches the number of data-sources) then the type, data-set and value-list is passed to all write-callbacks that are registered with the daemon. -B: Prior to version 4.4 of collectd, the data-set type used to be passed -as the first argument to B. This syntax is still supported -for backwards compatibility but has been deprecated and will be removed in -some future version of collectd. - =item B ([B => I<...>][, B => I<...>], B => I<...>) @@ -397,23 +400,6 @@ argument has been specified, only named plugins will be flushed. The value of the B and B arguments may either be a string or a reference to an array of strings. -=item B (I, I) - -This is identical to using "plugin_flush (timeout =E I, plugins -=E I". - -B: Starting with version 4.5 of collectd, B has been -deprecated and will be removed in some future version of collectd. Use -B instead. - -=item B (I) - -This is identical to using "plugin_flush (timeout =E I)". - -B: Starting with version 4.5 of collectd, B has been -deprecated and will be removed in some future version of collectd. Use -B instead. - =item B (I) Submits a I to the daemon which will then pass it to all diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 31a113a0..42addd27 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -115,6 +115,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 @@ -139,6 +140,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 @@ -164,10 +166,12 @@ # # -# URL "http://localhost/status?auto" -# User "www-user" -# Password "secret" -# CACert "/etc/ssl/ca.crt" +# +# URL "http://localhost/status?auto" +# User "www-user" +# Password "secret" +# CACert "/etc/ssl/ca.crt" +# # # @@ -754,6 +758,14 @@ # # +# +# +# Host "redis.example.com" +# Port "6379" +# Timeout 2000 +# +# + # # # Host "router.example.com" @@ -944,6 +956,14 @@ # # +# +# +# Host "localhost" +# Port "6379" +# Timeout 1000 +# +# + ############################################################################## # Filter configuration # #----------------------------------------------------------------------------# diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 36245e2c..1da35982 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 @@ -309,7 +336,25 @@ Since its C module is very similar to Apache's, B is also supported. It introduces a new field, called C, to count the number of currently connected clients. This field is also supported. -The following options are accepted by the C-plugin: +The configuration of the I plugin consists of one or more +CInstanceE/E> blocks. Each block requires one string argument +as the instance name. For example: + + + + URL "http://www1.example.com/mod_status?auto" + + + URL "http://www2.example.com/mod_status?auto" + + + +The instance name will be used as the I. To emulate the old +(versionE4) behavior, you can use an empty string (""). In order for the +plugin to work correctly, each instance name must be unique. This is not +enforced by the plugin and it is your responsibility to ensure it. + +The following options are accepted within each I block: =over 4 @@ -317,7 +362,7 @@ The following options are accepted by the C-plugin: Sets the URL of the C output. This needs to be the output generated by C and it needs to be the machine readable output -generated by appending the C argument. +generated by appending the C argument. This option is I. =item B I @@ -2718,7 +2763,7 @@ operating systems. =item B I<1024-65535> Set the maximum size for datagrams received over the network. Packets larger -than this will be truncated. +than this will be truncated. Defaults to 1452Ebytes. =item B I @@ -3362,11 +3407,6 @@ allowed as long as a single non-empty command has been specified only. The returned lines will be handled separately one after another. -=item B I - -This is a deprecated synonym for B. It will be removed in version 5 -of collectd. - =item B I|I|I|I Specify the parameters which should be passed to the SQL query. The parameters @@ -3442,21 +3482,6 @@ This option is required inside a B block and may be specified multiple times. If multiple B options are specified, the columns are read in the given order. -=item B I [I] - -This is a deprecated alternative to a B block. It will be removed in -version 5 of collectd. It is equivalent to the following B block: - - - Type I - InstancePrefix I - ValuesFrom I - - -The order of the B options defines which columns of the query result -should be used. The first option specifies the data found in the first column, -the second option that of the second column, and so on. - =item B I =item B I @@ -3471,13 +3496,6 @@ The I has to be specified as the concatenation of the major, minor and patch-level versions, each represented as two-decimal-digit numbers. For example, version 8.2.3 will become 80203. -=item B I - -=item B I - -These are deprecated synonyms for B and B -respectively. They will be removed in version 5 of collectd. - =back The following predefined queries are available (the definitions can be found @@ -3888,6 +3906,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, @@ -4284,25 +4348,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/collectd.h b/src/collectd.h index 8849b30b..6faa1a4e 100644 --- a/src/collectd.h +++ b/src/collectd.h @@ -56,21 +56,6 @@ #if HAVE_STDINT_H # include #endif -#if HAVE_STDBOOL_H -# include -#else -# ifndef HAVE__BOOL -# ifdef __cplusplus -typedef bool _Bool; -# else -# define _Bool signed char -# endif -# endif -# define bool _Bool -# define false 0 -# define true 1 -# define __bool_true_false_are_defined 1 -#endif #if HAVE_UNISTD_H # include #endif diff --git a/src/collectdctl.c b/src/collectdctl.c new file mode 100644 index 00000000..e1091cc6 --- /dev/null +++ b/src/collectdctl.c @@ -0,0 +1,591 @@ +/** + * collectd - src/collectdctl.c + * Copyright (C) 2010 Håkon J Dugstad Johnsen + * Copyright (C) 2010 Sebastian Harl + * + * 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; only version 2 of the License is applicable. + * + * 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: + * Håkon J Dugstad Johnsen + * Sebastian "tokkee" Harl + **/ + +#if HAVE_CONFIG_H +# 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 + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libcollectdclient/client.h" + + +#define DEFAULT_SOCK LOCALSTATEDIR"/run/"PACKAGE_NAME"-unixsock" + +extern char *optarg; +extern int optind; + +static void exit_usage (const char *name, int status) { + fprintf ((status == 0) ? stdout : stderr, + "Usage: %s [options] [cmd options]\n\n" + + "Available options:\n" + " -s Path to collectd's UNIX socket.\n" + " Default: "DEFAULT_SOCK"\n" + + "\n -h Display this help and exit.\n" + + "\nAvailable commands:\n\n" + + " * getval \n" + " * flush [timeout=] [plugin=] [identifier=]\n" + " * listval\n" + " * putval [interval=] \n" + + "\nIdentifiers:\n\n" + + "An identifier has the following format:\n\n" + + " [/][-]/[-]\n\n" + + "Hostname defaults to the local hostname if omitted (e.g., uptime/uptime).\n" + "No error is returned if the specified identifier does not exist.\n" + + "\n"PACKAGE" "VERSION", http://collectd.org/\n" + "by Florian octo Forster \n" + "for contributions see `AUTHORS'\n" + , name); + exit (status); +} + +/* Count the number of occurrences of the character 'chr' + * in the specified string. */ +static int count_chars (const char *str, char chr) { + int count = 0; + + while (*str != '\0') { + if (*str == chr) { + count++; + } + str++; + } + + return count; +} /* count_chars */ + +static int array_grow (void **array, int *array_len, size_t elem_size) +{ + void *tmp; + + assert ((array != NULL) && (array_len != NULL)); + + tmp = realloc (*array, (*array_len + 1) * elem_size); + if (tmp == NULL) { + fprintf (stderr, "ERROR: Failed to allocate memory.\n"); + return (-1); + } + + *array = tmp; + ++(*array_len); + return (0); +} /* array_grow */ + +static int parse_identifier (lcc_connection_t *c, + const char *value, lcc_identifier_t *ident) +{ + char hostname[1024]; + char ident_str[1024] = ""; + int n_slashes; + + int status; + + n_slashes = count_chars (value, '/'); + if (n_slashes == 1) { + /* The user has omitted the hostname part of the identifier + * (there is only one '/' in the identifier) + * Let's add the local hostname */ + if (gethostname (hostname, sizeof (hostname)) != 0) { + fprintf (stderr, "ERROR: Failed to get local hostname: %s", + strerror (errno)); + return (-1); + } + hostname[sizeof (hostname) - 1] = '\0'; + + snprintf (ident_str, sizeof (ident_str), "%s/%s", hostname, value); + ident_str[sizeof(ident_str) - 1] = '\0'; + } + else { + strncpy (ident_str, value, sizeof (ident_str)); + ident_str[sizeof (ident_str) - 1] = '\0'; + } + + status = lcc_string_to_identifier (c, ident, ident_str); + if (status != 0) { + fprintf (stderr, "ERROR: Failed to parse identifier ``%s'': %s.\n", + ident_str, lcc_strerror(c)); + return (-1); + } + return (0); +} /* parse_identifier */ + +static int getval (lcc_connection_t *c, int argc, char **argv) +{ + lcc_identifier_t ident; + + size_t ret_values_num = 0; + gauge_t *ret_values = NULL; + char **ret_values_names = NULL; + + int status; + size_t i; + + assert (strcasecmp (argv[0], "getval") == 0); + + if (argc != 2) { + fprintf (stderr, "ERROR: getval: Missing identifier.\n"); + return (-1); + } + + memset (&ident, 0, sizeof (ident)); + status = parse_identifier (c, argv[1], &ident); + if (status != 0) + return (status); + +#define BAIL_OUT(s) \ + do { \ + if (ret_values != NULL) \ + free (ret_values); \ + if (ret_values_names != NULL) { \ + for (i = 0; i < ret_values_num; ++i) \ + free (ret_values_names[i]); \ + free (ret_values_names); \ + } \ + ret_values_num = 0; \ + return (s); \ + } while (0) + + status = lcc_getval (c, &ident, + &ret_values_num, &ret_values, &ret_values_names); + if (status != 0) { + fprintf (stderr, "ERROR: %s\n", lcc_strerror (c)); + BAIL_OUT (-1); + } + + for (i = 0; i < ret_values_num; ++i) + printf ("%s=%e\n", ret_values_names[i], ret_values[i]); + BAIL_OUT (0); +#undef BAIL_OUT +} /* getval */ + +static int flush (lcc_connection_t *c, int argc, char **argv) +{ + int timeout = -1; + + lcc_identifier_t *identifiers = NULL; + int identifiers_num = 0; + + char **plugins = NULL; + int plugins_num = 0; + + int status; + int i; + + assert (strcasecmp (argv[0], "flush") == 0); + +#define BAIL_OUT(s) \ + do { \ + if (identifiers != NULL) \ + free (identifiers); \ + identifiers_num = 0; \ + if (plugins != NULL) \ + free (plugins); \ + plugins_num = 0; \ + return (s); \ + } while (0) + + for (i = 1; i < argc; ++i) { + char *key, *value; + + key = argv[i]; + value = strchr (argv[i], (int)'='); + + if (! value) { + fprintf (stderr, "ERROR: flush: Invalid option ``%s''.\n", argv[i]); + BAIL_OUT (-1); + } + + *value = '\0'; + ++value; + + if (strcasecmp (key, "timeout") == 0) { + char *endptr = NULL; + + timeout = (int) strtol (value, &endptr, 0); + + if (endptr == value) { + fprintf (stderr, "ERROR: Failed to parse timeout as number: %s.\n", + value); + BAIL_OUT (-1); + } + else if ((endptr != NULL) && (*endptr != '\0')) { + fprintf (stderr, "WARNING: Ignoring trailing garbage after timeout: " + "%s.\n", endptr); + } + } + else if (strcasecmp (key, "plugin") == 0) { + 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, + sizeof (*identifiers)); + if (status != 0) + BAIL_OUT (status); + + memset (identifiers + (identifiers_num - 1), 0, sizeof (*identifiers)); + status = parse_identifier (c, value, + identifiers + (identifiers_num - 1)); + if (status != 0) + BAIL_OUT (status); + } + else { + fprintf (stderr, "ERROR: flush: Unknown option `%s'.\n", key); + BAIL_OUT (-1); + } + } + + if (plugins_num == 0) { + status = array_grow ((void *)&plugins, &plugins_num, sizeof (*plugins)); + if (status != 0) + BAIL_OUT (status); + + assert (plugins_num == 1); + plugins[0] = NULL; + } + + for (i = 0; i < plugins_num; ++i) { + if (identifiers_num == 0) { + status = lcc_flush (c, plugins[i], NULL, timeout); + if (status != 0) + fprintf (stderr, "ERROR: Failed to flush plugin `%s': %s.\n", + (plugins[i] == NULL) ? "(all)" : plugins[i], lcc_strerror (c)); + } + else { + int j; + + for (j = 0; j < identifiers_num; ++j) { + status = lcc_flush (c, plugins[i], identifiers + j, timeout); + if (status != 0) { + char id[1024]; + + lcc_identifier_to_string (c, id, sizeof (id), identifiers + j); + fprintf (stderr, "ERROR: Failed to flush plugin `%s', " + "identifier `%s': %s.\n", + (plugins[i] == NULL) ? "(all)" : plugins[i], + id, lcc_strerror (c)); + } + } + } + } + + BAIL_OUT (0); +#undef BAIL_OUT +} /* flush */ + +static int listval (lcc_connection_t *c, int argc, char **argv) +{ + lcc_identifier_t *ret_ident = NULL; + size_t ret_ident_num = 0; + + int status; + size_t i; + + assert (strcasecmp (argv[0], "listval") == 0); + + if (argc != 1) { + fprintf (stderr, "ERROR: listval: Does not accept any arguments.\n"); + return (-1); + } + +#define BAIL_OUT(s) \ + do { \ + if (ret_ident != NULL) \ + free (ret_ident); \ + ret_ident_num = 0; \ + return (s); \ + } while (0) + + status = lcc_listval (c, &ret_ident, &ret_ident_num); + if (status != 0) { + fprintf (stderr, "ERROR: %s\n", lcc_strerror (c)); + BAIL_OUT (status); + } + + for (i = 0; i < ret_ident_num; ++i) { + char id[1024]; + + status = lcc_identifier_to_string (c, id, sizeof (id), ret_ident + i); + if (status != 0) { + fprintf (stderr, "ERROR: listval: Failed to convert returned " + "identifier to a string: %s\n", lcc_strerror (c)); + continue; + } + + printf ("%s\n", id); + } + BAIL_OUT (0); +#undef BAIL_OUT +} /* listval */ + +static int putval (lcc_connection_t *c, int argc, char **argv) +{ + lcc_value_list_t vl = LCC_VALUE_LIST_INIT; + + /* 64 ought to be enough for anybody ;-) */ + value_t values[64]; + int values_types[64]; + size_t values_len = 0; + + int status; + int i; + + assert (strcasecmp (argv[0], "putval") == 0); + + if (argc < 3) { + fprintf (stderr, "ERROR: putval: Missing identifier " + "and/or value list.\n"); + return (-1); + } + + vl.values = values; + vl.values_types = values_types; + + status = parse_identifier (c, argv[1], &vl.identifier); + if (status != 0) + return (status); + + for (i = 2; i < argc; ++i) { + char *tmp; + + tmp = strchr (argv[i], (int)'='); + + if (tmp != NULL) { /* option */ + char *key = argv[i]; + char *value = tmp; + + *value = '\0'; + ++value; + + if (strcasecmp (key, "interval") == 0) { + char *endptr; + + vl.interval = strtol (value, &endptr, 0); + + if (endptr == value) { + fprintf (stderr, "ERROR: Failed to parse interval as number: %s.\n", + value); + return (-1); + } + else if ((endptr != NULL) && (*endptr != '\0')) { + fprintf (stderr, "WARNING: Ignoring trailing garbage after " + "interval: %s.\n", endptr); + } + } + else { + fprintf (stderr, "ERROR: putval: Unknown option `%s'.\n", key); + return (-1); + } + } + else { /* value list */ + char *value; + + tmp = strchr (argv[i], (int)':'); + + if (tmp == NULL) { + fprintf (stderr, "ERROR: putval: Invalid value list: %s.\n", + argv[i]); + return (-1); + } + + *tmp = '\0'; + ++tmp; + + if (strcasecmp (argv[i], "N") == 0) { + vl.time = 0; + } + else { + char *endptr; + + vl.time = strtol (argv[i], &endptr, 0); + + if (endptr == argv[i]) { + fprintf (stderr, "ERROR: Failed to parse time as number: %s.\n", + argv[i]); + return (-1); + } + else if ((endptr != NULL) && (*endptr != '\0')) { + fprintf (stderr, "ERROR: Garbage after time: %s.\n", endptr); + return (-1); + } + } + + values_len = 0; + value = tmp; + while (value != 0) { + char *dot, *endptr; + + tmp = strchr (argv[i], (int)':'); + + if (tmp != NULL) { + *tmp = '\0'; + ++tmp; + } + + /* This is a bit of a hack, but parsing types.db just does not make + * much sense imho -- the server might have different types defined + * anyway. Also, lcc uses the type information for formatting the + * number only, so the real meaning does not matter. -tokkee */ + dot = strchr (value, (int)'.'); + endptr = NULL; + if (strcasecmp (value, "U") == 0) { + values[values_len].gauge = NAN; + values_types[values_len] = LCC_TYPE_GAUGE; + } + else if (dot) { /* floating point value */ + values[values_len].gauge = strtod (value, &endptr); + values_types[values_len] = LCC_TYPE_GAUGE; + } + else { /* integer */ + values[values_len].counter = strtol (value, &endptr, 0); + values_types[values_len] = LCC_TYPE_COUNTER; + } + ++values_len; + + if (endptr == value) { + fprintf (stderr, "ERROR: Failed to parse value as number: %s.\n", + argv[i]); + return (-1); + } + else if ((endptr != NULL) && (*endptr != '\0')) { + fprintf (stderr, "ERROR: Garbage after value: %s.\n", endptr); + return (-1); + } + + value = tmp; + } + + assert (values_len >= 1); + vl.values_len = values_len; + + status = lcc_putval (c, &vl); + if (status != 0) { + fprintf (stderr, "ERROR: %s\n", lcc_strerror (c)); + return (-1); + } + } + } + + if (values_len == 0) { + fprintf (stderr, "ERROR: putval: Missing value list(s).\n"); + return (-1); + } + return (0); +} /* putval */ + +int main (int argc, char **argv) { + char address[1024] = "unix:"DEFAULT_SOCK; + + lcc_connection_t *c; + + int status; + + while (42) { + int c; + + c = getopt (argc, argv, "s:h"); + + if (c == -1) + break; + + switch (c) { + case 's': + snprintf (address, sizeof (address), "unix:%s", optarg); + address[sizeof (address) - 1] = '\0'; + break; + case 'h': + exit_usage (argv[0], 0); + break; + default: + exit_usage (argv[0], 1); + } + } + + if (optind >= argc) { + fprintf (stderr, "%s: missing command\n", argv[0]); + exit_usage (argv[0], 1); + } + + c = NULL; + status = lcc_connect (address, &c); + if (status != 0) { + fprintf (stderr, "ERROR: Failed to connect to daemon at %s: %s.\n", + address, strerror (errno)); + return (1); + } + + if (strcasecmp (argv[optind], "getval") == 0) + status = getval (c, argc - optind, argv + optind); + else if (strcasecmp (argv[optind], "flush") == 0) + status = flush (c, argc - optind, argv + optind); + else if (strcasecmp (argv[optind], "listval") == 0) + status = listval (c, argc - optind, argv + optind); + else if (strcasecmp (argv[optind], "putval") == 0) + status = putval (c, argc - optind, argv + optind); + else { + fprintf (stderr, "%s: invalid command: %s\n", argv[0], argv[optind]); + return (1); + } + + LCC_DESTROY (c); + + if (status != 0) + return (status); + return (0); +} /* main */ + +/* vim: set sw=2 ts=2 tw=78 expandtab : */ + diff --git a/src/collectdctl.pod b/src/collectdctl.pod new file mode 100644 index 00000000..21c0b500 --- /dev/null +++ b/src/collectdctl.pod @@ -0,0 +1,160 @@ +=head1 NAME + +collectdctl - Control interface for collectd + +=head1 SYNOPSIS + +collectdctl I<[options]> IcommandE> I<[command options]> + +=head1 DESCRIPTION + +collectdctl provides a control interface for collectd, which may be used to +interact with the daemon using the C. + +=head1 OPTIONS + +collectdctl supports the following options: + +=over 4 + +=item B<-s> I + +Path to the UNIX socket opened by collectd's C. +Default: /var/run/collectd-unixsock + +=item B<-h> + +Display usage information and exit. + +=back + +=head1 AVAILABLE COMMANDS + +The following commands are supported: + +=over 4 + +=item B IidentifierE> + +Query the latest collected value identified by the specified +IidentifierE> (see below). The value-list associated with that +data-set is returned as a list of key-value-pairs, each on its own line. Keys +and values are separated by the equal sign (C<=>). + +=item B [BIsecondsE>] [BInameE>] +[BIidE>] + +Flush the daemon. This is useful, e.Eg., to make sure that the latest +values have been written to the respective RRD file before graphing them or +copying them to somewhere else. + +The following options are supported by the flush command: + +=over 4 + +=item BIsecondsE> + +Flush values older than the specified timeout (in seconds) only. + +=item BInameE> + +Flush the specified plugin only. I.Ee., data cached by the specified +plugin is written to disk (or network or whatever), if the plugin supports +that operation. + +Example: B. + +=item BIidE> + +If this option is present, only the data specified by the specified identifier +(see below) will be flushed. Note that this option is not supported by all +plugins (e.Eg., the C plugin does not support this). + +=back + +The B and B options may be specified more than once. In +that case, all combinations of specified plugins and identifiers will be +flushed only. + +=item B + +Returns a list of all values (by their identifier) available to the +C plugin. Each value is printed on its own line. I.Ee., this +command returns a list of valid identifiers that may be used with the other +commands. + +=item B IidentifierE> [BIsecondsE>] +Ivalue-list(s)E> + +Submit one or more values (identified by IidentifierE>, see below) +to the daemon which will then dispatch them to the write plugins. B +specifies the interval (in seconds) used to collect the values following that +option. It defaults to the default of the running collectd instance receiving +the data. Multiple Ivalue-list(s)E> (see below) may be specified. +Each of them will be submitted to the daemon. The values have to match the +data-set definition specified by the type as given in the identifier (see +L for details). + +=back + +=head1 IDENTIFIERS + +An identifier has the following format: + +[I/]I[-I]/I[-I] + +Examples: + somehost/cpu-0/cpu-idle + uptime/uptime + otherhost/memory/memory-used + +Hostname defaults to the local (non-fully qualified) hostname if omitted. No +error is returned if the specified identifier does not exist (this is a +limitation in the C library). + +=head1 VALUE-LIST + +A value list describes one data-set as handled by collectd. It is a colon +(C<:>) separated list of the time and the values. Each value is either given +as an integer if the data-type is a counter, or as a double if the data-type +is a gauge value. A literal C is interpreted as an undefined gauge value. +The number of values and the data-types have to match the type specified in +the identifier (see L for details). The time is specified as +epoch (i.Ee., standard UNIX time) or as a literal C which will be +interpreted as now. + +=head1 EXAMPLES + +=over 4 + +=item C + +Flushes all CPU wait RRD values of the first CPU of the local host. +I.Ee., writes all pending RRD updates of that data-source to disk. + +=item C + +Query the latest number of logged in users on all hosts known to the local +collectd instance. + +=back + +=head1 SEE ALSO + +L, +L, +L, +L + +=head1 AUTHOR + +collectd has been written by Florian Forster Eocto at verplant.orgE +and many contributors (see `AUTHORS'). + +collectdctl has been written by +Håkon J Dugstad Johnsen Ehakon-dugstad.johnsenEatEtelenor.comE +and Sebastian Harl Esh at tokkee.orgE. + +=cut 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/contextswitch.c b/src/contextswitch.c index 06055ca5..c207318f 100644 --- a/src/contextswitch.c +++ b/src/contextswitch.c @@ -1,6 +1,7 @@ /** * collectd - src/contextswitch.c * Copyright (C) 2009 Patrik Weiskircher + * Copyright (C) 2010 Kimo Rosenbaum * * 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 @@ -17,13 +18,26 @@ * * Authors: * Patrik Weiskircher + * Kimo Rosenbaum **/ #include "collectd.h" #include "common.h" #include "plugin.h" -#if !KERNEL_LINUX +#ifdef HAVE_SYS_SYSCTL_H +# include +#endif + +#if HAVE_SYSCTLBYNAME +/* no global variables */ +/* #endif HAVE_SYSCTLBYNAME */ + +#elif KERNEL_LINUX +/* no global variables */ +/* #endif KERNEL_LINUX */ + +#else # error "No applicable input method." #endif @@ -45,6 +59,25 @@ static void cs_submit (derive_t context_switches) static int cs_read (void) { +#if HAVE_SYSCTLBYNAME + int value = 0; + size_t value_len = sizeof (value); + int status; + + status = sysctlbyname ("vm.stats.sys.v_swtch", + &value, &value_len, + /* new pointer = */ NULL, /* new length = */ 0); + if (status != 0) + { + ERROR("contextswitch plugin: sysctlbyname " + "(vm.stats.sys.v_swtch) failed"); + return (-1); + } + + cs_submit (value); +/* #endif HAVE_SYSCTLBYNAME */ + +#elif KERNEL_LINUX FILE *fh; char buffer[64]; int numfields; @@ -88,6 +121,7 @@ static int cs_read (void) if (status == -2) ERROR ("contextswitch plugin: Unable to find context switch value."); +#endif /* KERNEL_LINUX */ return status; } diff --git a/src/curl.c b/src/curl.c index a533e147..8b95c80f 100644 --- a/src/curl.c +++ b/src/curl.c @@ -577,7 +577,6 @@ static void cc_submit (const web_page_t *wp, const web_match_t *wm, /* {{{ */ vl.values = values; vl.values_len = 1; - vl.time = time (NULL); sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "curl", sizeof (vl.plugin)); sstrncpy (vl.plugin_instance, wp->instance, sizeof (vl.plugin_instance)); @@ -596,7 +595,6 @@ static void cc_submit_response_time (const web_page_t *wp, double seconds) /* {{ vl.values = values; vl.values_len = 1; - vl.time = time (NULL); sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "curl", sizeof (vl.plugin)); sstrncpy (vl.plugin_instance, wp->instance, sizeof (vl.plugin_instance)); diff --git a/src/curl_json.c b/src/curl_json.c index 03ef6a34..fbac7ad1 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 @@ -98,18 +98,12 @@ static size_t cj_curl_callback (void *buf, /* {{{ */ return (0); status = yajl_parse(db->yajl, (unsigned char *)buf, len); - if (status == yajl_status_ok) - { - status = yajl_parse_complete(db->yajl); - return (len); - } - else if (status == yajl_status_insufficient_data) - return (len); - - if (status != yajl_status_ok) + if ((status != yajl_status_ok) + && (status != yajl_status_insufficient_data)) { unsigned char *msg = - yajl_get_error(db->yajl, 1, (unsigned char *)buf, len); + yajl_get_error(db->yajl, /* verbose = */ 1, + /* jsonText = */ (unsigned char *) buf, (unsigned int) len); ERROR ("curl_json plugin: yajl_parse failed: %s", msg); yajl_free_error(db->yajl, msg); return (0); /* abort write callback */ @@ -130,59 +124,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 +201,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 +212,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 +234,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 +243,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 +253,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 +279,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, @@ -770,27 +762,45 @@ static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */ } status = curl_easy_perform (curl); - - yajl_free (db->yajl); - db->yajl = yprev; + if (status != 0) + { + ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)", + status, db->curl_errbuf, url); + yajl_free (db->yajl); + db->yajl = yprev; + return (-1); + } 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); + ERROR ("curl_json plugin: curl_easy_perform failed with " + "response code %ld (%s)", rc, url); + yajl_free (db->yajl); + db->yajl = yprev; return (-1); } - if (status != 0) + status = yajl_parse_complete (db->yajl); + if (status != yajl_status_ok) { - ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)", - status, db->curl_errbuf, url); + unsigned char *errmsg; + + errmsg = yajl_get_error (db->yajl, /* verbose = */ 0, + /* jsonText = */ NULL, /* jsonTextLen = */ 0); + ERROR ("curl_json plugin: yajl_parse_complete failed: %s", + (char *) errmsg); + yajl_free_error (db->yajl, errmsg); + yajl_free (db->yajl); + db->yajl = yprev; return (-1); } + yajl_free (db->yajl); + db->yajl = yprev; return (0); } /* }}} int cj_curl_perform */ diff --git a/src/dbi.c b/src/dbi.c index 77f393f5..cd9240a6 100644 --- a/src/dbi.c +++ b/src/dbi.c @@ -400,7 +400,7 @@ static int cdbi_config (oconfig_item_t *ci) /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp ("Query", child->key) == 0) udb_query_create (&queries, &queries_num, child, - /* callback = */ NULL, /* legacy mode = */ 0); + /* callback = */ NULL); else if (strcasecmp ("Database", child->key) == 0) cdbi_config_add_database (child); else diff --git a/src/df.c b/src/df.c index b2be8e5e..4b3cba01 100644 --- a/src/df.c +++ b/src/df.c @@ -60,8 +60,8 @@ static ignorelist_t *il_device = NULL; static ignorelist_t *il_mountpoint = NULL; static ignorelist_t *il_fstype = NULL; -static _Bool by_device = false; -static _Bool report_inodes = false; +static _Bool by_device = 0; +static _Bool report_inodes = 0; static int df_init (void) { @@ -116,16 +116,16 @@ static int df_config (const char *key, const char *value) else if (strcasecmp (key, "ReportByDevice") == 0) { if (IS_TRUE (value)) - by_device = true; + by_device = 1; return (0); } else if (strcasecmp (key, "ReportInodes") == 0) { if (IS_TRUE (value)) - report_inodes = true; + report_inodes = 1; else - report_inodes = false; + report_inodes = 0; return (0); } diff --git a/src/libcollectdclient/client.c b/src/libcollectdclient/client.c index 0c748ba7..75ac7b6d 100644 --- a/src/libcollectdclient/client.c +++ b/src/libcollectdclient/client.c @@ -788,7 +788,7 @@ int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */ else if (vl->values_types[i] == LCC_TYPE_GAUGE) { if (isnan (vl->values[i].gauge)) - SSTRCPY (command, ":U"); + SSTRCATF (command, ":U"); else SSTRCATF (command, ":%g", vl->values[i].gauge); } diff --git a/src/libcollectdclient/client.h b/src/libcollectdclient/client.h index 11e7b13c..99003538 100644 --- a/src/libcollectdclient/client.h +++ b/src/libcollectdclient/client.h @@ -84,7 +84,7 @@ struct lcc_value_list_s lcc_identifier_t identifier; }; typedef struct lcc_value_list_s lcc_value_list_t; -#define LCC_VALUE_LIST_INIT { NULL, 0, 0, 0, LCC_IDENTIFIER_INIT } +#define LCC_VALUE_LIST_INIT { NULL, NULL, 0, 0, 0, LCC_IDENTIFIER_INIT } struct lcc_connection_s; typedef struct lcc_connection_s lcc_connection_t; diff --git a/src/memcachec.c b/src/memcachec.c index d066501c..8f51e22f 100644 --- a/src/memcachec.c +++ b/src/memcachec.c @@ -452,7 +452,6 @@ static void cmc_submit (const web_page_t *wp, const web_match_t *wm, /* {{{ */ vl.values = values; vl.values_len = 1; - vl.time = time (NULL); sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "memcachec", sizeof (vl.plugin)); sstrncpy (vl.plugin_instance, wp->instance, sizeof (vl.plugin_instance)); diff --git a/src/memcached.c b/src/memcached.c index 348591fd..8490bf66 100644 --- a/src/memcached.c +++ b/src/memcached.c @@ -302,7 +302,6 @@ static void submit_counter2 (const char *type, const char *type_inst, vl.values = values; vl.values_len = 2; - vl.time = time (NULL); sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin)); sstrncpy (vl.type, type, sizeof (vl.type)); @@ -323,7 +322,6 @@ static void submit_gauge (const char *type, const char *type_inst, vl.values = values; vl.values_len = 1; - vl.time = time (NULL); sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin)); sstrncpy (vl.type, type, sizeof (vl.type)); @@ -345,7 +343,6 @@ static void submit_gauge2 (const char *type, const char *type_inst, vl.values = values; vl.values_len = 2; - vl.time = time (NULL); sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "memcached", sizeof (vl.plugin)); sstrncpy (vl.type, type, sizeof (vl.type)); diff --git a/src/mysql.c b/src/mysql.c index 48ad528b..a01bbe40 100644 --- a/src/mysql.c +++ b/src/mysql.c @@ -42,7 +42,6 @@ struct mysql_database_s /* {{{ */ { - /* instance == NULL => legacy mode */ char *instance; char *host; char *user; @@ -243,10 +242,10 @@ static MYSQL *getconnection (mysql_database_t *db) int err; if ((err = mysql_ping (db->con)) != 0) { - WARNING ("mysql_ping failed for %s: %s", - (db->instance != NULL) - ? db->instance - : "", + /* Assured by "mysql_config_database" */ + assert (db->instance != NULL); + WARNING ("mysql_ping failed for instance \"%s\": %s", + db->instance, mysql_error (db->con)); db->state = 0; } @@ -290,29 +289,13 @@ static MYSQL *getconnection (mysql_database_t *db) static void set_host (mysql_database_t *db, char *buf, size_t buflen) { - /* XXX legacy mode - use hostname_g */ - if (db->instance == NULL) + if ((db->host == NULL) + || (strcmp ("", db->host) == 0) + || (strcmp ("localhost", db->host) == 0)) sstrncpy (buf, hostname_g, buflen); else - { - if ((db->host == NULL) - || (strcmp ("", db->host) == 0) - || (strcmp ("localhost", db->host) == 0)) - sstrncpy (buf, hostname_g, buflen); - else - sstrncpy (buf, db->host, buflen); - } -} - -static void set_plugin_instance (mysql_database_t *db, - char *buf, size_t buflen) -{ - /* XXX legacy mode - no plugin_instance */ - if (db->instance == NULL) - sstrncpy (buf, "", buflen); - else - sstrncpy (buf, db->instance, buflen); -} + sstrncpy (buf, db->host, buflen); +} /* void set_host */ static void submit (const char *type, const char *type_instance, value_t *values, size_t values_len, mysql_database_t *db) @@ -325,7 +308,10 @@ static void submit (const char *type, const char *type_instance, set_host (db, vl.host, sizeof (vl.host)); sstrncpy (vl.plugin, "mysql", sizeof (vl.plugin)); - set_plugin_instance (db, vl.plugin_instance, sizeof (vl.plugin_instance)); + + /* Assured by "mysql_config_database" */ + assert (db->instance != NULL); + sstrncpy (vl.plugin_instance, db->instance, sizeof (vl.plugin_instance)); sstrncpy (vl.type, type, sizeof (vl.type)); if (type_instance != NULL) @@ -508,8 +494,10 @@ static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con) sql = row[SLAVE_SQL_RUNNING_IDX]; set_host (db, n.host, sizeof (n.host)); - set_plugin_instance (db, - n.plugin_instance, sizeof (n.plugin_instance)); + + /* Assured by "mysql_config_database" */ + assert (db->instance != NULL); + sstrncpy (n.plugin_instance, db->instance, sizeof (n.plugin_instance)); if (((io == NULL) || (strcasecmp (io, "yes") != 0)) && (db->slave_io_running)) diff --git a/src/network.c b/src/network.c index 73e6d92d..1544ecfb 100644 --- a/src/network.c +++ b/src/network.c @@ -258,7 +258,7 @@ typedef struct receive_list_entry_s receive_list_entry_t; * Private variables */ static int network_config_ttl = 0; -static size_t network_config_packet_size = 1024; +static size_t network_config_packet_size = 1452; static int network_config_forward = 0; static int network_config_stats = 0; @@ -319,30 +319,30 @@ static _Bool check_receive_okay (const value_list_t *vl) /* {{{ */ /* This is a value we already sent. Don't allow it to be received again in * order to avoid looping. */ if ((status == 0) && (time_sent >= ((uint64_t) vl->time))) - return (false); + return (0); - return (true); + return (1); } /* }}} _Bool check_receive_okay */ static _Bool check_send_okay (const value_list_t *vl) /* {{{ */ { - _Bool received = false; + _Bool received = 0; int status; if (network_config_forward != 0) - return (true); + return (1); if (vl->meta == NULL) - return (true); + return (1); status = meta_data_get_boolean (vl->meta, "network:received", &received); if (status == -ENOENT) - return (true); + return (1); else if (status != 0) { ERROR ("network plugin: check_send_okay: meta_data_get_boolean failed " "with status %i.", status); - return (true); + return (1); } /* By default, only *send* value lists that were not *received* by the @@ -383,7 +383,7 @@ static int network_dispatch_values (value_list_t *vl, /* {{{ */ return (-ENOMEM); } - status = meta_data_add_boolean (vl->meta, "network:received", true); + status = meta_data_add_boolean (vl->meta, "network:received", 1); if (status != 0) { ERROR ("network plugin: meta_data_add_boolean failed."); @@ -3256,13 +3256,13 @@ static int network_stats_read (void) /* {{{ */ static int network_init (void) { - static _Bool have_init = false; + static _Bool have_init = 0; /* Check if we were already initialized. If so, just return - there's * nothing more to do (for now, that is). */ if (have_init) return (0); - have_init = true; + have_init = 1; #if HAVE_LIBGCRYPT gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); diff --git a/src/nginx.c b/src/nginx.c index 69768427..36d3d8d2 100644 --- a/src/nginx.c +++ b/src/nginx.c @@ -38,10 +38,9 @@ static char *cacert = NULL; static CURL *curl = NULL; -#define ABUFFER_SIZE 16384 -static char nginx_buffer[ABUFFER_SIZE]; -static int nginx_buffer_len = 0; -static char nginx_curl_error[CURL_ERROR_SIZE]; +static char nginx_buffer[16384]; +static size_t nginx_buffer_len = 0; +static char nginx_curl_error[CURL_ERROR_SIZE]; static const char *config_keys[] = { @@ -59,17 +58,19 @@ static size_t nginx_curl_callback (void *buf, size_t size, size_t nmemb, { size_t len = size * nmemb; - if ((nginx_buffer_len + len) >= ABUFFER_SIZE) + /* Check if the data fits into the memory. If not, truncate it. */ + if ((nginx_buffer_len + len) >= sizeof (nginx_buffer)) { - len = (ABUFFER_SIZE - 1) - nginx_buffer_len; + assert (sizeof (nginx_buffer) > nginx_buffer_len); + len = (sizeof (nginx_buffer) - 1) - nginx_buffer_len; } if (len <= 0) return (len); - memcpy (nginx_buffer + nginx_buffer_len, (char *) buf, len); + memcpy (&nginx_buffer[nginx_buffer_len], buf, len); nginx_buffer_len += len; - nginx_buffer[nginx_buffer_len] = '\0'; + nginx_buffer[nginx_buffer_len] = 0; return (len); } diff --git a/src/oracle.c b/src/oracle.c index 3fe21254..2f218519 100644 --- a/src/oracle.c +++ b/src/oracle.c @@ -331,7 +331,7 @@ static int o_config (oconfig_item_t *ci) /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp ("Query", child->key) == 0) udb_query_create (&queries, &queries_num, child, - /* callback = */ NULL, /* legacy mode = */ 0); + /* callback = */ NULL); else if (strcasecmp ("Database", child->key) == 0) o_config_add_database (child); else diff --git a/src/perl.c b/src/perl.c index a2f5da29..72605804 100644 --- a/src/perl.c +++ b/src/perl.c @@ -33,6 +33,10 @@ #include "configfile.h" +#if HAVE_STDBOOL_H +# include +#endif + #include #include @@ -1610,40 +1614,29 @@ static XS (Collectd_plugin_unregister_ds) static XS (Collectd_plugin_dispatch_values) { SV *values = NULL; - int values_idx = 0; int ret = 0; dXSARGS; - if (2 == items) { - log_warn ("Collectd::plugin_dispatch_values with two arguments " - "is deprecated - pass the type through values->{type}."); - values_idx = 1; - } - else if (1 != items) { + if (1 != items) { log_err ("Usage: Collectd::plugin_dispatch_values(values)"); XSRETURN_EMPTY; } log_debug ("Collectd::plugin_dispatch_values: values=\"%s\"", - SvPV_nolen (ST (values_idx))); + SvPV_nolen (ST (/* stack index = */ 0))); - values = ST (values_idx); + values = ST (/* stack index = */ 0); + /* Make sure the argument is a hash reference. */ if (! (SvROK (values) && (SVt_PVHV == SvTYPE (SvRV (values))))) { log_err ("Collectd::plugin_dispatch_values: Invalid values."); XSRETURN_EMPTY; } - if (((2 == items) && (NULL == ST (0))) || (NULL == values)) - XSRETURN_EMPTY; - - if ((2 == items) && (NULL == hv_store ((HV *)SvRV (values), "type", 4, - newSVsv (ST (0)), 0))) { - log_err ("Collectd::plugin_dispatch_values: Could not store type."); + if (NULL == values) XSRETURN_EMPTY; - } ret = pplugin_dispatch_values (aTHX_ (HV *)SvRV (values)); diff --git a/src/plugin.c b/src/plugin.c index 4a3c9171..65d3875e 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); } @@ -1649,7 +1654,7 @@ static int plugin_notification_meta_add (notification_t *n, } case NM_TYPE_BOOLEAN: { - meta->nm_value.nm_boolean = *((bool *) value); + meta->nm_value.nm_boolean = *((_Bool *) value); break; } default: @@ -1703,7 +1708,7 @@ int plugin_notification_meta_add_double (notification_t *n, int plugin_notification_meta_add_boolean (notification_t *n, const char *name, - bool value) + _Bool value) { return (plugin_notification_meta_add (n, name, NM_TYPE_BOOLEAN, &value)); } diff --git a/src/plugin.h b/src/plugin.h index 8b9449ee..d78aa4f8 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -135,7 +135,7 @@ typedef struct notification_meta_s int64_t nm_signed_int; uint64_t nm_unsigned_int; double nm_double; - bool nm_boolean; + _Bool nm_boolean; } nm_value; struct notification_meta_s *next; } notification_meta_t; @@ -340,7 +340,7 @@ int plugin_notification_meta_add_double (notification_t *n, double value); int plugin_notification_meta_add_boolean (notification_t *n, const char *name, - bool value); + _Bool value); int plugin_notification_meta_copy (notification_t *dst, const notification_t *src); diff --git a/src/postgresql.c b/src/postgresql.c index dd53cb4f..175cc095 100644 --- a/src/postgresql.c +++ b/src/postgresql.c @@ -737,8 +737,7 @@ static int c_psql_config (oconfig_item_t *ci) if (0 == strcasecmp (c->key, "Query")) udb_query_create (&queries, &queries_num, c, - /* callback = */ config_query_callback, - /* legacy mode = */ 1); + /* callback = */ config_query_callback); else if (0 == strcasecmp (c->key, "Database")) c_psql_config_database (c); else 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/rrdtool.c b/src/rrdtool.c index 4655b96e..cb8ad593 100644 --- a/src/rrdtool.c +++ b/src/rrdtool.c @@ -303,7 +303,7 @@ static void *rrd_queue_thread (void __attribute__((unused)) *data) pthread_mutex_lock (&queue_lock); /* Wait for values to arrive */ - while (true) + while (42) { struct timespec ts_wait; @@ -342,7 +342,7 @@ static void *rrd_queue_thread (void __attribute__((unused)) *data) &ts_wait); if (status == ETIMEDOUT) break; - } /* while (true) */ + } /* while (42) */ /* XXX: If you need to lock both, cache_lock and queue_lock, at * the same time, ALWAYS lock `cache_lock' first! */ diff --git a/src/ted.c b/src/ted.c index 8dc00e5a..bf519bbe 100644 --- a/src/ted.c +++ b/src/ted.c @@ -271,7 +271,6 @@ static void ted_submit (char *type, double value) values[0].gauge = value; - vl.time = time (NULL); vl.values = values; vl.values_len = 1; sstrncpy (vl.host, hostname_g, sizeof (vl.host)); diff --git a/src/types.db b/src/types.db index 7a962f0b..fc6348fe 100644 --- a/src/types.db +++ b/src/types.db @@ -27,6 +27,8 @@ 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 +current_sessions value:GAUGE:0:U delay seconds:GAUGE:-1000000:1000000 derive value:DERIVE:0:U df used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623 @@ -99,8 +101,6 @@ mysql_handler value:COUNTER:0:U mysql_locks value:COUNTER:0:U mysql_log_position value:COUNTER:0:4294967295 mysql_octets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 -mysql_qcache hits:COUNTER:0:U, inserts:COUNTER:0:U, not_cached:COUNTER:0:U, lowmem_prunes:COUNTER:0:U, queries_in_cache:GAUGE:0:U -mysql_threads running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created:COUNTER:0:U nfs_procedure value:COUNTER:0:4294967295 nginx_connections value:GAUGE:0:U nginx_requests value:COUNTER:0:134217728 @@ -157,6 +157,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 +172,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_db_query.c b/src/utils_db_query.c index 7d594d81..78c8052e 100644 --- a/src/utils_db_query.c +++ b/src/utils_db_query.c @@ -39,15 +39,6 @@ struct udb_result_s char **values; size_t values_num; - /* Legacy data */ - int legacy_mode; - size_t legacy_position; - /* When in legacy mode: - * - type/ds hold the format of the data - * - instance_prefix is used as type-instance if non-NULL - * - legacy_position holds the index of the column to use as value. - */ - udb_result_t *next; }; /* }}} */ @@ -57,8 +48,6 @@ struct udb_query_s /* {{{ */ char *statement; void *user_data; - int legacy_mode; - unsigned int min_version; unsigned int max_version; @@ -191,173 +180,6 @@ static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */ } /* }}} int udb_config_set_uint */ /* - * Legacy result private functions - */ -static void udb_legacy_result_finish_result (const udb_result_t const *r, /* {{{ */ - udb_result_preparation_area_t *prep_area) -{ - if ((r == NULL) || (prep_area)) - return; - - assert (r->legacy_mode == 1); - - prep_area->ds = NULL; -} /* }}} void udb_legacy_result_finish_result */ - -static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */ - udb_query_preparation_area_t *q_area, - udb_result_preparation_area_t *r_area, - const udb_query_t const *q, char **column_values) -{ - value_list_t vl = VALUE_LIST_INIT; - value_t value; - char *value_str; - - assert (r->legacy_mode == 1); - assert (r_area->ds != NULL); - assert (r_area->ds->ds_num == 1); - - vl.values = &value; - vl.values_len = 1; - - value_str = column_values[r->legacy_position]; - if (0 != parse_value (value_str, &vl.values[0], r_area->ds->ds[0].type)) - { - ERROR ("db query utils: udb_legacy_result_handle_result: " - "Parsing `%s' as %s failed.", value_str, - DS_TYPE_TO_STRING (r_area->ds->ds[0].type)); - errno = EINVAL; - return (-1); - } - - if (q_area->interval > 0) - vl.interval = q_area->interval; - - sstrncpy (vl.host, q_area->host, sizeof (vl.host)); - sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin)); - sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance)); - sstrncpy (vl.type, r->type, sizeof (vl.type)); - - if (r->instance_prefix != NULL) - sstrncpy (vl.type_instance, r->instance_prefix, - sizeof (vl.type_instance)); - - plugin_dispatch_values (&vl); - - return (0); -} /* }}} int udb_legacy_result_handle_result */ - -static int udb_legacy_result_prepare_result (const udb_result_t const *r, /* {{{ */ - udb_result_preparation_area_t *prep_area, - char **column_names, size_t column_num) -{ - if (r == NULL) - return (-EINVAL); - - assert (r->legacy_mode == 1); - - /* Make sure previous preparations are cleaned up. */ - udb_legacy_result_finish_result (r, prep_area); - - if (r->legacy_position >= column_num) - { - ERROR ("db query utils: The legacy configuration specified (at least) " - "%zu `Column's, but the query returned only %zu columns!", - r->legacy_position + 1, column_num); - return (-ENOENT); - } - - /* Read `ds' and check number of values {{{ */ - prep_area->ds = plugin_get_ds (r->type); - if (prep_area->ds == NULL) - { - ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not " - "known by the daemon. See types.db(5) for details.", - r->type); - return (-1); - } - - if (prep_area->ds->ds_num != 1) - { - ERROR ("db query utils: udb_result_prepare_result: The type `%s' " - "requires exactly %i values, but the legacy configuration " - "requires exactly one!", - r->type, - prep_area->ds->ds_num); - return (-1); - } - /* }}} */ - - return (0); -} /* }}} int udb_legacy_result_prepare_result */ - -static int udb_legacy_result_create (const char *query_name, /* {{{ */ - udb_result_t **r_head, oconfig_item_t *ci, size_t position) -{ - udb_result_t *r; - - if ((ci->values_num < 1) || (ci->values_num > 2) - || (ci->values[0].type != OCONFIG_TYPE_STRING) - || ((ci->values_num == 2) - && (ci->values[1].type != OCONFIG_TYPE_STRING))) - { - WARNING ("db query utils: The `Column' block needs either one or two " - "string arguments."); - return (-1); - } - - r = (udb_result_t *) malloc (sizeof (*r)); - if (r == NULL) - { - ERROR ("db query utils: malloc failed."); - return (-1); - } - memset (r, 0, sizeof (*r)); - - r->legacy_mode = 1; - r->legacy_position = position; - - r->type = strdup (ci->values[0].value.string); - if (r->type == NULL) - { - ERROR ("db query utils: strdup failed."); - free (r); - return (-1); - } - - r->instance_prefix = NULL; - if (ci->values_num == 2) - { - r->instance_prefix = strdup (ci->values[1].value.string); - if (r->instance_prefix == NULL) - { - ERROR ("db query utils: strdup failed."); - free (r->type); - free (r); - return (-1); - } - } - - /* If all went well, add this result to the list of results. */ - if (*r_head == NULL) - { - *r_head = r; - } - else - { - udb_result_t *last; - - last = *r_head; - while (last->next != NULL) - last = last->next; - - last->next = r; - } - - return (0); -} /* }}} int udb_legacy_result_create */ - -/* * Result private functions */ static int udb_result_submit (udb_result_t *r, /* {{{ */ @@ -368,7 +190,6 @@ static int udb_result_submit (udb_result_t *r, /* {{{ */ size_t i; assert (r != NULL); - assert (r->legacy_mode == 0); assert (r_area->ds != NULL); assert (((size_t) r_area->ds->ds_num) == r->values_num); @@ -444,14 +265,6 @@ static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */ if ((r == NULL) || (prep_area == NULL)) return; - if (r->legacy_mode == 1) - { - udb_legacy_result_finish_result (r, prep_area); - return; - } - - assert (r->legacy_mode == 0); - prep_area->ds = NULL; sfree (prep_area->instances_pos); sfree (prep_area->values_pos); @@ -468,12 +281,6 @@ static int udb_result_handle_result (udb_result_t *r, /* {{{ */ assert (r && q_area && r_area); - if (r->legacy_mode == 1) - return (udb_legacy_result_handle_result (r, q_area, r_area, - q, column_values)); - - assert (r->legacy_mode == 0); - for (i = 0; i < r->instances_num; i++) r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]]; @@ -492,12 +299,6 @@ static int udb_result_prepare_result (const udb_result_t const *r, /* {{{ */ if ((r == NULL) || (prep_area == NULL)) return (-EINVAL); - if (r->legacy_mode == 1) - return (udb_legacy_result_prepare_result (r, prep_area, - column_names, column_num)); - - assert (r->legacy_mode == 0); - #define BAIL_OUT(status) \ prep_area->ds = NULL; \ sfree (prep_area->instances_pos); \ @@ -759,7 +560,7 @@ void udb_query_free_one (udb_query_t *q) /* {{{ */ */ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */ size_t *ret_query_list_len, oconfig_item_t *ci, - udb_query_create_callback_t cb, int legacy_mode) + udb_query_create_callback_t cb) { udb_query_t **query_list; size_t query_list_len; @@ -768,8 +569,6 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */ int status; int i; - size_t legacy_position; - if ((ret_query_list == NULL) || (ret_query_list_len == NULL)) return (-EINVAL); query_list = *ret_query_list; @@ -790,12 +589,9 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */ return (-1); } memset (q, 0, sizeof (*q)); - q->legacy_mode = legacy_mode; q->min_version = 0; q->max_version = UINT_MAX; - legacy_position = 0; - status = udb_config_set_string (&q->name, ci); if (status != 0) { @@ -817,42 +613,6 @@ int udb_query_create (udb_query_t ***ret_query_list, /* {{{ */ else if (strcasecmp ("MaxVersion", child->key) == 0) status = udb_config_set_uint (&q->max_version, child); - /* PostgreSQL compatibility code */ - else if ((strcasecmp ("Query", child->key) == 0) - && (q->legacy_mode == 1)) - { - WARNING ("db query utils: Query `%s': The `Query' option is " - "deprecated. Please use `Statement' instead.", - q->name); - status = udb_config_set_string (&q->statement, child); - } - else if ((strcasecmp ("Column", child->key) == 0) - && (q->legacy_mode == 1)) - { - WARNING ("db query utils: Query `%s': The `Column' option is " - "deprecated. Please use the new syntax instead.", - q->name); - status = udb_legacy_result_create (q->name, &q->results, child, - legacy_position); - legacy_position++; - } - else if ((strcasecmp ("MinPGVersion", child->key) == 0) - && (q->legacy_mode == 1)) - { - WARNING ("db query utils: Query `%s': The `MinPGVersion' option is " - "deprecated. Please use `MinVersion' instead.", - q->name); - status = udb_config_set_uint (&q->min_version, child); - } - else if ((strcasecmp ("MaxPGVersion", child->key) == 0) - && (q->legacy_mode == 1)) - { - WARNING ("db query utils: Query `%s': The `MaxPGVersion' option is " - "deprecated. Please use `MaxVersion' instead.", - q->name); - status = udb_config_set_uint (&q->max_version, child); - } - /* Call custom callbacks */ else if (cb != NULL) { diff --git a/src/utils_db_query.h b/src/utils_db_query.h index fa2b2885..846f81c2 100644 --- a/src/utils_db_query.h +++ b/src/utils_db_query.h @@ -41,7 +41,7 @@ typedef int (*udb_query_create_callback_t) (udb_query_t *q, */ int udb_query_create (udb_query_t ***ret_query_list, size_t *ret_query_list_len, oconfig_item_t *ci, - udb_query_create_callback_t cb, int legacy_mode); + udb_query_create_callback_t cb); void udb_query_free (udb_query_t **query_list, size_t query_list_len); int udb_query_pick_from_list_by_name (const char *name, 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 : */