From: Florian Forster Date: Sun, 11 Nov 2012 11:05:17 +0000 (+0100) Subject: Merge branch 'collectd-4.10' into collectd-5.0 X-Git-Tag: collectd-5.0.5~1 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=9e7b3a035836474cb4af253b248de30d5eb97f4d;hp=96bcc6a8ec704702462784de938674f7e5a03e2f;p=collectd.git Merge branch 'collectd-4.10' into collectd-5.0 Conflicts: ChangeLog src/pyvalues.c version-gen.sh --- diff --git a/.gitignore b/.gitignore index b85bdec6..75cb307d 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,9 @@ bindings/java/org/collectd/java/*.class # python stuff *.pyc + +# tag stuff +src/tags + +# backup stuff +*~ diff --git a/AUTHORS b/AUTHORS index f090840e..0e0a5fed 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, @@ -75,6 +79,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. @@ -162,6 +169,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/ChangeLog b/ChangeLog index 21c046b7..8d478adc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,174 @@ +2012-04-01, Version 5.0.4 + * Build system: Fix the use of a libltdl macro. Thanks to Clemens Lang + for fixing this. Adresses some issues with building the iptables + plugin under Gentoo. + * libcollectdclient: A memory leak in the lcc_getval() function has + been fixed. Thanks to Jason Schmidlapp for finding and fixing this + issue. + * bind plugin: The use of 'QType" types has been fixed. + * df plugin: Fixed compiler issue under Mac OS X 10.7. + * conntrack plugin: Support zero as legitimate value. Thanks to Louis + Opter for his patch. + * memcached plugin: Increased the size of a static buffer, which was + truncating status messages form memcached. Thanks to Timon for the + patch. + * network plugin: Forwarding of notifications has been disabled. This + was a contition not checked for before, which may retult in an + endless loop. + * processes plugin: Support for process names with spaces has been + added to the Linux implementation. Thanks to Darrell Bishop for his + patch. + * perl plugin: A race condition in several callbacks, including log and + write callbacks, has been fixed. Thanks to "Rrpv" for reporting this + bug. + * snmp plugin: A bug when casting unsigned integers to gauge values has + been fixed: Unsigned integers would be cast to a signed integer and + then to a gauge, possibly resulting in a negative value. + * tcpconns plugin: Compilation with newer versions of the FreeBSD + runtime has been fixed. + +2012-02-19, Version 5.0.3 + * Build system: Fix problems when building the ipvs and iptables + plugins. Thanks to Sebastian Harl for his patch. A bashism in the + version-gen.sh script has been fixed. Thanks to Jo-Philipp Wich for + his patch. + * csv and rrdtool plugins: Print a more helpful error message when the + DataDir is a symlink pointing to a non-existing location. Thanks to + Jonathan Nieder for his patch. + * exec plugin: Fix a problem when using select(2) to read from file + handles. Thanks to Gerrie Roos for his patch. + * network plugin: An incorrect error message in the handling of the + "Interface" configuration option has been fixed. Thanks to Gerrie + Roos for his patch. + * oracle plugin: A potential endless loop in the error handling has + been fixed. + * python plugin: A crash bug in the configuration handling has been + fixed. Thanks to Sven Trenkel for his patch. + * interfaces plugin: The change which was supposed to ignore "bogus" + interfaces has been reverted, since it ignored legit interfaces, such + as bonding pseudo-devices as well. + +2012-01-21, Version 5.0.2 + * curl_xml plugin: Fix handling of file:// and other URLs (which don't + follow HTTP status codes). Thanks to Fabien Wernli for his patch! + * df plugin: Fix handling of negative "available" counts. This can + occur with some file systems, for example UFS. Thanks to Toni Ylenius + for his patch. + * interface plugin: "mac" interfaces are now ignored on Solaris. These + pseudo-interfaces occur multiple times, causing warnings. Also switch + to 64-bit counters on Solaris, improving overflow behavior for + high-speed interfaces. Thanks to Eddy Geez and Fabien Wernli for + their patches. + * memory plugin: Account kernel and unused memory under Solaris. Thanks + to Fabien Wernli for his patch. + * network plugin: A bug in the interaction between the Network plugin + and filter chains has been fixed: When a filter modified a field such + as the hostname, subsequent values in the same network packets could + have ended up using the modified name rather than the original name. + Thanks to Sebastian Harl for identifying the problem. + * oracle plugin: A memory leak has been fixed in the parameter handling. + * python plugin: A memory leak has been fixed. Thanks to Sven Trenkel + for fixing this bug! + +2011-10-07, Version 5.0.1 + * collectd: A mutex leak has been fixed in the meta data code. Thanks + to Rafal Lesniak for his patch. + * collectd: Compatibility fixes for GCC 4.6 have been applied. Thanks + to Peter Green for his patch. + * csv plugin: The line buffer size has been increased. Thanks to Colin + McCabe for the patch. + * curl_json plugin: Don't use the "parent" node to build the type + instance, if it is empty. Compatibility with libyajl 2 has been + added. Thanks to "spupykin" of the Arch Linux project for the initial + code. Formatting of time has been fixed in the JSON module. + * exec plugin: Fix the timestamp value passed to notification scripts. + Thanks to Alexander Kovalenko for fixing this. + * iptables plugin: Fix linking with some versions of libiptc. + * irq plugin: Fix support for interrupts under Linux. The old code + assumed that interrupts have a numeric value -- this is no longer + true for Linux. Thanks to Bostjan Skufca for implementing this. + * notify_desktop plugin: Compatibility with libnotify 0.7 has been + added. Thanks to Samuli Suominen for his patch. + * processes plugin: Fix handling of regular expressions containing + spaces. Thanks for Sebastian Harl for fixing this. + * rrdtool, rrdcached plugins: Improve precision of the XFF parameter. + Previously, values like 0.999 would have been rounded to 1.0. Thanks + to Francois-Xavier Bourlet for fixing this. + * varnish plugin: Fix data type handling of some metrics. Some values + were submitted as gauge even though they were derives. + * Various plugin: Set a multi-threading flag in libcurl. Thanks to Mike + Flisher for the fix. + +2011-03-28, Version 5.0.0 + * collectd: The "FQDNLookup" option is now enabled by default. + * collectd: The internal representation of time has been changed to + allow a higher accuracy than one second. + * collectdcmd: This new command line utility can send various commands + to collectd using the UnixSock plugin. Thanks to Håkon Dugstad + Johnsen and Sebastian Harl for their code. + * collectd-nagios: The "-m" option has been implemented (treat NaNs as + critical). + * collectd-tg: Traffic generator creating bogus network traffic + compatible to the Network plugin. This utility can be used to + stress-test new write plugins and collectd in general. + * libcollectdclient: Creating and sending network packets has been + added to the collectd client library. + * All data sets: The data source name of all data sets with exactly + one data source has been changed to "value". + * All plugins: All "counter" data sources have been converted to + "derive" data sources. All plugins now use "derive" by default, but + plugins such as the network plugin can still handle "counter", of + course. The minimum value of all derive data sources is zero, the + maximum value is unspecified. + * amqp plugin: The new AMQP plugin can send data to and receive data + from an AMQP broker. Thanks to Sebastien Pahl for his code. + * apache plugin: Backwards compatibility code has been removed. + Support for the IBM HTTP Server has been added. Thanks to Manuel + Luis Sanmartín Rozada for his patch. + * contextswitch plugin: Support for sysctlbyname(3) has been added. + Thanks to Kimo Rosenbaum for his patch. + * df plugin: The default behavior has been changed to be equivalent to + the "ReportReserved" behavior of v4. + * dns plugin: Improved RFC 1035 name parsing has been imported from + "dnstop". + * exec plugin: Backwards compatibility code has been removed. + * GenericJMX plugin: The "InstancePrefix" option has been added to + "Connection" blocks. + * hddtemp plugin: The "TranslateDevicename" config option has been + removed. + * interface plugin: Use the "plugin instance" to store the interface + value. + * libvirt plugin: The "InterfaceFormat" option has been added. Thanks + to Ruben Kerkhof for his patch. + * lpar plugin: New plugins for "logical partitions", a virtualization + technique of POWER CPUs. Thanks to Aurélien Reynaud for his code and + patience. + * modbus plugin: Support for libmodbus 2.9.2 has been added and the + license has been changes to LGPLv2.1. + * mysql plugin: Backwards compatibility code has been removed. The + data sets used have been improved. + * network plugin: The default buffer size has been increased to + 1452 bytes. + * perl plugin: Backwards compatibility code has been removed. + * postgresql plugin: Backwards compatibility code has been removed. + * redis plugin: Plugin for collecting statistics from Redis, a key- + value store, has been added. Thanks to Andres J. Diaz for his code. + * swap plugin: Implement collection of physical and virtual memory + statistics under Solaris. The new default is collecting physical + memory. Thanks to Aurélien Reynaud for his patches. + * threshold plugin: The threshold configuration has been moved into + this separate plugin. + * unixsock plugin: The "DeleteSocket" option has been added. + * varnish plugin: The new Varnish plugin reads statistics from + Varnish, a web accelerator. Thanks to Jérôme Renard and Marc + Fournier for their contributions. + * write_redis: New plugin for writing data to Redis, a key-value + store. + * zfs_arc plugin: The data sets have been replaced by more elegant + alternatives. + * v5upgrade target: Target for converting v4 data sets to the v5 + schema. + 2012-11-11, Version 4.10.8 * collectd: Create new directories with mode 0777 and let umask remove unwanted permission bits. diff --git a/README b/README index 34caa85d..ac79ce50 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 @@ -348,6 +363,9 @@ Features requests. The transmitted data is either in a form understood by the Exec plugin or formatted in JSON. + - write_redis + Sends the values to a Redis key-value database server. + * Logging is, as everything in collectd, provided by plugins. The following plugins keep up informed about what's going on: @@ -427,6 +445,10 @@ Features * Miscellaneous plugins: + - threshold + Checks values against configured thresholds and creates notifications if + values are out of bounds. See collectd-threshold(5) for details. + - uuid Sets the hostname to an unique identifier. This is meant for setups where each client may migrate to another physical host, possibly going @@ -504,6 +526,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. @@ -610,6 +636,10 @@ Prerequisites are 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'. @@ -656,6 +686,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/api/CollectdFlushInterface.java b/bindings/java/org/collectd/api/CollectdFlushInterface.java index 3e492ddf..410c61c6 100644 --- a/bindings/java/org/collectd/api/CollectdFlushInterface.java +++ b/bindings/java/org/collectd/api/CollectdFlushInterface.java @@ -29,5 +29,5 @@ package org.collectd.api; */ public interface CollectdFlushInterface { - public int flush (int timeout, String identifier); + public int flush (Number timeout, String identifier); } diff --git a/bindings/java/org/collectd/api/ValueList.java b/bindings/java/org/collectd/api/ValueList.java index 1baeff24..b8d6f40f 100644 --- a/bindings/java/org/collectd/api/ValueList.java +++ b/bindings/java/org/collectd/api/ValueList.java @@ -87,10 +87,16 @@ public class ValueList extends PluginData { _ds = new DataSet (_type, dsrc); } + /** + * Returns the interval (in milliseconds) of the value list. + */ public long getInterval() { return _interval; } + /** + * Sets the interval (in milliseconds) of the value list. + */ public void setInterval(long interval) { _interval = interval; } 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 b2b496ab..027bf9c3 100644 --- a/configure.in +++ b/configure.in @@ -91,6 +91,7 @@ fi if test "x$ac_system" = "xSolaris" then AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Define to enforce POSIX thread semantics under Solaris.]) + AC_DEFINE(_REENTRANT, 1, [Define to enable reentrancy interfaces.]) fi if test "x$ac_system" = "xAIX" then @@ -109,9 +110,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") @@ -486,6 +491,8 @@ AC_CHECK_HEADERS(netinet/if_ether.h, [], [], #endif ]) +AC_CHECK_HEADERS(netinet/ip_compat.h) + # For the multimeter plugin have_termios_h="no" AC_CHECK_HEADERS(termios.h, [have_termios_h="yes"]) @@ -587,6 +594,27 @@ socket_needs_socket="no" AC_CHECK_FUNCS(socket, [], AC_CHECK_LIB(socket, socket, [socket_needs_socket="yes"], AC_MSG_ERROR(cannot find socket))) AM_CONDITIONAL(BUILD_WITH_LIBSOCKET, test "x$socket_needs_socket" = "xyes") +clock_gettime_needs_rt="no" +clock_gettime_needs_posix4="no" +have_clock_gettime="no" +AC_CHECK_FUNCS(clock_gettime, [have_clock_gettime="yes"]) +if test "x$have_clock_gettime" = "xno" +then + AC_CHECK_LIB(rt, clock_gettime, [clock_gettime_needs_rt="yes" + have_clock_gettime="yes"]) +fi +if test "x$have_clock_gettime" = "xno" +then + AC_CHECK_LIB(posix4, clock_gettime, [clock_gettime_needs_posix4="yes" + have_clock_gettime="yes"]) +fi +if test "x$have_clock_gettime" = "xyes" +then + AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [Define if the clock_gettime(2) function is available.]) +else + AC_MSG_WARN(cannot find clock_gettime) +fi + nanosleep_needs_rt="no" nanosleep_needs_posix4="no" AC_CHECK_FUNCS(nanosleep, @@ -596,8 +624,9 @@ AC_CHECK_FUNCS(nanosleep, AC_CHECK_LIB(posix4, nanosleep, [nanosleep_needs_posix4="yes"], AC_MSG_ERROR(cannot find nanosleep)))) -AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes") -AM_CONDITIONAL(BUILD_WITH_LIBPOSIX4, test "x$nanosleep_needs_posix4" = "xyes") + +AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$clock_gettime_needs_rt" = "xyes" || test "x$nanosleep_needs_rt" = "xyes") +AM_CONDITIONAL(BUILD_WITH_LIBPOSIX4, test "x$clock_gettime_needs_posix4" = "xyes" || test "x$nanosleep_needs_posix4" = "xyes") AC_CHECK_FUNCS(sysctl, [have_sysctl="yes"], [have_sysctl="no"]) AC_CHECK_FUNCS(sysctlbyname, [have_sysctlbyname="yes"], [have_sysctlbyname="no"]) @@ -607,13 +636,127 @@ AC_CHECK_FUNCS(thread_info, [have_thread_info="yes"], [have_thread_info="no"]) AC_CHECK_FUNCS(statfs, [have_statfs="yes"], [have_statfs="no"]) AC_CHECK_FUNCS(statvfs, [have_statvfs="yes"], [have_statvfs="no"]) AC_CHECK_FUNCS(getifaddrs, [have_getifaddrs="yes"], [have_getifaddrs="no"]) +AC_CHECK_FUNCS(getloadavg, [have_getloadavg="yes"], [have_getloadavg="no"]) 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"]) -# For load module -AC_CHECK_FUNCS(getloadavg, [have_getloadavg="yes"], [have_getloadavg="no"]) +# Check for strptime {{{ +if test "x$GCC" = "xyes" +then + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -Wall -Wextra -Werror" +fi + +AC_CHECK_FUNCS(strptime, [have_strptime="yes"], [have_strptime="no"]) +if test "x$have_strptime" = "xyes" +then + AC_CACHE_CHECK([whether strptime is exported by default], + [c_cv_have_strptime_default], + AC_COMPILE_IFELSE( +AC_LANG_PROGRAM( +[[ +AC_INCLUDES_DEFAULT +#include +]], +[[ + struct tm stm; + (void) strptime ("2010-12-30%13:42:42", "%Y-%m-%dT%T", &stm); +]]), + [c_cv_have_strptime_default="yes"], + [c_cv_have_strptime_default="no"])) +fi +if test "x$have_strptime" = "xyes" && test "x$c_cv_have_strptime_default" = "xno" +then + AC_CACHE_CHECK([whether strptime needs standards mode], + [c_cv_have_strptime_standards], + AC_COMPILE_IFELSE( +AC_LANG_PROGRAM( +[[ +#ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE 1 +#endif +#ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112L +#endif +#ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 500 +#endif +AC_INCLUDES_DEFAULT +#include +]], +[[ + struct tm stm; + (void) strptime ("2010-12-30%13:42:42", "%Y-%m-%dT%T", &stm); +]]), + [c_cv_have_strptime_standards="yes"], + [c_cv_have_strptime_standards="no"])) + + if test "x$c_cv_have_strptime_standards" = "xyes" + then + AC_DEFINE([STRPTIME_NEEDS_STANDARDS], 1, [Set to true if strptime is only exported in X/Open mode (GNU libc).]) + else + have_strptime="no" + fi +fi + +if test "x$GCC" = "xyes" +then + CFLAGS="$SAVE_CFLAGS" +fi + +# }}} Check for strptime + +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 # Check for NAN AC_ARG_WITH(nan-emulation, [AS_HELP_STRING([--with-nan-emulation], [use emulated NAN. For crosscompiling only.])], @@ -635,7 +778,7 @@ if test "x$nan_type" = "xnone"; then [[ #include #include -static float foo = NAN; +static double foo = NAN; ]], [[ if (isnan (foo)) @@ -661,7 +804,7 @@ if test "x$nan_type" = "xnone"; then #include #define __USE_ISOC99 1 #include -static float foo = NAN; +static double foo = NAN; ]], [[ if (isnan (foo)) @@ -695,7 +838,7 @@ if test "x$nan_type" = "xnone"; then #ifndef isnan # define isnan(f) ((f) != (f)) #endif -static float foo = NAN; +static double foo = NAN; ]], [[ if (isnan (foo)) @@ -1165,6 +1308,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="" @@ -1957,7 +2164,7 @@ then $PKG_CONFIG --exists 'modbus' 2>/dev/null if test $? -ne 0 then - with_libmodbus="no (pkg-config doesn't know library)" + with_libmodbus="no (pkg-config doesn't know modbus)" fi fi if test "x$with_libmodbus" = "xuse_pkgconfig" @@ -1997,9 +2204,9 @@ then CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags" LDFLAGS="$LDFLAGS $with_libmodbus_libs" - AC_CHECK_LIB(modbus, modbus_init_tcp, + AC_CHECK_LIB(modbus, modbus_connect, [with_libmodbus="yes"], - [with_libmodbus="no (symbol modbus_init_tcp not found)"]) + [with_libmodbus="no (symbol modbus_connect not found)"]) CPPFLAGS="$SAVE_CPPFLAGS" LDFLAGS="$SAVE_LDFLAGS" @@ -2491,7 +2698,7 @@ then fi AC_CHECK_HEADERS(oping.h, [with_liboping="yes"], - [with_liboping="no ('oping.h' not found)"]) + [with_liboping="no (oping.h not found)"]) fi if test "x$with_liboping" = "xyes" then @@ -2662,7 +2869,8 @@ then fi if test "x$with_libpcap" = "xyes" then - AC_CHECK_HEADERS(pcap-bpf.h) + AC_CHECK_HEADERS(pcap-bpf.h,, + [with_libpcap="no (pcap-bpf.h not found)"]) fi AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes") # }}} @@ -3097,6 +3305,72 @@ 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" +]) +SAVE_CPPFLAGS="$CPPFLAGS" +SAVE_LDFLAGS="$LDFLAGS" +CPPFLAGS="$CPPFLAGS $with_librabbitmq_cppflags" +LDFLAGS="$LDFLAGS $with_librabbitmq_ldflags" +if test "x$with_librabbitmq" = "xyes" +then + AC_CHECK_HEADERS(amqp.h, [with_librabbitmq="yes"], [with_librabbitmq="no (amqp.h not found)"]) +fi +if test "x$with_librabbitmq" = "xyes" +then + # librabbitmq up to version 0.9.1 provides "library_errno", later + # versions use "library_error". The library does not provide a version + # macro :( Use "AC_CHECK_MEMBERS" (plural) for automatic defines. + AC_CHECK_MEMBERS([amqp_rpc_reply_t.library_errno],,, + [ +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_STDIO_H +# include +#endif +#if HAVE_STDINT_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#endif +#include + ]) +fi +if test "x$with_librabbitmq" = "xyes" +then + AC_CHECK_LIB(rabbitmq, amqp_basic_publish, [with_librabbitmq="yes"], [with_librabbitmq="no (Symbol 'amqp_basic_publish' not found)"]) +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 +CPPFLAGS="$SAVE_CPPFLAGS" +LDFLAGS="$SAVE_LDFLAGS" +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.])], [ @@ -3128,7 +3402,7 @@ then fi AC_CHECK_HEADERS(routeros_api.h, [with_librouteros="yes"], - [with_librouteros="no ('routeros_api.h' not found)"]) + [with_librouteros="no (routeros_api.h not found)"]) fi if test "x$with_librouteros" = "xyes" then @@ -3327,7 +3601,7 @@ then if test "$?" != "0" then with_libstatgrab_pkg_config="no" - with_libstatgrab="no ($PKG_CONFIG doesn't know libstatgrab)" + with_libstatgrab="no (pkg-config doesn't know libstatgrab)" temp_result="not found" fi AC_MSG_RESULT([$temp_result]) @@ -3547,7 +3821,7 @@ then $PKG_CONFIG --exists 'libupsclient' 2>/dev/null if test $? -ne 0 then - with_libupsclient="no (pkg-config doesn't know library)" + with_libupsclient="no (pkg-config doesn't know libupsclient)" fi fi if test "x$with_libupsclient" = "xuse_pkgconfig" @@ -3737,6 +4011,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 -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 varnishapi)" + 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="" @@ -3751,7 +4117,7 @@ then then with_libxml2="yes" else - with_libxml2="no (pkg-config doesn't know library)" + with_libxml2="no (pkg-config doesn't know libxml-2.0)" fi pkg-config --exists libvirt 2>/dev/null @@ -3759,7 +4125,7 @@ then then with_libvirt="yes" else - with_libvirt="no (pkg-config doesn't know library)" + with_libvirt="no (pkg-config doesn't know libvirt)" fi fi if test "x$with_libxml2" = "xyes" @@ -3876,7 +4242,7 @@ then $PKG_CONFIG --exists OpenIPMIpthread 2>/dev/null if test "$?" != "0" then - with_libopenipmipthread="no ($PKG_CONFIG doesn't know OpenIPMIpthread)" + with_libopenipmipthread="no (pkg-config doesn't know OpenIPMIpthread)" fi AC_MSG_RESULT([$with_libopenipmipthread]) fi @@ -3937,7 +4303,11 @@ fi PKG_CHECK_MODULES([LIBNOTIFY], [libnotify], [with_libnotify="yes"], - [with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)"]) + [if test "x$LIBNOTIFY_PKG_ERRORS" = "x"; then + with_libnotify="no" + else + with_libnotify="no ($LIBNOTIFY_PKG_ERRORS)" + fi]) # Check for enabled/disabled features # @@ -4180,11 +4550,6 @@ then plugin_tape="yes" fi -if test "x$have_sys_swap_h$with_kstat$ac_system" = "xyesyesSolaris" -then - plugin_swap="yes" -fi - # libstatgrab if test "x$with_libstatgrab" = "xyes" then @@ -4200,7 +4565,10 @@ fi if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes" then plugin_ascent="yes" - plugin_bind="yes" + if test "x$have_strptime" = "xyes" + then + plugin_bind="yes" + fi fi if test "x$with_libopenipmipthread" = "xyes" @@ -4226,11 +4594,15 @@ if test "x$have_sysctl" = "xyes" then plugin_cpu="yes" plugin_memory="yes" - plugin_swap="yes" plugin_uptime="yes" + if test "x$ac_system" = "xDarwin" + then + plugin_swap="yes" + fi fi if test "x$have_sysctlbyname" = "xyes" then + plugin_contextswitch="yes" plugin_cpu="yes" plugin_memory="yes" plugin_tcpconns="yes" @@ -4318,7 +4690,7 @@ then plugin_swap="yes" fi -if test "x$have_swapctl" = "xyes" +if test "x$have_swapctl" = "xyes" && test "x$c_cv_have_swapctl_two_args" = "xyes" then plugin_swap="yes" fi @@ -4358,6 +4730,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]) @@ -4392,6 +4765,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]) @@ -4427,6 +4801,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]) @@ -4442,19 +4817,23 @@ 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]) AC_PLUGIN([thermal], [$plugin_thermal], [Linux ACPI thermal zone statistics]) +AC_PLUGIN([threshold], [yes], [Threshold checking plugin]) AC_PLUGIN([tokyotyrant], [$with_libtokyotyrant], [TokyoTyrant database statistics]) 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]) @@ -4629,6 +5008,7 @@ Configuration: Libraries: libcurl . . . . . . . $with_libcurl libdbi . . . . . . . $with_libdbi + libcredis . . . . . . $with_libcredis libesmtp . . . . . . $with_libesmtp libganglia . . . . . $with_libganglia libgcrypt . . . . . . $with_libgcrypt @@ -4652,12 +5032,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 @@ -4675,6 +5057,7 @@ Configuration: perl . . . . . . . . $with_perl_bindings Modules: + amqp . . . . . . . $enable_amqp apache . . . . . . . $enable_apache apcups . . . . . . . $enable_apcups apple_sensors . . . . $enable_apple_sensors @@ -4709,6 +5092,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 @@ -4743,6 +5127,7 @@ Configuration: processes . . . . . . $enable_processes protocols . . . . . . $enable_protocols python . . . . . . . $enable_python + redis . . . . . . . . $enable_redis routeros . . . . . . $enable_routeros rrdcached . . . . . . $enable_rrdcached rrdtool . . . . . . . $enable_rrdtool @@ -4758,19 +5143,23 @@ 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 thermal . . . . . . . $enable_thermal + threshold . . . . . . $enable_threshold tokyotyrant . . . . . $enable_tokyotyrant unixsock . . . . . . $enable_unixsock 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/collectd_network.py b/contrib/collectd_network.py index 445b1838..9af0fb00 100644 --- a/contrib/collectd_network.py +++ b/contrib/collectd_network.py @@ -4,19 +4,23 @@ # # Copyright © 2009 Adrian Perez # -# Distributed under terms of the GPLv2 license. +# Distributed under terms of the GPLv2 license or newer. +# +# Frank Marien (frank@apsu.be) 6 Sep 2012 +# - quick fixes for 5.1 binary protocol +# - updated to python 3 +# - fixed for larger packet sizes (possible on lo interface) +# - fixed comment typo (decode_network_string decodes a string) """ Collectd network protocol implementation. """ -import socket -import struct - +import socket,struct,sys try: - from cStringIO import StringIO + from io import StringIO except ImportError: - from StringIO import StringIO + from cStringIO import StringIO from datetime import datetime from copy import deepcopy @@ -31,17 +35,19 @@ DEFAULT_IPv4_GROUP = "239.192.74.66" DEFAULT_IPv6_GROUP = "ff18::efc0:4a42" """Default IPv6 multicast group""" - +HR_TIME_DIV = (2.0**30) # Message kinds TYPE_HOST = 0x0000 TYPE_TIME = 0x0001 +TYPE_TIME_HR = 0x0008 TYPE_PLUGIN = 0x0002 TYPE_PLUGIN_INSTANCE = 0x0003 TYPE_TYPE = 0x0004 TYPE_TYPE_INSTANCE = 0x0005 TYPE_VALUES = 0x0006 TYPE_INTERVAL = 0x0007 +TYPE_INTERVAL_HR = 0x0009 # For notifications TYPE_MESSAGE = 0x0100 @@ -50,14 +56,14 @@ TYPE_SEVERITY = 0x0101 # DS kinds DS_TYPE_COUNTER = 0 DS_TYPE_GAUGE = 1 - +DS_TYPE_DERIVE = 2 +DS_TYPE_ABSOLUTE = 3 header = struct.Struct("!2H") number = struct.Struct("!Q") short = struct.Struct("!H") double = struct.Struct("{$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 02bd0a33..c7f18c58 100755 --- a/contrib/exec-nagios.px +++ b/contrib/exec-nagios.px @@ -25,7 +25,8 @@ our $ConfigFile = '/etc/exec-nagios.conf'; our $TypeMap = {}; our $NRPEMap = {}; 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); @@ -351,6 +352,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; @@ -366,7 +368,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 @@ -376,7 +380,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/contrib/migrate-4-5.px b/contrib/migrate-4-5.px new file mode 100755 index 00000000..c2a95558 --- /dev/null +++ b/contrib/migrate-4-5.px @@ -0,0 +1,255 @@ +#!/usr/bin/perl + +# collectd - contrib/migrate-4-5.px +# 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 + +use strict; +use warnings; + +use Getopt::Long ('GetOptions'); +use Data::Dumper (); +use File::Basename ('dirname'); + +our $InDir = '/var/lib/collectd'; +our $RRDtool = 'rrdtool'; +our $RRDFilter = 'rrd_filter.px'; + +our %TypesCounterToDerive = # {{{ +( + apache_bytes => ["count"], + apache_requests => ["count"], + arc_counts => ["demand_data", "demand_metadata", "prefetch_data", "prefetch_metadata"], + arc_l2_bytes => ["read", "write"], + ath_stat => ["value"], + compression => ["uncompressed", "compressed"], + connections => ["value"], + cpu => ["value"], + current => ["value"], + disk_merged => ["read", "write"], + disk_octets => ["read", "write"], + disk_ops => ["read", "write"], + disk_ops_complex => ["value"], + disk_time => ["read", "write"], + dns_answer => ["value"], + dns_notify => ["value"], + dns_octets => ["queries", "responses"], + dns_opcode => ["value"], + dns_qtype => ["value"], + dns_query => ["value"], + dns_question => ["value"], + dns_rcode => ["value"], + dns_reject => ["value"], + dns_request => ["value"], + dns_resolver => ["value"], + dns_response => ["value"], + dns_transfer => ["value"], + dns_update => ["value"], + dns_zops => ["value"], + fscache_stat => ["value"], + fork_rate => ["value"], + http_request_methods => ["count"], + http_requests => ["count"], + http_response_codes => ["count"], + if_collisions => ["value"], + if_dropped => ["rx", "tx"], + if_errors => ["rx", "tx"], + if_multicast => ["value"], + if_octets => ["rx", "tx"], + if_packets => ["rx", "tx"], + if_rx_errors => ["value"], + if_tx_errors => ["value"], + io_octets => ["rx", "tx"], + io_packets => ["rx", "tx"], + ipt_bytes => ["value"], + ipt_packets => ["value"], + irq => ["value"], + memcached_command => ["value"], + memcached_octets => ["rx", "tx"], + memcached_ops => ["value"], + mysql_commands => ["value"], + mysql_handler => ["value"], + mysql_locks => ["value"], + mysql_log_position => ["value"], + mysql_octets => ["rx", "tx"], + nfs_procedure => ["value"], + nginx_requests => ["value"], + node_octets => ["rx", "tx"], + node_stat => ["value"], + operations => ["value"], + pg_blks => ["value"], + pg_n_tup_c => ["value"], + pg_scan => ["value"], + pg_xact => ["value"], + protocol_counter => ["value"], + ps_cputime => ["user", "syst"], + ps_pagefaults => ["minflt", "majflt"], + ps_code => ["value"], + ps_data => ["value"], + serial_octets => ["rx", "tx"], + swap_io => ["value"], + virt_cpu_total => ["ns"], + virt_vcpu => ["ns"], + vmpage_action => ["value"], + vmpage_faults => ["minflt", "majflt"], + vmpage_io => ["in", "out"], +); # }}} %TypesCounterToDerive + +our %TypesRenameDataSource = # {{{ +( + absolute => "count", + apache_bytes => "count", + apache_connections => "count", + apache_idle_workers => "count", + apache_requests => "count", + apache_scoreboard => "count", + conntrack => "entropy", + contextswitch => "contextswitches", + delay => "seconds", + entropy => "entropy", + file_size => "bytes", + frequency => "frequency", + frequency_offset => "ppm", + http_request_methods => "count", + http_requests => "count", + http_response_codes => "count", + percent => "percent", + ping => "ping", + records => "count", + time_dispersion => "seconds", + timeleft => "timeleft", + time_offset => "seconds", + users => "users", + virt_cpu_total => "ns", + virt_vcpu => "ns", +); # }}} %TypesRenameDataSource + +sub handle_file # {{{ +{ + my @path = @_; + my $path = join ('/', @path); + + if (!($path =~ m/\.rrd$/)) + { + return; + } + + my $tmp = pop (@path); + $tmp =~ s/\.rrd$//; + my ($type, $type_inst) = split (m/-/, $tmp, 2); + $type_inst ||= ''; + + $tmp = pop (@path); + my ($plugin, $plugin_inst) = split (m/-/, $tmp, 2); + $plugin_inst ||= ''; + + if ($TypesRenameDataSource{$type}) + { + my $old_ds = $TypesRenameDataSource{$type}; + print "$RRDtool tune \"$path\" --data-source-rename ${old_ds}:value\n"; + } + + if ($TypesCounterToDerive{$type}) + { + my $ds_names = $TypesCounterToDerive{$type}; + + for (@$ds_names) + { + my $name = $_; + print "$RRDtool tune \"$path\" --data-source-type ${name}:DERIVE --minimum ${name}:0 --maximum ${name}:U\n"; + } + } + + if ((($plugin eq 'df') || ($plugin eq 'interface')) + && (!$plugin_inst) && ($type_inst)) + { + my $dir = join ('/', @path); + print "mkdir -p \"$dir/$plugin-$type_inst\"\n"; + if (($plugin eq 'df') and ($type eq 'df')) + { + print "$RRDFilter --infile=\"$path\" --outfile=\"$dir/$plugin-$type_inst/df_complex-free.rrd\" --map free:value\n"; + print "$RRDFilter --infile=\"$path\" --outfile=\"$dir/$plugin-$type_inst/df_complex-used.rrd\" --map used:value\n"; + } + else + { + print "mv \"$path\" \"$dir/$plugin-$type_inst/$type.rrd\"\n"; + } + } +} # }}} sub handle_file + +sub scan_dir # {{{ +{ + my @dir_parts = @_; + my $dir_str = join ('/', @dir_parts); + my $dh; + + opendir ($dh, $dir_str) || die; + while (my $entry = readdir ($dh)) + { + my $entry_path = "$dir_str/$entry"; + + if ($entry =~ m/^\./) + { + next; + } + + if (-d $entry_path) + { + scan_dir (@dir_parts, $entry); + } + elsif (-f $entry_path) + { + handle_file (@dir_parts, $entry); + } + } + closedir ($dh); +} # }}} sub scan_dir + +sub exit_usage # {{{ +{ + print STDERR < Source directory + Default: $InDir + --rrdtool Path to the RRDtool binary + Default: $RRDtool + --rrdfilter Path to the rrd_filter.px script + Default: $RRDFilter + +EOF + exit (1); +} # }}} sub exit_usage + +GetOptions ("indir|i=s" => \$InDir, + "rrdtool=s" => \$RRDtool, + "rrdfilter=s" => \$RRDFilter, + "help|h" => \&exit_usage) or exit_usage (); + +print "#!/bin/bash\n\n"; + +scan_dir ($InDir); + +# vim: set sw=2 sts=2 et fdm=marker : diff --git a/src/Makefile.am b/src/Makefile.am index 40b8293d..45fd6f1a 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 \ @@ -40,7 +40,7 @@ collectd_SOURCES = collectd.c collectd.h \ utils_match.c utils_match.h \ utils_subst.c utils_subst.h \ utils_tail.c utils_tail.h \ - utils_threshold.c utils_threshold.h \ + utils_time.c utils_time.h \ types_list.c types_list.h collectd_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) @@ -104,11 +104,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 @@ -500,6 +525,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 @@ -886,6 +920,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 @@ -1043,6 +1087,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 @@ -1079,6 +1131,14 @@ collectd_LDADD += "-dlopen" thermal.la collectd_DEPENDENCIES += thermal.la endif +if BUILD_PLUGIN_THRESHOLD +pkglib_LTLIBRARIES += threshold.la +threshold_la_SOURCES = threshold.c +threshold_la_LDFLAGS = -module -avoid-version +collectd_LDADD += "-dlopen" threshold.la +collectd_DEPENDENCIES += threshold.la +endif + if BUILD_PLUGIN_TOKYOTYRANT pkglib_LTLIBRARIES += tokyotyrant.la tokyotyrant_la_SOURCES = tokyotyrant.c @@ -1097,7 +1157,6 @@ pkglib_LTLIBRARIES += unixsock.la unixsock_la_SOURCES = unixsock.c \ utils_cmd_flush.h utils_cmd_flush.c \ utils_cmd_getval.h utils_cmd_getval.c \ - utils_cmd_getthreshold.h utils_cmd_getthreshold.c \ utils_cmd_listval.h utils_cmd_listval.c \ utils_cmd_putval.h utils_cmd_putval.c \ utils_cmd_putnotif.h utils_cmd_putnotif.c @@ -1144,6 +1203,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 @@ -1183,6 +1252,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 @@ -1209,12 +1288,14 @@ dist_man_MANS = collectd.1 \ collectd.conf.5 \ collectd-email.5 \ collectd-exec.5 \ + collectdctl.1 \ collectd-java.5 \ collectdmon.1 \ collectd-nagios.1 \ collectd-perl.5 \ collectd-python.5 \ collectd-snmp.5 \ + collectd-threshold.5 \ collectd-unixsock.5 \ types.db.5 @@ -1225,6 +1306,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 \ @@ -1232,6 +1314,7 @@ EXTRA_DIST += collectd.conf.pod \ collectd-python.pod \ collectd.pod \ collectd-snmp.pod \ + collectd-threshold.pod \ collectd-unixsock.pod \ postgresql_default.conf \ types.db.pod @@ -1253,7 +1336,7 @@ EXTRA_DIST += collectd.conf.pod \ fi pinba.pb-c.c pinba.pb-c.h: pinba.proto - protoc-c --c_out $(builddir) pinba.proto + protoc-c --c_out . pinba.proto install-exec-hook: $(mkinstalldirs) $(DESTDIR)$(sysconfdir) diff --git a/src/amqp.c b/src/amqp.c new file mode 100644 index 00000000..89284c81 --- /dev/null +++ b/src/amqp.c @@ -0,0 +1,994 @@ +/** + * collectd - src/amqp.c + * Copyright (C) 2009 Sebastien Pahl + * Copyright (C) 2010-2012 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 HAVE_AMQP_RPC_REPLY_T_LIBRARY_ERRNO + if (r.library_errno) + return (sstrerror (r.library_errno, buffer, buffer_size)); +#else + if (r.library_error) + return (sstrerror (r.library_error, buffer, buffer_size)); +#endif + 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 */ + +#if HAVE_AMQP_RPC_REPLY_T_LIBRARY_ERRNO +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 */ +#else +static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */ +{ + amqp_exchange_declare_ok_t *ed_ret; + amqp_table_t argument_table; + struct amqp_table_entry_t_ argument_table_entries[1]; + + if (conf->exchange_type == NULL) + return (0); + + /* Valid arguments: "auto_delete", "internal" */ + argument_table.num_entries = STATIC_ARRAY_SIZE (argument_table_entries); + argument_table.entries = argument_table_entries; + argument_table_entries[0].key = amqp_cstring_bytes ("auto_delete"); + argument_table_entries[0].value.kind = AMQP_FIELD_KIND_BOOLEAN; + argument_table_entries[0].value.value.boolean = 1; + + 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, + /* arguments = */ argument_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 */ +#endif + +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, + /* arguments = */ AMQP_EMPTY_TABLE + ); + 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) + { + struct timespec ts_interval; + ERROR ("amqp plugin: camqp_connect failed. " + "Will sleep for %.3f seconds.", + CDTIME_T_TO_DOUBLE (interval_g)); + CDTIME_T_TO_TIMESPEC (interval_g, &ts_interval); + nanosleep (&ts_interval, /* remaining = */ NULL); + continue; + } + + status = amqp_simple_wait_frame (conf->connection, &frame); + if (status < 0) + { + struct timespec ts_interval; + ERROR ("amqp plugin: amqp_simple_wait_frame failed. " + "Will sleep for %.3f seconds.", + CDTIME_T_TO_DOUBLE (interval_g)); + camqp_close_connection (conf); + CDTIME_T_TO_TIMESPEC (interval_g, &ts_interval); + nanosleep (&ts_interval, /* remaining = */ NULL); + 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); + return (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 5f5441ff..c31dd875 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 */ @@ -421,6 +388,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); @@ -508,13 +477,13 @@ static void submit_value (const char *type, const char *type_instance, plugin_dispatch_values (&vl); } /* void submit_value */ -static void submit_counter (const char *type, const char *type_instance, - counter_t c, apache_t *st) +static void submit_derive (const char *type, const char *type_instance, + derive_t c, apache_t *st) { value_t v; - v.counter = c; + v.derive = c; submit_value (type, type_instance, v, st); -} /* void submit_counter */ +} /* void submit_derive */ static void submit_gauge (const char *type, const char *type_instance, gauge_t g, apache_t *st) @@ -676,11 +645,11 @@ static int apache_read_host (user_data_t *user_data) /* {{{ */ { if ((strcmp (fields[0], "Total") == 0) && (strcmp (fields[1], "Accesses:") == 0)) - submit_counter ("apache_requests", "", + submit_derive ("apache_requests", "", atoll (fields[2]), st); else if ((strcmp (fields[0], "Total") == 0) && (strcmp (fields[1], "kBytes:") == 0)) - submit_counter ("apache_bytes", "", + submit_derive ("apache_bytes", "", 1024LL * atoll (fields[2]), st); } else if (fields_num == 2) diff --git a/src/bind.c b/src/bind.c index 28c1e56a..38d4a27f 100644 --- a/src/bind.c +++ b/src/bind.c @@ -1,7 +1,7 @@ /** * collectd - src/bind.c - * Copyright (C) 2009 Bruno Prémont - * Copyright (C) 2009 Florian Forster + * Copyright (C) 2009 Bruno Prémont + * Copyright (C) 2009,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 @@ -18,14 +18,22 @@ * * Authors: * Bruno Prémont - * Florian Forster + * Florian Forster **/ #include "config.h" -#ifndef _XOPEN_SOURCE -# define _XOPEN_SOURCE 600 /* glibc2 needs this for strptime */ -#endif +#if STRPTIME_NEEDS_STANDARDS +# ifndef _ISOC99_SOURCE +# define _ISOC99_SOURCE 1 +# endif +# ifndef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112L +# endif +# ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 500 +# endif +#endif /* STRPTIME_NEEDS_STANDARDS */ #include "collectd.h" #include "common.h" @@ -241,7 +249,7 @@ static void submit (time_t ts, const char *plugin_instance, /* {{{ */ vl.values = values; vl.values_len = 1; - vl.time = ts; + vl.time = TIME_T_TO_CDTIME_T (ts); sstrncpy(vl.host, hostname_g, sizeof(vl.host)); sstrncpy(vl.plugin, "bind", sizeof(vl.plugin)); if (plugin_instance) { @@ -337,36 +345,31 @@ static int bind_xml_list_callback (const char *name, /* {{{ */ return (0); } /* }}} int bind_xml_list_callback */ -static int bind_xml_read_counter (xmlDoc *doc, xmlNode *node, /* {{{ */ - counter_t *ret_value) +static int bind_xml_read_derive (xmlDoc *doc, xmlNode *node, /* {{{ */ + derive_t *ret_value) { - char *str_ptr, *end_ptr; - long long int value; + char *str_ptr; + value_t value; + int status; str_ptr = (char *) xmlNodeListGetString (doc, node->xmlChildrenNode, 1); if (str_ptr == NULL) { - ERROR ("bind plugin: bind_xml_read_counter: xmlNodeListGetString failed."); + ERROR ("bind plugin: bind_xml_read_derive: xmlNodeListGetString failed."); return (-1); } - errno = 0; - value = strtoll (str_ptr, &end_ptr, 10); - xmlFree(str_ptr); - if (str_ptr == end_ptr || errno) + status = parse_value (str_ptr, &value, DS_TYPE_DERIVE); + if (status != 0) { - if (errno && (value < 0)) - ERROR ("bind plugin: bind_xml_read_counter: strtoll failed with underflow."); - else if (errno && (value > 0)) - ERROR ("bind plugin: bind_xml_read_counter: strtoll failed with overflow."); - else - ERROR ("bind plugin: bind_xml_read_counter: strtoll failed."); + ERROR ("bind plugin: Parsing string \"%s\" to derive value failed.", + str_ptr); return (-1); } - *ret_value = value; + *ret_value = value.derive; return (0); -} /* }}} int bind_xml_read_counter */ +} /* }}} int bind_xml_read_derive */ static int bind_xml_read_gauge (xmlDoc *doc, xmlNode *node, /* {{{ */ gauge_t *ret_value) @@ -527,7 +530,7 @@ static int bind_parse_generic_name_value (const char *xpath_expression, /* {{{ * if (ds_type == DS_TYPE_GAUGE) status = bind_xml_read_gauge (doc, counter, &value.gauge); else - status = bind_xml_read_counter (doc, counter, &value.counter); + status = bind_xml_read_derive (doc, counter, &value.derive); if (status != 0) continue; @@ -600,7 +603,7 @@ static int bind_parse_generic_value_list (const char *xpath_expression, /* {{{ * if (ds_type == DS_TYPE_GAUGE) status = bind_xml_read_gauge (doc, child, &value.gauge); else - status = bind_xml_read_counter (doc, child, &value.counter); + status = bind_xml_read_derive (doc, child, &value.derive); if (status != 0) continue; 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..af8744de 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 @@ -35,14 +35,6 @@ #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*/ @@ -66,8 +58,16 @@ # ifndef isnan # define isnan(f) ((f) != (f)) # endif /* !defined(isnan) */ +# ifndef isfinite +# define isfinite(f) (((f) - (f)) == 0.0) +# endif +# ifndef isinf +# define isinf(f) (!isfinite(f) && !isnan(f)) +# endif #endif /* NAN_ZERO_ZERO */ +#include "libcollectdclient/client.h" + #define RET_OKAY 0 #define RET_WARNING 1 #define RET_CRITICAL 2 @@ -96,6 +96,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 +255,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 +282,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 +344,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 +403,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 +464,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 +595,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 +655,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..d5401dd4 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 @@ -678,9 +656,9 @@ A very simple read function might look like: sub foobar_read { - my $vl = { plugin => 'foobar' }; + my $vl = { plugin => 'foobar', type => 'gauge' }; $vl->{'values'} = [ rand(42) ]; - plugin_dispatch_values ('gauge', $vl); + plugin_dispatch_values ($vl); return 1; } diff --git a/src/collectd-threshold.pod b/src/collectd-threshold.pod new file mode 100644 index 00000000..5d1a645c --- /dev/null +++ b/src/collectd-threshold.pod @@ -0,0 +1,194 @@ +=head1 NAME + +collectd-threshold - Documentation of collectd's I + +=head1 SYNOPSIS + + LoadPlugin "threshold" + + + WarningMin 0.00 + WarningMax 1000.00 + FailureMin 0.00 + FailureMax 1200.00 + Invert false + Instance "bar" + + + +=head1 DESCRIPTION + +Starting with version C<4.3.0> I has support for B. By +that we mean that the values are not only stored or sent somewhere, but that +they are judged and, if a problem is recognized, acted upon. The only action +the I takes itself is to generate and dispatch a +I. Other plugins can register to receive notifications and +perform appropriate further actions. + +Since systems and what you expect them to do differ a lot, you can configure +I for your values freely. This gives you a lot of flexibility but +also a lot of responsibility. + +Every time a value is out of range, a notification is dispatched. This means +that the idle percentage of your CPU needs to be less then the configured +threshold only once for a notification to be generated. There's no such thing +as a moving average or similar - at least not now. + +Also, all values that match a threshold are considered to be relevant or +"interesting". As a consequence collectd will issue a notification if they are +not received for B iterations. The B configuration option is +explained in section L. If, for example, +B is set to "2" (the default) and some hosts sends it's CPU statistics +to the server every 60 seconds, a notification will be dispatched after about +120 seconds. It may take a little longer because the timeout is checked only +once each B on the server. + +When a value comes within range again or is received after it was missing, an +"OKAY-notification" is dispatched. + +=head1 CONFIGURATION + +Here is a configuration example to get you started. Read below for more +information. + + LoadPlugin "threshold" + + + WarningMin 0.00 + WarningMax 1000.00 + FailureMin 0.00 + FailureMax 1200.00 + Invert false + Instance "bar" + + + + Instance "eth0" + + FailureMax 10000000 + DataSource "rx" + + + + + + Instance "idle" + FailureMin 10 + + + + + Instance "cached" + WarningMin 100000000 + + + + + DataSource "midterm" + FailureMax 4 + Hits 3 + Hysteresis 3 + + + + +There are basically two types of configuration statements: The C, +C, and C blocks select the value for which a threshold should be +configured. The C and C blocks may be specified further using the +C option. You can combine the block by nesting the blocks, though +they must be nested in the above order, i.e. C may contain either +C and C blocks, C may only contain C blocks and +C may not contain other blocks. If multiple blocks apply to the same +value the most specific block is used. + +The other statements specify the threshold to configure. They B be +included in a C block. Currently the following statements are recognized: + +=over 4 + +=item B I + +=item B I + +Sets the upper bound of acceptable values. If unset defaults to positive +infinity. If a value is greater than B a B notification +will be created. If the value is greater than B but less than (or +equal to) B a B notification will be created. + +=item B I + +=item B I + +Sets the lower bound of acceptable values. If unset defaults to negative +infinity. If a value is less than B a B notification will +be created. If the value is less than B but greater than (or equal +to) B a B notification will be created. + +=item B I + +Some data sets have more than one "data source". Interesting examples are the +C data set, which has received (C) and sent (C) bytes and +the C data set, which holds C and C operations. The +system load data set, C, even has three data sources: C, +C, and C. + +Normally, all data sources are checked against a configured threshold. If this +is undesirable, or if you want to specify different limits for each data +source, you can use the B option to have a threshold apply only to +one data source. + +=item B B|B + +If set to B the range of acceptable values is inverted, i.e. values +between B and B (B and B) are +not okay. Defaults to B. + +=item B B|B + +Sets how often notifications are generated. If set to B one notification +will be generated for each value that is out of the acceptable range. If set to +B (the default) then a notification is only generated if a value is out +of range but the previous value was okay. + +This applies to missing values, too: If set to B a notification about a +missing value is generated once every B seconds. If set to B +only one such notification is generated until the value appears again. + +=item B B|B + +If set to B, the minimum and maximum values given are interpreted as +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 raised before to +dispatch any notification or, in other words, the number of Bs +that 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 +everything 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 SEE ALSO + +L, +L + +=head1 AUTHOR + +Florian Forster EoctoEatEcollectd.orgE diff --git a/src/collectd.c b/src/collectd.c index 6b77d599..ceb184b0 100644 --- a/src/collectd.c +++ b/src/collectd.c @@ -40,7 +40,7 @@ * Global variables */ char hostname_g[DATA_MAX_NAME_LEN]; -int interval_g; +cdtime_t interval_g; int timeout_g; #if HAVE_LIBKSTAT kstat_ctl_t *kc; @@ -51,7 +51,9 @@ static int loop = 0; static void *do_flush (void __attribute__((unused)) *arg) { INFO ("Flushing all data."); - plugin_flush (NULL, -1, NULL); + plugin_flush (/* plugin = */ NULL, + /* timeout = */ 0, + /* ident = */ NULL); INFO ("Finished flushing all data."); pthread_exit (NULL); return NULL; @@ -140,15 +142,25 @@ static int init_global_variables (void) str = global_option_get ("Interval"); if (str == NULL) - str = "10"; - interval_g = atoi (str); - if (interval_g <= 0) { - fprintf (stderr, "Cannot set the interval to a correct value.\n" - "Please check your settings.\n"); - return (-1); + interval_g = TIME_T_TO_CDTIME_T (10); + } + else + { + double tmp; + + tmp = atof (str); + if (tmp <= 0.0) + { + fprintf (stderr, "Cannot set the interval to a " + "correct value.\n" + "Please check your settings.\n"); + return (-1); + } + + interval_g = DOUBLE_TO_CDTIME_T (tmp); } - DEBUG ("interval_g = %i;", interval_g); + DEBUG ("interval_g = %.3f;", CDTIME_T_TO_DOUBLE (interval_g)); str = global_option_get ("Timeout"); if (str == NULL) @@ -315,22 +327,14 @@ static int do_init (void) static int do_loop (void) { - struct timeval tv_now; - struct timeval tv_next; - struct timeval tv_wait; - struct timespec ts_wait; + cdtime_t wait_until; + + wait_until = cdtime () + interval_g; while (loop == 0) { - if (gettimeofday (&tv_next, NULL) < 0) - { - char errbuf[1024]; - ERROR ("gettimeofday failed: %s", - sstrerror (errno, errbuf, - sizeof (errbuf))); - return (-1); - } - tv_next.tv_sec += interval_g; + struct timespec ts_wait = { 0, 0 }; + cdtime_t now; #if HAVE_LIBKSTAT update_kstat (); @@ -339,27 +343,20 @@ static int do_loop (void) /* Issue all plugins */ plugin_read_all (); - if (gettimeofday (&tv_now, NULL) < 0) - { - char errbuf[1024]; - ERROR ("gettimeofday failed: %s", - sstrerror (errno, errbuf, - sizeof (errbuf))); - return (-1); - } - - if (timeval_cmp (tv_next, tv_now, &tv_wait) <= 0) + now = cdtime (); + if (now >= wait_until) { WARNING ("Not sleeping because the next interval is " - "%i.%06i seconds in the past!", - (int) tv_wait.tv_sec, (int) tv_wait.tv_usec); + "%.3f seconds in the past!", + CDTIME_T_TO_DOUBLE (now - wait_until)); + wait_until = now + interval_g; continue; } - ts_wait.tv_sec = tv_wait.tv_sec; - ts_wait.tv_nsec = (long) (1000 * tv_wait.tv_usec); + CDTIME_T_TO_TIMESPEC (wait_until - now, &ts_wait); + wait_until = wait_until + interval_g; - while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) == -1)) + while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) != 0)) { if (errno != EINTR) { @@ -372,7 +369,6 @@ static int do_loop (void) } } /* while (loop == 0) */ - DEBUG ("return (0);"); return (0); } /* int do_loop */ diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 21d0eb17..cd65fd27 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 @@ -85,6 +86,7 @@ FQDNLookup true #@BUILD_PLUGIN_JAVA_TRUE@LoadPlugin java #@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt @BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load +#@BUILD_PLUGIN_LPAR_TRUE@LoadPlugin lpar #@BUILD_PLUGIN_MADWIFI_TRUE@LoadPlugin madwifi #@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon #@BUILD_PLUGIN_MEMCACHEC_TRUE@LoadPlugin memcachec @@ -118,6 +120,7 @@ FQDNLookup true #@BUILD_PLUGIN_PYTHON_TRUE@ #@BUILD_PLUGIN_PYTHON_TRUE@ Globals true #@BUILD_PLUGIN_PYTHON_TRUE@ +#@BUILD_PLUGIN_REDIS_TRUE@LoadPlugin redis #@BUILD_PLUGIN_ROUTEROS_TRUE@LoadPlugin routeros #@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached @LOAD_PLUGIN_RRDTOOL@LoadPlugin rrdtool @@ -137,10 +140,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 @@ -151,11 +156,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" +# # # @@ -342,13 +363,6 @@ FQDNLookup true # # Host "127.0.0.1" # Port "7634" -# -# #----------------------------------------------------------------# -# # `TranslateDevicename' enables backwards compatibility behavior # -# # and is enabled by default. Setting this option to `false' is # -# # highly recommended. # -# #----------------------------------------------------------------# -# TranslateDevicename false # # @@ -394,6 +408,12 @@ FQDNLookup true # InterfaceDevice "name:device" # IgnoreSelected false # HostnameFormat name +# InterfaceFormat name +# + +# +# CpuPoolStats false +# ReportBySerial false # # @@ -741,6 +761,14 @@ FQDNLookup true # # +# +# +# Host "redis.example.com" +# Port "6379" +# Timeout 2000 +# +# + # # # Host "router.example.com" @@ -888,12 +916,34 @@ FQDNLookup true # SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock" # SocketGroup "collectd" # SocketPerms "0660" +# DeleteSocket false # # # 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 # @@ -910,6 +960,14 @@ FQDNLookup true # # +# +# +# Host "localhost" +# Port "6379" +# Timeout 1000 +# +# + ############################################################################## # Filter configuration # #----------------------------------------------------------------------------# @@ -930,6 +988,7 @@ FQDNLookup true #@BUILD_PLUGIN_TARGET_REPLACE_TRUE@LoadPlugin target_replace #@BUILD_PLUGIN_TARGET_SCALE_TRUE@LoadPlugin target_scale #@BUILD_PLUGIN_TARGET_SET_TRUE@LoadPlugin target_set +#@BUILD_PLUGIN_TARGET_V5UPGRADE_TRUE@LoadPlugin target_v5upgrade #----------------------------------------------------------------------------# # The following block demonstrates the default behavior if no filtering is # @@ -939,3 +998,52 @@ FQDNLookup true # # Target "write" # + +############################################################################## +# Threshold configuration # +#----------------------------------------------------------------------------# +# The following outlines how to configure collectd's threshold checking # +# plugin. The plugin and possible configuration options are documented in # +# the collectd-threshold(5) manual page. # +############################################################################## + +#@BUILD_PLUGIN_THRESHOLD_TRUE@LoadPlugin "threshold" +# +# +# WarningMin 0.00 +# WarningMax 1000.00 +# FailureMin 0.00 +# FailureMax 1200.00 +# Invert false +# Instance "bar" +# +# +# +# Instance "eth0" +# +# FailureMax 10000000 +# DataSource "rx" +# +# +# +# +# +# Instance "idle" +# FailureMin 10 +# +# +# +# +# Instance "cached" +# WarningMin 100000000 +# +# +# +# +# DataSource "midterm" +# FailureMax 4 +# Hits 3 +# Hysteresis 3 +# +# +# diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 75f79429..bee566cd 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -82,15 +82,19 @@ options are allowed inside a B block: If enabled, collectd will export all global symbols of the plugin (and of all libraries loaded as dependencies of the plugin) and, thus, makes those symbols available for resolving unresolved symbols in subsequently loaded plugins if -that is supported by your system. By default, this is disabled. +that is supported by your system. -This is useful (or possibly even required), e.Eg., when loading a plugin -that embeds some scripting language into the daemon (e.Eg. the C -or C plugins). Scripting languages usually provide means to load +This is useful (or possibly even required), e.g., when loading a plugin that +embeds some scripting language into the daemon (e.g. the I and +I). Scripting languages usually provide means to load extensions written in C. Those extensions require symbols provided by the -interpreter, which is loaded as a dependency of the respective collectd -plugin. See the documentation of those plugins (e.Eg., -L or L) for details. +interpreter, which is loaded as a dependency of the respective collectd plugin. +See the documentation of those plugins (e.g., L or +L) for details. + +By default, this is disabled. As a special exception, if the plugin name is +either C or C, the default is changed to enabled in order to keep +the average user from ever having to deal with this low level linking stuff. =back @@ -151,7 +155,7 @@ missing when no update has been received for twice the update interval. Since this setting uses iterations, the maximum allowed time without update depends on the I information contained in each value list. This is used in the I configuration to dispatch notifications about missing values, -see L<"THRESHOLD CONFIGURATION"> below. +see L for details. =item B I @@ -169,13 +173,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 @@ -200,6 +199,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 @@ -218,7 +354,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 @@ -226,7 +380,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 @@ -595,22 +749,6 @@ runtime statistics module of CouchDB -Another CouchDB example: -The following example will collect the status values from each database: - - - Instance "dbs" - - Type "gauge" - - - Type "counter" - - - Type "bytes" - - - In the B block, there may be one or more B blocks, each defining a URL to be fetched via HTTP (using libcurl) and one or more B blocks. The B string argument must be in a path format, which is used to collect a @@ -1040,22 +1178,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 @@ -1375,13 +1497,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 @@ -1609,6 +1724,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 @@ -1627,8 +1752,8 @@ debugging support. Sets the file to write log messages to. The special strings B and B can be used to write to the standard output and standard error -channels, respectively. This, of course, only makes much sense when collectd is -running in foreground- or non-daemon-mode. +channels, respectively. This, of course, only makes much sense when I +is running in foreground- or non-daemon-mode. =item B B|B @@ -1645,6 +1770,33 @@ B: There is no need to notify the daemon after moving or removing the log file (e.Eg. when rotating the logs). The plugin reopens the file for each line it writes. +=head2 Plugin C + +The I reads CPU statistics of I, a +virtualization technique for IBM POWER processors. It takes into account CPU +time stolen from or donated to a partition, in addition to the usual user, +system, I/O statistics. + +The following configuration options are available: + +=over 4 + +=item B B|B + +When enabled, statistics about the processor pool are read, too. The partition +needs to have pool authority in order to be able to acquire this information. +Defaults to false. + +=item B B|B + +If enabled, the serial of the physical machine the partition is currently +running on is reported as I and the logical hostname of the machine +is reported in the I. Otherwise, the logical hostname will be +used (just like other plugins) and the I will be empty. +Defaults to false. + +=back + =head2 Plugin C The C uses mbmon to retrieve temperature, voltage, etc. @@ -1870,7 +2022,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, @@ -2652,7 +2804,18 @@ 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, which is the maximum +payload size that can be transmitted in one Ethernet frame using IPv6E/ +UDP. + +On the server side, this limit should be set to the largest value used on +I client. Likewise, the value on the client must not be larger than the +value on the server, or data will be lost. + +B Versions prior to I4.8> used a fixed sized +buffer of 1024Ebytes. Versions I<4.8>, I<4.9> and I<4.10> used a default +value of 1024Ebytes to avoid problems when sending data to an older +server. =item B I @@ -2663,16 +2826,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 @@ -3307,11 +3460,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 @@ -3387,21 +3535,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 @@ -3416,13 +3549,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 @@ -3833,6 +3959,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, @@ -3996,7 +4168,7 @@ because all values were added to the internal cache at roughly the same time. =head2 Plugin C -The C uses B to retrieve sensor-values. This means +The I uses B to retrieve sensor-values. This means that all the needed modules have to be loaded and lm_sensors has to be configured (most likely by editing F. Read L for details. @@ -4031,6 +4203,25 @@ Since the configuration of the C is a little more complicated than other plugins, its documentation has been moved to an own manpage, L. Please see there for details. +=head2 Plugin C + +The I collects information about used and available swap space. On +I, the following options are available: + +=over 4 + +=item B B|B + +Configures how to report physical swap devices. If set to B is used (the +default), the summary over all swap devices is reported only, i.e. the globally +used and available space over all devices. If B is configured, the used +and available space of each device will be reported separately. + +This option is only available if the I can use the L +mechanism under I. + +=back + =head2 Plugin C =over 4 @@ -4380,7 +4571,7 @@ port in numeric form. =item B I|I -By default, the C plugin tries to read the statistics from the Linux +By default, the I tries to read the statistics from the Linux C interface. If that is not available, the plugin falls back to the C interface. By setting this option to I, you can force the plugin to use the latter. This option defaults to I. @@ -4400,9 +4591,18 @@ selection is configured at all, B devices are selected. =back +=head2 Plugin C + +The I checks values collected or received by I +against a configurable I and issues I if values are +out of bounds. + +Documentation for this plugin is available in the L +manual page. + =head2 Plugin C -The C connects to a TokyoTyrant server and collects a +The I connects to a TokyoTyrant server and collects a couple metrics: number of records, and database size on disk. =over 4 @@ -4439,6 +4639,13 @@ Change the file permissions of the UNIX-socket after it has been created. The permissions must be given as a numeric, octal value as you would pass to L. Defaults to B<0770>. +=item B B|B + +If set to B, delete the socket file before calling L, if a file +with the given name already exists. If I crashes a socket file may be +left over, preventing the daemon from opening a new socket when restarted. +Since this is potentially dangerous, this defaults to B. + =back =head2 Plugin C @@ -4482,6 +4689,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. diff --git a/src/collectd.h b/src/collectd.h index e7fc4e35..4079ad1f 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 @@ -112,6 +97,12 @@ typedef bool _Bool; # define assert(...) /* nop */ #endif +#if !defined(HAVE__BOOL) || !HAVE__BOOL +typedef int _Bool; +# undef HAVE__BOOL +# define HAVE__BOOL 1 +#endif + #if NAN_STATIC_DEFAULT # include /* #endif NAN_STATIC_DEFAULT*/ @@ -294,8 +285,11 @@ typedef bool _Bool; # endif #endif -extern char hostname_g[]; -extern int interval_g; -extern int timeout_g; +/* Type for time as used by "utils_time.h" */ +typedef uint64_t cdtime_t; + +extern char hostname_g[]; +extern cdtime_t interval_g; +extern int timeout_g; #endif /* COLLECTD_H */ diff --git a/src/collectdctl.c b/src/collectdctl.c new file mode 100644 index 00000000..61b7fc55 --- /dev/null +++ b/src/collectdctl.c @@ -0,0 +1,608 @@ +/** + * 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 + +#include +#include +#include +#include +#include + +#include +#include + +#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) */ +# ifndef isfinite +# define isfinite(f) (((f) - (f)) == 0.0) +# endif +# ifndef isinf +# define isinf(f) (!isfinite(f) && !isnan(f)) +# endif +#endif /* NAN_ZERO_ZERO */ + +#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 (value, (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 35e006a2..0bd9f68f 100644 --- a/src/common.c +++ b/src/common.c @@ -1,6 +1,6 @@ /** * collectd - src/common.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 @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster * Niki W. Waibel * Sebastian Harl * Michał Mirosław @@ -29,6 +29,7 @@ #include "collectd.h" #include "common.h" #include "plugin.h" +#include "utils_cache.h" #if HAVE_PTHREAD_H # include @@ -38,11 +39,6 @@ # include #endif -/* for ntohl and htonl */ -#if HAVE_ARPA_INET_H -# include -#endif - /* for getaddrinfo */ #include #include @@ -52,6 +48,11 @@ # include #endif +/* for ntohl and htonl */ +#if HAVE_ARPA_INET_H +# include +#endif + #ifdef HAVE_LIBKSTAT extern kstat_ctl_t *kc; #endif @@ -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 ("%.3f", CDTIME_T_TO_DOUBLE (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) @@ -848,6 +918,40 @@ int parse_identifier (char *str, char **ret_host, return (0); } /* int parse_identifier */ +int parse_identifier_vl (const char *str, value_list_t *vl) /* {{{ */ +{ + char str_copy[6 * DATA_MAX_NAME_LEN]; + char *host = NULL; + char *plugin = NULL; + char *plugin_instance = NULL; + char *type = NULL; + char *type_instance = NULL; + int status; + + if ((str == NULL) || (vl == NULL)) + return (EINVAL); + + sstrncpy (str_copy, str, sizeof (str_copy)); + + status = parse_identifier (str_copy, &host, + &plugin, &plugin_instance, + &type, &type_instance); + if (status != 0) + return (status); + + sstrncpy (vl->host, host, sizeof (vl->host)); + sstrncpy (vl->plugin, plugin, sizeof (vl->plugin)); + sstrncpy (vl->plugin_instance, + (plugin_instance != NULL) ? plugin_instance : "", + sizeof (vl->plugin_instance)); + sstrncpy (vl->type, type, sizeof (vl->type)); + sstrncpy (vl->type_instance, + (type_instance != NULL) ? type_instance : "", + sizeof (vl->type_instance)); + + return (0); +} /* }}} int parse_identifier_vl */ + int parse_value (const char *value_orig, value_t *ret_value, int ds_type) { char *value; @@ -931,9 +1035,22 @@ int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds) if (i == -1) { if (strcmp ("N", ptr) == 0) - vl->time = time (NULL); + vl->time = cdtime (); else - vl->time = (time_t) atoi (ptr); + { + char *endptr = NULL; + double tmp; + + errno = 0; + tmp = strtod (ptr, &endptr); + if ((errno != 0) /* Overflow */ + || (endptr == ptr) /* Invalid string */ + || (endptr == NULL) /* This should not happen */ + || (*endptr != 0)) /* Trailing chars */ + return (-1); + + vl->time = DOUBLE_TO_CDTIME_T (tmp); + } } else { diff --git a/src/common.h b/src/common.h index c292abfd..6b11b538 100644 --- a/src/common.h +++ b/src/common.h @@ -1,6 +1,6 @@ /** * collectd - src/common.h - * 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 @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster * Niki W. Waibel **/ @@ -258,10 +258,14 @@ 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, char **ret_type, char **ret_type_instance); +int parse_identifier_vl (const char *str, value_list_t *vl); int parse_value (const char *value, value_t *ret_value, int ds_type); int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds); @@ -274,10 +278,10 @@ int notification_init (notification_t *n, int severity, const char *message, const char *host, const char *plugin, const char *plugin_instance, const char *type, const char *type_instance); -#define NOTIFICATION_INIT_VL(n, vl, ds) \ +#define NOTIFICATION_INIT_VL(n, vl) \ notification_init (n, NOTIF_FAILURE, NULL, \ (vl)->host, (vl)->plugin, (vl)->plugin_instance, \ - (ds)->type, (vl)->type_instance) + (vl)->type, (vl)->type_instance) typedef int (*dirwalk_callback_f)(const char *dirname, const char *filename, void *user_data); diff --git a/src/configfile.c b/src/configfile.c index 0b7786f9..7c8347b1 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-2011 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 @@ -17,7 +17,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster * Sebastian tokkee Harl **/ @@ -29,7 +29,6 @@ #include "plugin.h" #include "configfile.h" #include "types_list.h" -#include "utils_threshold.h" #include "filter_chain.h" #if HAVE_WORDEXP_H @@ -96,7 +95,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"}, @@ -243,7 +242,8 @@ static int dispatch_value_plugindir (const oconfig_item_t *ci) static int dispatch_loadplugin (const oconfig_item_t *ci) { int i; - uint32_t flags = 0; + const char *name; + unsigned int flags = 0; assert (strcasecmp (ci->key, "LoadPlugin") == 0); if (ci->values_num != 1) @@ -251,19 +251,34 @@ static int dispatch_loadplugin (const oconfig_item_t *ci) if (ci->values[0].type != OCONFIG_TYPE_STRING) return (-1); + name = ci->values[0].value.string; + + /* + * XXX: Magic at work: + * + * Some of the language bindings, for example the Python and Perl + * plugins, need to be able to export symbols to the scripts they run. + * For this to happen, the "Globals" flag needs to be set. + * Unfortunately, this technical detail is hard to explain to the + * average user and she shouldn't have to worry about this, ideally. + * So in order to save everyone's sanity use a different default for a + * handful of special plugins. --octo + */ + if ((strcasecmp ("Perl", name) == 0) + || (strcasecmp ("Python", name) == 0)) + flags |= PLUGIN_FLAGS_GLOBAL; + for (i = 0; i < ci->children_num; ++i) { - if (ci->children[i].values_num != 1 || - ci->children[i].values[0].type != OCONFIG_TYPE_BOOLEAN) { - WARNING("Ignoring unknown LoadPlugin option %s for plugin %s", ci->children[i].key, ci->values[0].value.string); - continue; - } - if (strcasecmp(ci->children[i].key, "globals") == 0) { - flags |= PLUGIN_FLAGS_GLOBAL; - } else { - WARNING("Ignoring unknown LoadPlugin option %s for plugin %s", ci->children[i].key, ci->values[0].value.string); + if (strcasecmp("Globals", ci->children[i].key) == 0) + cf_util_get_flag (ci->children + i, &flags, PLUGIN_FLAGS_GLOBAL); + else { + WARNING("Ignoring unknown LoadPlugin option \"%s\" " + "for plugin \"%s\"", + ci->children[i].key, ci->values[0].value.string); } } - return (plugin_load (ci->values[0].value.string, flags)); + + return (plugin_load (name, (uint32_t) flags)); } /* int dispatch_value_loadplugin */ static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci) @@ -372,8 +387,6 @@ static int dispatch_block (oconfig_item_t *ci) return (dispatch_loadplugin (ci)); else if (strcasecmp (ci->key, "Plugin") == 0) return (dispatch_block_plugin (ci)); - else if (strcasecmp (ci->key, "Threshold") == 0) - return (ut_config (ci)); else if (strcasecmp (ci->key, "Chain") == 0) return (fc_configure (ci)); @@ -1014,22 +1027,94 @@ 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 */ -/* 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. */ +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 or a number if the correct range + * of 1-65535. 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. */ int cf_util_get_port_number (const oconfig_item_t *ci) /* {{{ */ { - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) + int tmp; + + if ((ci->values_num != 1) + || ((ci->values[0].type != OCONFIG_TYPE_STRING) + && (ci->values[0].type != OCONFIG_TYPE_NUMBER))) { - ERROR ("cf_util_get_port_number: The %s option requires " + ERROR ("cf_util_get_port_number: The \"%s\" option requires " "exactly one string argument.", ci->key); return (-1); } - return (service_name_to_port_number (ci->values[0].value.string)); + if (ci->values[0].type == OCONFIG_TYPE_STRING) + return (service_name_to_port_number (ci->values[0].value.string)); + + assert (ci->values[0].type == OCONFIG_TYPE_NUMBER); + tmp = (int) (ci->values[0].value.number + 0.5); + if ((tmp < 1) || (tmp > 65535)) + { + ERROR ("cf_util_get_port_number: The \"%s\" option requires " + "a service name or a port number. The number " + "you specified, %i, is not in the valid " + "range of 1-65535.", + ci->key, tmp); + return (-1); + } + + return (tmp); } /* }}} int cf_util_get_port_number */ + +int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *ret_value) /* {{{ */ +{ + if ((ci == NULL) || (ret_value == NULL)) + return (EINVAL); + + if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + ERROR ("cf_util_get_cdtime: The %s option requires " + "exactly one numeric argument.", ci->key); + return (-1); + } + + if (ci->values[0].value.number < 0.0) + { + ERROR ("cf_util_get_cdtime: The numeric argument of the %s " + "option must not be negative.", ci->key); + return (-1); + } + + *ret_value = DOUBLE_TO_CDTIME_T (ci->values[0].value.number); + + return (0); +} /* }}} int cf_util_get_cdtime */ + diff --git a/src/configfile.h b/src/configfile.h index 432e09f5..e63a0ea0 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-2011 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 @@ -19,10 +19,11 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster **/ #include "collectd.h" +#include "utils_time.h" #include "liboconfig/oconfig.h" /* @@ -103,9 +104,18 @@ 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 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. */ +/* 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 or a number if the correct range + * of 1-65535. 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. */ int cf_util_get_port_number (const oconfig_item_t *ci); +int cf_util_get_cdtime (const oconfig_item_t *ci, cdtime_t *ret_value); + #endif /* defined(CONFIGFILE_H) */ diff --git a/src/contextswitch.c b/src/contextswitch.c index 06055ca5..c207318f 100644 --- a/src/contextswitch.c +++ b/src/contextswitch.c @@ -1,6 +1,7 @@ /** * collectd - src/contextswitch.c * Copyright (C) 2009 Patrik Weiskircher + * Copyright (C) 2010 Kimo Rosenbaum * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -17,13 +18,26 @@ * * Authors: * Patrik Weiskircher + * Kimo Rosenbaum **/ #include "collectd.h" #include "common.h" #include "plugin.h" -#if !KERNEL_LINUX +#ifdef HAVE_SYS_SYSCTL_H +# include +#endif + +#if HAVE_SYSCTLBYNAME +/* no global variables */ +/* #endif HAVE_SYSCTLBYNAME */ + +#elif KERNEL_LINUX +/* no global variables */ +/* #endif KERNEL_LINUX */ + +#else # error "No applicable input method." #endif @@ -45,6 +59,25 @@ static void cs_submit (derive_t context_switches) static int cs_read (void) { +#if HAVE_SYSCTLBYNAME + int value = 0; + size_t value_len = sizeof (value); + int status; + + status = sysctlbyname ("vm.stats.sys.v_swtch", + &value, &value_len, + /* new pointer = */ NULL, /* new length = */ 0); + if (status != 0) + { + ERROR("contextswitch plugin: sysctlbyname " + "(vm.stats.sys.v_swtch) failed"); + return (-1); + } + + cs_submit (value); +/* #endif HAVE_SYSCTLBYNAME */ + +#elif KERNEL_LINUX FILE *fh; char buffer[64]; int numfields; @@ -88,6 +121,7 @@ static int cs_read (void) if (status == -2) ERROR ("contextswitch plugin: Unable to find context switch value."); +#endif /* KERNEL_LINUX */ return status; } diff --git a/src/cpu.c b/src/cpu.c index 7aa6361b..12071a2c 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -1,6 +1,6 @@ /** * collectd - src/cpu.c - * Copyright (C) 2005-2009 Florian octo Forster + * Copyright (C) 2005-2010 Florian octo Forster * Copyright (C) 2008 Oleg King * Copyright (C) 2009 Simon Kuhnle * Copyright (C) 2009 Manuel Sanmartin @@ -163,7 +163,7 @@ static int init (void) DEBUG ("host_processors returned %i %s", (int) cpu_list_len, cpu_list_len == 1 ? "processor" : "processors"); INFO ("cpu plugin: Found %i processor%s.", (int) cpu_list_len, cpu_list_len == 1 ? "" : "s"); - cpu_temp_retry_max = 86400 / interval_g; + cpu_temp_retry_max = 86400 / CDTIME_T_TO_TIME_T (interval_g); /* #endif PROCESSOR_CPU_LOAD_INFO */ #elif defined(HAVE_LIBKSTAT) @@ -241,12 +241,12 @@ static int init (void) return (0); } /* int init */ -static void submit (int cpu_num, const char *type_instance, counter_t value) +static void submit (int cpu_num, const char *type_instance, derive_t value) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = value; + values[0].derive = value; vl.values = values; vl.values_len = 1; @@ -298,10 +298,10 @@ static int cpu_read (void) continue; } - submit (cpu, "user", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]); - submit (cpu, "nice", (counter_t) cpu_info.cpu_ticks[CPU_STATE_NICE]); - submit (cpu, "system", (counter_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM]); - submit (cpu, "idle", (counter_t) cpu_info.cpu_ticks[CPU_STATE_IDLE]); + submit (cpu, "user", (derive_t) cpu_info.cpu_ticks[CPU_STATE_USER]); + submit (cpu, "nice", (derive_t) cpu_info.cpu_ticks[CPU_STATE_NICE]); + submit (cpu, "system", (derive_t) cpu_info.cpu_ticks[CPU_STATE_SYSTEM]); + submit (cpu, "idle", (derive_t) cpu_info.cpu_ticks[CPU_STATE_IDLE]); #endif /* PROCESSOR_CPU_LOAD_INFO */ #if PROCESSOR_TEMPERATURE /* @@ -352,8 +352,8 @@ static int cpu_read (void) #elif defined(KERNEL_LINUX) int cpu; - counter_t user, nice, syst, idle; - counter_t wait, intr, sitr; /* sitr == soft interrupt */ + derive_t user, nice, syst, idle; + derive_t wait, intr, sitr; /* sitr == soft interrupt */ FILE *fh; char buf[1024]; @@ -410,7 +410,7 @@ static int cpu_read (void) #elif defined(HAVE_LIBKSTAT) int cpu; - counter_t user, syst, idle, wait; + derive_t user, syst, idle, wait; static cpu_stat_t cs; if (kc == NULL) @@ -421,10 +421,10 @@ static int cpu_read (void) if (kstat_read (kc, ksp[cpu], &cs) == -1) continue; /* error message? */ - idle = (counter_t) cs.cpu_sysinfo.cpu[CPU_IDLE]; - user = (counter_t) cs.cpu_sysinfo.cpu[CPU_USER]; - syst = (counter_t) cs.cpu_sysinfo.cpu[CPU_KERNEL]; - wait = (counter_t) cs.cpu_sysinfo.cpu[CPU_WAIT]; + idle = (derive_t) cs.cpu_sysinfo.cpu[CPU_IDLE]; + user = (derive_t) cs.cpu_sysinfo.cpu[CPU_USER]; + syst = (derive_t) cs.cpu_sysinfo.cpu[CPU_KERNEL]; + wait = (derive_t) cs.cpu_sysinfo.cpu[CPU_WAIT]; submit (ksp[cpu]->ks_instance, "user", user); submit (ksp[cpu]->ks_instance, "system", syst); @@ -551,12 +551,12 @@ static int cpu_read (void) return (-1); } - submit (0, "idle", (counter_t) cs->idle); - submit (0, "nice", (counter_t) cs->nice); - submit (0, "swap", (counter_t) cs->swap); - submit (0, "system", (counter_t) cs->kernel); - submit (0, "user", (counter_t) cs->user); - submit (0, "wait", (counter_t) cs->iowait); + submit (0, "idle", (derive_t) cs->idle); + submit (0, "nice", (derive_t) cs->nice); + submit (0, "swap", (derive_t) cs->swap); + submit (0, "system", (derive_t) cs->kernel); + submit (0, "user", (derive_t) cs->user); + submit (0, "wait", (derive_t) cs->iowait); /* #endif HAVE_LIBSTATGRAB */ #elif defined(HAVE_PERFSTAT) @@ -591,10 +591,10 @@ static int cpu_read (void) for (i = 0; i < cpus; i++) { - submit (i, "idle", (counter_t) perfcpu[i].idle); - submit (i, "system", (counter_t) perfcpu[i].sys); - submit (i, "user", (counter_t) perfcpu[i].user); - submit (i, "wait", (counter_t) perfcpu[i].wait); + submit (i, "idle", (derive_t) perfcpu[i].idle); + submit (i, "system", (derive_t) perfcpu[i].sys); + submit (i, "user", (derive_t) perfcpu[i].user); + submit (i, "wait", (derive_t) perfcpu[i].wait); } #endif /* HAVE_PERFSTAT */ diff --git a/src/cpython.h b/src/cpython.h index 46e2301a..4b8aa721 100644 --- a/src/cpython.h +++ b/src/cpython.h @@ -188,7 +188,7 @@ typedef struct { PluginData data; PyObject *values; /* Sequence */ PyObject *meta; /* dict */ - int interval; + double interval; } Values; PyTypeObject ValuesType; #define Values_New() PyObject_CallFunctionObjArgs((PyObject *) &ValuesType, (void *) 0) diff --git a/src/csv.c b/src/csv.c index f149edc0..02d62c1e 100644 --- a/src/csv.c +++ b/src/csv.c @@ -53,7 +53,8 @@ static int value_list_to_string (char *buffer, int buffer_len, memset (buffer, '\0', buffer_len); - status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time); + status = ssnprintf (buffer, buffer_len, "%.3f", + CDTIME_T_TO_DOUBLE (vl->time)); if ((status < 1) || (status >= buffer_len)) return (-1); offset = status; @@ -298,8 +299,10 @@ static int csv_write (const data_set_t *ds, const value_list_t *vl, } fprintf (use_stdio == 1 ? stdout : stderr, - "PUTVAL %s interval=%i %s\n", - filename, vl->interval, values); + "PUTVAL %s interval=%.3f %s\n", + filename, + CDTIME_T_TO_DOUBLE (vl->interval), + values); return (0); } diff --git a/src/curl.c b/src/curl.c index 31cda39a..2160b980 100644 --- a/src/curl.c +++ b/src/curl.c @@ -578,7 +578,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)); @@ -597,7 +596,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 968fc93c..cc8b4ad3 100644 --- a/src/curl_json.c +++ b/src/curl_json.c @@ -40,7 +40,7 @@ #define CJ_DEFAULT_HOST "localhost" #define CJ_KEY_MAGIC 0x43484b59UL /* CHKY */ -#define CJ_IS_KEY(key) (key)->magic == CJ_KEY_MAGIC +#define CJ_IS_KEY(key) ((key)->magic == CJ_KEY_MAGIC) #define CJ_ANY "*" #define COUCH_MIN(x,y) ((x) < (y) ? (x) : (y)) @@ -64,8 +64,8 @@ struct cj_s /* {{{ */ char *user; char *pass; char *credentials; - int verify_peer; - int verify_host; + _Bool verify_peer; + _Bool verify_host; char *cacert; CURL *curl; @@ -111,7 +111,7 @@ static size_t cj_curl_callback (void *buf, /* {{{ */ if (db == NULL) return (0); - status = yajl_parse(db->yajl, (unsigned char *)buf, len); + status = yajl_parse(db->yajl, (unsigned char *) buf, len); if (status == yajl_status_ok) { #if HAVE_YAJL_V2 @@ -129,7 +129,8 @@ static size_t cj_curl_callback (void *buf, /* {{{ */ if (status != yajl_status_ok) { 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 */ @@ -184,41 +185,21 @@ static int cj_cb_number (void *ctx, cj_t *db = (cj_t *)ctx; cj_key_t *key = db->state[db->depth].key; - char *endptr; value_t vt; int type; + int status; - if (key == NULL) + if ((key == NULL) || !CJ_IS_KEY (key)) return (CJ_CB_CONTINUE); memcpy (buffer, number, number_len); buffer[sizeof (buffer) - 1] = 0; type = cj_get_type (key); - if (type < 0) - return (CJ_CB_CONTINUE); - - endptr = NULL; - errno = 0; - - if (type == DS_TYPE_COUNTER) - vt.counter = (counter_t) strtoull (buffer, &endptr, /* base = */ 0); - else if (type == DS_TYPE_GAUGE) - vt.gauge = (gauge_t) strtod (buffer, &endptr); - else if (type == DS_TYPE_DERIVE) - vt.derive = (derive_t) strtoll (buffer, &endptr, /* base = */ 0); - else if (type == DS_TYPE_ABSOLUTE) - vt.absolute = (absolute_t) strtoull (buffer, &endptr, /* base = */ 0); - else - { - ERROR ("curl_json plugin: Unknown data source type: \"%s\"", key->type); - return (CJ_CB_ABORT); - } - - if ((endptr == &buffer[0]) || (errno != 0)) + status = parse_value (buffer, &vt, type); + if (status != 0) { - NOTICE ("curl_json plugin: Overflow while parsing number. " - "Ignoring this value."); + NOTICE ("curl_json plugin: Unable to parse number: \"%s\"", buffer); return (CJ_CB_CONTINUE); } @@ -258,34 +239,26 @@ static int cj_cb_string (void *ctx, const unsigned char *val, yajl_len_t len) { cj_t *db = (cj_t *)ctx; - c_avl_tree_t *tree; - char *ptr; - - if (db->depth != 1) /* e.g. _all_dbs */ - return (CJ_CB_CONTINUE); + char str[len + 1]; - cj_cb_map_key (ctx, val, len); /* same logic */ + /* Create a null-terminated version of the string. */ + memcpy (str, val, len); + str[len] = 0; - tree = db->state[db->depth].tree; + /* No configuration for this string -> simply return. */ + if (db->state[db->depth].key == NULL) + return (CJ_CB_CONTINUE); - if ((tree != NULL) && (ptr = rindex (db->url, '/'))) + if (!CJ_IS_KEY (db->state[db->depth].key)) { - char url[PATH_MAX]; - CURL *curl; - - /* url =~ s,[^/]+$,$name, */ - len = (ptr - db->url) + 1; - ptr = url; - sstrncpy (ptr, db->url, sizeof (url)); - sstrncpy (ptr + len, db->state[db->depth].name, sizeof (url) - len); - - curl = curl_easy_duphandle (db->curl); - curl_easy_setopt (curl, CURLOPT_URL, url); - cj_curl_perform (db, curl); - curl_easy_cleanup (curl); + NOTICE ("curl_json plugin: Found string \"%s\", but the configuration " + "expects a map here.", str); + return (CJ_CB_CONTINUE); } - return (CJ_CB_CONTINUE); -} + + /* Handle the string as if it was a number. */ + return (cj_cb_number (ctx, (const char *) val, len)); +} /* int cj_cb_string */ static int cj_cb_start (void *ctx) { @@ -407,37 +380,6 @@ static void cj_free (void *arg) /* {{{ */ /* Configuration handling functions {{{ */ -static int cj_config_add_string (const char *name, char **dest, /* {{{ */ - oconfig_item_t *ci) -{ - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("curl_json plugin: `%s' needs exactly one string argument.", name); - return (-1); - } - - sfree (*dest); - *dest = strdup (ci->values[0].value.string); - if (*dest == NULL) - return (-1); - - return (0); -} /* }}} int cj_config_add_string */ - -static int cj_config_set_boolean (const char *name, int *dest, /* {{{ */ - oconfig_item_t *ci) -{ - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN)) - { - WARNING ("curl_json plugin: `%s' needs exactly one boolean argument.", name); - return (-1); - } - - *dest = ci->values[0].value.boolean ? 1 : 0; - - return (0); -} /* }}} int cj_config_set_boolean */ - static c_avl_tree_t *cj_avl_create(void) { return c_avl_create ((int (*) (const void *, const void *)) strcmp); @@ -469,7 +411,7 @@ static int cj_config_add_key (cj_t *db, /* {{{ */ if (strcasecmp ("Key", ci->key) == 0) { - status = cj_config_add_string ("Key", &key->path, ci); + status = cf_util_get_string (ci, &key->path); if (status != 0) { sfree (key); @@ -489,9 +431,9 @@ static int cj_config_add_key (cj_t *db, /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp ("Type", child->key) == 0) - status = cj_config_add_string ("Type", &key->type, child); + status = cf_util_get_string (child, &key->type); else if (strcasecmp ("Instance", child->key) == 0) - status = cj_config_add_string ("Instance", &key->instance, child); + status = cf_util_get_string (child, &key->instance); else { WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key); @@ -607,9 +549,9 @@ static int cj_init_curl (cj_t *db) /* {{{ */ curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials); } - curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer); + curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, (int) db->verify_peer); curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST, - db->verify_host ? 2 : 0); + (int) (db->verify_host ? 2 : 0)); if (db->cacert != NULL) curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert); @@ -640,7 +582,7 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */ if (strcasecmp ("URL", ci->key) == 0) { - status = cj_config_add_string ("URL", &db->url, ci); + status = cf_util_get_string (ci, &db->url); if (status != 0) { sfree (db); @@ -660,19 +602,19 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp ("Instance", child->key) == 0) - status = cj_config_add_string ("Instance", &db->instance, child); + status = cf_util_get_string (child, &db->instance); else if (strcasecmp ("Host", child->key) == 0) - status = cj_config_add_string ("Host", &db->host, child); + status = cf_util_get_string (child, &db->host); else if (strcasecmp ("User", child->key) == 0) - status = cj_config_add_string ("User", &db->user, child); + status = cf_util_get_string (child, &db->user); else if (strcasecmp ("Password", child->key) == 0) - status = cj_config_add_string ("Password", &db->pass, child); + status = cf_util_get_string (child, &db->pass); else if (strcasecmp ("VerifyPeer", child->key) == 0) - status = cj_config_set_boolean ("VerifyPeer", &db->verify_peer, child); + status = cf_util_get_boolean (child, &db->verify_peer); else if (strcasecmp ("VerifyHost", child->key) == 0) - status = cj_config_set_boolean ("VerifyHost", &db->verify_host, child); + status = cf_util_get_boolean (child, &db->verify_host); else if (strcasecmp ("CACert", child->key) == 0) - status = cj_config_add_string ("CACert", &db->cacert, child); + status = cf_util_get_string (child, &db->cacert); else if (strcasecmp ("Key", child->key) == 0) status = cj_config_add_key (db, child); else @@ -824,29 +766,52 @@ static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */ return (-1); } - status = curl_easy_perform (curl); + url = NULL; + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); - yajl_free (db->yajl); - db->yajl = yprev; + status = curl_easy_perform (curl); + if (status != 0) + { + ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)", + status, db->curl_errbuf, (url != NULL) ? 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); /* 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) +#if HAVE_YAJL_V2 + status = yajl_complete_parse(db->yajl); +#else + status = yajl_parse_complete(db->yajl); +#endif + 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..caa41ef6 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 @@ -566,7 +566,7 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */ udb_query_prepare_result (q, prep_area, hostname_g, /* plugin = */ "dbi", db->name, - column_names, column_num, /* interval = */ -1); + column_names, column_num, /* interval = */ 0); /* 0 = error; 1 = success; */ status = dbi_result_first_row (res); /* {{{ */ diff --git a/src/df.c b/src/df.c index dc7be1eb..ded374b9 100644 --- a/src/df.c +++ b/src/df.c @@ -62,9 +62,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) { @@ -119,25 +118,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); } @@ -146,28 +136,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, @@ -215,6 +183,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) @@ -276,56 +247,39 @@ 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 - */ - /* Check for negative "available" byes. For example UFS can - * report negative free space for user. Notice. blk_reserved - * will start to diminish after this. */ + /* + * Sanity-check for the values in the struct + */ + /* Check for negative "available" byes. For example UFS can + * report negative free space for user. Notice. blk_reserved + * will start to diminish after this. */ #if HAVE_STATVFS - /* Cast and temporary variable are needed to avoid - * compiler warnings. - * ((struct statvfs).f_bavail is unsigned (POSIX)) */ - int64_t signed_bavail = (int64_t) statbuf.f_bavail; - if (signed_bavail < 0) - statbuf.f_bavail = 0; + /* Cast and temporary variable are needed to avoid + * compiler warnings. + * ((struct statvfs).f_bavail is unsigned (POSIX)) */ + int64_t signed_bavail = (int64_t) statbuf.f_bavail; + if (signed_bavail < 0) + statbuf.f_bavail = 0; #elif HAVE_STATFS - if (statbuf.f_bavail < 0) - statbuf.f_bavail = 0; + if (statbuf.f_bavail < 0) + statbuf.f_bavail = 0; #endif - /* Make sure that f_blocks >= f_bfree >= f_bavail */ - 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); - } + /* Make sure that f_blocks >= f_bfree >= f_bavail */ + 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/disk.c b/src/disk.c index 4a78f1bd..7411c225 100644 --- a/src/disk.c +++ b/src/disk.c @@ -1,6 +1,6 @@ /** * collectd - src/disk.c - * Copyright (C) 2005-2008 Florian octo Forster + * Copyright (C) 2005-2010 Florian octo Forster * Copyright (C) 2009 Manuel Sanmartin * * This program is free software; you can redistribute it and/or modify it @@ -85,19 +85,19 @@ typedef struct diskstats /* This overflows in roughly 1361 years */ unsigned int poll_count; - counter_t read_sectors; - counter_t write_sectors; + derive_t read_sectors; + derive_t write_sectors; - counter_t read_bytes; - counter_t write_bytes; + derive_t read_bytes; + derive_t write_bytes; - counter_t read_ops; - counter_t write_ops; - counter_t read_time; - counter_t write_time; + derive_t read_ops; + derive_t write_ops; + derive_t read_time; + derive_t write_time; - counter_t avg_read_time; - counter_t avg_write_time; + derive_t avg_read_time; + derive_t avg_write_time; struct diskstats *next; } diskstats_t; @@ -212,7 +212,7 @@ static int disk_init (void) static void disk_submit (const char *plugin_instance, const char *type, - counter_t read, counter_t write) + derive_t read, derive_t write) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; @@ -221,8 +221,8 @@ static void disk_submit (const char *plugin_instance, if (ignorelist_match (ignorelist, plugin_instance) != 0) return; - values[0].counter = read; - values[1].counter = write; + values[0].derive = read; + values[1].derive = write; vl.values = values; vl.values_len = 2; @@ -239,7 +239,7 @@ static void disk_submit (const char *plugin_instance, static counter_t disk_calc_time_incr (counter_t delta_time, counter_t delta_ops) { double avg_time = ((double) delta_time) / ((double) delta_ops); - double avg_time_incr = ((double) interval_g) * avg_time; + double avg_time_incr = CDTIME_T_TO_DOUBLE (interval_g) * avg_time; return ((counter_t) (avg_time_incr + .5)); } @@ -438,15 +438,15 @@ static int disk_read (void) int minor = 0; - counter_t read_sectors = 0; - counter_t write_sectors = 0; + derive_t read_sectors = 0; + derive_t write_sectors = 0; - counter_t read_ops = 0; - counter_t read_merged = 0; - counter_t read_time = 0; - counter_t write_ops = 0; - counter_t write_merged = 0; - counter_t write_time = 0; + derive_t read_ops = 0; + derive_t read_merged = 0; + derive_t read_time = 0; + derive_t write_ops = 0; + derive_t write_merged = 0; + derive_t write_time = 0; int is_disk = 0; diskstats_t *ds, *pre_ds; @@ -531,8 +531,8 @@ static int disk_read (void) } { - counter_t diff_read_sectors; - counter_t diff_write_sectors; + derive_t diff_read_sectors; + derive_t diff_write_sectors; /* If the counter wraps around, it's only 32 bits.. */ if (read_sectors < ds->read_sectors) @@ -555,18 +555,18 @@ static int disk_read (void) /* Calculate the average time an io-op needs to complete */ if (is_disk) { - counter_t diff_read_ops; - counter_t diff_write_ops; - counter_t diff_read_time; - counter_t diff_write_time; + derive_t diff_read_ops; + derive_t diff_write_ops; + derive_t diff_read_time; + derive_t diff_write_time; if (read_ops < ds->read_ops) diff_read_ops = 1 + read_ops + (UINT_MAX - ds->read_ops); else diff_read_ops = read_ops - ds->read_ops; - DEBUG ("disk plugin: disk_name = %s; read_ops = %llu; " - "ds->read_ops = %llu; diff_read_ops = %llu;", + DEBUG ("disk plugin: disk_name = %s; read_ops = %"PRIi64"; " + "ds->read_ops = %"PRIi64"; diff_read_ops = %"PRIi64";", disk_name, read_ops, ds->read_ops, diff_read_ops); @@ -706,12 +706,12 @@ static int disk_read (void) /* #endif defined(HAVE_LIBSTATGRAB) */ #elif defined(HAVE_PERFSTAT) - counter_t read_sectors; - counter_t write_sectors; - counter_t read_time; - counter_t write_time; - counter_t read_ops; - counter_t write_ops; + derive_t read_sectors; + derive_t write_sectors; + derive_t read_time; + derive_t write_time; + derive_t read_ops; + derive_t write_ops; perfstat_id_t firstpath; int rnumdisk; int i; diff --git a/src/dns.c b/src/dns.c index 364b9587..95797f54 100644 --- a/src/dns.c +++ b/src/dns.c @@ -33,9 +33,7 @@ #include #include -#if HAVE_PCAP_BPF_H -# include -#endif +#include /* * Private data types @@ -63,8 +61,8 @@ static int select_numeric_qtype = 1; #define PCAP_SNAPLEN 1460 static char *pcap_device = NULL; -static counter_t tr_queries; -static counter_t tr_responses; +static derive_t tr_queries; +static derive_t tr_responses; static counter_list_t *qtype_list; static counter_list_t *opcode_list; static counter_list_t *rcode_list; @@ -84,14 +82,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); } @@ -100,9 +94,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); @@ -126,7 +117,6 @@ static counter_list_t *counter_list_create (counter_list_t **list, last->next = entry; } - DEBUG ("return (%p)", (void *) entry); return (entry); } @@ -135,9 +125,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) @@ -148,7 +135,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) @@ -242,7 +228,7 @@ static void *dns_child_loop (__attribute__((unused)) void *dummy) pcap_obj = pcap_open_live ((pcap_device != NULL) ? pcap_device : "any", PCAP_SNAPLEN, 0 /* Not promiscuous */, - interval_g, + (int) CDTIME_T_TO_MS (interval_g / 2), pcap_error); if (pcap_obj == NULL) { @@ -265,7 +251,7 @@ static void *dns_child_loop (__attribute__((unused)) void *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); @@ -278,7 +264,7 @@ static void *dns_child_loop (__attribute__((unused)) void *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; @@ -315,13 +301,13 @@ static int dns_init (void) return (0); } /* int dns_init */ -static void submit_counter (const char *type, const char *type_instance, - counter_t value) +static void submit_derive (const char *type, const char *type_instance, + derive_t value) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = value; + values[0].derive = value; vl.values = values; vl.values_len = 1; @@ -331,15 +317,15 @@ static void submit_counter (const char *type, const char *type_instance, sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); plugin_dispatch_values (&vl); -} /* void submit_counter */ +} /* void submit_derive */ -static void submit_octets (counter_t queries, counter_t responses) +static void submit_octets (derive_t queries, derive_t responses) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = queries; - values[1].counter = responses; + values[0].derive = queries; + values[1].derive = responses; vl.values = values; vl.values_len = 2; @@ -348,7 +334,7 @@ static void submit_octets (counter_t queries, counter_t responses) sstrncpy (vl.type, "dns_octets", sizeof (vl.type)); plugin_dispatch_values (&vl); -} /* void submit_counter */ +} /* void submit_octets */ static int dns_read (void) { @@ -379,8 +365,8 @@ static int dns_read (void) for (i = 0; i < len; i++) { - DEBUG ("qtype = %u; counter = %u;", keys[i], values[i]); - submit_counter ("dns_qtype", qtype_str (keys[i]), values[i]); + DEBUG ("dns plugin: qtype = %u; counter = %u;", keys[i], values[i]); + submit_derive ("dns_qtype", qtype_str (keys[i]), values[i]); } pthread_mutex_lock (&opcode_mutex); @@ -395,8 +381,8 @@ static int dns_read (void) for (i = 0; i < len; i++) { - DEBUG ("opcode = %u; counter = %u;", keys[i], values[i]); - submit_counter ("dns_opcode", opcode_str (keys[i]), values[i]); + DEBUG ("dns plugin: opcode = %u; counter = %u;", keys[i], values[i]); + submit_derive ("dns_opcode", opcode_str (keys[i]), values[i]); } pthread_mutex_lock (&rcode_mutex); @@ -411,8 +397,8 @@ static int dns_read (void) for (i = 0; i < len; i++) { - DEBUG ("rcode = %u; counter = %u;", keys[i], values[i]); - submit_counter ("dns_rcode", rcode_str (keys[i]), values[i]); + DEBUG ("dns plugin: rcode = %u; counter = %u;", keys[i], values[i]); + submit_derive ("dns_rcode", rcode_str (keys[i]), values[i]); } return (0); diff --git a/src/email.c b/src/email.c index 8fc5509f..5870ab1d 100644 --- a/src/email.c +++ b/src/email.c @@ -376,6 +376,7 @@ static void *collect (void *arg) } /* while (1) */ pthread_exit ((void *)0); + return ((void *) 0); } /* static void *collect (void *) */ static void *open_connection (void __attribute__((unused)) *arg) @@ -548,7 +549,9 @@ static void *open_connection (void __attribute__((unused)) *arg) pthread_cond_signal (&conn_available); } - pthread_exit ((void *)0); + + pthread_exit ((void *) 0); + return ((void *) 0); } /* static void *open_connection (void *) */ static int email_init (void) diff --git a/src/exec.c b/src/exec.c index 52747977..0e807c75 100644 --- a/src/exec.c +++ b/src/exec.c @@ -270,13 +270,14 @@ static void set_environment (void) /* {{{ */ char buffer[1024]; #ifdef HAVE_SETENV - ssnprintf (buffer, sizeof (buffer), "%i", interval_g); + ssnprintf (buffer, sizeof (buffer), "%.3f", CDTIME_T_TO_DOUBLE (interval_g)); setenv ("COLLECTD_INTERVAL", buffer, /* overwrite = */ 1); ssnprintf (buffer, sizeof (buffer), "%s", hostname_g); setenv ("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1); #else - ssnprintf (buffer, sizeof (buffer), "COLLECTD_INTERVAL=%i", interval_g); + ssnprintf (buffer, sizeof (buffer), "COLLECTD_INTERVAL=%.3f", + CDTIME_T_TO_DOUBLE (interval_g)); putenv (buffer); ssnprintf (buffer, sizeof (buffer), "COLLECTD_HOSTNAME=%s", hostname_g); @@ -537,12 +538,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 }}} */ @@ -744,8 +742,8 @@ static void *exec_notification_one (void *arg) /* {{{ */ fprintf (fh, "Severity: %s\n" - "Time: %u\n", - severity, (unsigned int) n->time); + "Time: %.3f\n", + severity, CDTIME_T_TO_DOUBLE (n->time)); /* Print the optional fields */ if (strlen (n->host) > 0) diff --git a/src/fscache.c b/src/fscache.c index dc0eb119..8fbd2713 100644 --- a/src/fscache.c +++ b/src/fscache.c @@ -107,16 +107,13 @@ Ops pend=N Number of times async ops added to pending queues 63 events to collect in 13 groups */ static void fscache_submit (const char *section, const char *name, - counter_t counter_value) + value_t value) { - value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - vl.values = values; + vl.values = &value; vl.values_len = 1; - vl.values[0].counter = counter_value; - sstrncpy(vl.host, hostname_g, sizeof (vl.host)); sstrncpy(vl.plugin, "fscache", sizeof (vl.plugin)); sstrncpy(vl.plugin_instance, section, sizeof (vl.plugin_instance)); @@ -189,8 +186,8 @@ static void fscache_read_stats_file (FILE *fh) { char *field_name; char *field_value_str; - char *endptr; - counter_t field_value_cnt; + value_t field_value_cnt; + int status; field_name = fields[i]; assert (field_name != NULL); @@ -201,11 +198,9 @@ static void fscache_read_stats_file (FILE *fh) *field_value_str = 0; field_value_str++; - errno = 0; - endptr = NULL; - field_value_cnt = (counter_t) strtoull (field_value_str, - &endptr, /* base = */ 10); - if ((errno != 0) || (endptr == field_value_str)) + status = parse_value (field_value_str, &field_value_cnt, + DS_TYPE_DERIVE); + if (status != 0) continue; fscache_submit (section, field_name, field_value_cnt); diff --git a/src/gmond.c b/src/gmond.c index 2ffc42a5..3c746c48 100644 --- a/src/gmond.c +++ b/src/gmond.c @@ -1,6 +1,6 @@ /** * collectd - src/gmond.c - * 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 @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster **/ #include "collectd.h" @@ -545,6 +545,8 @@ static int staging_entry_update (const char *host, const char *name, /* {{{ */ se->vl.values[ds_index].derive += value.derive; else if (ds_type == DS_TYPE_ABSOLUTE) se->vl.values[ds_index].absolute = value.absolute; + else + assert (23 == 42); se->flags |= (0x01 << ds_index); @@ -593,32 +595,24 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */ case gmetric_string: { Ganglia_gmetric_string msg_string; - char *endptr; + int status; msg_string = msg->Ganglia_value_msg_u.gstr; host = msg_string.metric_id.host; name = msg_string.metric_id.name; - endptr = NULL; - errno = 0; - value_counter.counter = (counter_t) strtoull (msg_string.str, - &endptr, /* base = */ 0); - if ((endptr == msg_string.str) || (errno != 0)) - value_counter.counter = -1; - - endptr = NULL; - errno = 0; - value_gauge.gauge = (gauge_t) strtod (msg_string.str, &endptr); - if ((endptr == msg_string.str) || (errno != 0)) + status = parse_value (msg_string.str, &value_derive, DS_TYPE_DERIVE); + if (status != 0) + value_derive.derive = -1; + + status = parse_value (msg_string.str, &value_gauge, DS_TYPE_GAUGE); + if (status != 0) value_gauge.gauge = NAN; - endptr = NULL; - errno = 0; - value_derive.derive = (derive_t) strtoll (msg_string.str, - &endptr, /* base = */ 0); - if ((endptr == msg_string.str) || (errno != 0)) - value_derive.derive = 0; + status = parse_value (msg_string.str, &value_counter, DS_TYPE_COUNTER); + if (status != 0) + value_counter.counter = 0; break; } @@ -663,11 +657,15 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */ { value_t val_copy; - val_copy = value_counter; + if ((map->ds_type == DS_TYPE_COUNTER) + || (map->ds_type == DS_TYPE_ABSOLUTE)) + val_copy = value_counter; if (map->ds_type == DS_TYPE_GAUGE) val_copy = value_gauge; else if (map->ds_type == DS_TYPE_DERIVE) val_copy = value_derive; + else + assert (23 == 42); return (staging_entry_update (host, name, map->type, map->type_instance, @@ -719,7 +717,7 @@ static int mc_handle_metadata_msg (Ganglia_metadata_msg *msg) /* {{{ */ map->type, map->type_instance, ds->ds_num); if (se != NULL) - se->vl.interval = (int) msg_meta.metric.tmax; + se->vl.interval = TIME_T_TO_CDTIME_T (msg_meta.metric.tmax); pthread_mutex_unlock (&staging_lock); if (se == NULL) 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 db998a3f..ea820399 100644 --- a/src/interface.c +++ b/src/interface.c @@ -1,6 +1,6 @@ /** * collectd - src/interface.c - * Copyright (C) 2005-2008 Florian octo Forster + * Copyright (C) 2005-2010 Florian octo Forster * Copyright (C) 2009 Manuel Sanmartin * * This program is free software; you can redistribute it and/or modify it @@ -128,7 +128,7 @@ static int interface_config (const char *key, const char *value) static int interface_init (void) { kstat_t *ksp_chain; - unsigned long long val; + derive_t val; numif = 0; @@ -155,8 +155,8 @@ static int interface_init (void) #endif /* HAVE_LIBKSTAT */ static void if_submit (const char *dev, const char *type, - unsigned long long rx, - unsigned long long tx) + derive_t rx, + derive_t tx) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; @@ -164,15 +164,15 @@ static void if_submit (const char *dev, const char *type, if (ignorelist_match (ignorelist, dev) != 0) return; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; vl.values = values; 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 */ @@ -233,7 +233,7 @@ static int interface_read (void) #elif KERNEL_LINUX FILE *fh; char buffer[1024]; - unsigned long long incoming, outgoing; + derive_t incoming, outgoing; char *device; char *dummy; @@ -285,8 +285,8 @@ static int interface_read (void) #elif HAVE_LIBKSTAT int i; - unsigned long long rx; - unsigned long long tx; + derive_t rx; + derive_t tx; if (kc == NULL) return (-1); diff --git a/src/ipmi.c b/src/ipmi.c index 95b3dbf5..f341320d 100644 --- a/src/ipmi.c +++ b/src/ipmi.c @@ -136,7 +136,7 @@ static void sensor_read_handler (ipmi_sensor_t *sensor, if (c_ipmi_nofiy_notpresent) { - notification_t n = { NOTIF_WARNING, time(NULL), "", "", "ipmi", + notification_t n = { NOTIF_WARNING, cdtime (), "", "", "ipmi", "", "", "", NULL }; sstrncpy (n.host, hostname_g, sizeof (n.host)); @@ -190,7 +190,7 @@ static void sensor_read_handler (ipmi_sensor_t *sensor, if (c_ipmi_nofiy_notpresent) { - notification_t n = { NOTIF_OKAY, time(NULL), "", "", "ipmi", + notification_t n = { NOTIF_OKAY, cdtime (), "", "", "ipmi", "", "", "", NULL }; sstrncpy (n.host, hostname_g, sizeof (n.host)); @@ -363,7 +363,7 @@ static int sensor_list_add (ipmi_sensor_t *sensor) if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0)) { - notification_t n = { NOTIF_OKAY, time(NULL), "", "", "ipmi", + notification_t n = { NOTIF_OKAY, cdtime (), "", "", "ipmi", "", "", "", NULL }; sstrncpy (n.host, hostname_g, sizeof (n.host)); @@ -417,7 +417,7 @@ static int sensor_list_remove (ipmi_sensor_t *sensor) if (c_ipmi_nofiy_remove && c_ipmi_active) { - notification_t n = { NOTIF_WARNING, time(NULL), "", "", + notification_t n = { NOTIF_WARNING, cdtime (), "", "", "ipmi", "", "", "", NULL }; sstrncpy (n.host, hostname_g, sizeof (n.host)); @@ -664,7 +664,8 @@ static int c_ipmi_init (void) int status; /* Don't send `ADD' notifications during startup (~ 1 minute) */ - c_ipmi_init_in_progress = 1 + (60 / interval_g); + time_t iv = CDTIME_T_TO_TIME_T (interval_g); + c_ipmi_init_in_progress = 1 + (60 / iv); c_ipmi_active = 1; diff --git a/src/iptables.c b/src/iptables.c index aa53074a..c39aff8f 100644 --- a/src/iptables.c +++ b/src/iptables.c @@ -1,8 +1,8 @@ /** * collectd - src/iptables.c - * Copyright (C) 2007 Sjoerd van der Berg - * Copyright (C) 2007 Florian octo Forster - * Copyright (C) 2009 Marco Chiappero + * Copyright (C) 2007 Sjoerd van der Berg + * Copyright (C) 2007-2010 Florian octo Forster + * Copyright (C) 2009 Marco Chiappero * * 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 @@ -20,7 +20,7 @@ * * Authors: * Sjoerd van der Berg - * Florian Forster + * Florian Forster * Marco Chiappero **/ @@ -297,11 +297,11 @@ static int submit6_match (const struct ip6t_entry_match *match, } sstrncpy (vl.type, "ipt_bytes", sizeof (vl.type)); - values[0].counter = (counter_t) entry->counters.bcnt; + values[0].derive = (derive_t) entry->counters.bcnt; plugin_dispatch_values (&vl); sstrncpy (vl.type, "ipt_packets", sizeof (vl.type)); - values[0].counter = (counter_t) entry->counters.pcnt; + values[0].derive = (derive_t) entry->counters.pcnt; plugin_dispatch_values (&vl); return (0); @@ -358,11 +358,11 @@ static int submit_match (const struct ipt_entry_match *match, } sstrncpy (vl.type, "ipt_bytes", sizeof (vl.type)); - values[0].counter = (counter_t) entry->counters.bcnt; + values[0].derive = (derive_t) entry->counters.bcnt; plugin_dispatch_values (&vl); sstrncpy (vl.type, "ipt_packets", sizeof (vl.type)); - values[0].counter = (counter_t) entry->counters.pcnt; + values[0].derive = (derive_t) entry->counters.pcnt; plugin_dispatch_values (&vl); return (0); diff --git a/src/ipvs.c b/src/ipvs.c index 7bd8c698..fa894897 100644 --- a/src/ipvs.c +++ b/src/ipvs.c @@ -227,18 +227,16 @@ static int get_ti (struct ip_vs_dest_entry *de, char *ti, size_t size) return 0; } /* get_ti */ -static void cipvs_submit_connections (char *pi, char *ti, counter_t value) +static void cipvs_submit_connections (char *pi, char *ti, derive_t value) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = value; + values[0].derive = value; vl.values = values; vl.values_len = 1; - vl.interval = interval_g; - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "ipvs", sizeof (vl.plugin)); sstrncpy (vl.plugin_instance, pi, sizeof (vl.plugin_instance)); @@ -251,19 +249,17 @@ static void cipvs_submit_connections (char *pi, char *ti, counter_t value) } /* cipvs_submit_connections */ static void cipvs_submit_if (char *pi, char *t, char *ti, - counter_t rx, counter_t tx) + derive_t rx, derive_t tx) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; vl.values = values; vl.values_len = 2; - vl.interval = interval_g; - sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "ipvs", sizeof (vl.plugin)); sstrncpy (vl.plugin_instance, pi, sizeof (vl.plugin_instance)); diff --git a/src/irq.c b/src/irq.c index 70d6b5ab..7e738291 100644 --- a/src/irq.c +++ b/src/irq.c @@ -106,7 +106,7 @@ static int check_ignore_irq (const char *irq) return (1 - irq_list_action); } -static void irq_submit (const char *irq_name, counter_t value) +static void irq_submit (const char *irq_name, derive_t value) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; @@ -114,7 +114,7 @@ static void irq_submit (const char *irq_name, counter_t value) if (check_ignore_irq (irq_name)) return; - values[0].counter = value; + values[0].derive = value; vl.values = values; vl.values_len = 1; @@ -130,25 +130,25 @@ static int irq_read (void) { FILE *fh; char buffer[1024]; - unsigned long long irq_value; - unsigned long long value; - char *endptr; - int i; - char *fields[64]; - int fields_num; - - if ((fh = fopen ("/proc/interrupts", "r")) == NULL) + fh = fopen ("/proc/interrupts", "r"); + if (fh == NULL) { char errbuf[1024]; - WARNING ("irq plugin: fopen (/proc/interrupts): %s", + ERROR ("irq plugin: fopen (/proc/interrupts): %s", sstrerror (errno, errbuf, sizeof (errbuf))); return (-1); } + while (fgets (buffer, sizeof (buffer), fh) != NULL) { char *irq_name; size_t irq_name_len; + derive_t irq_value; + int i; + + char *fields[64]; + int fields_num; fields_num = strsplit (buffer, fields, 64); if (fields_num < 2) @@ -170,23 +170,22 @@ static int irq_read (void) irq_value = 0; for (i = 1; i < fields_num; i++) { - errno = 0; - endptr = NULL; - value = strtoull (fields[i], &endptr, 10); + /* Per-CPU value */ + value_t v; + int status; - /* Ignore all fields following a non-numeric field. */ - if ((errno != 0) || (endptr == NULL) || (*endptr != 0)) + status = parse_value (fields[i], &v, DS_TYPE_DERIVE); + if (status != 0) break; - irq_value += value; + irq_value += v.derive; } /* for (i) */ /* No valid fields -> do not submit anything. */ if (i <= 1) continue; - /* Force 32bit wrap-around */ - irq_submit (irq_name, irq_value % 4294967296ULL); + irq_submit (irq_name, irq_value); } fclose (fh); diff --git a/src/java.c b/src/java.c index a8ffd8e3..b69ca946 100644 --- a/src/java.c +++ b/src/java.c @@ -109,7 +109,7 @@ static int cjni_callback_register (JNIEnv *jvm_env, jobject o_name, static int cjni_read (user_data_t *user_data); static int cjni_write (const data_set_t *ds, const value_list_t *vl, user_data_t *ud); -static int cjni_flush (int timeout, const char *identifier, user_data_t *ud); +static int cjni_flush (cdtime_t timeout, const char *identifier, user_data_t *ud); static void cjni_log (int severity, const char *message, user_data_t *ud); static int cjni_notification (const notification_t *n, user_data_t *ud); @@ -809,7 +809,7 @@ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */ #undef SET_STRING /* Set the `time' member. Java stores time in milliseconds. */ - status = ctoj_long (jvm_env, ((jlong) vl->time) * ((jlong) 1000), + status = ctoj_long (jvm_env, (jlong) CDTIME_T_TO_MS (vl->time), c_valuelist, o_valuelist, "setTime"); if (status != 0) { @@ -819,7 +819,8 @@ static jobject ctoj_value_list (JNIEnv *jvm_env, /* {{{ */ } /* Set the `interval' member.. */ - status = ctoj_long (jvm_env, (jlong) vl->interval, + status = ctoj_long (jvm_env, + (jlong) CDTIME_T_TO_MS (vl->interval), c_valuelist, o_valuelist, "setInterval"); if (status != 0) { @@ -914,7 +915,7 @@ static jobject ctoj_notification (JNIEnv *jvm_env, /* {{{ */ return (NULL); } - /* Set the `interval' member.. */ + /* Set the `severity' member.. */ status = ctoj_int (jvm_env, (jint) n->severity, c_notification, o_notification, "setSeverity"); if (status != 0) @@ -1242,7 +1243,7 @@ static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */ return (-1); } /* Java measures time in milliseconds. */ - vl->time = (time_t) (tmp_long / ((jlong) 1000)); + vl->time = MS_TO_CDTIME_T (tmp_long); status = jtoc_long (jvm_env, &tmp_long, class_ptr, object_ptr, "getInterval"); @@ -1251,7 +1252,7 @@ static int jtoc_value_list (JNIEnv *jvm_env, value_list_t *vl, /* {{{ */ ERROR ("java plugin: jtoc_value_list: jtoc_long (getInterval) failed."); return (-1); } - vl->interval = (int) tmp_long; + vl->interval = MS_TO_CDTIME_T (tmp_long); status = jtoc_values_array (jvm_env, ds, vl, class_ptr, object_ptr); if (status != 0) @@ -1729,7 +1730,7 @@ static cjni_callback_info_t *cjni_callback_info_create (JNIEnv *jvm_env, /* {{{ case CB_TYPE_FLUSH: method_name = "flush"; - method_signature = "(ILjava/lang/String;)I"; + method_signature = "(Ljava/lang/Number;Ljava/lang/String;)I"; break; case CB_TYPE_SHUTDOWN: @@ -2552,11 +2553,12 @@ static int cjni_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */ } /* }}} int cjni_write */ /* Call the CB_TYPE_FLUSH callback pointed to by the `user_data_t' pointer. */ -static int cjni_flush (int timeout, const char *identifier, /* {{{ */ +static int cjni_flush (cdtime_t timeout, const char *identifier, /* {{{ */ user_data_t *ud) { JNIEnv *jvm_env; cjni_callback_info_t *cbi; + jobject o_timeout; jobject o_identifier; int status; int ret_status; @@ -2579,21 +2581,32 @@ static int cjni_flush (int timeout, const char *identifier, /* {{{ */ cbi = (cjni_callback_info_t *) ud->data; + o_timeout = ctoj_jdouble_to_number (jvm_env, + (jdouble) CDTIME_T_TO_DOUBLE (timeout)); + if (o_timeout == NULL) + { + ERROR ("java plugin: cjni_flush: Converting double " + "to Number object failed."); + return (-1); + } + o_identifier = NULL; if (identifier != NULL) { o_identifier = (*jvm_env)->NewStringUTF (jvm_env, identifier); if (o_identifier == NULL) { + (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout); ERROR ("java plugin: cjni_flush: NewStringUTF failed."); return (-1); } } ret_status = (*jvm_env)->CallIntMethod (jvm_env, - cbi->object, cbi->method, (jint) timeout, o_identifier); + cbi->object, cbi->method, o_timeout, o_identifier); (*jvm_env)->DeleteLocalRef (jvm_env, o_identifier); + (*jvm_env)->DeleteLocalRef (jvm_env, o_timeout); status = cjni_thread_detach (); if (status != 0) diff --git a/src/libcollectdclient/client.c b/src/libcollectdclient/client.c index 2f427a83..f5fe2049 100644 --- a/src/libcollectdclient/client.c +++ b/src/libcollectdclient/client.c @@ -164,27 +164,14 @@ static int lcc_set_errno (lcc_connection_t *c, int err) /* {{{ */ return (0); } /* }}} int lcc_set_errno */ -/* lcc_strdup: Since `strdup' is an XSI extension, we provide our own version - * here. */ -__attribute__((malloc, nonnull (1))) -static char *lcc_strdup (const char *str) /* {{{ */ -{ - size_t strsize; - char *ret; - - strsize = strlen (str) + 1; - ret = (char *) malloc (strsize); - if (ret != NULL) - memcpy (ret, str, strsize); - return (ret); -} /* }}} char *lcc_strdup */ - -__attribute__((nonnull (1, 2))) static char *lcc_strescape (char *dest, const char *src, size_t dest_size) /* {{{ */ { size_t dest_pos; size_t src_pos; + if ((dest == NULL) || (src == NULL)) + return (NULL); + dest_pos = 0; src_pos = 0; @@ -338,7 +325,7 @@ static int lcc_receive (lcc_connection_t *c, /* {{{ */ lcc_chomp (buffer); LCC_DEBUG ("receive: <-- %s\n", buffer); - res.lines[i] = lcc_strdup (buffer); + res.lines[i] = strdup (buffer); if (res.lines[i] == NULL) { lcc_set_errno (c, ENOMEM); @@ -733,7 +720,7 @@ int lcc_getval (lcc_connection_t *c, lcc_identifier_t *ident, /* {{{ */ if (values_names != NULL) { - values_names[i] = lcc_strdup (key); + values_names[i] = strdup (key); if (values_names[i] == NULL) BAIL_OUT (ENOMEM); } @@ -790,7 +777,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); } @@ -1015,7 +1002,7 @@ int lcc_string_to_identifier (lcc_connection_t *c, /* {{{ */ char *type; char *type_instance; - string_copy = lcc_strdup (string); + string_copy = strdup (string); if (string_copy == NULL) { lcc_set_errno (c, ENOMEM); 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 1f628792..774067cd 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,22 +112,19 @@ 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; static int refresh_lists (void); -/* Submit functions. */ -static void cpu_submit (unsigned long long cpu_time, - time_t t, - virDomainPtr dom, const char *type); -static void vcpu_submit (unsigned long long cpu_time, - time_t t, - virDomainPtr dom, int vcpu_nr, const char *type); -static void submit_counter2 (const char *type, counter_t v0, counter_t v1, - time_t t, - virDomainPtr dom, const char *devname); - /* ERROR(...) macro for virterrors. */ #define VIRT_ERROR(conn,s) do { \ virErrorPtr err; \ @@ -133,6 +132,109 @@ static void submit_counter2 (const char *type, counter_t v0, counter_t v1, if (err) ERROR ("%s: %s", (s), err->message); \ } while(0) +static void +init_value_list (value_list_t *vl, virDomainPtr dom) +{ + int i, n; + const char *name; + char uuid[VIR_UUID_STRING_BUFLEN]; + + vl->interval = interval_g; + + sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin)); + + vl->host[0] = '\0'; + + /* Construct the hostname field according to HostnameFormat. */ + for (i = 0; i < HF_MAX_FIELDS; ++i) { + if (hostname_format[i] == hf_none) + continue; + + n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2; + + if (i > 0 && n >= 1) { + strncat (vl->host, ":", 1); + n--; + } + + switch (hostname_format[i]) { + case hf_none: break; + case hf_hostname: + strncat (vl->host, hostname_g, n); + break; + case hf_name: + name = virDomainGetName (dom); + if (name) + strncat (vl->host, name, n); + break; + case hf_uuid: + if (virDomainGetUUIDString (dom, uuid) == 0) + strncat (vl->host, uuid, n); + break; + } + } + + vl->host[sizeof (vl->host) - 1] = '\0'; +} /* void init_value_list */ + +static void +cpu_submit (unsigned long long cpu_time, + virDomainPtr dom, const char *type) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + init_value_list (&vl, dom); + + values[0].derive = cpu_time; + + vl.values = values; + vl.values_len = 1; + + sstrncpy (vl.type, type, sizeof (vl.type)); + + plugin_dispatch_values (&vl); +} + +static void +vcpu_submit (derive_t cpu_time, + virDomainPtr dom, int vcpu_nr, const char *type) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + init_value_list (&vl, dom); + + values[0].derive = cpu_time; + vl.values = values; + vl.values_len = 1; + + sstrncpy (vl.type, type, sizeof (vl.type)); + ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr); + + plugin_dispatch_values (&vl); +} + +static void +submit_derive2 (const char *type, derive_t v0, derive_t v1, + virDomainPtr dom, const char *devname) +{ + value_t values[2]; + value_list_t vl = VALUE_LIST_INIT; + + init_value_list (&vl, dom); + + values[0].derive = v0; + values[1].derive = v1; + vl.values = values; + vl.values_len = 2; + + sstrncpy (vl.type, type, sizeof (vl.type)); + sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} /* void submit_derive2 */ + static int lv_init (void) { @@ -215,7 +317,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 +330,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 +343,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; } @@ -306,7 +420,7 @@ lv_read (void) continue; } - cpu_submit (info.cpuTime, t, domains[i], "virt_cpu_total"); + cpu_submit (info.cpuTime, domains[i], "virt_cpu_total"); vinfo = malloc (info.nrVirtCpu * sizeof (vinfo[0])); if (vinfo == NULL) { @@ -326,9 +440,9 @@ lv_read (void) for (j = 0; j < info.nrVirtCpu; ++j) vcpu_submit (vinfo[j].cpuTime, - t, domains[i], vinfo[j].number, "virt_vcpu"); + domains[i], vinfo[j].number, "virt_vcpu"); - free (vinfo); + sfree (vinfo); } /* Get block device stats for each domain. */ @@ -340,19 +454,23 @@ lv_read (void) continue; if ((stats.rd_req != -1) && (stats.wr_req != -1)) - submit_counter2 ("disk_ops", - (counter_t) stats.rd_req, (counter_t) stats.wr_req, - t, block_devices[i].dom, block_devices[i].path); + submit_derive2 ("disk_ops", + (derive_t) stats.rd_req, (derive_t) stats.wr_req, + block_devices[i].dom, block_devices[i].path); if ((stats.rd_bytes != -1) && (stats.wr_bytes != -1)) - submit_counter2 ("disk_octets", - (counter_t) stats.rd_bytes, (counter_t) stats.wr_bytes, - t, block_devices[i].dom, block_devices[i].path); + submit_derive2 ("disk_octets", + (derive_t) stats.rd_bytes, (derive_t) stats.wr_bytes, + block_devices[i].dom, block_devices[i].path); } /* for (nr_block_devices) */ /* 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, @@ -360,24 +478,24 @@ lv_read (void) continue; 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); + submit_derive2 ("if_octets", + (derive_t) stats.rx_bytes, (derive_t) stats.tx_bytes, + 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); + submit_derive2 ("if_packets", + (derive_t) stats.rx_packets, (derive_t) stats.tx_packets, + 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); + submit_derive2 ("if_errors", + (derive_t) stats.rx_errs, (derive_t) stats.tx_errs, + 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); + submit_derive2 ("if_dropped", + (derive_t) stats.rx_drop, (derive_t) stats.tx_drop, + interface_devices[i].dom, display_name); } /* for (nr_interface_devices) */ return 0; @@ -408,7 +526,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; } @@ -492,38 +610,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; @@ -537,7 +671,7 @@ free_domains () if (domains) { for (i = 0; i < nr_domains; ++i) virDomainFree (domains[i]); - free (domains); + sfree (domains); } domains = NULL; nr_domains = 0; @@ -569,8 +703,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; @@ -593,7 +727,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; @@ -608,36 +742,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++; } @@ -655,117 +796,10 @@ 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; } -static void -init_value_list (value_list_t *vl, time_t t, virDomainPtr dom) -{ - int i, n; - const char *name; - char uuid[VIR_UUID_STRING_BUFLEN]; - - vl->time = t; - vl->interval = interval_g; - - sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin)); - - vl->host[0] = '\0'; - - /* Construct the hostname field according to HostnameFormat. */ - for (i = 0; i < HF_MAX_FIELDS; ++i) { - if (hostname_format[i] == hf_none) - continue; - - n = DATA_MAX_NAME_LEN - strlen (vl->host) - 2; - - if (i > 0 && n >= 1) { - strncat (vl->host, ":", 1); - n--; - } - - switch (hostname_format[i]) { - case hf_none: break; - case hf_hostname: - strncat (vl->host, hostname_g, n); - break; - case hf_name: - name = virDomainGetName (dom); - if (name) - strncat (vl->host, name, n); - break; - case hf_uuid: - if (virDomainGetUUIDString (dom, uuid) == 0) - strncat (vl->host, uuid, n); - break; - } - } - - vl->host[sizeof (vl->host) - 1] = '\0'; -} /* void init_value_list */ - -static void -cpu_submit (unsigned long long cpu_time, - time_t t, - virDomainPtr dom, const char *type) -{ - value_t values[1]; - value_list_t vl = VALUE_LIST_INIT; - - init_value_list (&vl, t, dom); - - values[0].counter = cpu_time; - - vl.values = values; - vl.values_len = 1; - - sstrncpy (vl.type, type, sizeof (vl.type)); - - plugin_dispatch_values (&vl); -} - -static void -vcpu_submit (counter_t cpu_time, - time_t t, - virDomainPtr dom, int vcpu_nr, const char *type) -{ - value_t values[1]; - value_list_t vl = VALUE_LIST_INIT; - - init_value_list (&vl, t, dom); - - values[0].counter = cpu_time; - vl.values = values; - vl.values_len = 1; - - sstrncpy (vl.type, type, sizeof (vl.type)); - ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%d", vcpu_nr); - - plugin_dispatch_values (&vl); -} - -static void -submit_counter2 (const char *type, counter_t v0, counter_t v1, - time_t t, - virDomainPtr dom, const char *devname) -{ - value_t values[2]; - value_list_t vl = VALUE_LIST_INIT; - - init_value_list (&vl, t, dom); - - values[0].counter = v0; - values[1].counter = v1; - vl.values = values; - vl.values_len = 2; - - sstrncpy (vl.type, type, sizeof (vl.type)); - sstrncpy (vl.type_instance, devname, sizeof (vl.type_instance)); - - plugin_dispatch_values (&vl); -} /* void submit_counter2 */ - static int lv_shutdown (void) { diff --git a/src/logfile.c b/src/logfile.c index 6d0f6e07..60fb5d92 100644 --- a/src/logfile.c +++ b/src/logfile.c @@ -92,7 +92,8 @@ static int logfile_config (const char *key, const char *value) return 0; } /* int logfile_config (const char *, const char *) */ -static void logfile_print (const char *msg, int severity, time_t timestamp_time) +static void logfile_print (const char *msg, int severity, + cdtime_t timestamp_time) { FILE *fh; int do_close = 0; @@ -126,7 +127,8 @@ static void logfile_print (const char *msg, int severity, time_t timestamp_time) if (print_timestamp) { - localtime_r (×tamp_time, ×tamp_tm); + time_t tt = CDTIME_T_TO_TIME_T (timestamp_time); + localtime_r (&tt, ×tamp_tm); strftime (timestamp_str, sizeof (timestamp_str), "%Y-%m-%d %H:%M:%S", ×tamp_tm); @@ -179,7 +181,7 @@ static void logfile_log (int severity, const char *msg, if (severity > log_level) return; - logfile_print (msg, severity, time (NULL)); + logfile_print (msg, severity, cdtime ()); } /* void logfile_log (int, const char *) */ static int logfile_notification (const notification_t *n, @@ -218,7 +220,7 @@ static int logfile_notification (const notification_t *n, buf[sizeof (buf) - 1] = '\0'; logfile_print (buf, LOG_INFO, - (n->time > 0) ? n->time : time (NULL)); + (n->time != 0) ? n->time : cdtime ()); return (0); } /* int logfile_notification */ 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/madwifi.c b/src/madwifi.c index 8b3266dc..13301fff 100644 --- a/src/madwifi.c +++ b/src/madwifi.c @@ -561,20 +561,20 @@ static void submit (const char *dev, const char *type, const char *ti1, plugin_dispatch_values (&vl); } -static void submit_counter (const char *dev, const char *type, const char *ti1, - const char *ti2, counter_t val) +static void submit_derive (const char *dev, const char *type, const char *ti1, + const char *ti2, derive_t val) { value_t item; - item.counter = val; + item.derive = val; submit (dev, type, ti1, ti2, &item, 1); } -static void submit_counter2 (const char *dev, const char *type, const char *ti1, - const char *ti2, counter_t val1, counter_t val2) +static void submit_derive2 (const char *dev, const char *type, const char *ti1, + const char *ti2, derive_t val1, derive_t val2) { value_t items[2]; - items[0].counter = val1; - items[1].counter = val2; + items[0].derive = val1; + items[1].derive = val2; submit (dev, type, ti1, ti2, items, 2); } @@ -598,8 +598,8 @@ static void submit_antx (const char *dev, const char *name, continue; ssnprintf (ti2, sizeof (ti2), "%i", i); - submit_counter (dev, "ath_stat", name, ti2, - (counter_t) vals[i]); + submit_derive (dev, "ath_stat", name, ti2, + (derive_t) vals[i]); } } @@ -625,14 +625,14 @@ process_stat_struct (int which, const void *ptr, const char *dev, const char *ma uint32_t val = *(uint32_t *)(((char *) ptr) + specs[i].offset) ; if (item_watched (i) && (val != 0)) - submit_counter (dev, type_name, specs[i].name, mac, val); + submit_derive (dev, type_name, specs[i].name, mac, val); if (item_summed (i)) misc += val; } if (misc != 0) - submit_counter (dev, type_name, misc_name, mac, misc); + submit_derive (dev, type_name, misc_name, mac, misc); } @@ -734,13 +734,13 @@ process_station (int sk, const char *dev, struct ieee80211req_sta_info *si) /* These two stats are handled as a special case as they are a pair of 64bit values */ if (item_watched (STAT_NODE_OCTETS)) - submit_counter2 (dev, "node_octets", mac, NULL, + submit_derive2 (dev, "node_octets", mac, NULL, ns->ns_rx_bytes, ns->ns_tx_bytes); /* This stat is handled as a special case, because it is stored as uin64_t, but we will ignore upper half */ if (item_watched (STAT_NS_RX_BEACONS)) - submit_counter (dev, "node_stat", "ns_rx_beacons", mac, + submit_derive (dev, "node_stat", "ns_rx_beacons", mac, (ns->ns_rx_beacons & 0xFFFFFFFF)); /* All other node statistics */ diff --git a/src/match_timediff.c b/src/match_timediff.c index 4ac944af..2e274155 100644 --- a/src/match_timediff.c +++ b/src/match_timediff.c @@ -34,29 +34,13 @@ struct mt_match_s; typedef struct mt_match_s mt_match_t; struct mt_match_s { - time_t future; - time_t past; + cdtime_t future; + cdtime_t past; }; /* * internal helper functions */ -static int mt_config_add_time_t (time_t *ret_value, /* {{{ */ - oconfig_item_t *ci) -{ - - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) - { - ERROR ("timediff match: `%s' needs exactly one numeric argument.", - ci->key); - return (-1); - } - - *ret_value = (time_t) ci->values[0].value.number; - - return (0); -} /* }}} int mt_config_add_time_t */ - static int mt_create (const oconfig_item_t *ci, void **user_data) /* {{{ */ { mt_match_t *m; @@ -80,9 +64,9 @@ static int mt_create (const oconfig_item_t *ci, void **user_data) /* {{{ */ oconfig_item_t *child = ci->children + i; if (strcasecmp ("Future", child->key) == 0) - status = mt_config_add_time_t (&m->future, child); + status = cf_util_get_cdtime (child, &m->future); else if (strcasecmp ("Past", child->key) == 0) - status = mt_config_add_time_t (&m->past, child); + status = cf_util_get_cdtime (child, &m->past); else { ERROR ("timediff match: The `%s' configuration option is not " @@ -132,13 +116,13 @@ static int mt_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */ notification_meta_t __attribute__((unused)) **meta, void **user_data) { mt_match_t *m; - time_t now; + cdtime_t now; if ((user_data == NULL) || (*user_data == NULL)) return (-1); m = *user_data; - now = time (NULL); + now = cdtime (); if (m->future != 0) { diff --git a/src/memcachec.c b/src/memcachec.c index 73faa506..c57a8312 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 4c9e6adc..48fa713b 100644 --- a/src/memcached.c +++ b/src/memcached.c @@ -1,7 +1,7 @@ /** * collectd - src/memcached.c, based on src/hddtemp.c * Copyright (C) 2007 Antony Dovgal - * Copyright (C) 2007-2009 Florian Forster + * Copyright (C) 2007-2010 Florian Forster * Copyright (C) 2009 Doug MacEachern * Copyright (C) 2009 Franck Lombardi * @@ -21,7 +21,7 @@ * * Authors: * Antony Dovgal - * Florian octo Forster + * Florian octo Forster * Doug MacEachern * Franck Lombardi **/ @@ -176,12 +176,14 @@ static int memcached_query_daemon (char *buffer, int buffer_size) /* {{{ */ p.events = POLLIN | POLLERR | POLLHUP; p.revents = 0; - status = poll (&p, /* nfds = */ 1, /* timeout = */ 1000 * interval_g); + status = poll (&p, /* nfds = */ 1, + /* timeout = */ CDTIME_T_TO_MS (interval_g)); if (status <= 0) { if (status == 0) { - ERROR ("memcached: poll(2) timed out after %i seconds.", interval_g); + ERROR ("memcached: poll(2) timed out after %.3f seconds.", + CDTIME_T_TO_DOUBLE (interval_g)); } else { @@ -271,13 +273,13 @@ static int memcached_config (const char *key, const char *value) /* {{{ */ } /* }}} */ -static void submit_counter (const char *type, const char *type_inst, - counter_t value) /* {{{ */ +static void submit_derive (const char *type, const char *type_inst, + derive_t value) /* {{{ */ { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = value; + values[0].derive = value; vl.values = values; vl.values_len = 1; @@ -291,18 +293,17 @@ static void submit_counter (const char *type, const char *type_inst, } /* void memcached_submit_cmd */ /* }}} */ -static void submit_counter2 (const char *type, const char *type_inst, - counter_t value0, counter_t value1) /* {{{ */ +static void submit_derive2 (const char *type, const char *type_inst, + derive_t value0, derive_t value1) /* {{{ */ { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = value0; - values[1].counter = value1; + values[0].derive = value0; + values[1].derive = value1; 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 +324,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 +345,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)); @@ -369,10 +368,10 @@ static int memcached_read (void) /* {{{ */ gauge_t bytes_total = NAN; gauge_t hits = NAN; gauge_t gets = NAN; - counter_t rusage_user = 0; - counter_t rusage_syst = 0; - counter_t octets_rx = 0; - counter_t octets_tx = 0; + derive_t rusage_user = 0; + derive_t rusage_syst = 0; + derive_t octets_rx = 0; + derive_t octets_tx = 0; /* get data from daemon */ if (memcached_query_daemon (buf, sizeof (buf)) < 0) { @@ -457,7 +456,7 @@ static int memcached_read (void) /* {{{ */ else if ((name_len > 4) && (strncmp (fields[1], "cmd_", 4) == 0)) { const char *name = fields[1] + 4; - submit_counter ("memcached_command", name, atoll (fields[2])); + submit_derive ("memcached_command", name, atoll (fields[2])); if (strcmp (name, "get") == 0) gets = atof (fields[2]); } @@ -467,16 +466,16 @@ static int memcached_read (void) /* {{{ */ */ else if (FIELD_IS ("get_hits")) { - submit_counter ("memcached_ops", "hits", atoll (fields[2])); + submit_derive ("memcached_ops", "hits", atoll (fields[2])); hits = atof (fields[2]); } else if (FIELD_IS ("get_misses")) { - submit_counter ("memcached_ops", "misses", atoll (fields[2])); + submit_derive ("memcached_ops", "misses", atoll (fields[2])); } else if (FIELD_IS ("evictions")) { - submit_counter ("memcached_ops", "evictions", atoll (fields[2])); + submit_derive ("memcached_ops", "evictions", atoll (fields[2])); } /* @@ -496,10 +495,10 @@ static int memcached_read (void) /* {{{ */ submit_gauge2 ("df", "cache", bytes_used, bytes_total - bytes_used); if ((rusage_user != 0) || (rusage_syst != 0)) - submit_counter2 ("ps_cputime", NULL, rusage_user, rusage_syst); + submit_derive2 ("ps_cputime", NULL, rusage_user, rusage_syst); if ((octets_rx != 0) || (octets_tx != 0)) - submit_counter2 ("memcached_octets", NULL, octets_rx, octets_tx); + submit_derive2 ("memcached_octets", NULL, octets_rx, octets_tx); if (!isnan (gets) && !isnan (hits)) { diff --git a/src/modbus.c b/src/modbus.c index 40b6c57a..cad4b2cf 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -3,17 +3,18 @@ * Copyright (C) 2010 noris network AG * * 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. + * 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 - * General Public License for more details. + * Lesser 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 + * 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 @@ -28,6 +29,13 @@ #include +#ifndef LIBMODBUS_VERSION_CHECK +/* Assume version 2.0.3 */ +# define LEGACY_LIBMODBUS 1 +#else +/* Assume version 2.9.2 */ +#endif + #ifndef MODBUS_TCP_DEFAULT_PORT # ifdef MODBUS_TCP_PORT # define MODBUS_TCP_DEFAULT_PORT MODBUS_TCP_PORT @@ -94,12 +102,16 @@ struct mb_host_s /* {{{ */ char node[NI_MAXHOST]; /* char service[NI_MAXSERV]; */ int port; - int interval; + cdtime_t interval; mb_slave_t *slaves; size_t slaves_num; +#if LEGACY_LIBMODBUS modbus_param_t connection; +#else + modbus_t *connection; +#endif _Bool is_connected; _Bool have_reconnected; }; /* }}} */ @@ -265,6 +277,8 @@ static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */ return (conv.f); } /* }}} float mb_register_to_float */ +#if LEGACY_LIBMODBUS +/* Version 2.0.3 */ static int mb_init_connection (mb_host_t *host) /* {{{ */ { int status; @@ -306,6 +320,57 @@ static int mb_init_connection (mb_host_t *host) /* {{{ */ host->have_reconnected = 1; return (0); } /* }}} int mb_init_connection */ +/* #endif LEGACY_LIBMODBUS */ + +#else /* if !LEGACY_LIBMODBUS */ +/* Version 2.9.2 */ +static int mb_init_connection (mb_host_t *host) /* {{{ */ +{ + int status; + + if (host == NULL) + return (EINVAL); + + if (host->connection != NULL) + return (0); + + /* Only reconnect once per interval. */ + if (host->have_reconnected) + return (-1); + + if ((host->port < 1) || (host->port > 65535)) + host->port = MODBUS_TCP_DEFAULT_PORT; + + DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.", + host->node, host->port); + + host->connection = modbus_new_tcp (host->node, host->port); + if (host->connection == NULL) + { + host->have_reconnected = 1; + ERROR ("Modbus plugin: Creating new Modbus/TCP object failed."); + return (-1); + } + + modbus_set_debug (host->connection, 1); + + /* We'll do the error handling ourselves. */ + modbus_set_error_recovery (host->connection, 0); + + status = modbus_connect (host->connection); + if (status != 0) + { + ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.", + host->node, host->port, status); + modbus_free (host->connection); + host->connection = NULL; + return (status); + } + + host->have_reconnected = 1; + return (0); +} /* }}} int mb_init_connection */ +#endif /* !LEGACY_LIBMODBUS */ #define CAST_TO_VALUE_T(ds,vt,raw) do { \ if ((ds)->ds[0].type == DS_TYPE_COUNTER) \ @@ -360,22 +425,46 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ else values_num = 1; +#if LEGACY_LIBMODBUS + /* Version 2.0.3: Pass the connection struct as a pointer and pass the slave + * id to each call of "read_holding_registers". */ +# define modbus_read_registers(ctx, addr, nb, dest) \ + read_holding_registers (&(ctx), slave->id, (addr), (nb), (dest)) +#else /* if !LEGACY_LIBMODBUS */ + /* Version 2.9.2: Set the slave id once before querying the registers. */ + status = modbus_set_slave (host->connection, slave->id); + if (status != 0) + { + ERROR ("Modbus plugin: modbus_set_slave (%i) failed with status %i.", + slave->id, status); + return (-1); + } +#endif + for (i = 0; i < 2; i++) { - status = read_holding_registers (&host->connection, - /* slave = */ slave->id, /* start_addr = */ data->register_base, + status = modbus_read_registers (host->connection, + /* start_addr = */ data->register_base, /* num_registers = */ values_num, /* buffer = */ values); if (status > 0) break; if (host->is_connected) + { +#if LEGACY_LIBMODBUS modbus_close (&host->connection); - host->is_connected = 0; + host->is_connected = 0; +#else + modbus_close (host->connection); + modbus_free (host->connection); + host->connection = NULL; +#endif + } /* If we already tried reconnecting this round, give up. */ if (host->have_reconnected) { - ERROR ("Modbus plugin: read_holding_registers (%s) failed. " + ERROR ("Modbus plugin: modbus_read_registers (%s) failed. " "Reconnecting has already been tried. Giving up.", host->host); return (-1); } @@ -385,7 +474,7 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ status = mb_init_connection (host); if (status != 0) { - ERROR ("Modbus plugin: read_holding_registers (%s) failed. " + ERROR ("Modbus plugin: modbus_read_registers (%s) failed. " "While trying to reconnect, connecting to \"%s\" failed. " "Giving up.", host->host, host->node); @@ -399,7 +488,7 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */ } /* for (i = 0, 1) */ DEBUG ("Modbus plugin: mb_read_data: Success! " - "read_holding_registers returned with status %i.", status); + "modbus_read_registers returned with status %i.", status); if (data->register_type == REG_TYPE_FLOAT) { @@ -767,7 +856,7 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ status = -1; } else if (strcasecmp ("Interval", child->key) == 0) - status = cf_util_get_int (child, &host->interval); + status = cf_util_get_cdtime (child, &host->interval); else if (strcasecmp ("Slave", child->key) == 0) /* Don't set status: Gracefully continue if a slave fails. */ mb_config_add_slave (host, child); @@ -793,21 +882,19 @@ static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */ { user_data_t ud; char name[1024]; - struct timespec interval; + struct timespec interval = { 0, 0 }; ud.data = host; ud.free_func = host_free; ssnprintf (name, sizeof (name), "modbus-%s", host->host); - interval.tv_nsec = 0; - if (host->interval > 0) - interval.tv_sec = host->interval; - else - interval.tv_sec = 0; + CDTIME_T_TO_TIMESPEC (host->interval, &interval); plugin_register_complex_read (/* group = */ NULL, name, - mb_read, (interval.tv_sec > 0) ? &interval : NULL, &ud); + /* callback = */ mb_read, + /* interval = */ (host->interval > 0) ? &interval : NULL, + &ud); } else { diff --git a/src/mysql.c b/src/mysql.c index 69df7c70..32b352bb 100644 --- a/src/mysql.c +++ b/src/mysql.c @@ -1,6 +1,6 @@ /** * collectd - src/mysql.c - * Copyright (C) 2006-2009 Florian octo Forster + * Copyright (C) 2006-2010 Florian octo Forster * Copyright (C) 2008 Mirko Buffoni * Copyright (C) 2009 Doug MacEachern * Copyright (C) 2009 Sebastian tokkee Harl @@ -20,7 +20,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster * Mirko Buffoni * Doug MacEachern * Sebastian tokkee Harl @@ -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) { @@ -336,9 +192,9 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */ ud.data = (void *) db; ud.free_func = mysql_database_free; - if (db->database != NULL) + if (db->instance != NULL) ssnprintf (cb_name, sizeof (cb_name), "mysql-%s", - db->database); + db->instance); else sstrncpy (cb_name, "mysql", sizeof (cb_name)); @@ -352,6 +208,28 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */ return (-1); } + 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 */ @@ -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) @@ -456,11 +321,11 @@ static void submit (const char *type, const char *type_instance, } /* submit */ static void counter_submit (const char *type, const char *type_instance, - counter_t value, mysql_database_t *db) + derive_t value, mysql_database_t *db) { value_t values[1]; - values[0].counter = value; + values[0].derive = value; submit (type, type_instance, values, STATIC_ARRAY_SIZE (values), db); } /* void counter_submit */ @@ -473,40 +338,21 @@ 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) +static void traffic_submit (derive_t rx, derive_t tx, mysql_database_t *db) { value_t values[2]; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; submit ("mysql_octets", NULL, values, STATIC_ARRAY_SIZE (values), db); } /* void traffic_submit */ @@ -639,7 +485,7 @@ static int mysql_read_slave_stats (mysql_database_t *db, MYSQL *con) if (db->slave_notif) { - notification_t n = { 0, time (NULL), "", "", + notification_t n = { 0, cdtime (), "", "", "mysql", "", "time_offset", "", NULL }; char *io, *sql; @@ -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)) @@ -708,16 +556,16 @@ static int mysql_read (user_data_t *ud) MYSQL_ROW row; char *query; - 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; @@ -776,15 +624,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) @@ -798,13 +646,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) @@ -816,16 +664,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/netapp.c b/src/netapp.c index c9ff6b59..f7bc04d3 100644 --- a/src/netapp.c +++ b/src/netapp.c @@ -38,8 +38,8 @@ typedef void service_handler_t(host_config_t *host, na_elem_t *result, void *dat struct cna_interval_s { - time_t interval; - time_t last_read; + cdtime_t interval; + cdtime_t last_read; }; typedef struct cna_interval_s cna_interval_t; @@ -79,7 +79,7 @@ typedef struct { cna_interval_t interval; na_elem_t *query; - time_t timestamp; + cdtime_t timestamp; uint64_t name_cache_hit; uint64_t name_cache_miss; uint64_t find_dir_hit; @@ -104,7 +104,7 @@ typedef struct { typedef struct disk_s { char *name; uint32_t flags; - time_t timestamp; + cdtime_t timestamp; uint64_t disk_busy; uint64_t base_for_disk_busy; double disk_busy_percent; @@ -153,7 +153,7 @@ typedef struct data_volume_perf_s data_volume_perf_t; struct data_volume_perf_s { char *name; uint32_t flags; - time_t timestamp; + cdtime_t timestamp; uint64_t read_bytes; uint64_t write_bytes; @@ -242,7 +242,7 @@ struct host_config_s { int port; char *username; char *password; - int interval; + cdtime_t interval; na_server_t *srv; cfg_wafl_t *cfg_wafl; @@ -566,7 +566,7 @@ static int submit_values (const char *host, /* {{{ */ const char *plugin_inst, const char *type, const char *type_inst, value_t *values, int values_len, - time_t timestamp, int interval) + cdtime_t timestamp, cdtime_t interval) { value_list_t vl = VALUE_LIST_INIT; @@ -593,33 +593,34 @@ static int submit_values (const char *host, /* {{{ */ return (plugin_dispatch_values (&vl)); } /* }}} int submit_uint64 */ -static int submit_two_counters (const char *host, const char *plugin_inst, /* {{{ */ - const char *type, const char *type_inst, counter_t val0, counter_t val1, - time_t timestamp, int interval) +static int submit_two_derive (const char *host, const char *plugin_inst, /* {{{ */ + const char *type, const char *type_inst, derive_t val0, derive_t val1, + cdtime_t timestamp, cdtime_t interval) { value_t values[2]; - values[0].counter = val0; - values[1].counter = val1; + values[0].derive = val0; + values[1].derive = val1; return (submit_values (host, plugin_inst, type, type_inst, values, 2, timestamp, interval)); -} /* }}} int submit_two_counters */ +} /* }}} int submit_two_derive */ -static int submit_counter (const char *host, const char *plugin_inst, /* {{{ */ - const char *type, const char *type_inst, counter_t counter, time_t timestamp, int interval) +static int submit_derive (const char *host, const char *plugin_inst, /* {{{ */ + const char *type, const char *type_inst, derive_t counter, + cdtime_t timestamp, cdtime_t interval) { value_t v; - v.counter = counter; + v.derive = counter; return (submit_values (host, plugin_inst, type, type_inst, &v, 1, timestamp, interval)); -} /* }}} int submit_counter */ +} /* }}} int submit_derive */ static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ */ const char *type, const char *type_inst, gauge_t val0, gauge_t val1, - time_t timestamp, int interval) + cdtime_t timestamp, cdtime_t interval) { value_t values[2]; @@ -631,7 +632,8 @@ static int submit_two_gauge (const char *host, const char *plugin_inst, /* {{{ * } /* }}} int submit_two_gauge */ static int submit_double (const char *host, const char *plugin_inst, /* {{{ */ - const char *type, const char *type_inst, double d, time_t timestamp, int interval) + const char *type, const char *type_inst, double d, + cdtime_t timestamp, cdtime_t interval) { value_t v; @@ -650,8 +652,8 @@ static int submit_cache_ratio (const char *host, /* {{{ */ uint64_t new_misses, uint64_t old_hits, uint64_t old_misses, - time_t timestamp, - int interval) + cdtime_t timestamp, + cdtime_t interval) { value_t v; @@ -742,16 +744,16 @@ static int submit_volume_perf_data (const char *hostname, /* {{{ */ if (HAS_ALL_FLAGS (old_data->flags, CFG_VOLUME_PERF_IO) && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_BYTES_READ | HAVE_VOLUME_PERF_BYTES_WRITE)) { - submit_two_counters (hostname, plugin_instance, "disk_octets", /* type instance = */ NULL, - (counter_t) new_data->read_bytes, (counter_t) new_data->write_bytes, new_data->timestamp, interval); + submit_two_derive (hostname, plugin_instance, "disk_octets", /* type instance = */ NULL, + (derive_t) new_data->read_bytes, (derive_t) new_data->write_bytes, new_data->timestamp, interval); } /* Check for and submit disk-operations values */ if (HAS_ALL_FLAGS (old_data->flags, CFG_VOLUME_PERF_OPS) && HAS_ALL_FLAGS (new_data->flags, HAVE_VOLUME_PERF_OPS_READ | HAVE_VOLUME_PERF_OPS_WRITE)) { - submit_two_counters (hostname, plugin_instance, "disk_ops", /* type instance = */ NULL, - (counter_t) new_data->read_ops, (counter_t) new_data->write_ops, new_data->timestamp, interval); + submit_two_derive (hostname, plugin_instance, "disk_ops", /* type instance = */ NULL, + (derive_t) new_data->read_ops, (derive_t) new_data->write_ops, new_data->timestamp, interval); } /* Check for, calculate and submit disk-latency values */ @@ -816,6 +818,16 @@ static int submit_volume_perf_data (const char *hostname, /* {{{ */ return (0); } /* }}} int submit_volume_perf_data */ +static cdtime_t cna_child_get_cdtime (na_elem_t *data) /* {{{ */ +{ + time_t t; + + t = (time_t) na_child_get_uint64 (data, "timestamp", /* default = */ 0); + + return (TIME_T_TO_CDTIME_T (t)); +} /* }}} cdtime_t cna_child_get_cdtime */ + + /* * Query functions * @@ -835,7 +847,7 @@ static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /* memset (&perf_data, 0, sizeof (perf_data)); - perf_data.timestamp = (time_t) na_child_get_uint64 (data, "timestamp", 0); + perf_data.timestamp = cna_child_get_cdtime (data); instances = na_elem_child(na_elem_child (data, "instances"), "instance-data"); if (instances == NULL) @@ -950,7 +962,7 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */ { na_elem_t *data; int status; - time_t now; + cdtime_t now; if (host == NULL) return (EINVAL); @@ -959,7 +971,7 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */ if (host->cfg_wafl == NULL) return (0); - now = time (NULL); + now = cdtime (); if ((host->cfg_wafl->interval.interval + host->cfg_wafl->interval.last_read) > now) return (0); @@ -988,9 +1000,9 @@ static int cna_query_wafl (host_config_t *host) /* {{{ */ /* Data corresponding to */ static int cna_handle_disk_data (const char *hostname, /* {{{ */ - cfg_disk_t *cfg_disk, na_elem_t *data, int interval) + cfg_disk_t *cfg_disk, na_elem_t *data, cdtime_t interval) { - time_t timestamp; + cdtime_t timestamp; na_elem_t *instances; na_elem_t *instance; na_elem_iter_t instance_iter; @@ -999,7 +1011,7 @@ static int cna_handle_disk_data (const char *hostname, /* {{{ */ if ((cfg_disk == NULL) || (data == NULL)) return (EINVAL); - timestamp = (time_t) na_child_get_uint64(data, "timestamp", 0); + timestamp = cna_child_get_cdtime (data); instances = na_elem_child (data, "instances"); if (instances == NULL) @@ -1144,7 +1156,7 @@ static int cna_query_disk (host_config_t *host) /* {{{ */ { na_elem_t *data; int status; - time_t now; + cdtime_t now; if (host == NULL) return (EINVAL); @@ -1154,7 +1166,7 @@ static int cna_query_disk (host_config_t *host) /* {{{ */ if (host->cfg_disk == NULL) return (0); - now = time (NULL); + now = cdtime (); if ((host->cfg_disk->interval.interval + host->cfg_disk->interval.last_read) > now) return (0); @@ -1183,14 +1195,14 @@ static int cna_query_disk (host_config_t *host) /* {{{ */ /* Data corresponding to */ static int cna_handle_volume_perf_data (const char *hostname, /* {{{ */ - cfg_volume_perf_t *cvp, na_elem_t *data, int interval) + cfg_volume_perf_t *cvp, na_elem_t *data, cdtime_t interval) { - time_t timestamp; + cdtime_t timestamp; na_elem_t *elem_instances; na_elem_iter_t iter_instances; na_elem_t *elem_instance; - timestamp = (time_t) na_child_get_uint64(data, "timestamp", 0); + timestamp = cna_child_get_cdtime (data); elem_instances = na_elem_child(data, "instances"); if (elem_instances == NULL) @@ -1315,7 +1327,7 @@ static int cna_query_volume_perf (host_config_t *host) /* {{{ */ { na_elem_t *data; int status; - time_t now; + cdtime_t now; if (host == NULL) return (EINVAL); @@ -1325,7 +1337,7 @@ static int cna_query_volume_perf (host_config_t *host) /* {{{ */ if (host->cfg_volume_perf == NULL) return (0); - now = time (NULL); + now = cdtime (); if ((host->cfg_volume_perf->interval.interval + host->cfg_volume_perf->interval.last_read) > now) return (0); @@ -1444,7 +1456,7 @@ static int cna_change_volume_status (const char *hostname, /* {{{ */ notification_t n; memset (&n, 0, sizeof (&n)); - n.time = time (NULL); + n.time = cdtime (); sstrncpy (n.host, hostname, sizeof (n.host)); sstrncpy (n.plugin, "netapp", sizeof (n.plugin)); sstrncpy (n.plugin_instance, v->name, sizeof (n.plugin_instance)); @@ -1682,7 +1694,7 @@ static int cna_query_volume_usage (host_config_t *host) /* {{{ */ { na_elem_t *data; int status; - time_t now; + cdtime_t now; if (host == NULL) return (EINVAL); @@ -1692,7 +1704,7 @@ static int cna_query_volume_usage (host_config_t *host) /* {{{ */ if (host->cfg_volume_usage == NULL) return (0); - now = time (NULL); + now = cdtime (); if ((host->cfg_volume_usage->interval.interval + host->cfg_volume_usage->interval.last_read) > now) return (0); @@ -1727,15 +1739,15 @@ static int cna_handle_system_data (const char *hostname, /* {{{ */ na_elem_t *counter; na_elem_iter_t counter_iter; - counter_t disk_read = 0, disk_written = 0; - counter_t net_recv = 0, net_sent = 0; - counter_t cpu_busy = 0, cpu_total = 0; + derive_t disk_read = 0, disk_written = 0; + derive_t net_recv = 0, net_sent = 0; + derive_t cpu_busy = 0, cpu_total = 0; uint32_t counter_flags = 0; const char *instance; - time_t timestamp; + cdtime_t timestamp; - timestamp = (time_t) na_child_get_uint64 (data, "timestamp", 0); + timestamp = cna_child_get_cdtime (data); instances = na_elem_child(na_elem_child (data, "instances"), "instance-data"); if (instances == NULL) @@ -1772,47 +1784,47 @@ static int cna_handle_system_data (const char *hostname, /* {{{ */ continue; if (!strcmp(name, "disk_data_read")) { - disk_read = (counter_t) (value * 1024); + disk_read = (derive_t) (value * 1024); counter_flags |= 0x01; } else if (!strcmp(name, "disk_data_written")) { - disk_written = (counter_t) (value * 1024); + disk_written = (derive_t) (value * 1024); counter_flags |= 0x02; } else if (!strcmp(name, "net_data_recv")) { - net_recv = (counter_t) (value * 1024); + net_recv = (derive_t) (value * 1024); counter_flags |= 0x04; } else if (!strcmp(name, "net_data_sent")) { - net_sent = (counter_t) (value * 1024); + net_sent = (derive_t) (value * 1024); counter_flags |= 0x08; } else if (!strcmp(name, "cpu_busy")) { - cpu_busy = (counter_t) value; + cpu_busy = (derive_t) value; counter_flags |= 0x10; } else if (!strcmp(name, "cpu_elapsed_time")) { - cpu_total = (counter_t) value; + cpu_total = (derive_t) value; counter_flags |= 0x20; } else if ((cfg_system->flags & CFG_SYSTEM_OPS) && (value > 0) && (strlen(name) > 4) && (!strcmp(name + strlen(name) - 4, "_ops"))) { - submit_counter (hostname, instance, "disk_ops_complex", name, - (counter_t) value, timestamp, interval); + submit_derive (hostname, instance, "disk_ops_complex", name, + (derive_t) value, timestamp, interval); } } /* for (counter) */ if ((cfg_system->flags & CFG_SYSTEM_DISK) && (HAS_ALL_FLAGS (counter_flags, 0x01 | 0x02))) - submit_two_counters (hostname, instance, "disk_octets", NULL, + submit_two_derive (hostname, instance, "disk_octets", NULL, disk_read, disk_written, timestamp, interval); if ((cfg_system->flags & CFG_SYSTEM_NET) && (HAS_ALL_FLAGS (counter_flags, 0x04 | 0x08))) - submit_two_counters (hostname, instance, "if_octets", NULL, + submit_two_derive (hostname, instance, "if_octets", NULL, net_recv, net_sent, timestamp, interval); if ((cfg_system->flags & CFG_SYSTEM_CPU) && (HAS_ALL_FLAGS (counter_flags, 0x10 | 0x20))) { - submit_counter (hostname, instance, "cpu", "system", + submit_derive (hostname, instance, "cpu", "system", cpu_busy, timestamp, interval); - submit_counter (hostname, instance, "cpu", "idle", + submit_derive (hostname, instance, "cpu", "idle", cpu_total - cpu_busy, timestamp, interval); } @@ -1842,7 +1854,7 @@ static int cna_query_system (host_config_t *host) /* {{{ */ { na_elem_t *data; int status; - time_t now; + cdtime_t now; if (host == NULL) return (EINVAL); @@ -1851,7 +1863,7 @@ static int cna_query_system (host_config_t *host) /* {{{ */ if (host->cfg_system == NULL) return (0); - now = time (NULL); + now = cdtime (); if ((host->cfg_system->interval.interval + host->cfg_system->interval.last_read) > now) return (0); @@ -1908,23 +1920,12 @@ static int cna_config_bool_to_flag (const oconfig_item_t *ci, /* {{{ */ static int cna_config_get_interval (const oconfig_item_t *ci, /* {{{ */ cna_interval_t *out_interval) { - time_t tmp; - - if ((ci == NULL) || (out_interval == NULL)) - return (EINVAL); - - if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) - { - WARNING ("netapp plugin: The `Interval' option needs exactly one numeric argument."); - return (-1); - } + cdtime_t tmp = 0; + int status; - tmp = (time_t) (ci->values[0].value.number + .5); - if (tmp < 1) - { - WARNING ("netapp plugin: The `Interval' option needs a positive integer argument."); - return (-1); - } + status = cf_util_get_cdtime (ci, &tmp); + if (status != 0) + return (status); out_interval->interval = tmp; out_interval->last_read = 0; @@ -2424,11 +2425,7 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */ } else if (!strcasecmp(item->key, "Password")) { status = cf_util_get_string (item, &host->password); } else if (!strcasecmp(item->key, "Interval")) { - if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_NUMBER || item->values[0].value.number != (int) item->values[0].value.number || item->values[0].value.number < 2) { - WARNING("netapp plugin: \"Interval\" of host %s needs exactly one integer argument.", ci->values[0].value.string); - continue; - } - host->interval = item->values[0].value.number; + status = cf_util_get_cdtime (item, &host->interval); } else if (!strcasecmp(item->key, "WAFL")) { cna_config_wafl(host, item); } else if (!strcasecmp(item->key, "Disks")) { @@ -2587,8 +2584,7 @@ static int cna_config (oconfig_item_t *ci) { /* {{{ */ ssnprintf (cb_name, sizeof (cb_name), "netapp-%s", host->name); - memset (&interval, 0, sizeof (interval)); - interval.tv_sec = host->interval; + CDTIME_T_TO_TIMESPEC (host->interval, &interval); memset (&ud, 0, sizeof (ud)); ud.data = host; diff --git a/src/netlink.c b/src/netlink.c index bac87575..39536d92 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -1,6 +1,6 @@ /** * collectd - src/netlink.c - * Copyright (C) 2007 Florian octo Forster + * Copyright (C) 2007-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 @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster **/ #include "collectd.h" @@ -161,12 +161,12 @@ static int check_ignorelist (const char *dev, } /* int check_ignorelist */ static void submit_one (const char *dev, const char *type, - const char *type_instance, counter_t value) + const char *type_instance, derive_t value) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = value; + values[0].derive = value; vl.values = values; vl.values_len = 1; @@ -183,13 +183,13 @@ static void submit_one (const char *dev, const char *type, static void submit_two (const char *dev, const char *type, const char *type_instance, - counter_t rx, counter_t tx) + derive_t rx, derive_t tx) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; vl.values = values; vl.values_len = 2; diff --git a/src/network.c b/src/network.c index d6e0dc01..1f8fe0ad 100644 --- a/src/network.c +++ b/src/network.c @@ -1,6 +1,6 @@ /** * collectd - src/network.c - * Copyright (C) 2005-2009 Florian octo Forster + * Copyright (C) 2005-2010 Florian octo Forster * Copyright (C) 2009 Aman Gupta * * This program is free software; you can redistribute it and/or modify it @@ -271,7 +271,8 @@ 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; +/* Ethernet - (IPv6 + UDP) = 1500 - (40 + 8) = 1452 */ +static size_t network_config_packet_size = 1452; static int network_config_forward = 0; static int network_config_stats = 0; @@ -308,14 +309,14 @@ static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER; * example). Only if neither is true, the stats_lock is acquired. The counters * are always read without holding a lock in the hope that writing 8 bytes to * memory is an atomic operation. */ -static uint64_t stats_octets_rx = 0; -static uint64_t stats_octets_tx = 0; -static uint64_t stats_packets_rx = 0; -static uint64_t stats_packets_tx = 0; -static uint64_t stats_values_dispatched = 0; -static uint64_t stats_values_not_dispatched = 0; -static uint64_t stats_values_sent = 0; -static uint64_t stats_values_not_sent = 0; +static derive_t stats_octets_rx = 0; +static derive_t stats_octets_tx = 0; +static derive_t stats_packets_rx = 0; +static derive_t stats_packets_tx = 0; +static derive_t stats_values_dispatched = 0; +static derive_t stats_values_not_dispatched = 0; +static derive_t stats_values_sent = 0; +static derive_t stats_values_not_sent = 0; static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER; /* @@ -332,30 +333,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 @@ -433,7 +434,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."); @@ -1451,8 +1452,19 @@ static int parse_packet (sockent_t *se, /* {{{ */ &tmp); if (status == 0) { - vl.time = (time_t) tmp; - n.time = (time_t) tmp; + vl.time = TIME_T_TO_CDTIME_T (tmp); + n.time = TIME_T_TO_CDTIME_T (tmp); + } + } + else if (pkg_type == TYPE_TIME_HR) + { + uint64_t tmp = 0; + status = parse_part_number (&buffer, &buffer_size, + &tmp); + if (status == 0) + { + vl.time = (cdtime_t) tmp; + n.time = (cdtime_t) tmp; } } else if (pkg_type == TYPE_INTERVAL) @@ -1461,7 +1473,15 @@ static int parse_packet (sockent_t *se, /* {{{ */ status = parse_part_number (&buffer, &buffer_size, &tmp); if (status == 0) - vl.interval = (int) tmp; + vl.interval = TIME_T_TO_CDTIME_T (tmp); + } + else if (pkg_type == TYPE_INTERVAL_HR) + { + uint64_t tmp = 0; + status = parse_part_number (&buffer, &buffer_size, + &tmp); + if (status == 0) + vl.interval = (cdtime_t) tmp; } else if (pkg_type == TYPE_HOST) { @@ -2662,7 +2682,7 @@ static int add_to_buffer (char *buffer, int buffer_size, /* {{{ */ if (vl_def->time != vl->time) { - if (write_part_number (&buffer, &buffer_size, TYPE_TIME, + if (write_part_number (&buffer, &buffer_size, TYPE_TIME_HR, (uint64_t) vl->time)) return (-1); vl_def->time = vl->time; @@ -2670,7 +2690,7 @@ static int add_to_buffer (char *buffer, int buffer_size, /* {{{ */ if (vl_def->interval != vl->interval) { - if (write_part_number (&buffer, &buffer_size, TYPE_INTERVAL, + if (write_part_number (&buffer, &buffer_size, TYPE_INTERVAL_HR, (uint64_t) vl->interval)) return (-1); vl_def->interval = vl->interval; @@ -3132,8 +3152,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.", @@ -3157,7 +3175,7 @@ static int network_notification (const notification_t *n, memset (buffer, 0, sizeof (buffer)); - status = write_part_number (&buffer_ptr, &buffer_free, TYPE_TIME, + status = write_part_number (&buffer_ptr, &buffer_free, TYPE_TIME_HR, (uint64_t) n->time); if (status != 0) return (-1); @@ -3262,15 +3280,15 @@ static int network_shutdown (void) static int network_stats_read (void) /* {{{ */ { - uint64_t copy_octets_rx; - uint64_t copy_octets_tx; - uint64_t copy_packets_rx; - uint64_t copy_packets_tx; - uint64_t copy_values_dispatched; - uint64_t copy_values_not_dispatched; - uint64_t copy_values_sent; - uint64_t copy_values_not_sent; - uint64_t copy_receive_list_length; + derive_t copy_octets_rx; + derive_t copy_octets_tx; + derive_t copy_packets_rx; + derive_t copy_packets_tx; + derive_t copy_values_dispatched; + derive_t copy_values_not_dispatched; + derive_t copy_values_sent; + derive_t copy_values_not_sent; + derive_t copy_receive_list_length; value_list_t vl = VALUE_LIST_INIT; value_t values[2]; @@ -3293,14 +3311,14 @@ static int network_stats_read (void) /* {{{ */ sstrncpy (vl.plugin, "network", sizeof (vl.plugin)); /* Octets received / sent */ - vl.values[0].counter = (counter_t) copy_octets_rx; - vl.values[1].counter = (counter_t) copy_octets_tx; + vl.values[0].derive = (derive_t) copy_octets_rx; + vl.values[1].derive = (derive_t) copy_octets_tx; sstrncpy (vl.type, "if_octets", sizeof (vl.type)); plugin_dispatch_values_secure (&vl); /* Packets received / send */ - vl.values[0].counter = (counter_t) copy_packets_rx; - vl.values[1].counter = (counter_t) copy_packets_tx; + vl.values[0].derive = (derive_t) copy_packets_rx; + vl.values[1].derive = (derive_t) copy_packets_tx; sstrncpy (vl.type, "if_packets", sizeof (vl.type)); plugin_dispatch_values_secure (&vl); @@ -3339,13 +3357,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 /* http://lists.gnupg.org/pipermail/gcrypt-devel/2003-August/000458.html @@ -3439,9 +3457,9 @@ static int network_init (void) * just send the buffer if `flush' is called - if the requested value was in * there, good. If not, well, then there is nothing to flush.. -octo */ -static int network_flush (int timeout, - const char __attribute__((unused)) *identifier, - user_data_t __attribute__((unused)) *user_data) +static int network_flush (__attribute__((unused)) cdtime_t timeout, + __attribute__((unused)) const char *identifier, + __attribute__((unused)) user_data_t *user_data) { pthread_mutex_lock (&send_buffer_lock); diff --git a/src/network.h b/src/network.h index 777616c4..1b354561 100644 --- a/src/network.h +++ b/src/network.h @@ -56,12 +56,14 @@ #define TYPE_HOST 0x0000 #define TYPE_TIME 0x0001 +#define TYPE_TIME_HR 0x0008 #define TYPE_PLUGIN 0x0002 #define TYPE_PLUGIN_INSTANCE 0x0003 #define TYPE_TYPE 0x0004 #define TYPE_TYPE_INSTANCE 0x0005 #define TYPE_VALUES 0x0006 #define TYPE_INTERVAL 0x0007 +#define TYPE_INTERVAL_HR 0x0009 /* Types to transmit notifications */ #define TYPE_MESSAGE 0x0100 diff --git a/src/nfs.c b/src/nfs.c index e4d12e34..f3c636ec 100644 --- a/src/nfs.c +++ b/src/nfs.c @@ -195,7 +195,7 @@ static void nfs_procedures_submit (const char *plugin_instance, for (i = 0; i < len; i++) { - values[0].counter = val[i]; + values[0].derive = val[i]; sstrncpy (vl.type_instance, names[i], sizeof (vl.type_instance)); DEBUG ("%s-%s/nfs_procedure-%s = %llu", diff --git a/src/nginx.c b/src/nginx.c index d9529516..3e162bac 100644 --- a/src/nginx.c +++ b/src/nginx.c @@ -1,6 +1,6 @@ /** * collectd - src/nginx.c - * 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 @@ -18,7 +18,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster * Sebastian Harl **/ @@ -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); } @@ -178,7 +179,7 @@ static void submit (char *type, char *inst, long long value) if (strcmp (type, "nginx_connections") == 0) values[0].gauge = value; else if (strcmp (type, "nginx_requests") == 0) - values[0].counter = value; + values[0].derive = value; else return; diff --git a/src/notify_email.c b/src/notify_email.c index 6b4678a7..cd216ca2 100644 --- a/src/notify_email.c +++ b/src/notify_email.c @@ -229,6 +229,7 @@ static int notify_email_notification (const notification_t *n, user_data_t __attribute__((unused)) *user_data) { + time_t tt; struct tm timestamp_tm; char timestamp_str[64]; @@ -248,7 +249,8 @@ static int notify_email_notification (const notification_t *n, (email_subject == NULL) ? DEFAULT_SMTP_SUBJECT : email_subject, severity, n->host); - localtime_r (&n->time, ×tamp_tm); + tt = CDTIME_T_TO_TIME_T (n->time); + localtime_r (&tt, ×tamp_tm); strftime (timestamp_str, sizeof (timestamp_str), "%Y-%m-%d %H:%M:%S", ×tamp_tm); timestamp_str[sizeof (timestamp_str) - 1] = '\0'; diff --git a/src/onewire.c b/src/onewire.c index 462458c7..09a6bf09 100644 --- a/src/onewire.c +++ b/src/onewire.c @@ -59,7 +59,7 @@ static ow_family_features_t ow_family_features[] = static int ow_family_features_num = STATIC_ARRAY_SIZE (ow_family_features); static char *device_g = NULL; -static int ow_interval = 0; +static cdtime_t ow_interval = 0; static const char *config_keys[] = { @@ -106,10 +106,10 @@ static int cow_load_config (const char *key, const char *value) } else if (strcasecmp ("Interval", key) == 0) { - int tmp; - tmp = atoi (value); - if (tmp > 0) - ow_interval = tmp; + double tmp; + tmp = atof (value); + if (tmp > 0.0) + ow_interval = DOUBLE_TO_CDTIME_T (tmp); else ERROR ("onewire plugin: Invalid `Interval' setting: %s", value); } @@ -306,12 +306,11 @@ static int cow_init (void) return (1); } - memset (&cb_interval, 0, sizeof (cb_interval)); - if (ow_interval > 0) - cb_interval.tv_sec = (time_t) ow_interval; + CDTIME_T_TO_TIMESPEC (ow_interval, &cb_interval); plugin_register_complex_read (/* group = */ NULL, "onewire", cow_read, - &cb_interval, /* user data = */ NULL); + (ow_interval != 0) ? &cb_interval : NULL, + /* user data = */ NULL); plugin_register_shutdown ("onewire", cow_shutdown); return (0); diff --git a/src/openvpn.c b/src/openvpn.c index 2aca4145..9ce23b4f 100644 --- a/src/openvpn.c +++ b/src/openvpn.c @@ -1,9 +1,9 @@ /** * collectd - src/openvpn.c - * Copyright (C) 2008 Doug MacEachern - * Copyright (C) 2009 Florian octo Forster - * Copyright (C) 2009 Marco Chiappero - * Copyright (C) 2009 Fabian Schuh + * Copyright (C) 2008 Doug MacEachern + * Copyright (C) 2009,2010 Florian octo Forster + * Copyright (C) 2009 Marco Chiappero + * Copyright (C) 2009 Fabian Schuh * * 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 @@ -20,7 +20,7 @@ * * Authors: * Doug MacEachern - * Florian octo Forster + * Florian octo Forster * Marco Chiappero * Fabian Schuh **/ @@ -114,13 +114,13 @@ static void numusers_submit (char *pinst, char *tinst, gauge_t value) } /* void numusers_submit */ /* dispatches stats about traffic (TCP or UDP) generated by the tunnel per single endpoint */ -static void iostats_submit (char *pinst, char *tinst, counter_t rx, counter_t tx) +static void iostats_submit (char *pinst, char *tinst, derive_t rx, derive_t tx) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; /* NOTE ON THE NEW NAMING SCHEMA: * using plugin_instance to identify each vpn config (and @@ -144,13 +144,13 @@ static void iostats_submit (char *pinst, char *tinst, counter_t rx, counter_t tx /* dispatches stats about data compression shown when in single mode */ static void compression_submit (char *pinst, char *tinst, - counter_t uncompressed, counter_t compressed) + derive_t uncompressed, derive_t compressed) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = uncompressed; - values[1].counter = compressed; + values[0].derive = uncompressed; + values[1].derive = compressed; vl.values = values; vl.values_len = STATIC_ARRAY_SIZE (values); @@ -173,11 +173,11 @@ static int single_read (char *name, FILE *fh) const int max_fields = STATIC_ARRAY_SIZE (fields); int fields_num, read = 0; - counter_t link_rx, link_tx; - counter_t tun_rx, tun_tx; - counter_t pre_compress, post_compress; - counter_t pre_decompress, post_decompress; - counter_t overhead_rx, overhead_tx; + derive_t link_rx, link_tx; + derive_t tun_rx, tun_tx; + derive_t pre_compress, post_compress; + derive_t pre_decompress, post_decompress; + derive_t overhead_rx, overhead_tx; link_rx = 0; link_tx = 0; diff --git a/src/oracle.c b/src/oracle.c index 828fc2e5..80ae699c 100644 --- a/src/oracle.c +++ b/src/oracle.c @@ -352,7 +352,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 @@ -615,7 +615,7 @@ static int o_read_database_query (o_database_t *db, /* {{{ */ status = udb_query_prepare_result (q, prep_area, hostname_g, /* plugin = */ "oracle", db->name, column_names, column_num, - /* interval = */ -1); + /* interval = */ 0); if (status != 0) { ERROR ("oracle plugin: o_read_database_query (%s, %s): " diff --git a/src/perl.c b/src/perl.c index 41e763dc..a2568da2 100644 --- a/src/perl.c +++ b/src/perl.c @@ -33,6 +33,10 @@ #include "configfile.h" +#if HAVE_STDBOOL_H +# include +#endif + #include #include @@ -231,15 +235,6 @@ struct { { "", NULL } }; -struct { - char name[64]; - int *var; -} g_integers[] = -{ - { "Collectd::interval_g", &interval_g }, - { "", NULL } -}; - /* * Helper functions for data type conversion. */ @@ -393,10 +388,16 @@ static int hv2value_list (pTHX_ HV *hash, value_list_t *vl) } if (NULL != (tmp = hv_fetch (hash, "time", 4, 0))) - vl->time = (time_t)SvIV (*tmp); + { + double t = SvNV (*tmp); + vl->time = DOUBLE_TO_CDTIME_T (t); + } if (NULL != (tmp = hv_fetch (hash, "interval", 8, 0))) - vl->interval = SvIV (*tmp); + { + double t = SvNV (*tmp); + vl->interval = DOUBLE_TO_CDTIME_T (t); + } if (NULL != (tmp = hv_fetch (hash, "host", 4, 0))) sstrncpy (vl->host, SvPV_nolen (*tmp), sizeof (vl->host)); @@ -548,9 +549,12 @@ static int hv2notification (pTHX_ HV *hash, notification_t *n) n->severity = NOTIF_FAILURE; if (NULL != (tmp = hv_fetch (hash, "time", 4, 0))) - n->time = (time_t)SvIV (*tmp); + { + double t = SvNV (*tmp); + n->time = DOUBLE_TO_CDTIME_T (t); + } else - n->time = time (NULL); + n->time = cdtime (); if (NULL != (tmp = hv_fetch (hash, "message", 7, 0))) sstrncpy (n->message, SvPV_nolen (*tmp), sizeof (n->message)); @@ -668,11 +672,17 @@ static int value_list2hv (pTHX_ value_list_t *vl, data_set_t *ds, HV *hash) return -1; if (0 != vl->time) - if (NULL == hv_store (hash, "time", 4, newSViv (vl->time), 0)) + { + double t = CDTIME_T_TO_DOUBLE (vl->time); + if (NULL == hv_store (hash, "time", 4, newSVnv (t), 0)) return -1; + } - if (NULL == hv_store (hash, "interval", 8, newSViv (vl->interval), 0)) - return -1; + { + double t = CDTIME_T_TO_DOUBLE (vl->interval); + if (NULL == hv_store (hash, "interval", 8, newSVnv (t), 0)) + return -1; + } if ('\0' != vl->host[0]) if (NULL == hv_store (hash, "host", 4, newSVpv (vl->host, 0), 0)) @@ -750,8 +760,11 @@ static int notification2hv (pTHX_ notification_t *n, HV *hash) return -1; if (0 != n->time) - if (NULL == hv_store (hash, "time", 4, newSViv (n->time), 0)) + { + double t = CDTIME_T_TO_DOUBLE (n->time); + if (NULL == hv_store (hash, "time", 4, newSVnv (t), 0)) return -1; + } if ('\0' != *n->message) if (NULL == hv_store (hash, "message", 7, newSVpv (n->message, 0), 0)) @@ -1102,11 +1115,15 @@ static int pplugin_call_all (pTHX_ int type, ...) XPUSHs (sv_2mortal (newRV_noinc ((SV *)notif))); } else if (PLUGIN_FLUSH == type) { + cdtime_t timeout; + /* * $_[0] = $timeout; * $_[1] = $identifier; */ - XPUSHs (sv_2mortal (newSViv (va_arg (ap, int)))); + timeout = va_arg (ap, cdtime_t); + + XPUSHs (sv_2mortal (newSVnv (CDTIME_T_TO_DOUBLE (timeout)))); XPUSHs (sv_2mortal (newSVpv (va_arg (ap, char *), 0))); } @@ -1610,40 +1627,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)); @@ -2020,7 +2026,7 @@ static int perl_notify (const notification_t *notif, return pplugin_call_all (aTHX_ PLUGIN_NOTIF, notif); } /* static int perl_notify (const notification_t *) */ -static int perl_flush (int timeout, const char *identifier, +static int perl_flush (cdtime_t timeout, const char *identifier, user_data_t __attribute__((unused)) *user_data) { dTHX; @@ -2122,19 +2128,27 @@ static int g_pv_set (pTHX_ SV *var, MAGIC *mg) return 0; } /* static int g_pv_set (pTHX_ SV *, MAGIC *) */ -static int g_iv_get (pTHX_ SV *var, MAGIC *mg) +static int g_interval_get (pTHX_ SV *var, MAGIC *mg) { - int *iv = (int *)mg->mg_ptr; - sv_setiv (var, *iv); + cdtime_t *interval = (cdtime_t *)mg->mg_ptr; + double nv; + + nv = CDTIME_T_TO_DOUBLE (*interval); + + sv_setnv (var, nv); return 0; -} /* static int g_iv_get (pTHX_ SV *, MAGIC *) */ +} /* static int g_interval_get (pTHX_ SV *, MAGIC *) */ -static int g_iv_set (pTHX_ SV *var, MAGIC *mg) +static int g_interval_set (pTHX_ SV *var, MAGIC *mg) { - int *iv = (int *)mg->mg_ptr; - *iv = (int)SvIV (var); + cdtime_t *interval = (cdtime_t *)mg->mg_ptr; + double nv; + + nv = (double)SvNV (var); + + *interval = DOUBLE_TO_CDTIME_T (nv); return 0; -} /* static int g_iv_set (pTHX_ SV *, MAGIC *) */ +} /* static int g_interval_set (pTHX_ SV *, MAGIC *) */ static MGVTBL g_pv_vtbl = { g_pv_get, g_pv_set, NULL, NULL, NULL, NULL, NULL @@ -2142,8 +2156,8 @@ static MGVTBL g_pv_vtbl = { , NULL #endif }; -static MGVTBL g_iv_vtbl = { - g_iv_get, g_iv_set, NULL, NULL, NULL, NULL, NULL +static MGVTBL g_interval_vtbl = { + g_interval_get, g_interval_set, NULL, NULL, NULL, NULL, NULL #if HAVE_PERL_STRUCT_MGVTBL_SVT_LOCAL , NULL #endif @@ -2185,12 +2199,11 @@ static void xs_init (pTHX) g_strings[i].var, 0); } - /* global integers */ - for (i = 0; '\0' != g_integers[i].name[0]; ++i) { - tmp = get_sv (g_integers[i].name, 1); - sv_magicext (tmp, NULL, PERL_MAGIC_ext, &g_iv_vtbl, - (char *)g_integers[i].var, 0); - } + tmp = get_sv ("Collectd::interval_g", /* create = */ 1); + sv_magicext (tmp, NULL, /* how = */ PERL_MAGIC_ext, + /* vtbl = */ &g_interval_vtbl, + /* name = */ (char *) &interval_g, /* namelen = */ 0); + return; } /* static void xs_init (pTHX) */ diff --git a/src/plugin.c b/src/plugin.c index 3c92df21..91c40b6b 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -1,6 +1,6 @@ /** * collectd - src/plugin.c - * Copyright (C) 2005-2009 Florian octo Forster + * Copyright (C) 2005-2011 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 @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster * Sebastian Harl **/ @@ -36,7 +36,6 @@ #include "utils_llist.h" #include "utils_heap.h" #include "utils_cache.h" -#include "utils_threshold.h" #include "filter_chain.h" /* @@ -74,6 +73,7 @@ typedef struct read_func_s read_func_t; static llist_t *list_init; static llist_t *list_write; static llist_t *list_flush; +static llist_t *list_missing; static llist_t *list_shutdown; static llist_t *list_log; static llist_t *list_notification; @@ -346,7 +346,7 @@ static void *plugin_read_thread (void __attribute__((unused)) *args) while (read_loop != 0) { read_func_t *rf; - struct timeval now; + cdtime_t now; int status; int rf_type; int rc; @@ -357,10 +357,9 @@ static void *plugin_read_thread (void __attribute__((unused)) *args) { struct timespec abstime; - gettimeofday (&now, /* timezone = */ NULL); + now = cdtime (); - abstime.tv_sec = now.tv_sec + interval_g; - abstime.tv_nsec = 1000 * now.tv_usec; + CDTIME_T_TO_TIMESPEC (now + interval_g, &abstime); pthread_mutex_lock (&read_lock); pthread_cond_timedwait (&read_cond, &read_lock, @@ -371,15 +370,13 @@ static void *plugin_read_thread (void __attribute__((unused)) *args) if ((rf->rf_interval.tv_sec == 0) && (rf->rf_interval.tv_nsec == 0)) { - gettimeofday (&now, /* timezone = */ NULL); + now = cdtime (); - rf->rf_interval.tv_sec = interval_g; - rf->rf_interval.tv_nsec = 0; + CDTIME_T_TO_TIMESPEC (interval_g, &rf->rf_interval); rf->rf_effective_interval = rf->rf_interval; - rf->rf_next_read.tv_sec = now.tv_sec; - rf->rf_next_read.tv_nsec = 1000 * now.tv_usec; + CDTIME_T_TO_TIMESPEC (now, &rf->rf_next_read); } /* sleep until this entry is due, @@ -469,7 +466,7 @@ static void *plugin_read_thread (void __attribute__((unused)) *args) } /* update the ``next read due'' field */ - gettimeofday (&now, /* timezone = */ NULL); + now = cdtime (); DEBUG ("plugin_read_thread: Effective interval of the " "%s plugin is %i.%09i.", @@ -486,15 +483,12 @@ static void *plugin_read_thread (void __attribute__((unused)) *args) NORMALIZE_TIMESPEC (rf->rf_next_read); /* Check, if `rf_next_read' is in the past. */ - if ((rf->rf_next_read.tv_sec < now.tv_sec) - || ((rf->rf_next_read.tv_sec == now.tv_sec) - && (rf->rf_next_read.tv_nsec < (1000 * now.tv_usec)))) + if (TIMESPEC_TO_CDTIME_T (&rf->rf_next_read) < now) { /* `rf_next_read' is in the past. Insert `now' * so this value doesn't trail off into the * past too much. */ - rf->rf_next_read.tv_sec = now.tv_sec; - rf->rf_next_read.tv_nsec = 1000 * now.tv_usec; + CDTIME_T_TO_TIMESPEC (now, &rf->rf_next_read); } DEBUG ("plugin_read_thread: Next read of the %s plugin at %i.%09i.", @@ -744,6 +738,17 @@ static int plugin_insert_read (read_func_t *rf) } } + le = llist_search (read_list, rf->rf_name); + if (le != NULL) + { + pthread_mutex_unlock (&read_lock); + WARNING ("The read function \"%s\" is already registered. " + "Check for duplicate \"LoadPlugin\" lines " + "in your configuration!", + rf->rf_name); + return (EINVAL); + } + le = llentry_create (rf->rf_name, rf); if (le == NULL) { @@ -772,14 +777,13 @@ int plugin_register_read (const char *name, int (*callback) (void)) { read_func_t *rf; + int status; - rf = (read_func_t *) malloc (sizeof (read_func_t)); + rf = malloc (sizeof (*rf)); if (rf == NULL) { - char errbuf[1024]; - ERROR ("plugin_register_read: malloc failed: %s", - sstrerror (errno, errbuf, sizeof (errbuf))); - return (-1); + ERROR ("plugin_register_read: malloc failed."); + return (ENOMEM); } memset (rf, 0, sizeof (read_func_t)); @@ -793,7 +797,11 @@ int plugin_register_read (const char *name, rf->rf_interval.tv_nsec = 0; rf->rf_effective_interval = rf->rf_interval; - return (plugin_insert_read (rf)); + status = plugin_insert_read (rf); + if (status != 0) + sfree (rf); + + return (status); } /* int plugin_register_read */ int plugin_register_complex_read (const char *group, const char *name, @@ -802,12 +810,13 @@ int plugin_register_complex_read (const char *group, const char *name, user_data_t *user_data) { read_func_t *rf; + int status; - rf = (read_func_t *) malloc (sizeof (read_func_t)); + rf = malloc (sizeof (*rf)); if (rf == NULL) { ERROR ("plugin_register_complex_read: malloc failed."); - return (-1); + return (ENOMEM); } memset (rf, 0, sizeof (read_func_t)); @@ -835,7 +844,11 @@ int plugin_register_complex_read (const char *group, const char *name, rf->rf_udata = *user_data; } - return (plugin_insert_read (rf)); + status = plugin_insert_read (rf); + if (status != 0) + sfree (rf); + + return (status); } /* int plugin_register_complex_read */ int plugin_register_write (const char *name, @@ -852,6 +865,13 @@ int plugin_register_flush (const char *name, (void *) callback, ud)); } /* int plugin_register_flush */ +int plugin_register_missing (const char *name, + plugin_missing_cb callback, user_data_t *ud) +{ + return (create_register_callback (&list_missing, name, + (void *) callback, ud)); +} /* int plugin_register_missing */ + int plugin_register_shutdown (const char *name, int (*callback) (void)) { @@ -1038,6 +1058,11 @@ int plugin_unregister_flush (const char *name) return (plugin_unregister (list_flush, name)); } +int plugin_unregister_missing (const char *name) +{ + return (plugin_unregister (list_missing, name)); +} + int plugin_unregister_shutdown (const char *name) { return (plugin_unregister (list_shutdown, name)); @@ -1261,7 +1286,7 @@ int plugin_write (const char *plugin, /* {{{ */ return (status); } /* }}} int plugin_write */ -int plugin_flush (const char *plugin, int timeout, const char *identifier) +int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier) { llentry_t *le; @@ -1306,7 +1331,8 @@ void plugin_shutdown_all (void) destroy_read_heap (); - plugin_flush (/* plugin = */ NULL, /* timeout = */ -1, + plugin_flush (/* plugin = */ NULL, + /* timeout = */ 0, /* identifier = */ NULL); le = NULL; @@ -1336,6 +1362,7 @@ void plugin_shutdown_all (void) * the real free function when registering the write callback. This way * the data isn't freed twice. */ destroy_all_callbacks (&list_flush); + destroy_all_callbacks (&list_missing); destroy_all_callbacks (&list_write); destroy_all_callbacks (&list_notification); @@ -1343,6 +1370,44 @@ void plugin_shutdown_all (void) destroy_all_callbacks (&list_log); } /* void plugin_shutdown_all */ +int plugin_dispatch_missing (const value_list_t *vl) /* {{{ */ +{ + llentry_t *le; + + if (list_missing == NULL) + return (0); + + le = llist_head (list_missing); + while (le != NULL) + { + callback_func_t *cf; + plugin_missing_cb callback; + int status; + + cf = le->value; + callback = cf->cf_callback; + + status = (*callback) (vl, &cf->cf_udata); + if (status != 0) + { + if (status < 0) + { + ERROR ("plugin_dispatch_missing: Callback function \"%s\" " + "failed with status %i.", + le->key, status); + return (status); + } + else + { + return (0); + } + } + + le = le->next; + } + return (0); +} /* int }}} plugin_dispatch_missing */ + int plugin_dispatch_values (value_list_t *vl) { int status; @@ -1395,16 +1460,17 @@ int plugin_dispatch_values (value_list_t *vl) } if (vl->time == 0) - vl->time = time (NULL); + vl->time = cdtime (); if (vl->interval <= 0) vl->interval = interval_g; - DEBUG ("plugin_dispatch_values: time = %u; interval = %i; " + DEBUG ("plugin_dispatch_values: time = %.3f; interval = %.3f; " "host = %s; " "plugin = %s; plugin_instance = %s; " "type = %s; type_instance = %s;", - (unsigned int) vl->time, vl->interval, + CDTIME_T_TO_DOUBLE (vl->time), + CDTIME_T_TO_DOUBLE (vl->interval), vl->host, vl->plugin, vl->plugin_instance, vl->type, vl->type_instance); @@ -1488,9 +1554,6 @@ int plugin_dispatch_values (value_list_t *vl) /* Update the value cache */ uc_update (ds, vl); - /* Initiate threshold checking */ - ut_check_threshold (ds, vl); - if (post_cache_chain != NULL) { status = fc_process_chain (ds, vl, post_cache_chain); @@ -1575,9 +1638,9 @@ int plugin_dispatch_notification (const notification_t *notif) /* Possible TODO: Add flap detection here */ DEBUG ("plugin_dispatch_notification: severity = %i; message = %s; " - "time = %u; host = %s;", + "time = %.3f; host = %s;", notif->severity, notif->message, - (unsigned int) notif->time, notif->host); + CDTIME_T_TO_DOUBLE (notif->time), notif->host); /* Nobody cares for notifications */ if (list_notification == NULL) @@ -1711,7 +1774,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: @@ -1765,7 +1828,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 0b34ab1d..86d40340 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -18,13 +18,14 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster * Sebastian Harl **/ #include "collectd.h" #include "configfile.h" #include "meta_data.h" +#include "utils_time.h" #define PLUGIN_FLAGS_GLOBAL 0x0001 @@ -85,8 +86,8 @@ struct value_list_s { value_t *values; int values_len; - time_t time; - int interval; + cdtime_t time; + cdtime_t interval; char host[DATA_MAX_NAME_LEN]; char plugin[DATA_MAX_NAME_LEN]; char plugin_instance[DATA_MAX_NAME_LEN]; @@ -135,7 +136,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; @@ -143,7 +144,7 @@ typedef struct notification_meta_s typedef struct notification_s { int severity; - time_t time; + cdtime_t time; char message[NOTIF_MAX_MSG_LEN]; char host[DATA_MAX_NAME_LEN]; char plugin[DATA_MAX_NAME_LEN]; @@ -167,8 +168,12 @@ typedef int (*plugin_init_cb) (void); typedef int (*plugin_read_cb) (user_data_t *); typedef int (*plugin_write_cb) (const data_set_t *, const value_list_t *, user_data_t *); -typedef int (*plugin_flush_cb) (int timeout, const char *identifier, +typedef int (*plugin_flush_cb) (cdtime_t timeout, const char *identifier, user_data_t *); +/* "missing" callback. Returns less than zero on failure, zero if other + * callbacks should be called, greater than zero if no more callbacks should be + * called. */ +typedef int (*plugin_missing_cb) (const value_list_t *, user_data_t *); typedef void (*plugin_log_cb) (int severity, const char *message, user_data_t *); typedef int (*plugin_shutdown_cb) (void); @@ -248,7 +253,7 @@ void plugin_shutdown_all (void); int plugin_write (const char *plugin, const data_set_t *ds, const value_list_t *vl); -int plugin_flush (const char *plugin, int timeout, const char *identifier); +int plugin_flush (const char *plugin, cdtime_t timeout, const char *identifier); /* * The `plugin_register_*' functions are used to make `config', `init', @@ -264,6 +269,8 @@ int plugin_register_init (const char *name, plugin_init_cb callback); int plugin_register_read (const char *name, int (*callback) (void)); +/* "user_data" will be freed automatically, unless + * "plugin_register_complex_read" returns an error (non-zero). */ int plugin_register_complex_read (const char *group, const char *name, plugin_read_cb callback, const struct timespec *interval, @@ -272,6 +279,8 @@ int plugin_register_write (const char *name, plugin_write_cb callback, user_data_t *user_data); int plugin_register_flush (const char *name, plugin_flush_cb callback, user_data_t *user_data); +int plugin_register_missing (const char *name, + plugin_missing_cb callback, user_data_t *user_data); int plugin_register_shutdown (const char *name, plugin_shutdown_cb callback); int plugin_register_data_set (const data_set_t *ds); @@ -287,6 +296,7 @@ int plugin_unregister_read (const char *name); int plugin_unregister_read_group (const char *group); int plugin_unregister_write (const char *name); int plugin_unregister_flush (const char *name); +int plugin_unregister_missing (const char *name); int plugin_unregister_shutdown (const char *name); int plugin_unregister_data_set (const char *name); int plugin_unregister_log (const char *name); @@ -309,6 +319,7 @@ int plugin_unregister_notification (const char *name); */ int plugin_dispatch_values (value_list_t *vl); int plugin_dispatch_values_secure (const value_list_t *vl); +int plugin_dispatch_missing (const value_list_t *vl); int plugin_dispatch_notification (const notification_t *notif); @@ -341,7 +352,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..a8812e21 100644 --- a/src/postgresql.c +++ b/src/postgresql.c @@ -117,7 +117,7 @@ typedef struct { udb_query_t **queries; size_t queries_num; - int interval; + cdtime_t interval; char *host; char *port; @@ -335,8 +335,9 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db, params[i] = db->user; break; case C_PSQL_PARAM_INTERVAL: - ssnprintf (interval, sizeof (interval), "%i", - db->interval > 0 ? db->interval : interval_g); + ssnprintf (interval, sizeof (interval), "%.3f", + (db->interval > 0) + ? CDTIME_T_TO_DOUBLE (db->interval) : interval_g); params[i] = interval; break; default: @@ -535,28 +536,6 @@ static int config_set_s (char *name, char **var, const oconfig_item_t *ci) return 0; } /* config_set_s */ -static int config_set_i (char *name, int *var, - const oconfig_item_t *ci, int min) -{ - int value; - - if ((0 != ci->children_num) || (1 != ci->values_num) - || (OCONFIG_TYPE_NUMBER != ci->values[0].type)) { - log_err ("%s expects a single number argument.", name); - return 1; - } - - value = (int)ci->values[0].value.number; - - if (value < min) { - log_err ("%s expects a number greater or equal to %i.", name, min); - return 1; - } - - *var = value; - return 0; -} /* config_set_s */ - static int config_query_param_add (udb_query_t *q, oconfig_item_t *ci) { c_psql_user_data_t *data; @@ -618,7 +597,7 @@ static int c_psql_config_database (oconfig_item_t *ci) c_psql_database_t *db; char cb_name[DATA_MAX_NAME_LEN]; - struct timespec cb_interval; + struct timespec cb_interval = { 0, 0 }; user_data_t ud; int i; @@ -656,7 +635,7 @@ static int c_psql_config_database (oconfig_item_t *ci) udb_query_pick_from_list (c, queries, queries_num, &db->queries, &db->queries_num); else if (0 == strcasecmp (c->key, "Interval")) - config_set_i ("Interval", &db->interval, c, /* min = */ 1); + cf_util_get_cdtime (c, &db->interval); else log_warn ("Ignoring unknown config key \"%s\".", c->key); } @@ -701,12 +680,11 @@ static int c_psql_config_database (oconfig_item_t *ci) ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->database); - memset (&cb_interval, 0, sizeof (cb_interval)); - if (db->interval > 0) - cb_interval.tv_sec = (time_t)db->interval; + CDTIME_T_TO_TIMESPEC (db->interval, &cb_interval); plugin_register_complex_read ("postgresql", cb_name, c_psql_read, - /* interval = */ &cb_interval, &ud); + /* interval = */ (db->interval > 0) ? &cb_interval : NULL, + &ud); return 0; } /* c_psql_config_database */ @@ -737,8 +715,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/powerdns.c b/src/powerdns.c index 29f6bca5..a1b23555 100644 --- a/src/powerdns.c +++ b/src/powerdns.c @@ -321,6 +321,9 @@ static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */ struct sockaddr_un sa_unix; + struct timeval stv_timeout; + cdtime_t cdt_timeout; + sd = socket (PF_UNIX, item->socktype, 0); if (sd < 0) { @@ -361,12 +364,13 @@ static int powerdns_get_data_dgram (list_item_t *item, /* {{{ */ break; } - struct timeval timeout; - timeout.tv_sec=2; - if (timeout.tv_sec < interval_g * 3 / 4) - timeout.tv_sec = interval_g * 3 / 4; - timeout.tv_usec=0; - status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout)); + cdt_timeout = interval_g * 3 / 4; + if (cdt_timeout < TIME_T_TO_CDTIME_T (2)) + cdt_timeout = TIME_T_TO_CDTIME_T (2); + + CDTIME_T_TO_TIMEVAL (cdt_timeout, &stv_timeout); + + status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &stv_timeout, sizeof (stv_timeout)); if (status != 0) { FUNC_ERROR ("setsockopt"); diff --git a/src/processes.c b/src/processes.c index 5e448cf9..8a1436e1 100644 --- a/src/processes.c +++ b/src/processes.c @@ -1,7 +1,7 @@ /** * collectd - src/processes.c * Copyright (C) 2005 Lyonel Vincent - * Copyright (C) 2006-2008 Florian octo Forster + * Copyright (C) 2006-2010 Florian octo Forster * Copyright (C) 2008 Oleg King * Copyright (C) 2009 Sebastian Harl * Copyright (C) 2009 Andrés J. Díaz @@ -136,13 +136,13 @@ typedef struct procstat_entry_s unsigned long vmem_minflt; unsigned long vmem_majflt; - unsigned long vmem_minflt_counter; - unsigned long vmem_majflt_counter; + derive_t vmem_minflt_counter; + derive_t vmem_majflt_counter; unsigned long cpu_user; unsigned long cpu_system; - unsigned long cpu_user_counter; - unsigned long cpu_system_counter; + derive_t cpu_user_counter; + derive_t cpu_system_counter; /* io data */ derive_t io_rchar; @@ -169,11 +169,11 @@ typedef struct procstat unsigned long vmem_code; unsigned long stack_size; - unsigned long vmem_minflt_counter; - unsigned long vmem_majflt_counter; + derive_t vmem_minflt_counter; + derive_t vmem_majflt_counter; - unsigned long cpu_user_counter; - unsigned long cpu_system_counter; + derive_t cpu_user_counter; + derive_t cpu_system_counter; /* io data */ derive_t io_rchar; @@ -676,8 +676,8 @@ static void ps_submit_proc_list (procstat_t *ps) plugin_dispatch_values (&vl); sstrncpy (vl.type, "ps_cputime", sizeof (vl.type)); - vl.values[0].counter = ps->cpu_user_counter; - vl.values[1].counter = ps->cpu_system_counter; + vl.values[0].derive = ps->cpu_user_counter; + vl.values[1].derive = ps->cpu_system_counter; vl.values_len = 2; plugin_dispatch_values (&vl); @@ -688,8 +688,8 @@ static void ps_submit_proc_list (procstat_t *ps) plugin_dispatch_values (&vl); sstrncpy (vl.type, "ps_pagefaults", sizeof (vl.type)); - vl.values[0].counter = ps->vmem_minflt_counter; - vl.values[1].counter = ps->vmem_majflt_counter; + vl.values[0].derive = ps->vmem_minflt_counter; + vl.values[1].derive = ps->vmem_majflt_counter; vl.values_len = 2; plugin_dispatch_values (&vl); @@ -714,8 +714,8 @@ static void ps_submit_proc_list (procstat_t *ps) DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; " "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; " "vmem_code = %lu; " - "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; " - "cpu_user_counter = %lu; cpu_system_counter = %lu; " + "vmem_minflt_counter = %"PRIi64"; vmem_majflt_counter = %"PRIi64"; " + "cpu_user_counter = %"PRIi64"; cpu_system_counter = %"PRIi64"; " "io_rchar = %"PRIi64"; io_wchar = %"PRIi64"; " "io_syscr = %"PRIi64"; io_syscw = %"PRIi64";", ps->name, ps->num_proc, ps->num_lwp, @@ -888,8 +888,8 @@ int ps_read_process (int pid, procstat_t *ps, char *state) size_t name_end_pos; size_t name_len; - long long unsigned cpu_user_counter; - long long unsigned cpu_system_counter; + derive_t cpu_user_counter; + derive_t cpu_system_counter; long long unsigned vmem_size; long long unsigned vmem_rss; long long unsigned stack_size; @@ -1001,8 +1001,8 @@ int ps_read_process (int pid, procstat_t *ps, char *state) DEBUG("ps_read_process: did not get vmem data for pid %i",pid); } - ps->cpu_user_counter = (unsigned long) cpu_user_counter; - ps->cpu_system_counter = (unsigned long) cpu_system_counter; + ps->cpu_user_counter = cpu_user_counter; + ps->cpu_system_counter = cpu_system_counter; ps->vmem_size = (unsigned long) vmem_size; ps->vmem_rss = (unsigned long) vmem_rss; ps->stack_size = (unsigned long) stack_size; diff --git a/src/protocols.c b/src/protocols.c index e90c1a4e..0dfba210 100644 --- a/src/protocols.c +++ b/src/protocols.c @@ -1,6 +1,6 @@ /** * collectd - src/protocols.c - * 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 @@ -51,14 +51,10 @@ static void submit (const char *protocol_name, { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; + int status; - char *tmp_ptr; - - errno = 0; - tmp_ptr = NULL; - values[0].counter = (counter_t) strtoll (str_value, &tmp_ptr, - /* base = */ 0); - if ((errno != 0) || (tmp_ptr == str_value)) + status = parse_value (str_value, values, DS_TYPE_DERIVE); + if (status != 0) { ERROR ("protocols plugin: Parsing string as integer failed: %s", str_value); diff --git a/src/python.c b/src/python.c index 22f3db64..0fb49d4c 100644 --- a/src/python.c +++ b/src/python.c @@ -433,8 +433,8 @@ static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_li sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance)); sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin)); sstrncpy(v->data.plugin_instance, value_list->plugin_instance, sizeof(v->data.plugin_instance)); - v->data.time = value_list->time; - v->interval = value_list->interval; + v->data.time = CDTIME_T_TO_DOUBLE(value_list->time); + v->interval = CDTIME_T_TO_DOUBLE(value_list->interval); Py_CLEAR(v->values); v->values = list; Py_CLEAR(v->meta); @@ -463,7 +463,7 @@ static int cpy_notification_callback(const notification_t *notification, user_da sstrncpy(n->data.type_instance, notification->type_instance, sizeof(n->data.type_instance)); sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin)); sstrncpy(n->data.plugin_instance, notification->plugin_instance, sizeof(n->data.plugin_instance)); - n->data.time = notification->time; + n->data.time = CDTIME_T_TO_DOUBLE(notification->time); sstrncpy(n->message, notification->message, sizeof(n->message)); n->severity = notification->severity; ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */ diff --git a/src/pyvalues.c b/src/pyvalues.c index fd078822..307af175 100644 --- a/src/pyvalues.c +++ b/src/pyvalues.c @@ -99,7 +99,7 @@ static PyObject *cpy_common_repr(PyObject *s) { if (self->time != 0) { CPY_STRCAT(&ret, l_time); - tmp = PyInt_FromLong(self->time); + tmp = PyFloat_FromDouble(self->time); CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp); CPY_STRCAT_AND_DEL(&ret, tmp); } @@ -362,14 +362,13 @@ static PyObject *Values_new(PyTypeObject *type, PyObject *args, PyObject *kwds) static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) { Values *self = (Values *) s; - int interval = 0; - double time = 0; + double interval = 0, time = 0; PyObject *values = NULL, *meta = NULL, *tmp; char *type = NULL, *plugin_instance = NULL, *type_instance = NULL, *plugin = NULL, *host = NULL; static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance", "plugin", "host", "time", "interval", "meta", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdiO", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time, &interval, &meta)) return -1; @@ -504,13 +503,12 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) { value_t *value; value_list_t value_list = VALUE_LIST_INIT; PyObject *values = self->values, *meta = self->meta; - double time = self->data.time; - int interval = self->interval; + double time = self->data.time, interval = self->interval; char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL; static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance", "plugin", "host", "time", "interval", "meta", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetdiO", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etOetetetetddO", kwlist, NULL, &type, &values, NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time, &interval, &meta)) return NULL; @@ -589,8 +587,8 @@ static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) { value_list.values = value; value_list.meta = cpy_build_meta(meta); value_list.values_len = size; - value_list.time = time; - value_list.interval = interval; + value_list.time = DOUBLE_TO_CDTIME_T(time); + value_list.interval = DOUBLE_TO_CDTIME_T(interval); if (value_list.host[0] == 0) sstrncpy(value_list.host, hostname_g, sizeof(value_list.host)); if (value_list.plugin[0] == 0) @@ -614,8 +612,7 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) { value_t *value; value_list_t value_list = VALUE_LIST_INIT; PyObject *values = self->values, *meta = self->meta; - double time = self->data.time; - int interval = self->interval; + double time = self->data.time, interval = self->interval; char *host = NULL, *plugin = NULL, *plugin_instance = NULL, *type = NULL, *type_instance = NULL, *dest = NULL; static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance", @@ -693,8 +690,8 @@ static PyObject *Values_write(Values *self, PyObject *args, PyObject *kwds) { } value_list.values = value; value_list.values_len = size; - value_list.time = time; - value_list.interval = interval; + value_list.time = DOUBLE_TO_CDTIME_T(time); + value_list.interval = DOUBLE_TO_CDTIME_T(interval); value_list.meta = cpy_build_meta(meta);; if (value_list.host[0] == 0) sstrncpy(value_list.host, hostname_g, sizeof(value_list.host)); @@ -732,7 +729,7 @@ static PyObject *Values_repr(PyObject *s) { ret = cpy_common_repr(s); if (self->interval != 0) { CPY_STRCAT(&ret, l_interval); - tmp = PyInt_FromLong(self->interval); + tmp = PyFloat_FromDouble(self->interval); CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp); CPY_STRCAT_AND_DEL(&ret, tmp); } @@ -885,7 +882,7 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj NULL, &plugin, NULL, &host, &t, &severity)) return NULL; - notification.time = t; + notification.time = DOUBLE_TO_CDTIME_T(t); notification.severity = severity; sstrncpy(notification.message, message ? message : self->message, sizeof(notification.message)); sstrncpy(notification.host, host ? host : self->data.host, sizeof(notification.host)); @@ -907,8 +904,8 @@ static PyObject *Notification_dispatch(Notification *self, PyObject *args, PyObj return NULL; } - if (notification.time < 1) - notification.time = time(0); + if (notification.time == 0) + notification.time = cdtime(); if (notification.host[0] == 0) sstrncpy(notification.host, hostname_g, sizeof(notification.host)); if (notification.plugin[0] == 0) diff --git a/src/redis.c b/src/redis.c new file mode 100644 index 00000000..86062d9c --- /dev/null +++ b/src/redis.c @@ -0,0 +1,315 @@ +/** + * 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 + +#ifndef HOST_NAME_MAX +# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif + +#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_d (char *plugin_instance, + const char *type, const char *type_instance, + derive_t value) /* {{{ */ +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].derive = 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_d (rn->name, "total_connections", NULL, info.total_connections_received); + redis_submit_d (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/routeros.c b/src/routeros.c index ff8789ed..d61ffe98 100644 --- a/src/routeros.c +++ b/src/routeros.c @@ -1,6 +1,6 @@ /** * collectd - src/routeros.c - * 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 @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster **/ #include "collectd.h" @@ -44,13 +44,13 @@ struct cr_data_s typedef struct cr_data_s cr_data_t; static void cr_submit_io (cr_data_t *rd, const char *type, /* {{{ */ - const char *type_instance, counter_t rx, counter_t tx) + const char *type_instance, derive_t rx, derive_t tx) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; vl.values = values; vl.values_len = STATIC_ARRAY_SIZE (values); @@ -75,13 +75,13 @@ static void submit_interface (cr_data_t *rd, /* {{{ */ } cr_submit_io (rd, "if_packets", i->name, - (counter_t) i->rx_packets, (counter_t) i->tx_packets); + (derive_t) i->rx_packets, (derive_t) i->tx_packets); cr_submit_io (rd, "if_octets", i->name, - (counter_t) i->rx_bytes, (counter_t) i->tx_bytes); + (derive_t) i->rx_bytes, (derive_t) i->tx_bytes); cr_submit_io (rd, "if_errors", i->name, - (counter_t) i->rx_errors, (counter_t) i->tx_errors); + (derive_t) i->rx_errors, (derive_t) i->tx_errors); cr_submit_io (rd, "if_dropped", i->name, - (counter_t) i->rx_drops, (counter_t) i->tx_drops); + (derive_t) i->rx_drops, (derive_t) i->tx_drops); submit_interface (rd, i->next); } /* }}} void submit_interface */ @@ -116,12 +116,12 @@ static void cr_submit_gauge (cr_data_t *rd, const char *type, /* {{{ */ #if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) static void cr_submit_counter (cr_data_t *rd, const char *type, /* {{{ */ - const char *type_instance, counter_t value) + const char *type_instance, derive_t value) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = value; + values[0].derive = value; vl.values = values; vl.values_len = STATIC_ARRAY_SIZE (values); @@ -166,7 +166,7 @@ static void submit_regtable (cr_data_t *rd, /* {{{ */ ssnprintf (type_instance, sizeof (type_instance), "%s-%s", r->interface, r->radio_name); cr_submit_io (rd, "if_octets", type_instance, - (counter_t) r->rx_bytes, (counter_t) r->tx_bytes); + (derive_t) r->rx_bytes, (derive_t) r->tx_bytes); cr_submit_gauge (rd, "snr", type_instance, (gauge_t) r->signal_to_noise); submit_regtable (rd, r->next); @@ -212,7 +212,7 @@ static int handle_system_resource (__attribute__((unused)) ros_connection_t *c, if (rd->collect_disk) { - cr_submit_counter (rd, "counter", "secors_written", (counter_t) r->write_sect_total); + cr_submit_counter (rd, "counter", "secors_written", (derive_t) r->write_sect_total); cr_submit_gauge (rd, "gauge", "bad_blocks", (gauge_t) r->bad_blocks); } diff --git a/src/rrdcached.c b/src/rrdcached.c index 34e860ba..fb7eb79e 100644 --- a/src/rrdcached.c +++ b/src/rrdcached.c @@ -64,12 +64,14 @@ static int value_list_to_string (char *buffer, int buffer_len, int offset; int status; int i; + time_t t; assert (0 == strcmp (ds->type, vl->type)); memset (buffer, '\0', buffer_len); - status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time); + t = CDTIME_T_TO_TIME_T (vl->time); + status = ssnprintf (buffer, buffer_len, "%lu", (unsigned long) t); if ((status < 1) || (status >= buffer_len)) return (-1); offset = status; diff --git a/src/rrdtool.c b/src/rrdtool.c index 2b384d09..e5f964e5 100644 --- a/src/rrdtool.c +++ b/src/rrdtool.c @@ -40,11 +40,11 @@ */ struct rrd_cache_s { - int values_num; - char **values; - time_t first_value; - time_t last_value; - int random_variation; + int values_num; + char **values; + cdtime_t first_value; + cdtime_t last_value; + int64_t random_variation; enum { FLAG_NONE = 0x00, @@ -107,10 +107,10 @@ static rrdcreate_config_t rrdcreate_config = /* XXX: If you need to lock both, cache_lock and queue_lock, at the same time, * ALWAYS lock `cache_lock' first! */ -static int cache_timeout = 0; -static int cache_flush_timeout = 0; -static int random_timeout = 1; -static time_t cache_flush_last; +static cdtime_t cache_timeout = 0; +static cdtime_t cache_flush_timeout = 0; +static cdtime_t random_timeout = TIME_T_TO_CDTIME_T (1); +static cdtime_t cache_flush_last; static c_avl_tree_t *cache = NULL; static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER; @@ -199,11 +199,13 @@ static int value_list_to_string (char *buffer, int buffer_len, { int offset; int status; + time_t tt; int i; memset (buffer, '\0', buffer_len); - status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) vl->time); + tt = CDTIME_T_TO_TIME_T (vl->time); + status = ssnprintf (buffer, buffer_len, "%u", (unsigned int) tt); if ((status < 1) || (status >= buffer_len)) return (-1); offset = status; @@ -303,7 +305,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 +344,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! */ @@ -510,10 +512,11 @@ static int rrd_queue_dequeue (const char *filename, return (0); } /* int rrd_queue_dequeue */ -static void rrd_cache_flush (int timeout) +/* XXX: You must hold "cache_lock" when calling this function! */ +static void rrd_cache_flush (cdtime_t timeout) { rrd_cache_t *rc; - time_t now; + cdtime_t now; char **keys = NULL; int keys_num = 0; @@ -522,9 +525,11 @@ static void rrd_cache_flush (int timeout) c_avl_iterator_t *iter; int i; - DEBUG ("rrdtool plugin: Flushing cache, timeout = %i", timeout); + DEBUG ("rrdtool plugin: Flushing cache, timeout = %.3f", + CDTIME_T_TO_DOUBLE (timeout)); - now = time (NULL); + now = cdtime (); + timeout = TIME_T_TO_CDTIME_T (timeout); /* Build a list of entries to be flushed */ iter = c_avl_get_iterator (cache); @@ -532,7 +537,9 @@ static void rrd_cache_flush (int timeout) { if (rc->flags != FLAG_NONE) continue; - else if ((now - rc->first_value) < timeout) + /* timeout == 0 => flush everything */ + else if ((timeout != 0) + && ((now - rc->first_value) < timeout)) continue; else if (rc->values_num > 0) { @@ -585,10 +592,11 @@ static void rrd_cache_flush (int timeout) cache_flush_last = now; } /* void rrd_cache_flush */ -static int rrd_cache_flush_identifier (int timeout, const char *identifier) +static int rrd_cache_flush_identifier (cdtime_t timeout, + const char *identifier) { rrd_cache_t *rc; - time_t now; + cdtime_t now; int status; char key[2048]; @@ -598,7 +606,7 @@ static int rrd_cache_flush_identifier (int timeout, const char *identifier) return (0); } - now = time (NULL); + now = cdtime (); if (datadir == NULL) snprintf (key, sizeof (key), "%s.rrd", @@ -642,8 +650,43 @@ static int rrd_cache_flush_identifier (int timeout, const char *identifier) return (status); } /* int rrd_cache_flush_identifier */ +static int64_t rrd_get_random_variation (void) +{ + double dbl_timeout; + cdtime_t ctm_timeout; + double rand_fact; + _Bool negative; + int64_t ret; + + if (random_timeout <= 0) + return (0); + + /* Assure that "cache_timeout + random_variation" is never negative. */ + if (random_timeout > cache_timeout) + { + INFO ("rrdtool plugin: Adjusting \"RandomTimeout\" to %.3f seconds.", + CDTIME_T_TO_DOUBLE (cache_timeout)); + random_timeout = cache_timeout; + } + + /* This seems a bit complicated, but "random_timeout" is likely larger than + * RAND_MAX, so we can't simply use modulo here. */ + dbl_timeout = CDTIME_T_TO_DOUBLE (random_timeout); + rand_fact = ((double) random ()) + / ((double) RAND_MAX); + negative = (_Bool) (random () % 2); + + ctm_timeout = DOUBLE_TO_CDTIME_T (dbl_timeout * rand_fact); + + ret = (int64_t) ctm_timeout; + if (negative) + ret *= -1; + + return (ret); +} /* int64_t rrd_get_random_variation */ + static int rrd_cache_insert (const char *filename, - const char *value, time_t value_time) + const char *value, cdtime_t value_time) { rrd_cache_t *rc = NULL; int new_rc = 0; @@ -664,14 +707,14 @@ static int rrd_cache_insert (const char *filename, if (rc == NULL) { - rc = (rrd_cache_t *) malloc (sizeof (rrd_cache_t)); + rc = malloc (sizeof (*rc)); if (rc == NULL) return (-1); rc->values_num = 0; rc->values = NULL; rc->first_value = 0; rc->last_value = 0; - rc->random_variation = 0; + rc->random_variation = rrd_get_random_variation (); rc->flags = FLAG_NONE; new_rc = 1; } @@ -679,9 +722,9 @@ static int rrd_cache_insert (const char *filename, if (rc->last_value >= value_time) { pthread_mutex_unlock (&cache_lock); - DEBUG ("rrdtool plugin: (rc->last_value = %u) >= (value_time = %u)", - (unsigned int) rc->last_value, - (unsigned int) value_time); + DEBUG ("rrdtool plugin: (rc->last_value = %"PRIu64") " + ">= (value_time = %"PRIu64")", + rc->last_value, value_time); return (-1); } @@ -738,11 +781,11 @@ static int rrd_cache_insert (const char *filename, } DEBUG ("rrdtool plugin: rrd_cache_insert: file = %s; " - "values_num = %i; age = %lu;", + "values_num = %i; age = %.3f;", filename, rc->values_num, - (unsigned long)(rc->last_value - rc->first_value)); + CDTIME_T_TO_DOUBLE (rc->last_value - rc->first_value)); - if ((rc->last_value + rc->random_variation - rc->first_value) >= cache_timeout) + if ((rc->last_value - rc->first_value) >= (cache_timeout + rc->random_variation)) { /* XXX: If you need to lock both, cache_lock and queue_lock, at * the same time, ALWAYS lock `cache_lock' first! */ @@ -754,17 +797,7 @@ static int rrd_cache_insert (const char *filename, if (status == 0) rc->flags = FLAG_QUEUED; - /* Update the jitter value. Negative values are - * slightly preferred. */ - if (random_timeout > 0) - { - rc->random_variation = (rand () % (2 * random_timeout)) - - random_timeout; - } - else - { - rc->random_variation = 0; - } + rc->random_variation = rrd_get_random_variation (); } else { @@ -773,7 +806,7 @@ static int rrd_cache_insert (const char *filename, } if ((cache_timeout > 0) && - ((time (NULL) - cache_flush_last) > cache_flush_timeout)) + ((cdtime () - cache_flush_last) > cache_flush_timeout)) rrd_cache_flush (cache_flush_timeout); pthread_mutex_unlock (&cache_lock); @@ -899,8 +932,8 @@ static int rrd_write (const data_set_t *ds, const value_list_t *vl, return (status); } /* int rrd_write */ -static int rrd_flush (int timeout, const char *identifier, - user_data_t __attribute__((unused)) *user_data) +static int rrd_flush (cdtime_t timeout, const char *identifier, + __attribute__((unused)) user_data_t *user_data) { pthread_mutex_lock (&cache_lock); @@ -919,7 +952,7 @@ static int rrd_config (const char *key, const char *value) { if (strcasecmp ("CacheTimeout", key) == 0) { - int tmp = atoi (value); + double tmp = atof (value); if (tmp < 0) { fprintf (stderr, "rrdtool: `CacheTimeout' must " @@ -928,7 +961,7 @@ static int rrd_config (const char *key, const char *value) "be greater than 0.\n"); return (1); } - cache_timeout = tmp; + cache_timeout = DOUBLE_TO_CDTIME_T (tmp); } else if (strcasecmp ("CacheFlush", key) == 0) { @@ -965,7 +998,7 @@ static int rrd_config (const char *key, const char *value) } else if (strcasecmp ("StepSize", key) == 0) { - int temp = atoi (value); + unsigned long temp = strtoul (value, NULL, 0); if (temp > 0) rrdcreate_config.stepsize = temp; } @@ -1061,10 +1094,10 @@ static int rrd_config (const char *key, const char *value) } else if (strcasecmp ("RandomTimeout", key) == 0) { - int tmp; + double tmp; - tmp = atoi (value); - if (tmp < 0) + tmp = atof (value); + if (tmp < 0.0) { fprintf (stderr, "rrdtool: `RandomTimeout' must " "be greater than or equal to zero.\n"); @@ -1073,7 +1106,7 @@ static int rrd_config (const char *key, const char *value) } else { - random_timeout = tmp; + random_timeout = DOUBLE_TO_CDTIME_T (tmp); } } else @@ -1086,7 +1119,7 @@ static int rrd_config (const char *key, const char *value) static int rrd_shutdown (void) { pthread_mutex_lock (&cache_lock); - rrd_cache_flush (-1); + rrd_cache_flush (0); pthread_mutex_unlock (&cache_lock); pthread_mutex_lock (&queue_lock); @@ -1128,18 +1161,16 @@ static int rrd_init (void) return (0); init_once = 1; - if (rrdcreate_config.stepsize < 0) - rrdcreate_config.stepsize = 0; if (rrdcreate_config.heartbeat <= 0) rrdcreate_config.heartbeat = 2 * rrdcreate_config.stepsize; if ((rrdcreate_config.heartbeat > 0) - && (rrdcreate_config.heartbeat < interval_g)) + && (rrdcreate_config.heartbeat < CDTIME_T_TO_TIME_T (interval_g))) WARNING ("rrdtool plugin: Your `heartbeat' is " "smaller than your `interval'. This will " "likely cause problems."); else if ((rrdcreate_config.stepsize > 0) - && (rrdcreate_config.stepsize < interval_g)) + && (rrdcreate_config.stepsize < CDTIME_T_TO_TIME_T (interval_g))) WARNING ("rrdtool plugin: Your `stepsize' is " "smaller than your `interval'. This will " "create needlessly big RRD-files."); @@ -1154,10 +1185,9 @@ static int rrd_init (void) return (-1); } - cache_flush_last = time (NULL); - if (cache_timeout < 2) + cache_flush_last = cdtime (); + if (cache_timeout == 0) { - cache_timeout = 0; cache_flush_timeout = 0; } else if (cache_flush_timeout < cache_timeout) @@ -1174,7 +1204,7 @@ static int rrd_init (void) } queue_thread_running = 1; - DEBUG ("rrdtool plugin: rrd_init: datadir = %s; stepsize = %i;" + DEBUG ("rrdtool plugin: rrd_init: datadir = %s; stepsize = %lu;" " heartbeat = %i; rrarows = %i; xff = %lf;", (datadir == NULL) ? "(null)" : datadir, rrdcreate_config.stepsize, diff --git a/src/serial.c b/src/serial.c index cfa26bbd..9bd885db 100644 --- a/src/serial.c +++ b/src/serial.c @@ -30,13 +30,13 @@ #endif static void serial_submit (const char *type_instance, - counter_t rx, counter_t tx) + derive_t rx, derive_t tx) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; vl.values = values; vl.values_len = 2; @@ -54,8 +54,8 @@ static int serial_read (void) FILE *fh; char buffer[1024]; - counter_t rx = 0; - counter_t tx = 0; + derive_t rx = 0; + derive_t tx = 0; char *fields[16]; int i, numfields; diff --git a/src/snmp.c b/src/snmp.c index d440f7fe..b8bbee44 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -69,7 +69,7 @@ struct host_definition_s int version; void *sess_handle; c_complain_t complaint; - uint32_t interval; + cdtime_t interval; data_definition_t **data_list; int data_list_len; }; @@ -207,7 +207,6 @@ static void csnmp_host_definition_destroy (void *arg) /* {{{ */ * +-> csnmp_config_add_host_community * +-> csnmp_config_add_host_version * +-> csnmp_config_add_host_collect - * +-> csnmp_config_add_host_interval */ static void call_snmp_init_once (void) { @@ -591,22 +590,6 @@ static int csnmp_config_add_host_collect (host_definition_t *host, return (0); } /* int csnmp_config_add_host_collect */ -static int csnmp_config_add_host_interval (host_definition_t *hd, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) - { - WARNING ("snmp plugin: The `Interval' config option needs exactly one number argument."); - return (-1); - } - - hd->interval = ci->values[0].value.number >= 0 - ? (uint32_t) ci->values[0].value.number - : 0; - - return (0); -} /* int csnmp_config_add_host_interval */ - static int csnmp_config_add_host (oconfig_item_t *ci) { host_definition_t *hd; @@ -655,7 +638,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci) else if (strcasecmp ("Collect", option->key) == 0) csnmp_config_add_host_collect (hd, option); else if (strcasecmp ("Interval", option->key) == 0) - csnmp_config_add_host_interval (hd, option); + cf_util_get_cdtime (option, &hd->interval); else { WARNING ("snmp plugin: csnmp_config_add_host: Option `%s' not allowed here.", option->key); @@ -699,9 +682,7 @@ static int csnmp_config_add_host (oconfig_item_t *ci) cb_data.data = hd; cb_data.free_func = csnmp_host_definition_destroy; - memset (&cb_interval, 0, sizeof (cb_interval)); - if (hd->interval != 0) - cb_interval.tv_sec = (time_t) hd->interval; + CDTIME_T_TO_TIMESPEC (hd->interval, &cb_interval); status = plugin_register_complex_read (/* group = */ NULL, cb_name, csnmp_read_host, /* interval = */ &cb_interval, @@ -1648,8 +1629,8 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data) static int csnmp_read_host (user_data_t *ud) { host_definition_t *host; - time_t time_start; - time_t time_end; + cdtime_t time_start; + cdtime_t time_end; int status; int success; int i; @@ -1659,9 +1640,7 @@ static int csnmp_read_host (user_data_t *ud) if (host->interval == 0) host->interval = interval_g; - time_start = time (NULL); - DEBUG ("snmp plugin: csnmp_read_host (%s) started at %u;", host->name, - (unsigned int) time_start); + time_start = cdtime (); if (host->sess_handle == NULL) csnmp_host_open_session (host); @@ -1683,14 +1662,14 @@ static int csnmp_read_host (user_data_t *ud) success++; } - time_end = time (NULL); - DEBUG ("snmp plugin: csnmp_read_host (%s) finished at %u;", host->name, - (unsigned int) time_end); - if ((uint32_t) (time_end - time_start) > host->interval) + time_end = cdtime (); + if ((time_end - time_start) > host->interval) { - WARNING ("snmp plugin: Host `%s' should be queried every %"PRIu32 - " seconds, but reading all values takes %u seconds.", - host->name, host->interval, (unsigned int) (time_end - time_start)); + WARNING ("snmp plugin: Host `%s' should be queried every %.3f " + "seconds, but reading all values takes %.3f seconds.", + host->name, + CDTIME_T_TO_DOUBLE (host->interval), + CDTIME_T_TO_DOUBLE (time_end - time_start)); } if (success == 0) diff --git a/src/swap.c b/src/swap.c index 46ba6652..30daa4b1 100644 --- a/src/swap.c +++ b/src/swap.c @@ -1,8 +1,9 @@ /** * collectd - src/swap.c - * Copyright (C) 2005-2009 Florian octo Forster + * Copyright (C) 2005-2010 Florian octo Forster * Copyright (C) 2009 Stefan Völkel * Copyright (C) 2009 Manuel Sanmartin + * 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 @@ -18,8 +19,9 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster * Manuel Sanmartin + * Aurélien Reynaud **/ #if HAVE_CONFIG_H @@ -71,14 +73,17 @@ /* No global variables */ /* #endif KERNEL_LINUX */ -#elif HAVE_LIBKSTAT +#elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS static derive_t pagesize; -static kstat_t *ksp; -/* #endif HAVE_LIBKSTAT */ -#elif HAVE_SWAPCTL -/* No global variables */ -/* #endif HAVE_SWAPCTL */ +static const char *config_keys[] = +{ + "ReportByDevice" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static _Bool report_by_device = 0; +/* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */ #elif defined(VM_SWAPUSAGE) /* No global variables */ @@ -108,15 +113,9 @@ static int swap_init (void) /* No init stuff */ /* #endif KERNEL_LINUX */ -#elif HAVE_LIBKSTAT +#elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS /* getpagesize(3C) tells me this does not fail.. */ pagesize = (derive_t) getpagesize (); - if (get_kstat (&ksp, "unix", 0, "system_pages")) - ksp = NULL; -/* #endif HAVE_LIBKSTAT */ - -#elif HAVE_SWAPCTL - /* No init stuff */ /* #endif HAVE_SWAPCTL */ #elif defined(VM_SWAPUSAGE) @@ -155,7 +154,8 @@ static int swap_init (void) return (0); } -static void swap_submit (const char *type_instance, derive_t value, unsigned type) +static void swap_submit_inst (const char *plugin_instance, /* {{{ */ + const char *type_instance, derive_t value, unsigned type) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; @@ -179,14 +179,22 @@ static void swap_submit (const char *type_instance, derive_t value, unsigned typ vl.values_len = 1; sstrncpy (vl.host, hostname_g, sizeof (vl.host)); sstrncpy (vl.plugin, "swap", sizeof (vl.plugin)); + if (plugin_instance != NULL) + sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); plugin_dispatch_values (&vl); -} /* void swap_submit */ +} /* }}} void swap_submit_inst */ -static int swap_read (void) +static void swap_submit (const char *type_instance, derive_t value, unsigned type) { + swap_submit_inst (/* plugin instance = */ NULL, + type_instance, value, type); +} + #if KERNEL_LINUX +static int swap_read (void) /* {{{ */ +{ FILE *fh; char buffer[1024]; @@ -231,7 +239,7 @@ static int swap_read (void) sstrerror (errno, errbuf, sizeof (errbuf))); } - if ((swap_total == 0LL) || ((swap_free + swap_cached) > swap_total)) + if ((swap_free + swap_cached) > swap_total) return (-1); swap_used = swap_total - (swap_free + swap_cached); @@ -289,9 +297,24 @@ static int swap_read (void) swap_submit ("cached", 1024 * swap_cached, DS_TYPE_GAUGE); swap_submit ("in", swap_in, DS_TYPE_DERIVE); swap_submit ("out", swap_out, DS_TYPE_DERIVE); + + return (0); +} /* }}} int swap_read */ /* #endif KERNEL_LINUX */ -#elif HAVE_LIBKSTAT +/* + * Under Solaris, two mechanisms can be used to read swap statistics, swapctl + * and kstat. The former reads physical space used on a device, the latter + * reports the view from the virtual memory system. It was decided that the + * kstat-based information should be moved to the "vmem" plugin, but nobody + * with enough Solaris experience was available at that time to do this. The + * code below is still there for your reference but it won't be activated in + * *this* plugin again. --octo + */ +#elif 0 && HAVE_LIBKSTAT +/* kstat-based read function */ +static int swap_read_kstat (void) /* {{{ */ +{ derive_t swap_alloc; derive_t swap_resv; derive_t swap_avail; @@ -335,9 +358,168 @@ static int swap_read (void) swap_submit ("used", swap_alloc, DS_TYPE_GAUGE); swap_submit ("free", swap_avail, DS_TYPE_GAUGE); swap_submit ("reserved", swap_resv, DS_TYPE_GAUGE); -/* #endif HAVE_LIBKSTAT */ -#elif HAVE_SWAPCTL + return (0); +} /* }}} int swap_read_kstat */ +/* #endif 0 && HAVE_LIBKSTAT */ + +#elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS +/* swapctl-based read function */ +static int swap_read (void) /* {{{ */ +{ + swaptbl_t *s; + char *s_paths; + 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); + + /* Allocate and initialize the swaptbl_t structure */ + s = (swaptbl_t *) smalloc (swap_num * sizeof (swapent_t) + sizeof (struct swaptable)); + if (s == NULL) + { + ERROR ("swap plugin: smalloc failed."); + return (-1); + } + + /* Memory to store the path names. We only use these paths when the + * separate option has been configured, but it's easier to just + * allocate enough memory in any case. */ + s_paths = calloc (swap_num, PATH_MAX); + if (s_paths == NULL) + { + ERROR ("swap plugin: malloc failed."); + sfree (s); + return (-1); + } + for (i = 0; i < swap_num; i++) + s->swt_ent[i].ste_path = s_paths + (i * PATH_MAX); + s->swt_n = swap_num; + + status = swapctl (SC_LIST, s); + if (status < 0) + { + char errbuf[1024]; + ERROR ("swap plugin: swapctl (SC_LIST) failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + sfree (s_paths); + sfree (s); + return (-1); + } + else if (swap_num < status) + { + /* more elements returned than requested */ + ERROR ("swap plugin: I allocated memory for %i structure%s, " + "but swapctl(2) claims to have returned %i. " + "I'm confused and will give up.", + swap_num, (swap_num == 1) ? "" : "s", + status); + sfree (s_paths); + sfree (s); + return (-1); + } + else if (swap_num > status) + /* less elements returned than requested */ + swap_num = status; + + for (i = 0; i < swap_num; i++) + { + char path[PATH_MAX]; + derive_t this_total; + derive_t this_avail; + + if ((s->swt_ent[i].ste_flags & ST_INDEL) != 0) + continue; + + this_total = ((derive_t) s->swt_ent[i].ste_pages) * pagesize; + this_avail = ((derive_t) s->swt_ent[i].ste_free) * pagesize; + + /* Shortcut for the "combined" setting (default) */ + if (!report_by_device) + { + avail += this_avail; + total += this_total; + continue; + } + + /* Okay, using "/" as swap device would be super-weird, but + * we'll handle it anyway to cover all cases. */ + if (strcmp ("/", s->swt_ent[i].ste_path) == 0) + sstrncpy (path, "root", sizeof (path)); + else + { + int j; + + s->swt_ent[i].ste_path[PATH_MAX - 1] = 0; + /* Don't copy the leading slash */ + sstrncpy (path, &s->swt_ent[i].ste_path[1], sizeof (path)); + /* Convert slashes to dashes, just like the "df" plugin. */ + for (j = 0; path[j] != 0; j++) + if (path[j] == '/') + path[j] = '-'; + } + + swap_submit_inst (path, "used", this_total - this_avail, DS_TYPE_GAUGE); + swap_submit_inst (path, "free", this_avail, DS_TYPE_GAUGE); + } /* for (swap_num) */ + + if (total < avail) + { + ERROR ("swap plugin: Total swap space (%"PRIi64") " + "is less than free swap space (%"PRIi64").", + total, avail); + sfree (s_paths); + sfree (s); + return (-1); + } + + /* If the "separate" option was specified (report_by_device == 2), all + * values have already been dispatched from within the loop. */ + if (!report_by_device) + { + swap_submit ("used", total - avail, DS_TYPE_GAUGE); + swap_submit ("free", avail, DS_TYPE_GAUGE); + } + + sfree (s_paths); + sfree (s); + return (0); +} /* }}} int swap_read */ + +/* Configuration: Present when swapctl or both methods are available. */ +static int swap_config (const char *key, const char *value) /* {{{ */ +{ + if (strcasecmp ("ReportByDevice", key) == 0) + { + if (IS_TRUE (value)) + report_by_device = 1; + else + report_by_device = 0; + } + else + { + return (-1); + } + + return (0); +} /* }}} int swap_config */ +/* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */ + +#elif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS +static int swap_read (void) /* {{{ */ +{ struct swapent *swap_entries; int swap_num; int status; @@ -346,18 +528,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,9 +583,14 @@ static int swap_read (void) swap_submit ("free", total - used, DS_TYPE_GAUGE); sfree (swap_entries); -/* #endif HAVE_SWAPCTL */ + + return (0); +} /* }}} int swap_read */ +/* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS */ #elif defined(VM_SWAPUSAGE) +static int swap_read (void) /* {{{ */ +{ int mib[3]; size_t mib_len; struct xsw_usage sw_usage; @@ -433,9 +608,14 @@ static int swap_read (void) /* The returned values are bytes. */ swap_submit ("used", (derive_t) sw_usage.xsu_used, DS_TYPE_GAUGE); swap_submit ("free", (derive_t) sw_usage.xsu_avail, DS_TYPE_GAUGE); + + return (0); +} /* }}} int swap_read */ /* #endif VM_SWAPUSAGE */ #elif HAVE_LIBKVM_GETSWAPINFO +static int swap_read (void) /* {{{ */ +{ struct kvm_swap data_s; int status; @@ -461,9 +641,14 @@ static int swap_read (void) swap_submit ("used", used, DS_TYPE_GAUGE); swap_submit ("free", free, DS_TYPE_GAUGE); + + return (0); +} /* }}} int swap_read */ /* #endif HAVE_LIBKVM_GETSWAPINFO */ #elif HAVE_LIBSTATGRAB +static int swap_read (void) /* {{{ */ +{ sg_swap_stats *swap; swap = sg_get_swap_stats (); @@ -473,9 +658,14 @@ static int swap_read (void) swap_submit ("used", (derive_t) swap->used, DS_TYPE_GAUGE); swap_submit ("free", (derive_t) swap->free, DS_TYPE_GAUGE); + + return (0); +} /* }}} int swap_read */ /* #endif HAVE_LIBSTATGRAB */ #elif HAVE_PERFSTAT +static int swap_read (void) /* {{{ */ +{ if(perfstat_memory_total(NULL, &pmemory, sizeof(perfstat_memory_total_t), 1) < 0) { char errbuf[1024]; @@ -485,13 +675,18 @@ static int swap_read (void) } swap_submit ("used", (derive_t) (pmemory.pgsp_total - pmemory.pgsp_free) * pagesize, DS_TYPE_GAUGE); swap_submit ("free", (derive_t) pmemory.pgsp_free * pagesize , DS_TYPE_GAUGE); -#endif /* HAVE_PERFSTAT */ return (0); -} /* int swap_read */ +} /* }}} int swap_read */ +#endif /* HAVE_PERFSTAT */ void module_register (void) { +#if HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS + plugin_register_config ("swap", swap_config, config_keys, config_keys_num); +#endif plugin_register_init ("swap", swap_init); plugin_register_read ("swap", swap_read); } /* void module_register */ + +/* vim: set fdm=marker : */ diff --git a/src/tape.c b/src/tape.c index 32b42965..a8e7dc45 100644 --- a/src/tape.c +++ b/src/tape.c @@ -58,13 +58,13 @@ static int tape_init (void) static void tape_submit (const char *plugin_instance, const char *type, - counter_t read, counter_t write) + derive_t read, derive_t write) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = read; - values[1].counter = write; + values[0].derive = read; + values[1].derive = write; vl.values = values; vl.values_len = 2; diff --git a/src/target_notification.c b/src/target_notification.c index 96598afd..cb68048b 100644 --- a/src/target_notification.c +++ b/src/target_notification.c @@ -209,7 +209,7 @@ static int tn_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */ /* Initialize the structure. */ memset (&n, 0, sizeof (n)); n.severity = data->severity; - n.time = time (NULL); + n.time = cdtime (); sstrncpy (n.message, data->message, sizeof (n.message)); sstrncpy (n.host, vl->host, sizeof (n.host)); sstrncpy (n.plugin, vl->plugin, sizeof (n.plugin)); diff --git a/src/target_scale.c b/src/target_scale.c index 29fecdfd..af224f10 100644 --- a/src/target_scale.c +++ b/src/target_scale.c @@ -96,7 +96,7 @@ static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */ { difference = curr_counter - prev_counter; } - rate = ((double) difference) / ((double) vl->interval); + rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval); /* Modify the rate. */ if (!isnan (data->factor)) @@ -105,7 +105,7 @@ static int ts_invoke_counter (const data_set_t *ds, value_list_t *vl, /* {{{ */ rate += data->offset; /* Calculate the internal counter. */ - int_fraction += (rate * ((double) vl->interval)); + int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval)); difference = (uint64_t) int_fraction; int_fraction -= ((double) difference); int_counter += difference; @@ -199,7 +199,7 @@ static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */ /* Calcualte the rate */ difference = curr_derive - prev_derive; - rate = ((double) difference) / ((double) vl->interval); + rate = ((double) difference) / CDTIME_T_TO_DOUBLE (vl->interval); /* Modify the rate. */ if (!isnan (data->factor)) @@ -208,7 +208,7 @@ static int ts_invoke_derive (const data_set_t *ds, value_list_t *vl, /* {{{ */ rate += data->offset; /* Calculate the internal derive. */ - int_fraction += (rate * ((double) vl->interval)); + int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval)); if (int_fraction < 0.0) /* handle negative integer rounding correctly */ difference = ((int64_t) int_fraction) - 1; else @@ -263,7 +263,7 @@ static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */ if (status != 0) int_fraction = 0.0; - rate = ((double) curr_absolute) / ((double) vl->interval); + rate = ((double) curr_absolute) / CDTIME_T_TO_DOUBLE (vl->interval); /* Modify the rate. */ if (!isnan (data->factor)) @@ -272,7 +272,7 @@ static int ts_invoke_absolute (const data_set_t *ds, value_list_t *vl, /* {{{ */ rate += data->offset; /* Calculate the new absolute. */ - int_fraction += (rate * ((double) vl->interval)); + int_fraction += (rate * CDTIME_T_TO_DOUBLE (vl->interval)); curr_absolute = (uint64_t) int_fraction; int_fraction -= ((double) curr_absolute); diff --git a/src/target_v5upgrade.c b/src/target_v5upgrade.c new file mode 100644 index 00000000..25f4637d --- /dev/null +++ b/src/target_v5upgrade.c @@ -0,0 +1,472 @@ +/** + * 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 */ + +/* + * ZFS ARC hit and miss counters + * + * 4.* uses the flawed "arc_counts" type. In 5.* this has been replaced by the + * more generic "cache_result" type. + */ +static int v5_zfs_arc_counts (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + value_list_t new_vl; + value_t new_value; + _Bool is_hits; + + if (vl->values_len != 4) + return (FC_TARGET_STOP); + + if (strcmp ("hits", vl->type_instance) == 0) + is_hits = 1; + else if (strcmp ("misses", vl->type_instance) == 0) + is_hits = 0; + else + 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; + ssnprintf (new_vl.type_instance, sizeof (new_vl.type_instance), + "demand_data-%s", + is_hits ? "hit" : "miss"); + plugin_dispatch_values (&new_vl); + + new_vl.values[0].derive = (derive_t) vl->values[1].counter; + ssnprintf (new_vl.type_instance, sizeof (new_vl.type_instance), + "demand_metadata-%s", + is_hits ? "hit" : "miss"); + plugin_dispatch_values (&new_vl); + + new_vl.values[0].derive = (derive_t) vl->values[2].counter; + ssnprintf (new_vl.type_instance, sizeof (new_vl.type_instance), + "prefetch_data-%s", + is_hits ? "hit" : "miss"); + plugin_dispatch_values (&new_vl); + + new_vl.values[0].derive = (derive_t) vl->values[3].counter; + ssnprintf (new_vl.type_instance, sizeof (new_vl.type_instance), + "prefetch_metadata-%s", + is_hits ? "hit" : "miss"); + plugin_dispatch_values (&new_vl); + + /* Abort processing */ + return (FC_TARGET_STOP); +} /* }}} int v5_zfs_arc_counts */ + +/* + * ZFS ARC L2 bytes + * + * "arc_l2_bytes" -> "io_octets-L2". + */ +static int v5_zfs_arc_l2_bytes (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + value_list_t new_vl; + value_t new_values[2]; + + if (vl->values_len != 2) + 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_values; + new_vl.values_len = 2; + new_vl.meta = NULL; + + /* Change the type/-instance to "io_octets-L2" */ + sstrncpy (new_vl.type, "io_octets", sizeof (new_vl.type)); + sstrncpy (new_vl.type_instance, "L2", sizeof (new_vl.type_instance)); + + /* Copy the actual values. */ + new_vl.values[0].derive = (derive_t) vl->values[0].counter; + new_vl.values[1].derive = (derive_t) vl->values[1].counter; + + /* Dispatch new value lists instead of this one */ + plugin_dispatch_values (&new_vl); + + /* Abort processing */ + return (FC_TARGET_STOP); +} /* }}} int v5_zfs_arc_l2_bytes */ + +/* + * ZFS ARC L2 cache size + * + * 4.* uses a separate type for this. 5.* uses the generic "cache_size" type + * instead. + */ +static int v5_zfs_arc_l2_size (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + value_list_t new_vl; + value_t new_value; + + if (vl->values_len != 1) + 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; + + new_vl.values[0].gauge = (gauge_t) vl->values[0].gauge; + + /* Change the type to "cache_size" */ + sstrncpy (new_vl.type, "cache_size", sizeof (new_vl.type)); + + /* Adapt the type instance */ + sstrncpy (new_vl.type_instance, "L2", sizeof (new_vl.type_instance)); + + /* Dispatch new value lists instead of this one */ + plugin_dispatch_values (&new_vl); + + /* Abort processing */ + return (FC_TARGET_STOP); +} /* }}} int v5_zfs_arc_l2_size */ + +/* + * ZFS ARC ratio + * + * "arc_ratio-L1" -> "cache_ratio-arc" + * "arc_ratio-L2" -> "cache_ratio-L2" + */ +static int v5_zfs_arc_ratio (const data_set_t *ds, value_list_t *vl) /* {{{ */ +{ + value_list_t new_vl; + value_t new_value; + + if (vl->values_len != 1) + 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; + + new_vl.values[0].gauge = (gauge_t) vl->values[0].gauge; + + /* Change the type to "cache_ratio" */ + sstrncpy (new_vl.type, "cache_ratio", sizeof (new_vl.type)); + + /* Adapt the type instance */ + if (strcmp ("L1", vl->type_instance) == 0) + sstrncpy (new_vl.type_instance, "arc", sizeof (new_vl.type_instance)); + + /* Dispatch new value lists instead of this one */ + plugin_dispatch_values (&new_vl); + + /* Abort processing */ + return (FC_TARGET_STOP); +} /* }}} int v5_zfs_arc_ratio */ + +/* + * ZFS ARC size + * + * 4.* uses the "arc_size" type with four data sources. In 5.* this has been + * replaces with the "cache_size" type and static data has been removed. + */ +static int v5_zfs_arc_size (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 "cache_size" */ + sstrncpy (new_vl.type, "cache_size", 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, "arc", sizeof (new_vl.type_instance)); + plugin_dispatch_values (&new_vl); + + /* Abort processing */ + return (FC_TARGET_STOP); +} /* }}} int v5_zfs_arc_size */ + +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)); + else if (strcmp ("arc_counts", vl->type) == 0) + return (v5_zfs_arc_counts (ds, vl)); + else if (strcmp ("arc_l2_bytes", vl->type) == 0) + return (v5_zfs_arc_l2_bytes (ds, vl)); + else if (strcmp ("arc_l2_size", vl->type) == 0) + return (v5_zfs_arc_l2_size (ds, vl)); + else if (strcmp ("arc_ratio", vl->type) == 0) + return (v5_zfs_arc_ratio (ds, vl)); + else if (strcmp ("arc_size", vl->type) == 0) + return (v5_zfs_arc_size (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/teamspeak2.c b/src/teamspeak2.c index 502145d3..2552ad30 100644 --- a/src/teamspeak2.c +++ b/src/teamspeak2.c @@ -25,8 +25,8 @@ #include "common.h" #include "plugin.h" -#include #include +#include #include #include #include @@ -146,7 +146,7 @@ static void tss2_submit_gauge (const char *plugin_instance, } /* void tss2_submit_gauge */ static void tss2_submit_io (const char *plugin_instance, const char *type, - counter_t rx, counter_t tx) + derive_t rx, derive_t tx) { /* * Submits the io rx/tx tuple to the collectd daemon @@ -154,8 +154,8 @@ static void tss2_submit_io (const char *plugin_instance, const char *type, value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; vl.values = values; vl.values_len = 2; @@ -505,10 +505,10 @@ static int tss2_read_vserver (vserver_list_t *vserver) gauge_t users = NAN; gauge_t channels = NAN; gauge_t servers = NAN; - counter_t rx_octets = 0; - counter_t tx_octets = 0; - counter_t rx_packets = 0; - counter_t tx_packets = 0; + derive_t rx_octets = 0; + derive_t tx_octets = 0; + derive_t rx_packets = 0; + derive_t tx_packets = 0; gauge_t packet_loss = NAN; int valid = 0; 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/thermal.c b/src/thermal.c index b9d07bf5..0ad0d90a 100644 --- a/src/thermal.c +++ b/src/thermal.c @@ -29,13 +29,17 @@ # error "This module is for Linux only." #endif +static const char *config_keys[] = { + "Device", + "IgnoreSelected", + "ForceUseProcfs" +}; + const char *const dirname_sysfs = "/sys/class/thermal"; const char *const dirname_procfs = "/proc/acpi/thermal_zone"; -static char force_procfs = 0; +static _Bool force_procfs = 0; static ignorelist_t *device_list; -static value_list_t vl_temp_template = VALUE_LIST_STATIC; -static value_list_t vl_state_template = VALUE_LIST_STATIC; enum dev_type { TEMP = 0, @@ -45,16 +49,18 @@ enum dev_type { static void thermal_submit (const char *plugin_instance, enum dev_type dt, gauge_t value) { - value_list_t vl = (dt == TEMP) ? vl_temp_template : vl_state_template; - value_t vt; + value_list_t vl = VALUE_LIST_INIT; + value_t v; - vt.gauge = value; + v.gauge = value; + vl.values = &v; - vl.values = &vt; sstrncpy (vl.plugin, "thermal", sizeof(vl.plugin)); - sstrncpy (vl.plugin_instance, plugin_instance, - sizeof(vl.plugin_instance)); - sstrncpy (vl.type, (dt == TEMP) ? "temperature" : "gauge", + if (plugin_instance != NULL) + sstrncpy (vl.plugin_instance, plugin_instance, + sizeof (vl.plugin_instance)); + sstrncpy (vl.type, + (dt == TEMP) ? "temperature" : "gauge", sizeof (vl.type)); plugin_dispatch_values (&vl); @@ -66,7 +72,7 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir, char filename[256]; char data[1024]; int len; - int ok = 0; + _Bool success = 0; if (device_list && ignorelist_match (device_list, name)) return -1; @@ -87,7 +93,7 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir, if (endptr == data + len && errno == 0) { thermal_submit(name, TEMP, temp); - ++ok; + success = 1; } } @@ -107,11 +113,11 @@ static int thermal_sysfs_device_read (const char __attribute__((unused)) *dir, if (endptr == data + len && errno == 0) { thermal_submit(name, COOLING_DEV, state); - ++ok; + success = 1; } } - return ok ? 0 : -1; + return (success ? 0 : -1); } static int thermal_procfs_device_read (const char __attribute__((unused)) *dir, @@ -141,17 +147,17 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir, && (! strncmp(data, str_temp, sizeof(str_temp)-1))) { char *endptr = NULL; double temp; - double celsius, add; + double factor, add; if (data[--len] == 'C') { add = 0; - celsius = 1; + factor = 1.0; } else if (data[len] == 'F') { add = -32; - celsius = 5/9; + factor = 5.0/9.0; } else if (data[len] == 'K') { add = -273.15; - celsius = 1; + factor = 1.0; } else return -1; @@ -164,7 +170,7 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir, ++len; errno = 0; - temp = (strtod (data + len, &endptr) + add) * celsius; + temp = (strtod (data + len, &endptr) + add) * factor; if (endptr != data + len && errno == 0) { thermal_submit(name, TEMP, temp); @@ -175,12 +181,6 @@ static int thermal_procfs_device_read (const char __attribute__((unused)) *dir, return -1; } -static const char *config_keys[] = { - "Device", - "IgnoreSelected", - "ForceUseProcfs" -}; - static int thermal_config (const char *key, const char *value) { if (device_list == NULL) @@ -237,21 +237,6 @@ static int thermal_init (void) ret = plugin_register_read ("thermal", thermal_procfs_read); } - if (!ret) { - vl_temp_template.values_len = 1; - vl_temp_template.interval = interval_g; - sstrncpy (vl_temp_template.host, hostname_g, - sizeof(vl_temp_template.host)); - sstrncpy (vl_temp_template.plugin, "thermal", - sizeof(vl_temp_template.plugin)); - sstrncpy (vl_temp_template.type_instance, "temperature", - sizeof(vl_temp_template.type_instance)); - - vl_state_template = vl_temp_template; - sstrncpy (vl_state_template.type_instance, "cooling_state", - sizeof(vl_state_template.type_instance)); - } - return ret; } diff --git a/src/threshold.c b/src/threshold.c new file mode 100644 index 00000000..7bbc59ef --- /dev/null +++ b/src/threshold.c @@ -0,0 +1,1019 @@ +/** + * collectd - src/threshold.c + * Copyright (C) 2007-2010 Florian Forster + * Copyright (C) 2008-2009 Sebastian Harl + * Copyright (C) 2009 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; 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 + * + * Author: + * Florian octo Forster + * Sebastian Harl + * Andrés J. Díaz + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "utils_avltree.h" +#include "utils_cache.h" + +#include +#include + +/* + * Private data structures + * {{{ */ +#define UT_FLAG_INVERT 0x01 +#define UT_FLAG_PERSIST 0x02 +#define UT_FLAG_PERCENTAGE 0x04 +#define UT_FLAG_INTERESTING 0x08 +typedef struct threshold_s +{ + char host[DATA_MAX_NAME_LEN]; + char plugin[DATA_MAX_NAME_LEN]; + char plugin_instance[DATA_MAX_NAME_LEN]; + char type[DATA_MAX_NAME_LEN]; + char type_instance[DATA_MAX_NAME_LEN]; + char data_source[DATA_MAX_NAME_LEN]; + gauge_t warning_min; + gauge_t warning_max; + gauge_t failure_min; + gauge_t failure_max; + gauge_t hysteresis; + unsigned int flags; + int hits; + struct threshold_s *next; +} threshold_t; +/* }}} */ + +/* + * Private (static) variables + * {{{ */ +static c_avl_tree_t *threshold_tree = NULL; +static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER; +/* }}} */ + +/* + * Threshold management + * ==================== + * The following functions add, delete, search, etc. configured thresholds to + * the underlying AVL trees. + */ +/* + * threshold_t *threshold_get + * + * Retrieve one specific threshold configuration. For looking up a threshold + * matching a value_list_t, see "threshold_search" below. Returns NULL if the + * specified threshold doesn't exist. + */ +static threshold_t *threshold_get (const char *hostname, + const char *plugin, const char *plugin_instance, + const char *type, const char *type_instance) +{ /* {{{ */ + char name[6 * DATA_MAX_NAME_LEN]; + threshold_t *th = NULL; + + format_name (name, sizeof (name), + (hostname == NULL) ? "" : hostname, + (plugin == NULL) ? "" : plugin, plugin_instance, + (type == NULL) ? "" : type, type_instance); + name[sizeof (name) - 1] = '\0'; + + if (c_avl_get (threshold_tree, name, (void *) &th) == 0) + return (th); + else + return (NULL); +} /* }}} threshold_t *threshold_get */ + +/* + * int ut_threshold_add + * + * Adds a threshold configuration to the list of thresholds. The threshold_t + * structure is copied and may be destroyed after this call. Returns zero on + * success, non-zero otherwise. + */ +static int ut_threshold_add (const threshold_t *th) +{ /* {{{ */ + char name[6 * DATA_MAX_NAME_LEN]; + char *name_copy; + threshold_t *th_copy; + threshold_t *th_ptr; + int status = 0; + + if (format_name (name, sizeof (name), th->host, + th->plugin, th->plugin_instance, + th->type, th->type_instance) != 0) + { + ERROR ("ut_threshold_add: format_name failed."); + return (-1); + } + + name_copy = strdup (name); + if (name_copy == NULL) + { + ERROR ("ut_threshold_add: strdup failed."); + return (-1); + } + + th_copy = (threshold_t *) malloc (sizeof (threshold_t)); + if (th_copy == NULL) + { + sfree (name_copy); + ERROR ("ut_threshold_add: malloc failed."); + return (-1); + } + memcpy (th_copy, th, sizeof (threshold_t)); + th_ptr = NULL; + + DEBUG ("ut_threshold_add: Adding entry `%s'", name); + + pthread_mutex_lock (&threshold_lock); + + th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance, + th->type, th->type_instance); + + while ((th_ptr != NULL) && (th_ptr->next != NULL)) + th_ptr = th_ptr->next; + + if (th_ptr == NULL) /* no such threshold yet */ + { + status = c_avl_insert (threshold_tree, name_copy, th_copy); + } + else /* th_ptr points to the last threshold in the list */ + { + th_ptr->next = th_copy; + /* name_copy isn't needed */ + sfree (name_copy); + } + + pthread_mutex_unlock (&threshold_lock); + + if (status != 0) + { + ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name); + sfree (name_copy); + sfree (th_copy); + } + + return (status); +} /* }}} int ut_threshold_add */ + +/* + * threshold_t *threshold_search + * + * Searches for a threshold configuration using all the possible variations of + * "Host", "Plugin" and "Type" blocks. Returns NULL if no threshold could be + * found. + * XXX: This is likely the least efficient function in collectd. + */ +static threshold_t *threshold_search (const value_list_t *vl) +{ /* {{{ */ + threshold_t *th; + + if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance, + vl->type, vl->type_instance)) != NULL) + return (th); + else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance, + vl->type, NULL)) != NULL) + return (th); + else if ((th = threshold_get (vl->host, vl->plugin, NULL, + vl->type, vl->type_instance)) != NULL) + return (th); + else if ((th = threshold_get (vl->host, vl->plugin, NULL, + vl->type, NULL)) != NULL) + return (th); + else if ((th = threshold_get (vl->host, "", NULL, + vl->type, vl->type_instance)) != NULL) + return (th); + else if ((th = threshold_get (vl->host, "", NULL, + vl->type, NULL)) != NULL) + return (th); + else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance, + vl->type, vl->type_instance)) != NULL) + return (th); + else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance, + vl->type, NULL)) != NULL) + return (th); + else if ((th = threshold_get ("", vl->plugin, NULL, + vl->type, vl->type_instance)) != NULL) + return (th); + else if ((th = threshold_get ("", vl->plugin, NULL, + vl->type, NULL)) != NULL) + return (th); + else if ((th = threshold_get ("", "", NULL, + vl->type, vl->type_instance)) != NULL) + return (th); + else if ((th = threshold_get ("", "", NULL, + vl->type, NULL)) != NULL) + return (th); + + return (NULL); +} /* }}} threshold_t *threshold_search */ + +/* + * Configuration + * ============= + * The following approximately two hundred functions are used to handle the + * configuration and fill the threshold list. + * {{{ */ +static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("threshold values: The `DataSource' option needs exactly one " + "string argument."); + return (-1); + } + + sstrncpy (th->data_source, ci->values[0].value.string, + sizeof (th->data_source)); + + return (0); +} /* int ut_config_type_datasource */ + +static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("threshold values: The `Instance' option needs exactly one " + "string argument."); + return (-1); + } + + sstrncpy (th->type_instance, ci->values[0].value.string, + sizeof (th->type_instance)); + + return (0); +} /* int ut_config_type_instance */ + +static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + WARNING ("threshold values: The `%s' option needs exactly one " + "number argument.", ci->key); + return (-1); + } + + if (strcasecmp (ci->key, "WarningMax") == 0) + th->warning_max = ci->values[0].value.number; + else + th->failure_max = ci->values[0].value.number; + + return (0); +} /* int ut_config_type_max */ + +static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + WARNING ("threshold values: The `%s' option needs exactly one " + "number argument.", ci->key); + return (-1); + } + + if (strcasecmp (ci->key, "WarningMin") == 0) + th->warning_min = ci->values[0].value.number; + else + th->failure_min = ci->values[0].value.number; + + return (0); +} /* int ut_config_type_min */ + +static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + WARNING ("threshold values: The `%s' option needs exactly one " + "number argument.", ci->key); + return (-1); + } + + th->hits = ci->values[0].value.number; + + return (0); +} /* int ut_config_type_hits */ + +static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) + { + WARNING ("threshold values: The `%s' option needs exactly one " + "number argument.", ci->key); + return (-1); + } + + th->hysteresis = ci->values[0].value.number; + + return (0); +} /* int ut_config_type_hysteresis */ + +static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci) +{ + int i; + threshold_t th; + int status = 0; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("threshold values: The `Type' block needs exactly one string " + "argument."); + return (-1); + } + + if (ci->children_num < 1) + { + WARNING ("threshold values: The `Type' block needs at least one option."); + return (-1); + } + + memcpy (&th, th_orig, sizeof (th)); + sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type)); + + th.warning_min = NAN; + th.warning_max = NAN; + th.failure_min = NAN; + 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++) + { + oconfig_item_t *option = ci->children + i; + status = 0; + + if (strcasecmp ("Instance", option->key) == 0) + status = ut_config_type_instance (&th, option); + else if (strcasecmp ("DataSource", option->key) == 0) + status = ut_config_type_datasource (&th, option); + else if ((strcasecmp ("WarningMax", option->key) == 0) + || (strcasecmp ("FailureMax", option->key) == 0)) + status = ut_config_type_max (&th, option); + 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 = cf_util_get_flag (option, &th.flags, UT_FLAG_INVERT); + else if (strcasecmp ("Persist", option->key) == 0) + status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST); + else if (strcasecmp ("Percentage", option->key) == 0) + 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) + status = ut_config_type_hysteresis (&th, option); + else + { + WARNING ("threshold values: Option `%s' not allowed inside a `Type' " + "block.", option->key); + status = -1; + } + + if (status != 0) + break; + } + + if (status == 0) + { + status = ut_threshold_add (&th); + } + + return (status); +} /* int ut_config_type */ + +static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci) +{ + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("threshold values: The `Instance' option needs exactly one " + "string argument."); + return (-1); + } + + sstrncpy (th->plugin_instance, ci->values[0].value.string, + sizeof (th->plugin_instance)); + + return (0); +} /* int ut_config_plugin_instance */ + +static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci) +{ + int i; + threshold_t th; + int status = 0; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("threshold values: The `Plugin' block needs exactly one string " + "argument."); + return (-1); + } + + if (ci->children_num < 1) + { + WARNING ("threshold values: The `Plugin' block needs at least one nested " + "block."); + return (-1); + } + + memcpy (&th, th_orig, sizeof (th)); + sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin)); + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *option = ci->children + i; + status = 0; + + if (strcasecmp ("Type", option->key) == 0) + status = ut_config_type (&th, option); + else if (strcasecmp ("Instance", option->key) == 0) + status = ut_config_plugin_instance (&th, option); + else + { + WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' " + "block.", option->key); + status = -1; + } + + if (status != 0) + break; + } + + return (status); +} /* int ut_config_plugin */ + +static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci) +{ + int i; + threshold_t th; + int status = 0; + + if ((ci->values_num != 1) + || (ci->values[0].type != OCONFIG_TYPE_STRING)) + { + WARNING ("threshold values: The `Host' block needs exactly one string " + "argument."); + return (-1); + } + + if (ci->children_num < 1) + { + WARNING ("threshold values: The `Host' block needs at least one nested " + "block."); + return (-1); + } + + memcpy (&th, th_orig, sizeof (th)); + sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host)); + + for (i = 0; i < ci->children_num; i++) + { + oconfig_item_t *option = ci->children + i; + status = 0; + + if (strcasecmp ("Type", option->key) == 0) + status = ut_config_type (&th, option); + else if (strcasecmp ("Plugin", option->key) == 0) + status = ut_config_plugin (&th, option); + else + { + WARNING ("threshold values: Option `%s' not allowed inside a `Host' " + "block.", option->key); + status = -1; + } + + if (status != 0) + break; + } + + return (status); +} /* int ut_config_host */ + +int ut_config (oconfig_item_t *ci) +{ + int i; + int status = 0; + + threshold_t th; + + if (threshold_tree == NULL) + { + threshold_tree = c_avl_create ((void *) strcmp); + if (threshold_tree == NULL) + { + ERROR ("ut_config: c_avl_create failed."); + return (-1); + } + } + + memset (&th, '\0', sizeof (th)); + th.warning_min = NAN; + th.warning_max = NAN; + th.failure_min = NAN; + 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++) + { + oconfig_item_t *option = ci->children + i; + status = 0; + + if (strcasecmp ("Type", option->key) == 0) + status = ut_config_type (&th, option); + else if (strcasecmp ("Plugin", option->key) == 0) + status = ut_config_plugin (&th, option); + else if (strcasecmp ("Host", option->key) == 0) + status = ut_config_host (&th, option); + else + { + WARNING ("threshold values: Option `%s' not allowed here.", option->key); + status = -1; + } + + if (status != 0) + break; + } + + return (status); +} /* int um_config */ +/* + * End of the functions used to configure threshold values. + */ +/* }}} */ + +/* + * int ut_report_state + * + * Checks if the `state' differs from the old state and creates a notification + * if appropriate. + * Does not fail. + */ +static int ut_report_state (const data_set_t *ds, + const value_list_t *vl, + const threshold_t *th, + const gauge_t *values, + int ds_index, + int state) +{ /* {{{ */ + int state_old; + notification_t n; + + char *buf; + size_t bufsize; + + int status; + + /* Check if hits matched */ + if ( (th->hits != 0) ) + { + int hits = uc_get_hits(ds,vl); + /* The STATE_OKAY always reset hits, or if hits reaise the limit */ + if ( (state == STATE_OKAY) || (hits > th->hits) ) + { + DEBUG("ut_report_state: reset uc_get_hits = 0"); + uc_set_hits(ds,vl,0); /* reset hit counter and notify */ + } else { + DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl)); + (void) uc_inc_hits(ds,vl,1); /* increase hit counter */ + return (0); + } + } /* end check hits */ + + state_old = uc_get_state (ds, vl); + + /* If the state didn't change, only report if `persistent' is specified and + * the state is not `okay'. */ + if (state == state_old) + { + if ((th->flags & UT_FLAG_PERSIST) == 0) + return (0); + else if (state == STATE_OKAY) + return (0); + } + + if (state != state_old) + uc_set_state (ds, vl, state); + + NOTIFICATION_INIT_VL (&n, vl); + + buf = n.message; + bufsize = sizeof (n.message); + + if (state == STATE_OKAY) + n.severity = NOTIF_OKAY; + else if (state == STATE_WARNING) + n.severity = NOTIF_WARNING; + else + n.severity = NOTIF_FAILURE; + + n.time = vl->time; + + status = ssnprintf (buf, bufsize, "Host %s, plugin %s", + vl->host, vl->plugin); + buf += status; + bufsize -= status; + + if (vl->plugin_instance[0] != '\0') + { + status = ssnprintf (buf, bufsize, " (instance %s)", + vl->plugin_instance); + buf += status; + bufsize -= status; + } + + status = ssnprintf (buf, bufsize, " type %s", vl->type); + buf += status; + bufsize -= status; + + if (vl->type_instance[0] != '\0') + { + status = ssnprintf (buf, bufsize, " (instance %s)", + vl->type_instance); + buf += status; + bufsize -= status; + } + + plugin_notification_meta_add_string (&n, "DataSource", + ds->ds[ds_index].name); + plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]); + plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min); + plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max); + plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min); + plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max); + + /* Send an okay notification */ + if (state == STATE_OKAY) + { + if (state_old == STATE_MISSING) + status = ssnprintf (buf, bufsize, + ": Value is no longer missing."); + else + status = ssnprintf (buf, bufsize, + ": All data sources are within range again."); + buf += status; + bufsize -= status; + } + else + { + double min; + double max; + + min = (state == STATE_ERROR) ? th->failure_min : th->warning_min; + max = (state == STATE_ERROR) ? th->failure_max : th->warning_max; + + if (th->flags & UT_FLAG_INVERT) + { + if (!isnan (min) && !isnan (max)) + { + status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently " + "%f. That is within the %s region of %f%s and %f%s.", + ds->ds[ds_index].name, values[ds_index], + (state == STATE_ERROR) ? "failure" : "warning", + min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "", + max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : ""); + } + else + { + status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently " + "%f. That is %s the %s threshold of %f%s.", + ds->ds[ds_index].name, values[ds_index], + isnan (min) ? "below" : "above", + (state == STATE_ERROR) ? "failure" : "warning", + isnan (min) ? max : min, + ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : ""); + } + } + else if (th->flags & UT_FLAG_PERCENTAGE) + { + gauge_t value; + gauge_t sum; + int i; + + sum = 0.0; + for (i = 0; i < vl->values_len; i++) + { + if (isnan (values[i])) + continue; + + sum += values[i]; + } + + if (sum == 0.0) + value = NAN; + else + value = 100.0 * values[ds_index] / sum; + + status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently " + "%g (%.2f%%). That is %s the %s threshold of %.2f%%.", + ds->ds[ds_index].name, values[ds_index], value, + (value < min) ? "below" : "above", + (state == STATE_ERROR) ? "failure" : "warning", + (value < min) ? min : max); + } + else /* is not inverted */ + { + status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently " + "%f. That is %s the %s threshold of %f.", + ds->ds[ds_index].name, values[ds_index], + (values[ds_index] < min) ? "below" : "above", + (state == STATE_ERROR) ? "failure" : "warning", + (values[ds_index] < min) ? min : max); + } + buf += status; + bufsize -= status; + } + + plugin_dispatch_notification (&n); + + plugin_notification_meta_free (n.meta); + return (0); +} /* }}} int ut_report_state */ + +/* + * int ut_check_one_data_source + * + * Checks one data source against the given threshold configuration. If the + * `DataSource' option is set in the threshold, and the name does NOT match, + * `okay' is returned. If the threshold does match, its failure and warning + * min and max values are checked and `failure' or `warning' is returned if + * appropriate. + * Does not fail. + */ +static int ut_check_one_data_source (const data_set_t *ds, + const value_list_t __attribute__((unused)) *vl, + const threshold_t *th, + const gauge_t *values, + int ds_index) +{ /* {{{ */ + const char *ds_name; + int is_warning = 0; + int is_failure = 0; + int prev_state = STATE_OKAY; + + /* check if this threshold applies to this data source */ + if (ds != NULL) + { + ds_name = ds->ds[ds_index].name; + if ((th->data_source[0] != 0) + && (strcmp (ds_name, th->data_source) != 0)) + return (STATE_OKAY); + } + + if ((th->flags & UT_FLAG_INVERT) != 0) + { + is_warning--; + is_failure--; + } + + /* XXX: This is an experimental code, not optimized, not fast, not reliable, + * and probably, do not work as you expect. Enjoy! :D */ + if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) ) + { + switch(prev_state) + { + case STATE_ERROR: + if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) || + (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) ) + return (STATE_OKAY); + else + is_failure++; + case STATE_WARNING: + if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) || + (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) ) + return (STATE_OKAY); + else + is_warning++; + } + } + else { /* no hysteresis */ + if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index])) + || (!isnan (th->failure_max) && (th->failure_max < values[ds_index]))) + is_failure++; + + if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index])) + || (!isnan (th->warning_max) && (th->warning_max < values[ds_index]))) + is_warning++; + } + + if (is_failure != 0) + return (STATE_ERROR); + + if (is_warning != 0) + return (STATE_WARNING); + + return (STATE_OKAY); +} /* }}} int ut_check_one_data_source */ + +/* + * int ut_check_one_threshold + * + * Checks all data sources of a value list against the given threshold, using + * the ut_check_one_data_source function above. Returns the worst status, + * which is `okay' if nothing has failed. + * Returns less than zero if the data set doesn't have any data sources. + */ +static int ut_check_one_threshold (const data_set_t *ds, + const value_list_t *vl, + const threshold_t *th, + const gauge_t *values, + int *ret_ds_index) +{ /* {{{ */ + int ret = -1; + int ds_index = -1; + int i; + gauge_t values_copy[ds->ds_num]; + + memcpy (values_copy, values, sizeof (values_copy)); + + if ((th->flags & UT_FLAG_PERCENTAGE) != 0) + { + int num = 0; + gauge_t sum=0.0; + + if (ds->ds_num == 1) + { + WARNING ("ut_check_one_threshold: The %s type has only one data " + "source, but you have configured to check this as a percentage. " + "That doesn't make much sense, because the percentage will always " + "be 100%%!", ds->type); + } + + /* Prepare `sum' and `num'. */ + for (i = 0; i < ds->ds_num; i++) + if (!isnan (values[i])) + { + num++; + sum += values[i]; + } + + if ((num == 0) /* All data sources are undefined. */ + || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */ + { + for (i = 0; i < ds->ds_num; i++) + values_copy[i] = NAN; + } + else /* We can actually calculate the percentage. */ + { + for (i = 0; i < ds->ds_num; i++) + values_copy[i] = 100.0 * values[i] / sum; + } + } /* if (UT_FLAG_PERCENTAGE) */ + + for (i = 0; i < ds->ds_num; i++) + { + int status; + + status = ut_check_one_data_source (ds, vl, th, values_copy, i); + if (ret < status) + { + ret = status; + ds_index = i; + } + } /* for (ds->ds_num) */ + + if (ret_ds_index != NULL) + *ret_ds_index = ds_index; + + return (ret); +} /* }}} int ut_check_one_threshold */ + +/* + * int ut_check_threshold + * + * Gets a list of matching thresholds and searches for the worst status by one + * of the thresholds. Then reports that status using the ut_report_state + * function above. + * Returns zero on success and if no threshold has been configured. Returns + * less than zero on failure. + */ +static int ut_check_threshold (const data_set_t *ds, const value_list_t *vl, + __attribute__((unused)) user_data_t *ud) +{ /* {{{ */ + threshold_t *th; + gauge_t *values; + int status; + + int worst_state = -1; + threshold_t *worst_th = NULL; + int worst_ds_index = -1; + + if (threshold_tree == NULL) + return (0); + + /* Is this lock really necessary? So far, thresholds are only inserted at + * startup. -octo */ + pthread_mutex_lock (&threshold_lock); + th = threshold_search (vl); + pthread_mutex_unlock (&threshold_lock); + if (th == NULL) + return (0); + + DEBUG ("ut_check_threshold: Found matching threshold(s)"); + + values = uc_get_rate (ds, vl); + if (values == NULL) + return (0); + + while (th != NULL) + { + int ds_index = -1; + + status = ut_check_one_threshold (ds, vl, th, values, &ds_index); + if (status < 0) + { + ERROR ("ut_check_threshold: ut_check_one_threshold failed."); + sfree (values); + return (-1); + } + + if (worst_state < status) + { + worst_state = status; + worst_th = th; + worst_ds_index = ds_index; + } + + th = th->next; + } /* while (th) */ + + status = ut_report_state (ds, vl, worst_th, values, + worst_ds_index, worst_state); + if (status != 0) + { + ERROR ("ut_check_threshold: ut_report_state failed."); + sfree (values); + return (-1); + } + + sfree (values); + + return (0); +} /* }}} int ut_check_threshold */ + +/* + * int ut_missing + * + * This function is called whenever a value goes "missing". + */ +static int ut_missing (const value_list_t *vl, + __attribute__((unused)) user_data_t *ud) +{ /* {{{ */ + threshold_t *th; + cdtime_t missing_time; + char identifier[6 * DATA_MAX_NAME_LEN]; + notification_t n; + + th = threshold_search (vl); + if (th == NULL) + return (0); + + missing_time = cdtime () - vl->time; + FORMAT_VL (identifier, sizeof (identifier), vl); + + NOTIFICATION_INIT_VL (&n, vl); + ssnprintf (n.message, sizeof (n.message), + "%s has not been updated for %.3f seconds.", + identifier, CDTIME_T_TO_DOUBLE (missing_time)); + + plugin_dispatch_notification (&n); + + return (0); +} /* }}} int ut_missing */ + +void module_register (void) +{ + plugin_register_complex_config ("threshold", ut_config); + plugin_register_missing ("threshold", ut_missing, + /* user data = */ NULL); + plugin_register_write ("threshold", ut_check_threshold, + /* user data = */ NULL); +} + +/* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */ diff --git a/src/types.db b/src/types.db index ad542402..e6345ab6 100644 --- a/src/types.db +++ b/src/types.db @@ -1,173 +1,187 @@ -absolute count:ABSOLUTE:0:U -apache_bytes count:COUNTER:0:134217728 -apache_connections count:GAUGE:0:65535 -apache_idle_workers count:GAUGE:0:65535 -apache_requests count:COUNTER:0:134217728 -apache_scoreboard count:GAUGE:0:65535 -arc_counts demand_data:COUNTER:0:U, demand_metadata:COUNTER:0:U, prefetch_data:COUNTER:0:U, prefetch_metadata:COUNTER:0:U -arc_l2_bytes read:COUNTER:0:U, write:COUNTER:0:U -arc_l2_size value:GAUGE:0:U -arc_ratio value:GAUGE:0:U -arc_size current:GAUGE:0:U, target:GAUGE:0:U, minlimit:GAUGE:0:U, maxlimit:GAUGE:0:U +absolute value:ABSOLUTE:0:U +apache_bytes value:DERIVE:0:U +apache_connections value:GAUGE:0:65535 +apache_idle_workers value:GAUGE:0:65535 +apache_requests value:DERIVE:0:U +apache_scoreboard value:GAUGE:0:65535 ath_nodes value:GAUGE:0:65535 -ath_stat value:COUNTER:0:4294967295 +ath_stat value:DERIVE:0:U 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 compression_ratio value:GAUGE:0:2 -connections value:COUNTER:0:U -conntrack entropy:GAUGE:0:4294967295 -contextswitch contextswitches:DERIVE:0:U +compression uncompressed:DERIVE:0:U, compressed:DERIVE:0:U +connections value:DERIVE:0:U +conntrack value:GAUGE:0:4294967295 +contextswitch value:DERIVE:0:U counter value:COUNTER:U:U cpufreq value:GAUGE:0:U -cpu value:COUNTER:0:4294967295 +cpu value:DERIVE:0:U +current_connections value:GAUGE:0:U +current_sessions value:GAUGE:0:U current value:GAUGE:U:U -delay seconds:GAUGE:-1000000:1000000 +delay value:GAUGE:-1000000:1000000 derive value:DERIVE:0:U -df used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623 df_complex value:GAUGE:0:U df_inodes value:GAUGE:0:U +df used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623 disk_latency read:GAUGE:0:U, write:GAUGE:0:U -disk_merged read:COUNTER:0:4294967295, write:COUNTER:0:4294967295 -disk_octets read:COUNTER:0:17179869183, write:COUNTER:0:17179869183 -disk_ops read:COUNTER:0:4294967295, write:COUNTER:0:4294967295 -disk_ops_complex value:COUNTER:0:4294967296 -disk_time read:COUNTER:0:1000000, write:COUNTER:0:1000000 -dns_answer value:COUNTER:0:65535 -dns_notify value:COUNTER:0:65535 -dns_octets queries:COUNTER:0:125000000, responses:COUNTER:0:125000000 -dns_opcode value:COUNTER:0:65535 +disk_merged read:DERIVE:0:U, write:DERIVE:0:U +disk_octets read:DERIVE:0:U, write:DERIVE:0:U +disk_ops_complex value:DERIVE:0:U +disk_ops read:DERIVE:0:U, write:DERIVE:0:U +disk_time read:DERIVE:0:U, write:DERIVE:0:U +dns_answer value:DERIVE:0:U +dns_notify value:DERIVE:0:U +dns_octets queries:DERIVE:0:U, responses:DERIVE:0:U +dns_opcode value:DERIVE:0:U dns_qtype_cached value:GAUGE:0:4294967295 -dns_qtype value:COUNTER:0:65535 -dns_query value:COUNTER:0:65535 -dns_question value:COUNTER:0:65535 -dns_rcode value:COUNTER:0:65535 -dns_reject value:COUNTER:0:65535 -dns_request value:COUNTER:0:65535 -dns_resolver value:COUNTER:0:65535 -dns_response value:COUNTER:0:65535 -dns_transfer value:COUNTER:0:65535 -dns_update value:COUNTER:0:65535 -dns_zops value:COUNTER:0:65535 +dns_qtype value:DERIVE:0:U +dns_query value:DERIVE:0:U +dns_question value:DERIVE:0:U +dns_rcode value:DERIVE:0:U +dns_reject value:DERIVE:0:U +dns_request value:DERIVE:0:U +dns_resolver value:DERIVE:0:U +dns_response value:DERIVE:0:U +dns_transfer value:DERIVE:0:U +dns_update value:DERIVE:0:U +dns_zops value:DERIVE:0:U email_check value:GAUGE:0:U email_count value:GAUGE:0:U email_size value:GAUGE:0:U -entropy entropy:GAUGE:0:4294967295 +entropy value:GAUGE:0:4294967295 fanspeed value:GAUGE:0:U -file_size bytes:GAUGE:0:U +file_size value:GAUGE:0:U files value:GAUGE:0:U -frequency frequency:GAUGE:0:U -frequency_offset ppm:GAUGE:-1000000:1000000 -fscache_stat value:COUNTER:0:4294967295 fork_rate value:DERIVE:0:U +frequency value:GAUGE:0:U +frequency_offset value:GAUGE:-1000000:1000000 +fscache_stat value:DERIVE:0:U gauge value:GAUGE:U:U -http_request_methods count:COUNTER:0:134217728 -http_requests count:COUNTER:0:134217728 -http_response_codes count:COUNTER:0:134217728 +http_request_methods value:DERIVE:0:U +http_requests value:DERIVE:0:U +http_response_codes value:DERIVE:0:U humidity value:GAUGE:0:100 -if_collisions value:COUNTER:0:4294967295 -if_dropped rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 -if_errors rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 -if_multicast value:COUNTER:0:4294967295 -if_octets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 -if_packets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 -if_rx_errors value:COUNTER:0:4294967295 -if_tx_errors value:COUNTER:0:4294967295 +if_collisions value:DERIVE:0:U +if_dropped rx:DERIVE:0:U, tx:DERIVE:0:U +if_errors rx:DERIVE:0:U, tx:DERIVE:0:U +if_multicast value:DERIVE:0:U +if_octets rx:DERIVE:0:U, tx:DERIVE:0:U +if_packets rx:DERIVE:0:U, tx:DERIVE:0:U +if_rx_errors value:DERIVE:0:U +if_tx_errors value:DERIVE:0:U invocations value:DERIVE:0:U -io_octets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 -io_packets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 -ipt_bytes value:COUNTER:0:134217728 -ipt_packets value:COUNTER:0:134217728 -irq value:COUNTER:U:65535 +io_octets rx:DERIVE:0:U, tx:DERIVE:0:U +io_packets rx:DERIVE:0:U, tx:DERIVE:0:U +ipt_bytes value:DERIVE:0:U +ipt_packets value:DERIVE:0:U +irq value:DERIVE:0:U latency value:GAUGE:0:65535 links value:GAUGE:0:U load shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100 -memcached_command value:COUNTER:0:U +memcached_command value:DERIVE:0:U memcached_connections value:GAUGE:0:U memcached_items value:GAUGE:0:U -memcached_octets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 -memcached_ops value:COUNTER:0:134217728 +memcached_octets rx:DERIVE:0:U, tx:DERIVE:0:U +memcached_ops value:DERIVE:0:U memory value:GAUGE:0:281474976710656 multimeter value:GAUGE:U:U -mysql_commands value:COUNTER:0:U -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 +mysql_commands value:DERIVE:0:U +mysql_handler value:DERIVE:0:U +mysql_locks value:DERIVE:0:U +mysql_log_position value:DERIVE:0:U +mysql_octets rx:DERIVE:0:U, tx:DERIVE:0:U +nfs_procedure value:DERIVE:0:U nginx_connections value:GAUGE:0:U -nginx_requests value:COUNTER:0:134217728 -node_octets rx:COUNTER:0:U, tx:COUNTER:0:U +nginx_requests value:DERIVE:0:U +node_octets rx:DERIVE:0:U, tx:DERIVE:0:U node_rssi value:GAUGE:0:255 -node_stat value:COUNTER:0:4294967295 +node_stat value:DERIVE:0:U node_tx_rate value:GAUGE:0:127 -operations value:COUNTER:0:4294967295 -percent percent:GAUGE:0:100.1 -pg_blks value:COUNTER:0:U +operations value:DERIVE:0:U +percent value:GAUGE:0:100.1 +pg_blks value:DERIVE:0:U pg_db_size value:GAUGE:0:U -pg_n_tup_c value:COUNTER:0:U +pg_n_tup_c value:DERIVE:0:U pg_n_tup_g value:GAUGE:0:U pg_numbackends value:GAUGE:0:U -pg_scan value:COUNTER:0:U -pg_xact value:COUNTER:0:U +pg_scan value:DERIVE:0:U +pg_xact value:DERIVE:0:U ping_droprate value:GAUGE:0:100 -ping ping:GAUGE:0:65535 +ping value:GAUGE:0:65535 ping_stddev value:GAUGE:0:65535 players value:GAUGE:0:1000000 power value:GAUGE:0:U -protocol_counter value:COUNTER:0:U +protocol_counter value:DERIVE:0:U +ps_code value:GAUGE:0:9223372036854775807 ps_count processes:GAUGE:0:1000000, threads:GAUGE:0:1000000 -ps_cputime user:COUNTER:0:16000000, syst:COUNTER:0:16000000 -ps_pagefaults minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807 +ps_cputime user:DERIVE:0:U, syst:DERIVE:0:U +ps_data value:GAUGE:0:9223372036854775807 ps_disk_octets read:DERIVE:0:U, write:DERIVE:0:U ps_disk_ops read:DERIVE:0:U, write:DERIVE:0:U +ps_pagefaults minflt:DERIVE:0:U, majflt:DERIVE:0:U ps_rss value:GAUGE:0:9223372036854775807 -ps_code value:GAUGE:0:9223372036854775807 -ps_data value:GAUGE:0:9223372036854775807 ps_stacksize value:GAUGE:0:9223372036854775807 ps_state value:GAUGE:0:65535 ps_vm value:GAUGE:0:9223372036854775807 queue_length value:GAUGE:0:U +records value:GAUGE:0:U +requests value:GAUGE:0:U response_time value:GAUGE:0:U -records count:GAUGE:0:U route_etx value:GAUGE:0:U route_metric value:GAUGE:0:U routes value:GAUGE:0:U -serial_octets rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295 +serial_octets rx:DERIVE:0:U, tx:DERIVE:0:U signal_noise value:GAUGE:U:0 signal_power value:GAUGE:U:0 signal_quality value:GAUGE:0:U snr value:GAUGE:0:U spam_check value:GAUGE:0:U spam_score value:GAUGE:U:U -swap_io value:DERIVE:0:1099511627776 +swap_io value:DERIVE:0:U swap value:GAUGE:0:1099511627776 tcp_connections value:GAUGE:0:4294967295 temperature value:GAUGE:-273.15:U threads value:GAUGE:0:U -time_dispersion seconds:GAUGE:-1000000:1000000 -timeleft timeleft:GAUGE:0:3600 -time_offset seconds:GAUGE:-1000000:1000000 +time_dispersion value:GAUGE:-1000000:1000000 +timeleft value:GAUGE:0:3600 +time_offset value: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 -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 +users value:GAUGE:0:65535 +vcpu value:GAUGE:0:U +virt_cpu_total value:DERIVE:0:U +virt_vcpu value:DERIVE:0:U +vmpage_action value:DERIVE:0:U +vmpage_faults minflt:DERIVE:0:U, majflt:DERIVE:0:U +vmpage_io in:DERIVE:0:U, out:DERIVE:0:U 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 vs_processes value:GAUGE:0:65535 vs_threads value:GAUGE:0:65535 +# +# Legacy types +# (required for the v5 upgrade target) +# +arc_counts demand_data:COUNTER:0:U, demand_metadata:COUNTER:0:U, prefetch_data:COUNTER:0:U, prefetch_metadata:COUNTER:0:U +arc_l2_bytes read:COUNTER:0:U, write:COUNTER:0:U +arc_l2_size value:GAUGE:0:U +arc_ratio value:GAUGE:0:U +arc_size current:GAUGE:0:U, target:GAUGE:0:U, minlimit:GAUGE:0:U, maxlimit:GAUGE:0:U +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 diff --git a/src/types_list.c b/src/types_list.c index 49714176..10cb4f28 100644 --- a/src/types_list.c +++ b/src/types_list.c @@ -102,6 +102,10 @@ static void parse_line (char *buf) if (fields_num < 2) return; + /* Ignore lines which begin with a hash sign. */ + if (fields[0][0] == '#') + return; + ds = (data_set_t *) malloc (sizeof (data_set_t)); if (ds == NULL) return; diff --git a/src/unixsock.c b/src/unixsock.c index 35c77121..0dc7d659 100644 --- a/src/unixsock.c +++ b/src/unixsock.c @@ -26,7 +26,6 @@ #include "utils_cmd_flush.h" #include "utils_cmd_getval.h" -#include "utils_cmd_getthreshold.h" #include "utils_cmd_listval.h" #include "utils_cmd_putval.h" #include "utils_cmd_putnotif.h" @@ -54,7 +53,8 @@ static const char *config_keys[] = { "SocketFile", "SocketGroup", - "SocketPerms" + "SocketPerms", + "DeleteSocket" }; static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); @@ -65,6 +65,7 @@ static int sock_fd = -1; static char *sock_file = NULL; static char *sock_group = NULL; static int sock_perms = S_IRWXU | S_IRWXG; +static _Bool delete_socket = 0; static pthread_t listen_thread = (pthread_t) 0; @@ -89,10 +90,27 @@ static int us_open_socket (void) sa.sun_family = AF_UNIX; sstrncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH, sizeof (sa.sun_path)); - /* unlink (sa.sun_path); */ DEBUG ("unixsock plugin: socket path = %s", sa.sun_path); + if (delete_socket) + { + errno = 0; + status = unlink (sa.sun_path); + if ((status != 0) && (errno != ENOENT)) + { + char errbuf[1024]; + WARNING ("unixsock plugin: Deleting socket file \"%s\" failed: %s", + sa.sun_path, + sstrerror (errno, errbuf, sizeof (errbuf))); + } + else if (status == 0) + { + INFO ("unixsock plugin: Successfully deleted socket file \"%s\".", + sa.sun_path); + } + } + status = bind (sock_fd, (struct sockaddr *) &sa, sizeof (sa)); if (status != 0) { @@ -263,10 +281,6 @@ static void *us_handle_client (void *arg) { handle_getval (fhout, buffer); } - else if (strcasecmp (fields[0], "getthreshold") == 0) - { - handle_getthreshold (fhout, buffer); - } else if (strcasecmp (fields[0], "putval") == 0) { handle_putval (fhout, buffer); @@ -401,6 +415,13 @@ static int us_config (const char *key, const char *val) { sock_perms = (int) strtol (val, NULL, 8); } + else if (strcasecmp (key, "DeleteSocket") == 0) + { + if (IS_TRUE (val)) + delete_socket = 1; + else + delete_socket = 0; + } else { return (-1); diff --git a/src/utils_cache.c b/src/utils_cache.c index aeb662d5..dd5bcb59 100644 --- a/src/utils_cache.c +++ b/src/utils_cache.c @@ -1,6 +1,6 @@ /** * collectd - src/utils_cache.c - * Copyright (C) 2007,2008 Florian octo Forster + * Copyright (C) 2007-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 @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Author: - * Florian octo Forster + * Florian octo Forster **/ #include "collectd.h" @@ -24,7 +24,6 @@ #include "plugin.h" #include "utils_avltree.h" #include "utils_cache.h" -#include "utils_threshold.h" #include "meta_data.h" #include @@ -38,13 +37,13 @@ typedef struct cache_entry_s value_t *values_raw; /* Time contained in the package * (for calculating rates) */ - time_t last_time; + cdtime_t last_time; /* Time according to the local clock * (for purging old entries) */ - time_t last_update; + cdtime_t last_update; /* Interval in which the data is collected * (for purding old entries) */ - int interval; + cdtime_t interval; int state; int hits; @@ -120,80 +119,6 @@ static void cache_free (cache_entry_t *ce) sfree (ce); } /* void cache_free */ -static int uc_send_notification (const char *name) -{ - cache_entry_t *ce = NULL; - int status; - - char *name_copy; - char *host; - char *plugin; - char *plugin_instance; - char *type; - char *type_instance; - - notification_t n; - - name_copy = strdup (name); - if (name_copy == NULL) - { - ERROR ("uc_send_notification: strdup failed."); - return (-1); - } - - status = parse_identifier (name_copy, &host, - &plugin, &plugin_instance, - &type, &type_instance); - if (status != 0) - { - ERROR ("uc_send_notification: Cannot parse name `%s'", name); - return (-1); - } - - /* Copy the associative members */ - notification_init (&n, NOTIF_FAILURE, /* host = */ NULL, - host, plugin, plugin_instance, type, type_instance); - - sfree (name_copy); - name_copy = host = plugin = plugin_instance = type = type_instance = NULL; - - pthread_mutex_lock (&cache_lock); - - /* - * Set the time _after_ getting the lock because we don't know how long - * acquiring the lock takes and we will use this time later to decide - * whether or not the state is OKAY. - */ - n.time = time (NULL); - - status = c_avl_get (cache_tree, name, (void *) &ce); - if (status != 0) - { - pthread_mutex_unlock (&cache_lock); - sfree (name_copy); - return (-1); - } - - /* Check if the entry has been updated in the meantime */ - if ((n.time - ce->last_update) < (timeout_g * ce->interval)) - { - ce->state = STATE_OKAY; - pthread_mutex_unlock (&cache_lock); - sfree (name_copy); - return (-1); - } - - ssnprintf (n.message, sizeof (n.message), - "%s has not been updated for %i seconds.", name, - (int) (n.time - ce->last_update)); - - pthread_mutex_unlock (&cache_lock); - - plugin_dispatch_notification (&n); - - return (0); -} /* int uc_send_notification */ - static void uc_check_range (const data_set_t *ds, cache_entry_t *ce) { int i; @@ -258,7 +183,7 @@ static int uc_insert (const data_set_t *ds, const value_list_t *vl, ce->values_gauge[i] = NAN; if (vl->interval > 0) ce->values_gauge[i] = ((double) vl->values[i].absolute) - / ((double) vl->interval); + / CDTIME_T_TO_DOUBLE (vl->interval); ce->values_raw[i].absolute = vl->values[i].absolute; break; @@ -274,7 +199,7 @@ static int uc_insert (const data_set_t *ds, const value_list_t *vl, uc_check_range (ds, ce); ce->last_time = vl->time; - ce->last_update = time (NULL); + ce->last_update = cdtime (); ce->interval = vl->interval; ce->state = STATE_OKAY; @@ -300,145 +225,133 @@ int uc_init (void) int uc_check_timeout (void) { - time_t now; + cdtime_t now; cache_entry_t *ce; char **keys = NULL; + cdtime_t *keys_time = NULL; + cdtime_t *keys_interval = NULL; int keys_len = 0; char *key; c_avl_iterator_t *iter; + + int status; int i; pthread_mutex_lock (&cache_lock); - now = time (NULL); + now = cdtime (); /* Build a list of entries to be flushed */ iter = c_avl_get_iterator (cache_tree); while (c_avl_iterator_next (iter, (void *) &key, (void *) &ce) == 0) { - /* If entry has not been updated, add to `keys' array */ - if ((now - ce->last_update) >= (timeout_g * ce->interval)) - { - char **tmp; - - tmp = (char **) realloc ((void *) keys, - (keys_len + 1) * sizeof (char *)); - if (tmp == NULL) - { - ERROR ("uc_check_timeout: realloc failed."); - c_avl_iterator_destroy (iter); - sfree (keys); - pthread_mutex_unlock (&cache_lock); - return (-1); - } - - keys = tmp; - keys[keys_len] = strdup (key); - if (keys[keys_len] == NULL) - { - ERROR ("uc_check_timeout: strdup failed."); - continue; - } - keys_len++; - } - } /* while (c_avl_iterator_next) */ - - ce = NULL; - - for (i = 0; i < keys_len; i++) - { - int status; + char **tmp; + cdtime_t *tmp_time; - status = ut_check_interesting (keys[i]); + /* If the entry is fresh enough, continue. */ + if ((now - ce->last_update) < (ce->interval * timeout_g)) + continue; - if (status < 0) + /* If entry has not been updated, add to `keys' array */ + tmp = (char **) realloc ((void *) keys, + (keys_len + 1) * sizeof (char *)); + if (tmp == NULL) { - ERROR ("uc_check_timeout: ut_check_interesting failed."); - sfree (keys[i]); + ERROR ("uc_check_timeout: realloc failed."); continue; } - else if (status == 0) /* ``service'' is uninteresting */ + keys = tmp; + + tmp_time = realloc (keys_time, (keys_len + 1) * sizeof (*keys_time)); + if (tmp_time == NULL) { - DEBUG ("uc_check_timeout: %s is missing but ``uninteresting''", - keys[i]); - ce = NULL; - status = c_avl_remove (cache_tree, keys[i], - (void *) &key, (void *) &ce); - if (status != 0) - { - ERROR ("uc_check_timeout: c_avl_remove (%s) failed.", keys[i]); - } - sfree (keys[i]); - sfree (key); - if (ce != NULL) - cache_free (ce); + ERROR ("uc_check_timeout: realloc failed."); continue; } + keys_time = tmp_time; - /* If we get here, the value is ``interesting''. Query the record from the - * cache and update the state field. */ - if (c_avl_get (cache_tree, keys[i], (void *) &ce) != 0) + tmp_time = realloc (keys_interval, (keys_len + 1) * sizeof (*keys_interval)); + if (tmp_time == NULL) { - ERROR ("uc_check_timeout: cannot get data for %s from cache", keys[i]); - /* Do not free `keys[i]' so a notification is sent further down. */ + ERROR ("uc_check_timeout: realloc failed."); continue; } - assert (ce != NULL); + keys_interval = tmp_time; - if (status == 2) /* persist */ - { - DEBUG ("uc_check_timeout: %s is missing, sending notification.", - keys[i]); - ce->state = STATE_MISSING; - /* Do not free `keys[i]' so a notification is sent further down. */ - } - else if (status == 1) /* do not persist */ - { - if (ce->state == STATE_MISSING) - { - DEBUG ("uc_check_timeout: %s is missing but " - "notification has already been sent.", - keys[i]); - /* Set `keys[i]' to NULL to no notification is sent. */ - sfree (keys[i]); - } - else /* (ce->state != STATE_MISSING) */ - { - DEBUG ("uc_check_timeout: %s is missing, sending one notification.", - keys[i]); - ce->state = STATE_MISSING; - /* Do not free `keys[i]' so a notification is sent further down. */ - } - } - else + keys[keys_len] = strdup (key); + if (keys[keys_len] == NULL) { - WARNING ("uc_check_timeout: ut_check_interesting (%s) returned " - "invalid status %i.", - keys[i], status); - sfree (keys[i]); + ERROR ("uc_check_timeout: strdup failed."); + continue; } + keys_time[keys_len] = ce->last_time; + keys_interval[keys_len] = ce->interval; - /* Make really sure the next iteration doesn't work with this pointer. - * There have been too many bugs in the past.. :/ -- octo */ - ce = NULL; - } /* for (keys[i]) */ + keys_len++; + } /* while (c_avl_iterator_next) */ c_avl_iterator_destroy (iter); - pthread_mutex_unlock (&cache_lock); + if (keys_len == 0) + return (0); + + /* Call the "missing" callback for each value. Do this before removing the + * value from the cache, so that callbacks can still access the data stored, + * including plugin specific meta data, rates, history, …. This must be done + * without holding the lock, otherwise we will run into a deadlock if a + * plugin calls the cache interface. */ for (i = 0; i < keys_len; i++) { - if (keys[i] == NULL) + value_list_t vl = VALUE_LIST_INIT; + + vl.values = NULL; + vl.values_len = 0; + vl.meta = NULL; + + status = parse_identifier_vl (keys[i], &vl); + if (status != 0) + { + ERROR ("uc_check_timeout: parse_identifier_vl (\"%s\") failed.", keys[i]); + cache_free (ce); + continue; + } + + vl.time = keys_time[i]; + vl.interval = keys_interval[i]; + + plugin_dispatch_missing (&vl); + } /* for (i = 0; i < keys_len; i++) */ + + /* Now actually remove all the values from the cache. We don't re-evaluate + * the timestamp again, so in theory it is possible we remove a value after + * it is updated here. */ + pthread_mutex_lock (&cache_lock); + for (i = 0; i < keys_len; i++) + { + key = NULL; + ce = NULL; + + status = c_avl_remove (cache_tree, keys[i], + (void *) &key, (void *) &ce); + if (status != 0) + { + ERROR ("uc_check_timeout: c_avl_remove (\"%s\") failed.", keys[i]); + sfree (keys[i]); continue; + } - uc_send_notification (keys[i]); sfree (keys[i]); - } + sfree (key); + cache_free (ce); + } /* for (i = 0; i < keys_len; i++) */ + pthread_mutex_unlock (&cache_lock); sfree (keys); + sfree (keys_time); + sfree (keys_interval); return (0); } /* int uc_check_timeout */ @@ -447,9 +360,6 @@ int uc_update (const data_set_t *ds, const value_list_t *vl) { char name[6 * DATA_MAX_NAME_LEN]; cache_entry_t *ce = NULL; - int send_okay_notification = 0; - time_t update_delay = 0; - notification_t n; int status; int i; @@ -475,21 +385,14 @@ int uc_update (const data_set_t *ds, const value_list_t *vl) if (ce->last_time >= vl->time) { pthread_mutex_unlock (&cache_lock); - NOTICE ("uc_update: Value too old: name = %s; value time = %u; " - "last cache update = %u;", - name, (unsigned int) vl->time, (unsigned int) ce->last_time); + NOTICE ("uc_update: Value too old: name = %s; value time = %.3f; " + "last cache update = %.3f;", + name, + CDTIME_T_TO_DOUBLE (vl->time), + CDTIME_T_TO_DOUBLE (ce->last_time)); return (-1); } - /* Send a notification (after the lock has been released) if we switch the - * state from something else to `okay'. */ - if (ce->state == STATE_MISSING) - { - send_okay_notification = 1; - ce->state = STATE_OKAY; - update_delay = time (NULL) - ce->last_update; - } - for (i = 0; i < ds->ds_num; i++) { switch (ds->ds[i].type) @@ -514,7 +417,7 @@ int uc_update (const data_set_t *ds, const value_list_t *vl) } ce->values_gauge[i] = ((double) diff) - / ((double) (vl->time - ce->last_time)); + / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time)); ce->values_raw[i].counter = vl->values[i].counter; } break; @@ -531,14 +434,14 @@ int uc_update (const data_set_t *ds, const value_list_t *vl) diff = vl->values[i].derive - ce->values_raw[i].derive; ce->values_gauge[i] = ((double) diff) - / ((double) (vl->time - ce->last_time)); + / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time)); ce->values_raw[i].derive = vl->values[i].derive; } break; case DS_TYPE_ABSOLUTE: ce->values_gauge[i] = ((double) vl->values[i].absolute) - / ((double) (vl->time - ce->last_time)); + / (CDTIME_T_TO_DOUBLE (vl->time - ce->last_time)); ce->values_raw[i].absolute = vl->values[i].absolute; break; @@ -571,33 +474,11 @@ int uc_update (const data_set_t *ds, const value_list_t *vl) uc_check_range (ds, ce); ce->last_time = vl->time; - ce->last_update = time (NULL); + ce->last_update = cdtime (); ce->interval = vl->interval; pthread_mutex_unlock (&cache_lock); - if (send_okay_notification == 0) - return (0); - - /* Do not send okay notifications for uninteresting values, i. e. values for - * which no threshold is configured. */ - status = ut_check_interesting (name); - if (status <= 0) - return (0); - - /* Initialize the notification */ - memset (&n, '\0', sizeof (n)); - NOTIFICATION_INIT_VL (&n, vl, ds); - - n.severity = NOTIF_OKAY; - n.time = vl->time; - - ssnprintf (n.message, sizeof (n.message), - "Received a value for %s. It was missing for %u seconds.", - name, (unsigned int) update_delay); - - plugin_dispatch_notification (&n); - return (0); } /* int uc_update */ @@ -682,14 +563,14 @@ gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl) return (ret); } /* gauge_t *uc_get_rate */ -int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number) +int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number) { c_avl_iterator_t *iter; char *key; cache_entry_t *value; char **names = NULL; - time_t *times = NULL; + cdtime_t *times = NULL; size_t number = 0; int status = 0; @@ -710,9 +591,9 @@ int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number) if (ret_times != NULL) { - time_t *tmp_times; + cdtime_t *tmp_times; - tmp_times = (time_t *) realloc (times, sizeof (time_t) * (number + 1)); + tmp_times = (cdtime_t *) realloc (times, sizeof (cdtime_t) * (number + 1)); if (tmp_times == NULL) { status = -1; diff --git a/src/utils_cache.h b/src/utils_cache.h index f8059eca..87f93c0e 100644 --- a/src/utils_cache.h +++ b/src/utils_cache.h @@ -35,7 +35,7 @@ int uc_update (const data_set_t *ds, const value_list_t *vl); int uc_get_rate_by_name (const char *name, gauge_t **ret_values, size_t *ret_values_num); gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl); -int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number); +int uc_get_names (char ***ret_names, cdtime_t **ret_times, size_t *ret_number); int uc_get_state (const data_set_t *ds, const value_list_t *vl); int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state); diff --git a/src/utils_cmd_flush.c b/src/utils_cmd_flush.c index 0e7b350f..3584f3b7 100644 --- a/src/utils_cmd_flush.c +++ b/src/utils_cmd_flush.c @@ -54,7 +54,7 @@ int handle_flush (FILE *fh, char *buffer) int success = 0; int error = 0; - int timeout = -1; + double timeout = 0.0; char **plugins = NULL; int plugins_num = 0; char **identifiers = NULL; @@ -106,9 +106,9 @@ int handle_flush (FILE *fh, char *buffer) errno = 0; endptr = NULL; - timeout = strtol (opt_value, &endptr, 0); + timeout = strtod (opt_value, &endptr); - if ((endptr == opt_value) || (errno != 0)) + if ((endptr == opt_value) || (errno != 0) || (!isfinite (timeout))) { print_to_socket (fh, "-1 Invalid value for option `timeout': " "%s\n", opt_value); @@ -116,8 +116,10 @@ int handle_flush (FILE *fh, char *buffer) sfree (identifiers); return (-1); } - else if (timeout <= 0) - timeout = -1; + else if (timeout < 0.0) + { + timeout = 0.0; + } } else { @@ -149,7 +151,9 @@ int handle_flush (FILE *fh, char *buffer) int status; identifier = identifiers[j]; - status = plugin_flush (plugin, timeout, identifier); + status = plugin_flush (plugin, + DOUBLE_TO_CDTIME_T (timeout), + identifier); if (status == 0) success++; else diff --git a/src/utils_cmd_listval.c b/src/utils_cmd_listval.c index 4ca9646d..ef66af56 100644 --- a/src/utils_cmd_listval.c +++ b/src/utils_cmd_listval.c @@ -50,7 +50,7 @@ int handle_listval (FILE *fh, char *buffer) { char *command; char **names = NULL; - time_t *times = NULL; + cdtime_t *times = NULL; size_t number = 0; size_t i; int status; @@ -90,7 +90,8 @@ int handle_listval (FILE *fh, char *buffer) print_to_socket (fh, "%i Value%s found\n", (int) number, (number == 1) ? "" : "s"); for (i = 0; i < number; i++) - print_to_socket (fh, "%u %s\n", (unsigned int) times[i], names[i]); + print_to_socket (fh, "%.3f %s\n", CDTIME_T_TO_DOUBLE (times[i]), + names[i]); free_everything_and_return (0); } /* int handle_listval */ diff --git a/src/utils_cmd_putval.c b/src/utils_cmd_putval.c index ec2b5f87..dd43337e 100644 --- a/src/utils_cmd_putval.c +++ b/src/utils_cmd_putval.c @@ -56,16 +56,16 @@ static int set_option (value_list_t *vl, const char *key, const char *value) if (strcasecmp ("interval", key) == 0) { - int tmp; + double tmp; char *endptr; endptr = NULL; errno = 0; - tmp = strtol (value, &endptr, 0); + tmp = strtod (value, &endptr); if ((errno == 0) && (endptr != NULL) - && (endptr != value) && (tmp > 0)) - vl->interval = tmp; + && (endptr != value) && (tmp > 0.0)) + vl->interval = DOUBLE_TO_CDTIME_T (tmp); } else return (1); @@ -227,3 +227,31 @@ 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=%.3f %s", + buffer_ident, + (vl->interval > 0) + ? CDTIME_T_TO_DOUBLE (vl->interval) + : CDTIME_T_TO_DOUBLE (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 675272c0..aadf9c5e 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; @@ -84,7 +73,7 @@ struct udb_query_preparation_area_s /* {{{ */ char *plugin; char *db_name; - int interval; + cdtime_t interval; udb_result_preparation_area_t *result_prep_areas; }; /* }}} */ @@ -190,173 +179,6 @@ static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */ return (0); } /* }}} int udb_config_set_uint */ -/* - * Legacy result private functions - */ -static void udb_legacy_result_finish_result (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, - 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.plugin_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 (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 */ @@ -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 (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 (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) { @@ -1079,7 +839,7 @@ void udb_query_finish_result (udb_query_t const *q, /* {{{ */ sfree (prep_area->plugin); sfree (prep_area->db_name); - prep_area->interval = -1; + prep_area->interval = 0; for (r = q->results, r_area = prep_area->result_prep_areas; r != NULL; r = r->next, r_area = r_area->next) @@ -1147,7 +907,7 @@ int udb_query_handle_result (udb_query_t const *q, /* {{{ */ int udb_query_prepare_result (udb_query_t const *q, /* {{{ */ udb_query_preparation_area_t *prep_area, const char *host, const char *plugin, const char *db_name, - char **column_names, size_t column_num, int interval) + char **column_names, size_t column_num, cdtime_t interval) { udb_result_preparation_area_t *r_area; udb_result_t *r; diff --git a/src/utils_db_query.h b/src/utils_db_query.h index 7856bdbe..b6f4cea0 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, @@ -68,7 +68,7 @@ int udb_query_check_version (udb_query_t *q, unsigned int version); int udb_query_prepare_result (udb_query_t const *q, udb_query_preparation_area_t *prep_area, const char *host, const char *plugin, const char *db_name, - char **column_names, size_t column_num, int interval); + char **column_names, size_t column_num, cdtime_t interval); int udb_query_handle_result (udb_query_t const *q, udb_query_preparation_area_t *prep_area, char **column_values); void udb_query_finish_result (udb_query_t const *q, diff --git a/src/utils_dns.c b/src/utils_dns.c index 2899bd41..80a2ee57 100644 --- a/src/utils_dns.c +++ b/src/utils_dns.c @@ -45,6 +45,12 @@ #if HAVE_NETINET_IN_H # include #endif +#if HAVE_NETINET_IP6_H +# include +#endif +#if HAVE_NETINET_IP_COMPAT_H +# include +#endif #if HAVE_ARPA_INET_H # include #endif @@ -291,13 +297,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 +316,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 +340,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 +364,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 +398,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'))) @@ -424,6 +447,7 @@ handle_udp(const struct udphdr *udp, int len) return 1; } +#if HAVE_NETINET_IP6_H static int handle_ipv6 (struct ip6_hdr *ipv6, int len) { @@ -492,6 +516,16 @@ handle_ipv6 (struct ip6_hdr *ipv6, int len) return (1); /* Success */ } /* int handle_ipv6 */ +/* #endif HAVE_NETINET_IP6_H */ + +#else /* if !HAVE_NETINET_IP6_H */ +static int +handle_ipv6 (__attribute__((unused)) void *pkg, + __attribute__((unused)) int len) +{ + return (0); +} +#endif /* !HAVE_NETINET_IP6_H */ static int handle_ip(const struct ip *ip, int len) @@ -502,7 +536,7 @@ handle_ip(const struct ip *ip, int len) struct in6_addr c_dst_addr; if (ip->ip_v == 6) - return (handle_ipv6 ((struct ip6_hdr *) ip, len)); + return (handle_ipv6 ((void *) ip, len)); in6_addr_from_buffer (&c_src_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET); in6_addr_from_buffer (&c_dst_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET); @@ -600,7 +634,7 @@ handle_ether(const u_char * pkt, int len) return 0; memcpy(buf, pkt, len); if (ETHERTYPE_IPV6 == etype) - return (handle_ipv6 ((struct ip6_hdr *) buf, len)); + return (handle_ipv6 ((void *) buf, len)); else return handle_ip((struct ip *) buf, len); } @@ -633,7 +667,7 @@ handle_linux_sll (const u_char *pkt, int len) return 0; if (ETHERTYPE_IPV6 == etype) - return (handle_ipv6 ((struct ip6_hdr *) pkt, len)); + return (handle_ipv6 ((void *) pkt, len)); else return handle_ip((struct ip *) pkt, len); } @@ -644,10 +678,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; @@ -681,7 +711,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_format_json.c b/src/utils_format_json.c index ac88c0fa..2a5526b2 100644 --- a/src/utils_format_json.c +++ b/src/utils_format_json.c @@ -264,8 +264,8 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */ return (status); BUFFER_ADD (",\"dsnames\":%s", temp); - BUFFER_ADD (",\"time\":%lu", (unsigned long) vl->time); - BUFFER_ADD (",\"interval\":%i", vl->interval); + BUFFER_ADD (",\"time\":%.3f", CDTIME_T_TO_DOUBLE (vl->time)); + BUFFER_ADD (",\"interval\":%.3f", CDTIME_T_TO_DOUBLE (vl->interval)); #define BUFFER_ADD_KEYVAL(key, value) do { \ status = escape_string (temp, sizeof (temp), (value)); \ diff --git a/src/utils_rrdcreate.c b/src/utils_rrdcreate.c index 47bf9f66..091b5fa6 100644 --- a/src/utils_rrdcreate.c +++ b/src/utils_rrdcreate.c @@ -103,7 +103,10 @@ static int rra_get (char ***ret, const value_list_t *vl, /* {{{ */ return (-1); } - ss = (cfg->stepsize > 0) ? cfg->stepsize : vl->interval; + if (cfg->stepsize > 0) + ss = cfg->stepsize; + else + ss = (int) CDTIME_T_TO_TIME_T (vl->interval); if (ss <= 0) { *ret = NULL; @@ -241,7 +244,9 @@ static int ds_get (char ***ret, /* {{{ */ status = ssnprintf (buffer, sizeof (buffer), "DS:%s:%s:%i:%s:%s", d->name, type, - (cfg->heartbeat > 0) ? cfg->heartbeat : (2 * vl->interval), + (cfg->heartbeat > 0) + ? cfg->heartbeat + : (int) CDTIME_T_TO_TIME_T (2 * vl->interval), min, max); if ((status < 1) || ((size_t) status >= sizeof (buffer))) break; @@ -323,7 +328,7 @@ static int srrd_create (const char *filename, /* {{{ */ last_up = time (NULL) - 10; ssnprintf (pdp_step_str, sizeof (pdp_step_str), "%lu", pdp_step); - ssnprintf (last_up_str, sizeof (last_up_str), "%u", (unsigned int) last_up); + ssnprintf (last_up_str, sizeof (last_up_str), "%lu", (unsigned long) last_up); new_argv[0] = "create"; new_argv[1] = (void *) filename; @@ -368,6 +373,8 @@ int cu_rrd_create_file (const char *filename, /* {{{ */ char **ds_def; int ds_num; int status = 0; + time_t last_up; + unsigned long stepsize; if (check_create_dir (filename)) return (-1); @@ -398,9 +405,17 @@ int cu_rrd_create_file (const char *filename, /* {{{ */ memcpy (argv + ds_num, rra_def, rra_num * sizeof (char *)); argv[ds_num + rra_num] = NULL; - status = srrd_create (filename, - (cfg->stepsize > 0) ? cfg->stepsize : vl->interval, - (vl->time > 10) ? (vl->time - 10) : vl->time, + last_up = CDTIME_T_TO_TIME_T (vl->time); + if (last_up <= 0) + last_up = time (NULL); + last_up -= 1; + + if (cfg->stepsize > 0) + stepsize = cfg->stepsize; + else + stepsize = (unsigned long) CDTIME_T_TO_TIME_T (vl->interval); + + status = srrd_create (filename, stepsize, last_up, argc, (const char **) argv); free (argv); diff --git a/src/utils_rrdcreate.h b/src/utils_rrdcreate.h index 935e4e07..103ca570 100644 --- a/src/utils_rrdcreate.h +++ b/src/utils_rrdcreate.h @@ -28,7 +28,7 @@ struct rrdcreate_config_s { - int stepsize; + unsigned long stepsize; int heartbeat; int rrarows; double xff; diff --git a/src/utils_threshold.c b/src/utils_threshold.c deleted file mode 100644 index b14c79b5..00000000 --- a/src/utils_threshold.c +++ /dev/null @@ -1,1080 +0,0 @@ -/** - * collectd - src/utils_threshold.c - * Copyright (C) 2007-2009 Florian octo Forster - * Copyright (C) 2008-2009 Sebastian Harl - * Copyright (C) 2009 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; 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 - * - * Author: - * Florian octo Forster - * Sebastian Harl - * Andrés J. Díaz - **/ - -#include "collectd.h" -#include "common.h" -#include "plugin.h" -#include "utils_avltree.h" -#include "utils_cache.h" -#include "utils_threshold.h" - -#include -#include - -/* - * Private data structures - * {{{ */ -#define UT_FLAG_INVERT 0x01 -#define UT_FLAG_PERSIST 0x02 -#define UT_FLAG_PERCENTAGE 0x04 -/* }}} */ - -/* - * Private (static) variables - * {{{ */ -static c_avl_tree_t *threshold_tree = NULL; -static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER; -/* }}} */ - -/* - * Threshold management - * ==================== - * The following functions add, delete, search, etc. configured thresholds to - * the underlying AVL trees. - * {{{ */ -static threshold_t *threshold_get (const char *hostname, - const char *plugin, const char *plugin_instance, - const char *type, const char *type_instance) -{ - char name[6 * DATA_MAX_NAME_LEN]; - threshold_t *th = NULL; - - format_name (name, sizeof (name), - (hostname == NULL) ? "" : hostname, - (plugin == NULL) ? "" : plugin, plugin_instance, - (type == NULL) ? "" : type, type_instance); - name[sizeof (name) - 1] = '\0'; - - if (c_avl_get (threshold_tree, name, (void *) &th) == 0) - return (th); - else - return (NULL); -} /* threshold_t *threshold_get */ - -static int ut_threshold_add (const threshold_t *th) -{ - char name[6 * DATA_MAX_NAME_LEN]; - char *name_copy; - threshold_t *th_copy; - threshold_t *th_ptr; - int status = 0; - - if (format_name (name, sizeof (name), th->host, - th->plugin, th->plugin_instance, - th->type, th->type_instance) != 0) - { - ERROR ("ut_threshold_add: format_name failed."); - return (-1); - } - - name_copy = strdup (name); - if (name_copy == NULL) - { - ERROR ("ut_threshold_add: strdup failed."); - return (-1); - } - - th_copy = (threshold_t *) malloc (sizeof (threshold_t)); - if (th_copy == NULL) - { - sfree (name_copy); - ERROR ("ut_threshold_add: malloc failed."); - return (-1); - } - memcpy (th_copy, th, sizeof (threshold_t)); - th_ptr = NULL; - - DEBUG ("ut_threshold_add: Adding entry `%s'", name); - - pthread_mutex_lock (&threshold_lock); - - th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance, - th->type, th->type_instance); - - while ((th_ptr != NULL) && (th_ptr->next != NULL)) - th_ptr = th_ptr->next; - - if (th_ptr == NULL) /* no such threshold yet */ - { - status = c_avl_insert (threshold_tree, name_copy, th_copy); - } - else /* th_ptr points to the last threshold in the list */ - { - th_ptr->next = th_copy; - /* name_copy isn't needed */ - sfree (name_copy); - } - - pthread_mutex_unlock (&threshold_lock); - - if (status != 0) - { - ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name); - sfree (name_copy); - sfree (th_copy); - } - - return (status); -} /* int ut_threshold_add */ -/* - * End of the threshold management functions - * }}} */ - -/* - * Configuration - * ============= - * The following approximately two hundred functions are used to handle the - * configuration and fill the threshold list. - * {{{ */ -static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("threshold values: The `DataSource' option needs exactly one " - "string argument."); - return (-1); - } - - sstrncpy (th->data_source, ci->values[0].value.string, - sizeof (th->data_source)); - - return (0); -} /* int ut_config_type_datasource */ - -static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("threshold values: The `Instance' option needs exactly one " - "string argument."); - return (-1); - } - - sstrncpy (th->type_instance, ci->values[0].value.string, - sizeof (th->type_instance)); - - return (0); -} /* int ut_config_type_instance */ - -static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) - { - WARNING ("threshold values: The `%s' option needs exactly one " - "number argument.", ci->key); - return (-1); - } - - if (strcasecmp (ci->key, "WarningMax") == 0) - th->warning_max = ci->values[0].value.number; - else - th->failure_max = ci->values[0].value.number; - - return (0); -} /* int ut_config_type_max */ - -static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) - { - WARNING ("threshold values: The `%s' option needs exactly one " - "number argument.", ci->key); - return (-1); - } - - if (strcasecmp (ci->key, "WarningMin") == 0) - th->warning_min = ci->values[0].value.number; - else - th->failure_min = ci->values[0].value.number; - - 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) - || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) - { - WARNING ("threshold values: The `%s' option needs exactly one " - "number argument.", ci->key); - return (-1); - } - - th->hits = ci->values[0].value.number; - - return (0); -} /* int ut_config_type_hits */ - -static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_NUMBER)) - { - WARNING ("threshold values: The `%s' option needs exactly one " - "number argument.", ci->key); - return (-1); - } - - th->hysteresis = ci->values[0].value.number; - - return (0); -} /* int ut_config_type_hysteresis */ - -static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci) -{ - int i; - threshold_t th; - int status = 0; - - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("threshold values: The `Type' block needs exactly one string " - "argument."); - return (-1); - } - - if (ci->children_num < 1) - { - WARNING ("threshold values: The `Type' block needs at least one option."); - return (-1); - } - - memcpy (&th, th_orig, sizeof (th)); - sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type)); - - th.warning_min = NAN; - th.warning_max = NAN; - th.failure_min = NAN; - th.failure_max = NAN; - th.hits = 0; - th.hysteresis = 0; - - for (i = 0; i < ci->children_num; i++) - { - oconfig_item_t *option = ci->children + i; - status = 0; - - if (strcasecmp ("Instance", option->key) == 0) - status = ut_config_type_instance (&th, option); - else if (strcasecmp ("DataSource", option->key) == 0) - status = ut_config_type_datasource (&th, option); - else if ((strcasecmp ("WarningMax", option->key) == 0) - || (strcasecmp ("FailureMax", option->key) == 0)) - status = ut_config_type_max (&th, option); - else if ((strcasecmp ("WarningMin", option->key) == 0) - || (strcasecmp ("FailureMin", option->key) == 0)) - status = ut_config_type_min (&th, option); - else if (strcasecmp ("Invert", option->key) == 0) - status = ut_config_type_invert (&th, option); - else if (strcasecmp ("Persist", option->key) == 0) - status = ut_config_type_persist (&th, option); - else if (strcasecmp ("Percentage", option->key) == 0) - status = ut_config_type_percentage (&th, option); - else if (strcasecmp ("Hits", option->key) == 0) - status = ut_config_type_hits (&th, option); - else if (strcasecmp ("Hysteresis", option->key) == 0) - status = ut_config_type_hysteresis (&th, option); - else - { - WARNING ("threshold values: Option `%s' not allowed inside a `Type' " - "block.", option->key); - status = -1; - } - - if (status != 0) - break; - } - - if (status == 0) - { - status = ut_threshold_add (&th); - } - - return (status); -} /* int ut_config_type */ - -static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci) -{ - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("threshold values: The `Instance' option needs exactly one " - "string argument."); - return (-1); - } - - sstrncpy (th->plugin_instance, ci->values[0].value.string, - sizeof (th->plugin_instance)); - - return (0); -} /* int ut_config_plugin_instance */ - -static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci) -{ - int i; - threshold_t th; - int status = 0; - - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("threshold values: The `Plugin' block needs exactly one string " - "argument."); - return (-1); - } - - if (ci->children_num < 1) - { - WARNING ("threshold values: The `Plugin' block needs at least one nested " - "block."); - return (-1); - } - - memcpy (&th, th_orig, sizeof (th)); - sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin)); - - for (i = 0; i < ci->children_num; i++) - { - oconfig_item_t *option = ci->children + i; - status = 0; - - if (strcasecmp ("Type", option->key) == 0) - status = ut_config_type (&th, option); - else if (strcasecmp ("Instance", option->key) == 0) - status = ut_config_plugin_instance (&th, option); - else - { - WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' " - "block.", option->key); - status = -1; - } - - if (status != 0) - break; - } - - return (status); -} /* int ut_config_plugin */ - -static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci) -{ - int i; - threshold_t th; - int status = 0; - - if ((ci->values_num != 1) - || (ci->values[0].type != OCONFIG_TYPE_STRING)) - { - WARNING ("threshold values: The `Host' block needs exactly one string " - "argument."); - return (-1); - } - - if (ci->children_num < 1) - { - WARNING ("threshold values: The `Host' block needs at least one nested " - "block."); - return (-1); - } - - memcpy (&th, th_orig, sizeof (th)); - sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host)); - - for (i = 0; i < ci->children_num; i++) - { - oconfig_item_t *option = ci->children + i; - status = 0; - - if (strcasecmp ("Type", option->key) == 0) - status = ut_config_type (&th, option); - else if (strcasecmp ("Plugin", option->key) == 0) - status = ut_config_plugin (&th, option); - else - { - WARNING ("threshold values: Option `%s' not allowed inside a `Host' " - "block.", option->key); - status = -1; - } - - if (status != 0) - break; - } - - return (status); -} /* int ut_config_host */ - -int ut_config (const oconfig_item_t *ci) -{ - int i; - int status = 0; - - threshold_t th; - - if (ci->values_num != 0) - { - ERROR ("threshold values: The `Threshold' block may not have any " - "arguments."); - return (-1); - } - - if (threshold_tree == NULL) - { - threshold_tree = c_avl_create ((void *) strcmp); - if (threshold_tree == NULL) - { - ERROR ("ut_config: c_avl_create failed."); - return (-1); - } - } - - memset (&th, '\0', sizeof (th)); - th.warning_min = NAN; - th.warning_max = NAN; - th.failure_min = NAN; - th.failure_max = NAN; - - th.hits = 0; - th.hysteresis = 0; - - for (i = 0; i < ci->children_num; i++) - { - oconfig_item_t *option = ci->children + i; - status = 0; - - if (strcasecmp ("Type", option->key) == 0) - status = ut_config_type (&th, option); - else if (strcasecmp ("Plugin", option->key) == 0) - status = ut_config_plugin (&th, option); - else if (strcasecmp ("Host", option->key) == 0) - status = ut_config_host (&th, option); - else - { - WARNING ("threshold values: Option `%s' not allowed here.", option->key); - status = -1; - } - - if (status != 0) - break; - } - - return (status); -} /* int um_config */ -/* - * End of the functions used to configure threshold values. - */ -/* }}} */ - -static threshold_t *threshold_search (const value_list_t *vl) -{ - threshold_t *th; - - if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance, - vl->type, vl->type_instance)) != NULL) - return (th); - else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance, - vl->type, NULL)) != NULL) - return (th); - else if ((th = threshold_get (vl->host, vl->plugin, NULL, - vl->type, vl->type_instance)) != NULL) - return (th); - else if ((th = threshold_get (vl->host, vl->plugin, NULL, - vl->type, NULL)) != NULL) - return (th); - else if ((th = threshold_get (vl->host, "", NULL, - vl->type, vl->type_instance)) != NULL) - return (th); - else if ((th = threshold_get (vl->host, "", NULL, - vl->type, NULL)) != NULL) - return (th); - else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance, - vl->type, vl->type_instance)) != NULL) - return (th); - else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance, - vl->type, NULL)) != NULL) - return (th); - else if ((th = threshold_get ("", vl->plugin, NULL, - vl->type, vl->type_instance)) != NULL) - return (th); - else if ((th = threshold_get ("", vl->plugin, NULL, - vl->type, NULL)) != NULL) - return (th); - else if ((th = threshold_get ("", "", NULL, - vl->type, vl->type_instance)) != NULL) - return (th); - else if ((th = threshold_get ("", "", NULL, - vl->type, NULL)) != NULL) - return (th); - - return (NULL); -} /* threshold_t *threshold_search */ - -/* - * int ut_report_state - * - * Checks if the `state' differs from the old state and creates a notification - * if appropriate. - * Does not fail. - */ -static int ut_report_state (const data_set_t *ds, - const value_list_t *vl, - const threshold_t *th, - const gauge_t *values, - int ds_index, - int state) -{ /* {{{ */ - int state_old; - notification_t n; - - char *buf; - size_t bufsize; - - int status; - - /* Check if hits matched */ - if ( (th->hits != 0) ) - { - int hits = uc_get_hits(ds,vl); - /* The STATE_OKAY always reset hits, or if hits reaise the limit */ - if ( (state == STATE_OKAY) || (hits > th->hits) ) - { - DEBUG("ut_report_state: reset uc_get_hits = 0"); - uc_set_hits(ds,vl,0); /* reset hit counter and notify */ - } else { - DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl)); - (void) uc_inc_hits(ds,vl,1); /* increase hit counter */ - return (0); - } - } /* end check hits */ - - state_old = uc_get_state (ds, vl); - - /* If the state didn't change, only report if `persistent' is specified and - * the state is not `okay'. */ - if (state == state_old) - { - if ((th->flags & UT_FLAG_PERSIST) == 0) - return (0); - else if (state == STATE_OKAY) - return (0); - } - - if (state != state_old) - uc_set_state (ds, vl, state); - - NOTIFICATION_INIT_VL (&n, vl, ds); - - buf = n.message; - bufsize = sizeof (n.message); - - if (state == STATE_OKAY) - n.severity = NOTIF_OKAY; - else if (state == STATE_WARNING) - n.severity = NOTIF_WARNING; - else - n.severity = NOTIF_FAILURE; - - n.time = vl->time; - - status = ssnprintf (buf, bufsize, "Host %s, plugin %s", - vl->host, vl->plugin); - buf += status; - bufsize -= status; - - if (vl->plugin_instance[0] != '\0') - { - status = ssnprintf (buf, bufsize, " (instance %s)", - vl->plugin_instance); - buf += status; - bufsize -= status; - } - - status = ssnprintf (buf, bufsize, " type %s", vl->type); - buf += status; - bufsize -= status; - - if (vl->type_instance[0] != '\0') - { - status = ssnprintf (buf, bufsize, " (instance %s)", - vl->type_instance); - buf += status; - bufsize -= status; - } - - plugin_notification_meta_add_string (&n, "DataSource", - ds->ds[ds_index].name); - plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]); - plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min); - plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max); - plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min); - plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max); - - /* Send an okay notification */ - if (state == STATE_OKAY) - { - status = ssnprintf (buf, bufsize, ": All data sources are within range again."); - buf += status; - bufsize -= status; - } - else - { - double min; - double max; - - min = (state == STATE_ERROR) ? th->failure_min : th->warning_min; - max = (state == STATE_ERROR) ? th->failure_max : th->warning_max; - - if (th->flags & UT_FLAG_INVERT) - { - if (!isnan (min) && !isnan (max)) - { - status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently " - "%f. That is within the %s region of %f%s and %f%s.", - ds->ds[ds_index].name, values[ds_index], - (state == STATE_ERROR) ? "failure" : "warning", - min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "", - max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : ""); - } - else - { - status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently " - "%f. That is %s the %s threshold of %f%s.", - ds->ds[ds_index].name, values[ds_index], - isnan (min) ? "below" : "above", - (state == STATE_ERROR) ? "failure" : "warning", - isnan (min) ? max : min, - ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : ""); - } - } - else if (th->flags & UT_FLAG_PERCENTAGE) - { - gauge_t value; - gauge_t sum; - int i; - - sum = 0.0; - for (i = 0; i < vl->values_len; i++) - { - if (isnan (values[i])) - continue; - - sum += values[i]; - } - - if (sum == 0.0) - value = NAN; - else - value = 100.0 * values[ds_index] / sum; - - status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently " - "%g (%.2f%%). That is %s the %s threshold of %.2f%%.", - ds->ds[ds_index].name, values[ds_index], value, - (value < min) ? "below" : "above", - (state == STATE_ERROR) ? "failure" : "warning", - (value < min) ? min : max); - } - else /* is not inverted */ - { - status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently " - "%f. That is %s the %s threshold of %f.", - ds->ds[ds_index].name, values[ds_index], - (values[ds_index] < min) ? "below" : "above", - (state == STATE_ERROR) ? "failure" : "warning", - (values[ds_index] < min) ? min : max); - } - buf += status; - bufsize -= status; - } - - plugin_dispatch_notification (&n); - - plugin_notification_meta_free (n.meta); - return (0); -} /* }}} int ut_report_state */ - -/* - * int ut_check_one_data_source - * - * Checks one data source against the given threshold configuration. If the - * `DataSource' option is set in the threshold, and the name does NOT match, - * `okay' is returned. If the threshold does match, its failure and warning - * min and max values are checked and `failure' or `warning' is returned if - * appropriate. - * Does not fail. - */ -static int ut_check_one_data_source (const data_set_t *ds, - const value_list_t __attribute__((unused)) *vl, - const threshold_t *th, - const gauge_t *values, - int ds_index) -{ /* {{{ */ - const char *ds_name; - int is_warning = 0; - int is_failure = 0; - int prev_state = STATE_OKAY; - - /* check if this threshold applies to this data source */ - if (ds != NULL) - { - ds_name = ds->ds[ds_index].name; - if ((th->data_source[0] != 0) - && (strcmp (ds_name, th->data_source) != 0)) - return (STATE_OKAY); - } - - if ((th->flags & UT_FLAG_INVERT) != 0) - { - is_warning--; - is_failure--; - } - - /* XXX: This is an experimental code, not optimized, not fast, not reliable, - * and probably, do not work as you expect. Enjoy! :D */ - if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) ) - { - switch(prev_state) - { - case STATE_ERROR: - if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) || - (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) ) - return (STATE_OKAY); - else - is_failure++; - case STATE_WARNING: - if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) || - (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) ) - return (STATE_OKAY); - else - is_warning++; - } - } - else { /* no hysteresis */ - if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index])) - || (!isnan (th->failure_max) && (th->failure_max < values[ds_index]))) - is_failure++; - - if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index])) - || (!isnan (th->warning_max) && (th->warning_max < values[ds_index]))) - is_warning++; - } - - if (is_failure != 0) - return (STATE_ERROR); - - if (is_warning != 0) - return (STATE_WARNING); - - return (STATE_OKAY); -} /* }}} int ut_check_one_data_source */ - -/* - * int ut_check_one_threshold - * - * Checks all data sources of a value list against the given threshold, using - * the ut_check_one_data_source function above. Returns the worst status, - * which is `okay' if nothing has failed. - * Returns less than zero if the data set doesn't have any data sources. - */ -static int ut_check_one_threshold (const data_set_t *ds, - const value_list_t *vl, - const threshold_t *th, - const gauge_t *values, - int *ret_ds_index) -{ /* {{{ */ - int ret = -1; - int ds_index = -1; - int i; - gauge_t values_copy[ds->ds_num]; - - memcpy (values_copy, values, sizeof (values_copy)); - - if ((th->flags & UT_FLAG_PERCENTAGE) != 0) - { - int num = 0; - gauge_t sum=0.0; - - if (ds->ds_num == 1) - { - WARNING ("ut_check_one_threshold: The %s type has only one data " - "source, but you have configured to check this as a percentage. " - "That doesn't make much sense, because the percentage will always " - "be 100%%!", ds->type); - } - - /* Prepare `sum' and `num'. */ - for (i = 0; i < ds->ds_num; i++) - if (!isnan (values[i])) - { - num++; - sum += values[i]; - } - - if ((num == 0) /* All data sources are undefined. */ - || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */ - { - for (i = 0; i < ds->ds_num; i++) - values_copy[i] = NAN; - } - else /* We can actually calculate the percentage. */ - { - for (i = 0; i < ds->ds_num; i++) - values_copy[i] = 100.0 * values[i] / sum; - } - } /* if (UT_FLAG_PERCENTAGE) */ - - for (i = 0; i < ds->ds_num; i++) - { - int status; - - status = ut_check_one_data_source (ds, vl, th, values_copy, i); - if (ret < status) - { - ret = status; - ds_index = i; - } - } /* for (ds->ds_num) */ - - if (ret_ds_index != NULL) - *ret_ds_index = ds_index; - - return (ret); -} /* }}} int ut_check_one_threshold */ - -/* - * int ut_check_threshold (PUBLIC) - * - * Gets a list of matching thresholds and searches for the worst status by one - * of the thresholds. Then reports that status using the ut_report_state - * function above. - * Returns zero on success and if no threshold has been configured. Returns - * less than zero on failure. - */ -int ut_check_threshold (const data_set_t *ds, const value_list_t *vl) -{ /* {{{ */ - threshold_t *th; - gauge_t *values; - int status; - - int worst_state = -1; - threshold_t *worst_th = NULL; - int worst_ds_index = -1; - - if (threshold_tree == NULL) - return (0); - - /* Is this lock really necessary? So far, thresholds are only inserted at - * startup. -octo */ - pthread_mutex_lock (&threshold_lock); - th = threshold_search (vl); - pthread_mutex_unlock (&threshold_lock); - if (th == NULL) - return (0); - - DEBUG ("ut_check_threshold: Found matching threshold(s)"); - - values = uc_get_rate (ds, vl); - if (values == NULL) - return (0); - - while (th != NULL) - { - int ds_index = -1; - - status = ut_check_one_threshold (ds, vl, th, values, &ds_index); - if (status < 0) - { - ERROR ("ut_check_threshold: ut_check_one_threshold failed."); - sfree (values); - return (-1); - } - - if (worst_state < status) - { - worst_state = status; - worst_th = th; - worst_ds_index = ds_index; - } - - th = th->next; - } /* while (th) */ - - status = ut_report_state (ds, vl, worst_th, values, - worst_ds_index, worst_state); - if (status != 0) - { - ERROR ("ut_check_threshold: ut_report_state failed."); - sfree (values); - return (-1); - } - - sfree (values); - - return (0); -} /* }}} int ut_check_threshold */ - -/* - * int ut_check_interesting (PUBLIC) - * - * Given an identification returns - * 0: No threshold is defined. - * 1: A threshold has been found. The flag `persist' is off. - * 2: A threshold has been found. The flag `persist' is on. - * (That is, it is expected that many notifications are sent until the - * problem disappears.) - */ -int ut_check_interesting (const char *name) -{ /* {{{ */ - char *name_copy = NULL; - char *host = NULL; - char *plugin = NULL; - char *plugin_instance = NULL; - char *type = NULL; - char *type_instance = NULL; - int status; - data_set_t ds; - value_list_t vl; - threshold_t *th; - - /* If there is no tree nothing is interesting. */ - if (threshold_tree == NULL) - return (0); - - name_copy = strdup (name); - if (name_copy == NULL) - { - ERROR ("ut_check_interesting: strdup failed."); - return (-1); - } - - status = parse_identifier (name_copy, &host, - &plugin, &plugin_instance, &type, &type_instance); - if (status != 0) - { - ERROR ("ut_check_interesting: parse_identifier failed."); - sfree (name_copy); - return (-1); - } - - memset (&ds, '\0', sizeof (ds)); - memset (&vl, '\0', sizeof (vl)); - - sstrncpy (vl.host, host, sizeof (vl.host)); - sstrncpy (vl.plugin, plugin, sizeof (vl.plugin)); - if (plugin_instance != NULL) - sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); - sstrncpy (ds.type, type, sizeof (ds.type)); - sstrncpy (vl.type, type, sizeof (vl.type)); - if (type_instance != NULL) - sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); - - sfree (name_copy); - host = plugin = plugin_instance = type = type_instance = NULL; - - th = threshold_search (&vl); - if (th == NULL) - return (0); - if ((th->flags & UT_FLAG_PERSIST) == 0) - return (1); - return (2); -} /* }}} int ut_check_interesting */ - -int ut_search_threshold (const value_list_t *vl, /* {{{ */ - threshold_t *ret_threshold) -{ - threshold_t *t; - - if (vl == NULL) - return (EINVAL); - - t = threshold_search (vl); - if (t == NULL) - return (ENOENT); - - memcpy (ret_threshold, t, sizeof (*ret_threshold)); - ret_threshold->next = NULL; - - return (0); -} /* }}} int ut_search_threshold */ - -/* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */ diff --git a/src/utils_threshold.h b/src/utils_threshold.h deleted file mode 100644 index 8aaf34c6..00000000 --- a/src/utils_threshold.h +++ /dev/null @@ -1,87 +0,0 @@ -/** - * collectd - src/utils_threshold.h - * Copyright (C) 2007-2009 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 - * 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 - * - * Author: - * Florian octo Forster - **/ - -#ifndef UTILS_THRESHOLD_H -#define UTILS_THRESHOLD_H 1 - -#include "collectd.h" -#include "liboconfig/oconfig.h" -#include "plugin.h" - -typedef struct threshold_s -{ - char host[DATA_MAX_NAME_LEN]; - char plugin[DATA_MAX_NAME_LEN]; - char plugin_instance[DATA_MAX_NAME_LEN]; - char type[DATA_MAX_NAME_LEN]; - char type_instance[DATA_MAX_NAME_LEN]; - char data_source[DATA_MAX_NAME_LEN]; - gauge_t warning_min; - gauge_t warning_max; - gauge_t failure_min; - gauge_t failure_max; - gauge_t hysteresis; - int flags; - int hits; - struct threshold_s *next; -} threshold_t; - -/* - * ut_config - * - * Parses the configuration and sets up the module. This is called from - * `src/configfile.c'. - */ -int ut_config (const oconfig_item_t *ci); - -/* - * ut_check_threshold - * - * Checks if a threshold is defined for this value and if such a threshold is - * configured, check if the value within the acceptable range. If it is not, a - * notification is dispatched to inform the user that a problem exists. This is - * called from `plugin_read_all'. - */ -int ut_check_threshold (const data_set_t *ds, const value_list_t *vl); - -/* - * Given an identification returns - * 0: No threshold is defined. - * 1: A threshold has been found. The flag `persist' is off. - * 2: A threshold has been found. The flag `persist' is on. - * (That is, it is expected that many notifications are sent until the - * problem disappears.) - */ -int ut_check_interesting (const char *name); - -/* - * Given an identifier in form of a `value_list_t', searches for the best - * matching threshold configuration. `ret_threshold' may be NULL. - * - * Returns: - * 0: Success. Threshold configuration has been copied to - * `ret_threshold' (if it is non-NULL). - * ENOENT: No configuration for this identifier found. - * else: Error. - */ -int ut_search_threshold (const value_list_t *vl, threshold_t *ret_threshold); - -#endif /* UTILS_THRESHOLD_H */ diff --git a/src/utils_time.c b/src/utils_time.c new file mode 100644 index 00000000..aac6135e --- /dev/null +++ b/src/utils_time.c @@ -0,0 +1,64 @@ +/** + * collectd - src/utils_time.h + * Copyright (C) 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 + * 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: + * Florian octo Forster + **/ + +#include "collectd.h" +#include "utils_time.h" +#include "plugin.h" +#include "common.h" + +#if HAVE_CLOCK_GETTIME +cdtime_t cdtime (void) /* {{{ */ +{ + int status; + struct timespec ts = { 0, 0 }; + + status = clock_gettime (CLOCK_REALTIME, &ts); + if (status != 0) + { + char errbuf[1024]; + ERROR ("cdtime: clock_gettime failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (0); + } + + return (TIMESPEC_TO_CDTIME_T (&ts)); +} /* }}} cdtime_t cdtime */ +#else +/* Work around for Mac OS X which doesn't have clock_gettime(2). *sigh* */ +cdtime_t cdtime (void) /* {{{ */ +{ + int status; + struct timeval tv = { 0, 0 }; + + status = gettimeofday (&tv, /* struct timezone = */ NULL); + if (status != 0) + { + char errbuf[1024]; + ERROR ("cdtime: gettimeofday failed: %s", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (0); + } + + return (TIMEVAL_TO_CDTIME_T (&tv)); +} /* }}} cdtime_t cdtime */ +#endif + +/* vim: set sw=2 sts=2 et fdm=marker : */ diff --git a/src/utils_time.h b/src/utils_time.h new file mode 100644 index 00000000..0fd809ac --- /dev/null +++ b/src/utils_time.h @@ -0,0 +1,70 @@ +/** + * collectd - src/utils_time.h + * Copyright (C) 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 + * 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: + * Florian octo Forster + **/ + +#ifndef UTILS_TIME_H +#define UTILS_TIME_H 1 + +#include "collectd.h" + +/* + * "cdtime_t" is a 64bit unsigned integer. The time is stored at a 2^-30 second + * resolution, i.e. the most significant 34 bit are used to store the time in + * seconds, the least significant bits store the sub-second part in something + * very close to nanoseconds. *The* big advantage of storing time in this + * manner is that comparing times and calculating differences is as simple as + * it is with "time_t", i.e. a simple integer comparison / subtraction works. + */ +/* + * cdtime_t is defined in "collectd.h" */ +/* typedef uint64_t cdtime_t; */ + +/* 2^30 = 1073741824 */ +#define TIME_T_TO_CDTIME_T(t) (((cdtime_t) (t)) * 1073741824) +#define CDTIME_T_TO_TIME_T(t) ((time_t) ((t) / 1073741824)) + +#define CDTIME_T_TO_DOUBLE(t) (((double) (t)) / 1073741824.0) +#define DOUBLE_TO_CDTIME_T(d) ((cdtime_t) ((d) * 1073741824.0)) + +#define MS_TO_CDTIME_T(ms) ((cdtime_t) (((double) (ms)) * 1073741.824)) +#define CDTIME_T_TO_MS(t) ((long) (((double) (t)) / 1073741.824)) +#define US_TO_CDTIME_T(us) ((cdtime_t) (((double) (us)) * 1073.741824)) +#define CDTIME_T_TO_US(t) ((suseconds_t) (((double) (t)) / 1073.741824)) +#define NS_TO_CDTIME_T(ns) ((cdtime_t) (((double) (ns)) * 1.073741824)) +#define CDTIME_T_TO_NS(t) ((long) (((double) (t)) / 1.073741824)) + +#define CDTIME_T_TO_TIMEVAL(cdt,tvp) do { \ + (tvp)->tv_sec = CDTIME_T_TO_TIME_T (cdt); \ + (tvp)->tv_usec = CDTIME_T_TO_US ((cdt) % 1073741824); \ +} while (0) +#define TIMEVAL_TO_CDTIME_T(tv) (TIME_T_TO_CDTIME_T ((tv)->tv_sec) \ + + US_TO_CDTIME_T ((tv)->tv_usec)) + +#define CDTIME_T_TO_TIMESPEC(cdt,tsp) do { \ + (tsp)->tv_sec = CDTIME_T_TO_TIME_T (cdt); \ + (tsp)->tv_nsec = CDTIME_T_TO_NS ((cdt) % 1073741824); \ +} while (0) +#define TIMESPEC_TO_CDTIME_T(ts) (TIME_T_TO_CDTIME_T ((ts)->tv_sec) \ + + NS_TO_CDTIME_T ((ts)->tv_nsec)) + +cdtime_t cdtime (void); + +#endif /* UTILS_TIME_H */ +/* vim: set sw=2 sts=2 et : */ diff --git a/src/varnish.c b/src/varnish.c new file mode 100644 index 00000000..37fd4bb3 --- /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_derive (conf->instance, "sm", "total_bytes", "allocated", VSL_stats->sm_balloc); + /* bytes free */ + varnish_submit_derive (conf->instance, "sm", "total_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_derive (conf->instance, "sma", "total_bytes", "allocated", VSL_stats->sma_balloc); + /* SMA bytes free */ + varnish_submit_derive (conf->instance, "sma", "total_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_derive (conf->instance, "sms", "total_bytes", "allocated", VSL_stats->sms_balloc); + /* SMS bytes freed */ + varnish_submit_derive (conf->instance, "sms", "total_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_derive (conf->instance, "workers", "total_threads", "created", VSL_stats->n_wrk_create); + /* worker threads not created */ + varnish_submit_derive (conf->instance, "workers", "total_threads", "failed", VSL_stats->n_wrk_failed); + /* worker threads limited */ + varnish_submit_derive (conf->instance, "workers", "total_threads", "limited", VSL_stats->n_wrk_max); + /* queued work requests */ + varnish_submit_derive (conf->instance, "workers", "total_requests", "queued", VSL_stats->n_wrk_queue); + /* overflowed work requests */ + varnish_submit_derive (conf->instance, "workers", "total_requests", "overflowed", VSL_stats->n_wrk_overflow); + /* dropped work requests */ + varnish_submit_derive (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/vmem.c b/src/vmem.c index d32f1db8..56997bf1 100644 --- a/src/vmem.c +++ b/src/vmem.c @@ -1,6 +1,6 @@ /** * collectd - src/vmem.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 @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Florian octo Forster + * Florian octo Forster **/ #include "collectd.h" @@ -57,12 +57,12 @@ static void submit (const char *plugin_instance, const char *type, } /* void vmem_submit */ static void submit_two (const char *plugin_instance, const char *type, - const char *type_instance, counter_t c0, counter_t c1) + const char *type_instance, derive_t c0, derive_t c1) { value_t values[2]; - values[0].counter = c0; - values[1].counter = c1; + values[0].derive = c0; + values[1].derive = c1; submit (plugin_instance, type, type_instance, values, 2); } /* void submit_one */ @@ -93,16 +93,16 @@ static int vmem_config (const char *key, const char *value) static int vmem_read (void) { #if KERNEL_LINUX - counter_t pgpgin = 0; - counter_t pgpgout = 0; + derive_t pgpgin = 0; + derive_t pgpgout = 0; int pgpgvalid = 0; - counter_t pswpin = 0; - counter_t pswpout = 0; + derive_t pswpin = 0; + derive_t pswpout = 0; int pswpvalid = 0; - counter_t pgfault = 0; - counter_t pgmajfault = 0; + derive_t pgfault = 0; + derive_t pgmajfault = 0; int pgfaultvalid = 0; FILE *fh; @@ -123,7 +123,7 @@ static int vmem_read (void) int fields_num; char *key; char *endptr; - counter_t counter; + derive_t counter; gauge_t gauge; fields_num = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields)); @@ -205,31 +205,31 @@ static int vmem_read (void) else if (strncmp ("pgalloc_", key, strlen ("pgalloc_")) == 0) { char *inst = key + strlen ("pgalloc_"); - value_t value = { .counter = counter }; + value_t value = { .derive = counter }; submit_one (inst, "vmpage_action", "alloc", value); } else if (strncmp ("pgrefill_", key, strlen ("pgrefill_")) == 0) { char *inst = key + strlen ("pgrefill_"); - value_t value = { .counter = counter }; + value_t value = { .derive = counter }; submit_one (inst, "vmpage_action", "refill", value); } else if (strncmp ("pgsteal_", key, strlen ("pgsteal_")) == 0) { char *inst = key + strlen ("pgsteal_"); - value_t value = { .counter = counter }; + value_t value = { .derive = counter }; submit_one (inst, "vmpage_action", "steal", value); } else if (strncmp ("pgscan_kswapd_", key, strlen ("pgscan_kswapd_")) == 0) { char *inst = key + strlen ("pgscan_kswapd_"); - value_t value = { .counter = counter }; + value_t value = { .derive = counter }; submit_one (inst, "vmpage_action", "scan_kswapd", value); } else if (strncmp ("pgscan_direct_", key, strlen ("pgscan_direct_")) == 0) { char *inst = key + strlen ("pgscan_direct_"); - value_t value = { .counter = counter }; + value_t value = { .derive = counter }; submit_one (inst, "vmpage_action", "scan_direct", value); } @@ -241,17 +241,17 @@ static int vmem_read (void) */ else if (strcmp ("pgfree", key) == 0) { - value_t value = { .counter = counter }; + value_t value = { .derive = counter }; submit_one (NULL, "vmpage_action", "free", value); } else if (strcmp ("pgactivate", key) == 0) { - value_t value = { .counter = counter }; + value_t value = { .derive = counter }; submit_one (NULL, "vmpage_action", "activate", value); } else if (strcmp ("pgdeactivate", key) == 0) { - value_t value = { .counter = counter }; + value_t value = { .derive = counter }; submit_one (NULL, "vmpage_action", "deactivate", value); } } /* while (fgets) */ diff --git a/src/vserver.c b/src/vserver.c index 1615ca04..d80717cd 100644 --- a/src/vserver.c +++ b/src/vserver.c @@ -1,7 +1,7 @@ /** * collectd - src/vserver.c * Copyright (C) 2006,2007 Sebastian Harl - * Copyright (C) 2007,2008 Florian octo Forster + * Copyright (C) 2007-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 @@ -48,13 +48,13 @@ static int vserver_init (void) } /* static void vserver_init(void) */ static void traffic_submit (const char *plugin_instance, - const char *type_instance, counter_t rx, counter_t tx) + const char *type_instance, derive_t rx, derive_t tx) { value_t values[2]; value_list_t vl = VALUE_LIST_INIT; - values[0].counter = rx; - values[1].counter = tx; + values[0].derive = rx; + values[1].derive = tx; vl.values = values; vl.values_len = STATIC_ARRAY_SIZE (values); @@ -107,14 +107,21 @@ static void submit_gauge (const char *plugin_instance, const char *type, plugin_dispatch_values (&vl); } /* void submit_gauge */ -static inline long long __get_sock_bytes(const char *s) +static derive_t vserver_get_sock_bytes(const char *s) { + value_t v; + int status; + while (s[0] != '/') ++s; /* Remove '/' */ ++s; - return atoll(s); + + status = parse_value (s, &v, DS_TYPE_DERIVE); + if (status != 0) + return (-1); + return (v.derive); } static int vserver_read (void) @@ -201,8 +208,8 @@ static int vserver_read (void) while ((fh != NULL) && (NULL != fgets (buffer, BUFSIZE, fh))) { - counter_t rx; - counter_t tx; + derive_t rx; + derive_t tx; char *type_instance; if (strsplit (buffer, cols, 4) < 4) @@ -221,8 +228,8 @@ static int vserver_read (void) else continue; - rx = __get_sock_bytes (cols[1]); - tx = __get_sock_bytes (cols[2]); + rx = vserver_get_sock_bytes (cols[1]); + tx = vserver_get_sock_bytes (cols[2]); /* cols[3] == errors */ traffic_submit (dent->d_name, type_instance, rx, tx); diff --git a/src/write_http.c b/src/write_http.c index 22b5842a..3035e43a 100644 --- a/src/write_http.c +++ b/src/write_http.c @@ -61,7 +61,7 @@ struct wh_callback_s char send_buffer[4096]; size_t send_buffer_free; size_t send_buffer_fill; - time_t send_buffer_init_time; + cdtime_t send_buffer_init_time; pthread_mutex_t send_lock; }; @@ -72,7 +72,7 @@ static void wh_reset_buffer (wh_callback_t *cb) /* {{{ */ memset (cb->send_buffer, 0, sizeof (cb->send_buffer)); cb->send_buffer_free = sizeof (cb->send_buffer); cb->send_buffer_fill = 0; - cb->send_buffer_init_time = time (NULL); + cb->send_buffer_init_time = cdtime (); if (cb->format == WH_FORMAT_JSON) { @@ -158,19 +158,21 @@ static int wh_callback_init (wh_callback_t *cb) /* {{{ */ return (0); } /* }}} int wh_callback_init */ -static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */ +static int wh_flush_nolock (cdtime_t timeout, wh_callback_t *cb) /* {{{ */ { int status; - DEBUG ("write_http plugin: wh_flush_nolock: timeout = %i; " + DEBUG ("write_http plugin: wh_flush_nolock: timeout = %.3f; " "send_buffer_fill = %zu;", - timeout, cb->send_buffer_fill); + CDTIME_T_TO_DOUBLE (timeout), + cb->send_buffer_fill); + /* timeout == 0 => flush unconditionally */ if (timeout > 0) { - time_t now; + cdtime_t now; - now = time (NULL); + now = cdtime (); if ((cb->send_buffer_init_time + timeout) > now) return (0); } @@ -179,7 +181,7 @@ static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */ { if (cb->send_buffer_fill <= 0) { - cb->send_buffer_init_time = time (NULL); + cb->send_buffer_init_time = cdtime (); return (0); } @@ -190,7 +192,7 @@ static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */ { if (cb->send_buffer_fill <= 2) { - cb->send_buffer_init_time = time (NULL); + cb->send_buffer_init_time = cdtime (); return (0); } @@ -219,7 +221,7 @@ static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */ return (status); } /* }}} wh_flush_nolock */ -static int wh_flush (int timeout, /* {{{ */ +static int wh_flush (cdtime_t timeout, /* {{{ */ const char *identifier __attribute__((unused)), user_data_t *user_data) { @@ -259,7 +261,7 @@ static void wh_callback_free (void *data) /* {{{ */ cb = data; - wh_flush_nolock (/* timeout = */ -1, cb); + wh_flush_nolock (/* timeout = */ 0, cb); curl_easy_cleanup (cb->curl); sfree (cb->location); @@ -271,76 +273,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) { @@ -367,7 +299,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"); @@ -375,8 +307,10 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{ } command_len = (size_t) ssnprintf (command, sizeof (command), - "PUTVAL %s interval=%i %s\r\n", - key, vl->interval, values); + "PUTVAL %s interval=%.3f %s\r\n", + key, + CDTIME_T_TO_DOUBLE (vl->interval), + values); if (command_len >= sizeof (command)) { ERROR ("write_http plugin: Command buffer too small: " "Need %zu bytes.", command_len + 1); @@ -398,7 +332,7 @@ static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{ if (command_len >= cb->send_buffer_free) { - status = wh_flush_nolock (/* timeout = */ -1, cb); + status = wh_flush_nolock (/* timeout = */ 0, cb); if (status != 0) { pthread_mutex_unlock (&cb->send_lock); @@ -450,7 +384,7 @@ static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ * ds, vl, cb->store_rates); if (status == (-ENOMEM)) { - status = wh_flush_nolock (/* timeout = */ -1, cb); + status = wh_flush_nolock (/* timeout = */ 0, cb); if (status != 0) { wh_reset_buffer (cb); 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 : */ diff --git a/src/zfs_arc.c b/src/zfs_arc.c index 79a234f4..f886c86a 100644 --- a/src/zfs_arc.c +++ b/src/zfs_arc.c @@ -46,54 +46,48 @@ static void za_submit (const char* type, const char* type_instance, value_t* val static void za_submit_gauge (const char* type, const char* type_instance, gauge_t value) { - value_t values[1]; + value_t vv; - values[0].gauge = value; - - za_submit (type, type_instance, values, STATIC_ARRAY_SIZE(values)); + vv.gauge = value; + za_submit (type, type_instance, &vv, 1); } -static void za_submit_size (gauge_t size, gauge_t size_target, gauge_t limit_min, gauge_t limit_max) +static void za_submit_derive (const char* type, const char* type_instance, derive_t dv) { - value_t values[4]; - - values[0].gauge = size; - values[1].gauge = size_target; - values[2].gauge = limit_min; - values[3].gauge = limit_max; + value_t vv; - za_submit ("arc_size", "", values, STATIC_ARRAY_SIZE(values)); + vv.derive = dv; + za_submit (type, type_instance, &vv, 1); } -static void za_submit_bytes (counter_t read, counter_t write) +static void za_submit_ratio (const char* type_instance, gauge_t hits, gauge_t misses) { - value_t values[2]; + gauge_t ratio = NAN; - values[0].counter = read; - values[1].counter = write; + if (!isfinite (hits) || (hits < 0.0)) + hits = 0.0; + if (!isfinite (misses) || (misses < 0.0)) + misses = 0.0; - za_submit ("arc_l2_bytes", "", values, STATIC_ARRAY_SIZE(values)); -} - -static void za_submit_counts (char *type_instance, counter_t demand_data, counter_t demand_metadata, - counter_t prefetch_data, counter_t prefetch_metadata) -{ - value_t values[4]; + if ((hits != 0.0) || (misses != 0.0)) + ratio = hits / (hits + misses); - values[0].counter = demand_data; - values[1].counter = demand_metadata; - values[2].counter = prefetch_data; - values[3].counter = prefetch_metadata; - - za_submit ("arc_counts", type_instance, values, STATIC_ARRAY_SIZE(values)); + za_submit_gauge ("cache_ratio", type_instance, ratio); } static int za_read (void) { - gauge_t arcsize, targetsize, minlimit, maxlimit, hits, misses, l2_size, l2_hits, l2_misses; - counter_t demand_data_hits, demand_metadata_hits, prefetch_data_hits, prefetch_metadata_hits; - counter_t demand_data_misses, demand_metadata_misses, prefetch_data_misses, prefetch_metadata_misses; - counter_t l2_read_bytes, l2_write_bytes; + gauge_t arc_size, l2_size; + derive_t demand_data_hits, + demand_metadata_hits, + prefetch_data_hits, + prefetch_metadata_hits, + demand_data_misses, + demand_metadata_misses, + prefetch_data_misses, + prefetch_metadata_misses; + gauge_t arc_hits, arc_misses, l2_hits, l2_misses; + value_t l2_io[2]; kstat_t *ksp = NULL; get_kstat (&ksp, "zfs", 0, "arcstats"); @@ -103,11 +97,14 @@ static int za_read (void) return (-1); } - arcsize = get_kstat_value(ksp, "size"); - targetsize = get_kstat_value(ksp, "c"); - minlimit = get_kstat_value(ksp, "c_min"); - maxlimit = get_kstat_value(ksp, "c_max"); + /* Sizes */ + arc_size = get_kstat_value(ksp, "size"); + l2_size = get_kstat_value(ksp, "l2_size"); + + za_submit_gauge ("cache_size", "arc", arc_size); + za_submit_gauge ("cache_size", "L2", l2_size); + /* Hits / misses */ demand_data_hits = get_kstat_value(ksp, "demand_data_hits"); demand_metadata_hits = get_kstat_value(ksp, "demand_metadata_hits"); prefetch_data_hits = get_kstat_value(ksp, "prefetch_data_hits"); @@ -118,31 +115,33 @@ static int za_read (void) prefetch_data_misses = get_kstat_value(ksp, "prefetch_data_misses"); prefetch_metadata_misses = get_kstat_value(ksp, "prefetch_metadata_misses"); - hits = get_kstat_value(ksp, "hits"); - misses = get_kstat_value(ksp, "misses"); + za_submit_derive ("cache_result", "demand_data-hit", demand_data_hits); + za_submit_derive ("cache_result", "demand_metadata-hit", demand_metadata_hits); + za_submit_derive ("cache_result", "prefetch_data-hit", prefetch_data_hits); + za_submit_derive ("cache_result", "prefetch_metadata-hit", prefetch_metadata_hits); - l2_size = get_kstat_value(ksp, "l2_size"); - l2_read_bytes = get_kstat_value(ksp, "l2_read_bytes"); - l2_write_bytes = get_kstat_value(ksp, "l2_write_bytes"); - l2_hits = get_kstat_value(ksp, "l2_hits"); - l2_misses = get_kstat_value(ksp, "l2_misses"); + za_submit_derive ("cache_result", "demand_data-miss", demand_data_misses); + za_submit_derive ("cache_result", "demand_metadata-miss", demand_metadata_misses); + za_submit_derive ("cache_result", "prefetch_data-miss", prefetch_data_misses); + za_submit_derive ("cache_result", "prefetch_metadata-miss", prefetch_metadata_misses); + /* Ratios */ + arc_hits = (gauge_t) get_kstat_value(ksp, "hits"); + arc_misses = (gauge_t) get_kstat_value(ksp, "misses"); + l2_hits = (gauge_t) get_kstat_value(ksp, "l2_hits"); + l2_misses = (gauge_t) get_kstat_value(ksp, "l2_misses"); - za_submit_size (arcsize, targetsize, minlimit, maxlimit); - za_submit_gauge ("arc_l2_size", "", l2_size); + za_submit_ratio ("arc", arc_hits, arc_misses); + za_submit_ratio ("L2", l2_hits, l2_misses); - za_submit_counts ("hits", demand_data_hits, demand_metadata_hits, - prefetch_data_hits, prefetch_metadata_hits); - za_submit_counts ("misses", demand_data_misses, demand_metadata_misses, - prefetch_data_misses, prefetch_metadata_misses); + /* I/O */ + l2_io[0].derive = get_kstat_value(ksp, "l2_read_bytes"); + l2_io[1].derive = get_kstat_value(ksp, "l2_write_bytes"); - za_submit_gauge ("arc_ratio", "L1", hits / (hits + misses)); - za_submit_gauge ("arc_ratio", "L2", l2_hits / (l2_hits + l2_misses)); - - za_submit_bytes (l2_read_bytes, l2_write_bytes); + za_submit ("io_octets", "L2", l2_io, /* num values = */ 2); return (0); -} +} /* int za_read */ static int za_init (void) /* {{{ */ { diff --git a/version-gen.sh b/version-gen.sh index 0e0d8d45..d5546431 100755 --- a/version-gen.sh +++ b/version-gen.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -DEFAULT_VERSION="4.10.8.git" +DEFAULT_VERSION="5.0.4.git" VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"