From: Florian Forster Date: Sat, 6 Nov 2010 11:11:23 +0000 (+0100) Subject: Merge branch 'collectd-4.10' X-Git-Tag: collectd-5.0.0-beta0~24 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=a898c17330d9a2039bcdb8f7e6dbedba516a6cd8;hp=171d6a4bd5e05b56be83860701d35bfe1373d6a7;p=collectd.git Merge branch 'collectd-4.10' --- 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/AUTHORS b/AUTHORS index 45689652..c57f90b2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -34,6 +34,10 @@ Anthony Gialluca Antony Dovgal - memcached plugin. +Aurélien Reynaud + - LPAR plugin. + - Various fixes for AIX, HP-UX and Solaris. + Bruno Prémont - BIND plugin. - Many bugreports and -fixes in various plugins, @@ -72,6 +76,9 @@ Franck Lombardi Jason Pepas - nfs plugin. +Jérôme Renard + - varnish plugin. + Luboš Staněk - sensors plugin improvements. - Time and effort to find a nasty bug in the ntpd-plugin. @@ -159,6 +166,9 @@ Rodolphe Quiédeville Scott Garrett - tape plugin. +Sebastien Pahl + - AMQP plugin. + Simon Kuhnle - OpenBSD code for the cpu and memory plugins. diff --git a/README b/README index 50630690..2ed8934f 100644 --- a/README +++ b/README @@ -125,6 +125,10 @@ Features - load System load average over the last 1, 5 and 15 minutes. + - lpar + Detailed CPU statistics of the “Logical Partitions” virtualization + technique built into IBM's POWER processors. + - libvirt CPU, disk and network I/O statistics from virtual machines. @@ -235,6 +239,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. @@ -288,6 +296,9 @@ Features - users Users currently logged in. + - varnish + Various statistics from Varnish, an HTTP accelerator. + - vmem Virtual memory statistics, e. g. the number of page-ins/-outs or the number of pagefaults. @@ -308,6 +319,10 @@ Features * Output can be written or sent to various destinations by the following plugins: + - amqp + Sends JSON-encoded data to an Advanced Message Queuing Protocol (AMQP) + server, such as RabbitMQ. + - csv Write to comma separated values (CSV) files. This needs lots of diskspace but is extremely portable and can be analysed with almost @@ -504,6 +519,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. @@ -607,6 +626,10 @@ Prerequisites Used by the `python' plugin. Currently, only 2.3 ≦ Python < 3 is supported. + * librabbitmq (optional; also called “rabbitmq-c”) + Used by the AMQP plugin for AMQP connections, for example to RabbitMQ. + + * librouteros (optional) Used by the `routeros' plugin to connect to a device running `RouterOS'. @@ -653,6 +676,10 @@ Prerequisites Parse JSON data. This is needed for the `curl_json' plugin. + * libvarnish (optional) + Fetches statistics from a Varnish instance. This is needed for the Varnish plugin + + Configuring / Compiling / Installing ------------------------------------ diff --git a/bindings/java/org/collectd/java/GenericJMXConfConnection.java b/bindings/java/org/collectd/java/GenericJMXConfConnection.java index ffa9ded4..0c81bc9a 100644 --- a/bindings/java/org/collectd/java/GenericJMXConfConnection.java +++ b/bindings/java/org/collectd/java/GenericJMXConfConnection.java @@ -1,6 +1,6 @@ /* * collectd/java - org/collectd/java/GenericJMXConfConnection.java - * Copyright (C) 2009 Florian octo Forster + * Copyright (C) 2009,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 @@ -45,6 +45,7 @@ class GenericJMXConfConnection private String _username = null; private String _password = null; private String _host = null; + private String _instance_prefix = null; private String _service_url = null; private MBeanServerConnection _jmx_connection = null; private List _mbeans = null; @@ -162,6 +163,12 @@ private void connect () /* {{{ */ if (tmp != null) this._service_url = tmp; } + else if (child.getKey ().equalsIgnoreCase ("InstancePrefix")) + { + String tmp = getConfigString (child); + if (tmp != null) + this._instance_prefix = tmp; + } else if (child.getKey ().equalsIgnoreCase ("Collect")) { String tmp = getConfigString (child); @@ -211,7 +218,8 @@ private void connect () /* {{{ */ { int status; - status = this._mbeans.get (i).query (this._jmx_connection, pd); + status = this._mbeans.get (i).query (this._jmx_connection, pd, + this._instance_prefix); if (status != 0) { this._jmx_connection = null; diff --git a/bindings/java/org/collectd/java/GenericJMXConfMBean.java b/bindings/java/org/collectd/java/GenericJMXConfMBean.java index 1587bd5f..b1fbfb3e 100644 --- a/bindings/java/org/collectd/java/GenericJMXConfMBean.java +++ b/bindings/java/org/collectd/java/GenericJMXConfMBean.java @@ -1,6 +1,6 @@ /* * collectd/java - org/collectd/java/GenericJMXConfMBean.java - * Copyright (C) 2009 Florian octo Forster + * Copyright (C) 2009,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 @@ -67,22 +67,6 @@ class GenericJMXConfMBean return (v.getString ()); } /* }}} String getConfigString */ - private String join (String separator, List list) /* {{{ */ - { - StringBuffer sb; - - sb = new StringBuffer (); - - for (int i = 0; i < list.size (); i++) - { - if (i > 0) - sb.append ("-"); - sb.append (list.get (i)); - } - - return (sb.toString ()); - } /* }}} String join */ - /* * * ObjectName "object name" @@ -170,7 +154,8 @@ class GenericJMXConfMBean return (this._name); } /* }}} */ - public int query (MBeanServerConnection conn, PluginData pd) /* {{{ */ + public int query (MBeanServerConnection conn, PluginData pd, /* {{{ */ + String instance_prefix) { Set names; Iterator iter; @@ -197,11 +182,12 @@ class GenericJMXConfMBean ObjectName objName; PluginData pd_tmp; List instanceList; - String instance; + StringBuffer instance; objName = iter.next (); pd_tmp = new PluginData (pd); instanceList = new ArrayList (); + instance = new StringBuffer (); Collectd.logDebug ("GenericJMXConfMBean: objName = " + objName.toString ()); @@ -224,14 +210,22 @@ class GenericJMXConfMBean } } + if (instance_prefix != null) + instance.append (instance_prefix); + if (this._instance_prefix != null) - instance = new String (this._instance_prefix - + join ("-", instanceList)); - else - instance = join ("-", instanceList); - pd_tmp.setPluginInstance (instance); + instance.append (this._instance_prefix); + + for (int i = 0; i < instanceList.size (); i++) + { + if (i > 0) + instance.append ("-"); + instance.append (instanceList.get (i)); + } + + pd_tmp.setPluginInstance (instance.toString ()); - Collectd.logDebug ("GenericJMXConfMBean: instance = " + instance); + Collectd.logDebug ("GenericJMXConfMBean: instance = " + instance.toString ()); for (int i = 0; i < this._values.size (); i++) this._values.get (i).query (conn, objName, pd_tmp); 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 a47fed03..5d82c47c 100644 --- a/configure.in +++ b/configure.in @@ -108,9 +108,13 @@ AC_ARG_ENABLE(standards, if test "x$enable_standards" = "xyes" then AC_DEFINE(_ISOC99_SOURCE, 1, [Define to enforce ISO C99 compliance.]) - AC_DEFINE(_POSIX_C_SOURCE, 200112L, [Define to enforce POSIX.1-2001 compliance.]) - AC_DEFINE(_XOPEN_SOURCE, 600, [Define to enforce X/Open 6 (XSI) compliance.]) + AC_DEFINE(_POSIX_C_SOURCE, 200809L, [Define to enforce POSIX.1-2008 compliance.]) + AC_DEFINE(_XOPEN_SOURCE, 700, [Define to enforce X/Open 7 (XSI) compliance.]) AC_DEFINE(_REENTRANT, 1, [Define to enable reentrancy interfaces.]) + if test "x$GCC" = "xyes" + then + CFLAGS="$CFLAGS -std=c99" + fi fi AM_CONDITIONAL(BUILD_FEATURE_STANDARDS, test "x$enable_standards" = "xyes") @@ -592,6 +596,55 @@ AC_CHECK_FUNCS(syslog, [have_syslog="yes"], [have_syslog="no"]) AC_CHECK_FUNCS(getutent, [have_getutent="yes"], [have_getutent="no"]) AC_CHECK_FUNCS(getutxent, [have_getutxent="yes"], [have_getutxent="no"]) AC_CHECK_FUNCS(swapctl, [have_swapctl="yes"], [have_swapctl="no"]) +if test "x$have_swapctl" = "xyes"; then + AC_CACHE_CHECK([whether swapctl takes two arguments], + [c_cv_have_swapctl_two_args], + AC_COMPILE_IFELSE( + AC_LANG_PROGRAM([[AC_INCLUDES_DEFAULT +#if HAVE_SYS_SWAP_H && !defined(_LP64) && _FILE_OFFSET_BITS == 64 +# undef _FILE_OFFSET_BITS +# undef _LARGEFILE64_SOURCE +#endif +#include +#include ]], + [[ + int num = swapctl(0, NULL); + ]] + ), + [c_cv_have_swapctl_two_args="yes"], + [c_cv_have_swapctl_two_args="no"] + ) + ) + AC_CACHE_CHECK([whether swapctl takes three arguments], + [c_cv_have_swapctl_three_args], + AC_COMPILE_IFELSE( + AC_LANG_PROGRAM([[AC_INCLUDES_DEFAULT +#if HAVE_SYS_SWAP_H && !defined(_LP64) && _FILE_OFFSET_BITS == 64 +# undef _FILE_OFFSET_BITS +# undef _LARGEFILE64_SOURCE +#endif +#include +#include ]], + [[ + int num = swapctl(0, NULL,0); + ]] + ), + [c_cv_have_swapctl_three_args="yes"], + [c_cv_have_swapctl_three_args="no"] + ) + ) +fi +# Check for different versions of `swapctl' here.. +if test "x$have_swapctl" = "xyes"; then + if test "x$c_cv_have_swapctl_two_args" = "xyes"; then + AC_DEFINE(HAVE_SWAPCTL_TWO_ARGS, 1, + [Define if the function swapctl exists and takes two arguments.]) + fi + if test "x$c_cv_have_swapctl_three_args" = "xyes"; then + AC_DEFINE(HAVE_SWAPCTL_THREE_ARGS, 1, + [Define if the function swapctl exists and takes three arguments.]) + fi +fi # For load module AC_CHECK_FUNCS(getloadavg, [have_getloadavg="yes"], [have_getloadavg="no"]) @@ -1144,6 +1197,12 @@ fi if test "x$with_perfstat" = "xyes" then AC_DEFINE(HAVE_PERFSTAT, 1, [Define to 1 if you have the 'perfstat' library (-lperfstat)]) + # struct members pertaining to donation have been added to libperfstat somewhere between AIX5.3ML5 and AIX5.3ML9 + AC_CHECK_MEMBER([perfstat_partition_type_t.b.donate_enabled], [], [], [[#include @], [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="" @@ -3002,6 +3119,57 @@ then fi # }}} --with-python +# --with-librabbitmq {{{ +with_librabbitmq_cppflags="" +with_librabbitmq_ldflags="" +AC_ARG_WITH(librabbitmq, [AS_HELP_STRING([--with-librabbitmq@<:@=PREFIX@:>@], [Path to librabbitmq.])], +[ + if test "x$withval" != "xno" && test "x$withval" != "xyes" + then + with_librabbitmq_cppflags="-I$withval/include" + with_librabbitmq_ldflags="-L$withval/lib" + with_librabbitmq="yes" + else + with_librabbitmq="$withval" + fi +], +[ + with_librabbitmq="yes" +]) +if test "x$with_librabbitmq" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_librabbitmq_cppflags" + + AC_CHECK_HEADERS(amqp.h, [with_librabbitmq="yes"], [with_librabbitmq="no (amqp.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi +if test "x$with_librabbitmq" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + SAVE_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS $with_librabbitmq_cppflags" + LDFLAGS="$LDFLAGS $with_librabbitmq_ldflags" + + AC_CHECK_LIB(rabbitmq, amqp_basic_publish, [with_librabbitmq="yes"], [with_librabbitmq="no (Symbol 'amqp_basic_publish' not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" + LDFLAGS="$SAVE_LDFLAGS" +fi +if test "x$with_librabbitmq" = "xyes" +then + BUILD_WITH_LIBRABBITMQ_CPPFLAGS="$with_librabbitmq_cppflags" + BUILD_WITH_LIBRABBITMQ_LDFLAGS="$with_librabbitmq_ldflags" + BUILD_WITH_LIBRABBITMQ_LIBS="-lrabbitmq" + AC_SUBST(BUILD_WITH_LIBRABBITMQ_CPPFLAGS) + AC_SUBST(BUILD_WITH_LIBRABBITMQ_LDFLAGS) + AC_SUBST(BUILD_WITH_LIBRABBITMQ_LIBS) + AC_DEFINE(HAVE_LIBRABBITMQ, 1, [Define if librabbitmq is present and usable.]) +fi +AM_CONDITIONAL(BUILD_WITH_LIBRABBITMQ, test "x$with_librabbitmq" = "xyes") +# }}} + # --with-librouteros {{{ AC_ARG_WITH(librouteros, [AS_HELP_STRING([--with-librouteros@<:@=PREFIX@:>@], [Path to librouteros.])], [ @@ -3641,6 +3809,98 @@ fi AM_CONDITIONAL(BUILD_WITH_LIBYAJL, test "x$with_libyajl" = "xyes") # }}} +# --with-libvarnish {{{ +with_libvarnish_cppflags="" +with_libvarnish_cflags="" +with_libvarnish_libs="" +AC_ARG_WITH(libvarnish, [AS_HELP_STRING([--with-libvarnish@<:@=PREFIX@:>@], [Path to libvarnish.])], +[ + if test "x$withval" = "xno" + then + with_libvarnish="no" + else if test "x$withval" = "xyes" + then + with_libvarnish="use_pkgconfig" + else if test -d "$with_libvarnish/lib" + then + AC_MSG_NOTICE([Not checking for libvarnish: Manually configured]) + with_libvarnish_cflags="-I$withval/include" + with_libvarnish_libs="-L$withval/lib -lvarnish -lvarnishcompat -lvarnishapi" + with_libvarnish="yes" + fi; fi; fi +], +[with_libvarnish="use_pkgconfig"]) + +# configure using pkg-config +if test "x$with_libvarnish" = "xuse_pkgconfig" +then + if test "x$PKG_CONFIG" = "x" + then + with_libvarnish="no (Don't have pkg-config)" + fi +fi +if test "x$with_libvarnish" = "xuse_pkgconfig" +then + AC_MSG_NOTICE([Checking for varnishapi using $PKG_CONFIG]) + $PKG_CONFIG --exists 'varnishapi' 2>/dev/null + if test $? -ne 0 + then + with_libvarnish="no (pkg-config doesn't know library)" + fi +fi +if test "x$with_libvarnish" = "xuse_pkgconfig" +then + with_libvarnish_cflags="`$PKG_CONFIG --cflags 'varnishapi'`" + if test $? -ne 0 + then + with_libvarnish="no ($PKG_CONFIG failed)" + fi + with_libvarnish_libs="`$PKG_CONFIG --libs 'varnishapi'`" + if test $? -ne 0 + then + with_libvarnish="no ($PKG_CONFIG failed)" + fi +fi +if test "x$with_libvarnish" = "xuse_pkgconfig" +then + with_libvarnish="yes" +fi + +# with_libvarnish_cflags and with_libvarnish_libs are set up now, let's do +# the actual checks. +if test "x$with_libvarnish" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $with_libvarnish_cflags" + AC_CHECK_HEADERS(varnish/varnishapi.h, [], [with_libvarnish="no (varnish/varnishapi.h not found)"]) + + CPPFLAGS="$SAVE_CPPFLAGS" +fi +if test "x$with_libvarnish" = "xyes" +then + SAVE_CPPFLAGS="$CPPFLAGS" + #SAVE_LDFLAGS="$LDFLAGS" + + CPPFLAGS="$CPPFLAGS $with_libvarnish_cflags" + #LDFLAGS="$LDFLAGS $with_libvarnish_libs" + + AC_CHECK_LIB(varnishapi, VSL_OpenStats, + [with_libvarnish="yes"], + [with_libvarnish="no (symbol VSL_OpenStats not found)"], + [$with_libvarnish_libs]) + + CPPFLAGS="$SAVE_CPPFLAGS" + #LDFLAGS="$SAVE_LDFLAGS" +fi +if test "x$with_libvarnish" = "xyes" +then + BUILD_WITH_LIBVARNISH_CFLAGS="$with_libvarnish_cflags" + BUILD_WITH_LIBVARNISH_LIBS="$with_libvarnish_libs" + AC_SUBST(BUILD_WITH_LIBVARNISH_CFLAGS) + AC_SUBST(BUILD_WITH_LIBVARNISH_LIBS) +fi +# }}} + # pkg-config --exists 'libxml-2.0'; pkg-config --exists libvirt {{{ with_libxml2="no (pkg-config isn't available)" with_libxml2_cflags="" @@ -4135,6 +4395,7 @@ then fi if test "x$have_sysctlbyname" = "xyes" then + plugin_contextswitch="yes" plugin_cpu="yes" plugin_memory="yes" plugin_tcpconns="yes" @@ -4262,6 +4523,7 @@ AC_ARG_ENABLE([all-plugins], m4_divert_once([HELP_ENABLE], []) +AC_PLUGIN([amqp], [$with_librabbitmq], [AMQP output plugin]) AC_PLUGIN([apache], [$with_libcurl], [Apache httpd statistics]) AC_PLUGIN([apcups], [yes], [Statistics of UPSes by APC]) AC_PLUGIN([apple_sensors], [$with_libiokit], [Apple's hardware sensors]) @@ -4296,6 +4558,7 @@ AC_PLUGIN([java], [$with_java], [Embed the Java Virtual Machine]) AC_PLUGIN([libvirt], [$plugin_libvirt], [Virtual machine statistics]) AC_PLUGIN([load], [$plugin_load], [System load]) AC_PLUGIN([logfile], [yes], [File logging plugin]) +AC_PLUGIN([lpar], [$with_perfstat], [AIX logical partitions statistics]) AC_PLUGIN([madwifi], [$have_linux_wireless_h], [Madwifi wireless statistics]) AC_PLUGIN([match_empty_counter], [yes], [The empty counter match]) AC_PLUGIN([match_hashed], [yes], [The hashed match]) @@ -4331,6 +4594,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]) @@ -4346,6 +4610,7 @@ AC_PLUGIN([target_notification], [yes], [The notification target]) AC_PLUGIN([target_replace], [yes], [The replace target]) AC_PLUGIN([target_scale],[yes], [The scale target]) AC_PLUGIN([target_set], [yes], [The set target]) +AC_PLUGIN([target_v5upgrade], [yes], [The v5upgrade target]) AC_PLUGIN([tcpconns], [$plugin_tcpconns], [TCP connection statistics]) AC_PLUGIN([teamspeak2], [yes], [TeamSpeak2 server statistics]) AC_PLUGIN([ted], [$plugin_ted], [Read The Energy Detective values]) @@ -4355,10 +4620,12 @@ AC_PLUGIN([unixsock], [yes], [Unixsock communication plugin]) AC_PLUGIN([uptime], [$plugin_uptime], [Uptime statistics]) AC_PLUGIN([users], [$plugin_users], [User statistics]) AC_PLUGIN([uuid], [yes], [UUID as hostname plugin]) +AC_PLUGIN([varnish], [$with_libvarnish], [Varnish cache statistics]) 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]) @@ -4533,6 +4800,7 @@ Configuration: Libraries: libcurl . . . . . . . $with_libcurl libdbi . . . . . . . $with_libdbi + libcredis . . . . . . $with_libcredis libesmtp . . . . . . $with_libesmtp libganglia . . . . . $with_libganglia libgcrypt . . . . . . $with_libgcrypt @@ -4556,12 +4824,14 @@ Configuration: libperl . . . . . . . $with_libperl libpq . . . . . . . . $with_libpq libpthread . . . . . $with_libpthread + librabbitmq . . . . . $with_librabbitmq librouteros . . . . . $with_librouteros librrd . . . . . . . $with_librrd libsensors . . . . . $with_libsensors libstatgrab . . . . . $with_libstatgrab libtokyotyrant . . . $with_libtokyotyrant libupsclient . . . . $with_libupsclient + libvarnish . . . . . $with_libvarnish libvirt . . . . . . . $with_libvirt libxml2 . . . . . . . $with_libxml2 libxmms . . . . . . . $with_libxmms @@ -4579,6 +4849,7 @@ Configuration: perl . . . . . . . . $with_perl_bindings Modules: + amqp . . . . . . . $enable_amqp apache . . . . . . . $enable_apache apcups . . . . . . . $enable_apcups apple_sensors . . . . $enable_apple_sensors @@ -4613,6 +4884,7 @@ Configuration: libvirt . . . . . . . $enable_libvirt load . . . . . . . . $enable_load logfile . . . . . . . $enable_logfile + lpar... . . . . . . . $enable_lpar madwifi . . . . . . . $enable_madwifi match_empty_counter . $enable_match_empty_counter match_hashed . . . . $enable_match_hashed @@ -4647,6 +4919,7 @@ Configuration: processes . . . . . . $enable_processes protocols . . . . . . $enable_protocols python . . . . . . . $enable_python + redis . . . . . . . . $enable_redis routeros . . . . . . $enable_routeros rrdcached . . . . . . $enable_rrdcached rrdtool . . . . . . . $enable_rrdtool @@ -4662,6 +4935,7 @@ Configuration: target_replace . . . $enable_target_replace target_scale . . . . $enable_target_scale target_set . . . . . $enable_target_set + target_v5upgrade . . $enable_target_v5upgrade tcpconns . . . . . . $enable_tcpconns teamspeak2 . . . . . $enable_teamspeak2 ted . . . . . . . . . $enable_ted @@ -4671,10 +4945,12 @@ Configuration: uptime . . . . . . . $enable_uptime users . . . . . . . . $enable_users uuid . . . . . . . . $enable_uuid + varnish . . . . . . . $enable_varnish vmem . . . . . . . . $enable_vmem 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/contrib/exec-munin.px b/contrib/exec-munin.px index 907ea9bc..3e62ce00 100755 --- a/contrib/exec-munin.px +++ b/contrib/exec-munin.px @@ -46,7 +46,8 @@ use Regexp::Common ('number'); our $ConfigFile = '/etc/exec-munin.conf'; our $TypeMap = {}; our $Scripts = []; -our $Interval = 300; +our $Interval = defined ($ENV{'COLLECTD_INTERVAL'}) ? (0 + $ENV{'COLLECTD_INTERVAL'}) : 300; +our $Hostname = defined ($ENV{'COLLECTD_HOSTNAME'}) ? $ENV{'COLLECTD_HOSTNAME'} : ''; main (); exit (0); @@ -189,7 +190,7 @@ sub execute_script my $pinst; my $time = time (); my $script = shift; - my $host = hostname () || 'localhost'; + my $host = $Hostname || hostname () || 'localhost'; if (!open ($fh, '-|', $script)) { print STDERR "Cannot execute $script: $!"; @@ -206,8 +207,11 @@ sub execute_script my $field = $1; my $value = $2; my $type = (defined ($TypeMap->{$field})) ? $TypeMap->{$field} : $field; + my $ident = "$host/munin-$pinst/$type"; - print "$host/munin-$pinst/$type interval=$Interval $time:$value\n"; + $ident =~ s/"/\\"/g; + + print qq(PUTVAL "$ident" interval=$Interval $time:$value\n); } } diff --git a/contrib/exec-nagios.px b/contrib/exec-nagios.px index 938721fc..4b112f95 100755 --- a/contrib/exec-nagios.px +++ b/contrib/exec-nagios.px @@ -24,7 +24,8 @@ use Regexp::Common ('number'); our $ConfigFile = '/etc/exec-nagios.conf'; our $TypeMap = {}; our $Scripts = []; -our $Interval = 300; +our $Interval = defined ($ENV{'COLLECTD_INTERVAL'}) ? (0 + $ENV{'COLLECTD_INTERVAL'}) : 300; +our $Hostname = defined ($ENV{'COLLECTD_HOSTNAME'}) ? $ENV{'COLLECTD_HOSTNAME'} : ''; main (); exit (0); @@ -235,6 +236,7 @@ sub handle_performance_data my $type = shift; my $time = shift; my $line = shift; + my $ident = "$host/$plugin-$pinst/$type-$tinst"; my $tinst; my $value; @@ -250,7 +252,9 @@ sub handle_performance_data return; } - print "PUTVAL $host/$plugin-$pinst/$type-$tinst interval=$Interval ${time}:$value\n"; + $ident =~ s/"/\\"/g; + + print qq(PUTVAL "$ident" interval=$Interval ${time}:$value\n); } sub execute_script @@ -260,7 +264,7 @@ sub execute_script my $time = time (); my $script = shift; my @args = (); - my $host = hostname () || 'localhost'; + my $host = $Hostname || hostname () || 'localhost'; my $state = 0; my $serviceoutput; diff --git a/contrib/exec-smartctl b/contrib/exec-smartctl index d4698160..99b69860 100755 --- a/contrib/exec-smartctl +++ b/contrib/exec-smartctl @@ -18,31 +18,29 @@ # smart ALL = (root) NOPASSWD: SMARTCTL # -- >8 -- -HOST="huhu" -INTERVAL=60 +HOSTNAME="${COLLECTD_HOSTNAME:-`hostname -f`}" +INTERVAL="${COLLECTD_INTERVAL:-60}" -while true +while sleep "$INTERVAL" do TEMP=$((sudo smartctl -d 3ware,0 -A /dev/twe0 | grep Temperature_Celsius | awk '{ print $10; }') 2>/dev/null); if [ $? -ne 0 ] then TEMP="U" fi - echo "$HOST/exec-smart/temperature-3ware_0 interval=$INTERVAL N:$TEMP" + echo "PUTVAL $HOSTNAME/exec-smart/temperature-3ware_0 interval=$INTERVAL N:$TEMP" TEMP=$((sudo smartctl -d 3ware,1 -A /dev/twe0 | grep Temperature_Celsius | awk '{ print $10; }') 2>/dev/null); if [ $? -ne 0 ] then TEMP="U" fi - echo "$HOST/exec-smart/temperature-3ware_1 interval=$INTERVAL N:$TEMP" + echo "PUTVAL $HOSTNAME/exec-smart/temperature-3ware_1 interval=$INTERVAL N:$TEMP" TEMP=$((sudo smartctl -d ata -A /dev/sda | grep Temperature_Celsius | awk '{ print $10; }') 2>/dev/null); if [ $? -ne 0 ] then TEMP="U" fi - echo "$HOST/exec-smart/temperature-sata_0 interval=$INTERVAL N:$TEMP" - - sleep $INTERVAL + echo "PUTVAL $HOSTNAME/exec-smart/temperature-sata_0 interval=$INTERVAL N:$TEMP" done diff --git a/src/Makefile.am b/src/Makefile.am index c6b0538d..247892bb 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 +bin_PROGRAMS = collectd-nagios collectdctl collectd_SOURCES = collectd.c collectd.h \ common.c common.h \ @@ -105,11 +105,36 @@ endif collectd_nagios_LDADD += libcollectdclient/libcollectdclient.la collectd_nagios_DEPENDENCIES = libcollectdclient/libcollectdclient.la + +collectdctl_SOURCES = collectdctl.c +collectdctl_LDADD = +if BUILD_WITH_LIBSOCKET +collectdctl_LDADD += -lsocket +endif +if BUILD_AIX +collectdctl_LDADD += -lm +endif +collectdctl_LDADD += libcollectdclient/libcollectdclient.la +collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la + + pkglib_LTLIBRARIES = BUILT_SOURCES = CLEANFILES = +if BUILD_PLUGIN_AMQP +pkglib_LTLIBRARIES += amqp.la +amqp_la_SOURCES = amqp.c \ + utils_cmd_putval.c utils_cmd_putval.h \ + utils_format_json.c utils_format_json.h +amqp_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBRABBITMQ_LDFLAGS) +amqp_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRABBITMQ_CPPFLAGS) +amqp_la_LIBADD = $(BUILD_WITH_LIBRABBITMQ_LIBS) +collectd_LDADD += "-dlopen" amqp.la +collectd_DEPENDENCIES += amqp.la +endif + if BUILD_PLUGIN_APACHE pkglib_LTLIBRARIES += apache.la apache_la_SOURCES = apache.c @@ -499,6 +524,15 @@ collectd_LDADD += "-dlopen" logfile.la collectd_DEPENDENCIES += logfile.la endif +if BUILD_PLUGIN_LPAR +pkglib_LTLIBRARIES += lpar.la +lpar_la_SOURCES = lpar.c +lpar_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" lpar.la +collectd_DEPENDENCIES += lpar.la +lpar_la_LIBADD = -lperfstat +endif + if BUILD_PLUGIN_MADWIFI pkglib_LTLIBRARIES += madwifi.la madwifi_la_SOURCES = madwifi.c madwifi.h @@ -885,6 +919,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 @@ -1042,6 +1086,14 @@ collectd_LDADD += "-dlopen" target_set.la collectd_DEPENDENCIES += target_set.la endif +if BUILD_PLUGIN_TARGET_V5UPGRADE +pkglib_LTLIBRARIES += target_v5upgrade.la +target_v5upgrade_la_SOURCES = target_v5upgrade.c +target_v5upgrade_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" target_v5upgrade.la +collectd_DEPENDENCIES += target_v5upgrade.la +endif + if BUILD_PLUGIN_TCPCONNS pkglib_LTLIBRARIES += tcpconns.la tcpconns_la_SOURCES = tcpconns.c @@ -1143,6 +1195,16 @@ collectd_LDADD += "-dlopen" uuid.la collectd_DEPENDENCIES += uuid.la endif +if BUILD_PLUGIN_VARNISH +pkglib_LTLIBRARIES += varnish.la +varnish_la_SOURCES = varnish.c +varnish_la_LDFLAGS = -module -avoid-version +varnish_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBVARNISH_CFLAGS) +varnish_la_LIBADD = $(BUILD_WITH_LIBVARNISH_LIBS) +collectd_LDADD += "-dlopen" varnish.la +collectd_DEPENDENCIES += varnish.la +endif + if BUILD_PLUGIN_VMEM pkglib_LTLIBRARIES += vmem.la vmem_la_SOURCES = vmem.c @@ -1182,6 +1244,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 @@ -1206,6 +1278,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 \ @@ -1222,6 +1295,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/amqp.c b/src/amqp.c new file mode 100644 index 00000000..f0abd44b --- /dev/null +++ b/src/amqp.c @@ -0,0 +1,939 @@ +/** + * collectd - src/amqp.c + * Copyright (C) 2009 Sebastien Pahl + * 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: + * Sebastien Pahl + * Florian Forster + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "utils_cmd_putval.h" +#include "utils_format_json.h" + +#include + +#include +#include + +/* Defines for the delivery mode. I have no idea why they're not defined by the + * library.. */ +#define CAMQP_DM_VOLATILE 1 +#define CAMQP_DM_PERSISTENT 2 + +#define CAMQP_FORMAT_COMMAND 1 +#define CAMQP_FORMAT_JSON 2 + +#define CAMQP_CHANNEL 1 + +/* + * Data types + */ +struct camqp_config_s +{ + _Bool publish; + char *name; + + char *host; + int port; + char *vhost; + char *user; + char *password; + + char *exchange; + char *routing_key; + + /* publish only */ + uint8_t delivery_mode; + _Bool store_rates; + int format; + + /* subscribe only */ + char *exchange_type; + char *queue; + + amqp_connection_state_t connection; + pthread_mutex_t lock; +}; +typedef struct camqp_config_s camqp_config_t; + +/* + * Global variables + */ +static const char *def_host = "localhost"; +static const char *def_vhost = "/"; +static const char *def_user = "guest"; +static const char *def_password = "guest"; +static const char *def_exchange = "amq.fanout"; + +static pthread_t *subscriber_threads = NULL; +static size_t subscriber_threads_num = 0; +static _Bool subscriber_threads_running = 1; + +#define CONF(c,f) (((c)->f != NULL) ? (c)->f : def_##f) + +/* + * Functions + */ +static void camqp_close_connection (camqp_config_t *conf) /* {{{ */ +{ + int sockfd; + + if ((conf == NULL) || (conf->connection == NULL)) + return; + + sockfd = amqp_get_sockfd (conf->connection); + amqp_channel_close (conf->connection, CAMQP_CHANNEL, AMQP_REPLY_SUCCESS); + amqp_connection_close (conf->connection, AMQP_REPLY_SUCCESS); + amqp_destroy_connection (conf->connection); + close (sockfd); + conf->connection = NULL; +} /* }}} void camqp_close_connection */ + +static void camqp_config_free (void *ptr) /* {{{ */ +{ + camqp_config_t *conf = ptr; + + if (conf == NULL) + return; + + camqp_close_connection (conf); + + sfree (conf->name); + sfree (conf->host); + sfree (conf->vhost); + sfree (conf->user); + sfree (conf->password); + sfree (conf->exchange); + sfree (conf->exchange_type); + sfree (conf->queue); + sfree (conf->routing_key); + + sfree (conf); +} /* }}} void camqp_config_free */ + +static char *camqp_bytes_cstring (amqp_bytes_t *in) /* {{{ */ +{ + char *ret; + + if ((in == NULL) || (in->bytes == NULL)) + return (NULL); + + ret = malloc (in->len + 1); + if (ret == NULL) + return (NULL); + + memcpy (ret, in->bytes, in->len); + ret[in->len] = 0; + + return (ret); +} /* }}} char *camqp_bytes_cstring */ + +static _Bool camqp_is_error (camqp_config_t *conf) /* {{{ */ +{ + amqp_rpc_reply_t r; + + r = amqp_get_rpc_reply (conf->connection); + if (r.reply_type == AMQP_RESPONSE_NORMAL) + return (0); + + return (1); +} /* }}} _Bool camqp_is_error */ + +static char *camqp_strerror (camqp_config_t *conf, /* {{{ */ + char *buffer, size_t buffer_size) +{ + amqp_rpc_reply_t r; + + r = amqp_get_rpc_reply (conf->connection); + switch (r.reply_type) + { + case AMQP_RESPONSE_NORMAL: + sstrncpy (buffer, "Success", sizeof (buffer)); + break; + + case AMQP_RESPONSE_NONE: + sstrncpy (buffer, "Missing RPC reply type", sizeof (buffer)); + break; + + case AMQP_RESPONSE_LIBRARY_EXCEPTION: + if (r.library_errno) + return (sstrerror (r.library_errno, buffer, buffer_size)); + else + sstrncpy (buffer, "End of stream", sizeof (buffer)); + break; + + case AMQP_RESPONSE_SERVER_EXCEPTION: + if (r.reply.id == AMQP_CONNECTION_CLOSE_METHOD) + { + amqp_connection_close_t *m = r.reply.decoded; + char *tmp = camqp_bytes_cstring (&m->reply_text); + ssnprintf (buffer, buffer_size, "Server connection error %d: %s", + m->reply_code, tmp); + sfree (tmp); + } + else if (r.reply.id == AMQP_CHANNEL_CLOSE_METHOD) + { + amqp_channel_close_t *m = r.reply.decoded; + char *tmp = camqp_bytes_cstring (&m->reply_text); + ssnprintf (buffer, buffer_size, "Server channel error %d: %s", + m->reply_code, tmp); + sfree (tmp); + } + else + { + ssnprintf (buffer, buffer_size, "Server error method %#"PRIx32, + r.reply.id); + } + break; + + default: + ssnprintf (buffer, buffer_size, "Unknown reply type %i", + (int) r.reply_type); + } + + return (buffer); +} /* }}} char *camqp_strerror */ + +static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */ +{ + amqp_exchange_declare_ok_t *ed_ret; + + if (conf->exchange_type == NULL) + return (0); + + ed_ret = amqp_exchange_declare (conf->connection, + /* channel = */ CAMQP_CHANNEL, + /* exchange = */ amqp_cstring_bytes (conf->exchange), + /* type = */ amqp_cstring_bytes (conf->exchange_type), + /* passive = */ 0, + /* durable = */ 0, + /* auto_delete = */ 1, + /* arguments = */ AMQP_EMPTY_TABLE); + if ((ed_ret == NULL) && camqp_is_error (conf)) + { + char errbuf[1024]; + ERROR ("amqp plugin: amqp_exchange_declare failed: %s", + camqp_strerror (conf, errbuf, sizeof (errbuf))); + camqp_close_connection (conf); + return (-1); + } + + INFO ("amqp plugin: Successfully created exchange \"%s\" " + "with type \"%s\".", + conf->exchange, conf->exchange_type); + + return (0); +} /* }}} int camqp_create_exchange */ + +static int camqp_setup_queue (camqp_config_t *conf) /* {{{ */ +{ + amqp_queue_declare_ok_t *qd_ret; + amqp_basic_consume_ok_t *cm_ret; + + qd_ret = amqp_queue_declare (conf->connection, + /* channel = */ CAMQP_CHANNEL, + /* queue = */ (conf->queue != NULL) + ? amqp_cstring_bytes (conf->queue) + : AMQP_EMPTY_BYTES, + /* passive = */ 0, + /* durable = */ 0, + /* exclusive = */ 0, + /* auto_delete = */ 1, + /* arguments = */ AMQP_EMPTY_TABLE); + if (qd_ret == NULL) + { + ERROR ("amqp plugin: amqp_queue_declare failed."); + camqp_close_connection (conf); + return (-1); + } + + if (conf->queue == NULL) + { + conf->queue = camqp_bytes_cstring (&qd_ret->queue); + if (conf->queue == NULL) + { + ERROR ("amqp plugin: camqp_bytes_cstring failed."); + camqp_close_connection (conf); + return (-1); + } + + INFO ("amqp plugin: Created queue \"%s\".", conf->queue); + } + DEBUG ("amqp plugin: Successfully created queue \"%s\".", conf->queue); + + /* bind to an exchange */ + if (conf->exchange != NULL) + { + amqp_queue_bind_ok_t *qb_ret; + + assert (conf->queue != NULL); + qb_ret = amqp_queue_bind (conf->connection, + /* channel = */ CAMQP_CHANNEL, + /* queue = */ amqp_cstring_bytes (conf->queue), + /* exchange = */ amqp_cstring_bytes (conf->exchange), + /* routing_key = */ (conf->routing_key != NULL) + ? amqp_cstring_bytes (conf->routing_key) + : AMQP_EMPTY_BYTES, + /* arguments = */ AMQP_EMPTY_TABLE); + if ((qb_ret == NULL) && camqp_is_error (conf)) + { + char errbuf[1024]; + ERROR ("amqp plugin: amqp_queue_bind failed: %s", + camqp_strerror (conf, errbuf, sizeof (errbuf))); + camqp_close_connection (conf); + return (-1); + } + + DEBUG ("amqp plugin: Successfully bound queue \"%s\" to exchange \"%s\".", + conf->queue, conf->exchange); + } /* if (conf->exchange != NULL) */ + + cm_ret = amqp_basic_consume (conf->connection, + /* channel = */ CAMQP_CHANNEL, + /* queue = */ amqp_cstring_bytes (conf->queue), + /* consumer_tag = */ AMQP_EMPTY_BYTES, + /* no_local = */ 0, + /* no_ack = */ 1, + /* exclusive = */ 0); + if ((cm_ret == NULL) && camqp_is_error (conf)) + { + char errbuf[1024]; + ERROR ("amqp plugin: amqp_basic_consume failed: %s", + camqp_strerror (conf, errbuf, sizeof (errbuf))); + camqp_close_connection (conf); + return (-1); + } + + return (0); +} /* }}} int camqp_setup_queue */ + +static int camqp_connect (camqp_config_t *conf) /* {{{ */ +{ + amqp_rpc_reply_t reply; + int sockfd; + int status; + + if (conf->connection != NULL) + return (0); + + conf->connection = amqp_new_connection (); + if (conf->connection == NULL) + { + ERROR ("amqp plugin: amqp_new_connection failed."); + return (ENOMEM); + } + + sockfd = amqp_open_socket (CONF(conf, host), conf->port); + if (sockfd < 0) + { + char errbuf[1024]; + status = (-1) * sockfd; + ERROR ("amqp plugin: amqp_open_socket failed: %s", + sstrerror (status, errbuf, sizeof (errbuf))); + amqp_destroy_connection (conf->connection); + conf->connection = NULL; + return (status); + } + amqp_set_sockfd (conf->connection, sockfd); + + reply = amqp_login (conf->connection, CONF(conf, vhost), + /* channel max = */ 0, + /* frame max = */ 131072, + /* heartbeat = */ 0, + /* authentication = */ AMQP_SASL_METHOD_PLAIN, + CONF(conf, user), CONF(conf, password)); + if (reply.reply_type != AMQP_RESPONSE_NORMAL) + { + ERROR ("amqp plugin: amqp_login (vhost = %s, user = %s) failed.", + CONF(conf, vhost), CONF(conf, user)); + amqp_destroy_connection (conf->connection); + close (sockfd); + conf->connection = NULL; + return (1); + } + + amqp_channel_open (conf->connection, /* channel = */ 1); + /* FIXME: Is checking "reply.reply_type" really correct here? How does + * it get set? --octo */ + if (reply.reply_type != AMQP_RESPONSE_NORMAL) + { + ERROR ("amqp plugin: amqp_channel_open failed."); + amqp_connection_close (conf->connection, AMQP_REPLY_SUCCESS); + amqp_destroy_connection (conf->connection); + close(sockfd); + conf->connection = NULL; + return (1); + } + + INFO ("amqp plugin: Successfully opened connection to vhost \"%s\" " + "on %s:%i.", CONF(conf, vhost), CONF(conf, host), conf->port); + + status = camqp_create_exchange (conf); + if (status != 0) + return (status); + + if (!conf->publish) + return (camqp_setup_queue (conf)); + return (0); +} /* }}} int camqp_connect */ + +static int camqp_shutdown (void) /* {{{ */ +{ + size_t i; + + DEBUG ("amqp plugin: Shutting down %zu subscriber threads.", + subscriber_threads_num); + + subscriber_threads_running = 0; + for (i = 0; i < subscriber_threads_num; i++) + { + /* FIXME: Sending a signal is not very elegant here. Maybe find out how + * to use a timeout in the thread and check for the variable in regular + * intervals. */ + pthread_kill (subscriber_threads[i], SIGTERM); + pthread_join (subscriber_threads[i], /* retval = */ NULL); + } + + subscriber_threads_num = 0; + sfree (subscriber_threads); + + DEBUG ("amqp plugin: All subscriber threads exited."); + + return (0); +} /* }}} int camqp_shutdown */ + +/* + * Subscribing code + */ +static int camqp_read_body (camqp_config_t *conf, /* {{{ */ + size_t body_size, const char *content_type) +{ + char body[body_size + 1]; + char *body_ptr; + size_t received; + amqp_frame_t frame; + int status; + + memset (body, 0, sizeof (body)); + body_ptr = &body[0]; + received = 0; + + while (received < body_size) + { + status = amqp_simple_wait_frame (conf->connection, &frame); + if (status < 0) + { + char errbuf[1024]; + status = (-1) * status; + ERROR ("amqp plugin: amqp_simple_wait_frame failed: %s", + sstrerror (status, errbuf, sizeof (errbuf))); + camqp_close_connection (conf); + return (status); + } + + if (frame.frame_type != AMQP_FRAME_BODY) + { + NOTICE ("amqp plugin: Unexpected frame type: %#"PRIx8, + frame.frame_type); + return (-1); + } + + if ((body_size - received) < frame.payload.body_fragment.len) + { + WARNING ("amqp plugin: Body is larger than indicated by header."); + return (-1); + } + + memcpy (body_ptr, frame.payload.body_fragment.bytes, + frame.payload.body_fragment.len); + body_ptr += frame.payload.body_fragment.len; + received += frame.payload.body_fragment.len; + } /* while (received < body_size) */ + + if (strcasecmp ("text/collectd", content_type) == 0) + { + status = handle_putval (stderr, body); + if (status != 0) + ERROR ("amqp plugin: handle_putval failed with status %i.", + status); + return (status); + } + else if (strcasecmp ("application/json", content_type) == 0) + { + ERROR ("amqp plugin: camqp_read_body: Parsing JSON data has not " + "been implemented yet. FIXME!"); + return (0); + } + else + { + ERROR ("amqp plugin: camqp_read_body: Unknown content type \"%s\".", + content_type); + return (EINVAL); + } + + /* not reached */ + return (0); +} /* }}} int camqp_read_body */ + +static int camqp_read_header (camqp_config_t *conf) /* {{{ */ +{ + int status; + amqp_frame_t frame; + amqp_basic_properties_t *properties; + char *content_type; + + status = amqp_simple_wait_frame (conf->connection, &frame); + if (status < 0) + { + char errbuf[1024]; + status = (-1) * status; + ERROR ("amqp plugin: amqp_simple_wait_frame failed: %s", + sstrerror (status, errbuf, sizeof (errbuf))); + camqp_close_connection (conf); + return (status); + } + + if (frame.frame_type != AMQP_FRAME_HEADER) + { + NOTICE ("amqp plugin: Unexpected frame type: %#"PRIx8, + frame.frame_type); + return (-1); + } + + properties = frame.payload.properties.decoded; + content_type = camqp_bytes_cstring (&properties->content_type); + if (content_type == NULL) + { + ERROR ("amqp plugin: Unable to determine content type."); + return (-1); + } + + status = camqp_read_body (conf, + (size_t) frame.payload.properties.body_size, + content_type); + + sfree (content_type); + return (status); +} /* }}} int camqp_read_header */ + +static void *camqp_subscribe_thread (void *user_data) /* {{{ */ +{ + camqp_config_t *conf = user_data; + int status; + + while (subscriber_threads_running) + { + amqp_frame_t frame; + + status = camqp_connect (conf); + if (status != 0) + { + ERROR ("amqp plugin: camqp_connect failed. " + "Will sleep for %i seconds.", interval_g); + sleep (interval_g); + continue; + } + + status = amqp_simple_wait_frame (conf->connection, &frame); + if (status < 0) + { + ERROR ("amqp plugin: amqp_simple_wait_frame failed. " + "Will sleep for %i seconds.", interval_g); + camqp_close_connection (conf); + sleep (interval_g); + continue; + } + + if (frame.frame_type != AMQP_FRAME_METHOD) + { + DEBUG ("amqp plugin: Unexpected frame type: %#"PRIx8, + frame.frame_type); + continue; + } + + if (frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) + { + DEBUG ("amqp plugin: Unexpected method id: %#"PRIx32, + frame.payload.method.id); + continue; + } + + status = camqp_read_header (conf); + + amqp_maybe_release_buffers (conf->connection); + } /* while (subscriber_threads_running) */ + + camqp_config_free (conf); + pthread_exit (NULL); +} /* }}} void *camqp_subscribe_thread */ + +static int camqp_subscribe_init (camqp_config_t *conf) /* {{{ */ +{ + int status; + pthread_t *tmp; + + tmp = realloc (subscriber_threads, + sizeof (*subscriber_threads) * (subscriber_threads_num + 1)); + if (tmp == NULL) + { + ERROR ("amqp plugin: realloc failed."); + camqp_config_free (conf); + return (ENOMEM); + } + subscriber_threads = tmp; + tmp = subscriber_threads + subscriber_threads_num; + memset (tmp, 0, sizeof (*tmp)); + + status = pthread_create (tmp, /* attr = */ NULL, + camqp_subscribe_thread, conf); + if (status != 0) + { + char errbuf[1024]; + ERROR ("amqp plugin: pthread_create failed: %s", + sstrerror (status, errbuf, sizeof (errbuf))); + camqp_config_free (conf); + return (status); + } + + subscriber_threads_num++; + + return (0); +} /* }}} int camqp_subscribe_init */ + +/* + * Publishing code + */ +/* XXX: You must hold "conf->lock" when calling this function! */ +static int camqp_write_locked (camqp_config_t *conf, /* {{{ */ + const char *buffer, const char *routing_key) +{ + amqp_basic_properties_t props; + int status; + + status = camqp_connect (conf); + if (status != 0) + return (status); + + memset (&props, 0, sizeof (props)); + props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG + | AMQP_BASIC_DELIVERY_MODE_FLAG + | AMQP_BASIC_APP_ID_FLAG; + if (conf->format == CAMQP_FORMAT_COMMAND) + props.content_type = amqp_cstring_bytes("text/collectd"); + else if (conf->format == CAMQP_FORMAT_JSON) + props.content_type = amqp_cstring_bytes("application/json"); + else + assert (23 == 42); + props.delivery_mode = conf->delivery_mode; + props.app_id = amqp_cstring_bytes("collectd"); + + status = amqp_basic_publish(conf->connection, + /* channel = */ 1, + amqp_cstring_bytes(CONF(conf, exchange)), + amqp_cstring_bytes (routing_key), + /* mandatory = */ 0, + /* immediate = */ 0, + &props, + amqp_cstring_bytes(buffer)); + if (status != 0) + { + ERROR ("amqp plugin: amqp_basic_publish failed with status %i.", + status); + camqp_close_connection (conf); + } + + return (status); +} /* }}} int camqp_write_locked */ + +static int camqp_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */ + user_data_t *user_data) +{ + camqp_config_t *conf = user_data->data; + char routing_key[6 * DATA_MAX_NAME_LEN]; + char buffer[4096]; + int status; + + if ((ds == NULL) || (vl == NULL) || (conf == NULL)) + return (EINVAL); + + memset (buffer, 0, sizeof (buffer)); + + if (conf->routing_key != NULL) + { + sstrncpy (routing_key, conf->routing_key, sizeof (routing_key)); + } + else + { + size_t i; + ssnprintf (routing_key, sizeof (routing_key), "collectd/%s/%s/%s/%s/%s", + vl->host, + vl->plugin, vl->plugin_instance, + vl->type, vl->type_instance); + + /* Switch slashes (the only character forbidden by collectd) and dots + * (the separation character used by AMQP). */ + for (i = 0; routing_key[i] != 0; i++) + { + if (routing_key[i] == '.') + routing_key[i] = '/'; + else if (routing_key[i] == '/') + routing_key[i] = '.'; + } + } + + if (conf->format == CAMQP_FORMAT_COMMAND) + { + status = create_putval (buffer, sizeof (buffer), ds, vl); + if (status != 0) + { + ERROR ("amqp plugin: create_putval failed with status %i.", + status); + return (status); + } + } + else if (conf->format == CAMQP_FORMAT_JSON) + { + size_t bfree = sizeof (buffer); + size_t bfill = 0; + + format_json_initialize (buffer, &bfill, &bfree); + format_json_value_list (buffer, &bfill, &bfree, ds, vl, conf->store_rates); + format_json_finalize (buffer, &bfill, &bfree); + } + else + { + ERROR ("amqp plugin: Invalid format (%i).", conf->format); + return (-1); + } + + pthread_mutex_lock (&conf->lock); + status = camqp_write_locked (conf, buffer, routing_key); + pthread_mutex_unlock (&conf->lock); + + return (status); +} /* }}} int camqp_write */ + +/* + * Config handling + */ +static int camqp_config_set_format (oconfig_item_t *ci, /* {{{ */ + camqp_config_t *conf) +{ + char *string; + int status; + + string = NULL; + status = cf_util_get_string (ci, &string); + if (status != 0) + return (status); + + assert (string != NULL); + if (strcasecmp ("Command", string) == 0) + conf->format = CAMQP_FORMAT_COMMAND; + else if (strcasecmp ("JSON", string) == 0) + conf->format = CAMQP_FORMAT_JSON; + else + { + WARNING ("amqp plugin: Invalid format string: %s", + string); + } + + free (string); + + return (0); +} /* }}} int config_set_string */ + +static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */ + _Bool publish) +{ + camqp_config_t *conf; + int status; + int i; + + conf = malloc (sizeof (*conf)); + if (conf == NULL) + { + ERROR ("amqp plugin: malloc failed."); + return (ENOMEM); + } + + /* Initialize "conf" {{{ */ + memset (conf, 0, sizeof (*conf)); + conf->publish = publish; + conf->name = NULL; + conf->format = CAMQP_FORMAT_COMMAND; + conf->host = NULL; + conf->port = 5672; + conf->vhost = NULL; + conf->user = NULL; + conf->password = NULL; + conf->exchange = NULL; + conf->routing_key = NULL; + /* publish only */ + conf->delivery_mode = CAMQP_DM_VOLATILE; + conf->store_rates = 0; + /* subscribe only */ + conf->exchange_type = NULL; + conf->queue = NULL; + /* general */ + conf->connection = NULL; + pthread_mutex_init (&conf->lock, /* attr = */ NULL); + /* }}} */ + + status = cf_util_get_string (ci, &conf->name); + if (status != 0) + { + sfree (conf); + 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, &conf->host); + else if (strcasecmp ("Port", child->key) == 0) + { + status = cf_util_get_port_number (child); + if (status > 0) + { + conf->port = status; + status = 0; + } + } + else if (strcasecmp ("VHost", child->key) == 0) + status = cf_util_get_string (child, &conf->vhost); + else if (strcasecmp ("User", child->key) == 0) + status = cf_util_get_string (child, &conf->user); + else if (strcasecmp ("Password", child->key) == 0) + status = cf_util_get_string (child, &conf->password); + else if (strcasecmp ("Exchange", child->key) == 0) + status = cf_util_get_string (child, &conf->exchange); + else if ((strcasecmp ("ExchangeType", child->key) == 0) && !publish) + status = cf_util_get_string (child, &conf->exchange_type); + else if ((strcasecmp ("Queue", child->key) == 0) && !publish) + status = cf_util_get_string (child, &conf->queue); + else if (strcasecmp ("RoutingKey", child->key) == 0) + status = cf_util_get_string (child, &conf->routing_key); + else if ((strcasecmp ("Persistent", child->key) == 0) && publish) + { + _Bool tmp = 0; + status = cf_util_get_boolean (child, &tmp); + if (tmp) + conf->delivery_mode = CAMQP_DM_PERSISTENT; + else + conf->delivery_mode = CAMQP_DM_VOLATILE; + } + else if ((strcasecmp ("StoreRates", child->key) == 0) && publish) + status = cf_util_get_boolean (child, &conf->store_rates); + else if ((strcasecmp ("Format", child->key) == 0) && publish) + status = camqp_config_set_format (child, conf); + else + WARNING ("amqp plugin: Ignoring unknown " + "configuration option \"%s\".", child->key); + + if (status != 0) + break; + } /* for (i = 0; i < ci->children_num; i++) */ + + if ((status == 0) && (conf->exchange == NULL)) + { + if (conf->exchange_type != NULL) + WARNING ("amqp plugin: The option \"ExchangeType\" was given " + "without the \"Exchange\" option. It will be ignored."); + + if (!publish && (conf->routing_key != NULL)) + WARNING ("amqp plugin: The option \"RoutingKey\" was given " + "without the \"Exchange\" option. It will be ignored."); + + } + + if (status != 0) + { + camqp_config_free (conf); + return (status); + } + + if (conf->exchange != NULL) + { + DEBUG ("amqp plugin: camqp_config_connection: exchange = %s;", + conf->exchange); + } + + if (publish) + { + char cbname[128]; + user_data_t ud = { conf, camqp_config_free }; + + ssnprintf (cbname, sizeof (cbname), "amqp/%s", conf->name); + + status = plugin_register_write (cbname, camqp_write, &ud); + if (status != 0) + { + camqp_config_free (conf); + return (status); + } + } + else + { + status = camqp_subscribe_init (conf); + if (status != 0) + { + camqp_config_free (conf); + return (status); + } + } + + return (0); +} /* }}} int camqp_config_connection */ + +static int camqp_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Publish", child->key) == 0) + camqp_config_connection (child, /* publish = */ 1); + else if (strcasecmp ("Subscribe", child->key) == 0) + camqp_config_connection (child, /* publish = */ 0); + else + WARNING ("amqp plugin: Ignoring unknown config option \"%s\".", + child->key); + } /* for (ci->children_num) */ + + return (0); +} /* }}} int camqp_config */ + +void module_register (void) +{ + plugin_register_complex_config ("amqp", camqp_config); + plugin_register_shutdown ("amqp", camqp_shutdown); +} /* void module_register */ + +/* vim: set sw=4 sts=4 et fdm=marker : */ 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-exec.pod b/src/collectd-exec.pod index 81b3a2ec..10f68290 100644 --- a/src/collectd-exec.pod +++ b/src/collectd-exec.pod @@ -273,6 +273,14 @@ to make use of collectd's more powerful interface. The user, the binary is executed as, may not have root privileges, i.Ee. must have an UID that is non-zero. This is for your own good. +=item + +Early versions of the plugin did not use a command but treated all lines as if +they were arguments to the I command. When the I command was +implemented, this behavior was kept for lines which start with an unknown +command for backwards compatibility. This compatibility code has been removed +in I5>. + =back =head1 SEE ALSO diff --git a/src/collectd-java.pod b/src/collectd-java.pod index 9c0c6eba..9e2f81aa 100644 --- a/src/collectd-java.pod +++ b/src/collectd-java.pod @@ -667,6 +667,14 @@ will be used. Use I to authenticate to the server. If not given, unauthenticated access is used. +=item B I + +Prefixes the generated I with I. If a second +I is specified in a referenced I block, the prefix +specified in the I block will appear at the beginning of the +I, the prefix specified in the I block will be appended +to it. + =item B I Configures which of the I blocks to use with this connection. May be diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c index 45162bd3..b3c1855a 100644 --- a/src/collectd-nagios.c +++ b/src/collectd-nagios.c @@ -1,6 +1,6 @@ /** * collectd-nagios - src/collectd-nagios.c - * Copyright (C) 2008 Florian octo Forster + * Copyright (C) 2008-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 @@ -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 @@ -96,6 +78,7 @@ static char *hostname_g = NULL; static range_t range_critical_g; static range_t range_warning_g; static int consolitation_g = CON_NONE; +static _Bool nan_is_error_g = 0; static char **match_ds_g = NULL; static int match_ds_num_g = 0; @@ -254,6 +237,7 @@ static void usage (const char *name) " -H Hostname to query the values for.\n" " -c Critical range\n" " -w Warning range\n" + " -m Treat \"Not a Number\" (NaN) as critical (default: warning)\n" "\n" "Consolidation functions:\n" " none: Apply the warning- and critical-ranges to each data-source\n" @@ -280,7 +264,12 @@ static int do_check_con_none (size_t values_num, for (i = 0; i < values_num; i++) { if (isnan (values[i])) - num_warning++; + { + if (nan_is_error_g) + num_critical++; + else + num_warning++; + } else if (match_range (&range_critical_g, values[i]) != 0) num_critical++; else if (match_range (&range_warning_g, values[i]) != 0) @@ -337,11 +326,18 @@ static int do_check_con_average (size_t values_num, total_num = 0; for (i = 0; i < values_num; i++) { - if (!isnan (values[i])) + if (isnan (values[i])) { - total += values[i]; - total_num++; + if (!nan_is_error_g) + continue; + + printf ("CRITICAL: Data source \"%s\" is NaN\n", + values_names[i]); + return (RET_CRITICAL); } + + total += values[i]; + total_num++; } if (total_num == 0) @@ -389,11 +385,18 @@ static int do_check_con_sum (size_t values_num, total_num = 0; for (i = 0; i < values_num; i++) { - if (!isnan (values[i])) + if (isnan (values[i])) { - total += values[i]; - total_num++; + if (!nan_is_error_g) + continue; + + printf ("CRITICAL: Data source \"%s\" is NaN\n", + values_names[i]); + return (RET_CRITICAL); } + + total += values[i]; + total_num++; } if (total_num == 0) @@ -443,8 +446,19 @@ static int do_check_con_percentage (size_t values_num, } for (i = 0; i < values_num; i++) - if (!isnan (values[i])) - sum += values[i]; + { + if (isnan (values[i])) + { + if (!nan_is_error_g) + continue; + + printf ("CRITICAL: Data source \"%s\" is NaN\n", + values_names[i]); + return (RET_CRITICAL); + } + + sum += values[i]; + } if (sum == 0.0) { @@ -563,7 +577,7 @@ int main (int argc, char **argv) { int c; - c = getopt (argc, argv, "w:c:s:n:H:g:d:h"); + c = getopt (argc, argv, "w:c:s:n:H:g:d:hm"); if (c < 0) break; @@ -623,6 +637,9 @@ int main (int argc, char **argv) match_ds_num_g++; break; } + case 'm': + nan_is_error_g = 1; + break; default: usage (argv[0]); } /* switch (c) */ diff --git a/src/collectd-nagios.pod b/src/collectd-nagios.pod index c6347eac..d7c749cd 100644 --- a/src/collectd-nagios.pod +++ b/src/collectd-nagios.pod @@ -94,6 +94,12 @@ I (and the colon) may be omitted, I is then assumed to be zero. If I (but not the trailing colon) is omitted, I is assumed to be positive infinity. +=item B<-m> + +If this option is given, "Not a Number" (NaN) is treated as I. By +default, the I consolidation reports NaNs as I. Other +consolidations simply ignore NaN values. + =back =head1 RETURN VALUE diff --git a/src/collectd-perl.pod b/src/collectd-perl.pod index 5c11b652..6b44722c 100644 --- a/src/collectd-perl.pod +++ b/src/collectd-perl.pod @@ -376,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<...>) @@ -405,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 d8e39dbe..42addd27 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -11,7 +11,7 @@ ############################################################################## #Hostname "localhost" -FQDNLookup true +#FQDNLookup true #BaseDir "@prefix@/var/lib/@PACKAGE_NAME@" #PIDFile "@prefix@/var/run/@PACKAGE_NAME@.pid" #PluginDir "@prefix@/lib/@PACKAGE_NAME@" @@ -52,6 +52,7 @@ FQDNLookup true # to missing dependencies or because they have been deactivated explicitly. # ############################################################################## +#@BUILD_PLUGIN_AMQP_TRUE@LoadPlugin amqp #@BUILD_PLUGIN_APACHE_TRUE@LoadPlugin apache #@BUILD_PLUGIN_APCUPS_TRUE@LoadPlugin apcups #@BUILD_PLUGIN_APPLE_SENSORS_TRUE@LoadPlugin apple_sensors @@ -114,6 +115,7 @@ FQDNLookup true #@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 @@ -133,10 +135,12 @@ FQDNLookup true #@BUILD_PLUGIN_UPTIME_TRUE@LoadPlugin uptime #@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users #@BUILD_PLUGIN_UUID_TRUE@LoadPlugin uuid +#@BUILD_PLUGIN_VARNISH_TRUE@LoadPlugin varnish #@BUILD_PLUGIN_VMEM_TRUE@LoadPlugin vmem #@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 @@ -147,11 +151,27 @@ FQDNLookup true # ription of those options is available in the collectd.conf(5) manual page. # ############################################################################## +# +# +# Host "localhost" +# Port "5672" +# VHost "/" +# User "guest" +# Password "guest" +# Exchange "amq.fanout" +# RoutingKey "collectd" +# Persistent false +# StoreRates false +# +# + # -# 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" +# # # @@ -390,6 +410,7 @@ FQDNLookup true # InterfaceDevice "name:device" # IgnoreSelected false # HostnameFormat name +# InterfaceFormat name # # @@ -737,6 +758,14 @@ FQDNLookup true # # +# +# +# Host "redis.example.com" +# Port "6379" +# Timeout 2000 +# +# + # # # Host "router.example.com" @@ -890,6 +919,27 @@ FQDNLookup true # UUIDFile "/etc/uuid" # +# +# This tag support an argument if you want to +# monitor the local instance just use +# If you prefer defining another instance you can do +# so by using +# +# CollectCache true +# CollectBackend true +# CollectConnections true +# CollectSHM true +# CollectESI false +# CollectFetch false +# CollectHCB false +# CollectSMA false +# CollectSMS false +# CollectSM false +# CollectTotals false +# CollectWorkers false +# +# + # # Verbose false # @@ -906,6 +956,14 @@ FQDNLookup true # # +# +# +# Host "localhost" +# Port "6379" +# Timeout 1000 +# +# + ############################################################################## # Filter configuration # #----------------------------------------------------------------------------# diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 4faba997..1da35982 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -155,13 +155,8 @@ hostname will be determined using the L system call. If B is determined automatically this setting controls whether or not the daemon should try to figure out the "fully qualified domain name", FQDN. -This is done using a lookup of the name returned by C. - -Using this feature (i.Ee. setting this option to B) is recommended. -However, to preserve backwards compatibility the default is set to B. -The sample config file that is installed with Cinstall> includes a -line which sets this option, though, so that default installations will have -this setting enabled. +This is done using a lookup of the name returned by C. This option +is enabled by default. =item B I @@ -186,6 +181,143 @@ A list of all plugins and a short summary for each plugin can be found in the F file shipped with the sourcecode and hopefully binary packets as well. +=head2 Plugin C + +The I can be used to communicate with other instances of +I or third party applications using an AMQP message broker. Values +are sent to or received from the broker, which handles routing, queueing and +possibly filtering or messages. + + + # Send values to an AMQP broker + + Host "localhost" + Port "5672" + VHost "/" + User "guest" + Password "guest" + Exchange "amq.fanout" + # ExchangeType "fanout" + # RoutingKey "collectd" + # Persistent false + # Format "command" + # StoreRates false + + + # Receive values from an AMQP broker + + Host "localhost" + Port "5672" + VHost "/" + User "guest" + Password "guest" + Exchange "amq.fanout" + # ExchangeType "fanout" + # Queue "queue_name" + # RoutingKey "collectd.#" + + + +The plugin's configuration consists of a number of I and I +blocks, which configure sending and receiving of values respectively. The two +blocks are very similar, so unless otherwise noted, an option can be used in +either block. The name given in the blocks starting tag is only used for +reporting messages, but may be used to support I of certain +I blocks in the future. + +=over 4 + +=item B I + +Hostname or IP-address of the AMQP broker. Defaults to the default behavior of +the underlying communications library, I, which is "localhost". + +=item B I + +Service name or port number on which the AMQP broker accepts connections. This +argument must be a string, even if the numeric form is used. Defaults to +"5672". + +=item B I + +Name of the I on the AMQP broker to use. Defaults to "/". + +=item B I + +=item B I + +Credentials used to authenticate to the AMQP broker. By default "guest"/"guest" +is used. + +=item B I + +In I blocks, this option specifies the I to send values to. +By default, "amq.fanout" will be used. + +In I blocks this option is optional. If given, a I between +the given exchange and the I is created, using the I if +configured. See the B and B options below. + +=item B I + +If given, the plugin will try to create the configured I with this +I after connecting. When in a I block, the I will then +be bound to this exchange. + +=item B I (Subscribe only) + +Configures the I name to subscribe to. If no queue name was configures +explicitly, a unique queue name will be created by the broker. + +=item B I + +In I blocks, this configures the routing key to set on all outgoing +messages. If not given, the routing key will be computed from the I +of the value. The host, plugin, type and the two instances are concatenated +together using dots as the separator and all containing dots replaced with +slashes. For example "collectd.host/example/com.cpu.0.cpu.user". This makes it +possible to receive only specific values using a "topic" exchange. + +In I blocks, configures the I used when creating a +I between an I and the I. The usual wildcards can be +used to filter messages when using a "topic" exchange. If you're only +interested in CPU statistics, you could use the routing key "collectd.*.cpu.#" +for example. + +=item B B|B (Publish only) + +Selects the I to use. If set to B, the I +mode will be used, i.e. delivery is guaranteed. If set to B (the +default), the I delivery mode will be used, i.e. messages may be +lost due to high load, overflowing queues or similar issues. + +=item B B|B (Publish only) + +Selects the format in which messages are sent to the broker. If set to +B (the default), values are sent as C commands which are +identical to the syntax used by the I and I. In this +case, the C header field will be set to C. + +If set to B, the values are encoded in the I, +an easy and straight forward exchange format. The C header field +will be set to C. + +A subscribing client I use the C header field to +determine how to decode the values. Currently, the I itself can +only decode the B format. + +=item B B|B (Publish only) + +Determines whether or not C, C and C data sources +are converted to a I (i.e. a C value). If set to B (the +default), no conversion is performed. Otherwise the conversion is performed +using the internal value cache. + +Please note that currently this option is only used if the B option has +been set to B. + +=back + =head2 Plugin C To configure the C-plugin you first need to configure the Apache @@ -204,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 @@ -212,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 @@ -1026,22 +1176,6 @@ Report using the device name rather than the mountpoint. i.e. with this I (the default), it will report a disk as "root", but with it I, it will be "sda1" (or whichever). -=item B B|B - -When enabled, the blocks reserved for root are reported separately. When -disabled (the default for backwards compatibility reasons) the reserved space -will be included in the "free" space. - -When disabled, the "df" type will be used to store "free" and "used" space. The -mount point or disk name (see option B) is used as type -instance in this case (again: backwards compatibility). - -When enabled, the type "df_complex" is used and three files are created. The -mount point or disk name is used as plugin instance and the type instance is -set to "free", "reserved" and "used" as appropriate. - -Enabling this option is recommended. - =item B B|B Enables or disables reporting of free, reserved and used inodes. Defaults to @@ -1361,13 +1495,6 @@ Hostname to connect to. Defaults to B<127.0.0.1>. TCP-Port to connect to. Defaults to B<7634>. -=item B I|I - -If enabled, translate the disk names to major/minor device numbers -(e.Eg. "8-0" for /dev/sda). For backwards compatibility this defaults to -I but it's recommended to disable it as it will probably be removed in -the next major version. - =back =head2 Plugin C @@ -1595,6 +1722,16 @@ You can also specify combinations of these fields. For example B means to concatenate the guest name and UUID (with a literal colon character between, thus I<"foo:1234-1234-1234-1234">). +=item B B|B
+ +When the libvirt plugin logs interface data, it sets the name of the collected +data according to this setting. The default is to use the path as provided by +the hypervisor (the "dev" property of the target node), which is equal to +setting B. + +B
means use the interface's mac address. This is useful since the +interface path might change between reboots of a guest or across migrations. + =back =head2 Plugin C @@ -1856,7 +1993,7 @@ B option is mandatory. The C requires B to be installed. It connects to one or more databases when started and keeps the connection up as long as possible. When the connection is interrupted for whatever reason it will try -to re-connect. The plugin will complaint loudly in case anything goes wrong. +to re-connect. The plugin will complain loudly in case anything goes wrong. This plugin issues the MySQL C / C command and collects information about MySQL network traffic, executed statements, @@ -2626,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 @@ -2637,16 +2774,6 @@ the same multicast group. While this results in more network traffic than necessary it's not a huge problem since the plugin has a duplicate detection, so the values will not loop. -=item B I - -For each host/plugin/type combination the C caches the time of -the last value being sent or received. Every I seconds the plugin -searches and removes all entries that are older than I seconds, thus -freeing the unused memory again. Since this process is somewhat expensive and -normally doesn't do much, this value should not be too small. The default is -1800 seconds, but setting this to 86400 seconds (one day) will not do much harm -either. - =item B B|B The network plugin cannot only receive and send statistics, it can also create @@ -3280,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 @@ -3360,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 @@ -3389,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 @@ -3806,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, @@ -4455,6 +4601,68 @@ Take the UUID from the given file (default I). =back +=head2 Plugin C + +The Varnish plugin collects information about Varnish, an HTTP accelerator. + +=over 4 + +=item B B|B + +Cache hits and misses. True by default. + +=item B B|B + +Number of client connections received, accepted and dropped. True by default. + +=item B B|B + +Back-end connection statistics, such as successful, reused, +and closed connections. True by default. + +=item B B|B + +Statistics about the shared memory log, a memory region to store +log messages which is flushed to disk when full. True by default. + +=item B B|B + +Edge Side Includes (ESI) parse statistics. False by default. + +=item B B|B + +Statistics about fetches (HTTP requests sent to the backend). False by default. + +=item B B|B + +Inserts and look-ups in the crit bit tree based hash. Look-ups are +divided into locked and unlocked look-ups. False by default. + +=item B B|B + +malloc or umem (umem_alloc(3MALLOC) based) storage statistics. +The umem storage component is Solaris specific. False by default. + +=item B B|B + +synth (synthetic content) storage statistics. This storage +component is used internally only. False by default. + +=item B B|B + +file (memory mapped file) storage statistics. False by default. + +=item B B|B + +Collects overview counters, such as the number of sessions created, +the number of requests and bytes transferred. False by default. + +=item B B|B + +Collect statistics about worker threads. False by default. + +=back + =head2 Plugin C The C plugin collects information about the usage of virtual memory. @@ -4615,6 +4823,13 @@ information. WarningMin 100000000 + + + DataSource "midterm" + FailureMax 4 + Hits 3 + Hysteresis 3 + @@ -4687,6 +4902,27 @@ percentage value, relative to the other data sources. This is helpful for example for the "df" type, where you may want to issue a warning when less than 5E% of the total space is available. Defaults to B. +=item B I + +Sets the number of occurrences which the threshold must be arised before to +dispatch any notification or, in other words, the number of Bs +than the threshold must be match before dispatch any notification. + +=item B I + +Sets the hysteresis value for threshold. The hysteresis is a method to +prevent flapping between states, until a new received value for +a previously matched threshold down below the threshold condition +(B, B or everthing else) minus the hysteresis value, +the failure (respectively warning) state will be keep. + +=item B B|B + +If set to B (the default), the threshold must be treated as +interesting and, when a number of B values will lost, then +a missing notification will be dispatched. On the other hand, if set to +B, the missing notification will never dispatched for this threshold. + =back =head1 FILTER CONFIGURATION diff --git a/src/collectd.h b/src/collectd.h index e7fc4e35..93d356e3 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/common.c b/src/common.c index 2598036d..08653dcd 100644 --- a/src/common.c +++ b/src/common.c @@ -29,6 +29,7 @@ #include "collectd.h" #include "common.h" #include "plugin.h" +#include "utils_cache.h" #if HAVE_PTHREAD_H # include @@ -802,6 +803,75 @@ int format_name (char *ret, int ret_len, return (0); } /* int format_name */ +int format_values (char *ret, size_t ret_len, /* {{{ */ + const data_set_t *ds, const value_list_t *vl, + _Bool store_rates) +{ + size_t offset = 0; + int status; + int i; + gauge_t *rates = NULL; + + assert (0 == strcmp (ds->type, vl->type)); + + memset (ret, 0, ret_len); + +#define BUFFER_ADD(...) do { \ + status = ssnprintf (ret + offset, ret_len - offset, \ + __VA_ARGS__); \ + if (status < 1) \ + { \ + sfree (rates); \ + return (-1); \ + } \ + else if (((size_t) status) >= (ret_len - offset)) \ + { \ + sfree (rates); \ + return (-1); \ + } \ + else \ + offset += ((size_t) status); \ +} while (0) + + BUFFER_ADD ("%lu", (unsigned long) vl->time); + + for (i = 0; i < ds->ds_num; i++) + { + if (ds->ds[i].type == DS_TYPE_GAUGE) + BUFFER_ADD (":%f", vl->values[i].gauge); + else if (store_rates) + { + if (rates == NULL) + rates = uc_get_rate (ds, vl); + if (rates == NULL) + { + WARNING ("format_values: " + "uc_get_rate failed."); + return (-1); + } + BUFFER_ADD (":%g", rates[i]); + } + else if (ds->ds[i].type == DS_TYPE_COUNTER) + BUFFER_ADD (":%llu", vl->values[i].counter); + else if (ds->ds[i].type == DS_TYPE_DERIVE) + BUFFER_ADD (":%"PRIi64, vl->values[i].derive); + else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) + BUFFER_ADD (":%"PRIu64, vl->values[i].absolute); + else + { + ERROR ("format_values plugin: Unknown data source type: %i", + ds->ds[i].type); + sfree (rates); + return (-1); + } + } /* for ds->ds_num */ + +#undef BUFFER_ADD + + sfree (rates); + return (0); +} /* }}} int format_values */ + int parse_identifier (char *str, char **ret_host, char **ret_plugin, char **ret_plugin_instance, char **ret_type, char **ret_type_instance) diff --git a/src/common.h b/src/common.h index 229f7098..63ecca39 100644 --- a/src/common.h +++ b/src/common.h @@ -258,6 +258,9 @@ int format_name (char *ret, int ret_len, #define FORMAT_VL(ret, ret_len, vl) \ format_name (ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \ (vl)->type, (vl)->type_instance) +int format_values (char *ret, size_t ret_len, + const data_set_t *ds, const value_list_t *vl, + _Bool store_rates); int parse_identifier (char *str, char **ret_host, char **ret_plugin, char **ret_plugin_instance, diff --git a/src/configfile.c b/src/configfile.c index 0b7786f9..99dded93 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -1,6 +1,6 @@ /** * collectd - src/configfile.c - * Copyright (C) 2005-2009 Florian octo Forster + * Copyright (C) 2005-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 @@ -96,7 +96,7 @@ static cf_global_option_t cf_global_options[] = {"BaseDir", NULL, PKGLOCALSTATEDIR}, {"PIDFile", NULL, PIDFILE}, {"Hostname", NULL, NULL}, - {"FQDNLookup", NULL, "false"}, + {"FQDNLookup", NULL, "true"}, {"Interval", NULL, "10"}, {"ReadThreads", NULL, "5"}, {"Timeout", NULL, "2"}, @@ -1014,11 +1014,37 @@ int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */ return (-1); } - *ret_bool = ci->values[0].value.boolean ? true : false; + *ret_bool = ci->values[0].value.boolean ? 1 : 0; return (0); } /* }}} int cf_util_get_boolean */ +int cf_util_get_flag (const oconfig_item_t *ci, /* {{{ */ + unsigned int *ret_value, unsigned int flag) +{ + int status; + _Bool b; + + if (ret_value == NULL) + return (EINVAL); + + b = 0; + status = cf_util_get_boolean (ci, &b); + if (status != 0) + return (status); + + if (b) + { + *ret_value |= flag; + } + else + { + *ret_value &= ~flag; + } + + return (0); +} /* }}} int cf_util_get_flag */ + /* Assures that the config option is a string. The string is then converted to * a port number using `service_name_to_port_number' and returned. Returns the * port number in the range [1-65535] or less than zero upon failure. */ diff --git a/src/configfile.h b/src/configfile.h index 432e09f5..519a6ff8 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -2,7 +2,7 @@ #define CONFIGFILE_H /** * collectd - src/configfile.h - * Copyright (C) 2005,2006 Florian octo Forster + * Copyright (C) 2005-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 @@ -103,6 +103,11 @@ int cf_util_get_int (const oconfig_item_t *ci, int *ret_value); * Otherwise, `ret_bool' is not changed and non-zero is returned. */ int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool); +/* Assures the config option is a boolean and set or unset the given flag in + * `ret_value' as appropriate. Returns non-zero on error. */ +int cf_util_get_flag (const oconfig_item_t *ci, + unsigned int *ret_value, unsigned int flag); + /* Assures that the config option is a string. The string is then converted to * a port number using `service_name_to_port_number' and returned. Returns the * port number in the range [1-65535] or less than zero upon failure. */ 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 0527dc8d..433764e2 100644 --- a/src/curl_json.c +++ b/src/curl_json.c @@ -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 */ @@ -769,9 +763,14 @@ 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); @@ -779,18 +778,30 @@ static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */ /* 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 fc80ac3f..4b3cba01 100644 --- a/src/df.c +++ b/src/df.c @@ -60,9 +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_reserved = false; -static _Bool report_inodes = false; +static _Bool by_device = 0; +static _Bool report_inodes = 0; static int df_init (void) { @@ -117,25 +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; - - return (0); - } - else if (strcasecmp (key, "ReportReserved") == 0) - { - if (IS_TRUE (value)) - report_reserved = true; - else - report_reserved = false; + 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); } @@ -144,28 +134,6 @@ static int df_config (const char *key, const char *value) return (-1); } -static void df_submit_two (char *df_name, - const char *type, - gauge_t df_used, - gauge_t df_free) -{ - value_t values[2]; - value_list_t vl = VALUE_LIST_INIT; - - values[0].gauge = df_used; - values[1].gauge = df_free; - - vl.values = values; - vl.values_len = 2; - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); - sstrncpy (vl.plugin, "df", sizeof (vl.plugin)); - sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance)); - sstrncpy (vl.type, type, sizeof (vl.type)); - sstrncpy (vl.type_instance, df_name, sizeof (vl.type_instance)); - - plugin_dispatch_values (&vl); -} /* void df_submit_two */ - __attribute__ ((nonnull(2))) static void df_submit_one (char *plugin_instance, const char *type, const char *type_instance, @@ -213,6 +181,9 @@ static int df_read (void) { unsigned long long blocksize; char disk_name[256]; + uint64_t blk_free; + uint64_t blk_reserved; + uint64_t blk_used; if (ignorelist_match (il_device, (mnt_ptr->spec_device != NULL) @@ -271,39 +242,22 @@ static int df_read (void) blocksize = BLOCKSIZE(statbuf); - if (report_reserved) - { - uint64_t blk_free; - uint64_t blk_reserved; - uint64_t blk_used; - - /* Sanity-check for the values in the struct */ - if (statbuf.f_bfree < statbuf.f_bavail) - statbuf.f_bfree = statbuf.f_bavail; - if (statbuf.f_blocks < statbuf.f_bfree) - statbuf.f_blocks = statbuf.f_bfree; - - blk_free = (uint64_t) statbuf.f_bavail; - blk_reserved = (uint64_t) (statbuf.f_bfree - statbuf.f_bavail); - blk_used = (uint64_t) (statbuf.f_blocks - statbuf.f_bfree); - - df_submit_one (disk_name, "df_complex", "free", - (gauge_t) (blk_free * blocksize)); - df_submit_one (disk_name, "df_complex", "reserved", - (gauge_t) (blk_reserved * blocksize)); - df_submit_one (disk_name, "df_complex", "used", - (gauge_t) (blk_used * blocksize)); - } - else /* compatibility code */ - { - gauge_t df_free; - gauge_t df_used; - - df_free = statbuf.f_bfree * blocksize; - df_used = (statbuf.f_blocks - statbuf.f_bfree) * blocksize; - - df_submit_two (disk_name, "df", df_used, df_free); - } + /* Sanity-check for the values in the struct */ + if (statbuf.f_bfree < statbuf.f_bavail) + statbuf.f_bfree = statbuf.f_bavail; + if (statbuf.f_blocks < statbuf.f_bfree) + statbuf.f_blocks = statbuf.f_bfree; + + blk_free = (uint64_t) statbuf.f_bavail; + blk_reserved = (uint64_t) (statbuf.f_bfree - statbuf.f_bavail); + blk_used = (uint64_t) (statbuf.f_blocks - statbuf.f_bfree); + + df_submit_one (disk_name, "df_complex", "free", + (gauge_t) (blk_free * blocksize)); + df_submit_one (disk_name, "df_complex", "reserved", + (gauge_t) (blk_reserved * blocksize)); + df_submit_one (disk_name, "df_complex", "used", + (gauge_t) (blk_used * blocksize)); /* inode handling */ if (report_inodes) diff --git a/src/dns.c b/src/dns.c index f3280c0b..6e63fe44 100644 --- a/src/dns.c +++ b/src/dns.c @@ -80,14 +80,10 @@ static counter_list_t *counter_list_search (counter_list_t **list, unsigned int { counter_list_t *entry; - DEBUG ("counter_list_search (list = %p, key = %u)", - (void *) *list, key); - for (entry = *list; entry != NULL; entry = entry->next) if (entry->key == key) break; - DEBUG ("return (%p)", (void *) entry); return (entry); } @@ -96,9 +92,6 @@ static counter_list_t *counter_list_create (counter_list_t **list, { counter_list_t *entry; - DEBUG ("counter_list_create (list = %p, key = %u, value = %u)", - (void *) *list, key, value); - entry = (counter_list_t *) malloc (sizeof (counter_list_t)); if (entry == NULL) return (NULL); @@ -122,7 +115,6 @@ static counter_list_t *counter_list_create (counter_list_t **list, last->next = entry; } - DEBUG ("return (%p)", (void *) entry); return (entry); } @@ -131,9 +123,6 @@ static void counter_list_add (counter_list_t **list, { counter_list_t *entry; - DEBUG ("counter_list_add (list = %p, key = %u, increment = %u)", - (void *) *list, key, increment); - entry = counter_list_search (list, key); if (entry != NULL) @@ -144,7 +133,6 @@ static void counter_list_add (counter_list_t **list, { counter_list_create (list, key, increment); } - DEBUG ("return ()"); } static int dns_config (const char *key, const char *value) @@ -261,7 +249,7 @@ static void *dns_child_loop (void __attribute__((unused)) *dummy) return (NULL); } - DEBUG ("PCAP object created."); + DEBUG ("dns plugin: PCAP object created."); dnstop_set_pcap_obj (pcap_obj); dnstop_set_callback (dns_child_callback); @@ -274,7 +262,7 @@ static void *dns_child_loop (void __attribute__((unused)) *dummy) ERROR ("dns plugin: Listener thread is exiting " "abnormally: %s", pcap_geterr (pcap_obj)); - DEBUG ("child is exiting"); + DEBUG ("dns plugin: Child is exiting."); pcap_close (pcap_obj); listen_thread_init = 0; @@ -375,7 +363,7 @@ static int dns_read (void) for (i = 0; i < len; i++) { - DEBUG ("qtype = %u; counter = %u;", keys[i], values[i]); + DEBUG ("dns plugin: qtype = %u; counter = %u;", keys[i], values[i]); submit_counter ("dns_qtype", qtype_str (keys[i]), values[i]); } @@ -391,7 +379,7 @@ static int dns_read (void) for (i = 0; i < len; i++) { - DEBUG ("opcode = %u; counter = %u;", keys[i], values[i]); + DEBUG ("dns plugin: opcode = %u; counter = %u;", keys[i], values[i]); submit_counter ("dns_opcode", opcode_str (keys[i]), values[i]); } @@ -407,7 +395,7 @@ static int dns_read (void) for (i = 0; i < len; i++) { - DEBUG ("rcode = %u; counter = %u;", keys[i], values[i]); + DEBUG ("dns plugin: rcode = %u; counter = %u;", keys[i], values[i]); submit_counter ("dns_rcode", rcode_str (keys[i]), values[i]); } diff --git a/src/exec.c b/src/exec.c index c64f949f..9aabe9fc 100644 --- a/src/exec.c +++ b/src/exec.c @@ -541,12 +541,9 @@ static int parse_line (char *buffer) /* {{{ */ return (handle_putnotif (stdout, buffer)); else { - /* For backwards compatibility */ - char tmp[1220]; - /* Let's annoy the user a bit.. */ - INFO ("exec plugin: Prepending `PUTVAL' to this line: %s", buffer); - ssnprintf (tmp, sizeof (tmp), "PUTVAL %s", buffer); - return (handle_putval (stdout, tmp)); + ERROR ("exec plugin: Unable to parse command, ignoring line: \"%s\"", + buffer); + return (-1); } } /* int parse_line }}} */ diff --git a/src/hddtemp.c b/src/hddtemp.c index 4e083753..4428b75d 100644 --- a/src/hddtemp.c +++ b/src/hddtemp.c @@ -1,7 +1,7 @@ /** * collectd - src/hddtemp.c * Copyright (C) 2005,2006 Vincent Stehlé - * Copyright (C) 2006,2007 Florian octo Forster + * Copyright (C) 2006-2010 Florian octo Forster * Copyright (C) 2008 Sebastian Harl * * This program is free software; you can redistribute it and/or modify it @@ -50,23 +50,12 @@ static const char *config_keys[] = { "Host", - "Port", - "TranslateDevicename" + "Port" }; static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); -typedef struct hddname -{ - int major; - int minor; - char *name; - struct hddname *next; -} hddname_t; - -static hddname_t *first_hddname = NULL; static char *hddtemp_host = NULL; static char hddtemp_port[16]; -static int translate_devicename = 1; /* * NAME @@ -231,13 +220,6 @@ static int hddtemp_config (const char *key, const char *value) else sstrncpy (hddtemp_port, value, sizeof (hddtemp_port)); } - else if (strcasecmp (key, "TranslateDevicename") == 0) - { - if (IS_TRUE (value)) - translate_devicename = 1; - else - translate_devicename = 0; - } else { return (-1); @@ -246,199 +228,6 @@ static int hddtemp_config (const char *key, const char *value) return (0); } -/* In the init-function we initialize the `hddname_t' list used to translate - * disk-names. Under Linux that's done using `/proc/partitions'. Under other - * operating-systems, it's not done at all. */ -static int hddtemp_init (void) -{ -#if KERNEL_LINUX - FILE *fh; - char buf[1024]; - int buflen; - - char *fields[16]; - int num_fields; - - int major; - int minor; - char *name; - hddname_t *next; - hddname_t *entry; - - next = first_hddname; - while (next != NULL) - { - entry = next; - next = entry->next; - - free (entry->name); - free (entry); - } - first_hddname = NULL; - - if ((fh = fopen ("/proc/partitions", "r")) != NULL) - { - DEBUG ("hddtemp plugin: Looking at /proc/partitions..."); - - while (fgets (buf, sizeof (buf), fh) != NULL) - { - /* Delete trailing newlines */ - buflen = strlen (buf); - - while ((buflen > 0) && ((buf[buflen-1] == '\n') || (buf[buflen-1] == '\r'))) - buf[--buflen] = '\0'; - - /* We want lines of the form: - * - * 3 1 77842926 hda1 - * - * ...so, skip everything else. */ - if (buflen == 0) - continue; - - num_fields = strsplit (buf, fields, 16); - - if (num_fields != 4) - continue; - - major = atoi (fields[0]); - minor = atoi (fields[1]); - - /* We try to keep only entries, which may correspond to - * physical disks and that may have a corresponding - * entry in the hddtemp daemon. Basically, this means - * IDE and SCSI. */ - switch (major) - { - /* SCSI. */ - case SCSI_DISK0_MAJOR: - case SCSI_DISK1_MAJOR: - case SCSI_DISK2_MAJOR: - case SCSI_DISK3_MAJOR: - case SCSI_DISK4_MAJOR: - case SCSI_DISK5_MAJOR: - case SCSI_DISK6_MAJOR: - case SCSI_DISK7_MAJOR: -#ifdef SCSI_DISK8_MAJOR - case SCSI_DISK8_MAJOR: - case SCSI_DISK9_MAJOR: - case SCSI_DISK10_MAJOR: - case SCSI_DISK11_MAJOR: - case SCSI_DISK12_MAJOR: - case SCSI_DISK13_MAJOR: - case SCSI_DISK14_MAJOR: - case SCSI_DISK15_MAJOR: -#endif /* SCSI_DISK8_MAJOR */ - /* SCSI disks minors are multiples of 16. - * Keep only those. */ - if (minor % 16) - continue; - break; - - /* IDE. */ - case IDE0_MAJOR: - case IDE1_MAJOR: - case IDE2_MAJOR: - case IDE3_MAJOR: - case IDE4_MAJOR: - case IDE5_MAJOR: - case IDE6_MAJOR: - case IDE7_MAJOR: - case IDE8_MAJOR: - case IDE9_MAJOR: - /* IDE disks minors can only be 0 or 64. - * Keep only those. */ - if(minor != 0 && minor != 64) - continue; - break; - - /* Skip all other majors. */ - default: - DEBUG ("hddtemp plugin: Skipping unknown major %i", major); - continue; - } /* switch (major) */ - - if ((name = strdup (fields[3])) == NULL) - { - ERROR ("hddtemp plugin: strdup(%s) == NULL", fields[3]); - continue; - } - - if ((entry = (hddname_t *) malloc (sizeof (hddname_t))) == NULL) - { - ERROR ("hddtemp plugin: malloc (%u) == NULL", - (unsigned int) sizeof (hddname_t)); - free (name); - continue; - } - - DEBUG ("hddtemp plugin: Found disk: %s (%u:%u).", name, major, minor); - - entry->major = major; - entry->minor = minor; - entry->name = name; - entry->next = NULL; - - if (first_hddname == NULL) - { - first_hddname = entry; - } - else - { - entry->next = first_hddname; - first_hddname = entry; - } - } - fclose (fh); - } -#if COLLECT_DEBUG - else - { - char errbuf[1024]; - DEBUG ("hddtemp plugin: Could not open /proc/partitions: %s", - sstrerror (errno, errbuf, sizeof (errbuf))); - } -#endif /* COLLECT_DEBUG */ -#endif /* KERNEL_LINUX */ - - return (0); -} /* int hddtemp_init */ - -/* - * hddtemp_get_major_minor - * - * Description: - * Try to "cook" a bit the drive name as returned - * by the hddtemp daemon. The intend is to transform disk - * names into - when possible. - */ -static char *hddtemp_get_major_minor (char *drive) -{ - hddname_t *list; - char *ret; - - for (list = first_hddname; list != NULL; list = list->next) - if (strcmp (drive, list->name) == 0) - break; - - if (list == NULL) - { - DEBUG ("hddtemp plugin: Don't know %s, keeping name as-is.", drive); - return (strdup (drive)); - } - - if ((ret = (char *) malloc (128 * sizeof (char))) == NULL) - return (NULL); - - if (ssnprintf (ret, 128, "%i-%i", list->major, list->minor) >= 128) - { - free (ret); - return (NULL); - } - - return (ret); -} - static void hddtemp_submit (char *type_instance, double value) { value_t values[1]; @@ -487,7 +276,7 @@ static int hddtemp_read (void) for (i = 0; i < num_disks; i++) { - char *name, *major_minor; + char *name; double temperature; char *mode; @@ -504,16 +293,7 @@ static int hddtemp_read (void) if (mode[0] == 'F') temperature = (temperature - 32.0) * 5.0 / 9.0; - if (translate_devicename - && (major_minor = hddtemp_get_major_minor (name)) != NULL) - { - hddtemp_submit (major_minor, temperature); - free (major_minor); - } - else - { - hddtemp_submit (name, temperature); - } + hddtemp_submit (name, temperature); } return (0); @@ -525,6 +305,5 @@ void module_register (void) { plugin_register_config ("hddtemp", hddtemp_config, config_keys, config_keys_num); - plugin_register_init ("hddtemp", hddtemp_init); plugin_register_read ("hddtemp", hddtemp_read); } diff --git a/src/interface.c b/src/interface.c index 1ba6c8c3..177afbaa 100644 --- a/src/interface.c +++ b/src/interface.c @@ -171,8 +171,8 @@ static void if_submit (const char *dev, const char *type, vl.values_len = 2; sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "interface", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance)); sstrncpy (vl.type, type, sizeof (vl.type)); - sstrncpy (vl.type_instance, dev, sizeof (vl.type_instance)); plugin_dispatch_values (&vl); } /* void if_submit */ 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/libvirt.c b/src/libvirt.c index bcbf0e6a..44f38320 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -43,6 +43,7 @@ static const char *config_keys[] = { "IgnoreSelected", "HostnameFormat", + "InterfaceFormat", NULL }; @@ -89,13 +90,14 @@ static int add_block_device (virDomainPtr dom, const char *path); struct interface_device { virDomainPtr dom; /* domain */ char *path; /* name of interface device */ + char *address; /* mac address of interface device */ }; static struct interface_device *interface_devices = NULL; static int nr_interface_devices = 0; static void free_interface_devices (void); -static int add_interface_device (virDomainPtr dom, const char *path); +static int add_interface_device (virDomainPtr dom, const char *path, const char *address); /* HostnameFormat. */ #define HF_MAX_FIELDS 3 @@ -110,6 +112,14 @@ enum hf_field { static enum hf_field hostname_format[HF_MAX_FIELDS] = { hf_name }; +/* InterfaceFormat. */ +enum if_field { + if_address, + if_name +}; + +static enum if_field interface_format = if_name; + /* Time that we last refreshed. */ static time_t last_refresh = (time_t) 0; @@ -215,7 +225,7 @@ lv_config (const char *key, const char *value) n = strsplit (value_copy, fields, HF_MAX_FIELDS); if (n < 1) { - free (value_copy); + sfree (value_copy); ERROR ("HostnameFormat: no fields"); return -1; } @@ -228,12 +238,12 @@ lv_config (const char *key, const char *value) else if (strcasecmp (fields[i], "uuid") == 0) hostname_format[i] = hf_uuid; else { - free (value_copy); + sfree (value_copy); ERROR ("unknown HostnameFormat field: %s", fields[i]); return -1; } } - free (value_copy); + sfree (value_copy); for (i = n; i < HF_MAX_FIELDS; ++i) hostname_format[i] = hf_none; @@ -241,6 +251,18 @@ lv_config (const char *key, const char *value) return 0; } + if (strcasecmp (key, "InterfaceFormat") == 0) { + if (strcasecmp (value, "name") == 0) + interface_format = if_name; + else if (strcasecmp (value, "address") == 0) + interface_format = if_address; + else { + ERROR ("unknown InterfaceFormat: %s", value); + return -1; + } + return 0; + } + /* Unrecognised option. */ return -1; } @@ -310,7 +332,7 @@ lv_read (void) if (virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu, NULL, 0) != 0) { - free (vinfo); + sfree (vinfo); continue; } @@ -318,7 +340,7 @@ lv_read (void) vcpu_submit (vinfo[j].cpuTime, t, domains[i], vinfo[j].number, "virt_vcpu"); - free (vinfo); + sfree (vinfo); } /* Get block device stats for each domain. */ @@ -343,6 +365,10 @@ lv_read (void) /* Get interface stats for each domain. */ for (i = 0; i < nr_interface_devices; ++i) { struct _virDomainInterfaceStats stats; + char *display_name = interface_devices[i].path; + + if (interface_format == if_address) + display_name = interface_devices[i].address; if (virDomainInterfaceStats (interface_devices[i].dom, interface_devices[i].path, @@ -352,22 +378,22 @@ lv_read (void) if ((stats.rx_bytes != -1) && (stats.tx_bytes != -1)) submit_counter2 ("if_octets", (counter_t) stats.rx_bytes, (counter_t) stats.tx_bytes, - t, interface_devices[i].dom, interface_devices[i].path); + t, interface_devices[i].dom, display_name); if ((stats.rx_packets != -1) && (stats.tx_packets != -1)) submit_counter2 ("if_packets", (counter_t) stats.rx_packets, (counter_t) stats.tx_packets, - t, interface_devices[i].dom, interface_devices[i].path); + t, interface_devices[i].dom, display_name); if ((stats.rx_errs != -1) && (stats.tx_errs != -1)) submit_counter2 ("if_errors", (counter_t) stats.rx_errs, (counter_t) stats.tx_errs, - t, interface_devices[i].dom, interface_devices[i].path); + t, interface_devices[i].dom, display_name); if ((stats.rx_drop != -1) && (stats.tx_drop != -1)) submit_counter2 ("if_dropped", (counter_t) stats.rx_drop, (counter_t) stats.tx_drop, - t, interface_devices[i].dom, interface_devices[i].path); + t, interface_devices[i].dom, display_name); } /* for (nr_interface_devices) */ return 0; @@ -398,7 +424,7 @@ refresh_lists (void) n = virConnectListDomains (conn, domids, n); if (n < 0) { VIRT_ERROR (conn, "reading list of domains"); - free (domids); + sfree (domids); return -1; } @@ -482,38 +508,54 @@ refresh_lists (void) /* Network interfaces. */ xpath_obj = xmlXPathEval - ((xmlChar *) "/domain/devices/interface/target[@dev]", + ((xmlChar *) "/domain/devices/interface[target[@dev]]", xpath_ctx); if (xpath_obj == NULL || xpath_obj->type != XPATH_NODESET || xpath_obj->nodesetval == NULL) goto cont; - for (j = 0; j < xpath_obj->nodesetval->nodeNr; ++j) { - xmlNodePtr node; + xmlNodeSetPtr xml_interfaces = xpath_obj->nodesetval; + + for (j = 0; j < xml_interfaces->nodeNr; ++j) { char *path = NULL; + char *address = NULL; + xmlNodePtr xml_interface; - node = xpath_obj->nodesetval->nodeTab[j]; - if (!node) continue; - path = (char *) xmlGetProp (node, (xmlChar *) "dev"); - if (!path) continue; + xml_interface = xml_interfaces->nodeTab[j]; + if (!xml_interface) continue; + xmlNodePtr child = NULL; + + for (child = xml_interface->children; child; child = child->next) { + if (child->type != XML_ELEMENT_NODE) continue; + + if (xmlStrEqual(child->name, (const xmlChar *) "target")) { + path = (char *) xmlGetProp (child, (const xmlChar *) "dev"); + if (!path) continue; + } else if (xmlStrEqual(child->name, (const xmlChar *) "mac")) { + address = (char *) xmlGetProp (child, (const xmlChar *) "address"); + if (!address) continue; + } + } if (il_interface_devices && - ignore_device_match (il_interface_devices, name, path) != 0) + (ignore_device_match (il_interface_devices, name, path) != 0 || + ignore_device_match (il_interface_devices, name, address) != 0)) goto cont3; - add_interface_device (dom, path); - cont3: - if (path) xmlFree (path); + add_interface_device (dom, path, address); + cont3: + if (path) xmlFree (path); + if (address) xmlFree (address); } cont: if (xpath_obj) xmlXPathFreeObject (xpath_obj); if (xpath_ctx) xmlXPathFreeContext (xpath_ctx); if (xml_doc) xmlFreeDoc (xml_doc); - if (xml) free (xml); + sfree (xml); } - free (domids); + sfree (domids); } return 0; @@ -527,7 +569,7 @@ free_domains () if (domains) { for (i = 0; i < nr_domains; ++i) virDomainFree (domains[i]); - free (domains); + sfree (domains); } domains = NULL; nr_domains = 0; @@ -559,8 +601,8 @@ free_block_devices () if (block_devices) { for (i = 0; i < nr_block_devices; ++i) - free (block_devices[i].path); - free (block_devices); + sfree (block_devices[i].path); + sfree (block_devices); } block_devices = NULL; nr_block_devices = 0; @@ -583,7 +625,7 @@ add_block_device (virDomainPtr dom, const char *path) new_ptr = malloc (new_size); if (new_ptr == NULL) { - free (path_copy); + sfree (path_copy); return -1; } block_devices = new_ptr; @@ -598,36 +640,43 @@ free_interface_devices () int i; if (interface_devices) { - for (i = 0; i < nr_interface_devices; ++i) - free (interface_devices[i].path); - free (interface_devices); + for (i = 0; i < nr_interface_devices; ++i) { + sfree (interface_devices[i].path); + sfree (interface_devices[i].address); + } + sfree (interface_devices); } interface_devices = NULL; nr_interface_devices = 0; } static int -add_interface_device (virDomainPtr dom, const char *path) +add_interface_device (virDomainPtr dom, const char *path, const char *address) { struct interface_device *new_ptr; int new_size = sizeof (interface_devices[0]) * (nr_interface_devices+1); - char *path_copy; + char *path_copy, *address_copy; path_copy = strdup (path); if (!path_copy) return -1; + address_copy = strdup (address); + if (!address_copy) return -1; + if (interface_devices) new_ptr = realloc (interface_devices, new_size); else new_ptr = malloc (new_size); if (new_ptr == NULL) { - free (path_copy); + sfree (path_copy); + sfree (address_copy); return -1; } interface_devices = new_ptr; interface_devices[nr_interface_devices].dom = dom; interface_devices[nr_interface_devices].path = path_copy; + interface_devices[nr_interface_devices].address = address_copy; return nr_interface_devices++; } @@ -645,7 +694,7 @@ ignore_device_match (ignorelist_t *il, const char *domname, const char *devpath) } ssnprintf (name, n, "%s:%s", domname, devpath); r = ignorelist_match (il, name); - free (name); + sfree (name); return r; } diff --git a/src/lpar.c b/src/lpar.c new file mode 100644 index 00000000..4d534476 --- /dev/null +++ b/src/lpar.c @@ -0,0 +1,273 @@ +/** + * collectd - src/lpar.c + * Copyright (C) 2010 Aurélien Reynaud + * + * 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: + * Aurélien Reynaud + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#include +#include +#include + +/* XINTFRAC was defined in libperfstat.h somewhere between AIX 5.3 and 6.1 */ +#ifndef XINTFRAC +# include +# define XINTFRAC ((double)(_system_configuration.Xint) / \ + (double)(_system_configuration.Xfrac)) +#endif + +#define CLOCKTICKS_TO_TICKS(cticks) ((cticks) / XINTFRAC) + +static const char *config_keys[] = +{ + "CpuPoolStats", + "ReportBySerial" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static _Bool pool_stats = 0; +static _Bool report_by_serial = 0; +#if PERFSTAT_SUPPORTS_DONATION +static _Bool donate_flag = 0; +#endif +static char serial[SYS_NMLN]; + +static perfstat_partition_total_t lparstats_old; + +static int lpar_config (const char *key, const char *value) +{ + if (strcasecmp ("CpuPoolStats", key) == 0) + { + if (IS_TRUE (value)) + pool_stats = 1; + else + pool_stats = 0; + } + else if (strcasecmp ("ReportBySerial", key) == 0) + { + if (IS_TRUE (value)) + report_by_serial = 1; + else + report_by_serial = 0; + } + else + { + return (-1); + } + + return (0); +} /* int lpar_config */ + +static int lpar_init (void) +{ + int status; + + /* Retrieve the initial metrics. Returns the number of structures filled. */ + status = perfstat_partition_total (/* name = */ NULL, /* (must be NULL) */ + &lparstats_old, sizeof (perfstat_partition_total_t), + /* number = */ 1 /* (must be 1) */); + if (status != 1) + { + char errbuf[1024]; + ERROR ("lpar plugin: perfstat_partition_total failed: %s (%i)", + sstrerror (errno, errbuf, sizeof (errbuf)), + status); + return (-1); + } + +#if PERFSTAT_SUPPORTS_DONATION + if (!lparstats_old.type.b.shared_enabled + && lparstats_old.type.b.donate_enabled) + { + donate_flag = 1; + } +#endif + + if (pool_stats && !lparstats_old.type.b.pool_util_authority) + { + WARNING ("lpar plugin: This partition does not have pool authority. " + "Disabling CPU pool statistics collection."); + pool_stats = 0; + } + + return (0); +} /* int lpar_init */ + +static void lpar_submit (const char *type_instance, double value) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = (gauge_t)value; + + vl.values = values; + vl.values_len = 1; + if (report_by_serial) + { + sstrncpy (vl.host, serial, sizeof (vl.host)); + sstrncpy (vl.plugin_instance, hostname_g, sizeof (vl.plugin)); + } + else + { + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + } + sstrncpy (vl.plugin, "lpar", sizeof (vl.plugin)); + sstrncpy (vl.type, "vcpu", sizeof (vl.type)); + sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} /* void lpar_submit */ + +static int lpar_read (void) +{ + perfstat_partition_total_t lparstats; + int status; + struct utsname name; + u_longlong_t ticks; + u_longlong_t user_ticks, syst_ticks, wait_ticks, idle_ticks; + u_longlong_t consumed_ticks; + double entitled_proc_capacity; + + /* An LPAR has the same serial number as the physical system it is currently + running on. It is a convenient way of tracking LPARs as they are moved + from chassis to chassis through Live Partition Mobility (LPM). */ + if (uname (&name) != 0) + { + ERROR ("lpar plugin: uname failed."); + return (-1); + } + sstrncpy (serial, name.machine, sizeof (serial)); + + /* Retrieve the current metrics. Returns the number of structures filled. */ + status = perfstat_partition_total (/* name = */ NULL, /* (must be NULL) */ + &lparstats, sizeof (perfstat_partition_total_t), + /* number = */ 1 /* (must be 1) */); + if (status != 1) + { + char errbuf[1024]; + ERROR ("lpar plugin: perfstat_partition_total failed: %s (%i)", + sstrerror (errno, errbuf, sizeof (errbuf)), + status); + return (-1); + } + + /* Number of ticks since we last run. */ + ticks = lparstats.timebase_last - lparstats_old.timebase_last; + if (ticks == 0) + { + /* The stats have not been updated. Return now to avoid + * dividing by zero */ + return (0); + } + + /* + * On a shared partition, we're "entitled" to a certain amount of + * processing power, for example 250/100 of a physical CPU. Processing + * capacity not used by the partition may be assigned to a different + * partition by the hypervisor, so "idle" is hopefully a very small + * number. + * + * A dedicated partition may donate its CPUs to another partition and + * may steal ticks from somewhere else (another partition or maybe the + * shared pool, I don't know --octo). + */ + + /* entitled_proc_capacity is in 1/100th of a CPU */ + entitled_proc_capacity = 0.01 * ((double) lparstats.entitled_proc_capacity); + lpar_submit ("entitled", entitled_proc_capacity); + + /* The number of ticks actually spent in the various states */ + user_ticks = lparstats.puser - lparstats_old.puser; + syst_ticks = lparstats.psys - lparstats_old.psys; + wait_ticks = lparstats.pwait - lparstats_old.pwait; + idle_ticks = lparstats.pidle - lparstats_old.pidle; + consumed_ticks = user_ticks + syst_ticks + wait_ticks + idle_ticks; + + lpar_submit ("user", (double) user_ticks / (double) ticks); + lpar_submit ("system", (double) syst_ticks / (double) ticks); + lpar_submit ("wait", (double) wait_ticks / (double) ticks); + lpar_submit ("idle", (double) idle_ticks / (double) ticks); + +#if PERFSTAT_SUPPORTS_DONATION + if (donate_flag) + { + /* donated => ticks given to another partition + * stolen => ticks received from another partition */ + u_longlong_t idle_donated_ticks, busy_donated_ticks; + u_longlong_t idle_stolen_ticks, busy_stolen_ticks; + + /* FYI: PURR == Processor Utilization of Resources Register + * SPURR == Scaled PURR */ + idle_donated_ticks = lparstats.idle_donated_purr - lparstats_old.idle_donated_purr; + busy_donated_ticks = lparstats.busy_donated_purr - lparstats_old.busy_donated_purr; + idle_stolen_ticks = lparstats.idle_stolen_purr - lparstats_old.idle_stolen_purr; + busy_stolen_ticks = lparstats.busy_stolen_purr - lparstats_old.busy_stolen_purr; + + lpar_submit ("idle_donated", (double) idle_donated_ticks / (double) ticks); + lpar_submit ("busy_donated", (double) busy_donated_ticks / (double) ticks); + lpar_submit ("idle_stolen", (double) idle_stolen_ticks / (double) ticks); + lpar_submit ("busy_stolen", (double) busy_stolen_ticks / (double) ticks); + + /* Donated ticks will be accounted for as stolen ticks in other LPARs */ + consumed_ticks += idle_stolen_ticks + busy_stolen_ticks; + } +#endif + + lpar_submit ("consumed", (double) consumed_ticks / (double) ticks); + + if (pool_stats) + { + char typinst[DATA_MAX_NAME_LEN]; + u_longlong_t pool_idle_cticks; + double pool_idle_cpus; + double pool_busy_cpus; + + /* We're calculating "busy" from "idle" and the total number of + * CPUs, because the "busy" member didn't exist in early versions + * of libperfstat. It was added somewhere between AIX 5.3 ML5 and ML9. */ + pool_idle_cticks = lparstats.pool_idle_time - lparstats_old.pool_idle_time; + pool_idle_cpus = CLOCKTICKS_TO_TICKS ((double) pool_idle_cticks) / (double) ticks; + pool_busy_cpus = ((double) lparstats.phys_cpus_pool) - pool_idle_cpus; + if (pool_busy_cpus < 0.0) + pool_busy_cpus = 0.0; + + ssnprintf (typinst, sizeof (typinst), "pool-%X-busy", lparstats.pool_id); + lpar_submit (typinst, pool_busy_cpus); + + ssnprintf (typinst, sizeof (typinst), "pool-%X-idle", lparstats.pool_id); + lpar_submit (typinst, pool_idle_cpus); + } + + memcpy (&lparstats_old, &lparstats, sizeof (lparstats_old)); + + return (0); +} /* int lpar_read */ + +void module_register (void) +{ + plugin_register_config ("lpar", lpar_config, + config_keys, config_keys_num); + plugin_register_init ("lpar", lpar_init); + plugin_register_read ("lpar", lpar_read); +} /* void module_register */ + +/* vim: set sw=8 noet : */ + 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 c7b796b1..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; @@ -51,12 +50,12 @@ struct mysql_database_s /* {{{ */ char *socket; int port; - int master_stats; - int slave_stats; + _Bool master_stats; + _Bool slave_stats; - int slave_notif; - int slave_io_running; - int slave_sql_running; + _Bool slave_notif; + _Bool slave_io_running; + _Bool slave_sql_running; MYSQL *con; int state; @@ -98,88 +97,9 @@ static void mysql_database_free (void *arg) /* {{{ */ * * */ - -static int mysql_config_set_string (char **ret_string, /* {{{ */ - oconfig_item_t *ci) -{ - char *string; - - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("mysql plugin: The `%s' config option " - "needs exactly one string argument.", ci->key); - return (-1); - } - - string = strdup (ci->values[0].value.string); - if (string == NULL) - { - ERROR ("mysql plugin: strdup failed."); - return (-1); - } - - if (*ret_string != NULL) - free (*ret_string); - *ret_string = string; - - return (0); -} /* }}} int mysql_config_set_string */ - -static int mysql_config_set_int (int *ret_int, /* {{{ */ - oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) - { - WARNING ("mysql plugin: The `%s' config option " - "needs exactly one string argument.", ci->key); - return (-1); - } - - *ret_int = ci->values[0].value.number; - - return (0); -} /* }}} int mysql_config_set_int */ - -static int mysql_config_set_boolean (int *ret_boolean, /* {{{ */ - oconfig_item_t *ci) -{ - int status = 0; - - if (ci->values_num != 1) - status = -1; - - if (status == 0) - { - if (ci->values[0].type == OCONFIG_TYPE_BOOLEAN) - *ret_boolean = ci->values[0].value.boolean; - else if (ci->values[0].type == OCONFIG_TYPE_STRING) - { - if (IS_TRUE (ci->values[0].value.string)) - *ret_boolean = 1; - else if (IS_FALSE (ci->values[0].value.string)) - *ret_boolean = 0; - else - status = -1; - } - else - status = -1; - } - - if (status != 0) - { - WARNING ("mysql plugin: The `%s' config option " - "needs exactly one boolean argument.", ci->key); - return (-1); - } - return (0); -} /* }}} mysql_config_set_boolean */ - -static int mysql_config (oconfig_item_t *ci) /* {{{ */ +static int mysql_config_database (oconfig_item_t *ci) /* {{{ */ { mysql_database_t *db; - int plugin_block; int status = 0; int i; @@ -211,28 +131,13 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */ db->slave_io_running = 1; db->slave_sql_running = 1; - plugin_block = 1; - if (strcasecmp ("Plugin", ci->key) == 0) - { - db->instance = NULL; - } - else if (strcasecmp ("Database", ci->key) == 0) - { - plugin_block = 0; - status = mysql_config_set_string (&db->instance, ci); - if (status != 0) - { - sfree (db); - return (status); - } - assert (db->instance != NULL); - } - else + status = cf_util_get_string (ci, &db->instance); + if (status != 0) { - ERROR ("mysql plugin: mysql_config: " - "Invalid key: %s", ci->key); - return (-1); + sfree (db); + return (status); } + assert (db->instance != NULL); /* Fill the `mysql_database_t' structure.. */ for (i = 0; i < ci->children_num; i++) @@ -240,36 +145,30 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp ("Host", child->key) == 0) - status = mysql_config_set_string (&db->host, child); + status = cf_util_get_string (child, &db->host); else if (strcasecmp ("User", child->key) == 0) - status = mysql_config_set_string (&db->user, child); + status = cf_util_get_string (child, &db->user); else if (strcasecmp ("Password", child->key) == 0) - status = mysql_config_set_string (&db->pass, child); + status = cf_util_get_string (child, &db->pass); else if (strcasecmp ("Port", child->key) == 0) - status = mysql_config_set_int (&db->port, child); - else if (strcasecmp ("Socket", child->key) == 0) - status = mysql_config_set_string (&db->socket, child); - /* Check if we're currently handling the `Plugin' block. If so, - * handle `Database' _blocks_, too. */ - else if ((plugin_block != 0) - && (strcasecmp ("Database", child->key) == 0) - && (child->children != NULL)) { - /* If `plugin_block > 1', there has been at least one - * `Database' block */ - plugin_block++; - status = mysql_config (child); + status = cf_util_get_port_number (child); + if (status > 0) + { + db->port = status; + status = 0; + } } - /* Now handle ordinary `Database' options (without children) */ - else if ((strcasecmp ("Database", child->key) == 0) - && (child->children == NULL)) - status = mysql_config_set_string (&db->database, child); + else if (strcasecmp ("Socket", child->key) == 0) + status = cf_util_get_string (child, &db->socket); + else if (strcasecmp ("Database", child->key) == 0) + status = cf_util_get_string (child, &db->database); else if (strcasecmp ("MasterStats", child->key) == 0) - status = mysql_config_set_boolean (&db->master_stats, child); + status = cf_util_get_boolean (child, &db->master_stats); else if (strcasecmp ("SlaveStats", child->key) == 0) - status = mysql_config_set_boolean (&db->slave_stats, child); + status = cf_util_get_boolean (child, &db->slave_stats); else if (strcasecmp ("SlaveNotifications", child->key) == 0) - status = mysql_config_set_boolean (&db->slave_notif, child); + status = cf_util_get_boolean (child, &db->slave_notif); else { WARNING ("mysql plugin: Option `%s' not allowed here.", child->key); @@ -280,49 +179,6 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */ break; } - /* Check if there were any `Database' blocks. */ - if (plugin_block > 1) - { - /* There were connection blocks. Don't use any legacy stuff. */ - if ((db->host != NULL) - || (db->user != NULL) - || (db->pass != NULL) - || (db->database != NULL) - || (db->socket != NULL) - || (db->port != 0)) - { - WARNING ("mysql plugin: At least one " - "block has been found. The legacy " - "configuration will be ignored."); - } - mysql_database_free (db); - return (0); - } - else if (plugin_block != 0) - { - WARNING ("mysql plugin: You're using the legacy " - "configuration options. Please consider " - "updating your configuration!"); - } - - /* Check that all necessary options have been given. */ - while (status == 0) - { - /* Zero is allowed and automatically handled by - * `mysql_real_connect'. */ - if ((db->port < 0) || (db->port > 65535)) - { - ERROR ("mysql plugin: Database %s: Port number out " - "of range: %i", - (db->instance != NULL) - ? db->instance - : "", - db->port); - status = -1; - } - break; - } /* while (status == 0) */ - /* If all went well, register this database for reading */ if (status == 0) { @@ -353,6 +209,28 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */ } return (0); +} /* }}} int mysql_config_database */ + +static int mysql_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + if (ci == NULL) + return (EINVAL); + + /* Fill the `mysql_database_t' structure.. */ + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Database", child->key) == 0) + mysql_config_database (child); + else + WARNING ("mysql plugin: Option \"%s\" not allowed here.", + child->key); + } + + return (0); } /* }}} int mysql_config */ /* }}} End of configuration handling functions */ @@ -364,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; } @@ -411,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) @@ -446,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) @@ -473,33 +338,14 @@ static void gauge_submit (const char *type, const char *type_instance, submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db); } /* void gauge_submit */ -static void qcache_submit (counter_t hits, counter_t inserts, - counter_t not_cached, counter_t lowmem_prunes, - gauge_t queries_in_cache, mysql_database_t *db) +static void derive_submit (const char *type, const char *type_instance, + derive_t value, mysql_database_t *db) { - value_t values[5]; - - values[0].counter = hits; - values[1].counter = inserts; - values[2].counter = not_cached; - values[3].counter = lowmem_prunes; - values[4].gauge = queries_in_cache; - - submit ("mysql_qcache", NULL, values, STATIC_ARRAY_SIZE (values), db); -} /* void qcache_submit */ - -static void threads_submit (gauge_t running, gauge_t connected, gauge_t cached, - counter_t created, mysql_database_t *db) -{ - value_t values[4]; - - values[0].gauge = running; - values[1].gauge = connected; - values[2].gauge = cached; - values[3].counter = created; + value_t values[1]; - submit ("mysql_threads", NULL, values, STATIC_ARRAY_SIZE (values), db); -} /* void threads_submit */ + values[0].derive = value; + submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db); +} /* void derive_submit */ static void traffic_submit (counter_t rx, counter_t tx, mysql_database_t *db) { @@ -648,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)) @@ -709,16 +557,16 @@ static int mysql_read (user_data_t *ud) char *query; int field_num; - unsigned long long qcache_hits = 0ULL; - unsigned long long qcache_inserts = 0ULL; - unsigned long long qcache_not_cached = 0ULL; - unsigned long long qcache_lowmem_prunes = 0ULL; - int qcache_queries_in_cache = -1; + derive_t qcache_hits = 0; + derive_t qcache_inserts = 0; + derive_t qcache_not_cached = 0; + derive_t qcache_lowmem_prunes = 0; + gauge_t qcache_queries_in_cache = NAN; - int threads_running = -1; - int threads_connected = -1; - int threads_cached = -1; - unsigned long long threads_created = 0ULL; + gauge_t threads_running = NAN; + gauge_t threads_connected = NAN; + gauge_t threads_cached = NAN; + derive_t threads_created = 0; unsigned long long traffic_incoming = 0ULL; unsigned long long traffic_outgoing = 0ULL; @@ -778,15 +626,15 @@ static int mysql_read (user_data_t *ud) strlen ("Qcache_")) == 0) { if (strcmp (key, "Qcache_hits") == 0) - qcache_hits = val; + qcache_hits = (derive_t) val; else if (strcmp (key, "Qcache_inserts") == 0) - qcache_inserts = val; + qcache_inserts = (derive_t) val; else if (strcmp (key, "Qcache_not_cached") == 0) - qcache_not_cached = val; + qcache_not_cached = (derive_t) val; else if (strcmp (key, "Qcache_lowmem_prunes") == 0) - qcache_lowmem_prunes = val; + qcache_lowmem_prunes = (derive_t) val; else if (strcmp (key, "Qcache_queries_in_cache") == 0) - qcache_queries_in_cache = (int) val; + qcache_queries_in_cache = (gauge_t) val; } else if (strncmp (key, "Bytes_", strlen ("Bytes_")) == 0) @@ -800,13 +648,13 @@ static int mysql_read (user_data_t *ud) strlen ("Threads_")) == 0) { if (strcmp (key, "Threads_running") == 0) - threads_running = (int) val; + threads_running = (gauge_t) val; else if (strcmp (key, "Threads_connected") == 0) - threads_connected = (int) val; + threads_connected = (gauge_t) val; else if (strcmp (key, "Threads_cached") == 0) - threads_cached = (int) val; + threads_cached = (gauge_t) val; else if (strcmp (key, "Threads_created") == 0) - threads_created = val; + threads_created = (derive_t) val; } else if (strncmp (key, "Table_locks_", strlen ("Table_locks_")) == 0) @@ -818,16 +666,36 @@ static int mysql_read (user_data_t *ud) } mysql_free_result (res); res = NULL; - if ((qcache_hits != 0ULL) - || (qcache_inserts != 0ULL) - || (qcache_not_cached != 0ULL) - || (qcache_lowmem_prunes != 0ULL)) - qcache_submit (qcache_hits, qcache_inserts, qcache_not_cached, - qcache_lowmem_prunes, qcache_queries_in_cache, db); + if ((qcache_hits != 0) + || (qcache_inserts != 0) + || (qcache_not_cached != 0) + || (qcache_lowmem_prunes != 0)) + { + derive_submit ("cache_result", "qcache-hits", + qcache_hits, db); + derive_submit ("cache_result", "qcache-inserts", + qcache_inserts, db); + derive_submit ("cache_result", "qcache-not_cached", + qcache_not_cached, db); + derive_submit ("cache_result", "qcache-prunes", + qcache_lowmem_prunes, db); + + gauge_submit ("cache_size", "qcache", + qcache_queries_in_cache, db); + } - if (threads_created != 0ULL) - threads_submit (threads_running, threads_connected, - threads_cached, threads_created, db); + if (threads_created != 0) + { + gauge_submit ("threads", "running", + threads_running, db); + gauge_submit ("threads", "connected", + threads_connected, db); + gauge_submit ("threads", "cached", + threads_cached, db); + + derive_submit ("total_threads", "created", + threads_created, db); + } traffic_submit (traffic_incoming, traffic_outgoing, db); diff --git a/src/network.c b/src/network.c index 412b457b..3ad11778 100644 --- a/src/network.c +++ b/src/network.c @@ -259,7 +259,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; @@ -320,30 +320,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 @@ -384,7 +384,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."); @@ -3057,8 +3057,6 @@ static int network_config (oconfig_item_t *ci) /* {{{ */ network_config_set_boolean (child, &network_config_forward); else if (strcasecmp ("ReportStats", child->key) == 0) network_config_set_boolean (child, &network_config_stats); - else if (strcasecmp ("CacheFlush", child->key) == 0) - /* no op for backwards compatibility only */; else { WARNING ("network plugin: Option `%s' is not allowed here.", @@ -3262,13 +3260,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 af894d54..65d3875e 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -1654,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: @@ -1708,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/swap.c b/src/swap.c index 60ede04b..f5a0ea2d 100644 --- a/src/swap.c +++ b/src/swap.c @@ -76,8 +76,8 @@ static derive_t pagesize; static kstat_t *ksp; /* #endif HAVE_LIBKSTAT */ -#elif HAVE_SWAPCTL -/* No global variables */ +#elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS +static derive_t pagesize; /* #endif HAVE_SWAPCTL */ #elif defined(VM_SWAPUSAGE) @@ -115,8 +115,9 @@ static int swap_init (void) ksp = NULL; /* #endif HAVE_LIBKSTAT */ -#elif HAVE_SWAPCTL - /* No init stuff */ +#elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS + /* getpagesize(3C) tells me this does not fail.. */ + pagesize = (derive_t) getpagesize (); /* #endif HAVE_SWAPCTL */ #elif defined(VM_SWAPUSAGE) @@ -338,6 +339,74 @@ static int swap_read (void) /* #endif HAVE_LIBKSTAT */ #elif HAVE_SWAPCTL + #if HAVE_SWAPCTL_TWO_ARGS + swaptbl_t *s; + char strtab[255]; + int swap_num; + int status; + int i; + + derive_t avail = 0; + derive_t total = 0; + + swap_num = swapctl (SC_GETNSWP, NULL); + if (swap_num < 0) + { + ERROR ("swap plugin: swapctl (SC_GETNSWP) failed with status %i.", + swap_num); + return (-1); + } + else if (swap_num == 0) + return (0); + + s = (swaptbl_t *) smalloc (swap_num * sizeof (swapent_t) + sizeof (struct swaptable)); + if (s == NULL) + { + ERROR ("swap plugin: smalloc failed."); + return (-1); + } + /* Initialize string pointers. We have them share the same buffer as we don't care + * about the device's name, only its size. This saves memory and alloc/free ops */ + for (i = 0; i < (swap_num + 1); i++) { + s->swt_ent[i].ste_path = strtab; + } + s->swt_n = swap_num + 1; + status = swapctl (SC_LIST, s); + if (status != swap_num) + { + ERROR ("swap plugin: swapctl (SC_LIST) failed with status %i.", + status); + sfree (s); + return (-1); + } + + for (i = 0; i < swap_num; i++) + { + if ((s->swt_ent[i].ste_flags & ST_INDEL) != 0) + continue; + + avail += ((derive_t) s->swt_ent[i].ste_free) + * pagesize; + total += ((derive_t) s->swt_ent[i].ste_pages) + * pagesize; + } + + if (total < avail) + { + ERROR ("swap plugin: Total swap space (%"PRIi64") " + "is less than free swap space (%"PRIi64").", + total, avail); + sfree (s); + return (-1); + } + + swap_submit ("used", total - avail, DS_TYPE_GAUGE); + swap_submit ("free", avail, DS_TYPE_GAUGE); + + sfree (s); + /* #endif HAVE_SWAPCTL_TWO_ARGS */ + #elif HAVE_SWAPCTL_THREE_ARGS + struct swapent *swap_entries; int swap_num; int status; @@ -346,18 +415,6 @@ static int swap_read (void) derive_t used = 0; derive_t total = 0; - /* - * XXX: This is the syntax for the *BSD `swapctl', which has the - * following prototype: - * swapctl (int cmd, void *arg, int misc); - * - * HP-UX and Solaris (and possibly other UNIXes) provide `swapctl', - * too, but with the following prototype: - * swapctl (int cmd, void *arg); - * - * Solaris is usually handled in the KSTAT case above. For other UNIXes - * a separate case for the other version of `swapctl' may be necessary. - */ swap_num = swapctl (SWAP_NSWAP, NULL, 0); if (swap_num < 0) { @@ -413,6 +470,7 @@ static int swap_read (void) swap_submit ("free", total - used, DS_TYPE_GAUGE); sfree (swap_entries); + #endif /* HAVE_SWAPCTL_THREE_ARGS */ /* #endif HAVE_SWAPCTL */ #elif defined(VM_SWAPUSAGE) diff --git a/src/target_v5upgrade.c b/src/target_v5upgrade.c new file mode 100644 index 00000000..7fc0d421 --- /dev/null +++ b/src/target_v5upgrade.c @@ -0,0 +1,255 @@ +/** + * collectd - src/target_set.c + * Copyright (C) 2008-2010 Florian Forster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; only version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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: + * Florian Forster + **/ + +#include "collectd.h" +#include "plugin.h" +#include "common.h" +#include "filter_chain.h" + +static void v5_swap_instances (value_list_t *vl) /* {{{ */ +{ + char tmp[DATA_MAX_NAME_LEN]; + + assert (sizeof (tmp) == sizeof (vl->plugin_instance)); + assert (sizeof (tmp) == sizeof (vl->type_instance)); + + memcpy (tmp, vl->plugin_instance, sizeof (tmp)); + memcpy (vl->plugin_instance, vl->type_instance, sizeof (tmp)); + memcpy (vl->type_instance, tmp, sizeof (tmp)); +} /* }}} void v5_swap_instances */ + +/* + * Df type + * + * By default, the "df" plugin of version 4.* uses the "df" type and puts the + * mount point in the type instance. Detect this behavior and convert the type + * to "df_complex". This can be selected in versions 4.9 and 4.10 by setting + * the "ReportReserved" option of the "df" plugin. + */ +static int v5_df (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + value_list_t new_vl; + value_t new_value; + + /* Can't upgrade if both instances have been set. */ + if ((vl->plugin_instance[0] != 0) + && (vl->type_instance[0] != 0)) + return (FC_TARGET_CONTINUE); + + /* Copy everything: Time, interval, host, ... */ + memcpy (&new_vl, vl, sizeof (new_vl)); + + /* Reset data we can't simply copy */ + new_vl.values = &new_value; + new_vl.values_len = 1; + new_vl.meta = NULL; + + /* Move the mount point name to the plugin instance */ + if (new_vl.plugin_instance[0] == 0) + v5_swap_instances (&new_vl); + + /* Change the type to "df_complex" */ + sstrncpy (new_vl.type, "df_complex", sizeof (new_vl.type)); + + /* Dispatch two new value lists instead of this one */ + new_vl.values[0].gauge = vl->values[0].gauge; + sstrncpy (new_vl.type_instance, "used", sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + new_vl.values[0].gauge = vl->values[1].gauge; + sstrncpy (new_vl.type_instance, "free", sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + /* Abort processing */ + return (FC_TARGET_STOP); +} /* }}} int v5_df */ + +/* + * Interface plugin + * + * 4.* stores the interface in the type instance and leaves the plugin + * instance empty. If this is the case, put the interface name into the plugin + * instance and clear the type instance. + */ +static int v5_interface (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + if ((vl->plugin_instance[0] != 0) || (vl->type_instance[0] == 0)) + return (FC_TARGET_CONTINUE); + + v5_swap_instances (vl); + return (FC_TARGET_CONTINUE); +} /* }}} int v5_interface */ + +/* + * MySQL query cache + * + * 4.* uses the "mysql_qcache" type which mixes different types of + * information. In 5.* this has been broken up. + */ +static int v5_mysql_qcache (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + value_list_t new_vl; + value_t new_value; + + if (vl->values_len != 5) + return (FC_TARGET_STOP); + + /* Copy everything: Time, interval, host, ... */ + memcpy (&new_vl, vl, sizeof (new_vl)); + + /* Reset data we can't simply copy */ + new_vl.values = &new_value; + new_vl.values_len = 1; + new_vl.meta = NULL; + + /* Change the type to "cache_result" */ + sstrncpy (new_vl.type, "cache_result", sizeof (new_vl.type)); + + /* Dispatch new value lists instead of this one */ + new_vl.values[0].derive = (derive_t) vl->values[0].counter; + sstrncpy (new_vl.type_instance, "qcache-hits", + sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + new_vl.values[0].derive = (derive_t) vl->values[1].counter; + sstrncpy (new_vl.type_instance, "qcache-inserts", + sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + new_vl.values[0].derive = (derive_t) vl->values[2].counter; + sstrncpy (new_vl.type_instance, "qcache-not_cached", + sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + new_vl.values[0].derive = (derive_t) vl->values[3].counter; + sstrncpy (new_vl.type_instance, "qcache-prunes", + sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + /* The last data source is a gauge value, so we have to use a different type + * here. */ + new_vl.values[0].gauge = vl->values[4].gauge; + sstrncpy (new_vl.type, "cache_size", sizeof (new_vl.type)); + sstrncpy (new_vl.type_instance, "qcache", + sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + /* Abort processing */ + return (FC_TARGET_STOP); +} /* }}} int v5_mysql_qcache */ + +/* + * MySQL thread count + * + * 4.* uses the "mysql_threads" type which mixes different types of + * information. In 5.* this has been broken up. + */ +static int v5_mysql_threads (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + value_list_t new_vl; + value_t new_value; + + if (vl->values_len != 4) + return (FC_TARGET_STOP); + + /* Copy everything: Time, interval, host, ... */ + memcpy (&new_vl, vl, sizeof (new_vl)); + + /* Reset data we can't simply copy */ + new_vl.values = &new_value; + new_vl.values_len = 1; + new_vl.meta = NULL; + + /* Change the type to "threads" */ + sstrncpy (new_vl.type, "threads", sizeof (new_vl.type)); + + /* Dispatch new value lists instead of this one */ + new_vl.values[0].gauge = vl->values[0].gauge; + sstrncpy (new_vl.type_instance, "running", + sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + new_vl.values[0].gauge = vl->values[1].gauge; + sstrncpy (new_vl.type_instance, "connected", + sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + new_vl.values[0].gauge = vl->values[2].gauge; + sstrncpy (new_vl.type_instance, "cached", + sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + /* The last data source is a counter value, so we have to use a different + * type here. */ + new_vl.values[0].derive = (derive_t) vl->values[3].counter; + sstrncpy (new_vl.type, "total_threads", sizeof (new_vl.type)); + sstrncpy (new_vl.type_instance, "created", + sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + /* Abort processing */ + return (FC_TARGET_STOP); +} /* }}} int v5_mysql_threads */ + +static int v5_destroy (void **user_data) /* {{{ */ +{ + return (0); +} /* }}} int v5_destroy */ + +static int v5_create (const oconfig_item_t *ci, void **user_data) /* {{{ */ +{ + *user_data = NULL; + return (0); +} /* }}} int v5_create */ + +static int v5_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */ + notification_meta_t __attribute__((unused)) **meta, + void __attribute__((unused)) **user_data) +{ + if ((ds == NULL) || (vl == NULL) || (user_data == NULL)) + return (-EINVAL); + + if (strcmp ("df", vl->type) == 0) + return (v5_df (ds, vl)); + else if (strcmp ("interface", vl->plugin) == 0) + return (v5_interface (ds, vl)); + else if (strcmp ("mysql_qcache", vl->type) == 0) + return (v5_mysql_qcache (ds, vl)); + else if (strcmp ("mysql_threads", vl->type) == 0) + return (v5_mysql_threads (ds, vl)); + + return (FC_TARGET_CONTINUE); +} /* }}} int v5_invoke */ + +void module_register (void) +{ + target_proc_t tproc; + + memset (&tproc, 0, sizeof (tproc)); + tproc.create = v5_create; + tproc.destroy = v5_destroy; + tproc.invoke = v5_invoke; + fc_register_target ("v5upgrade", tproc); +} /* module_register */ + +/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */ + 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 ad542402..04849832 100644 --- a/src/types.db +++ b/src/types.db @@ -13,8 +13,9 @@ ath_nodes value:GAUGE:0:65535 ath_stat value:COUNTER:0:4294967295 bitrate value:GAUGE:0:4294967295 bytes value:GAUGE:0:U +cache_operation value:DERIVE:0:U cache_ratio value:GAUGE:0:100 -cache_result value:COUNTER:0:4294967295 +cache_result value:DERIVE:0:U cache_size value:GAUGE:0:4294967295 charge value:GAUGE:0:U compression uncompressed:COUNTER:0:U, compressed:COUNTER:0:U @@ -26,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 @@ -98,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 @@ -134,8 +135,9 @@ ps_stacksize value:GAUGE:0:9223372036854775807 ps_state value:GAUGE:0:65535 ps_vm value:GAUGE:0:9223372036854775807 queue_length value:GAUGE:0:U -response_time value:GAUGE:0:U records count:GAUGE:0:U +requests value:GAUGE:0:U +response_time value:GAUGE:0:U route_etx value:GAUGE:0:U route_metric value:GAUGE:0:U routes value:GAUGE:0:U @@ -155,17 +157,23 @@ 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 +total_threads value:DERIVE:0:U total_time_in_ms value:DERIVE:0:U total_values value:DERIVE:0:U uptime value:GAUGE:0:4294967295 users users:GAUGE:0:65535 +vcpu value:GAUGE:0:U virt_cpu_total ns:COUNTER:0:256000000000 virt_vcpu ns:COUNTER:0:1000000000 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_cmd_putval.c b/src/utils_cmd_putval.c index ec2b5f87..15cd939b 100644 --- a/src/utils_cmd_putval.c +++ b/src/utils_cmd_putval.c @@ -227,3 +227,29 @@ int handle_putval (FILE *fh, char *buffer) return (0); } /* int handle_putval */ +int create_putval (char *ret, size_t ret_len, /* {{{ */ + const data_set_t *ds, const value_list_t *vl) +{ + char buffer_ident[6 * DATA_MAX_NAME_LEN]; + char buffer_values[1024]; + int status; + + status = FORMAT_VL (buffer_ident, sizeof (buffer_ident), vl); + if (status != 0) + return (status); + escape_string (buffer_ident, sizeof (buffer_ident)); + + status = format_values (buffer_values, sizeof (buffer_values), + ds, vl, /* store rates = */ 0); + if (status != 0) + return (status); + escape_string (buffer_values, sizeof (buffer_values)); + + ssnprintf (ret, ret_len, + "PUTVAL %s interval=%i %s", + buffer_ident, + (vl->interval > 0) ? vl->interval : interval_g, + buffer_values); + + return (0); +} /* }}} int create_putval */ diff --git a/src/utils_cmd_putval.h b/src/utils_cmd_putval.h index 8460b133..9c92fd31 100644 --- a/src/utils_cmd_putval.h +++ b/src/utils_cmd_putval.h @@ -24,6 +24,11 @@ #include +#include "plugin.h" + int handle_putval (FILE *fh, char *buffer); +int create_putval (char *ret, size_t ret_len, + const data_set_t *ds, const value_list_t *vl); + #endif /* UTILS_CMD_PUTVAL_H */ 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_dns.c b/src/utils_dns.c index 2348be24..22c9b959 100644 --- a/src/utils_dns.c +++ b/src/utils_dns.c @@ -291,13 +291,18 @@ rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns off_t no = 0; unsigned char c; size_t len; - assert(ns > 0); + static int loop_detect = 0; + if (loop_detect > 2) + return 4; /* compression loop */ + if (ns <= 0) + return 4; /* probably compression loop */ do { if ((*off) >= sz) break; c = *(buf + (*off)); if (c > 191) { /* blasted compression */ + int rc; unsigned short s; off_t ptr; memcpy(&s, buf + (*off), sizeof(s)); @@ -305,18 +310,23 @@ rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns (*off) += sizeof(s); /* Sanity check */ if ((*off) >= sz) - return 1; + return 1; /* message too short */ ptr = s & 0x3FFF; /* Make sure the pointer is inside this message */ if (ptr >= sz) - return 2; - return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no); + return 2; /* bad compression ptr */ + if (ptr < DNS_MSG_HDR_SZ) + return 2; /* bad compression ptr */ + loop_detect++; + rc = rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no); + loop_detect--; + return rc; } else if (c > RFC1035_MAXLABELSZ) { /* * "(The 10 and 01 combinations are reserved for future use.)" */ + return 3; /* reserved label/compression flags */ break; - return 3; } else { (*off)++; len = (size_t) c; @@ -324,15 +334,18 @@ rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns break; if (len > (ns - 1)) len = ns - 1; - if ((*off) + len > sz) /* message is too short */ - return 4; + if ((*off) + len > sz) + return 4; /* message is too short */ + if (no + len + 1 > ns) + return 5; /* qname would overflow name buffer */ memcpy(name + no, buf + (*off), len); (*off) += len; no += len; *(name + (no++)) = '.'; } } while (c > 0); - *(name + no - 1) = '\0'; + if (no > 0) + *(name + no - 1) = '\0'; /* make sure we didn't allow someone to overflow the name buffer */ assert(no <= ns); return 0; @@ -345,10 +358,10 @@ handle_dns(const char *buf, int len) uint16_t us; off_t offset; char *t; - int x; + int status; /* The DNS header is 12 bytes long */ - if (len < 12) + if (len < DNS_MSG_HDR_SZ) return 0; memcpy(&us, buf + 0, 2); @@ -379,11 +392,15 @@ handle_dns(const char *buf, int len) memcpy(&us, buf + 10, 2); qh.arcount = ntohs(us); - offset = 12; + offset = DNS_MSG_HDR_SZ; memset(qh.qname, '\0', MAX_QNAME_SZ); - x = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ); - if (0 != x) + status = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ); + if (status != 0) + { + INFO ("utils_dns: handle_dns: rfc1035NameUnpack failed " + "with status %i.", status); return 0; + } if ('\0' == qh.qname[0]) sstrncpy (qh.qname, ".", sizeof (qh.qname)); while ((t = strchr(qh.qname, '\n'))) @@ -647,10 +664,6 @@ void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt { int status; - DEBUG ("handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n", - (void *) udata, (void *) hdr, (void *) pkt, - hdr->caplen); - if (hdr->caplen < ETHER_HDR_LEN) return; @@ -684,7 +697,7 @@ void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt break; default: - ERROR ("handle_pcap: unsupported data link type %d\n", + ERROR ("handle_pcap: unsupported data link type %d", pcap_datalink(pcap_obj)); status = 0; break; diff --git a/src/utils_dns.h b/src/utils_dns.h index efc79031..56213afa 100644 --- a/src/utils_dns.h +++ b/src/utils_dns.h @@ -42,6 +42,8 @@ # include #endif +#define DNS_MSG_HDR_SZ 12 + #define T_MAX 65536 #define OP_MAX 16 #define C_MAX 65536 diff --git a/src/utils_threshold.c b/src/utils_threshold.c index 090cc752..99309b93 100644 --- a/src/utils_threshold.c +++ b/src/utils_threshold.c @@ -40,6 +40,7 @@ #define UT_FLAG_INVERT 0x01 #define UT_FLAG_PERSIST 0x02 #define UT_FLAG_PERCENTAGE 0x04 +#define UT_FLAG_INTERESTING 0x08 /* }}} */ /* @@ -217,60 +218,6 @@ static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci) return (0); } /* int ut_config_type_min */ -static int ut_config_type_invert (threshold_t *th, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) - { - WARNING ("threshold values: The `Invert' option needs exactly one " - "boolean argument."); - return (-1); - } - - if (ci->values[0].value.boolean) - th->flags |= UT_FLAG_INVERT; - else - th->flags &= ~UT_FLAG_INVERT; - - return (0); -} /* int ut_config_type_invert */ - -static int ut_config_type_persist (threshold_t *th, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) - { - WARNING ("threshold values: The `Persist' option needs exactly one " - "boolean argument."); - return (-1); - } - - if (ci->values[0].value.boolean) - th->flags |= UT_FLAG_PERSIST; - else - th->flags &= ~UT_FLAG_PERSIST; - - return (0); -} /* int ut_config_type_persist */ - -static int ut_config_type_percentage(threshold_t *th, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) - { - WARNING ("threshold values: The `Percentage' option needs exactly one " - "boolean argument."); - return (-1); - } - - if (ci->values[0].value.boolean) - th->flags |= UT_FLAG_PERCENTAGE; - else - th->flags &= ~UT_FLAG_PERCENTAGE; - - return (0); -} /* int ut_config_type_percentage */ - static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci) { if ((ci->values_num != 1) @@ -330,6 +277,7 @@ static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci) th.failure_max = NAN; th.hits = 0; th.hysteresis = 0; + th.flags = UT_FLAG_INTERESTING; /* interesting by default */ for (i = 0; i < ci->children_num; i++) { @@ -346,12 +294,14 @@ static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci) else if ((strcasecmp ("WarningMin", option->key) == 0) || (strcasecmp ("FailureMin", option->key) == 0)) status = ut_config_type_min (&th, option); + else if (strcasecmp ("Interesting", option->key) == 0) + status = cf_util_get_flag (option, &th.flags, UT_FLAG_INTERESTING); else if (strcasecmp ("Invert", option->key) == 0) - status = ut_config_type_invert (&th, option); + status = cf_util_get_flag (option, &th.flags, UT_FLAG_INVERT); else if (strcasecmp ("Persist", option->key) == 0) - status = ut_config_type_persist (&th, option); + status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST); else if (strcasecmp ("Percentage", option->key) == 0) - status = ut_config_type_percentage (&th, option); + status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERCENTAGE); else if (strcasecmp ("Hits", option->key) == 0) status = ut_config_type_hits (&th, option); else if (strcasecmp ("Hysteresis", option->key) == 0) @@ -517,6 +467,7 @@ int ut_config (const oconfig_item_t *ci) th.hits = 0; th.hysteresis = 0; + th.flags = UT_FLAG_INTERESTING; /* interesting by default */ for (i = 0; i < ci->children_num; i++) { @@ -1028,6 +979,10 @@ int ut_check_interesting (const char *name) th = threshold_search (&vl); if (th == NULL) return (0); + + if ((th->flags & UT_FLAG_INTERESTING) == 0) + return (0); + if ((th->flags & UT_FLAG_PERSIST) == 0) return (1); return (2); diff --git a/src/utils_threshold.h b/src/utils_threshold.h index 8aaf34c6..5955ca6e 100644 --- a/src/utils_threshold.h +++ b/src/utils_threshold.h @@ -39,7 +39,7 @@ typedef struct threshold_s gauge_t failure_min; gauge_t failure_max; gauge_t hysteresis; - int flags; + unsigned int flags; int hits; struct threshold_s *next; } threshold_t; diff --git a/src/varnish.c b/src/varnish.c new file mode 100644 index 00000000..6bf2db73 --- /dev/null +++ b/src/varnish.c @@ -0,0 +1,603 @@ +/** + * collectd - src/varnish.c + * Copyright (C) 2010 Jérôme Renard + * Copyright (C) 2010 Marc Fournier + * Copyright (C) 2010 Florian 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 + * 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: + * Jérôme Renard + * Marc Fournier + * Florian octo Forster + **/ + +/** + * Current list of what is monitored and what is not monitored (yet) + * {{{ + * Field name Description Monitored + * ---------- ----------- --------- + * uptime Child uptime N + * client_conn Client connections accepted Y + * client_drop Connection dropped, no sess Y + * client_req Client requests received Y + * cache_hit Cache hits Y + * cache_hitpass Cache hits for pass Y + * cache_miss Cache misses Y + * backend_conn Backend conn. success Y + * backend_unhealthy Backend conn. not attempted Y + * backend_busy Backend conn. too many Y + * backend_fail Backend conn. failures Y + * backend_reuse Backend conn. reuses Y + * backend_toolate Backend conn. was closed Y + * backend_recycle Backend conn. recycles Y + * backend_unused Backend conn. unused Y + * fetch_head Fetch head Y + * fetch_length Fetch with Length Y + * fetch_chunked Fetch chunked Y + * fetch_eof Fetch EOF Y + * fetch_bad Fetch had bad headers Y + * fetch_close Fetch wanted close Y + * fetch_oldhttp Fetch pre HTTP/1.1 closed Y + * fetch_zero Fetch zero len Y + * fetch_failed Fetch failed Y + * n_sess_mem N struct sess_mem N + * n_sess N struct sess N + * n_object N struct object N + * n_vampireobject N unresurrected objects N + * n_objectcore N struct objectcore N + * n_objecthead N struct objecthead N + * n_smf N struct smf N + * n_smf_frag N small free smf N + * n_smf_large N large free smf N + * n_vbe_conn N struct vbe_conn N + * n_wrk N worker threads Y + * n_wrk_create N worker threads created Y + * n_wrk_failed N worker threads not created Y + * n_wrk_max N worker threads limited Y + * n_wrk_queue N queued work requests Y + * n_wrk_overflow N overflowed work requests Y + * n_wrk_drop N dropped work requests Y + * n_backend N backends N + * n_expired N expired objects N + * n_lru_nuked N LRU nuked objects N + * n_lru_saved N LRU saved objects N + * n_lru_moved N LRU moved objects N + * n_deathrow N objects on deathrow N + * losthdr HTTP header overflows N + * n_objsendfile Objects sent with sendfile N + * n_objwrite Objects sent with write N + * n_objoverflow Objects overflowing workspace N + * s_sess Total Sessions Y + * s_req Total Requests Y + * s_pipe Total pipe Y + * s_pass Total pass Y + * s_fetch Total fetch Y + * s_hdrbytes Total header bytes Y + * s_bodybytes Total body bytes Y + * sess_closed Session Closed N + * sess_pipeline Session Pipeline N + * sess_readahead Session Read Ahead N + * sess_linger Session Linger N + * sess_herd Session herd N + * shm_records SHM records Y + * shm_writes SHM writes Y + * shm_flushes SHM flushes due to overflow Y + * shm_cont SHM MTX contention Y + * shm_cycles SHM cycles through buffer Y + * sm_nreq allocator requests Y + * sm_nobj outstanding allocations Y + * sm_balloc bytes allocated Y + * sm_bfree bytes free Y + * sma_nreq SMA allocator requests Y + * sma_nobj SMA outstanding allocations Y + * sma_nbytes SMA outstanding bytes Y + * sma_balloc SMA bytes allocated Y + * sma_bfree SMA bytes free Y + * sms_nreq SMS allocator requests Y + * sms_nobj SMS outstanding allocations Y + * sms_nbytes SMS outstanding bytes Y + * sms_balloc SMS bytes allocated Y + * sms_bfree SMS bytes freed Y + * backend_req Backend requests made N + * n_vcl N vcl total N + * n_vcl_avail N vcl available N + * n_vcl_discard N vcl discarded N + * n_purge N total active purges N + * n_purge_add N new purges added N + * n_purge_retire N old purges deleted N + * n_purge_obj_test N objects tested N + * n_purge_re_test N regexps tested against N + * n_purge_dups N duplicate purges removed N + * hcb_nolock HCB Lookups without lock Y + * hcb_lock HCB Lookups with lock Y + * hcb_insert HCB Inserts Y + * esi_parse Objects ESI parsed (unlock) Y + * esi_errors ESI parse errors (unlock) Y + * }}} + */ +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" + +#include + +/* {{{ user_config_s */ +struct user_config_s { + char *instance; + + _Bool collect_cache; + _Bool collect_connections; + _Bool collect_esi; + _Bool collect_backend; + _Bool collect_fetch; + _Bool collect_hcb; + _Bool collect_shm; + _Bool collect_sma; + _Bool collect_sms; + _Bool collect_sm; + _Bool collect_totals; + _Bool collect_workers; +}; +typedef struct user_config_s user_config_t; /* }}} */ + +static _Bool have_instance = 0; + +static int varnish_submit (const char *plugin_instance, /* {{{ */ + const char *category, const char *type, const char *type_instance, value_t value) +{ + value_list_t vl = VALUE_LIST_INIT; + + vl.values = &value; + vl.values_len = 1; + + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + + sstrncpy (vl.plugin, "varnish", sizeof (vl.plugin)); + + if (plugin_instance == NULL) + plugin_instance = "default"; + + ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance), + "%s-%s", plugin_instance, category); + + sstrncpy (vl.type, type, sizeof (vl.type)); + + if (type_instance != NULL) + sstrncpy (vl.type_instance, type_instance, + sizeof (vl.type_instance)); + + return (plugin_dispatch_values (&vl)); +} /* }}} int varnish_submit */ + +static int varnish_submit_gauge (const char *plugin_instance, /* {{{ */ + const char *category, const char *type, const char *type_instance, + uint64_t gauge_value) +{ + value_t value; + + value.gauge = (gauge_t) gauge_value; + + return (varnish_submit (plugin_instance, category, type, type_instance, value)); +} /* }}} int varnish_submit_gauge */ + +static int varnish_submit_derive (const char *plugin_instance, /* {{{ */ + const char *category, const char *type, const char *type_instance, + uint64_t derive_value) +{ + value_t value; + + value.derive = (derive_t) derive_value; + + return (varnish_submit (plugin_instance, category, type, type_instance, value)); +} /* }}} int varnish_submit_derive */ + +static void varnish_monitor (const user_config_t *conf, struct varnish_stats *VSL_stats) /* {{{ */ +{ + if (conf->collect_cache) + { + /* Cache hits */ + varnish_submit_derive (conf->instance, "cache", "cache_result", "hit", VSL_stats->cache_hit); + /* Cache misses */ + varnish_submit_derive (conf->instance, "cache", "cache_result", "miss", VSL_stats->cache_miss); + /* Cache hits for pass */ + varnish_submit_derive (conf->instance, "cache", "cache_result", "hitpass", VSL_stats->cache_hitpass); + } + + if (conf->collect_connections) + { + /* Client connections accepted */ + varnish_submit_derive (conf->instance, "connections", "connections", "accepted", VSL_stats->client_conn); + /* Connection dropped, no sess */ + varnish_submit_derive (conf->instance, "connections", "connections", "dropped" , VSL_stats->client_drop); + /* Client requests received */ + varnish_submit_derive (conf->instance, "connections", "connections", "received", VSL_stats->client_req); + } + + if (conf->collect_esi) + { + /* Objects ESI parsed (unlock) */ + varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed", VSL_stats->esi_parse); + /* ESI parse errors (unlock) */ + varnish_submit_derive (conf->instance, "esi", "total_operations", "error", VSL_stats->esi_errors); + } + + if (conf->collect_backend) + { + /* Backend conn. success */ + varnish_submit_derive (conf->instance, "backend", "connections", "success" , VSL_stats->backend_conn); + /* Backend conn. not attempted */ + varnish_submit_derive (conf->instance, "backend", "connections", "not-attempted", VSL_stats->backend_unhealthy); + /* Backend conn. too many */ + varnish_submit_derive (conf->instance, "backend", "connections", "too-many" , VSL_stats->backend_busy); + /* Backend conn. failures */ + varnish_submit_derive (conf->instance, "backend", "connections", "failures" , VSL_stats->backend_fail); + /* Backend conn. reuses */ + varnish_submit_derive (conf->instance, "backend", "connections", "reuses" , VSL_stats->backend_reuse); + /* Backend conn. was closed */ + varnish_submit_derive (conf->instance, "backend", "connections", "was-closed" , VSL_stats->backend_toolate); + /* Backend conn. recycles */ + varnish_submit_derive (conf->instance, "backend", "connections", "recycled" , VSL_stats->backend_recycle); + /* Backend conn. unused */ + varnish_submit_derive (conf->instance, "backend", "connections", "unused" , VSL_stats->backend_unused); + } + + if (conf->collect_fetch) + { + /* Fetch head */ + varnish_submit_derive (conf->instance, "fetch", "http_requests", "head" , VSL_stats->fetch_head); + /* Fetch with length */ + varnish_submit_derive (conf->instance, "fetch", "http_requests", "length" , VSL_stats->fetch_length); + /* Fetch chunked */ + varnish_submit_derive (conf->instance, "fetch", "http_requests", "chunked" , VSL_stats->fetch_chunked); + /* Fetch EOF */ + varnish_submit_derive (conf->instance, "fetch", "http_requests", "eof" , VSL_stats->fetch_eof); + /* Fetch bad headers */ + varnish_submit_derive (conf->instance, "fetch", "http_requests", "bad_headers", VSL_stats->fetch_bad); + /* Fetch wanted close */ + varnish_submit_derive (conf->instance, "fetch", "http_requests", "close" , VSL_stats->fetch_close); + /* Fetch pre HTTP/1.1 closed */ + varnish_submit_derive (conf->instance, "fetch", "http_requests", "oldhttp" , VSL_stats->fetch_oldhttp); + /* Fetch zero len */ + varnish_submit_derive (conf->instance, "fetch", "http_requests", "zero" , VSL_stats->fetch_zero); + /* Fetch failed */ + varnish_submit_derive (conf->instance, "fetch", "http_requests", "failed" , VSL_stats->fetch_failed); + } + + if (conf->collect_hcb) + { + /* HCB Lookups without lock */ + varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_nolock", VSL_stats->hcb_nolock); + /* HCB Lookups with lock */ + varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_lock", VSL_stats->hcb_lock); + /* HCB Inserts */ + varnish_submit_derive (conf->instance, "hcb", "cache_operation", "insert", VSL_stats->hcb_insert); + } + + if (conf->collect_shm) + { + /* SHM records */ + varnish_submit_derive (conf->instance, "shm", "total_operations", "records" , VSL_stats->shm_records); + /* SHM writes */ + varnish_submit_derive (conf->instance, "shm", "total_operations", "writes" , VSL_stats->shm_writes); + /* SHM flushes due to overflow */ + varnish_submit_derive (conf->instance, "shm", "total_operations", "flushes" , VSL_stats->shm_flushes); + /* SHM MTX contention */ + varnish_submit_derive (conf->instance, "shm", "total_operations", "contention", VSL_stats->shm_cont); + /* SHM cycles through buffer */ + varnish_submit_derive (conf->instance, "shm", "total_operations", "cycles" , VSL_stats->shm_cycles); + } + + if (conf->collect_sm) + { + /* allocator requests */ + varnish_submit_derive (conf->instance, "sm", "total_requests", "nreq", VSL_stats->sm_nreq); + /* outstanding allocations */ + varnish_submit_gauge (conf->instance, "sm", "requests", "outstanding", VSL_stats->sm_nobj); + /* bytes allocated */ + varnish_submit_gauge (conf->instance, "sm", "bytes", "allocated", VSL_stats->sm_balloc); + /* bytes free */ + varnish_submit_gauge (conf->instance, "sm", "bytes", "free", VSL_stats->sm_bfree); + } + + if (conf->collect_sma) + { + /* SMA allocator requests */ + varnish_submit_derive (conf->instance, "sma", "total_requests", "nreq", VSL_stats->sma_nreq); + /* SMA outstanding allocations */ + varnish_submit_gauge (conf->instance, "sma", "requests", "outstanding", VSL_stats->sma_nobj); + /* SMA outstanding bytes */ + varnish_submit_gauge (conf->instance, "sma", "bytes", "outstanding", VSL_stats->sma_nbytes); + /* SMA bytes allocated */ + varnish_submit_gauge (conf->instance, "sma", "bytes", "allocated", VSL_stats->sma_balloc); + /* SMA bytes free */ + varnish_submit_gauge (conf->instance, "sma", "bytes", "free" , VSL_stats->sma_bfree); + } + + if (conf->collect_sms) + { + /* SMS allocator requests */ + varnish_submit_derive (conf->instance, "sms", "total_requests", "allocator", VSL_stats->sms_nreq); + /* SMS outstanding allocations */ + varnish_submit_gauge (conf->instance, "sms", "requests", "outstanding", VSL_stats->sms_nobj); + /* SMS outstanding bytes */ + varnish_submit_gauge (conf->instance, "sms", "bytes", "outstanding", VSL_stats->sms_nbytes); + /* SMS bytes allocated */ + varnish_submit_gauge (conf->instance, "sms", "bytes", "allocated", VSL_stats->sms_balloc); + /* SMS bytes freed */ + varnish_submit_gauge (conf->instance, "sms", "bytes", "free", VSL_stats->sms_bfree); + } + + if (conf->collect_totals) + { + /* Total Sessions */ + varnish_submit_derive (conf->instance, "totals", "total_sessions", "sessions", VSL_stats->s_sess); + /* Total Requests */ + varnish_submit_derive (conf->instance, "totals", "total_requests", "requests", VSL_stats->s_req); + /* Total pipe */ + varnish_submit_derive (conf->instance, "totals", "total_operations", "pipe", VSL_stats->s_pipe); + /* Total pass */ + varnish_submit_derive (conf->instance, "totals", "total_operations", "pass", VSL_stats->s_pass); + /* Total fetch */ + varnish_submit_derive (conf->instance, "totals", "total_operations", "fetches", VSL_stats->s_fetch); + /* Total header bytes */ + varnish_submit_derive (conf->instance, "totals", "total_bytes", "header-bytes", VSL_stats->s_hdrbytes); + /* Total body byte */ + varnish_submit_derive (conf->instance, "totals", "total_bytes", "body-bytes", VSL_stats->s_bodybytes); + } + + if (conf->collect_workers) + { + /* worker threads */ + varnish_submit_gauge (conf->instance, "workers", "threads", "worker", VSL_stats->n_wrk); + /* worker threads created */ + varnish_submit_gauge (conf->instance, "workers", "total_threads", "created", VSL_stats->n_wrk_create); + /* worker threads not created */ + varnish_submit_gauge (conf->instance, "workers", "total_threads", "failed", VSL_stats->n_wrk_failed); + /* worker threads limited */ + varnish_submit_gauge (conf->instance, "workers", "total_threads", "limited", VSL_stats->n_wrk_max); + /* queued work requests */ + varnish_submit_gauge (conf->instance, "workers", "total_requests", "queued", VSL_stats->n_wrk_queue); + /* overflowed work requests */ + varnish_submit_gauge (conf->instance, "workers", "total_requests", "overflowed", VSL_stats->n_wrk_overflow); + /* dropped work requests */ + varnish_submit_gauge (conf->instance, "workers", "total_requests", "dropped", VSL_stats->n_wrk_drop); + } +} /* }}} void varnish_monitor */ + +static int varnish_read (user_data_t *ud) /* {{{ */ +{ + struct varnish_stats *VSL_stats; + user_config_t *conf; + + if ((ud == NULL) || (ud->data == NULL)) + return (EINVAL); + + conf = ud->data; + + VSL_stats = VSL_OpenStats (conf->instance); + if (VSL_stats == NULL) + { + ERROR ("Varnish plugin : unable to load statistics"); + + return (-1); + } + + varnish_monitor (conf, VSL_stats); + + return (0); +} /* }}} */ + +static void varnish_config_free (void *ptr) /* {{{ */ +{ + user_config_t *conf = ptr; + + if (conf == NULL) + return; + + sfree (conf->instance); + sfree (conf); +} /* }}} */ + +static int varnish_config_apply_default (user_config_t *conf) /* {{{ */ +{ + if (conf == NULL) + return (EINVAL); + + conf->collect_backend = 1; + conf->collect_cache = 1; + conf->collect_connections = 1; + conf->collect_esi = 0; + conf->collect_fetch = 0; + conf->collect_hcb = 0; + conf->collect_shm = 1; + conf->collect_sm = 0; + conf->collect_sma = 0; + conf->collect_sms = 0; + conf->collect_totals = 0; + + return (0); +} /* }}} int varnish_config_apply_default */ + +static int varnish_init (void) /* {{{ */ +{ + user_config_t *conf; + user_data_t ud; + + if (have_instance) + return (0); + + conf = malloc (sizeof (*conf)); + if (conf == NULL) + return (ENOMEM); + memset (conf, 0, sizeof (*conf)); + + /* Default settings: */ + conf->instance = NULL; + + varnish_config_apply_default (conf); + + ud.data = conf; + ud.free_func = varnish_config_free; + + plugin_register_complex_read (/* group = */ "varnish", + /* name = */ "varnish/localhost", + /* callback = */ varnish_read, + /* interval = */ NULL, + /* user data = */ &ud); + + return (0); +} /* }}} int varnish_init */ + +static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */ +{ + user_config_t *conf; + user_data_t ud; + char callback_name[DATA_MAX_NAME_LEN]; + int i; + + conf = malloc (sizeof (*conf)); + if (conf == NULL) + return (ENOMEM); + memset (conf, 0, sizeof (*conf)); + conf->instance = NULL; + + varnish_config_apply_default (conf); + + if (ci->values_num == 1) + { + int status; + + status = cf_util_get_string (ci, &conf->instance); + if (status != 0) + { + sfree (conf); + return (status); + } + assert (conf->instance != NULL); + + if (strcmp ("localhost", conf->instance) == 0) + { + sfree (conf->instance); + conf->instance = NULL; + } + } + else if (ci->values_num > 1) + { + WARNING ("Varnish plugin: \"Instance\" blocks accept only " + "one argument."); + return (EINVAL); + } + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("CollectCache", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_cache); + else if (strcasecmp ("CollectConnections", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_connections); + else if (strcasecmp ("CollectESI", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_esi); + else if (strcasecmp ("CollectBackend", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_backend); + else if (strcasecmp ("CollectFetch", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_fetch); + else if (strcasecmp ("CollectHCB", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_hcb); + else if (strcasecmp ("CollectSHM", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_shm); + else if (strcasecmp ("CollectSMA", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_sma); + else if (strcasecmp ("CollectSMS", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_sms); + else if (strcasecmp ("CollectSM", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_sm); + else if (strcasecmp ("CollectTotals", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_totals); + else if (strcasecmp ("CollectWorkers", child->key) == 0) + cf_util_get_boolean (child, &conf->collect_workers); + else + { + WARNING ("Varnish plugin: Ignoring unknown " + "configuration option: \"%s\"", + child->key); + } + } + + if (!conf->collect_cache + && !conf->collect_connections + && !conf->collect_esi + && !conf->collect_backend + && !conf->collect_fetch + && !conf->collect_hcb + && !conf->collect_shm + && !conf->collect_sma + && !conf->collect_sms + && !conf->collect_sm + && !conf->collect_totals + && !conf->collect_workers) + { + WARNING ("Varnish plugin: No metric has been configured for " + "instance \"%s\". Disabling this instance.", + (conf->instance == NULL) ? "localhost" : conf->instance); + return (EINVAL); + } + + ssnprintf (callback_name, sizeof (callback_name), "varnish/%s", + (conf->instance == NULL) ? "localhost" : conf->instance); + + ud.data = conf; + ud.free_func = varnish_config_free; + + plugin_register_complex_read (/* group = */ "varnish", + /* name = */ callback_name, + /* callback = */ varnish_read, + /* interval = */ NULL, + /* user data = */ &ud); + + have_instance = 1; + + return (0); +} /* }}} int varnish_config_instance */ + +static int varnish_config (oconfig_item_t *ci) /* {{{ */ +{ + int i; + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *child = ci->children + i; + + if (strcasecmp ("Instance", child->key) == 0) + varnish_config_instance (child); + else + { + WARNING ("Varnish plugin: Ignoring unknown " + "configuration option: \"%s\"", + child->key); + } + } + + return (0); +} /* }}} int varnish_config */ + +void module_register (void) /* {{{ */ +{ + plugin_register_complex_config ("varnish", varnish_config); + plugin_register_init ("varnish", varnish_init); +} /* }}} */ + +/* vim: set sw=8 noet fdm=marker : */ diff --git a/src/write_http.c b/src/write_http.c index ab8757ed..bac8e986 100644 --- a/src/write_http.c +++ b/src/write_http.c @@ -270,76 +270,6 @@ static void wh_callback_free (void *data) /* {{{ */ sfree (cb); } /* }}} void wh_callback_free */ -static int wh_value_list_to_string (char *buffer, /* {{{ */ - size_t buffer_size, - const data_set_t *ds, const value_list_t *vl, - wh_callback_t *cb) -{ - size_t offset = 0; - int status; - int i; - gauge_t *rates = NULL; - - assert (0 == strcmp (ds->type, vl->type)); - - memset (buffer, 0, buffer_size); - -#define BUFFER_ADD(...) do { \ - status = ssnprintf (buffer + offset, buffer_size - offset, \ - __VA_ARGS__); \ - if (status < 1) \ - { \ - sfree (rates); \ - return (-1); \ - } \ - else if (((size_t) status) >= (buffer_size - offset)) \ - { \ - sfree (rates); \ - return (-1); \ - } \ - else \ - offset += ((size_t) status); \ -} while (0) - - BUFFER_ADD ("%lu", (unsigned long) vl->time); - - for (i = 0; i < ds->ds_num; i++) - { - if (ds->ds[i].type == DS_TYPE_GAUGE) - BUFFER_ADD (":%f", vl->values[i].gauge); - else if (cb->store_rates) - { - if (rates == NULL) - rates = uc_get_rate (ds, vl); - if (rates == NULL) - { - WARNING ("write_http plugin: " - "uc_get_rate failed."); - return (-1); - } - BUFFER_ADD (":%g", rates[i]); - } - else if (ds->ds[i].type == DS_TYPE_COUNTER) - BUFFER_ADD (":%llu", vl->values[i].counter); - else if (ds->ds[i].type == DS_TYPE_DERIVE) - BUFFER_ADD (":%"PRIi64, vl->values[i].derive); - else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) - BUFFER_ADD (":%"PRIu64, vl->values[i].absolute); - else - { - ERROR ("write_http plugin: Unknown data source type: %i", - ds->ds[i].type); - sfree (rates); - return (-1); - } - } /* for ds->ds_num */ - -#undef BUFFER_ADD - - sfree (rates); - return (0); -} /* }}} int wh_value_list_to_string */ - static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{{ */ wh_callback_t *cb) { @@ -366,7 +296,7 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{ /* Convert the values to an ASCII representation and put that into * `values'. */ - status = wh_value_list_to_string (values, sizeof (values), ds, vl, cb); + status = format_values (values, sizeof (values), ds, vl, cb->store_rates); if (status != 0) { ERROR ("write_http plugin: error with " "wh_value_list_to_string"); 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 : */