# python stuff
*.pyc
+
+# tag stuff
+src/tags
+
+# backup stuff
+*~
Amit Gupta <amit.gupta221 at gmail.com>
- Multiple servers in the apache plugin.
+ - curl_xml plugin.
Anthony Dewhurst <dewhurst at gmail.com>
- zfs_arc plugin.
Antony Dovgal <tony at daylessday.org>
- memcached plugin.
+Aurélien Reynaud <collectd at wattapower.net>
+ - LPAR plugin.
+ - Various fixes for AIX, HP-UX and Solaris.
+
Bruno Prémont <bonbons at linux-vserver.org>
- BIND plugin.
- Many bugreports and -fixes in various plugins,
Jason Pepas <cell at ices.utexas.edu>
- nfs plugin.
+Jérôme Renard <jerome.renard at gmail.com>
+ - varnish plugin.
+
Luboš Staněk <kolektor at atlas.cz>
- sensors plugin improvements.
- Time and effort to find a nasty bug in the ntpd-plugin.
- Some bugfixes in the exec plugin.
- Notifications in the ipmi plugin.
+Phoenix Kayo <kayo.k11.4 at gmail.com>
+ - pinba plugin.
+
Piotr Hosowicz <the55 at wp.pl>
- SMF manifest for collectd.
Scott Garrett <sgarrett at technomancer.com>
- tape plugin.
+Sebastien Pahl <sebastien.pahl at dotcloud.com>
+ - AMQP plugin.
+
Simon Kuhnle <simon at blarzwurst.de>
- OpenBSD code for the cpu and memory plugins.
+yyyy-mm-dd, 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.
+
+2010-11-27, Version 4.10.2
+ * Documentation: Various documentation fixes.
+ * collectd: If including one configuration file fails, continue with
+ the rest of the configuration if possible.
+ * collectd: Fix a bug in the read function scheduling. In rare cases
+ read functions may not have been called as often as requested.
+ * collectd: Concurrency issues with errno(3) under AIX have been
+ fixed: A thread-safe version of errno has to be requested under AIX.
+ Thanks to Aurélien Reynaud for his patch.
+ * collectd: A left-over hard-coded 2 has been replaced by the
+ configurable timeout value.
+ * curl, memcachec, tail plugins: Fix handling of "DERIVE" data
+ sources. Matching the end of a string has been improved; thanks to
+ Sebastian Harl for the patch.
+ * curl_json plugin: Fix a problem when parsing 64bit integers. Reading
+ JSON data from non-HTTP sources has been fixed.
+ * netapp plugin: Pass the interval setting to the dispatch function.
+ Restore compatibility to NetApp Release 7.3. Thanks to Sven Trenkel
+ for the patch.
+ * network plugin: Be less verbose about unchecked signatures, in order
+ to prevent spamming the logs.
+ * notify_email plugin: Concurrency problems have been fixed.
+ * python plugin: Set "sys.argv", since many scripts don't expect that
+ it may not be set. Thanks to Sven Trenkel for the patch.
+ * rrdtool, rrdcached plugin: Fix a too strict assertion when creating
+ RRD files.
+ * swap plugin: A bug which lead to incorrect I/O values has been
+ fixed.
+ * value match: A minor memory leak has been fixed. Thanks to Sven
+ Trenkel for the patch.
+
+2010-07-09, Version 4.10.1
+ * Build system: Checking for "strtok_r" under Solaris has been fixed.
+ * Portability: Fixes for Solaris 8 have been applied. Thanks to
+ Alexander Wuerstlein for his patch.
+ * collectd: The shutdown speed when terminating the read threads has
+ been improved.
+ * libcollectdclient: A format error in the PUTVAL command has been
+ removed. Thanks to Johan Van den Brande for fixing this.
+ * df plugin: An error message shown when "cu_mount_getlist" fails has
+ been added.
+ * processes plugin: Missing initialization code for IO members of a
+ struct has been added. Thanks to Aurélien Reynaud for fixing this.
+ * python plugin: Memory leaks in the write and notification callbacks
+ have been fixed. A possible crash when the plugin was loaded but not
+ configured has been fixed. Thanks to Sven Trenkel for his patches.
+ * snmp plugin: Verbosity with regard to unknown ASN types has been
+ increased. A build problem on PowerPC and ARM processors has been
+ fixed by Aurélien Reynaud; thanks!
+ * powerdns plugin: Compatibility changes for PowerDNS 2.9.22 and above
+ have been applied. Thanks to Luke Heberling for his changes.
+
+2010-05-01, Version 4.10.0
+ * collectd: JSON output now includes the "dstypes" and "dsnames"
+ fields. This makes it easier for external applications to interpret
+ the data. Thanks to Chris Buben for his work.
+ * collectd: The new "Timeout" option can be used to specify a
+ "timeout" for missing values. This is used in the threshold checking
+ code to detect missing values. Thanks to Andrés J. Díaz for the
+ patch.
+ * apache plugin: Support for "IdleWorkers" (Apache 1.*: "IdleServers")
+ has been added.
+ * curl plugin: The new "ExcludeRegex" allows to easily exclude certain
+ lines from the match.
+ * curl_xml plugin: This new plugin allows to read XML files using cURL
+ and extract metrics included in the files. Thanks to Amit Gupta for
+ his work.
+ * filecount plugin: The new "IncludeHidden" option allows to include
+ "hidden" files and directories in the statistics. Thanks to Vaclav
+ Malek for the patch.
+ * logfile plugin: The new "PrintSeverity" option allows to include the
+ severity of a message in the output. Thanks to Clément Stenac for
+ his patch.
+ * memcachec plugin: The new "ExcludeRegex" allows to easily exclude
+ certain lines from the match.
+ * modbus plugin: This new plugin allows to read registers from
+ Modbus-TCP enabled devices.
+ * network plugin: The new "Interface" option allows to set the
+ interface to be used for multicast and, if supported, unicast
+ traffic. Thanks to Max Henkel for his work.
+ * openvpn plugin: The "CollectUserCount" and "CollectIndividualUsers"
+ options allow more detailed control over how to report sessions of
+ multiple users. Thanks to Fabian Schuh for his work.
+ * pinba plugin: This new plugin receives timing information from the
+ Pinba PHP extension, which can be used for profiling PHP code and
+ webserver performance. Thanks to Phoenix Kayo for his work.
+ * ping plugin: The new "MaxMissed" allows to re-resolve a hosts
+ address when it doesn't reply to a number of ping requests. Thanks
+ to Stefan Völkel for the patch.
+ * postgresql plugin: The "Interval" config option has been added. The
+ plugin has been relicensed under the 2-clause BSD license. Thanks to
+ Sebastian Harl for his work.
+ * processes plugin: Support for "code" and "data" virtual memory sizes
+ has been added. Thanks to Clément Stenac for his patch.
+ * python plugin: Support for Python 3 has been implemented. Thanks to
+ Sven Trenkel for his work.
+ * routeros plugin: Support for collecting CPU load, memory usage, used
+ and free disk space, sectors written and number of bad blocks from
+ MikroTik devices has been added.
+ * swap plugin: Support for Linux < 2.6 has been added. Thanks to Lorin
+ Scraba for his patch.
+ * tail plugin: The new "ExcludeRegex" allows to easily exclude certain
+ lines from the match. Thanks to Peter Warasin for his patch.
+ * write_http plugin: The "StoreRates" option has been added. Thanks to
+ Paul Sadauskas for his patch.
+ * regex match: The "Invert" option has been added. Thanks to Julien
+ Ammous for his patch.
+
+2010-11-27, Version 4.9.4
+ * Documentation: Various documentation fixes.
+ * collectd: If including one configuration file fails, continue with
+ the rest of the configuration if possible.
+ * collectd: Fix a bug in the read function scheduling. In rare cases
+ read functions may not have been called as often as requested.
+ * collectd: Concurrency issues with errno(3) under AIX have been
+ fixed: A thread-safe version of errno has to be requested under AIX.
+ Thanks to Aurélien Reynaud for his patch.
+ * curl, memcachec, tail plugins: Fix handling of "DERIVE" data
+ sources. Matching the end of a string has been improved; thanks to
+ Sebastian Harl for the patch.
+ * curl_json plugin: Fix a problem when parsing 64bit integers. Reading
+ JSON data from non-HTTP sources has been fixed.
+ * netapp plugin: Pass the interval setting to the dispatch function.
+ Restore compatibility to NetApp Release 7.3. Thanks to Sven Trenkel
+ for the patch.
+ * network plugin: Be less verbose about unchecked signatures, in order
+ to prevent spamming the logs.
+ * notify_email plugin: Concurrency problems have been fixed.
+ * python plugin: Set "sys.argv", since many scripts don't expect that
+ it may not be set. Thanks to Sven Trenkel for the patch.
+ * rrdtool, rrdcached plugin: Fix a too strict assertion when creating
+ RRD files.
+ * value match: A minor memory leak has been fixed. Thanks to Sven
+ Trenkel for the patch.
+
+2010-07-09, Version 4.9.3
+ * Build system: Checking for "strtok_r" under Solaris has been fixed.
+ * Portability: Fixes for Solaris 8 have been applied. Thanks to
+ Aurélien Reynaud and Alexander Wuerstlein for their patches.
+ * collectd: The shutdown speed when terminating the read threads has
+ been improved.
+ * collectd-nagios: The format of the performance data has been fixed.
+ * libcollectdclient: A format error in the PUTVAL command has been
+ removed. Thanks to Johan Van den Brande for fixing this.
+ * df plugin: An error message shown when "cu_mount_getlist" fails has
+ been added.
+ * processes plugin: Missing initialization code for IO members of a
+ struct has been added. Thanks to Aurélien Reynaud for fixing this.
+ * python plugin: Memory leaks in the write and notification callbacks
+ have been fixed. A possible crash when the plugin was loaded but not
+ configured has been fixed. Thanks to Sven Trenkel for his patches.
+ * rrdcached plugin: A build issue has been resolved. Thanks to
+ Thorsten von Eicken for the patch.
+ * snmp plugin: Verbosity with regard to unknown ASN types has been
+ increased. A build problem on PowerPC and ARM processors has been
+ fixed by Aurélien Reynaud; thanks!
+ * powerdns plugin: Compatibility changes for PowerDNS 2.9.22 and above
+ have been applied. Thanks to Luke Heberling for his changes.
+
+2010-04-22, Version 4.9.2
+ * Build system, various plugins: Fixes for AIX compatibility have been
+ added. Thanks to Manuel Sanmartin for his patches.
+ * Build system: Checking for "nanosleep" on old Solaris machines has
+ been fixed. Thanks to Vincent McIntyre and Sebastian Harl for
+ figuring out a way to make this work.
+ * collectd: Append a newline to messages written to STDERR.
+ * collectd: Serialization of NANs in JSON format has been fixed.
+ Thanks to Chris Buben for pointing out the resulting syntax error.
+ * collectd: Checks whether a "sleep" returned early have been added;
+ the cases are now handled correctly. Thanks to Michael Stapelberg
+ for the patch.
+ * collectd: Continue reading files in a directory when parsing one
+ file fails.
+ * apache plugin: Collection of the number of active connections has
+ been fixed for Apache 2.*.
+ * contextswitch plugin: Handle large counter/derive values correctly.
+ Thanks to Martin Merkel for reporting the bug.
+ * exec plugin: Error messages have been improved. The "running" flag
+ is now cleared correctly when forking a child fails.
+ * iptables plugin: Fix a violation of aliasing rules. This resolves a
+ warning / error with new GCC versions. Thanks to Jan Engelhardt for
+ the work-around.
+ * java plugin: The Java API files are now packaged into a .jar file.
+ Thanks to Amit Gupta for his patch.
+ * network plugin: Fix a segmentation fault when receiving packets with
+ an unknown data source type.
+ * network plugin: A memory leak when receiving encrypted network
+ packets has been fixed.
+ * openvpn plugin: Fix naming schema when reading "MULTI1" type status
+ files.
+ * oracle plugin: Fix checking for lost connections and reconnect in
+ this case. Thanks to Sven Trenkel for pointing out the problem.
+ * unixsock plugin: A memory leak in the "LISTVAL" command has been
+ fixed. Thanks to Peter Warasin for pointing it out.
+ * write_http plugin: Use the "any" authentication schema. This used to
+ be "digest". Thanks to Paul Sadauskas for the patch.
+
2010-01-14, Version 4.9.1
* Documentation: Some manpage fixes.
* Default config: Added sample configuration for missing plugins.
* scale target: This target to scale (multiply) values by an arbitrary
value has been added.
+2010-04-22, Version 4.8.5
+ * collectd: Append a newline to messages written to STDERR.
+ * network plugin: Fix a segmentation fault when receiving packets with
+ an unknown data source type.
+
+2010-04-07, Version 4.8.4
+ * Build system, various plugins: Fixes for AIX compatibility have been
+ added. Thanks to Manuel Sanmartin for his patches.
+ * Build system: Checking for "nanosleep" on old Solaris machines has
+ been fixed. Thanks to Vincent McIntyre and Sebastian Harl for
+ figuring out a way to make this work.
+ * collectd: Serialization of NANs in JSON format has been fixed.
+ Thanks to Chris Buben for pointing out the resulting syntax error.
+ * collectd: Checks whether a "sleep" returned early have been added;
+ the cases are now handled correctly. Thanks to Michael Stapelberg
+ for the patch.
+ * collectd: Continue reading files in a directory when parsing one
+ file fails.
+ * apache plugin: Collection of the number of active connections has
+ been fixed for Apache 2.*.
+ * exec plugin: Error messages have been improved. The "running" flag
+ is now cleared correctly when forking a child fails.
+ * iptables plugin: Fix a violation of aliasing rules. This resolves a
+ warning / error with new GCC versions. Thanks to Jan Engelhardt for
+ the work-around.
+ * java plugin: The Java API files are now packaged into a .jar file.
+ Thanks to Amit Gupta for his patch.
+ * network plugin: A memory leak when receiving encrypted network
+ packets has been fixed.
+ * oracle plugin: Fix checking for lost connections and reconnect in
+ this case. Thanks to Sven Trenkel for pointing out the problem.
+ * unixsock plugin: A memory leak in the "LISTVAL" command has been
+ fixed. Thanks to Peter Warasin for pointing it out.
+ * write_http plugin: Use the "any" authentication schema. This used to
+ be "digest". Thanks to Paul Sadauskas for the patch.
+
2010-01-14, Version 4.8.3
* Documentation: Some manpage fixes.
* rrdtool plugin: Fix a bug with random write timeouts. Due to an
Retrieves JSON data via cURL and parses it according to user
configuration.
+ - curl_xml
+ Retrieves XML data via cURL and parses it according to user
+ configuration.
+
- dbi
Executes SQL statements on various databases and interprets the returned
data.
- 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.
Memory utilization: Memory occupied by running processes, page cache,
buffer cache and free.
+ - modbus
+ Reads values from Modbus/TCP enabled devices. Supports reading values
+ from multiple "slaves" so gateway devices can be used.
+
- multimeter
Information provided by serial multimeters, such as the `Metex
M-4650CR'.
write your own plugins in Perl and return arbitrary values using this
API. See collectd-perl(5).
+ - pinba
+ Receive and dispatch timing values from Pinba, a profiling extension for
+ PHP.
+
- ping
Network latency: Time to reach the default gateway or another given
host.
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.
- 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.
- zfs_arc
Statistics for ZFS' “Adaptive Replacement Cache” (ARC).
- * Output can be written or send to various destinations by the following
+ * 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
* 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
network plugins, makes sure your resources are used efficiently. Also,
since collectd is programmed multithreaded it benefits from hyperthreading
and multicore processors and makes sure that the daemon isn't idle if only
- one plugins waits for an IO-operation to complete.
+ one plugin waits for an IO-operation to complete.
* Once set up, hardly any maintenance is necessary. Setup is kept as easy
as possible and the default values should be okay for most users.
* libclntsh (optional)
Used by the `oracle' plugin.
+ * libcredis (optional)
+ Used by the redis plugin. Please note that you require a 0.2.2 version
+ or higher. <http://code.google.com/p/credis/>
+
* libcurl (optional)
If you want to use the `apache', `ascent', `curl', `nginx', or `write_http'
plugin.
Used by the `memcachec' plugin to connect to a memcache daemon.
<http://tangent.org/552/libmemcached.html>
+ * libmodbus (optional)
+ Used by the “modbus” plugin to communicate with Modbus/TCP devices. The
+ “modbus” plugin works with version 2.0.3 of the library – due to frequent
+ API changes other versions may or may not compile cleanly.
+ <http://www.libmodbus.org/>
+
* libmysqlclient (optional)
Unsurprisingly used by the `mysql' plugin.
<http://dev.mysql.com/>
- * libnatapp (optional)
+ * libnetapp (optional)
Required for the “netapp” plugin.
This library is part of the “Manage ONTAP SDK” published by NetApp.
Used to capture packets by the `dns' plugin.
<http://www.tcpdump.org/>
+ * libperfstat (optional)
+ Used by various plugins to gather statistics under AIX.
+
* libperl (optional)
Obviously used by the `perl' plugin. The library has to be compiled with
ithread support (introduced in Perl 5.6.0).
The PostgreSQL C client library used by the `postgresql' plugin.
<http://www.postgresql.org/>
+ * libprotobuf-c, protoc-c (optional)
+ Used by the `pinba' plugin to generate a parser for the network packets
+ sent by the Pinba PHP extension.
+ <http://code.google.com/p/protobuf-c/>
+
* libpython (optional)
Used by the `python' plugin. Currently, only 2.3 ≦ Python < 3 is supported.
<http://www.python.org/>
+ * librabbitmq (optional; also called “rabbitmq-c”)
+ Used by the AMQP plugin for AMQP connections, for example to RabbitMQ.
+ <http://hg.rabbitmq.com/rabbitmq-c/>
+
* librouteros (optional)
Used by the `routeros' plugin to connect to a device running `RouterOS'.
<http://verplant.org/librouteros/>
Parse JSON data. This is needed for the `curl_json' plugin.
<http://github.com/lloyd/yajl>
+ * libvarnish (optional)
+ Fetches statistics from a Varnish instance. This is needed for the Varnish plugin
+ <http://varnish-cache.org>
+
Configuring / Compiling / Installing
------------------------------------
org/collectd/java/GenericJMX.java \
org/collectd/java/JMXMemory.java
-java-build-stamp: org/collectd/api/*.java
+java-build-stamp: org/collectd/api/*.java org/collectd/java/*.java
$(JAVAC) -d "." "$(srcdir)/org/collectd/api"/*.java
$(JAVAC) -d "." "$(srcdir)/org/collectd/java"/*.java
mkdir -p .libs
*/
public interface CollectdFlushInterface
{
- public int flush (int timeout, String identifier);
+ public int flush (Number timeout, String identifier);
}
_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;
}
/*
* 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
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<GenericJMXConfMBean> _mbeans = null;
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);
{
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;
/*
* 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
return (v.getString ());
} /* }}} String getConfigString */
- private String join (String separator, List<String> 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 */
-
/*
* <MBean "alias name">
* ObjectName "object name"
return (this._name);
} /* }}} */
- public int query (MBeanServerConnection conn, PluginData pd) /* {{{ */
+ public int query (MBeanServerConnection conn, PluginData pd, /* {{{ */
+ String instance_prefix)
{
Set<ObjectName> names;
Iterator<ObjectName> iter;
ObjectName objName;
PluginData pd_tmp;
List<String> instanceList;
- String instance;
+ StringBuffer instance;
objName = iter.next ();
pd_tmp = new PluginData (pd);
instanceList = new ArrayList<String> ();
+ instance = new StringBuffer ();
Collectd.logDebug ("GenericJMXConfMBean: objName = "
+ objName.toString ());
}
}
+ 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);
}
}
-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;
AC_PROG_YACC
PKG_PROG_PKG_CONFIG
+AC_CHECK_PROG([have_protoc_c], [protoc-c], [yes], [no])
+AM_CONDITIONAL(HAVE_PROTOC_C, test "x$have_protoc_c" = "xyes")
+
AC_MSG_CHECKING([for kernel type ($host_os)])
case $host_os in
*linux*)
then
AC_DEFINE(_POSIX_PTHREAD_SEMANTICS, 1, [Define to enforce POSIX thread semantics under Solaris.])
fi
+if test "x$ac_system" = "xAIX"
+then
+ AC_DEFINE(_THREAD_SAFE_ERRNO, 1, [Define to use the thread-safe version of errno under AIX.])
+fi
# Where to install .pc files.
pkgconfigdir="${libdir}/pkgconfig"
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")
AC_HEADER_DIRENT
AC_HEADER_STDBOOL
-AC_CHECK_HEADERS(stdio.h stdint.h errno.h math.h stdarg.h syslog.h fcntl.h signal.h assert.h sys/types.h sys/socket.h sys/select.h poll.h netdb.h arpa/inet.h sys/resource.h sys/param.h kstat.h regex.h sys/ioctl.h endian.h sys/isa_defs.h)
+AC_CHECK_HEADERS(stdio.h errno.h math.h stdarg.h syslog.h fcntl.h signal.h assert.h sys/types.h sys/socket.h sys/select.h poll.h netdb.h arpa/inet.h sys/resource.h sys/param.h kstat.h regex.h sys/ioctl.h endian.h sys/isa_defs.h)
# For ping library
AC_CHECK_HEADERS(netinet/in_systm.h, [], [],
# Checks for library functions.
#
AC_PROG_GCC_TRADITIONAL
-AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf)
+AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf setenv if_indextoname)
AC_FUNC_STRERROR_R
+SAVE_CFLAGS="$CFLAGS"
+# Emulate behavior of src/Makefile.am
+if test "x$GCC" = "xyes"
+then
+ CFLAGS="$CFLAGS -Wall -Werror"
+fi
+
AC_CACHE_CHECK([for strtok_r],
[c_cv_have_strtok_r_default],
AC_LINK_IFELSE(
if test "x$c_cv_have_strtok_r_default" = "xno"
then
- SAVE_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -D_REENTRANT=1"
AC_CACHE_CHECK([if strtok_r needs _REENTRANT],
)
fi
+CFLAGS="$SAVE_CFLAGS"
+if test "x$c_cv_have_strtok_r_reentrant" = "xyes"
+then
+ CFLAGS="$CFLAGS -D_REENTRANT=1"
+fi
+
AC_CHECK_FUNCS(getpwnam_r getgrnam_r setgroups regcomp regerror regexec regfree)
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"
-AC_CHECK_FUNCS(nanosleep, [], AC_CHECK_LIB(rt, nanosleep, [nanosleep_needs_rt="yes"], AC_MSG_ERROR(cannot find nanosleep)))
-AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes")
+nanosleep_needs_posix4="no"
+AC_CHECK_FUNCS(nanosleep,
+ [],
+ AC_CHECK_LIB(rt, nanosleep,
+ [nanosleep_needs_rt="yes"],
+ AC_CHECK_LIB(posix4, nanosleep,
+ [nanosleep_needs_posix4="yes"],
+ AC_MSG_ERROR(cannot find nanosleep))))
+
+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"])
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 <time.h>
+]],
+[[
+ 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 <time.h>
+]],
+[[
+ 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 <sys/stat.h>
+#include <sys/swap.h>]],
+ [[
+ 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 <sys/stat.h>
+#include <sys/swap.h>]],
+ [[
+ 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.])],
[[
#include <stdlib.h>
#include <math.h>
-static float foo = NAN;
+static double foo = NAN;
]],
[[
if (isnan (foo))
#include <stdlib.h>
#define __USE_ISOC99 1
#include <math.h>
-static float foo = NAN;
+static double foo = NAN;
]],
[[
if (isnan (foo))
#ifndef isnan
# define isnan(f) ((f) != (f))
#endif
-static float foo = NAN;
+static double foo = NAN;
]],
[[
if (isnan (foo))
#if HAVE_STDINT_H
# include <stdint.h>
#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
#if HAVE_STDBOOL_H
# include <stdbool.h>
#endif
#if HAVE_STDINT_H
# include <stdint.h>
#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
#if HAVE_STDBOOL_H
# include <stdbool.h>
#endif
#if HAVE_STDINT_H
# include <stdint.h>
#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
#if HAVE_STDBOOL_H
# include <stdbool.h>
#endif
AC_MSG_CHECKING([if have htonll defined])
have_htonll="no"
- AC_RUN_IFELSE([
+ AC_LINK_IFELSE([
AC_LANG_PROGRAM([
#include <sys/types.h>
#include <netinet/in.h>
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
#endif
], [
return htonll(0);
#include <linux/netdevice.h>
])
+AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [],
+ [],
+ [
+ #include <netinet/in.h>
+ #include <net/if.h>
+ ])
+
AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct kinfo_proc.ki_rusage],
[
AC_DEFINE(HAVE_STRUCT_KINFO_PROC_FREEBSD, 1,
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 <libperfstat.h]])
+ if test "x$av_cv_member_perfstat_partition_type_t_b_donate_enabled" = "xyes"
+ then
+ AC_DEFINE(PERFSTAT_SUPPORTS_DONATION, 1, [Define to 1 if your version of the 'perfstat' library supports donation])
+ fi
fi
AM_CONDITIONAL(BUILD_WITH_PERFSTAT, test "x$with_perfstat" = "xyes")
fi
AM_CONDITIONAL(BUILD_WITH_LIBKVM_OPENFILES, test "x$with_kvm_openfiles" = "xyes")
+# --with-libcredis {{{
+AC_ARG_WITH(libcredis, [AS_HELP_STRING([--with-libcredis@<:@=PREFIX@:>@], [Path to libcredis.])],
+[
+ if test "x$withval" = "xyes"
+ then
+ with_libcredis="yes"
+ else if test "x$withval" = "xno"
+ then
+ with_libcredis="no"
+ else
+ with_libcredis="yes"
+ LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS -I$withval/include"
+ LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS -L$withval/lib"
+ fi; fi
+],
+[with_libcredis="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBCREDIS_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBCREDIS_LDFLAGS"
+
+if test "x$with_libcredis" = "xyes"
+then
+ if test "x$LIBCREDIS_CPPFLAGS" != "x"
+ then
+ AC_MSG_NOTICE([libcredis CPPFLAGS: $LIBCREDIS_CPPFLAGS])
+ fi
+ AC_CHECK_HEADERS(credis.h,
+ [with_libcredis="yes"],
+ [with_libcredis="no (credis.h not found)"])
+fi
+if test "x$with_libcredis" = "xyes"
+then
+ if test "x$LIBCREDIS_LDFLAGS" != "x"
+ then
+ AC_MSG_NOTICE([libcredis LDFLAGS: $LIBCREDIS_LDFLAGS])
+ fi
+ AC_CHECK_LIB(credis, credis_info,
+ [with_libcredis="yes"],
+ [with_libcredis="no (symbol 'credis_info' not found)"])
+
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libcredis" = "xyes"
+then
+ BUILD_WITH_LIBCREDIS_CPPFLAGS="$LIBCREDIS_CPPFLAGS"
+ BUILD_WITH_LIBCREDIS_LDFLAGS="$LIBCREDIS_LDFLAGS"
+ AC_SUBST(BUILD_WITH_LIBCREDIS_CPPFLAGS)
+ AC_SUBST(BUILD_WITH_LIBCREDIS_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBCREDIS, test "x$with_libcredis" = "xyes")
+# }}}
+
# --with-libcurl {{{
with_curl_config="curl-config"
with_curl_cflags=""
AM_CONDITIONAL(BUILD_WITH_LIBMEMCACHED, test "x$with_libmemcached" = "xyes")
# }}}
+# --with-libmodbus {{{
+with_libmodbus_config=""
+with_libmodbus_cflags=""
+with_libmodbus_libs=""
+AC_ARG_WITH(libmodbus, [AS_HELP_STRING([--with-libmodbus@<:@=PREFIX@:>@], [Path to the modbus library.])],
+[
+ if test "x$withval" = "xno"
+ then
+ with_libmodbus="no"
+ else if test "x$withval" = "xyes"
+ then
+ with_libmodbus="use_pkgconfig"
+ else if test -d "$with_libmodbus/lib"
+ then
+ AC_MSG_NOTICE([Not checking for libmodbus: Manually configured])
+ with_libmodbus_cflags="-I$withval/include"
+ with_libmodbus_libs="-L$withval/lib -lmodbus"
+ with_libmodbus="yes"
+ fi; fi; fi
+],
+[with_libmodbus="use_pkgconfig"])
+
+# configure using pkg-config
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+ if test "x$PKG_CONFIG" = "x"
+ then
+ with_libmodbus="no (Don't have pkg-config)"
+ fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+ AC_MSG_NOTICE([Checking for modbus using $PKG_CONFIG])
+ $PKG_CONFIG --exists 'modbus' 2>/dev/null
+ if test $? -ne 0
+ then
+ with_libmodbus="no (pkg-config doesn't know modbus)"
+ fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+ with_libmodbus_cflags="`$PKG_CONFIG --cflags 'modbus'`"
+ if test $? -ne 0
+ then
+ with_libmodbus="no ($PKG_CONFIG failed)"
+ fi
+ with_libmodbus_libs="`$PKG_CONFIG --libs 'modbus'`"
+ if test $? -ne 0
+ then
+ with_libmodbus="no ($PKG_CONFIG failed)"
+ fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+ with_libmodbus="yes"
+fi
+
+# with_libmodbus_cflags and with_libmodbus_libs are set up now, let's do
+# the actual checks.
+if test "x$with_libmodbus" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
+
+ AC_CHECK_HEADERS(modbus/modbus.h, [], [with_libmodbus="no (modbus/modbus.h not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libmodbus" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ SAVE_LDFLAGS="$LDFLAGS"
+
+ CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
+ LDFLAGS="$LDFLAGS $with_libmodbus_libs"
+
+ AC_CHECK_LIB(modbus, modbus_connect,
+ [with_libmodbus="yes"],
+ [with_libmodbus="no (symbol modbus_connect not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libmodbus" = "xyes"
+then
+ BUILD_WITH_LIBMODBUS_CFLAGS="$with_libmodbus_cflags"
+ BUILD_WITH_LIBMODBUS_LIBS="$with_libmodbus_libs"
+ AC_SUBST(BUILD_WITH_LIBMODBUS_CFLAGS)
+ AC_SUBST(BUILD_WITH_LIBMODBUS_LIBS)
+fi
+# }}}
+
# --with-libmysql {{{
with_mysql_config="mysql_config"
with_mysql_cflags=""
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
fi
if test "x$with_libpcap" = "xyes"
then
- AC_CHECK_HEADERS(pcap.h,
- [
- AC_DEFINE(HAVE_PCAP_H, 1, [Define to 1 if you have the <pcap.h> header file.])
- ], [with_libpcap="no (pcap.h not found)"])
+ AC_CHECK_HEADERS(pcap.h,,
+ [with_libpcap="no (pcap.h not found)"])
fi
if test "x$with_libpcap" = "xyes"
then
- collect_libpcap=1
-else
- collect_libpcap=0
+ AC_CHECK_HEADERS(pcap-bpf.h,,
+ [with_libpcap="no (pcap-bpf.h not found)"])
fi
-AC_DEFINE_UNQUOTED(COLLECT_LIBPCAP, [$collect_libpcap],
- [Wether or not to use the pcap library])
AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
# }}}
if test "x$with_python" = "xyes"
then
AC_MSG_CHECKING([for Python CPPFLAGS])
- python_include_path=`echo "import distutils.sysconfig;print distutils.sysconfig.get_python_inc()" | "$with_python_prog" 2>&1`
+ python_include_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_python_inc())" | "$with_python_prog" 2>&1`
python_config_status=$?
if test "$python_config_status" -ne 0 || test "x$python_include_path" = "x"
if test "x$with_python" = "xyes"
then
AC_MSG_CHECKING([for Python LDFLAGS])
- python_library_path=`echo "import distutils.sysconfig;print distutils.sysconfig.get_config_vars(\"LIBDIR\").__getitem__(0)" | "$with_python_prog" 2>&1`
+ python_library_path=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"LIBDIR\").__getitem__(0))" | "$with_python_prog" 2>&1`
python_config_status=$?
if test "$python_config_status" -ne 0 || test "x$python_library_path" = "x"
if test "x$with_python" = "xyes"
then
AC_MSG_CHECKING([for Python LIBS])
- python_library_flags=`echo "import distutils.sysconfig;print distutils.sysconfig.get_config_vars(\"BLDLIBRARY\").__getitem__(0)" | "$with_python_prog" 2>&1`
+ python_library_flags=`echo "import distutils.sysconfig;import sys;sys.stdout.write(distutils.sysconfig.get_config_vars(\"BLDLIBRARY\").__getitem__(0))" | "$with_python_prog" 2>&1`
python_config_status=$?
if test "$python_config_status" -ne 0 || test "x$python_library_flags" = "x"
fi
# }}} --with-python
+# --with-librabbitmq {{{
+with_librabbitmq_cppflags=""
+with_librabbitmq_ldflags=""
+AC_ARG_WITH(librabbitmq, [AS_HELP_STRING([--with-librabbitmq@<:@=PREFIX@:>@], [Path to librabbitmq.])],
+[
+ if test "x$withval" != "xno" && test "x$withval" != "xyes"
+ then
+ with_librabbitmq_cppflags="-I$withval/include"
+ with_librabbitmq_ldflags="-L$withval/lib"
+ with_librabbitmq="yes"
+ else
+ with_librabbitmq="$withval"
+ fi
+],
+[
+ with_librabbitmq="yes"
+])
+if test "x$with_librabbitmq" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_librabbitmq_cppflags"
+
+ AC_CHECK_HEADERS(amqp.h, [with_librabbitmq="yes"], [with_librabbitmq="no (amqp.h not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_librabbitmq" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ SAVE_LDFLAGS="$LDFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_librabbitmq_cppflags"
+ LDFLAGS="$LDFLAGS $with_librabbitmq_ldflags"
+
+ AC_CHECK_LIB(rabbitmq, amqp_basic_publish, [with_librabbitmq="yes"], [with_librabbitmq="no (Symbol 'amqp_basic_publish' not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_librabbitmq" = "xyes"
+then
+ BUILD_WITH_LIBRABBITMQ_CPPFLAGS="$with_librabbitmq_cppflags"
+ BUILD_WITH_LIBRABBITMQ_LDFLAGS="$with_librabbitmq_ldflags"
+ BUILD_WITH_LIBRABBITMQ_LIBS="-lrabbitmq"
+ AC_SUBST(BUILD_WITH_LIBRABBITMQ_CPPFLAGS)
+ AC_SUBST(BUILD_WITH_LIBRABBITMQ_LDFLAGS)
+ AC_SUBST(BUILD_WITH_LIBRABBITMQ_LIBS)
+ AC_DEFINE(HAVE_LIBRABBITMQ, 1, [Define if librabbitmq is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBRABBITMQ, test "x$with_librabbitmq" = "xyes")
+# }}}
+
# --with-librouteros {{{
AC_ARG_WITH(librouteros, [AS_HELP_STRING([--with-librouteros@<:@=PREFIX@:>@], [Path to librouteros.])],
[
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
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])
with_libupsclient_config=""
with_libupsclient_cflags=""
with_libupsclient_libs=""
-AC_ARG_WITH(libupsclient, [AS_HELP_STRING([--with-libupsclient@<:@=PREFIX@:>@], [Path to the Net-SNMPD library.])],
+AC_ARG_WITH(libupsclient, [AS_HELP_STRING([--with-libupsclient@<:@=PREFIX@:>@], [Path to the upsclient library.])],
[
if test "x$withval" = "xno"
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"
AM_CONDITIONAL(BUILD_WITH_LIBYAJL, test "x$with_libyajl" = "xyes")
# }}}
+# --with-libvarnish {{{
+with_libvarnish_cppflags=""
+with_libvarnish_cflags=""
+with_libvarnish_libs=""
+AC_ARG_WITH(libvarnish, [AS_HELP_STRING([--with-libvarnish@<:@=PREFIX@:>@], [Path to libvarnish.])],
+[
+ if test "x$withval" = "xno"
+ then
+ with_libvarnish="no"
+ else if test "x$withval" = "xyes"
+ then
+ with_libvarnish="use_pkgconfig"
+ else if test -d "$with_libvarnish/lib"
+ then
+ AC_MSG_NOTICE([Not checking for libvarnish: Manually configured])
+ with_libvarnish_cflags="-I$withval/include"
+ with_libvarnish_libs="-L$withval/lib -lvarnish -lvarnishcompat -lvarnishapi"
+ with_libvarnish="yes"
+ fi; fi; fi
+],
+[with_libvarnish="use_pkgconfig"])
+
+# configure using pkg-config
+if test "x$with_libvarnish" = "xuse_pkgconfig"
+then
+ if test "x$PKG_CONFIG" = "x"
+ then
+ with_libvarnish="no (Don't have pkg-config)"
+ fi
+fi
+if test "x$with_libvarnish" = "xuse_pkgconfig"
+then
+ AC_MSG_NOTICE([Checking for varnishapi using $PKG_CONFIG])
+ $PKG_CONFIG --exists 'varnishapi' 2>/dev/null
+ if test $? -ne 0
+ then
+ with_libvarnish="no (pkg-config doesn't know 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=""
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
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"
$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
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
#
enable_plugin="yes"
force="yes"
else
- enable_plugin="no"
+ enable_plugin="no (disabled on command line)"
fi; fi
],
[
plugin_cpu="no"
plugin_cpufreq="no"
plugin_curl_json="no"
+plugin_curl_xml="no"
plugin_df="no"
plugin_disk="no"
plugin_entropy="no"
plugin_vserver="yes"
plugin_wireless="yes"
- if test "x$have_net_ip_vs_h" = "xyes" -o "x$have_ip_vs_h" = "xyes"
+ if test "x$have_net_ip_vs_h" = "xyes" || test "x$have_ip_vs_h" = "xyes"
then
plugin_ipvs="yes"
fi
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
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"
plugin_curl_json="yes"
fi
+if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
+then
+ plugin_curl_xml="yes"
+fi
+
if test "x$have_processor_info" = "xyes"
then
plugin_cpu="yes"
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"
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
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])
AC_PLUGIN([csv], [yes], [CSV output plugin])
AC_PLUGIN([curl], [$with_libcurl], [CURL generic web statistics])
AC_PLUGIN([curl_json], [$plugin_curl_json], [CouchDB statistics])
+AC_PLUGIN([curl_xml], [$plugin_curl_xml], [CURL generic xml statistics])
AC_PLUGIN([dbi], [$with_libdbi], [General database statistics])
AC_PLUGIN([df], [$plugin_df], [Filesystem usage statistics])
AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics])
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])
AC_PLUGIN([memcachec], [$with_libmemcached], [memcachec statistics])
AC_PLUGIN([memcached], [yes], [memcached statistics])
AC_PLUGIN([memory], [$plugin_memory], [Memory usage])
+AC_PLUGIN([modbus], [$with_libmodbus], [Modbus plugin])
AC_PLUGIN([multimeter], [$plugin_multimeter], [Read multimeter values])
AC_PLUGIN([mysql], [$with_libmysql], [MySQL statistics])
AC_PLUGIN([netapp], [$with_libnetapp], [NetApp plugin])
AC_PLUGIN([openvpn], [yes], [OpenVPN client statistics])
AC_PLUGIN([oracle], [$with_oracle], [Oracle plugin])
AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter])
+# FIXME: Check for libevent, too.
+AC_PLUGIN([pinba], [$have_protoc_c], [Pinba statistics])
AC_PLUGIN([ping], [$with_liboping], [Network latency statistics])
AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics])
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])
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])
with_librrd="yes (warning: librrd is not thread-safe)"
fi
-if test "x$with_libiptc" = "xyes" -a "x$with_own_libiptc" = "xyes"
+if test "x$with_libiptc" = "xyes" && test "x$with_own_libiptc" = "xyes"
then
with_libiptc="yes (shipped version)"
fi
Libraries:
libcurl . . . . . . . $with_libcurl
libdbi . . . . . . . $with_libdbi
+ libcredis . . . . . . $with_libcredis
libesmtp . . . . . . $with_libesmtp
libganglia . . . . . $with_libganglia
libgcrypt . . . . . . $with_libgcrypt
libkstat . . . . . . $with_kstat
libkvm . . . . . . . $with_libkvm
libmemcached . . . . $with_libmemcached
+ libmodbus . . . . . . $with_libmodbus
libmysql . . . . . . $with_libmysql
libnetapp . . . . . . $with_libnetapp
libnetlink . . . . . $with_libnetlink
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
libyajl . . . . . . . $with_libyajl
+ libevent . . . . . . $with_libevent
+ protobuf-c . . . . . $have_protoc_c
oracle . . . . . . . $with_oracle
python . . . . . . . $with_python
perl . . . . . . . . $with_perl_bindings
Modules:
+ amqp . . . . . . . $enable_amqp
apache . . . . . . . $enable_apache
apcups . . . . . . . $enable_apcups
apple_sensors . . . . $enable_apple_sensors
csv . . . . . . . . . $enable_csv
curl . . . . . . . . $enable_curl
curl_json . . . . . . $enable_curl_json
+ curl_xml . . . . . . $enable_curl_xml
dbi . . . . . . . . . $enable_dbi
df . . . . . . . . . $enable_df
disk . . . . . . . . $enable_disk
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
memcachec . . . . . . $enable_memcachec
memcached . . . . . . $enable_memcached
memory . . . . . . . $enable_memory
+ modbus . . . . . . . $enable_modbus
multimeter . . . . . $enable_multimeter
mysql . . . . . . . . $enable_mysql
netapp . . . . . . . $enable_netapp
openvpn . . . . . . . $enable_openvpn
oracle . . . . . . . $enable_oracle
perl . . . . . . . . $enable_perl
+ pinba . . . . . . . . $enable_pinba
ping . . . . . . . . $enable_ping
postgresql . . . . . $enable_postgresql
powerdns . . . . . . $enable_powerdns
processes . . . . . . $enable_processes
protocols . . . . . . $enable_protocols
python . . . . . . . $enable_python
+ redis . . . . . . . . $enable_redis
routeros . . . . . . $enable_routeros
rrdcached . . . . . . $enable_rrdcached
rrdtool . . . . . . . $enable_rrdtool
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
# </Value>
</MBean>
+ ######################################
+ # Define the "jmx_memory" type as: #
+ # jmx_memory value:GAUGE:0:U #
+ # See types.db(5) for details. #
+ ######################################
+
# Generic heap/nonheap memory usage.
<MBean "memory">
ObjectName "java.lang:type=Memory"
# Creates four values: committed, init, max, used
<Value>
- Type "memory"
+ Type "jmx_memory"
#InstancePrefix ""
#InstanceFrom ""
Table true
# Creates four values: committed, init, max, used
<Value>
- Type "memory"
+ Type "jmx_memory"
#InstancePrefix ""
#InstanceFrom ""
Table true
Attribute "NonHeapMemoryUsage"
+ InstancePrefix "nonheap-"
</Value>
</MBean>
InstanceFrom "name"
<Value>
- Type "memory"
+ Type "jmx_memory"
#InstancePrefix ""
#InstanceFrom ""
Table true
my $DIR = "/var/lib/collectd";
my $HOST = undef;
my $IMG_FMT = "PNG";
-my $RECURSIVE = 0;
+my $RECURSIVE = 1;
GetOptions (
"host=s" => \$HOST,
my @COLORS = (0xff7777, 0x7777ff, 0x55ff55, 0xffcc77, 0xff77ff, 0x77ffff,
0xffff77, 0x55aaff);
-my @tmp = `/bin/hostname`; chomp(@tmp);
+my @tmp = `/bin/hostname -f`; chomp(@tmp);
$HOST = $tmp[0] if (! defined $HOST);
my $svg_p = ($IMG_FMT eq "SVG");
my $IMG_SFX = $svg_p ? ".svg" : ".png";
my $IMG_DIR = "${HOST}.dir";
-my $HTML = "${HOST}.html";
+my $HTML = "${HOST}.xhtml";
################################################################################
#
my $title="Rrd plot for $HOST";
print OUT <<END;
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- "http://www.w3.org/TR/html4/loose.dtd">
-<html>
+<!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
<head>
+<style type="text/css" media="screen">
+.graph { text-align: center; }
+object.graph { width: 670; height: 179; }
+</style>
<title>$title</title>
+<meta http-equiv="Content-Type"
+ content="application/xhtml+xml; charset=us-ascii" />
</head>
<body>
-<center>
END
# list interesting rrd
}
chomp(@list);
-foreach my $rrd (sort @list){
+@list = sort @list;
+foreach my $rrd (@list){
$rrd =~ m/^$DIR\/(.*)\.rrd$/;
push(@rrds, $1);
}
# table of contents
print OUT <<END;
-<A name="top"></A><H1>$title</H1>
-<P>
+<h1><a id="top">$title</a></h1>
+<p>
END
foreach my $bn (@rrds){
my $cleaned_bn = $bn;
$cleaned_bn =~ tr/%\//__/;
print OUT <<END;
-<A href="#$cleaned_bn">$bn</A>
+<a href="#$cleaned_bn">$bn</a>
END
}
print OUT <<END;
-</P>
+</p>
END
# graph interesting rrd
my $cleaned_bn = $bn;
$cleaned_bn =~ tr/%\//__/;
print OUT <<END;
-<A name="$cleaned_bn"></A><H1>$bn</H1>
+<h2><a id="$cleaned_bn">$bn</a></h2>
END
# graph various ranges
my $cleaned_img = $img; $cleaned_img =~ s/%/%25/g;
if (! $svg_p) {
print OUT <<END;
-<P><IMG src="$cleaned_img" alt="${bn} $span"></P>
+<p class="graph"><img src="$cleaned_img" alt="${bn} $span" /></p>
END
} else {
print OUT <<END;
-<P><object data="$cleaned_img" type="image/svg+xml"
- width="670" height="179">
- ${bn} $span</object></P>
+<p class="graph"><object data="$cleaned_img" type="image/svg+xml">
+ ${bn} $span</object></p>
END
}
}
print OUT <<END;
-<A href="#top">[top]</A>
+<p><a href="#top">[top]</a></p>
END
}
print OUT <<END;
-</center>
+<hr />
+<p>
+ <a href="http://validator.w3.org/check?uri=referer"><img
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
+</p>
</body>
</html>
END
# 3. This notice may not be removed or altered from any source distribution.
import socket
+import sys
class Collectd():
def __init__(self, path='/var/run/collectd-unixsock', noisy=False):
self.noisy = noisy
- self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- self._sock.connect(path)
+ self.path = path
+ self._sock = self._connect()
def flush(self, timeout=None, plugins=[], identifiers=[]):
"""Send a FLUSH command.
return self._cmd('PUTVAL %s' % ' '.join(args))
def _cmd(self, c):
+ try:
+ return self._cmdattempt(c)
+ except socket.error, (errno, errstr):
+ sys.stderr.write("[error] Sending to socket failed: [%d] %s\n"
+ % (errno, errstr))
+ self._sock = self._connect()
+ return self._cmdattempt(c)
+
+ def _cmdattempt(self, c):
if self.noisy:
print "[send] %s" % c
+ if not self._sock:
+ sys.stderr.write("[error] Socket unavailable. Can not send.")
+ return False
self._sock.send(c + "\n")
status_message = self._readline()
if self.noisy:
return int(code)
return False
+ def _connect(self):
+ try:
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(self.path)
+ if self.noisy:
+ print "[socket] connected to %s" % self.path
+ return sock
+ except socket.error, (errno, errstr):
+ sys.stderr.write("[error] Connecting to socket failed: [%d] %s"
+ % (errno, errstr))
+ return None
+
def _readline(self):
"""Read single line from socket"""
- data = ''
- buf = []
- recv = self._sock.recv
- while data != "\n":
- data = recv(1)
- if not data:
- break
- if data != "\n":
- buf.append(data)
- return ''.join(buf)
+ if not self._sock:
+ sys.stderr.write("[error] Socket unavailable. Can not read.")
+ return None
+ try:
+ data = ''
+ buf = []
+ recv = self._sock.recv
+ while data != "\n":
+ data = recv(1)
+ if not data:
+ break
+ if data != "\n":
+ buf.append(data)
+ return ''.join(buf)
+ except socket.error, (errno, errstr):
+ sys.stderr.write("[error] Reading from socket failed: [%d] %s"
+ % (errno, errstr))
+ self._sock = self._connect()
+ return None
def _readlines(self, sizehint=0):
"""Read multiple lines from socket"""
return list
def __del__(self):
- self._sock.close()
+ if not self._sock:
+ return
+ try:
+ self._sock.close()
+ except socket.error, (errno, errstr):
+ sys.stderr.write("[error] Closing socket failed: [%d] %s"
+ % (errno, errstr))
if __name__ == '__main__':
'GPRINT:avg:LAST:%5.1lf%s Last',
'GPRINT:avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
],
+ apache_connections => ['DEF:min={file}:count:MIN',
+ 'DEF:avg={file}:count:AVERAGE',
+ 'DEF:max={file}:count:MAX',
+ "AREA:max#$HalfBlue",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullBlue:Connections",
+ 'GPRINT:min:MIN:%6.2lf Min,',
+ 'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+ 'GPRINT:max:MAX:%6.2lf Max,',
+ 'GPRINT:avg:LAST:%6.2lf Last'
+ ],
+ apache_idle_workers => ['DEF:min={file}:count:MIN',
+ 'DEF:avg={file}:count:AVERAGE',
+ 'DEF:max={file}:count:MAX',
+ "AREA:max#$HalfBlue",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullBlue:Idle Workers",
+ 'GPRINT:min:MIN:%6.2lf Min,',
+ 'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+ 'GPRINT:max:MAX:%6.2lf Max,',
+ 'GPRINT:avg:LAST:%6.2lf Last'
+ ],
apache_requests => ['DEF:min={file}:count:MIN',
'DEF:avg={file}:count:AVERAGE',
'DEF:max={file}:count:MAX',
RRDOptions -l 0
DSName "value Hit ratio"
</Type>
+<Type bitrate>
+ DataSources value
+ RRDTitle "Bitrate ({instance})"
+ RRDVerticalLabel "Bit/s"
+ RRDFormat "%5.1lf%s"
+ DSName "value Bitrate"
+</Type>
<Type cache_ratio>
DataSources value
DSName value Percent
RRDVerticalLabel "Byte"
RRDFormat "%5.1lf%s"
DSName "sis_saved SIS saved "
+ DSName "reserved Reserved "
DSName "free Free "
DSName "used Used "
DSName "snap_normal_used Snap used (normal)"
DSName "snap_reserved Snap reserved "
DSName "snap_reserve_used Snap used (resv) "
- Order sis_saved free used snap_normal_used snap_reserved snap_reserve_used
+ Order sis_saved reserved free used snap_normal_used snap_reserved snap_reserve_used
Color sis_saved 00e0e0
+ Color reserved ffb000
Color free 00ff00
Color snap_reverse ff8000
Color used ff0000
DSName "buffered Buffered"
DSName "locked Locked "
DSName "used Used "
+ DSName "available Available "
+ DSName "system_cache System Cache "
+ DSName "pool_paged Paged Pool "
+ DSName "pool_nonpaged Nonpaged Pool"
+ DSName "working_set Working Set "
+ DSName "system_code System Code "
+ DSName "system_driver System Driver"
#Order used buffered cached free
- Order free cached buffered used
+ Order free cached buffered used available system_cache system_driver system_code pool_paged pool_nonpaged working_set
Color free 00e000
Color cached 0000ff
Color buffered ffb000
Color locked ff00ff
Color used ff0000
+ Color available 00e000
+ Color system_cache 0000ff
+ Color system_driver ff00ff
+ Color system_code a000a0
+ Color pool_paged ffb000
+ Color pool_nonpaged ff8000
+ Color working_set ff0000
</Type>
<Type mysql_commands>
Module GenericStacked
<Type ps_cputime>
Module PsCputime
</Type>
+<Type ps_disk_octets>
+ Module GenericIO
+ DataSources read write
+ DSName "read Read "
+ DSName write Written
+ RRDTitle "Process disk traffic ({instance})"
+ RRDVerticalLabel "Bytes per second"
+# RRDOptions ...
+ RRDFormat "%5.1lf%s"
+</Type>
<Type ps_rss>
DataSources value
DSName value RSS
Color blocked ff00ff
Color stopped a000a0
</Type>
+<Type signal_power>
+ DataSources value
+ RRDTitle "Signal power ({instance})"
+ RRDVerticalLabel "dB"
+ RRDFormat "%5.1lf"
+ DSName "value Signal power"
+</Type>
+<Type signal_quality>
+ DataSources value
+ RRDTitle "Signal quality ({instance})"
+ RRDVerticalLabel "Percent"
+ RRDFormat "%5.1lf%%"
+ DSName "value Signal quality"
+</Type>
+<Type snr>
+ DataSources value
+ RRDTitle "Signal / noise ratio ({instance})"
+ RRDVerticalLabel "dBm"
+ RRDFormat "%5.1lf"
+ DSName "value S/N"
+</Type>
<Type swap>
Module GenericStacked
DataSources value
for (my $i = 0; $i < @files; $i++)
{
my $file = $files[$i];
- my $key = $file->{'plugin_instance'} || '';
+ my $key1 = $file->{'hostname'} || '';
+ my $key2 = $file->{'plugin_instance'} || '';
+ my $key = "$key1-$key2";
$data->{$key} ||= [];
push (@{$data->{$key}}, $file);
our $ConfigFile = '/etc/exec-munin.conf';
our $TypeMap = {};
our $Scripts = [];
-our $Interval = 300;
+our $Interval = defined ($ENV{'COLLECTD_INTERVAL'}) ? (0 + $ENV{'COLLECTD_INTERVAL'}) : 300;
+our $Hostname = defined ($ENV{'COLLECTD_HOSTNAME'}) ? $ENV{'COLLECTD_HOSTNAME'} : '';
main ();
exit (0);
my $pinst;
my $time = time ();
my $script = shift;
- my $host = hostname () || 'localhost';
+ my $host = $Hostname || hostname () || 'localhost';
if (!open ($fh, '-|', $script))
{
print STDERR "Cannot execute $script: $!";
my $field = $1;
my $value = $2;
my $type = (defined ($TypeMap->{$field})) ? $TypeMap->{$field} : $field;
+ my $ident = "$host/munin-$pinst/$type";
- print "$host/munin-$pinst/$type interval=$Interval $time:$value\n";
+ $ident =~ s/"/\\"/g;
+
+ print qq(PUTVAL "$ident" interval=$Interval $time:$value\n);
}
}
our $ConfigFile = '/etc/exec-nagios.conf';
our $TypeMap = {};
our $Scripts = [];
-our $Interval = 300;
+our $Interval = defined ($ENV{'COLLECTD_INTERVAL'}) ? (0 + $ENV{'COLLECTD_INTERVAL'}) : 300;
+our $Hostname = defined ($ENV{'COLLECTD_HOSTNAME'}) ? $ENV{'COLLECTD_HOSTNAME'} : '';
main ();
exit (0);
=back
+=back
+
=cut
sub handle_config_addtype
my $type = shift;
my $time = shift;
my $line = shift;
+ my $ident = "$host/$plugin-$pinst/$type-$tinst";
my $tinst;
my $value;
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
my $time = time ();
my $script = shift;
my @args = ();
- my $host = hostname () || 'localhost';
+ my $host = $Hostname || hostname () || 'localhost';
my $state = 0;
my $serviceoutput;
if ($perfdata)
{
- push (@serviceperfdata, split (' ', $perfdata));
+ push (@serviceperfdata, split (' ', $perfdata));
}
$state = 1;
if ($perfdata)
{
- push (@serviceperfdata, split (' ', $perfdata));
- $state = 2;
+ push (@serviceperfdata, split (' ', $perfdata));
+ $state = 2;
}
}
else # ($state == 2)
for (@serviceperfdata)
{
handle_performance_data ($host, 'nagios', $pinst, $script->{'type'},
- $time, $_);
+ $time, $_);
}
}
} # execute_script
# 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
--- /dev/null
+#!/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 <octo at collectd.org>
+
+use strict;
+use warnings;
+
+use Getopt::Long ('GetOptions');
+use Data::Dumper ();
+use File::Basename ('dirname');
+
+our $InDir = '/var/lib/collectd';
+our $OutDir = '/tmp/collectd-5';
+our $RRDtool = 'rrdtool';
+
+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"],
+ df => ["used", "free"],
+ 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 "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 <<EOF;
+migrate-4-5.px [OPTIONS]
+
+Valid options are:
+
+ --indir <dir> Source directory
+ Default: $InDir
+ --outdir <dir> Destination directory
+ Default: $OutDir
+ --rrdtool <path> Path to the RRDtool binary
+ Default: $RRDtool
+
+EOF
+ exit (1);
+} # }}} sub exit_usage
+
+GetOptions ("indir|i=s" => \$InDir,
+ "outdir|o=s" => \$OutDir,
+ "rrdtool=s" => \$RRDtool,
+ "help|h" => \&exit_usage) or exit_usage ();
+
+scan_dir ($InDir);
+
+# vim: set sw=2 sts=2 et fdm=marker :
--- /dev/null
+#!/usr/bin/python
+
+###############################################################################
+# WARNING! Importing this script will break the exec plugin! #
+###############################################################################
+# Use this if you want to create new processes from your python scripts. #
+# Normally you will get a OSError exception when the new process terminates #
+# because collectd will ignore the SIGCHLD python is waiting for. #
+# This script will restore the default SIGCHLD behavior so python scripts can #
+# create new processes without errors. #
+###############################################################################
+# WARNING! Importing this script will break the exec plugin! #
+###############################################################################
+
+import signal
+import collectd
+
+def init():
+ signal.signal(signal.SIGCHLD, signal.SIG_DFL)
+
+collectd.register_init(init)
#!/bin/bash
#
# collectd Startup script for the Collectd statistics gathering daemon
-# chkconfig: - 86 15
+# chkconfig: - 99 01
# description: Collectd is a statistics gathering daemon used to collect \
# system information ie. cpu, memory, disk, network
# processname: collectd
RETVAL=0
ARGS=""
prog="collectdmon"
+service="collectd"
CONFIG=/etc/collectd.conf
COLLECTD=/usr/sbin/collectd
COLLECTDMONPID=/var/run/collectdmon.pid
daemon $prog -P $COLLECTDMONPID -c $COLLECTD -- -C "$CONFIG"
RETVAL=$?
echo
- [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
+ [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$service
fi
}
stop () {
killproc $prog
RETVAL=$?
echo
- [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
+ [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$service
}
# See how we were called.
case "$1" in
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 \
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)
if BUILD_WITH_LIBRT
collectd_LDADD += -lrt
endif
+if BUILD_WITH_LIBPOSIX4
+collectd_LDADD += -lposix4
+endif
if BUILD_WITH_LIBSOCKET
collectd_LDADD += -lsocket
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
collectd_DEPENDENCIES += curl_json.la
endif
+if BUILD_PLUGIN_CURL_XML
+pkglib_LTLIBRARIES += curl_xml.la
+curl_xml_la_SOURCES = curl_xml.c
+curl_xml_la_LDFLAGS = -module -avoid-version
+curl_xml_la_CFLAGS = $(AM_CFLAGS) \
+ $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+curl_xml_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
+collectd_LDADD += "-dlopen" curl_xml.la
+collectd_DEPENDENCIES += curl_xml.la
+endif
+
if BUILD_PLUGIN_DBI
pkglib_LTLIBRARIES += dbi.la
dbi_la_SOURCES = dbi.c \
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
endif
endif
+if BUILD_PLUGIN_MODBUS
+pkglib_LTLIBRARIES += modbus.la
+modbus_la_SOURCES = modbus.c
+modbus_la_LDFLAGS = -module -avoid-version
+modbus_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMODBUS_CFLAGS)
+modbus_la_LIBADD = $(BUILD_WITH_LIBMODBUS_LIBS)
+collectd_LDADD += "-dlopen" modbus.la
+collectd_DEPENDENCIES += modbus.la
+endif
+
if BUILD_PLUGIN_MULTIMETER
pkglib_LTLIBRARIES += multimeter.la
multimeter_la_SOURCES = multimeter.c
collectd_DEPENDENCIES += perl.la
endif
+if BUILD_PLUGIN_PINBA
+BUILT_SOURCES += pinba.pb-c.c pinba.pb-c.h
+CLEANFILES += pinba.pb-c.c pinba.pb-c.h
+pkglib_LTLIBRARIES += pinba.la
+pinba_la_SOURCES = pinba.c
+pinba_la_LDFLAGS = -module -avoid-version
+pinba_la_LIBADD = -lprotobuf-c
+collectd_LDADD += "-dlopen" pinba.la
+collectd_DEPENDENCIES += pinba.la
+endif
+
if BUILD_PLUGIN_PING
pkglib_LTLIBRARIES += ping.la
ping_la_SOURCES = ping.c
pkglib_LTLIBRARIES += python.la
python_la_SOURCES = python.c pyconfig.c pyvalues.c cpython.h
python_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_PYTHON_CPPFLAGS)
+python_la_CFLAGS = $(AM_CFLAGS)
+if COMPILER_IS_GCC
+python_la_CFLAGS += -fno-strict-aliasing -Wno-strict-aliasing
+endif
python_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_PYTHON_LDFLAGS)
python_la_LIBADD = $(BUILD_WITH_PYTHON_LIBS)
collectd_LDADD += "-dlopen" python.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
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
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
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
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
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
collectd_DEPENDENCIES += zfs_arc.la
endif
-
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
#collectd_1_SOURCES = collectd.pod
-EXTRA_DIST = types.db
+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 \
collectd-python.pod \
collectd.pod \
collectd-snmp.pod \
+ collectd-threshold.pod \
collectd-unixsock.pod \
postgresql_default.conf \
types.db.pod
echo "$@ has some POD errors!"; false; \
fi
+pinba.pb-c.c pinba.pb-c.h: pinba.proto
+ protoc-c --c_out $(builddir) pinba.proto
+
install-exec-hook:
$(mkinstalldirs) $(DESTDIR)$(sysconfdir)
if test -e $(DESTDIR)$(sysconfdir)/collectd.conf; \
--- /dev/null
+/**
+ * collectd - src/amqp.c
+ * Copyright (C) 2009 Sebastien Pahl
+ * Copyright (C) 2010 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Sebastien Pahl <sebastien.pahl at dotcloud.com>
+ * Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_cmd_putval.h"
+#include "utils_format_json.h"
+
+#include <pthread.h>
+
+#include <amqp.h>
+#include <amqp_framing.h>
+
+/* Defines for the delivery mode. I have no idea why they're not defined by the
+ * library.. */
+#define CAMQP_DM_VOLATILE 1
+#define CAMQP_DM_PERSISTENT 2
+
+#define CAMQP_FORMAT_COMMAND 1
+#define CAMQP_FORMAT_JSON 2
+
+#define CAMQP_CHANNEL 1
+
+/*
+ * Data types
+ */
+struct camqp_config_s
+{
+ _Bool publish;
+ char *name;
+
+ char *host;
+ int port;
+ char *vhost;
+ char *user;
+ char *password;
+
+ char *exchange;
+ char *routing_key;
+
+ /* publish only */
+ uint8_t delivery_mode;
+ _Bool store_rates;
+ int format;
+
+ /* subscribe only */
+ char *exchange_type;
+ char *queue;
+
+ amqp_connection_state_t connection;
+ pthread_mutex_t lock;
+};
+typedef struct camqp_config_s camqp_config_t;
+
+/*
+ * Global variables
+ */
+static const char *def_host = "localhost";
+static const char *def_vhost = "/";
+static const char *def_user = "guest";
+static const char *def_password = "guest";
+static const char *def_exchange = "amq.fanout";
+
+static pthread_t *subscriber_threads = NULL;
+static size_t subscriber_threads_num = 0;
+static _Bool subscriber_threads_running = 1;
+
+#define CONF(c,f) (((c)->f != NULL) ? (c)->f : def_##f)
+
+/*
+ * Functions
+ */
+static void camqp_close_connection (camqp_config_t *conf) /* {{{ */
+{
+ int sockfd;
+
+ if ((conf == NULL) || (conf->connection == NULL))
+ return;
+
+ sockfd = amqp_get_sockfd (conf->connection);
+ amqp_channel_close (conf->connection, CAMQP_CHANNEL, AMQP_REPLY_SUCCESS);
+ amqp_connection_close (conf->connection, AMQP_REPLY_SUCCESS);
+ amqp_destroy_connection (conf->connection);
+ close (sockfd);
+ conf->connection = NULL;
+} /* }}} void camqp_close_connection */
+
+static void camqp_config_free (void *ptr) /* {{{ */
+{
+ camqp_config_t *conf = ptr;
+
+ if (conf == NULL)
+ return;
+
+ camqp_close_connection (conf);
+
+ sfree (conf->name);
+ sfree (conf->host);
+ sfree (conf->vhost);
+ sfree (conf->user);
+ sfree (conf->password);
+ sfree (conf->exchange);
+ sfree (conf->exchange_type);
+ sfree (conf->queue);
+ sfree (conf->routing_key);
+
+ sfree (conf);
+} /* }}} void camqp_config_free */
+
+static char *camqp_bytes_cstring (amqp_bytes_t *in) /* {{{ */
+{
+ char *ret;
+
+ if ((in == NULL) || (in->bytes == NULL))
+ return (NULL);
+
+ ret = malloc (in->len + 1);
+ if (ret == NULL)
+ return (NULL);
+
+ memcpy (ret, in->bytes, in->len);
+ ret[in->len] = 0;
+
+ return (ret);
+} /* }}} char *camqp_bytes_cstring */
+
+static _Bool camqp_is_error (camqp_config_t *conf) /* {{{ */
+{
+ amqp_rpc_reply_t r;
+
+ r = amqp_get_rpc_reply (conf->connection);
+ if (r.reply_type == AMQP_RESPONSE_NORMAL)
+ return (0);
+
+ return (1);
+} /* }}} _Bool camqp_is_error */
+
+static char *camqp_strerror (camqp_config_t *conf, /* {{{ */
+ char *buffer, size_t buffer_size)
+{
+ amqp_rpc_reply_t r;
+
+ r = amqp_get_rpc_reply (conf->connection);
+ switch (r.reply_type)
+ {
+ case AMQP_RESPONSE_NORMAL:
+ sstrncpy (buffer, "Success", sizeof (buffer));
+ break;
+
+ case AMQP_RESPONSE_NONE:
+ sstrncpy (buffer, "Missing RPC reply type", sizeof (buffer));
+ break;
+
+ case AMQP_RESPONSE_LIBRARY_EXCEPTION:
+ if (r.library_errno)
+ return (sstrerror (r.library_errno, buffer, buffer_size));
+ else
+ sstrncpy (buffer, "End of stream", sizeof (buffer));
+ break;
+
+ case AMQP_RESPONSE_SERVER_EXCEPTION:
+ if (r.reply.id == AMQP_CONNECTION_CLOSE_METHOD)
+ {
+ amqp_connection_close_t *m = r.reply.decoded;
+ char *tmp = camqp_bytes_cstring (&m->reply_text);
+ ssnprintf (buffer, buffer_size, "Server connection error %d: %s",
+ m->reply_code, tmp);
+ sfree (tmp);
+ }
+ else if (r.reply.id == AMQP_CHANNEL_CLOSE_METHOD)
+ {
+ amqp_channel_close_t *m = r.reply.decoded;
+ char *tmp = camqp_bytes_cstring (&m->reply_text);
+ ssnprintf (buffer, buffer_size, "Server channel error %d: %s",
+ m->reply_code, tmp);
+ sfree (tmp);
+ }
+ else
+ {
+ ssnprintf (buffer, buffer_size, "Server error method %#"PRIx32,
+ r.reply.id);
+ }
+ break;
+
+ default:
+ ssnprintf (buffer, buffer_size, "Unknown reply type %i",
+ (int) r.reply_type);
+ }
+
+ return (buffer);
+} /* }}} char *camqp_strerror */
+
+static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */
+{
+ amqp_exchange_declare_ok_t *ed_ret;
+
+ if (conf->exchange_type == NULL)
+ return (0);
+
+ ed_ret = amqp_exchange_declare (conf->connection,
+ /* channel = */ CAMQP_CHANNEL,
+ /* exchange = */ amqp_cstring_bytes (conf->exchange),
+ /* type = */ amqp_cstring_bytes (conf->exchange_type),
+ /* passive = */ 0,
+ /* durable = */ 0,
+ /* auto_delete = */ 1,
+ /* arguments = */ AMQP_EMPTY_TABLE);
+ if ((ed_ret == NULL) && camqp_is_error (conf))
+ {
+ char errbuf[1024];
+ ERROR ("amqp plugin: amqp_exchange_declare failed: %s",
+ camqp_strerror (conf, errbuf, sizeof (errbuf)));
+ camqp_close_connection (conf);
+ return (-1);
+ }
+
+ INFO ("amqp plugin: Successfully created exchange \"%s\" "
+ "with type \"%s\".",
+ conf->exchange, conf->exchange_type);
+
+ return (0);
+} /* }}} int camqp_create_exchange */
+
+static int camqp_setup_queue (camqp_config_t *conf) /* {{{ */
+{
+ amqp_queue_declare_ok_t *qd_ret;
+ amqp_basic_consume_ok_t *cm_ret;
+
+ qd_ret = amqp_queue_declare (conf->connection,
+ /* channel = */ CAMQP_CHANNEL,
+ /* queue = */ (conf->queue != NULL)
+ ? amqp_cstring_bytes (conf->queue)
+ : AMQP_EMPTY_BYTES,
+ /* passive = */ 0,
+ /* durable = */ 0,
+ /* exclusive = */ 0,
+ /* auto_delete = */ 1,
+ /* arguments = */ AMQP_EMPTY_TABLE);
+ if (qd_ret == NULL)
+ {
+ ERROR ("amqp plugin: amqp_queue_declare failed.");
+ camqp_close_connection (conf);
+ return (-1);
+ }
+
+ if (conf->queue == NULL)
+ {
+ conf->queue = camqp_bytes_cstring (&qd_ret->queue);
+ if (conf->queue == NULL)
+ {
+ ERROR ("amqp plugin: camqp_bytes_cstring failed.");
+ camqp_close_connection (conf);
+ return (-1);
+ }
+
+ INFO ("amqp plugin: Created queue \"%s\".", conf->queue);
+ }
+ DEBUG ("amqp plugin: Successfully created queue \"%s\".", conf->queue);
+
+ /* bind to an exchange */
+ if (conf->exchange != NULL)
+ {
+ amqp_queue_bind_ok_t *qb_ret;
+
+ assert (conf->queue != NULL);
+ qb_ret = amqp_queue_bind (conf->connection,
+ /* channel = */ CAMQP_CHANNEL,
+ /* queue = */ amqp_cstring_bytes (conf->queue),
+ /* exchange = */ amqp_cstring_bytes (conf->exchange),
+ /* routing_key = */ (conf->routing_key != NULL)
+ ? amqp_cstring_bytes (conf->routing_key)
+ : AMQP_EMPTY_BYTES,
+ /* arguments = */ AMQP_EMPTY_TABLE);
+ if ((qb_ret == NULL) && camqp_is_error (conf))
+ {
+ char errbuf[1024];
+ ERROR ("amqp plugin: amqp_queue_bind failed: %s",
+ camqp_strerror (conf, errbuf, sizeof (errbuf)));
+ camqp_close_connection (conf);
+ return (-1);
+ }
+
+ DEBUG ("amqp plugin: Successfully bound queue \"%s\" to exchange \"%s\".",
+ conf->queue, conf->exchange);
+ } /* if (conf->exchange != NULL) */
+
+ cm_ret = amqp_basic_consume (conf->connection,
+ /* channel = */ CAMQP_CHANNEL,
+ /* queue = */ amqp_cstring_bytes (conf->queue),
+ /* consumer_tag = */ AMQP_EMPTY_BYTES,
+ /* no_local = */ 0,
+ /* no_ack = */ 1,
+ /* exclusive = */ 0);
+ if ((cm_ret == NULL) && camqp_is_error (conf))
+ {
+ char errbuf[1024];
+ ERROR ("amqp plugin: amqp_basic_consume failed: %s",
+ camqp_strerror (conf, errbuf, sizeof (errbuf)));
+ camqp_close_connection (conf);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int camqp_setup_queue */
+
+static int camqp_connect (camqp_config_t *conf) /* {{{ */
+{
+ amqp_rpc_reply_t reply;
+ int sockfd;
+ int status;
+
+ if (conf->connection != NULL)
+ return (0);
+
+ conf->connection = amqp_new_connection ();
+ if (conf->connection == NULL)
+ {
+ ERROR ("amqp plugin: amqp_new_connection failed.");
+ return (ENOMEM);
+ }
+
+ sockfd = amqp_open_socket (CONF(conf, host), conf->port);
+ if (sockfd < 0)
+ {
+ char errbuf[1024];
+ status = (-1) * sockfd;
+ ERROR ("amqp plugin: amqp_open_socket failed: %s",
+ sstrerror (status, errbuf, sizeof (errbuf)));
+ amqp_destroy_connection (conf->connection);
+ conf->connection = NULL;
+ return (status);
+ }
+ amqp_set_sockfd (conf->connection, sockfd);
+
+ reply = amqp_login (conf->connection, CONF(conf, vhost),
+ /* channel max = */ 0,
+ /* frame max = */ 131072,
+ /* heartbeat = */ 0,
+ /* authentication = */ AMQP_SASL_METHOD_PLAIN,
+ CONF(conf, user), CONF(conf, password));
+ if (reply.reply_type != AMQP_RESPONSE_NORMAL)
+ {
+ ERROR ("amqp plugin: amqp_login (vhost = %s, user = %s) failed.",
+ CONF(conf, vhost), CONF(conf, user));
+ amqp_destroy_connection (conf->connection);
+ close (sockfd);
+ conf->connection = NULL;
+ return (1);
+ }
+
+ amqp_channel_open (conf->connection, /* channel = */ 1);
+ /* FIXME: Is checking "reply.reply_type" really correct here? How does
+ * it get set? --octo */
+ if (reply.reply_type != AMQP_RESPONSE_NORMAL)
+ {
+ ERROR ("amqp plugin: amqp_channel_open failed.");
+ amqp_connection_close (conf->connection, AMQP_REPLY_SUCCESS);
+ amqp_destroy_connection (conf->connection);
+ close(sockfd);
+ conf->connection = NULL;
+ return (1);
+ }
+
+ INFO ("amqp plugin: Successfully opened connection to vhost \"%s\" "
+ "on %s:%i.", CONF(conf, vhost), CONF(conf, host), conf->port);
+
+ status = camqp_create_exchange (conf);
+ if (status != 0)
+ return (status);
+
+ if (!conf->publish)
+ return (camqp_setup_queue (conf));
+ return (0);
+} /* }}} int camqp_connect */
+
+static int camqp_shutdown (void) /* {{{ */
+{
+ size_t i;
+
+ DEBUG ("amqp plugin: Shutting down %zu subscriber threads.",
+ subscriber_threads_num);
+
+ subscriber_threads_running = 0;
+ for (i = 0; i < subscriber_threads_num; i++)
+ {
+ /* FIXME: Sending a signal is not very elegant here. Maybe find out how
+ * to use a timeout in the thread and check for the variable in regular
+ * intervals. */
+ pthread_kill (subscriber_threads[i], SIGTERM);
+ pthread_join (subscriber_threads[i], /* retval = */ NULL);
+ }
+
+ subscriber_threads_num = 0;
+ sfree (subscriber_threads);
+
+ DEBUG ("amqp plugin: All subscriber threads exited.");
+
+ return (0);
+} /* }}} int camqp_shutdown */
+
+/*
+ * Subscribing code
+ */
+static int camqp_read_body (camqp_config_t *conf, /* {{{ */
+ size_t body_size, const char *content_type)
+{
+ char body[body_size + 1];
+ char *body_ptr;
+ size_t received;
+ amqp_frame_t frame;
+ int status;
+
+ memset (body, 0, sizeof (body));
+ body_ptr = &body[0];
+ received = 0;
+
+ while (received < body_size)
+ {
+ status = amqp_simple_wait_frame (conf->connection, &frame);
+ if (status < 0)
+ {
+ char errbuf[1024];
+ status = (-1) * status;
+ ERROR ("amqp plugin: amqp_simple_wait_frame failed: %s",
+ sstrerror (status, errbuf, sizeof (errbuf)));
+ camqp_close_connection (conf);
+ return (status);
+ }
+
+ if (frame.frame_type != AMQP_FRAME_BODY)
+ {
+ NOTICE ("amqp plugin: Unexpected frame type: %#"PRIx8,
+ frame.frame_type);
+ return (-1);
+ }
+
+ if ((body_size - received) < frame.payload.body_fragment.len)
+ {
+ WARNING ("amqp plugin: Body is larger than indicated by header.");
+ return (-1);
+ }
+
+ memcpy (body_ptr, frame.payload.body_fragment.bytes,
+ frame.payload.body_fragment.len);
+ body_ptr += frame.payload.body_fragment.len;
+ received += frame.payload.body_fragment.len;
+ } /* while (received < body_size) */
+
+ if (strcasecmp ("text/collectd", content_type) == 0)
+ {
+ status = handle_putval (stderr, body);
+ if (status != 0)
+ ERROR ("amqp plugin: handle_putval failed with status %i.",
+ status);
+ return (status);
+ }
+ else if (strcasecmp ("application/json", content_type) == 0)
+ {
+ ERROR ("amqp plugin: camqp_read_body: Parsing JSON data has not "
+ "been implemented yet. FIXME!");
+ return (0);
+ }
+ else
+ {
+ ERROR ("amqp plugin: camqp_read_body: Unknown content type \"%s\".",
+ content_type);
+ return (EINVAL);
+ }
+
+ /* not reached */
+ return (0);
+} /* }}} int camqp_read_body */
+
+static int camqp_read_header (camqp_config_t *conf) /* {{{ */
+{
+ int status;
+ amqp_frame_t frame;
+ amqp_basic_properties_t *properties;
+ char *content_type;
+
+ status = amqp_simple_wait_frame (conf->connection, &frame);
+ if (status < 0)
+ {
+ char errbuf[1024];
+ status = (-1) * status;
+ ERROR ("amqp plugin: amqp_simple_wait_frame failed: %s",
+ sstrerror (status, errbuf, sizeof (errbuf)));
+ camqp_close_connection (conf);
+ return (status);
+ }
+
+ if (frame.frame_type != AMQP_FRAME_HEADER)
+ {
+ NOTICE ("amqp plugin: Unexpected frame type: %#"PRIx8,
+ frame.frame_type);
+ return (-1);
+ }
+
+ properties = frame.payload.properties.decoded;
+ content_type = camqp_bytes_cstring (&properties->content_type);
+ if (content_type == NULL)
+ {
+ ERROR ("amqp plugin: Unable to determine content type.");
+ return (-1);
+ }
+
+ status = camqp_read_body (conf,
+ (size_t) frame.payload.properties.body_size,
+ content_type);
+
+ sfree (content_type);
+ return (status);
+} /* }}} int camqp_read_header */
+
+static void *camqp_subscribe_thread (void *user_data) /* {{{ */
+{
+ camqp_config_t *conf = user_data;
+ int status;
+
+ while (subscriber_threads_running)
+ {
+ amqp_frame_t frame;
+
+ status = camqp_connect (conf);
+ if (status != 0)
+ {
+ ERROR ("amqp plugin: camqp_connect failed. "
+ "Will sleep for %i seconds.", interval_g);
+ sleep (interval_g);
+ continue;
+ }
+
+ status = amqp_simple_wait_frame (conf->connection, &frame);
+ if (status < 0)
+ {
+ ERROR ("amqp plugin: amqp_simple_wait_frame failed. "
+ "Will sleep for %i seconds.", interval_g);
+ camqp_close_connection (conf);
+ sleep (interval_g);
+ continue;
+ }
+
+ if (frame.frame_type != AMQP_FRAME_METHOD)
+ {
+ DEBUG ("amqp plugin: Unexpected frame type: %#"PRIx8,
+ frame.frame_type);
+ continue;
+ }
+
+ if (frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD)
+ {
+ DEBUG ("amqp plugin: Unexpected method id: %#"PRIx32,
+ frame.payload.method.id);
+ continue;
+ }
+
+ status = camqp_read_header (conf);
+
+ amqp_maybe_release_buffers (conf->connection);
+ } /* while (subscriber_threads_running) */
+
+ camqp_config_free (conf);
+ pthread_exit (NULL);
+} /* }}} void *camqp_subscribe_thread */
+
+static int camqp_subscribe_init (camqp_config_t *conf) /* {{{ */
+{
+ int status;
+ pthread_t *tmp;
+
+ tmp = realloc (subscriber_threads,
+ sizeof (*subscriber_threads) * (subscriber_threads_num + 1));
+ if (tmp == NULL)
+ {
+ ERROR ("amqp plugin: realloc failed.");
+ camqp_config_free (conf);
+ return (ENOMEM);
+ }
+ subscriber_threads = tmp;
+ tmp = subscriber_threads + subscriber_threads_num;
+ memset (tmp, 0, sizeof (*tmp));
+
+ status = pthread_create (tmp, /* attr = */ NULL,
+ camqp_subscribe_thread, conf);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("amqp plugin: pthread_create failed: %s",
+ sstrerror (status, errbuf, sizeof (errbuf)));
+ camqp_config_free (conf);
+ return (status);
+ }
+
+ subscriber_threads_num++;
+
+ return (0);
+} /* }}} int camqp_subscribe_init */
+
+/*
+ * Publishing code
+ */
+/* XXX: You must hold "conf->lock" when calling this function! */
+static int camqp_write_locked (camqp_config_t *conf, /* {{{ */
+ const char *buffer, const char *routing_key)
+{
+ amqp_basic_properties_t props;
+ int status;
+
+ status = camqp_connect (conf);
+ if (status != 0)
+ return (status);
+
+ memset (&props, 0, sizeof (props));
+ props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG
+ | AMQP_BASIC_DELIVERY_MODE_FLAG
+ | AMQP_BASIC_APP_ID_FLAG;
+ if (conf->format == CAMQP_FORMAT_COMMAND)
+ props.content_type = amqp_cstring_bytes("text/collectd");
+ else if (conf->format == CAMQP_FORMAT_JSON)
+ props.content_type = amqp_cstring_bytes("application/json");
+ else
+ assert (23 == 42);
+ props.delivery_mode = conf->delivery_mode;
+ props.app_id = amqp_cstring_bytes("collectd");
+
+ status = amqp_basic_publish(conf->connection,
+ /* channel = */ 1,
+ amqp_cstring_bytes(CONF(conf, exchange)),
+ amqp_cstring_bytes (routing_key),
+ /* mandatory = */ 0,
+ /* immediate = */ 0,
+ &props,
+ amqp_cstring_bytes(buffer));
+ if (status != 0)
+ {
+ ERROR ("amqp plugin: amqp_basic_publish failed with status %i.",
+ status);
+ camqp_close_connection (conf);
+ }
+
+ return (status);
+} /* }}} int camqp_write_locked */
+
+static int camqp_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
+ user_data_t *user_data)
+{
+ camqp_config_t *conf = user_data->data;
+ char routing_key[6 * DATA_MAX_NAME_LEN];
+ char buffer[4096];
+ int status;
+
+ if ((ds == NULL) || (vl == NULL) || (conf == NULL))
+ return (EINVAL);
+
+ memset (buffer, 0, sizeof (buffer));
+
+ if (conf->routing_key != NULL)
+ {
+ sstrncpy (routing_key, conf->routing_key, sizeof (routing_key));
+ }
+ else
+ {
+ size_t i;
+ ssnprintf (routing_key, sizeof (routing_key), "collectd/%s/%s/%s/%s/%s",
+ vl->host,
+ vl->plugin, vl->plugin_instance,
+ vl->type, vl->type_instance);
+
+ /* Switch slashes (the only character forbidden by collectd) and dots
+ * (the separation character used by AMQP). */
+ for (i = 0; routing_key[i] != 0; i++)
+ {
+ if (routing_key[i] == '.')
+ routing_key[i] = '/';
+ else if (routing_key[i] == '/')
+ routing_key[i] = '.';
+ }
+ }
+
+ if (conf->format == CAMQP_FORMAT_COMMAND)
+ {
+ status = create_putval (buffer, sizeof (buffer), ds, vl);
+ if (status != 0)
+ {
+ ERROR ("amqp plugin: create_putval failed with status %i.",
+ status);
+ return (status);
+ }
+ }
+ else if (conf->format == CAMQP_FORMAT_JSON)
+ {
+ size_t bfree = sizeof (buffer);
+ size_t bfill = 0;
+
+ format_json_initialize (buffer, &bfill, &bfree);
+ format_json_value_list (buffer, &bfill, &bfree, ds, vl, conf->store_rates);
+ format_json_finalize (buffer, &bfill, &bfree);
+ }
+ else
+ {
+ ERROR ("amqp plugin: Invalid format (%i).", conf->format);
+ return (-1);
+ }
+
+ pthread_mutex_lock (&conf->lock);
+ status = camqp_write_locked (conf, buffer, routing_key);
+ pthread_mutex_unlock (&conf->lock);
+
+ return (status);
+} /* }}} int camqp_write */
+
+/*
+ * Config handling
+ */
+static int camqp_config_set_format (oconfig_item_t *ci, /* {{{ */
+ camqp_config_t *conf)
+{
+ char *string;
+ int status;
+
+ string = NULL;
+ status = cf_util_get_string (ci, &string);
+ if (status != 0)
+ return (status);
+
+ assert (string != NULL);
+ if (strcasecmp ("Command", string) == 0)
+ conf->format = CAMQP_FORMAT_COMMAND;
+ else if (strcasecmp ("JSON", string) == 0)
+ conf->format = CAMQP_FORMAT_JSON;
+ else
+ {
+ WARNING ("amqp plugin: Invalid format string: %s",
+ string);
+ }
+
+ free (string);
+
+ return (0);
+} /* }}} int config_set_string */
+
+static int camqp_config_connection (oconfig_item_t *ci, /* {{{ */
+ _Bool publish)
+{
+ camqp_config_t *conf;
+ int status;
+ int i;
+
+ conf = malloc (sizeof (*conf));
+ if (conf == NULL)
+ {
+ ERROR ("amqp plugin: malloc failed.");
+ return (ENOMEM);
+ }
+
+ /* Initialize "conf" {{{ */
+ memset (conf, 0, sizeof (*conf));
+ conf->publish = publish;
+ conf->name = NULL;
+ conf->format = CAMQP_FORMAT_COMMAND;
+ conf->host = NULL;
+ conf->port = 5672;
+ conf->vhost = NULL;
+ conf->user = NULL;
+ conf->password = NULL;
+ conf->exchange = NULL;
+ conf->routing_key = NULL;
+ /* publish only */
+ conf->delivery_mode = CAMQP_DM_VOLATILE;
+ conf->store_rates = 0;
+ /* subscribe only */
+ conf->exchange_type = NULL;
+ conf->queue = NULL;
+ /* general */
+ conf->connection = NULL;
+ pthread_mutex_init (&conf->lock, /* attr = */ NULL);
+ /* }}} */
+
+ status = cf_util_get_string (ci, &conf->name);
+ if (status != 0)
+ {
+ sfree (conf);
+ return (status);
+ }
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Host", child->key) == 0)
+ status = cf_util_get_string (child, &conf->host);
+ else if (strcasecmp ("Port", child->key) == 0)
+ {
+ status = cf_util_get_port_number (child);
+ if (status > 0)
+ {
+ conf->port = status;
+ status = 0;
+ }
+ }
+ else if (strcasecmp ("VHost", child->key) == 0)
+ status = cf_util_get_string (child, &conf->vhost);
+ else if (strcasecmp ("User", child->key) == 0)
+ status = cf_util_get_string (child, &conf->user);
+ else if (strcasecmp ("Password", child->key) == 0)
+ status = cf_util_get_string (child, &conf->password);
+ else if (strcasecmp ("Exchange", child->key) == 0)
+ status = cf_util_get_string (child, &conf->exchange);
+ else if ((strcasecmp ("ExchangeType", child->key) == 0) && !publish)
+ status = cf_util_get_string (child, &conf->exchange_type);
+ else if ((strcasecmp ("Queue", child->key) == 0) && !publish)
+ status = cf_util_get_string (child, &conf->queue);
+ else if (strcasecmp ("RoutingKey", child->key) == 0)
+ status = cf_util_get_string (child, &conf->routing_key);
+ else if ((strcasecmp ("Persistent", child->key) == 0) && publish)
+ {
+ _Bool tmp = 0;
+ status = cf_util_get_boolean (child, &tmp);
+ if (tmp)
+ conf->delivery_mode = CAMQP_DM_PERSISTENT;
+ else
+ conf->delivery_mode = CAMQP_DM_VOLATILE;
+ }
+ else if ((strcasecmp ("StoreRates", child->key) == 0) && publish)
+ status = cf_util_get_boolean (child, &conf->store_rates);
+ else if ((strcasecmp ("Format", child->key) == 0) && publish)
+ status = camqp_config_set_format (child, conf);
+ else
+ WARNING ("amqp plugin: Ignoring unknown "
+ "configuration option \"%s\".", child->key);
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ if ((status == 0) && (conf->exchange == NULL))
+ {
+ if (conf->exchange_type != NULL)
+ WARNING ("amqp plugin: The option \"ExchangeType\" was given "
+ "without the \"Exchange\" option. It will be ignored.");
+
+ if (!publish && (conf->routing_key != NULL))
+ WARNING ("amqp plugin: The option \"RoutingKey\" was given "
+ "without the \"Exchange\" option. It will be ignored.");
+
+ }
+
+ if (status != 0)
+ {
+ camqp_config_free (conf);
+ return (status);
+ }
+
+ if (conf->exchange != NULL)
+ {
+ DEBUG ("amqp plugin: camqp_config_connection: exchange = %s;",
+ conf->exchange);
+ }
+
+ if (publish)
+ {
+ char cbname[128];
+ user_data_t ud = { conf, camqp_config_free };
+
+ ssnprintf (cbname, sizeof (cbname), "amqp/%s", conf->name);
+
+ status = plugin_register_write (cbname, camqp_write, &ud);
+ if (status != 0)
+ {
+ camqp_config_free (conf);
+ return (status);
+ }
+ }
+ else
+ {
+ status = camqp_subscribe_init (conf);
+ if (status != 0)
+ {
+ camqp_config_free (conf);
+ return (status);
+ }
+ }
+
+ return (0);
+} /* }}} int camqp_config_connection */
+
+static int camqp_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Publish", child->key) == 0)
+ camqp_config_connection (child, /* publish = */ 1);
+ else if (strcasecmp ("Subscribe", child->key) == 0)
+ camqp_config_connection (child, /* publish = */ 0);
+ else
+ WARNING ("amqp plugin: Ignoring unknown config option \"%s\".",
+ child->key);
+ } /* for (ci->children_num) */
+
+ return (0);
+} /* }}} int camqp_config */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("amqp", camqp_config);
+ plugin_register_shutdown ("amqp", camqp_shutdown);
+} /* void module_register */
+
+/* vim: set sw=4 sts=4 et fdm=marker : */
/**
* 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
*
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;
(st->host != NULL) ? st->host : hostname_g,
(st->name != NULL) ? st->name : "default"),
- status = plugin_register_complex_read (callback_name,
+ status = plugin_register_complex_read (/* group = */ NULL,
+ /* name = */ callback_name,
/* callback = */ apache_read_host,
/* interval = */ NULL,
/* user_data = */ &ud);
{
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 <Instance ...> 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 <Instance /> block "
+ "around the configuration?",
+ child->key);
} /* for (ci->children) */
- if (lci)
- {
- /* create a <Instance ""> 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 */
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);
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)
{
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)
else if ((strcmp (fields[0], "BusyServers:") == 0) /* Apache 1.* */
|| (strcmp (fields[0], "BusyWorkers:") == 0) /* Apache 2.* */)
submit_gauge ("apache_connections", NULL, atol (fields[1]), st);
+ else if ((strcmp (fields[0], "IdleServers:") == 0) /* Apache 1.x */
+ || (strcmp (fields[0], "IdleWorkers:") == 0) /* Apache 2.x */)
+ submit_gauge ("apache_idle_workers", NULL, atol (fields[1]), st);
}
}
if (0 == access (battery_acpi_dir, R_OK))
walk_directory (battery_acpi_dir, battery_read_acpi,
- /* user_data = */ NULL);
+ /* user_data = */ NULL,
+ /* include hidden */ 0);
else
{
char errbuf[1024];
/**
* 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
*
* Authors:
* Bruno Prémont <bonbons at linux-vserver.org>
- * Florian Forster <octo at verplant.org>
+ * Florian Forster <octo at collectd.org>
**/
-#define _XOPEN_SOURCE 600 /* glibc2 needs this for strptime */
+#include "config.h"
+
+#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"
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) {
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)
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;
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;
The user, the binary is executed as, may not have root privileges, i.E<nbsp>e.
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<PUTVAL> command. When the I<PUTNOTIF> 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 I<collectdE<nbsp>5>.
+
=back
=head1 SEE ALSO
Use I<password> to authenticate to the server. If not given, unauthenticated
access is used.
+=item B<InstancePrefix> I<prefix>
+
+Prefixes the generated I<plugin instance> with I<prefix>. If a second
+I<InstancePrefix> is specified in a referenced I<MBean> block, the prefix
+specified in the I<Connection> block will appear at the beginning of the
+I<plugin instance>, the prefix specified in the I<MBean> block will be appended
+to it.
+
=item B<Collect> I<mbean_block_name>
Configures which of the I<MBean> blocks to use with this connection. May be
/**
* 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
#include <strings.h>
#include <assert.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include "libcollectdclient/client.h"
-
-/*
- * This is copied directly from collectd.h. Make changes there!
- */
#if NAN_STATIC_DEFAULT
# include <math.h>
/* #endif NAN_STATIC_DEFAULT*/
# 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
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;
" -H <host> Hostname to query the values for.\n"
" -c <range> Critical range\n"
" -w <range> 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"
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)
{
printf (" |");
for (i = 0; i < values_num; i++)
- printf (" %s=%g;;;;", values_names[i], values[i]);
+ printf (" %s=%f;;;;", values_names[i], values[i]);
}
printf ("\n");
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)
printf ("%s: %g average |", status_str, average);
for (i = 0; i < values_num; i++)
- printf (" %s=%g;;;;", values_names[i], values[i]);
+ printf (" %s=%f;;;;", values_names[i], values[i]);
printf ("\n");
return (status_code);
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)
printf ("%s: %g sum |", status_str, total);
for (i = 0; i < values_num; i++)
- printf (" %s=%g;;;;", values_names[i], values[i]);
+ printf (" %s=%f;;;;", values_names[i], values[i]);
printf ("\n");
return (status_code);
}
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)
{
{
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;
match_ds_num_g++;
break;
}
+ case 'm':
+ nan_is_error_g = 1;
+ break;
default:
usage (argv[0]);
} /* switch (c) */
I<min> is then assumed to be zero. If I<max> (but not the trailing colon) is
omitted, I<max> is assumed to be positive infinity.
+=item B<-m>
+
+If this option is given, "Not a Number" (NaN) is treated as I<critical>. By
+default, the I<none> consolidation reports NaNs as I<warning>. Other
+consolidations simply ignore NaN values.
+
=back
=head1 RETURN VALUE
=head1 SYNOPSIS
- LoadPlugin perl
+ <LoadPlugin perl>
+ Globals true
+ </LoadPlugin>
# ...
<Plugin perl>
IncludeDir "/path/to/perl/plugins"
- BaseName "Collectd::Plugin"
+ BaseName "Collectd::Plugins"
EnableDebugger ""
LoadPlugin "FooBar"
Perl-script every time you want to read a value with the C<exec plugin> (see
L<collectd-exec(5)>) and provides a lot more functionality, too.
+When loading the C<perl plugin>, the B<Globals> option should be enabled.
+Else, the perl plugin will fail to load any Perl modules implemented in C,
+which includes, amongst many others, the B<threads> module used by the plugin
+itself. See the documentation of the B<Globals> option in L<collectd.conf(5)>
+for details.
+
=head1 CONFIGURATION
=over 4
=item B<BaseName> I<Name>
Prepends I<Name>B<::> to all plugin names loaded after this option. This is
-provided for convenience to keep plugin names short.
+provided for convenience to keep plugin names short. All Perl-based plugins
+provided with the I<collectd> distributions reside in the C<Collectd::Plugins>
+namespace.
=item E<lt>B<Plugin> I<Name>E<gt> block
type, data-set and value-list is passed to all write-callbacks that are
registered with the daemon.
-B<Note>: Prior to version 4.4 of collectd, the data-set type used to be passed
-as the first argument to B<plugin_register>. This syntax is still supported
-for backwards compatibility but has been deprecated and will be removed in
-some future version of collectd.
-
=item B<plugin_write> ([B<plugins> => I<...>][, B<datasets> => I<...>],
B<valuelists> => I<...>)
the B<plugins> and B<identifiers> arguments may either be a string or a
reference to an array of strings.
-=item B<plugin_flush_one> (I<timeout>, I<plugin>)
-
-This is identical to using "plugin_flush (timeout =E<gt> I<timeout>, plugins
-=E<gt> I<plugin>".
-
-B<Note>: Starting with version 4.5 of collectd, B<plugin_flush_one> has been
-deprecated and will be removed in some future version of collectd. Use
-B<plugin_flush> instead.
-
-=item B<plugin_flush_all> (I<timeout>)
-
-This is identical to using "plugin_flush (timeout =E<gt> I<timeout>)".
-
-B<Note>: Starting with version 4.5 of collectd, B<plugin_flush_all> has been
-deprecated and will be removed in some future version of collectd. Use
-B<plugin_flush> instead.
-
=item B<plugin_dispatch_notification> (I<notification>)
Submits a I<notification> to the daemon which will then pass it to all
=item
-Please feel free to send in new plugins to collectd's mailinglist at
+Please feel free to send in new plugins to collectd's mailing list at
E<lt>collectdE<nbsp>atE<nbsp>verplant.orgE<gt> for review and, possibly,
inclusion in the main distribution. In the latter case, we will take care of
keeping the plugin up to date and adapting it to new versions of collectd.
+# 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.
+
=head1 NAME
collectd-python - Documentation of collectd's C<python plugin>
Python-script every time you want to read a value with the C<exec plugin> (see
L<collectd-exec(5)>) and provides a lot more functionality, too.
-Currently only I<Python 2> is supported and at least I<version 2.3> is
-required.
+At least python I<version 2.3> is required.
=head1 CONFIGURATION
Depending on your version of python this might or might not result in an
B<OSError> exception which can be ignored.
+If you really need to spawn new processes from python you can register an init
+callback and reset the action for SIGCHLD to the default behavior. Please note
+that this I<will> break the exec plugin. Do not even load the exec plugin if
+you intend to do this!
+
+There is an example script located in B<contrib/python/getsigchld.py> to do
+this. If you import this from I<collectd.conf> SIGCHLD will be handled
+normally and spawning processes from python will work as intended.
+
=back
=item E<lt>B<Module> I<Name>E<gt> block
=back
+=head1 STRINGS
+
+There are a lot of places where strings are send from collectd to python and
+from python to collectd. How exactly this works depends on wheather byte or
+unicode strings or python2 or python3 are used.
+
+Python2 has I<str>, which is just bytes, and I<unicode>. Python3 has I<str>,
+which is a unicode object, and I<bytes>.
+
+When passing strings from python to collectd all of these object are supported
+in all places, however I<str> should be used if possible. These strings must
+not contain a NUL byte. Ignoring this will result in a I<TypeError> exception.
+If a byte string was used it will be used as is by collectd. If a unicode
+object was used it will be encoded using the default encoding (see above). If
+this is not possible python will raise a I<UnicodeEncodeError> exception.
+
+Wenn passing strings from collectd to python the behavior depends on the
+python version used. Python2 will always receive a I<str> object. Python3 will
+usually receive a I<str> object as well, however the original string will be
+decoded to unicode using the default encoding. If this fails because the
+string is not a valid sequence for this encoding a I<bytes> object will be
+returned instead.
+
=head1 WRITING YOUR OWN PLUGINS
Writing your own plugins is quite simple. collectd manages plugins by means of
The following complex types are used to pass values between the Python plugin
and collectd:
+=head2 Signed
+
+The Signed class is just a long. It has all its methods and behaves exactly
+like any other long object. It is used to indicate if an integer was or should
+be stored as a signed or unsigned integer object.
+
+ class Signed(long)
+
+This is a long by another name. Use it in meta data dicts
+to choose the way it is stored in the meta data.
+
+=head2 Unsigned
+
+The Unsigned class is just a long. It has all its methods and behaves exactly
+like any other long object. It is used to indicate if an integer was or should
+be stored as a signed or unsigned integer object.
+
+ class Unsigned(long)
+
+This is a long by another name. Use it in meta data dicts
+to choose the way it is stored in the meta data.
+
=head2 Config
The Config class is an object which keeps the information provided in the
exception will be raised. If the content of the sequence is not a number, a
I<TypeError> exception will be raised.
+=item meta
+
+These are the meta data for this Value object.
+It has to be a dictionary of numbers, strings or bools. All keys must be
+strings. I<int> and <long> objects will be dispatched as signed integers unless
+they are between 2**63 and 2**64-1, which will result in a unsigned integer.
+You can force one of these storage classes by using the classes
+B<collectd.Signed> and B<collectd.Unsigned>. A meta object received by a write
+callback will always contain B<Signed> or B<Unsigned> objects.
+
=back
=head2 Notification
=item
-This plugin is not compatible with python3. Trying to compile it with python3
-will fail because of the ways string, unicode and bytearray behavior was
-changed.
-
-=item
-
Not all aspects of the collectd API are accessible from python. This includes
-but is not limited to meta-data, filters and data sets.
+but is not limited to filters and data sets.
=back
--- /dev/null
+=head1 NAME
+
+collectd-threshold - Documentation of collectd's I<Threshold plugin>
+
+=head1 SYNOPSIS
+
+ LoadPlugin "threshold"
+ <Plugin "threshold">
+ <Type "foo">
+ WarningMin 0.00
+ WarningMax 1000.00
+ FailureMin 0.00
+ FailureMax 1200.00
+ Invert false
+ Instance "bar"
+ </Type>
+ </Plugin>
+
+=head1 DESCRIPTION
+
+Starting with version C<4.3.0> I<collectd> has support for B<monitoring>. 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<Threshold plugin> takes itself is to generate and dispatch a
+I<notification>. 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<thresholds> 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<Timeout> iterations. The B<Timeout> configuration option is
+explained in section L<collectd.conf(5)/"GLOBAL OPTIONS">. If, for example,
+B<Timeout> 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<Interval> 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"
+ <Plugin "threshold">
+ <Type "foo">
+ WarningMin 0.00
+ WarningMax 1000.00
+ FailureMin 0.00
+ FailureMax 1200.00
+ Invert false
+ Instance "bar"
+ </Type>
+
+ <Plugin "interface">
+ Instance "eth0"
+ <Type "if_octets">
+ FailureMax 10000000
+ DataSource "rx"
+ </Type>
+ </Plugin>
+
+ <Host "hostname">
+ <Type "cpu">
+ Instance "idle"
+ FailureMin 10
+ </Type>
+
+ <Plugin "memory">
+ <Type "memory">
+ Instance "cached"
+ WarningMin 100000000
+ </Type>
+ </Plugin>
+
+ <Type "load">
+ DataSource "midterm"
+ FailureMax 4
+ Hits 3
+ Hysteresis 3
+ </Type>
+ </Host>
+ </Plugin>
+
+There are basically two types of configuration statements: The C<Host>,
+C<Plugin>, and C<Type> blocks select the value for which a threshold should be
+configured. The C<Plugin> and C<Type> blocks may be specified further using the
+C<Instance> option. You can combine the block by nesting the blocks, though
+they must be nested in the above order, i.e. C<Host> may contain either
+C<Plugin> and C<Type> blocks, C<Plugin> may only contain C<Type> blocks and
+C<Type> 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<must> be
+included in a C<Type> block. Currently the following statements are recognized:
+
+=over 4
+
+=item B<FailureMax> I<Value>
+
+=item B<WarningMax> I<Value>
+
+Sets the upper bound of acceptable values. If unset defaults to positive
+infinity. If a value is greater than B<FailureMax> a B<FAILURE> notification
+will be created. If the value is greater than B<WarningMax> but less than (or
+equal to) B<FailureMax> a B<WARNING> notification will be created.
+
+=item B<FailureMin> I<Value>
+
+=item B<WarningMin> I<Value>
+
+Sets the lower bound of acceptable values. If unset defaults to negative
+infinity. If a value is less than B<FailureMin> a B<FAILURE> notification will
+be created. If the value is less than B<WarningMin> but greater than (or equal
+to) B<FailureMin> a B<WARNING> notification will be created.
+
+=item B<DataSource> I<DSName>
+
+Some data sets have more than one "data source". Interesting examples are the
+C<if_octets> data set, which has received (C<rx>) and sent (C<tx>) bytes and
+the C<disk_ops> data set, which holds C<read> and C<write> operations. The
+system load data set, C<load>, even has three data sources: C<shortterm>,
+C<midterm>, and C<longterm>.
+
+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<DataSource> option to have a threshold apply only to
+one data source.
+
+=item B<Invert> B<true>|B<false>
+
+If set to B<true> the range of acceptable values is inverted, i.e. values
+between B<FailureMin> and B<FailureMax> (B<WarningMin> and B<WarningMax>) are
+not okay. Defaults to B<false>.
+
+=item B<Persist> B<true>|B<false>
+
+Sets how often notifications are generated. If set to B<true> one notification
+will be generated for each value that is out of the acceptable range. If set to
+B<false> (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<true> a notification about a
+missing value is generated once every B<Interval> seconds. If set to B<false>
+only one such notification is generated until the value appears again.
+
+=item B<Percentage> B<true>|B<false>
+
+If set to B<true>, 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<nbsp>% of the total space is available. Defaults to B<false>.
+
+=item B<Hits> I<Value>
+
+Sets the number of occurrences which the threshold must be raised before to
+dispatch any notification or, in other words, the number of B<Interval>s
+that the threshold must be match before dispatch any notification.
+
+=item B<Hysteresis> I<Value>
+
+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<WarningMax>, B<FailureMin> or
+everything else) minus the hysteresis value, the failure (respectively warning)
+state will be keep.
+
+=item B<Interesting> B<true>|B<false>
+
+If set to B<true> (the default), the threshold must be treated as interesting
+and, when a number of B<Timeout> values will lost, then a missing notification
+will be dispatched. On the other hand, if set to B<false>, the missing
+notification will never dispatched for this threshold.
+
+=back
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>
+
+=head1 AUTHOR
+
+Florian Forster E<lt>octoE<nbsp>atE<nbsp>collectd.orgE<gt>
* 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;
#endif /* HAVE_LIBKSTAT */
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;
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"
+ 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 = %.3f;", CDTIME_T_TO_DOUBLE (interval_g));
+
+ str = global_option_get ("Timeout");
+ if (str == NULL)
+ str = "2";
+ timeout_g = atoi (str);
+ if (timeout_g <= 1)
+ {
+ fprintf (stderr, "Cannot set the timeout to a correct value.\n"
"Please check your settings.\n");
return (-1);
}
- DEBUG ("interval_g = %i;", interval_g);
+ DEBUG ("timeout_g = %i;", timeout_g);
if (init_hostname () != 0)
return (-1);
#endif
" -h Display help (this message)\n"
"\nBuiltin defaults:\n"
- " Config-File "CONFIGFILE"\n"
- " PID-File "PIDFILE"\n"
- " Data-Directory "PKGLOCALSTATEDIR"\n"
+ " Config file "CONFIGFILE"\n"
+ " PID file "PIDFILE"\n"
+ " Plugin directory "PLUGINDIR"\n"
+ " Data directory "PKGLOCALSTATEDIR"\n"
"\n"PACKAGE" "VERSION", http://collectd.org/\n"
"by Florian octo Forster <octo@verplant.org>\n"
"for contributions see `AUTHORS'\n");
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 ();
/* 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)
{
}
} /* while (loop == 0) */
- DEBUG ("return (0);");
return (0);
} /* int do_loop */
##############################################################################
#Hostname "localhost"
-FQDNLookup true
+#FQDNLookup true
#BaseDir "@prefix@/var/lib/@PACKAGE_NAME@"
#PIDFile "@prefix@/var/run/@PACKAGE_NAME@.pid"
#PluginDir "@prefix@/lib/@PACKAGE_NAME@"
#TypesDB "@prefix@/share/@PACKAGE_NAME@/types.db"
#Interval 10
+#Timeout 2
#ReadThreads 5
##############################################################################
# LogLevel @DEFAULT_LOG_LEVEL@
# File STDOUT
# Timestamp true
+# PrintSeverity false
#</Plugin>
#<Plugin syslog>
# 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
@LOAD_PLUGIN_CSV@LoadPlugin csv
#@BUILD_PLUGIN_CURL_TRUE@LoadPlugin curl
#@BUILD_PLUGIN_CURL_JSON_TRUE@LoadPlugin curl_json
+#@BUILD_PLUGIN_CURL_XML_TRUE@LoadPlugin curl_xml
#@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi
#@BUILD_PLUGIN_DF_TRUE@LoadPlugin df
#@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk
#@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
#@BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached
@BUILD_PLUGIN_MEMORY_TRUE@@BUILD_PLUGIN_MEMORY_TRUE@LoadPlugin memory
+#@BUILD_PLUGIN_MODBUS_TRUE@LoadPlugin modbus
#@BUILD_PLUGIN_MULTIMETER_TRUE@LoadPlugin multimeter
#@BUILD_PLUGIN_MYSQL_TRUE@LoadPlugin mysql
#@BUILD_PLUGIN_NETAPP_TRUE@LoadPlugin netapp
#@BUILD_PLUGIN_OPENVPN_TRUE@LoadPlugin openvpn
#@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle
#@BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl
+#@BUILD_PLUGIN_PINBA_TRUE@LoadPlugin pinba
#@BUILD_PLUGIN_PING_TRUE@LoadPlugin ping
#@BUILD_PLUGIN_POSTGRESQL_TRUE@LoadPlugin postgresql
#@BUILD_PLUGIN_POWERDNS_TRUE@LoadPlugin powerdns
#@BUILD_PLUGIN_PROCESSES_TRUE@LoadPlugin processes
#@BUILD_PLUGIN_PROTOCOLS_TRUE@LoadPlugin protocols
#@BUILD_PLUGIN_PYTHON_TRUE@LoadPlugin python
+#@BUILD_PLUGIN_REDIS_TRUE@LoadPlugin redis
#@BUILD_PLUGIN_ROUTEROS_TRUE@LoadPlugin routeros
#@BUILD_PLUGIN_RRDCACHED_TRUE@LoadPlugin rrdcached
@LOAD_PLUGIN_RRDTOOL@LoadPlugin rrdtool
#@BUILD_PLUGIN_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
# ription of those options is available in the collectd.conf(5) manual page. #
##############################################################################
+#<Plugin "amqp">
+# <Publish "name">
+# Host "localhost"
+# Port "5672"
+# VHost "/"
+# User "guest"
+# Password "guest"
+# Exchange "amq.fanout"
+# RoutingKey "collectd"
+# Persistent false
+# StoreRates false
+# </Publish>
+#</Plugin>
+
#<Plugin apache>
-# URL "http://localhost/status?auto"
-# User "www-user"
-# Password "secret"
-# CACert "/etc/ssl/ca.crt"
+# <Instance "local">
+# URL "http://localhost/status?auto"
+# User "www-user"
+# Password "secret"
+# CACert "/etc/ssl/ca.crt"
+# </Instance>
#</Plugin>
#<Plugin apcups>
# </URL>
#</Plugin>
+#<Plugin "curl_xml">
+# <URL "http://localhost/stats.xml">
+# Host "my_host"
+# Instance "some_instance"
+# User "collectd"
+# Password "thaiNg0I"
+# VerifyPeer true
+# VerifyHost true
+# CACert "/path/to/ca.crt"
+#
+# <XPath "table[@id=\"magic_level\"]/tr">
+# Type "magic_level"
+# #InstancePrefix "prefix-"
+# InstanceFrom "td[1]"
+# ValuesFrom "td[2]/span[@class=\"level\"]"
+# </XPath>
+# </URL>
+#</Plugin>
+
#<Plugin dbi>
# <Query "num_of_customers">
# Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl"
# MTime "-5m"
# Size "+10k"
# Recursive true
+# IncludeHidden false
# </Directory>
#</Plugin>
#<Plugin hddtemp>
# 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
#</Plugin>
#<Plugin interface>
# InterfaceDevice "name:device"
# IgnoreSelected false
# HostnameFormat name
+# InterfaceFormat name
+#</Plugin>
+
+#<Plugin lpar>
+# CpuPoolStats false
+# ReportBySerial false
#</Plugin>
#<Plugin madwifi>
# Key "page_key"
# <Match>
# Regex "(\\d+) bytes sent"
+# ExcludeRegex "<lines to be excluded>"
# DSType CounterAdd
# Type "ipt_octets"
# Instance "type_instance"
# Port "11211"
#</Plugin>
+#<Plugin modbus>
+# <Data "data_name">
+# RegisterBase 1234
+# RegisterType float
+# Type gauge
+# Instance "..."
+# </Data>
+#
+# <Host "name">
+# Address "addr"
+# Port "1234"
+# Interval 60
+#
+# <Slave 1>
+# Instance "foobar" # optional
+# Collect "data_name"
+# </Slave>
+# </Host>
+#</Plugin>
+
#<Plugin mysql>
# <Database db_name>
# Host "database.serv.er"
#</Plugin>
@LOAD_PLUGIN_NETWORK@<Plugin network>
+# # client setup:
@LOAD_PLUGIN_NETWORK@ Server "ff18::efc0:4a42" "25826"
-@LOAD_PLUGIN_NETWORK@ Server "239.192.74.66" "25826"
-# Listen "ff18::efc0:4a42" "25826"
-# Listen "239.192.74.66" "25826"
+@LOAD_PLUGIN_NETWORK@ <Server "239.192.74.66" "25826">
+# SecurityLevel Encrypt
+# Username "user"
+# Password "secret"
+# Interface "eth0"
+@LOAD_PLUGIN_NETWORK@ </Server>
# TimeToLive "128"
-# Forward false
-# CacheFlush 1800
+#
+# # server setup:
+# Listen "ff18::efc0:4a42" "25826"
+# <Listen "239.192.74.66" "25826">
+# SecurityLevel Sign
+# AuthFile "/etc/collectd/passwd"
+# Interface "eth0"
+# </Listen>
+# MaxPacketSize 1024
+#
+# # proxy setup (client and server as above):
+# Forward true
+#
+# # statistics about the network plugin itself
# ReportStats false
+#
+# # "garbage collection"
+# CacheFlush 1800
@LOAD_PLUGIN_NETWORK@</Plugin>
#<Plugin nginx>
#<Plugin openvpn>
# StatusFile "/etc/openvpn/openvpn-status.log"
+# ImprovedNamingSchema false
+# CollectCompression true
+# CollectIndividualUsers true
+# CollectUserCount false
#</Plugin>
#<Plugin oracle>
#<Plugin perl>
# IncludeDir "/my/include/path"
-# BaseName "Collectd::Plugin"
+# BaseName "Collectd::Plugins"
# EnableDebugger ""
# LoadPlugin Monitorus
# LoadPlugin OpenVZ
# </Plugin>
#</Plugin>
+#<Plugin pinba>
+# Address "::0"
+# Port "30002"
+# <View "name">
+# Host "host name"
+# Server "server name"
+# Script "script name"
+# </View>
+#</Plugin>
+
#<Plugin ping>
# Host "host.foo.bar"
# Interval 1.0
# Query magic
# </Database>
# <Database bar>
+# Interval 60
# Service "service_name"
# Query backend # predefined
# Query rt36_tickets
# </Module>
#</Plugin>
+#<Plugin redis>
+# <Node example>
+# Host "redis.example.com"
+# Port "6379"
+# Timeout 2000
+# </Node>
+#</Plugin>
+
#<Plugin routeros>
# <Router>
# Host "router.example.com"
# Password "dozaiTh4"
# CollectInterface true
# CollectRegistrationTable true
+# CollectCPULoad true
+# CollectMemory true
+# CollectDF true
+# CollectDisk true
# </Router>
#</Plugin>
# SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock"
# SocketGroup "collectd"
# SocketPerms "0660"
+# DeleteSocket false
#</Plugin>
#<Plugin uuid>
# UUIDFile "/etc/uuid"
#</Plugin>
+#<Plugin varnish>
+# This tag support an argument if you want to
+# monitor the local instance just use </Instance>
+# If you prefer defining another instance you can do
+# so by using <Instance "myinstance">
+# <Instance>
+# CollectCache true
+# CollectBackend true
+# CollectConnections true
+# CollectSHM true
+# CollectESI false
+# CollectFetch false
+# CollectHCB false
+# CollectSMA false
+# CollectSMS false
+# CollectSM false
+# CollectTotals false
+# CollectWorkers false
+# </Instance>
+#</Plugin>
+
#<Plugin vmem>
# Verbose false
#</Plugin>
# <URL "http://example.com/collectd-post">
# User "collectd"
# Password "weCh3ik0"
+# VerifyPeer true
+# VerifyHost true
+# CACert "/etc/ssl/ca.crt"
+# Format "Command"
+# StoreRates false
# </URL>
#</Plugin>
+#<Plugin write_redis>
+# <Node "example">
+# Host "localhost"
+# Port "6379"
+# Timeout 1000
+# </Node>
+#</Plugin>
+
##############################################################################
# Filter configuration #
#----------------------------------------------------------------------------#
#<Chain "PostCache">
# Target "write"
#</Chain>
+
+##############################################################################
+# 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"
+#<Plugin "threshold">
+# <Type "foo">
+# WarningMin 0.00
+# WarningMax 1000.00
+# FailureMin 0.00
+# FailureMax 1200.00
+# Invert false
+# Instance "bar"
+# </Type>
+#
+# <Plugin "interface">
+# Instance "eth0"
+# <Type "if_octets">
+# FailureMax 10000000
+# DataSource "rx"
+# </Type>
+# </Plugin>
+#
+# <Host "hostname">
+# <Type "cpu">
+# Instance "idle"
+# FailureMin 10
+# </Type>
+#
+# <Plugin "memory">
+# <Type "memory">
+# Instance "cached"
+# WarningMin 100000000
+# </Type>
+# </Plugin>
+#
+# <Type "load">
+# DataSource "midterm"
+# FailureMax 4
+# Hits 3
+# Hysteresis 3
+# </Type>
+# </Host>
+#</Plugin>
Loads the plugin I<Plugin>. There must be at least one such line or B<collectd>
will be mostly useless.
+Starting with collectd 4.9, this may also be a block in which further options
+affecting the behavior of B<LoadPlugin> may be specified. The following
+options are allowed inside a B<LoadPlugin> block:
+
+ <LoadPlugin perl>
+ Globals true
+ </LoadPlugin>
+
+=over 4
+
+=item B<Globals> B<true|false>
+
+If enabled, collectd will export all global symbols of the plugin (and of all
+libraries loaded as dependencies of the plugin) and, thus, makes those symbols
+available for resolving unresolved symbols in subsequently loaded plugins if
+that is supported by your system.
+
+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<Perl> and
+I<Python plugins>). Scripting languages usually provide means to load
+extensions written in C. Those extensions require symbols provided by the
+interpreter, which is loaded as a dependency of the respective collectd plugin.
+See the documentation of those plugins (e.g., L<collectd-perl(5)> or
+L<collectd-python(5)>) for details.
+
+By default, this is disabled. As a special exception, if the plugin name is
+either C<perl> or C<python>, 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
+
=item B<Include> I<Path>
If I<Path> points to a file, includes that file. If I<Path> points to a
values lead to a higher system load produced by collectd, while higher values
lead to more coarse statistics.
+B<Warning:> You should set this once and then never touch it again. If you do,
+I<you will have to delete all your RRD files> or know some serious RRDtool
+magic! (Assuming you're using the I<RRDtool> or I<RRDCacheD> plugin.)
+
+=item B<Timeout> I<Iterations>
+
+Consider a value list "missing" when no update has been read or received for
+I<Iterations> iterations. By default, I<collectd> considers a value list
+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<Interval> information contained in each value list. This is used in
+the I<Threshold> configuration to dispatch notifications about missing values,
+see L<collectd-threshold(5)> for details.
+
=item B<ReadThreads> I<Num>
Number of threads to start for reading plugins. The default value is B<5>, but
If B<Hostname> 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<gethostname>.
-
-Using this feature (i.E<nbsp>e. setting this option to B<true>) is recommended.
-However, to preserve backwards compatibility the default is set to B<false>.
-The sample config file that is installed with C<makeE<nbsp>install> 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<gethostname>. This option
+is enabled by default.
=item B<PreCacheChain> I<ChainName>
F<README> file shipped with the sourcecode and hopefully binary packets as
well.
+=head2 Plugin C<amqp>
+
+The I<AMQMP plugin> can be used to communicate with other instances of
+I<collectd> 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.
+
+ <Plugin "amqp">
+ # Send values to an AMQP broker
+ <Publish "some_name">
+ Host "localhost"
+ Port "5672"
+ VHost "/"
+ User "guest"
+ Password "guest"
+ Exchange "amq.fanout"
+ # ExchangeType "fanout"
+ # RoutingKey "collectd"
+ # Persistent false
+ # Format "command"
+ # StoreRates false
+ </Publish>
+
+ # Receive values from an AMQP broker
+ <Subscribe "some_name">
+ Host "localhost"
+ Port "5672"
+ VHost "/"
+ User "guest"
+ Password "guest"
+ Exchange "amq.fanout"
+ # ExchangeType "fanout"
+ # Queue "queue_name"
+ # RoutingKey "collectd.#"
+ </Subscribe>
+ </Plugin>
+
+The plugin's configuration consists of a number of I<Publish> and I<Subscribe>
+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<flushing> of certain
+I<Publish> blocks in the future.
+
+=over 4
+
+=item B<Host> I<Host>
+
+Hostname or IP-address of the AMQP broker. Defaults to the default behavior of
+the underlying communications library, I<rabbitmq-c>, which is "localhost".
+
+=item B<Port> I<Port>
+
+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<VHost> I<VHost>
+
+Name of the I<virtual host> on the AMQP broker to use. Defaults to "/".
+
+=item B<User> I<User>
+
+=item B<Password> I<Password>
+
+Credentials used to authenticate to the AMQP broker. By default "guest"/"guest"
+is used.
+
+=item B<Exchange> I<Exchange>
+
+In I<Publish> blocks, this option specifies the I<exchange> to send values to.
+By default, "amq.fanout" will be used.
+
+In I<Subscribe> blocks this option is optional. If given, a I<binding> between
+the given exchange and the I<queue> is created, using the I<routing key> if
+configured. See the B<Queue> and B<RoutingKey> options below.
+
+=item B<ExchangeType> I<Type>
+
+If given, the plugin will try to create the configured I<exchange> with this
+I<type> after connecting. When in a I<Subscribe> block, the I<queue> will then
+be bound to this exchange.
+
+=item B<Queue> I<Queue> (Subscribe only)
+
+Configures the I<queue> name to subscribe to. If no queue name was configures
+explicitly, a unique queue name will be created by the broker.
+
+=item B<RoutingKey> I<Key>
+
+In I<Publish> blocks, this configures the routing key to set on all outgoing
+messages. If not given, the routing key will be computed from the I<identifier>
+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<Subscribe> blocks, configures the I<routing key> used when creating a
+I<binding> between an I<exchange> and the I<queue>. 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<Persistent> B<true>|B<false> (Publish only)
+
+Selects the I<delivery method> to use. If set to B<true>, the I<persistent>
+mode will be used, i.e. delivery is guaranteed. If set to B<false> (the
+default), the I<transient> delivery mode will be used, i.e. messages may be
+lost due to high load, overflowing queues or similar issues.
+
+=item B<Format> B<Command>|B<JSON> (Publish only)
+
+Selects the format in which messages are sent to the broker. If set to
+B<Command> (the default), values are sent as C<PUTVAL> commands which are
+identical to the syntax used by the I<Exec> and I<UnixSock plugins>. In this
+case, the C<Content-Type> header field will be set to C<text/collectd>.
+
+If set to B<JSON>, the values are encoded in the I<JavaScript Object Notation>,
+an easy and straight forward exchange format. The C<Content-Type> header field
+will be set to C<application/json>.
+
+A subscribing client I<should> use the C<Content-Type> header field to
+determine how to decode the values. Currently, the I<AMQP plugin> itself can
+only decode the B<Command> format.
+
+=item B<StoreRates> B<true>|B<false> (Publish only)
+
+Determines whether or not C<COUNTER>, C<DERIVE> and C<ABSOLUTE> data sources
+are converted to a I<rate> (i.e. a C<GAUGE> value). If set to B<false> (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<Format> option has
+been set to B<JSON>.
+
+=back
+
=head2 Plugin C<apache>
To configure the C<apache>-plugin you first need to configure the Apache
also supported. It introduces a new field, called C<BusyServers>, to count the
number of currently connected clients. This field is also supported.
-The following options are accepted by the C<apache>-plugin:
+The configuration of the I<Apache> plugin consists of one or more
+C<E<lt>InstanceE<nbsp>/E<gt>> blocks. Each block requires one string argument
+as the instance name. For example:
+
+ <Plugin "apache">
+ <Instance "www1">
+ URL "http://www1.example.com/mod_status?auto"
+ </Instance>
+ <Instance "www2">
+ URL "http://www2.example.com/mod_status?auto"
+ </Instance>
+ </Plugin>
+
+The instance name will be used as the I<plugin instance>. To emulate the old
+(versionE<nbsp>4) 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<Instance> block:
=over 4
Sets the URL of the C<mod_status> output. This needs to be the output generated
by C<ExtendedStatus on> and it needs to be the machine readable output
-generated by appending the C<?auto> argument.
+generated by appending the C<?auto> argument. This option is I<mandatory>.
=item B<User> I<Username>
</URL>
</Plugin>
-Another CouchDB example:
-The following example will collect the status values from each database:
-
- <URL "http://localhost:5984/_all_dbs">
- Instance "dbs"
- <Key "*/doc_count">
- Type "gauge"
- </Key>
- <Key "*/doc_del_count">
- Type "counter"
- </Key>
- <Key "*/disk_size">
- Type "bytes"
- </Key>
- </URL>
-
In the B<Plugin> block, there may be one or more B<URL> blocks, each defining
a URL to be fetched via HTTP (using libcurl) and one or more B<Key> blocks.
The B<Key> string argument must be in a path format, which is used to collect a
=back
+=head2 Plugin C<curl_xml>
+
+The B<curl_xml plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and B<libxml2>
+(L<http://xmlsoft.org/>) to retrieve XML data via cURL.
+
+ <Plugin "curl_xml">
+ <URL "http://localhost/stats.xml">
+ Host "my_host"
+ Instance "some_instance"
+ User "collectd"
+ Password "thaiNg0I"
+ VerifyPeer true
+ VerifyHost true
+ CACert "/path/to/ca.crt"
+
+ <XPath "table[@id=\"magic_level\"]/tr">
+ Type "magic_level"
+ #InstancePrefix "prefix-"
+ InstanceFrom "td[1]"
+ ValuesFrom "td[2]/span[@class=\"level\"]"
+ </XPath>
+ </URL>
+ </Plugin>
+
+In the B<Plugin> block, there may be one or more B<URL> blocks, each defining a
+URL to be fetched via HTTP (using libcurl). Within each B<URL> block there are
+options which specify the connection parameters, for example authentication
+information, and one or more B<XPath> blocks.
+
+Each B<XPath> block specifies how to get one type of information. The
+string argument must be a valid XPath expression which returns a list
+of "base elements". One value is dispatched for each "base element". The
+I<type instance> and values are looked up using further I<XPath> expressions
+that should be relative to the base element.
+
+Within the B<URL> block the following options are accepted:
+
+=over 4
+
+=item B<Host> I<Name>
+
+Use I<Name> as the host name when submitting values. Defaults to the global
+host name setting.
+
+=item B<Instance> I<Instance>
+
+Use I<Instance> as the plugin instance when submitting values. Defaults to an
+empty string (no plugin instance).
+
+=item B<User> I<User>
+=item B<Password> I<Password>
+=item B<VerifyPeer> B<true>|B<false>
+=item B<VerifyHost> B<true>|B<false>
+=item B<CACert> I<CA Cert File>
+
+These options behave exactly equivalent to the appropriate options of the
+I<cURL> and I<cURL-JSON> plugins. Please see there for a detailed description.
+
+=item E<lt>B<XPath> I<XPath-expression>E<gt>
+
+Within each B<URL> block, there must be one or more B<XPath> blocks. Each
+B<XPath> block specifies how to get one type of information. The string
+argument must be a valid XPath expression which returns a list of "base
+elements". One value is dispatched for each "base element".
+
+Within the B<XPath> block the following options are accepted:
+
+=over 4
+
+=item B<Type> I<Type>
+
+Specifies the I<Type> used for submitting patches. This determines the number
+of values that are required / expected and whether the strings are parsed as
+signed or unsigned integer or as double values. See L<types.db(5)> for details.
+This option is required.
+
+=item B<InstancePrefix> I<InstancePrefix>
+
+Prefix the I<type instance> with I<InstancePrefix>. The values are simply
+concatenated together without any separator.
+This option is optional.
+
+=item B<InstanceFrom> I<InstanceFrom>
+
+Specifies a XPath expression to use for determining the I<type instance>. The
+XPath expression must return exactly one element. The element's value is then
+used as I<type instance>, possibly prefixed with I<InstancePrefix> (see above).
+
+This value is required. As a special exception, if the "base XPath expression"
+(the argument to the B<XPath> block) returns exactly one argument, then this
+option may be omitted.
+
+=item B<ValuesFrom> I<ValuesFrom> [I<ValuesFrom> ...]
+
+Specifies one or more XPath expression to use for reading the values. The
+number of XPath expressions must match the number of data sources in the
+I<type> specified with B<Type> (see above). Each XPath expression must return
+exactly one element. The element's value is then parsed as a number and used as
+value for the appropriate value in the value list dispatched to the daemon.
+
+=back
+
+=back
+
=head2 Plugin C<dbi>
This plugin uses the B<dbi> library (L<http://libdbi.sourceforge.net/>) to
(the default), it will report a disk as "root", but with it I<true>, it will be
"sda1" (or whichever).
-=item B<ReportReserved> B<true>|B<false>
-
-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<ReportByDevice>) 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<ReportInodes> B<true>|B<false>
Enables or disables reporting of free, reserved and used inodes. Defaults to
Controls whether or not to recurse into subdirectories. Enabled by default.
+=item B<IncludeHidden> I<true>|I<false>
+
+Controls whether or not to include "hidden" files and directories in the count.
+"Hidden" files and directories are those, whose name begins with a dot.
+Defaults to I<false>, i.e. by default hidden files and directories are ignored.
+
=back
=head2 Plugin C<GenericJMX>
TCP-Port to connect to. Defaults to B<7634>.
-=item B<TranslateDevicename> I<true>|I<false>
-
-If enabled, translate the disk names to major/minor device numbers
-(e.E<nbsp>g. "8-0" for /dev/sda). For backwards compatibility this defaults to
-I<true> but it's recommended to disable it as it will probably be removed in
-the next major version.
-
=back
=head2 Plugin C<interface>
means to concatenate the guest name and UUID (with a literal colon character
between, thus I<"foo:1234-1234-1234-1234">).
+=item B<InterfaceFormat> B<name>|B<address>
+
+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<name>.
+
+B<address> 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<logfile>
Sets the file to write log messages to. The special strings B<stdout> and
B<stderr> 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<collectd>
+is running in foreground- or non-daemon-mode.
=item B<Timestamp> B<true>|B<false>
Prefix all lines printed by the current time. Defaults to B<true>.
+=item B<PrintSeverity> B<true>|B<false>
+
+When enabled, all lines are prefixed by the severity of the log message, for
+example "warning". Defaults to B<false>.
+
=back
B<Note>: There is no need to notify the daemon after moving or removing the
log file (e.E<nbsp>g. when rotating the logs). The plugin reopens the file
for each line it writes.
+=head2 Plugin C<lpar>
+
+The I<LPAR plugin> reads CPU statistics of I<Logical Partitions>, 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<CpuPoolStats> B<false>|B<true>
+
+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<ReportBySerial> B<false>|B<true>
+
+If enabled, the serial of the physical machine the partition is currently
+running on is reported as I<hostname> and the logical hostname of the machine
+is reported in the I<plugin instance>. Otherwise, the logical hostname will be
+used (just like other plugins) and the I<plugin instance> will be empty.
+Defaults to false.
+
+=back
+
=head2 Plugin C<mbmon>
The C<mbmon plugin> uses mbmon to retrieve temperature, voltage, etc.
=back
+=head2 Plugin C<modbus>
+
+The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
+register values. It supports reading single registers (unsigned 16E<nbsp>bit
+values), large integer values (unsigned 32E<nbsp>bit values) and floating point
+values (two registers interpreted as IEEE floats in big endian notation).
+
+Synopsis:
+
+ <Data "voltage-input-1">
+ RegisterBase 0
+ RegisterType float
+ Type voltage
+ Instance "input-1"
+ </Data>
+
+ <Data "voltage-input-2">
+ RegisterBase 2
+ RegisterType float
+ Type voltage
+ Instance "input-2"
+ </Data>
+
+ <Host "modbus.example.com">
+ Address "192.168.0.42"
+ Port "502"
+ Interval 60
+
+ <Slave 1>
+ Instance "power-supply"
+ Collect "voltage-input-1"
+ Collect "voltage-input-2"
+ </Slave>
+ </Host>
+
+=over 4
+
+=item E<lt>B<Data> I<Name>E<gt> blocks
+
+Data blocks define a mapping between register numbers and the "types" used by
+I<collectd>.
+
+Within E<lt>DataE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<RegisterBase> I<Number>
+
+Configures the base register to read from the device. If the option
+B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
+register will be read (the register number is increased by one).
+
+=item B<RegisterType> B<Uint16>|B<Uint32>|B<Float>
+
+Specifies what kind of data is returned by the device. If the type is B<Uint32>
+or B<Float>, two 16E<nbsp>bit registers will be read and the data is combined
+into one value. Defaults to B<Uint16>.
+
+=item B<Type> I<Type>
+
+Specifies the "type" (data set) to use when dispatching the value to
+I<collectd>. Currently, only data sets with exactly one data source are
+supported.
+
+=item B<Instance> I<Instance>
+
+Sets the type instance to use when dispatching the value to I<collectd>. If
+unset, an empty string (no type instance) is used.
+
+=back
+
+=item E<lt>B<Host> I<Name>E<gt> blocks
+
+Host blocks are used to specify to which hosts to connect and what data to read
+from their "slaves". The string argument I<Name> is used as hostname when
+dispatching the values to I<collectd>.
+
+Within E<lt>HostE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<Address> I<Hostname>
+
+Specifies the node name (the actual network address) used to connect to the
+host. This may be an IP address or a hostname. Please note that the used
+I<libmodbus> library only supports IPv4 at the moment.
+
+=item B<Port> I<Service>
+
+Specifies the port used to connect to the host. The port can either be given as
+a number or as a service name. Please note that the I<Service> argument must be
+a string, even if ports are given in their numerical form. Defaults to "502".
+
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+host. By default the global B<Interval> setting will be used.
+
+=item E<lt>B<Slave> I<ID>E<gt>
+
+Over each TCP connection, multiple Modbus devices may be reached. The slave ID
+is used to specify which device should be addressed. For each device you want
+to query, one B<Slave> block must be given.
+
+Within E<lt>SlaveE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<Instance> I<Instance>
+
+Specify the plugin instance to use when dispatching the values to I<collectd>.
+By default "slave_I<ID>" is used.
+
+=item B<Collect> I<DataName>
+
+Specifies which data to retrieve from the device. I<DataName> must be the same
+string as the I<Name> argument passed to a B<Data> block. You can specify this
+option multiple times to collect more than one value from a slave. At least one
+B<Collect> option is mandatory.
+
+=back
+
+=back
+
+=back
+
=head2 Plugin C<mysql>
The C<mysql plugin> requires B<mysqlclient> 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<SHOW STATUS> / C<SHOW GLOBAL STATUS> command
and collects information about MySQL network traffic, executed statements,
This feature is only available if the I<network> plugin was linked with
I<libgcrypt>.
+=item B<Interface> I<Interface name>
+
+Set the outgoing interface for IP packets. This applies at least
+to IPv6 packets and if possible to IPv4. If this option is not applicable,
+undefined or a non-existent interface name is specified, the default
+behavior is to let the kernel choose the appropriate interface. Be warned
+that the manual selection of an interface for unicast traffic is only
+necessary in rare cases.
+
=back
=item B<E<lt>Listen> I<Host> [I<Port>]B<E<gt>>
using L<stat(2)>. If the file has been changed, the contents is re-read. While
the file is being read, it is locked using L<fcntl(2)>.
+=item B<Interface> I<Interface name>
+
+Set the incoming interface for IP packets explicitly. This applies at least
+to IPv6 packets and if possible to IPv4. If this option is not applicable,
+undefined or a non-existent interface name is specified, the default
+behavior is, to let the kernel choose the appropriate interface. Thus incoming
+traffic gets only accepted, if it arrives on the given interface.
+
=back
=item B<TimeToLive> I<1-255>
=item B<MaxPacketSize> 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 1452E<nbsp>bytes.
=item B<Forward> I<true|false>
necessary it's not a huge problem since the plugin has a duplicate detection,
so the values will not loop.
-=item B<CacheFlush> I<Seconds>
-
-For each host/plugin/type combination the C<network plugin> caches the time of
-the last value being sent or received. Every I<Seconds> seconds the plugin
-searches and removes all entries that are older than I<Seconds> 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<ReportStats> B<true>|B<false>
The network plugin cannot only receive and send statistics, it can also create
when reading multiple status files. Enabling this option is recommended, but to
maintain backwards compatibility this option is disabled by default.
-=item B<Compression> B<true>|B<false>
+=item B<CollectCompression> B<true>|B<false>
Sets whether or not statistics about the compression used by OpenVPN should be
collected. This information is only available in I<single> mode. Enabled by
default.
+=item B<CollectIndividualUsers> B<true>|B<false>
+
+Sets whether or not traffic information is collected for each connected client
+individually. If set to false, currently no traffic data is collected at all
+because aggregating this data in a save manner is tricky. Defaults to B<true>.
+
+=item B<CollectUserCount> B<true>|B<false>
+
+When enabled, the number of currently connected clients or users is collected.
+This is especially interesting when B<CollectIndividualUsers> is disabled, but
+can be configured independently from that option. Defaults to B<false>.
+
=back
=head2 Plugin C<oracle>
This plugin embeds a Perl-interpreter into collectd and provides an interface
to collectd's plugin system. See L<collectd-perl(5)> for its documentation.
+=head2 Plugin C<pinba>
+
+The I<Pinba plugin> receives profiling information from I<Pinba>, an extension
+for the I<PHP> interpreter. At the end of executing a script, i.e. after a
+PHP-based webpage has been delivered, the extension will send a UDP packet
+containing timing information, peak memory usage and so on. The plugin will
+wait for such packets, parse them and account the provided information, which
+is then dispatched to the daemon once per interval.
+
+Synopsis:
+
+ <Plugin pinba>
+ Address "::0"
+ Port "30002"
+ # Overall statistics for the website.
+ <View "www-total">
+ Server "www.example.com"
+ </View>
+ # Statistics for www-a only
+ <View "www-a">
+ Host "www-a.example.com"
+ Server "www.example.com"
+ </View>
+ # Statistics for www-b only
+ <View "www-b">
+ Host "www-b.example.com"
+ Server "www.example.com"
+ </View>
+ </Plugin>
+
+The plugin provides the following configuration options:
+
+=over 4
+
+=item B<Address> I<Node>
+
+Configures the address used to open a listening socket. By default, plugin will
+bind to the I<any> address C<::0>.
+
+=item B<Port> I<Service>
+
+Configures the port (service) to bind to. By default the default Pinba port
+"30002" will be used. The option accepts service names in addition to port
+numbers and thus requires a I<string> argument.
+
+=item E<lt>B<View> I<Name>E<gt> block
+
+The packets sent by the Pinba extension include the hostname of the server, the
+server name (the name of the virtual host) and the script that was executed.
+Using B<View> blocks it is possible to separate the data into multiple groups
+to get more meaningful statistics. Each packet is added to all matching groups,
+so that a packet may be accounted for more than once.
+
+=over 4
+
+=item B<Host> I<Host>
+
+Matches the hostname of the system the webserver / script is running on. This
+will contain the result of the L<gethostname(2)> system call. If not
+configured, all hostnames will be accepted.
+
+=item B<Server> I<Server>
+
+Matches the name of the I<virtual host>, i.e. the contents of the
+C<$_SERVER["SERVER_NAME"]> variable when within PHP. If not configured, all
+server names will be accepted.
+
+=item B<Script> I<Script>
+
+Matches the name of the I<script name>, i.e. the contents of the
+C<$_SERVER["SCRIPT_NAME"]> variable when within PHP. If not configured, all
+script names will be accepted.
+
+=back
+
+=back
+
=head2 Plugin C<ping>
The I<Ping> plugin starts a new thread which sends ICMP "ping" packets to the
=item B<MaxMissed> I<Packets>
-Trigger a DNS resolv after the host has not replied to I<Packets> packets. This
+Trigger a DNS resolve after the host has not replied to I<Packets> packets. This
enables the use of dynamic DNS services (like dyndns.org) with the ping plugin.
Default: B<-1> (disabled)
</Database>
<Database bar>
+ Interval 300
Service "service_name"
Query backend # predefined
Query rt36_tickets
The returned lines will be handled separately one after another.
-=item B<Query> I<sql query statement>
-
-This is a deprecated synonym for B<Statement>. It will be removed in version 5
-of collectd.
-
=item B<Param> I<hostname>|I<database>|I<username>|I<interval>
Specify the parameters which should be passed to the SQL query. The parameters
=item I<interval>
-The interval collectd is using (as specified by the B<Interval> option).
+The interval with which this database is queried (as specified by the database
+specific or global B<Interval> options).
=back
times. If multiple B<ValuesFrom> options are specified, the columns are read
in the given order.
-=item B<Column> I<type> [I<type instance>]
-
-This is a deprecated alternative to a B<Result> block. It will be removed in
-version 5 of collectd. It is equivalent to the following B<Result> block:
-
- <Result>
- Type I<type>
- InstancePrefix I<type instance>
- ValuesFrom I<name of the x. column>
- </Result>
-
-The order of the B<Column> 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<MinVersion> I<version>
=item B<MaxVersion> I<version>
and patch-level versions, each represented as two-decimal-digit numbers. For
example, version 8.2.3 will become 80203.
-=item B<MinPGVersion> I<version>
-
-=item B<MaxPGVersion> I<version>
-
-These are deprecated synonyms for B<MinVersion> and B<MaxVersion>
-respectively. They will be removed in version 5 of collectd.
-
=back
The following predefined queries are available (the definitions can be found
=over 4
+=item B<Interval> I<seconds>
+
+Specify the interval with which the database should be queried. The default is
+to use the global B<Interval> setting.
+
=item B<Host> I<hostname>
Specify the hostname or IP of the PostgreSQL server to connect to. If the
User "collectd"
Password "secr3t"
CollectInterface true
+ CollectCPULoad true
+ CollectMemory true
</Router>
<Router>
Host "router1.example.com"
Password "5ecret"
CollectInterface true
CollectRegistrationTable true
+ CollectDF true
+ CollectDisk true
</Router>
</Plugin>
When set to B<true>, information about wireless LAN connections will be
collected. Defaults to B<false>.
+=item B<CollectCPULoad> B<true>|B<false>
+
+When set to B<true>, information about the CPU usage will be collected. The
+number is a dimensionless value where zero indicates no CPU usage at all.
+Defaults to B<false>.
+
+=item B<CollectMemory> B<true>|B<false>
+
+When enabled, the amount of used and free memory will be collected. How used
+memory is calculated is unknown, for example whether or not caches are counted
+as used space.
+Defaults to B<false>.
+
+=item B<CollectDF> B<true>|B<false>
+
+When enabled, the amount of used and free disk space will be collected.
+Defaults to B<false>.
+
+=item B<CollectDisk> B<true>|B<false>
+
+When enabled, the number of sectors written and bad blocks will be collected.
+Defaults to B<false>.
+
+=back
+
+=head2 Plugin C<redis>
+
+The I<Redis plugin> connects to one or more Redis servers and gathers
+information about each server's state. For each server there is a I<Node> block
+which configures the connection parameters for this node.
+
+ <Plugin redis>
+ <Node "example">
+ Host "localhost"
+ Port "6379"
+ Timeout 2000
+ </Node>
+ </Plugin>
+
+The information shown in the synopsis above is the I<default configuration>
+which is used by the plugin if no configuration is present.
+
+=over 4
+
+=item B<Node> I<Nodename>
+
+The B<Node> block identifies a new Redis node, that is a new Redis instance
+running in an specified host and port. The name for node is a canonical
+identifier which is used as I<plugin instance>. It is limited to
+64E<nbsp>characters in length.
+
+=item B<Host> I<Hostname>
+
+The B<Host> option is the hostname or IP-address where the Redis instance is
+running on.
+
+=item B<Port> I<Port>
+
+The B<Port> option is the TCP port on which the Redis instance accepts
+connections. Either a service name of a port number may be given. Please note
+that numerical port numbers must be given as a string, too.
+
+=item B<Timeout> I<Timeout in miliseconds>
+
+The B<Timeout> option set the socket timeout for node response. Since the Redis
+read function is blocking, you should keep this value as low as possible. Keep
+in mind that the sum of all B<Timeout> values for all B<Nodes> should be lower
+than B<Interval> defined globally.
+
=back
=head2 Plugin C<rrdcached>
=head2 Plugin C<sensors>
-The C<sensors plugin> uses B<lm_sensors> to retrieve sensor-values. This means
+The I<Sensors plugin> uses B<lm_sensors> 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</etc/sensors.conf>. Read
L<sensors.conf(5)> for details.
other plugins, its documentation has been moved to an own manpage,
L<collectd-snmp(5)>. Please see there for details.
+=head2 Plugin C<swap>
+
+The I<Swap plugin> collects information about used and available swap space. On
+I<Solaris>, the following options are available:
+
+=over 4
+
+=item B<ReportByDevice> B<false>|B<true>
+
+Configures how to report physical swap devices. If set to B<false> 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<true> is configured, the used
+and available space of each device will be reported separately.
+
+This option is only available if the I<Swap plugin> can use the L<swapctl(2)>
+mechanism under I<Solaris>.
+
+=back
+
=head2 Plugin C<syslog>
=over 4
=item B<CounterSet>
-The matched number is a counter. Simply sets the internal counter to this
-value.
+=item B<DeriveSet>
+
+=item B<AbsoluteSet>
+
+The matched number is a counter. Simply I<sets> the internal counter to this
+value. Variants exist for C<COUNTER>, C<DERIVE>, and C<ABSOLUTE> data sources.
=item B<CounterAdd>
-Add the matched value to the internal counter.
+=item B<DeriveAdd>
+
+Add the matched value to the internal counter. In case of B<DeriveAdd>, the
+matched number may be negative, which will effectively subtract from the
+internal counter.
=item B<CounterInc>
-Increase the internal counter by one. This B<DSType> is the only one that does
-not use the matched subexpression, but simply counts the number of matched
+=item B<DeriveInc>
+
+Increase the internal counter by one. These B<DSType> are the only ones that do
+not use the matched subexpression, but simply count the number of matched
lines. Thus, you may use a regular expression without submatch in this case.
=back
As you'd expect the B<Gauge*> types interpret the submatch as a floating point
-number, using L<strtod(3)>. The B<CounterSet> and B<CounterAdd> interpret the
-submatch as an integer using L<strtoll(3)>. B<CounterInc> does not use the
-submatch at all and it may be omitted in this case.
+number, using L<strtod(3)>. The B<Counter*> and B<AbsoluteSet> types interpret
+the submatch as an unsigned integer using L<strtoull(3)>. The B<Derive*> types
+interpret the submatch as a signed integer using L<strtoll(3)>. B<CounterInc>
+and B<DeriveInc> do not use the submatch at all and it may be omitted in this
+case.
=item B<Type> I<Type>
=item B<ForceUseProcfs> I<true>|I<false>
-By default, the C<thermal> plugin tries to read the statistics from the Linux
+By default, the I<Thermal plugin> tries to read the statistics from the Linux
C<sysfs> interface. If that is not available, the plugin falls back to the
C<procfs> interface. By setting this option to I<true>, you can force the
plugin to use the latter. This option defaults to I<false>.
=back
+=head2 Plugin C<threshold>
+
+The I<Threshold plugin> checks values collected or received by I<collectd>
+against a configurable I<threshold> and issues I<notifications> if values are
+out of bounds.
+
+Documentation for this plugin is available in the L<collectd-threshold(5)>
+manual page.
+
=head2 Plugin C<tokyotyrant>
-The C<tokyotyrant plugin> connects to a TokyoTyrant server and collects a
+The I<TokyoTyrant plugin> connects to a TokyoTyrant server and collects a
couple metrics: number of records, and database size on disk.
=over 4
permissions must be given as a numeric, octal value as you would pass to
L<chmod(1)>. Defaults to B<0770>.
+=item B<DeleteSocket> B<false>|B<true>
+
+If set to B<true>, delete the socket file before calling L<bind(2)>, if a file
+with the given name already exists. If I<collectd> 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<false>.
+
=back
=head2 Plugin C<uuid>
=back
+=head2 Plugin C<varnish>
+
+The Varnish plugin collects information about Varnish, an HTTP accelerator.
+
+=over 4
+
+=item B<CollectCache> B<true>|B<false>
+
+Cache hits and misses. True by default.
+
+=item B<CollectConnections> B<true>|B<false>
+
+Number of client connections received, accepted and dropped. True by default.
+
+=item B<CollectBackend> B<true>|B<false>
+
+Back-end connection statistics, such as successful, reused,
+and closed connections. True by default.
+
+=item B<CollectSHM> B<true>|B<false>
+
+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<CollectESI> B<true>|B<false>
+
+Edge Side Includes (ESI) parse statistics. False by default.
+
+=item B<CollectFetch> B<true>|B<false>
+
+Statistics about fetches (HTTP requests sent to the backend). False by default.
+
+=item B<CollectHCB> B<true>|B<false>
+
+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<CollectSMA> B<true>|B<false>
+
+malloc or umem (umem_alloc(3MALLOC) based) storage statistics.
+The umem storage component is Solaris specific. False by default.
+
+=item B<CollectSMS> B<true>|B<false>
+
+synth (synthetic content) storage statistics. This storage
+component is used internally only. False by default.
+
+=item B<CollectSM> B<true>|B<false>
+
+file (memory mapped file) storage statistics. False by default.
+
+=item B<CollectTotals> B<true>|B<false>
+
+Collects overview counters, such as the number of sessions created,
+the number of requests and bytes transferred. False by default.
+
+=item B<CollectWorkers> B<true>|B<false>
+
+Collect statistics about worker threads. False by default.
+
+=back
+
=head2 Plugin C<vmem>
The C<vmem> plugin collects information about the usage of virtual memory.
Defaults to B<Command>.
-=back
-
-=head1 THRESHOLD CONFIGURATION
-
-Starting with version C<4.3.0> collectd has support for B<monitoring>. 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
-collectd takes itself is to generate and dispatch a "notification". 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
-B<thresholds> 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 twice the last timeout of the values. If, for example, 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<Interval> on the server.
-
-When a value comes within range again or is received after it was missing, an
-"OKAY-notification" is dispatched.
-
-Here is a configuration example to get you started. Read below for more
-information.
-
- <Threshold>
- <Type "foo">
- WarningMin 0.00
- WarningMax 1000.00
- FailureMin 0.00
- FailureMax 1200.00
- Invert false
- Instance "bar"
- </Type>
-
- <Plugin "interface">
- Instance "eth0"
- <Type "if_octets">
- FailureMax 10000000
- DataSource "rx"
- </Type>
- </Plugin>
-
- <Host "hostname">
- <Type "cpu">
- Instance "idle"
- FailureMin 10
- </Type>
-
- <Plugin "memory">
- <Type "memory">
- Instance "cached"
- WarningMin 100000000
- </Type>
- </Plugin>
- </Host>
- </Threshold>
-
-There are basically two types of configuration statements: The C<Host>,
-C<Plugin>, and C<Type> blocks select the value for which a threshold should be
-configured. The C<Plugin> and C<Type> blocks may be specified further using the
-C<Instance> option. You can combine the block by nesting the blocks, though
-they must be nested in the above order, i.E<nbsp>e. C<Host> may contain either
-C<Plugin> and C<Type> blocks, C<Plugin> may only contain C<Type> blocks and
-C<Type> 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<must> be
-included in a C<Type> block. Currently the following statements are recognized:
-
-=over 4
-
-=item B<FailureMax> I<Value>
-
-=item B<WarningMax> I<Value>
-
-Sets the upper bound of acceptable values. If unset defaults to positive
-infinity. If a value is greater than B<FailureMax> a B<FAILURE> notification
-will be created. If the value is greater than B<WarningMax> but less than (or
-equal to) B<FailureMax> a B<WARNING> notification will be created.
-
-=item B<FailureMin> I<Value>
-
-=item B<WarningMin> I<Value>
-
-Sets the lower bound of acceptable values. If unset defaults to negative
-infinity. If a value is less than B<FailureMin> a B<FAILURE> notification will
-be created. If the value is less than B<WarningMin> but greater than (or equal
-to) B<FailureMin> a B<WARNING> notification will be created.
-
-=item B<DataSource> I<DSName>
-
-Some data sets have more than one "data source". Interesting examples are the
-C<if_octets> data set, which has received (C<rx>) and sent (C<tx>) bytes and
-the C<disk_ops> data set, which holds C<read> and C<write> operations. The
-system load data set, C<load>, even has three data sources: C<shortterm>,
-C<midterm>, and C<longterm>.
-
-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<DataSource> option to have a threshold apply only to
-one data source.
-
-=item B<Invert> B<true>|B<false>
-
-If set to B<true> the range of acceptable values is inverted, i.E<nbsp>e.
-values between B<FailureMin> and B<FailureMax> (B<WarningMin> and
-B<WarningMax>) are not okay. Defaults to B<false>.
-
-=item B<Persist> B<true>|B<false>
-
-Sets how often notifications are generated. If set to B<true> one notification
-will be generated for each value that is out of the acceptable range. If set to
-B<false> (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<true> a notification about a
-missing value is generated once every B<Interval> seconds. If set to B<false>
-only one such notification is generated until the value appears again.
-
-=item B<Percentage> B<true>|B<false>
+=item B<StoreRates> B<true|false>
-If set to B<true>, 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<nbsp>% of the total space is available. Defaults to B<false>.
+If set to B<true>, convert counter values to rates. If set to B<false> (the
+default) counter values are stored as is, i.E<nbsp>e. as an increasing integer
+number.
=back
the identifier of a value. If multiple regular expressions are given, B<all>
regexen must match for a value to match.
+=item B<Invert> B<false>|B<true>
+
+When set to B<true>, the result of the match is inverted, i.e. all value lists
+where all regular expressions apply are not matched, all other value lists are
+matched. Defaults to B<false>.
+
=back
Example:
#if HAVE_STDINT_H
# include <stdint.h>
#endif
-#if HAVE_STDBOOL_H
-# include <stdbool.h>
-#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 <unistd.h>
#endif
# include <kstat.h>
#endif
-#if HAVE_SENSORS_SENSORS_H
-# include <sensors/sensors.h>
-#endif
-
#ifndef PACKAGE_NAME
#define PACKAGE_NAME "collectd"
#endif
# endif
#endif
-extern char hostname_g[];
-extern int interval_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 */
--- /dev/null
+/**
+ * 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 <hakon-dugstad.johnsen at telenor.com>
+ * Sebastian "tokkee" Harl <sh@tokkee.org>
+ **/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include <assert.h>
+#include <errno.h>
+
+#if NAN_STATIC_DEFAULT
+# include <math.h>
+/* #endif NAN_STATIC_DEFAULT*/
+#elif NAN_STATIC_ISOC
+# ifndef __USE_ISOC99
+# define DISABLE_ISOC99 1
+# define __USE_ISOC99 1
+# endif /* !defined(__USE_ISOC99) */
+# include <math.h>
+# if DISABLE_ISOC99
+# undef DISABLE_ISOC99
+# undef __USE_ISOC99
+# endif /* DISABLE_ISOC99 */
+/* #endif NAN_STATIC_ISOC */
+#elif NAN_ZERO_ZERO
+# include <math.h>
+# ifdef NAN
+# undef NAN
+# endif
+# define NAN (0.0 / 0.0)
+# ifndef isnan
+# define isnan(f) ((f) != (f))
+# endif /* !defined(isnan) */
+# 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] <command> [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 <identifier>\n"
+ " * flush [timeout=<seconds>] [plugin=<name>] [identifier=<id>]\n"
+ " * listval\n"
+ " * putval <identifier> [interval=<seconds>] <value-list(s)>\n"
+
+ "\nIdentifiers:\n\n"
+
+ "An identifier has the following format:\n\n"
+
+ " [<hostname>/]<plugin>[-<plugin_instance>]/<type>[-<type_instance>]\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 <octo@verplant.org>\n"
+ "for contributions see `AUTHORS'\n"
+ , name);
+ exit (status);
+}
+
+/* Count the number of occurrences of the character 'chr'
+ * in the specified string. */
+static int count_chars (const char *str, char chr) {
+ int count = 0;
+
+ while (*str != '\0') {
+ if (*str == chr) {
+ count++;
+ }
+ str++;
+ }
+
+ return count;
+} /* count_chars */
+
+static int array_grow (void **array, int *array_len, size_t elem_size)
+{
+ void *tmp;
+
+ assert ((array != NULL) && (array_len != NULL));
+
+ tmp = realloc (*array, (*array_len + 1) * elem_size);
+ if (tmp == NULL) {
+ fprintf (stderr, "ERROR: Failed to allocate memory.\n");
+ return (-1);
+ }
+
+ *array = tmp;
+ ++(*array_len);
+ return (0);
+} /* array_grow */
+
+static int parse_identifier (lcc_connection_t *c,
+ const char *value, lcc_identifier_t *ident)
+{
+ char hostname[1024];
+ char ident_str[1024] = "";
+ int n_slashes;
+
+ int status;
+
+ n_slashes = count_chars (value, '/');
+ if (n_slashes == 1) {
+ /* The user has omitted the hostname part of the identifier
+ * (there is only one '/' in the identifier)
+ * Let's add the local hostname */
+ if (gethostname (hostname, sizeof (hostname)) != 0) {
+ fprintf (stderr, "ERROR: Failed to get local hostname: %s",
+ strerror (errno));
+ return (-1);
+ }
+ hostname[sizeof (hostname) - 1] = '\0';
+
+ snprintf (ident_str, sizeof (ident_str), "%s/%s", hostname, value);
+ ident_str[sizeof(ident_str) - 1] = '\0';
+ }
+ else {
+ strncpy (ident_str, value, sizeof (ident_str));
+ ident_str[sizeof (ident_str) - 1] = '\0';
+ }
+
+ status = lcc_string_to_identifier (c, ident, ident_str);
+ if (status != 0) {
+ fprintf (stderr, "ERROR: Failed to parse identifier ``%s'': %s.\n",
+ ident_str, lcc_strerror(c));
+ return (-1);
+ }
+ return (0);
+} /* parse_identifier */
+
+static int getval (lcc_connection_t *c, int argc, char **argv)
+{
+ lcc_identifier_t ident;
+
+ size_t ret_values_num = 0;
+ gauge_t *ret_values = NULL;
+ char **ret_values_names = NULL;
+
+ int status;
+ size_t i;
+
+ assert (strcasecmp (argv[0], "getval") == 0);
+
+ if (argc != 2) {
+ fprintf (stderr, "ERROR: getval: Missing identifier.\n");
+ return (-1);
+ }
+
+ memset (&ident, 0, sizeof (ident));
+ status = parse_identifier (c, argv[1], &ident);
+ if (status != 0)
+ return (status);
+
+#define BAIL_OUT(s) \
+ do { \
+ if (ret_values != NULL) \
+ free (ret_values); \
+ if (ret_values_names != NULL) { \
+ for (i = 0; i < ret_values_num; ++i) \
+ free (ret_values_names[i]); \
+ free (ret_values_names); \
+ } \
+ ret_values_num = 0; \
+ return (s); \
+ } while (0)
+
+ status = lcc_getval (c, &ident,
+ &ret_values_num, &ret_values, &ret_values_names);
+ if (status != 0) {
+ fprintf (stderr, "ERROR: %s\n", lcc_strerror (c));
+ BAIL_OUT (-1);
+ }
+
+ for (i = 0; i < ret_values_num; ++i)
+ printf ("%s=%e\n", ret_values_names[i], ret_values[i]);
+ BAIL_OUT (0);
+#undef BAIL_OUT
+} /* getval */
+
+static int flush (lcc_connection_t *c, int argc, char **argv)
+{
+ int timeout = -1;
+
+ lcc_identifier_t *identifiers = NULL;
+ int identifiers_num = 0;
+
+ char **plugins = NULL;
+ int plugins_num = 0;
+
+ int status;
+ int i;
+
+ assert (strcasecmp (argv[0], "flush") == 0);
+
+#define BAIL_OUT(s) \
+ do { \
+ if (identifiers != NULL) \
+ free (identifiers); \
+ identifiers_num = 0; \
+ if (plugins != NULL) \
+ free (plugins); \
+ plugins_num = 0; \
+ return (s); \
+ } while (0)
+
+ for (i = 1; i < argc; ++i) {
+ char *key, *value;
+
+ key = argv[i];
+ value = strchr (argv[i], (int)'=');
+
+ if (! value) {
+ fprintf (stderr, "ERROR: flush: Invalid option ``%s''.\n", argv[i]);
+ BAIL_OUT (-1);
+ }
+
+ *value = '\0';
+ ++value;
+
+ if (strcasecmp (key, "timeout") == 0) {
+ char *endptr = NULL;
+
+ timeout = (int) strtol (value, &endptr, 0);
+
+ if (endptr == value) {
+ fprintf (stderr, "ERROR: Failed to parse timeout as number: %s.\n",
+ value);
+ BAIL_OUT (-1);
+ }
+ else if ((endptr != NULL) && (*endptr != '\0')) {
+ fprintf (stderr, "WARNING: Ignoring trailing garbage after timeout: "
+ "%s.\n", endptr);
+ }
+ }
+ else if (strcasecmp (key, "plugin") == 0) {
+ status = array_grow ((void *)&plugins, &plugins_num,
+ sizeof (*plugins));
+ if (status != 0)
+ BAIL_OUT (status);
+
+ plugins[plugins_num - 1] = value;
+ }
+ else if (strcasecmp (key, "identifier") == 0) {
+ status = array_grow ((void *)&identifiers, &identifiers_num,
+ sizeof (*identifiers));
+ if (status != 0)
+ BAIL_OUT (status);
+
+ memset (identifiers + (identifiers_num - 1), 0, sizeof (*identifiers));
+ status = parse_identifier (c, value,
+ identifiers + (identifiers_num - 1));
+ if (status != 0)
+ BAIL_OUT (status);
+ }
+ else {
+ fprintf (stderr, "ERROR: flush: Unknown option `%s'.\n", key);
+ BAIL_OUT (-1);
+ }
+ }
+
+ if (plugins_num == 0) {
+ status = array_grow ((void *)&plugins, &plugins_num, sizeof (*plugins));
+ if (status != 0)
+ BAIL_OUT (status);
+
+ assert (plugins_num == 1);
+ plugins[0] = NULL;
+ }
+
+ for (i = 0; i < plugins_num; ++i) {
+ if (identifiers_num == 0) {
+ status = lcc_flush (c, plugins[i], NULL, timeout);
+ if (status != 0)
+ fprintf (stderr, "ERROR: Failed to flush plugin `%s': %s.\n",
+ (plugins[i] == NULL) ? "(all)" : plugins[i], lcc_strerror (c));
+ }
+ else {
+ int j;
+
+ for (j = 0; j < identifiers_num; ++j) {
+ status = lcc_flush (c, plugins[i], identifiers + j, timeout);
+ if (status != 0) {
+ char id[1024];
+
+ lcc_identifier_to_string (c, id, sizeof (id), identifiers + j);
+ fprintf (stderr, "ERROR: Failed to flush plugin `%s', "
+ "identifier `%s': %s.\n",
+ (plugins[i] == NULL) ? "(all)" : plugins[i],
+ id, lcc_strerror (c));
+ }
+ }
+ }
+ }
+
+ BAIL_OUT (0);
+#undef BAIL_OUT
+} /* flush */
+
+static int listval (lcc_connection_t *c, int argc, char **argv)
+{
+ lcc_identifier_t *ret_ident = NULL;
+ size_t ret_ident_num = 0;
+
+ int status;
+ size_t i;
+
+ assert (strcasecmp (argv[0], "listval") == 0);
+
+ if (argc != 1) {
+ fprintf (stderr, "ERROR: listval: Does not accept any arguments.\n");
+ return (-1);
+ }
+
+#define BAIL_OUT(s) \
+ do { \
+ if (ret_ident != NULL) \
+ free (ret_ident); \
+ ret_ident_num = 0; \
+ return (s); \
+ } while (0)
+
+ status = lcc_listval (c, &ret_ident, &ret_ident_num);
+ if (status != 0) {
+ fprintf (stderr, "ERROR: %s\n", lcc_strerror (c));
+ BAIL_OUT (status);
+ }
+
+ for (i = 0; i < ret_ident_num; ++i) {
+ char id[1024];
+
+ status = lcc_identifier_to_string (c, id, sizeof (id), ret_ident + i);
+ if (status != 0) {
+ fprintf (stderr, "ERROR: listval: Failed to convert returned "
+ "identifier to a string: %s\n", lcc_strerror (c));
+ continue;
+ }
+
+ printf ("%s\n", id);
+ }
+ BAIL_OUT (0);
+#undef BAIL_OUT
+} /* listval */
+
+static int putval (lcc_connection_t *c, int argc, char **argv)
+{
+ lcc_value_list_t vl = LCC_VALUE_LIST_INIT;
+
+ /* 64 ought to be enough for anybody ;-) */
+ value_t values[64];
+ int values_types[64];
+ size_t values_len = 0;
+
+ int status;
+ int i;
+
+ assert (strcasecmp (argv[0], "putval") == 0);
+
+ if (argc < 3) {
+ fprintf (stderr, "ERROR: putval: Missing identifier "
+ "and/or value list.\n");
+ return (-1);
+ }
+
+ vl.values = values;
+ vl.values_types = values_types;
+
+ status = parse_identifier (c, argv[1], &vl.identifier);
+ if (status != 0)
+ return (status);
+
+ for (i = 2; i < argc; ++i) {
+ char *tmp;
+
+ tmp = strchr (argv[i], (int)'=');
+
+ if (tmp != NULL) { /* option */
+ char *key = argv[i];
+ char *value = tmp;
+
+ *value = '\0';
+ ++value;
+
+ if (strcasecmp (key, "interval") == 0) {
+ char *endptr;
+
+ vl.interval = strtol (value, &endptr, 0);
+
+ if (endptr == value) {
+ fprintf (stderr, "ERROR: Failed to parse interval as number: %s.\n",
+ value);
+ return (-1);
+ }
+ else if ((endptr != NULL) && (*endptr != '\0')) {
+ fprintf (stderr, "WARNING: Ignoring trailing garbage after "
+ "interval: %s.\n", endptr);
+ }
+ }
+ else {
+ fprintf (stderr, "ERROR: putval: Unknown option `%s'.\n", key);
+ return (-1);
+ }
+ }
+ else { /* value list */
+ char *value;
+
+ tmp = strchr (argv[i], (int)':');
+
+ if (tmp == NULL) {
+ fprintf (stderr, "ERROR: putval: Invalid value list: %s.\n",
+ argv[i]);
+ return (-1);
+ }
+
+ *tmp = '\0';
+ ++tmp;
+
+ if (strcasecmp (argv[i], "N") == 0) {
+ vl.time = 0;
+ }
+ else {
+ char *endptr;
+
+ vl.time = strtol (argv[i], &endptr, 0);
+
+ if (endptr == argv[i]) {
+ fprintf (stderr, "ERROR: Failed to parse time as number: %s.\n",
+ argv[i]);
+ return (-1);
+ }
+ else if ((endptr != NULL) && (*endptr != '\0')) {
+ fprintf (stderr, "ERROR: Garbage after time: %s.\n", endptr);
+ return (-1);
+ }
+ }
+
+ values_len = 0;
+ value = tmp;
+ while (value != 0) {
+ char *dot, *endptr;
+
+ tmp = strchr (argv[i], (int)':');
+
+ if (tmp != NULL) {
+ *tmp = '\0';
+ ++tmp;
+ }
+
+ /* This is a bit of a hack, but parsing types.db just does not make
+ * much sense imho -- the server might have different types defined
+ * anyway. Also, lcc uses the type information for formatting the
+ * number only, so the real meaning does not matter. -tokkee */
+ dot = strchr (value, (int)'.');
+ endptr = NULL;
+ if (strcasecmp (value, "U") == 0) {
+ values[values_len].gauge = NAN;
+ values_types[values_len] = LCC_TYPE_GAUGE;
+ }
+ else if (dot) { /* floating point value */
+ values[values_len].gauge = strtod (value, &endptr);
+ values_types[values_len] = LCC_TYPE_GAUGE;
+ }
+ else { /* integer */
+ values[values_len].counter = strtol (value, &endptr, 0);
+ values_types[values_len] = LCC_TYPE_COUNTER;
+ }
+ ++values_len;
+
+ if (endptr == value) {
+ fprintf (stderr, "ERROR: Failed to parse value as number: %s.\n",
+ argv[i]);
+ return (-1);
+ }
+ else if ((endptr != NULL) && (*endptr != '\0')) {
+ fprintf (stderr, "ERROR: Garbage after value: %s.\n", endptr);
+ return (-1);
+ }
+
+ value = tmp;
+ }
+
+ assert (values_len >= 1);
+ vl.values_len = values_len;
+
+ status = lcc_putval (c, &vl);
+ if (status != 0) {
+ fprintf (stderr, "ERROR: %s\n", lcc_strerror (c));
+ return (-1);
+ }
+ }
+ }
+
+ if (values_len == 0) {
+ fprintf (stderr, "ERROR: putval: Missing value list(s).\n");
+ return (-1);
+ }
+ return (0);
+} /* putval */
+
+int main (int argc, char **argv) {
+ char address[1024] = "unix:"DEFAULT_SOCK;
+
+ lcc_connection_t *c;
+
+ int status;
+
+ while (42) {
+ int c;
+
+ c = getopt (argc, argv, "s:h");
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 's':
+ snprintf (address, sizeof (address), "unix:%s", optarg);
+ address[sizeof (address) - 1] = '\0';
+ break;
+ case 'h':
+ exit_usage (argv[0], 0);
+ break;
+ default:
+ exit_usage (argv[0], 1);
+ }
+ }
+
+ if (optind >= argc) {
+ fprintf (stderr, "%s: missing command\n", argv[0]);
+ exit_usage (argv[0], 1);
+ }
+
+ c = NULL;
+ status = lcc_connect (address, &c);
+ if (status != 0) {
+ fprintf (stderr, "ERROR: Failed to connect to daemon at %s: %s.\n",
+ address, strerror (errno));
+ return (1);
+ }
+
+ if (strcasecmp (argv[optind], "getval") == 0)
+ status = getval (c, argc - optind, argv + optind);
+ else if (strcasecmp (argv[optind], "flush") == 0)
+ status = flush (c, argc - optind, argv + optind);
+ else if (strcasecmp (argv[optind], "listval") == 0)
+ status = listval (c, argc - optind, argv + optind);
+ else if (strcasecmp (argv[optind], "putval") == 0)
+ status = putval (c, argc - optind, argv + optind);
+ else {
+ fprintf (stderr, "%s: invalid command: %s\n", argv[0], argv[optind]);
+ return (1);
+ }
+
+ LCC_DESTROY (c);
+
+ if (status != 0)
+ return (status);
+ return (0);
+} /* main */
+
+/* vim: set sw=2 ts=2 tw=78 expandtab : */
+
--- /dev/null
+=head1 NAME
+
+collectdctl - Control interface for collectd
+
+=head1 SYNOPSIS
+
+collectdctl I<[options]> I<E<lt>commandE<gt>> I<[command options]>
+
+=head1 DESCRIPTION
+
+collectdctl provides a control interface for collectd, which may be used to
+interact with the daemon using the C<unixsock plugin>.
+
+=head1 OPTIONS
+
+collectdctl supports the following options:
+
+=over 4
+
+=item B<-s> I<socket>
+
+Path to the UNIX socket opened by collectd's C<unixsock plugin>.
+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<getval> I<E<lt>identifierE<gt>>
+
+Query the latest collected value identified by the specified
+I<E<lt>identifierE<gt>> (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<flush> [B<timeout=>I<E<lt>secondsE<gt>>] [B<plugin=>I<E<lt>nameE<gt>>]
+[B<identifier=>I<E<lt>idE<gt>>]
+
+Flush the daemon. This is useful, e.E<nbsp>g., 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 B<timeout=>I<E<lt>secondsE<gt>>
+
+Flush values older than the specified timeout (in seconds) only.
+
+=item B<plugin=>I<E<lt>nameE<gt>>
+
+Flush the specified plugin only. I.E<nbsp>e., data cached by the specified
+plugin is written to disk (or network or whatever), if the plugin supports
+that operation.
+
+Example: B<rrdtool>.
+
+=item B<identifier=>I<E<lt>idE<gt>>
+
+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.E<nbsp>g., the C<network> plugin does not support this).
+
+=back
+
+The B<plugin> and B<identifier> options may be specified more than once. In
+that case, all combinations of specified plugins and identifiers will be
+flushed only.
+
+=item B<listval>
+
+Returns a list of all values (by their identifier) available to the
+C<unixsock> plugin. Each value is printed on its own line. I.E<nbsp>e., this
+command returns a list of valid identifiers that may be used with the other
+commands.
+
+=item B<putval> I<E<lt>identifierE<gt>> [B<interval=>I<E<lt>secondsE<gt>>]
+I<E<lt>value-list(s)E<gt>>
+
+Submit one or more values (identified by I<E<lt>identifierE<gt>>, see below)
+to the daemon which will then dispatch them to the write plugins. B<interval>
+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 I<E<lt>value-list(s)E<gt>> (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<types.db(5)> for details).
+
+=back
+
+=head1 IDENTIFIERS
+
+An identifier has the following format:
+
+[I<hostname>/]I<plugin>[-I<plugin_instance>]/I<type>[-I<type_instance>]
+
+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<libcollectdclient> 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<U> 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<types.db(5)> for details). The time is specified as
+epoch (i.E<nbsp>e., standard UNIX time) or as a literal C<N> which will be
+interpreted as now.
+
+=head1 EXAMPLES
+
+=over 4
+
+=item C<collectdctl flush plugin=rrdtool identifier=somehost/cpu-0/cpu-wait>
+
+Flushes all CPU wait RRD values of the first CPU of the local host.
+I.E<nbsp>e., writes all pending RRD updates of that data-source to disk.
+
+=item C<for ident in `collectdctl listval | grep users/users`; do
+ collectdctl getval $ident;
+ done>
+
+Query the latest number of logged in users on all hosts known to the local
+collectd instance.
+
+=back
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+L<collectd-unixsock(5)>,
+L<types.db(5)>
+
+=head1 AUTHOR
+
+collectd has been written by Florian Forster E<lt>octo at verplant.orgE<gt>
+and many contributors (see `AUTHORS').
+
+collectdctl has been written by
+Håkon J Dugstad Johnsen E<lt>hakon-dugstad.johnsenE<nbsp>atE<nbsp>telenor.comE<gt>
+and Sebastian Harl E<lt>sh at tokkee.orgE<gt>.
+
+=cut
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Niki W. Waibel <niki.waibel@gmx.net>
* Sebastian Harl <sh at tokkee.org>
* Michał Mirosław <mirq-linux at rere.qmqm.pl>
#include "collectd.h"
#include "common.h"
#include "plugin.h"
+#include "utils_cache.h"
#if HAVE_PTHREAD_H
# include <pthread.h>
# include <math.h>
#endif
-/* for ntohl and htonl */
-#if HAVE_ARPA_INET_H
-# include <arpa/inet.h>
-#endif
-
/* for getaddrinfo */
#include <sys/types.h>
#include <sys/socket.h>
# include <netinet/in.h>
#endif
+/* for ntohl and htonl */
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
#ifdef HAVE_LIBKSTAT
extern kstat_ctl_t *kc;
#endif
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)
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 (host));
+ sstrncpy (vl->plugin, plugin, sizeof (plugin));
+ sstrncpy (vl->plugin_instance,
+ (plugin_instance != NULL) ? plugin_instance : "",
+ sizeof (plugin_instance));
+ sstrncpy (vl->type, type, sizeof (type));
+ sstrncpy (vl->type_instance,
+ (type_instance != NULL) ? type_instance : "",
+ sizeof (type_instance));
+
+ return (0);
+} /* }}} int parse_identifier_vl */
+
int parse_value (const char *value, value_t *ret_value, int ds_type)
{
char *endptr = NULL;
break;
case DS_TYPE_DERIVE:
- ret_value->counter = (derive_t) strtoll (value, &endptr, 0);
+ ret_value->derive = (derive_t) strtoll (value, &endptr, 0);
break;
case DS_TYPE_ABSOLUTE:
- ret_value->counter = (absolute_t) strtoull (value, &endptr, 0);
+ ret_value->absolute = (absolute_t) strtoull (value, &endptr, 0);
break;
default:
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
{
} /* int notification_init */
int walk_directory (const char *dir, dirwalk_callback_f callback,
- void *user_data)
+ void *user_data, int include_hidden)
{
struct dirent *ent;
DIR *dh;
while ((ent = readdir (dh)) != NULL)
{
int status;
-
- if (ent->d_name[0] == '.')
- continue;
+
+ if (include_hidden)
+ {
+ if ((strcmp (".", ent->d_name) == 0)
+ || (strcmp ("..", ent->d_name) == 0))
+ continue;
+ }
+ else /* if (!include_hidden) */
+ {
+ if (ent->d_name[0]=='.')
+ continue;
+ }
status = (*callback) (dir, ent->d_name, user_data);
if (status != 0)
return (service_number);
return (-1);
} /* int service_name_to_port_number */
+
+int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
+{
+ derive_t tmp;
+ char *endptr;
+
+ if ((string == NULL) || (ret_value == NULL))
+ return (EINVAL);
+
+ errno = 0;
+ endptr = NULL;
+ tmp = (derive_t) strtoll (string, &endptr, /* base = */ 0);
+ if ((endptr == string) || (errno != 0))
+ return (-1);
+
+ *ret_value = tmp;
+ return (0);
+} /* }}} int strtoderive */
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Niki W. Waibel <niki.waibel@gmx.net>
**/
#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);
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);
int walk_directory (const char *dir, dirwalk_callback_f callback,
- void *user_data);
+ void *user_data, int hidden);
int read_file_contents (const char *filename, char *buf, int bufsize);
counter_t counter_diff (counter_t old_value, counter_t new_value);
* (in the range [1-65535]). Returns less than zero on error. */
int service_name_to_port_number (const char *service_name);
+/** Parse a string to a derive_t value. Returns zero on success or non-zero on
+ * failure. If failure is returned, ret_value is not touched. */
+int strtoderive (const char *string, derive_t *ret_value);
+
#endif /* COMMON_H */
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Sebastian tokkee Harl <sh at tokkee.org>
**/
#include "plugin.h"
#include "configfile.h"
#include "types_list.h"
-#include "utils_threshold.h"
#include "filter_chain.h"
#if HAVE_WORDEXP_H
{"BaseDir", NULL, PKGLOCALSTATEDIR},
{"PIDFile", NULL, PIDFILE},
{"Hostname", NULL, NULL},
- {"FQDNLookup", NULL, "false"},
+ {"FQDNLookup", NULL, "true"},
{"Interval", NULL, "10"},
{"ReadThreads", NULL, "5"},
+ {"Timeout", NULL, "2"},
{"PreCacheChain", NULL, "PreCache"},
{"PostCacheChain", NULL, "PostCache"}
};
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)
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)
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));
if (status != 0)
{
char errbuf[1024];
- ERROR ("configfile: stat (%s) failed: %s",
+ WARNING ("configfile: stat (%s) failed: %s",
path_ptr,
sstrerror (errno, errbuf, sizeof (errbuf)));
- oconfig_free (root);
- return (NULL);
+ continue;
}
if (S_ISREG (statbuf.st_mode))
temp = cf_read_dir (path_ptr, depth);
else
{
- ERROR ("configfile: %s is neither a file nor a "
+ WARNING ("configfile: %s is neither a file nor a "
"directory.", path);
continue;
}
wordfree (&we);
+ if (root->children == NULL)
+ {
+ oconfig_free (root);
+ return (NULL);
+ }
+
return (root);
} /* oconfig_item_t *cf_read_generic */
/* #endif HAVE_WORDEXP_H */
return (0);
} /* }}} int cf_util_get_string */
+/* Assures the config option is a string and copies it to the provided buffer.
+ * Assures null-termination. */
+int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer, /* {{{ */
+ size_t buffer_size)
+{
+ if ((ci == NULL) || (buffer == NULL) || (buffer_size < 1))
+ return (EINVAL);
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ ERROR ("cf_util_get_string_buffer: The %s option requires "
+ "exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ strncpy (buffer, ci->values[0].value.string, buffer_size);
+ buffer[buffer_size - 1] = 0;
+
+ return (0);
+} /* }}} int cf_util_get_string_buffer */
+
+/* Assures the config option is a number and returns it as an int. */
+int cf_util_get_int (const oconfig_item_t *ci, int *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_int: The %s option requires "
+ "exactly one numeric argument.", ci->key);
+ return (-1);
+ }
+
+ *ret_value = (int) ci->values[0].value.number;
+
+ return (0);
+} /* }}} int cf_util_get_int */
+
int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
{
if ((ci == NULL) || (ret_bool == NULL))
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
{
ERROR ("cf_util_get_boolean: The %s option requires "
- "exactly one string argument.", ci->key);
+ "exactly one boolean argument.", ci->key);
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 */
+
#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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
**/
#include "collectd.h"
+#include "utils_time.h"
#include "liboconfig/oconfig.h"
/*
* success. */
int cf_util_get_string (const oconfig_item_t *ci, char **ret_string);
+/* Assures the config option is a string and copies it to the provided buffer.
+ * Assures null-termination. */
+int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer,
+ size_t buffer_size);
+
+/* Assures the config option is a number and returns it as an int. */
+int cf_util_get_int (const oconfig_item_t *ci, int *ret_value);
+
/* Assures the config option is a boolean and assignes it to `ret_bool'.
* 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) */
/**
* 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
*
* Authors:
* Patrik Weiskircher <weiskircher at inqnet.at>
+ * Kimo Rosenbaum <http://github.com/kimor79>
**/
#include "collectd.h"
#include "common.h"
#include "plugin.h"
-#if !KERNEL_LINUX
+#ifdef HAVE_SYS_SYSCTL_H
+# include <sys/sysctl.h>
+#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
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;
if (status == -2)
ERROR ("contextswitch plugin: Unable to find context switch value.");
+#endif /* KERNEL_LINUX */
return status;
}
/**
* 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
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)
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;
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
/*
#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];
#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)
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);
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)
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 */
* Sven Trenkel <collectd at semidefinite.de>
**/
+/* Some python versions don't include this by default. */
+
+#include <longintrepr.h>
+
/* These two macros are basicly Py_BEGIN_ALLOW_THREADS and Py_BEGIN_ALLOW_THREADS
* from the other direction. If a Python thread calls a C function
* Py_BEGIN_ALLOW_THREADS is used to allow other python threads to run because
# define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
#endif
+/* This macro is a shortcut for calls like
+ * x = PyObject_Repr(x);
+ * This can't be done like this example because this would leak
+ * a reference the the original x and crash in case of x == NULL.
+ * This calling syntax is less than elegant but it works, saves
+ * a lot of lines and avoids potential refcount errors. */
+
+#define CPY_SUBSTITUTE(func, a, ...) do {\
+ if ((a) != NULL) {\
+ PyObject *__tmp = (a);\
+ (a) = func(__VA_ARGS__);\
+ Py_DECREF(__tmp);\
+ }\
+} while(0)
+
+/* Python3 compatibility layer. To keep the actual code as clean as possible
+ * do a lot of defines here. */
+
+#if PY_MAJOR_VERSION >= 3
+#define IS_PY3K
+#endif
+
+#ifdef IS_PY3K
+
+#define PyInt_FromLong PyLong_FromLong
+#define CPY_INIT_TYPE PyVarObject_HEAD_INIT(NULL, 0)
+#define IS_BYTES_OR_UNICODE(o) (PyUnicode_Check(o) || PyBytes_Check(o))
+#define CPY_STRCAT_AND_DEL(a, b) do {\
+ CPY_STRCAT((a), (b));\
+ Py_XDECREF((b));\
+} while (0)
+static inline void CPY_STRCAT(PyObject **a, PyObject *b) {
+ PyObject *ret;
+
+ if (!a || !*a)
+ return;
+
+ ret = PyUnicode_Concat(*a, b);
+ Py_DECREF(*a);
+ *a = ret;
+}
+
+#else
+
+#define CPY_INIT_TYPE PyObject_HEAD_INIT(NULL) 0,
+#define IS_BYTES_OR_UNICODE(o) (PyUnicode_Check(o) || PyString_Check(o))
+#define CPY_STRCAT_AND_DEL PyString_ConcatAndDel
+#define CPY_STRCAT PyString_Concat
+
+#endif
+
+static inline const char *cpy_unicode_or_bytes_to_string(PyObject **o) {
+ if (PyUnicode_Check(*o)) {
+ PyObject *tmp;
+ tmp = PyUnicode_AsEncodedString(*o, NULL, NULL); /* New reference. */
+ if (tmp == NULL)
+ return NULL;
+ Py_DECREF(*o);
+ *o = tmp;
+ }
+#ifdef IS_PY3K
+ return PyBytes_AsString(*o);
+#else
+ return PyString_AsString(*o);
+#endif
+}
+
+static inline PyObject *cpy_string_to_unicode_or_bytes(const char *buf) {
+#ifdef IS_PY3K
+/* Python3 preferrs unicode */
+ PyObject *ret;
+ ret = PyUnicode_Decode(buf, strlen(buf), NULL, NULL);
+ if (ret != NULL)
+ return ret;
+ PyErr_Clear();
+ return PyBytes_FromString(buf);
+#else
+ return PyString_FromString(buf);
+#endif
+}
+
+void cpy_log_exception(const char *context);
+
+/* Python object declarations. */
+
typedef struct {
PyObject_HEAD /* No semicolon! */
PyObject *parent; /* Config */
PyObject *values; /* Sequence */
PyObject *children; /* Sequence */
} Config;
-
PyTypeObject ConfigType;
typedef struct {
char type[DATA_MAX_NAME_LEN];
char type_instance[DATA_MAX_NAME_LEN];
} PluginData;
-
PyTypeObject PluginDataType;
+#define PluginData_New() PyObject_CallFunctionObjArgs((PyObject *) &PluginDataType, (void *) 0)
typedef struct {
PluginData data;
PyObject *values; /* Sequence */
- int interval;
+ PyObject *meta; /* dict */
+ double interval;
} Values;
-
PyTypeObject ValuesType;
+#define Values_New() PyObject_CallFunctionObjArgs((PyObject *) &ValuesType, (void *) 0)
typedef struct {
PluginData data;
int severity;
char message[NOTIF_MAX_MSG_LEN];
} Notification;
-
PyTypeObject NotificationType;
+#define Notification_New() PyObject_CallFunctionObjArgs((PyObject *) &NotificationType, (void *) 0)
+
+typedef PyLongObject Signed;
+PyTypeObject SignedType;
+
+typedef PyLongObject Unsigned;
+PyTypeObject UnsignedType;
+
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;
}
fprintf (use_stdio == 1 ? stdout : stderr,
- "PUTVAL %s interval=%i %s\n",
- filename, interval_g, values);
+ "PUTVAL %s interval=%.3f %s\n",
+ filename,
+ CDTIME_T_TO_DOUBLE (vl->interval),
+ values);
return (0);
}
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));
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));
/**
* collectd - src/curl_json.c
* Copyright (C) 2009 Doug MacEachern
- * Copyright (C) 2006-2009 Florian octo Forster
+ * Copyright (C) 2006-2010 Florian octo Forster
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
#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))
char *user;
char *pass;
char *credentials;
- int verify_peer;
- int verify_host;
+ _Bool verify_peer;
+ _Bool verify_host;
char *cacert;
CURL *curl;
return (0);
status = yajl_parse(db->yajl, (unsigned char *)buf, len);
- if (status == yajl_status_ok)
- {
- status = yajl_parse_complete(db->yajl);
- return (len);
- }
- else if (status == yajl_status_insufficient_data)
- return (len);
-
- if (status != yajl_status_ok)
+ if ((status != yajl_status_ok)
+ && (status != yajl_status_insufficient_data))
{
unsigned char *msg =
- yajl_get_error(db->yajl, 1, (unsigned char *)buf, len);
+ yajl_get_error(db->yajl, /* verbose = */ 1,
+ /* jsonText = */ (unsigned char *) buf, (unsigned int) len);
ERROR ("curl_json plugin: yajl_parse failed: %s", msg);
yajl_free_error(db->yajl, msg);
return (0); /* abort write callback */
}
/* yajl callbacks */
-static int cj_cb_integer (void *ctx, long val)
+#define CJ_CB_ABORT 0
+#define CJ_CB_CONTINUE 1
+
+/* "number" may not be null terminated, so copy it into a buffer before
+ * parsing. */
+static int cj_cb_number (void *ctx,
+ const char *number, unsigned int number_len)
{
+ char buffer[number_len + 1];
+
cj_t *db = (cj_t *)ctx;
cj_key_t *key = db->state[db->depth].key;
+ value_t vt;
+ int type;
+ int status;
- if (key != NULL)
- {
- value_t vt;
- int type;
-
- type = cj_get_type (key);
- if (type == DS_TYPE_COUNTER)
- vt.counter = (counter_t) val;
- else if (type == DS_TYPE_GAUGE)
- vt.gauge = (gauge_t) val;
- else if (type == DS_TYPE_DERIVE)
- vt.derive = (derive_t) val;
- else if (type == DS_TYPE_ABSOLUTE)
- vt.absolute = (absolute_t) val;
- else
- return 0;
+ if ((key == NULL) || !CJ_IS_KEY (key))
+ return (CJ_CB_CONTINUE);
- cj_submit (db, key, &vt);
- }
- return 1;
-}
+ memcpy (buffer, number, number_len);
+ buffer[sizeof (buffer) - 1] = 0;
-static int cj_cb_double (void *ctx, double val)
-{
- cj_t *db = (cj_t *)ctx;
- cj_key_t *key = db->state[db->depth].key;
-
- if (key != NULL)
+ type = cj_get_type (key);
+ status = parse_value (buffer, &vt, type);
+ if (status != 0)
{
- value_t vt;
- int type;
-
- type = cj_get_type (key);
- if (type == DS_TYPE_COUNTER)
- vt.counter = (counter_t) val;
- else if (type == DS_TYPE_GAUGE)
- vt.gauge = (gauge_t) val;
- else if (type == DS_TYPE_DERIVE)
- vt.derive = (derive_t) val;
- else if (type == DS_TYPE_ABSOLUTE)
- vt.absolute = (absolute_t) val;
- else
- return 0;
-
- cj_submit (db, key, &vt);
+ NOTICE ("curl_json plugin: Unable to parse number: \"%s\"", buffer);
+ return (CJ_CB_CONTINUE);
}
- return 1;
-}
+
+ cj_submit (db, key, &vt);
+ return (CJ_CB_CONTINUE);
+} /* int cj_cb_number */
static int cj_cb_map_key (void *ctx, const unsigned char *val,
unsigned int len)
db->state[db->depth].key = NULL;
}
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_string (void *ctx, const unsigned char *val,
unsigned int len)
{
cj_t *db = (cj_t *)ctx;
- c_avl_tree_t *tree;
- char *ptr;
-
- if (db->depth != 1) /* e.g. _all_dbs */
- return 1;
+ 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 1;
-}
+
+ /* 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)
{
if (++db->depth >= YAJL_MAX_DEPTH)
{
ERROR ("curl_json plugin: %s depth exceeds max, aborting.", db->url);
- return 0;
+ return (CJ_CB_ABORT);
}
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_end (void *ctx)
cj_t *db = (cj_t *)ctx;
db->state[db->depth].tree = NULL;
--db->depth;
- return 1;
+ return (CJ_CB_CONTINUE);
}
static int cj_cb_start_map (void *ctx)
static int cj_cb_end_array (void * ctx)
{
- return cj_cb_start (ctx);
+ return cj_cb_end (ctx);
}
static yajl_callbacks ycallbacks = {
NULL, /* null */
NULL, /* boolean */
- cj_cb_integer,
- cj_cb_double,
- NULL, /* number */
+ NULL, /* integer */
+ NULL, /* double */
+ cj_cb_number,
cj_cb_string,
cj_cb_start_map,
cj_cb_map_key,
/* 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);
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);
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);
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);
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);
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
ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
db->instance, db->url);
- plugin_register_complex_read (cb_name, cj_read,
+ plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
/* interval = */ NULL, &ud);
}
else
if (db->yajl == NULL)
{
ERROR ("curl_json plugin: yajl_alloc failed.");
+ db->yajl = yprev;
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 : "<null>");
+ yajl_free (db->yajl);
+ db->yajl = yprev;
+ return (-1);
+ }
- curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
- if (rc != 200)
+ /* The response code is zero if a non-HTTP transport was used. */
+ if ((rc != 0) && (rc != 200))
{
- ERROR ("curl_json plugin: curl_easy_perform failed with response code %ld (%s)",
- rc, url);
+ ERROR ("curl_json plugin: curl_easy_perform failed with "
+ "response code %ld (%s)", rc, url);
+ yajl_free (db->yajl);
+ db->yajl = yprev;
return (-1);
}
- if (status != 0)
+ status = yajl_parse_complete (db->yajl);
+ if (status != yajl_status_ok)
{
- ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
- status, db->curl_errbuf, url);
+ unsigned char *errmsg;
+
+ errmsg = yajl_get_error (db->yajl, /* verbose = */ 0,
+ /* jsonText = */ NULL, /* jsonTextLen = */ 0);
+ ERROR ("curl_json plugin: yajl_parse_complete failed: %s",
+ (char *) errmsg);
+ yajl_free_error (db->yajl, errmsg);
+ yajl_free (db->yajl);
+ db->yajl = yprev;
return (-1);
}
+ yajl_free (db->yajl);
+ db->yajl = yprev;
return (0);
} /* }}} int cj_curl_perform */
--- /dev/null
+/**
+ * collectd - src/curl_xml.c
+ * Copyright (C) 2009,2010 Amit Gupta
+ *
+ * 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:
+ * Amit Gupta <amit.gupta221 at gmail.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_llist.h"
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+#include <curl/curl.h>
+
+#define CX_DEFAULT_HOST "localhost"
+
+/*
+ * Private data structures
+ */
+struct cx_values_s /* {{{ */
+{
+ char path[DATA_MAX_NAME_LEN];
+ size_t path_len;
+};
+typedef struct cx_values_s cx_values_t;
+/* }}} */
+
+struct cx_xpath_s /* {{{ */
+{
+ char *path;
+ char *type;
+ cx_values_t *values;
+ int values_len;
+ char *instance_prefix;
+ char *instance;
+ int is_table;
+ unsigned long magic;
+};
+typedef struct cx_xpath_s cx_xpath_t;
+/* }}} */
+
+struct cx_s /* {{{ */
+{
+ char *instance;
+ char *host;
+
+ char *url;
+ char *user;
+ char *pass;
+ char *credentials;
+ _Bool verify_peer;
+ _Bool verify_host;
+ char *cacert;
+
+ CURL *curl;
+ char curl_errbuf[CURL_ERROR_SIZE];
+ char *buffer;
+ size_t buffer_size;
+ size_t buffer_fill;
+
+ llist_t *list; /* list of xpath blocks */
+};
+typedef struct cx_s cx_t; /* }}} */
+
+/*
+ * Private functions
+ */
+static size_t cx_curl_callback (void *buf, /* {{{ */
+ size_t size, size_t nmemb, void *user_data)
+{
+ size_t len = size * nmemb;
+ cx_t *db;
+
+ db = user_data;
+ if (db == NULL)
+ {
+ ERROR ("curl_xml plugin: cx_curl_callback: "
+ "user_data pointer is NULL.");
+ return (0);
+ }
+
+ if (len <= 0)
+ return (len);
+
+ if ((db->buffer_fill + len) >= db->buffer_size)
+ {
+ char *temp;
+
+ temp = (char *) realloc (db->buffer,
+ db->buffer_fill + len + 1);
+ if (temp == NULL)
+ {
+ ERROR ("curl_xml plugin: realloc failed.");
+ return (0);
+ }
+ db->buffer = temp;
+ db->buffer_size = db->buffer_fill + len + 1;
+ }
+
+ memcpy (db->buffer + db->buffer_fill, (char *) buf, len);
+ db->buffer_fill += len;
+ db->buffer[db->buffer_fill] = 0;
+
+ return (len);
+} /* }}} size_t cx_curl_callback */
+
+static void cx_xpath_free (cx_xpath_t *xpath) /* {{{ */
+{
+ if (xpath == NULL)
+ return;
+
+ sfree (xpath->path);
+ sfree (xpath->type);
+ sfree (xpath->instance_prefix);
+ sfree (xpath->instance);
+ sfree (xpath->values);
+ sfree (xpath);
+} /* }}} void cx_xpath_free */
+
+static void cx_list_free (llist_t *list) /* {{{ */
+{
+ llentry_t *le;
+
+ le = llist_head (list);
+ while (le != NULL)
+ {
+ llentry_t *le_next;
+
+ le_next = le->next;
+
+ sfree (le->key);
+ cx_xpath_free (le->value);
+
+ le = le_next;
+ }
+
+ llist_destroy (list);
+ list = NULL;
+} /* }}} void cx_list_free */
+
+static void cx_free (void *arg) /* {{{ */
+{
+ cx_t *db;
+
+ DEBUG ("curl_xml plugin: cx_free (arg = %p);", arg);
+
+ db = (cx_t *) arg;
+
+ if (db == NULL)
+ return;
+
+ if (db->curl != NULL)
+ curl_easy_cleanup (db->curl);
+ db->curl = NULL;
+
+ if (db->list != NULL)
+ cx_list_free (db->list);
+
+ sfree (db->buffer);
+ sfree (db->instance);
+ sfree (db->host);
+
+ sfree (db->url);
+ sfree (db->user);
+ sfree (db->pass);
+ sfree (db->credentials);
+ sfree (db->cacert);
+
+ sfree (db);
+} /* }}} void cx_free */
+
+static int cx_check_type (const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */
+{
+ if (!ds)
+ {
+ WARNING ("curl_xml plugin: DataSet `%s' not defined.", xpath->type);
+ return (-1);
+ }
+
+ if (ds->ds_num != xpath->values_len)
+ {
+ WARNING ("curl_xml plugin: DataSet `%s' requires %i values, but config talks about %i",
+ xpath->type, ds->ds_num, xpath->values_len);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} cx_check_type */
+
+static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ xmlChar *expr)
+{
+ xmlXPathObjectPtr xpath_obj;
+
+ /* XXX: When to free this? */
+ xpath_obj = xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
+ if (xpath_obj == NULL)
+ {
+ WARNING ("curl_xml plugin: "
+ "Error unable to evaluate xpath expression \"%s\". Skipping...", expr);
+ return NULL;
+ }
+
+ return xpath_obj;
+} /* }}} cx_evaluate_xpath */
+
+static int cx_if_not_text_node (xmlNodePtr node) /* {{{ */
+{
+ if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE)
+ return (0);
+
+ WARNING ("curl_xml plugin: "
+ "Node \"%s\" doesn't seem to be a text node. Skipping...", node->name);
+ return -1;
+} /* }}} cx_if_not_text_node */
+
+static int cx_handle_single_value_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath,
+ const data_set_t *ds, value_list_t *vl, int index)
+{
+ xmlXPathObjectPtr values_node_obj;
+ xmlNodeSetPtr values_node;
+ int tmp_size;
+ char *node_value;
+
+ values_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->values[index].path);
+ if (values_node_obj == NULL)
+ return (-1); /* Error already logged. */
+
+ values_node = values_node_obj->nodesetval;
+ tmp_size = (values_node) ? values_node->nodeNr : 0;
+
+ if (tmp_size == 0)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" doesn't match any of the nodes. "
+ "Skipping...", xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ if (tmp_size > 1)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return "
+ "only one node. Skipping...", xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ /* ignoring the element if other than textnode/attribute*/
+ if (cx_if_not_text_node(values_node->nodeTab[0]))
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return "
+ "only text/attribute node which is not the case. Skipping...",
+ xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ node_value = (char *) xmlNodeGetContent(values_node->nodeTab[0]);
+ switch (ds->ds[index].type)
+ {
+ case DS_TYPE_COUNTER:
+ vl->values[index].counter = (counter_t) strtoull (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_DERIVE:
+ vl->values[index].derive = (derive_t) strtoll (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_ABSOLUTE:
+ vl->values[index].absolute = (absolute_t) strtoull (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_GAUGE:
+ vl->values[index].gauge = (gauge_t) strtod (node_value,
+ /* endptr = */ NULL);
+ }
+
+ /* free up object */
+ xmlXPathFreeObject (values_node_obj);
+
+ /* We have reached here which means that
+ * we have got something to work */
+ return (0);
+} /* }}} int cx_handle_single_value_xpath */
+
+static int cx_handle_all_value_xpaths (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath,
+ const data_set_t *ds, value_list_t *vl)
+{
+ value_t values[xpath->values_len];
+ int status;
+ int i;
+
+ assert (xpath->values_len > 0);
+ assert (xpath->values_len == vl->values_len);
+ assert (xpath->values_len == ds->ds_num);
+ vl->values = values;
+
+ for (i = 0; i < xpath->values_len; i++)
+ {
+ status = cx_handle_single_value_xpath (xpath_ctx, xpath, ds, vl, i);
+ if (status != 0)
+ return (-1); /* An error has been printed. */
+ } /* for (i = 0; i < xpath->values_len; i++) */
+
+ plugin_dispatch_values (vl);
+ vl->values = NULL;
+
+ return (0);
+} /* }}} int cx_handle_all_value_xpaths */
+
+static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath, value_list_t *vl,
+ _Bool is_table)
+{
+ xmlXPathObjectPtr instance_node_obj = NULL;
+ xmlNodeSetPtr instance_node = NULL;
+
+ memset (vl->type_instance, 0, sizeof (vl->type_instance));
+
+ /* If the base xpath returns more than one block, the result is assumed to be
+ * a table. The `Instnce' option is not optional in this case. Check for the
+ * condition and inform the user. */
+ if (is_table && (vl->type_instance == NULL))
+ {
+ WARNING ("curl_xml plugin: "
+ "Base-XPath %s is a table (more than one result was returned), "
+ "but no instance-XPath has been defined.",
+ xpath->path);
+ return (-1);
+ }
+
+ /* instance has to be an xpath expression */
+ if (xpath->instance != NULL)
+ {
+ int tmp_size;
+
+ instance_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->instance);
+ if (instance_node_obj == NULL)
+ return (-1); /* error is logged already */
+
+ instance_node = instance_node_obj->nodesetval;
+ tmp_size = (instance_node) ? instance_node->nodeNr : 0;
+
+ if ( (tmp_size == 0) && (is_table) )
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression for 'InstanceFrom' \"%s\" doesn't match "
+ "any of the nodes. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+
+ if (tmp_size > 1)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression for 'InstanceFrom' \"%s\" is expected "
+ "to return only one text node. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+
+ /* ignoring the element if other than textnode/attribute */
+ if (cx_if_not_text_node(instance_node->nodeTab[0]))
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return only text node "
+ "which is not the case. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+ } /* if (xpath->instance != NULL) */
+
+ if (xpath->instance_prefix != NULL)
+ {
+ if (instance_node != NULL)
+ ssnprintf (vl->type_instance, sizeof (vl->type_instance),"%s%s",
+ xpath->instance_prefix, (char *) xmlNodeGetContent(instance_node->nodeTab[0]));
+ else
+ sstrncpy (vl->type_instance, xpath->instance_prefix,
+ sizeof (vl->type_instance));
+ }
+ else
+ {
+ /* If instance_prefix and instance_node are NULL, then
+ * don't set the type_instance */
+ if (instance_node != NULL)
+ sstrncpy (vl->type_instance, (char *) xmlNodeGetContent(instance_node->nodeTab[0]),
+ sizeof (vl->type_instance));
+ }
+
+ /* Free `instance_node_obj' this late, because `instance_node' points to
+ * somewhere inside this structure. */
+ xmlXPathFreeObject (instance_node_obj);
+
+ return (0);
+} /* }}} int cx_handle_instance_xpath */
+
+static int cx_handle_base_xpath (char *plugin_instance, /* {{{ */
+ xmlXPathContextPtr xpath_ctx, const data_set_t *ds,
+ char *base_xpath, cx_xpath_t *xpath)
+{
+ int total_nodes;
+ int i;
+
+ xmlXPathObjectPtr base_node_obj = NULL;
+ xmlNodeSetPtr base_nodes = NULL;
+
+ value_list_t vl = VALUE_LIST_INIT;
+
+ base_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST base_xpath);
+ if (base_node_obj == NULL)
+ return -1; /* error is logged already */
+
+ base_nodes = base_node_obj->nodesetval;
+ total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
+
+ if (total_nodes == 0)
+ {
+ ERROR ("curl_xml plugin: "
+ "xpath expression \"%s\" doesn't match any of the nodes. "
+ "Skipping the xpath block...", base_xpath);
+ xmlXPathFreeObject (base_node_obj);
+ return -1;
+ }
+
+ /* If base_xpath returned multiple results, then */
+ /* Instance in the xpath block is required */
+ if (total_nodes > 1 && xpath->instance == NULL)
+ {
+ ERROR ("curl_xml plugin: "
+ "InstanceFrom is must in xpath block since the base xpath expression \"%s\" "
+ "returned multiple results. Skipping the xpath block...", base_xpath);
+ return -1;
+ }
+
+ /* set the values for the value_list */
+ vl.values_len = ds->ds_num;
+ sstrncpy (vl.type, xpath->type, sizeof (vl.type));
+ sstrncpy (vl.plugin, "curl_xml", sizeof (vl.plugin));
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+
+ for (i = 0; i < total_nodes; i++)
+ {
+ int status;
+
+ xpath_ctx->node = base_nodes->nodeTab[i];
+
+ status = cx_handle_instance_xpath (xpath_ctx, xpath, &vl,
+ /* is_table = */ (total_nodes > 1));
+ if (status != 0)
+ continue; /* An error has already been reported. */
+
+ status = cx_handle_all_value_xpaths (xpath_ctx, xpath, ds, &vl);
+ if (status != 0)
+ continue; /* An error has been logged. */
+ } /* for (i = 0; i < total_nodes; i++) */
+
+ /* free up the allocated memory */
+ xmlXPathFreeObject (base_node_obj);
+
+ return (0);
+} /* }}} cx_handle_base_xpath */
+
+static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
+ xmlXPathContextPtr xpath_ctx, cx_t *db)
+{
+ llentry_t *le;
+ const data_set_t *ds;
+ cx_xpath_t *xpath;
+ int status=-1;
+
+
+ le = llist_head (db->list);
+ while (le != NULL)
+ {
+ /* get the ds */
+ xpath = (cx_xpath_t *) le->value;
+ ds = plugin_get_ds (xpath->type);
+
+ if ( (cx_check_type(ds, xpath) == 0) &&
+ (cx_handle_base_xpath(db->instance, xpath_ctx, ds, le->key, xpath) == 0) )
+ status = 0; /* we got atleast one success */
+
+ le = le->next;
+ } /* while (le != NULL) */
+
+ return status;
+} /* }}} cx_handle_parsed_xml */
+
+static int cx_parse_stats_xml(xmlChar* xml, cx_t *db) /* {{{ */
+{
+ int status;
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpath_ctx;
+
+ /* Load the XML */
+ doc = xmlParseDoc(xml);
+ if (doc == NULL)
+ {
+ ERROR ("curl_xml plugin: Failed to parse the xml document - %s", xml);
+ return (-1);
+ }
+
+ xpath_ctx = xmlXPathNewContext(doc);
+ if(xpath_ctx == NULL)
+ {
+ ERROR ("curl_xml plugin: Failed to create the xml context");
+ xmlFreeDoc(doc);
+ return (-1);
+ }
+
+ status = cx_handle_parsed_xml (doc, xpath_ctx, db);
+ /* Cleanup */
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc(doc);
+ return status;
+} /* }}} cx_parse_stats_xml */
+
+static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */
+{
+ int status;
+ long rc;
+ char *ptr;
+ char *url;
+
+ db->buffer_fill = 0;
+ status = curl_easy_perform (curl);
+
+ curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+
+ if (rc != 200)
+ {
+ ERROR ("curl_xml plugin: curl_easy_perform failed with response code %ld (%s)",
+ rc, url);
+ return (-1);
+ }
+
+ if (status != 0)
+ {
+ ERROR ("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
+ status, db->curl_errbuf, url);
+ return (-1);
+ }
+
+ ptr = db->buffer;
+
+ status = cx_parse_stats_xml(BAD_CAST ptr, db);
+ db->buffer_fill = 0;
+
+ return status;
+} /* }}} int cx_curl_perform */
+
+static int cx_read (user_data_t *ud) /* {{{ */
+{
+ cx_t *db;
+
+ if ((ud == NULL) || (ud->data == NULL))
+ {
+ ERROR ("curl_xml plugin: cx_read: Invalid user data.");
+ return (-1);
+ }
+
+ db = (cx_t *) ud->data;
+
+ return cx_curl_perform (db, db->curl);
+} /* }}} int cx_read */
+
+/* Configuration handling functions {{{ */
+
+static int cx_config_add_values (const char *name, cx_xpath_t *xpath, /* {{{ */
+ oconfig_item_t *ci)
+{
+ int i;
+
+ if (ci->values_num < 1)
+ {
+ WARNING ("curl_xml plugin: `ValuesFrom' needs at least one argument.");
+ return (-1);
+ }
+
+ for (i = 0; i < ci->values_num; i++)
+ if (ci->values[i].type != OCONFIG_TYPE_STRING)
+ {
+ WARNING ("curl_xml plugin: `ValuesFrom' needs only string argument.");
+ return (-1);
+ }
+
+ sfree (xpath->values);
+
+ xpath->values_len = 0;
+ xpath->values = (cx_values_t *) malloc (sizeof (cx_values_t) * ci->values_num);
+ if (xpath->values == NULL)
+ return (-1);
+ xpath->values_len = ci->values_num;
+
+ /* populate cx_values_t structure */
+ for (i = 0; i < ci->values_num; i++)
+ {
+ xpath->values[i].path_len = sizeof (ci->values[i].value.string);
+ sstrncpy (xpath->values[i].path, ci->values[i].value.string, sizeof (xpath->values[i].path));
+ }
+
+ return (0);
+} /* }}} cx_config_add_values */
+
+static int cx_config_add_xpath (cx_t *db, /* {{{ */
+ oconfig_item_t *ci)
+{
+ cx_xpath_t *xpath;
+ int status;
+ int i;
+
+ xpath = (cx_xpath_t *) malloc (sizeof (*xpath));
+ if (xpath == NULL)
+ {
+ ERROR ("curl_xml plugin: malloc failed.");
+ return (-1);
+ }
+ memset (xpath, 0, sizeof (*xpath));
+
+ status = cf_util_get_string (ci, &xpath->path);
+ if (status != 0)
+ {
+ sfree (xpath);
+ return (status);
+ }
+
+ /* error out if xpath->path is an empty string */
+ if (*xpath->path == 0)
+ {
+ ERROR ("curl_xml plugin: invalid xpath. "
+ "xpath value can't be an empty string");
+ return (-1);
+ }
+
+ status = 0;
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Type", child->key) == 0)
+ status = cf_util_get_string (child, &xpath->type);
+ else if (strcasecmp ("InstancePrefix", child->key) == 0)
+ status = cf_util_get_string (child, &xpath->instance_prefix);
+ else if (strcasecmp ("InstanceFrom", child->key) == 0)
+ status = cf_util_get_string (child, &xpath->instance);
+ else if (strcasecmp ("ValuesFrom", child->key) == 0)
+ status = cx_config_add_values ("ValuesFrom", xpath, child);
+ else
+ {
+ WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ if (status == 0 && xpath->type == NULL)
+ {
+ WARNING ("curl_xml plugin: `Type' missing in `xpath' block.");
+ status = -1;
+ }
+
+ if (status == 0)
+ {
+ char *name;
+ llentry_t *le;
+
+ if (db->list == NULL)
+ {
+ db->list = llist_create();
+ if (db->list == NULL)
+ {
+ ERROR ("curl_xml plugin: list creation failed.");
+ return (-1);
+ }
+ }
+
+ name = strdup(xpath->path);
+ if (name == NULL)
+ {
+ ERROR ("curl_xml plugin: strdup failed.");
+ return (-1);
+ }
+
+ le = llentry_create (name, xpath);
+ if (le == NULL)
+ {
+ ERROR ("curl_xml plugin: llentry_create failed.");
+ return (-1);
+ }
+
+ llist_append (db->list, le);
+ }
+
+ return (status);
+} /* }}} int cx_config_add_xpath */
+
+/* Initialize db->curl */
+static int cx_init_curl (cx_t *db) /* {{{ */
+{
+ db->curl = curl_easy_init ();
+ if (db->curl == NULL)
+ {
+ ERROR ("curl_xml plugin: curl_easy_init failed.");
+ return (-1);
+ }
+
+ curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cx_curl_callback);
+ curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
+ curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
+ PACKAGE_NAME"/"PACKAGE_VERSION);
+ curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
+ curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
+
+ if (db->user != NULL)
+ {
+ size_t credentials_size;
+
+ credentials_size = strlen (db->user) + 2;
+ if (db->pass != NULL)
+ credentials_size += strlen (db->pass);
+
+ db->credentials = (char *) malloc (credentials_size);
+ if (db->credentials == NULL)
+ {
+ ERROR ("curl_xml plugin: malloc failed.");
+ return (-1);
+ }
+
+ ssnprintf (db->credentials, credentials_size, "%s:%s",
+ db->user, (db->pass == NULL) ? "" : db->pass);
+ curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
+ }
+
+ curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer ? 1L : 0L);
+ curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
+ db->verify_host ? 2L : 0L);
+ if (db->cacert != NULL)
+ curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
+
+ return (0);
+} /* }}} int cx_init_curl */
+
+static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
+{
+ cx_t *db;
+ int status = 0;
+ int i;
+
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("curl_xml plugin: The `URL' block "
+ "needs exactly one string argument.");
+ return (-1);
+ }
+
+ db = (cx_t *) malloc (sizeof (*db));
+ if (db == NULL)
+ {
+ ERROR ("curl_xml plugin: malloc failed.");
+ return (-1);
+ }
+ memset (db, 0, sizeof (*db));
+
+ if (strcasecmp ("URL", ci->key) == 0)
+ {
+ status = cf_util_get_string (ci, &db->url);
+ if (status != 0)
+ {
+ sfree (db);
+ return (status);
+ }
+ }
+ else
+ {
+ ERROR ("curl_xml plugin: cx_config: "
+ "Invalid key: %s", ci->key);
+ return (-1);
+ }
+
+ /* Fill the `cx_t' structure.. */
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Instance", child->key) == 0)
+ status = cf_util_get_string (child, &db->instance);
+ else if (strcasecmp ("Host", child->key) == 0)
+ status = cf_util_get_string (child, &db->host);
+ else if (strcasecmp ("User", child->key) == 0)
+ status = cf_util_get_string (child, &db->user);
+ else if (strcasecmp ("Password", child->key) == 0)
+ status = cf_util_get_string (child, &db->pass);
+ else if (strcasecmp ("VerifyPeer", child->key) == 0)
+ status = cf_util_get_boolean (child, &db->verify_peer);
+ else if (strcasecmp ("VerifyHost", child->key) == 0)
+ status = cf_util_get_boolean (child, &db->verify_host);
+ else if (strcasecmp ("CACert", child->key) == 0)
+ status = cf_util_get_string (child, &db->cacert);
+ else if (strcasecmp ("xpath", child->key) == 0)
+ status = cx_config_add_xpath (db, child);
+ else
+ {
+ WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if (status == 0)
+ {
+ if (db->list == NULL)
+ {
+ WARNING ("curl_xml plugin: No (valid) `Key' block "
+ "within `URL' block `%s'.", db->url);
+ status = -1;
+ }
+ if (status == 0)
+ status = cx_init_curl (db);
+ }
+
+ /* If all went well, register this database for reading */
+ if (status == 0)
+ {
+ user_data_t ud;
+ char cb_name[DATA_MAX_NAME_LEN];
+
+ if (db->instance == NULL)
+ db->instance = strdup("default");
+
+ DEBUG ("curl_xml plugin: Registering new read callback: %s",
+ db->instance);
+
+ memset (&ud, 0, sizeof (ud));
+ ud.data = (void *) db;
+ ud.free_func = cx_free;
+
+ ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s",
+ db->instance, db->url);
+
+ plugin_register_complex_read (/* group = */ NULL, cb_name, cx_read,
+ /* interval = */ NULL, &ud);
+ }
+ else
+ {
+ cx_free (db);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int cx_config_add_url */
+
+/* }}} End of configuration handling functions */
+
+static int cx_config (oconfig_item_t *ci) /* {{{ */
+{
+ int success;
+ int errors;
+ int status;
+ int i;
+
+ success = 0;
+ errors = 0;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("URL", child->key) == 0)
+ {
+ status = cx_config_add_url (child);
+ if (status == 0)
+ success++;
+ else
+ errors++;
+ }
+ else
+ {
+ WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+ errors++;
+ }
+ }
+
+ if ((success == 0) && (errors > 0))
+ {
+ ERROR ("curl_xml plugin: All statements failed.");
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int cx_config */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("curl_xml", cx_config);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
cdbi_driver_option_t *driver_options;
size_t driver_options_num;
+ udb_query_preparation_area_t **q_prep_areas;
udb_query_t **queries;
size_t queries_num;
}
sfree (db->driver_options);
+ if (db->q_prep_areas)
+ for (i = 0; i < db->queries_num; ++i)
+ udb_query_delete_preparation_area (db->q_prep_areas[i]);
+ free (db->q_prep_areas);
+
sfree (db);
} /* }}} void cdbi_database_free */
break;
} /* while (status == 0) */
+ while ((status == 0) && (db->queries_num > 0))
+ {
+ db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+ db->queries_num, sizeof (*db->q_prep_areas));
+
+ if (db->q_prep_areas == NULL)
+ {
+ WARNING ("dbi plugin: malloc failed");
+ status = -1;
+ break;
+ }
+
+ for (i = 0; i < db->queries_num; ++i)
+ {
+ db->q_prep_areas[i]
+ = udb_query_allocate_preparation_area (db->queries[i]);
+
+ if (db->q_prep_areas[i] == NULL)
+ {
+ WARNING ("dbi plugin: udb_query_allocate_preparation_area failed");
+ status = -1;
+ break;
+ }
+ }
+
+ break;
+ }
+
/* If all went well, add this database to the global list of databases. */
if (status == 0)
{
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
} /* }}} int cdbi_init */
static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
- udb_query_t *q)
+ udb_query_t *q, udb_query_preparation_area_t *prep_area)
{
const char *statement;
dbi_result res;
sstrncpy (column_names[i], column_name, DATA_MAX_NAME_LEN);
} /* }}} for (i = 0; i < column_num; i++) */
- udb_query_prepare_result (q, hostname_g, /* plugin = */ "dbi", db->name,
- column_names, column_num);
+ udb_query_prepare_result (q, prep_area, hostname_g,
+ /* plugin = */ "dbi", db->name,
+ column_names, column_num, /* interval = */ 0);
/* 0 = error; 1 = success; */
status = dbi_result_first_row (res); /* {{{ */
"return any rows?",
db->name, udb_query_get_name (q),
cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
BAIL_OUT (-1);
} /* }}} */
* to dispatch the row to the daemon. */
if (status == 0) /* {{{ */
{
- status = udb_query_handle_result (q, column_values);
+ status = udb_query_handle_result (q, prep_area, column_values);
if (status != 0)
{
ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
} /* }}} while (42) */
/* Tell the db query interface that we're done with this query. */
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
/* Clean up and return `status = 0' (success) */
BAIL_OUT (0);
&& (udb_query_check_version (db->queries[i], db_version) == 0))
continue;
- status = cdbi_read_database_query (db, db->queries[i]);
+ status = cdbi_read_database_query (db,
+ db->queries[i], db->q_prep_areas[i]);
if (status == 0)
success++;
}
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)
{
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);
}
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,
mnt_list = NULL;
if (cu_mount_getlist (&mnt_list) == NULL)
+ {
+ ERROR ("df plugin: cu_mount_getlist failed.");
return (-1);
+ }
for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next)
{
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)
blocksize = BLOCKSIZE(statbuf);
- if (report_reserved)
- {
- uint64_t blk_free;
- uint64_t blk_reserved;
- uint64_t blk_used;
-
- /* Sanity-check for the values in the struct */
- if (statbuf.f_bfree < statbuf.f_bavail)
- statbuf.f_bfree = statbuf.f_bavail;
- if (statbuf.f_blocks < statbuf.f_bfree)
- statbuf.f_blocks = statbuf.f_bfree;
-
- blk_free = (uint64_t) statbuf.f_bavail;
- blk_reserved = (uint64_t) (statbuf.f_bfree - statbuf.f_bavail);
- blk_used = (uint64_t) (statbuf.f_blocks - statbuf.f_bfree);
-
- df_submit_one (disk_name, "df_complex", "free",
- (gauge_t) (blk_free * blocksize));
- df_submit_one (disk_name, "df_complex", "reserved",
- (gauge_t) (blk_reserved * blocksize));
- df_submit_one (disk_name, "df_complex", "used",
- (gauge_t) (blk_used * blocksize));
- }
- else /* compatibility code */
- {
- gauge_t df_free;
- gauge_t df_used;
-
- df_free = statbuf.f_bfree * blocksize;
- df_used = (statbuf.f_blocks - statbuf.f_bfree) * blocksize;
-
- df_submit_two (disk_name, "df", df_used, df_free);
- }
+ /* Sanity-check for the values in the struct */
+ if (statbuf.f_bfree < statbuf.f_bavail)
+ statbuf.f_bfree = statbuf.f_bavail;
+ if (statbuf.f_blocks < statbuf.f_bfree)
+ statbuf.f_blocks = statbuf.f_bfree;
+
+ blk_free = (uint64_t) statbuf.f_bavail;
+ blk_reserved = (uint64_t) (statbuf.f_bfree - statbuf.f_bavail);
+ blk_used = (uint64_t) (statbuf.f_blocks - statbuf.f_bfree);
+
+ df_submit_one (disk_name, "df_complex", "free",
+ (gauge_t) (blk_free * blocksize));
+ df_submit_one (disk_name, "df_complex", "reserved",
+ (gauge_t) (blk_reserved * blocksize));
+ df_submit_one (disk_name, "df_complex", "used",
+ (gauge_t) (blk_used * blocksize));
/* inode handling */
if (report_inodes)
/**
* 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
/* 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;
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;
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;
int major = 0;
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;
}
{
- 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)
/* 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);
/* #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;
/**
* collectd - src/dns.c
- * Copyright (C) 2006,2007 Florian octo Forster
+ * Copyright (C) 2006-2011 Florian octo Forster
* Copyright (C) 2009 Mirko Buffoni
*
* This program is free software; you can redistribute it and/or modify it
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Mirko Buffoni <briareos at eswat.org>
**/
#include "utils_dns.h"
#include <pthread.h>
-#include <pcap.h>
#include <poll.h>
+#include <pcap.h>
+#include <pcap-bpf.h>
+
/*
* Private data types
*/
#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;
{
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);
}
{
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);
last->next = entry;
}
- DEBUG ("return (%p)", (void *) entry);
return (entry);
}
{
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)
{
counter_list_create (list, key, increment);
}
- DEBUG ("return ()");
}
static int dns_config (const char *key, const char *value)
pthread_mutex_unlock (&opcode_mutex);
}
-static void *dns_child_loop (void __attribute__((unused)) *dummy)
+static void *dns_child_loop (__attribute__((unused)) void *dummy)
{
pcap_t *pcap_obj;
char pcap_error[PCAP_ERRBUF_SIZE];
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)
{
return (NULL);
}
- DEBUG ("PCAP object created.");
+ DEBUG ("dns plugin: PCAP object created.");
dnstop_set_pcap_obj (pcap_obj);
dnstop_set_callback (dns_child_callback);
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;
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;
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;
sstrncpy (vl.type, "dns_octets", sizeof (vl.type));
plugin_dispatch_values (&vl);
-} /* void submit_counter */
+} /* void submit_octets */
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);
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);
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);
{
char buffer[1024];
- ssnprintf (buffer, sizeof (buffer), "%i", interval_g);
+#ifdef HAVE_SETENV
+ 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=%.3f",
+ CDTIME_T_TO_DOUBLE (interval_g));
+ putenv (buffer);
+
+ ssnprintf (buffer, sizeof (buffer), "COLLECTD_HOSTNAME=%s", hostname_g);
+ putenv (buffer);
+#endif
+
+#ifdef HAVE_SETENV
+#else
+#endif
} /* }}} void set_environment */
__attribute__((noreturn))
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 }}} */
#include <fnmatch.h>
#define FC_RECURSIVE 1
+#define FC_HIDDEN 2
struct fc_directory_conf_s
{
return (0);
} /* int fc_config_add_dir_size */
-static int fc_config_add_dir_recursive (fc_directory_conf_t *dir,
- oconfig_item_t *ci)
+static int fc_config_add_dir_option (fc_directory_conf_t *dir,
+ oconfig_item_t *ci, int bit)
{
if ((ci->values_num != 1)
|| (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
}
if (ci->values[0].value.boolean)
- dir->options |= FC_RECURSIVE;
+ dir->options |= bit;
else
- dir->options &= ~FC_RECURSIVE;
+ dir->options &= ~bit;
return (0);
-} /* int fc_config_add_dir_recursive */
+} /* int fc_config_add_dir_option */
static int fc_config_add_dir (oconfig_item_t *ci)
{
else if (strcasecmp ("Size", option->key) == 0)
status = fc_config_add_dir_size (dir, option);
else if (strcasecmp ("Recursive", option->key) == 0)
- status = fc_config_add_dir_recursive (dir, option);
+ status = fc_config_add_dir_option (dir, option, FC_RECURSIVE);
+ else if (strcasecmp ("IncludeHidden", option->key) == 0)
+ status = fc_config_add_dir_option (dir, option, FC_HIDDEN);
else
{
WARNING ("filecount plugin: fc_config_add_dir: "
if (S_ISDIR (statbuf.st_mode) && (dir->options & FC_RECURSIVE))
{
- status = walk_directory (abs_path, fc_read_dir_callback, dir);
+ status = walk_directory (abs_path, fc_read_dir_callback, dir,
+ /* include hidden = */ (dir->options & FC_HIDDEN) ? 1 : 0);
return (status);
}
else if (!S_ISREG (statbuf.st_mode))
if (dir->mtime != 0)
dir->now = time (NULL);
-
- status = walk_directory (dir->path, fc_read_dir_callback, dir);
+
+ status = walk_directory (dir->path, fc_read_dir_callback, dir,
+ /* include hidden */ (dir->options & FC_HIDDEN) ? 1 : 0);
if (status != 0)
{
WARNING ("filecount plugin: walk_directory (%s) failed.", dir->path);
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));
{
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);
*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);
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
**/
#include "collectd.h"
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);
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;
}
{
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,
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)
/**
* 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
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
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);
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 <major>-<minor> 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];
for (i = 0; i < num_disks; i++)
{
- char *name, *major_minor;
+ char *name;
double temperature;
char *mode;
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);
{
plugin_register_config ("hddtemp", hddtemp_config,
config_keys, config_keys_num);
- plugin_register_init ("hddtemp", hddtemp_init);
plugin_register_read ("hddtemp", hddtemp_read);
}
/**
* 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
static int interface_init (void)
{
kstat_t *ksp_chain;
- unsigned long long val;
+ derive_t val;
numif = 0;
#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;
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 */
#elif KERNEL_LINUX
FILE *fh;
char buffer[1024];
- unsigned long long incoming, outgoing;
+ derive_t incoming, outgoing;
char *device;
char *dummy;
#elif HAVE_LIBKSTAT
int i;
- unsigned long long rx;
- unsigned long long tx;
+ derive_t rx;
+ derive_t tx;
if (kc == NULL)
return (-1);
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));
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));
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));
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));
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;
/**
* 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
*
* Authors:
* Sjoerd van der Berg <harekiet at users.sourceforge.net>
- * Florian Forster <octo at verplant.org>
+ * Florian Forster <octo at collectd.org>
* Marco Chiappero <marco at absence.it>
**/
}
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);
}
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);
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));
} /* 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));
return (1 - irq_list_action);
}
-static void irq_submit (unsigned int irq, counter_t value)
+static void irq_submit (unsigned int irq, derive_t value)
{
value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
if (check_ignore_irq (irq))
return;
- values[0].counter = value;
+ values[0].derive = value;
vl.values = values;
vl.values_len = 1;
static int irq_read (void)
{
-#undef BUFSIZE
-#define BUFSIZE 256
-
FILE *fh;
- char buffer[BUFSIZE];
- unsigned int irq;
- unsigned long long irq_value;
- unsigned long long value;
- char *endptr;
- int i;
+ char buffer[1024];
- 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, BUFSIZE, fh) != NULL)
{
+ unsigned int irq;
+ derive_t irq_value;
+ char *endptr;
+ int i;
+
+ char *fields[64];
+ int fields_num;
+
fields_num = strsplit (buffer, fields, 64);
if (fields_num < 2)
continue;
errno = 0; /* To distinguish success/failure after call */
- irq = strtol (fields[0], &endptr, 10);
+ irq = (unsigned int) strtoul (fields[0], &endptr, /* base = */ 10);
if ((endptr == fields[0]) || (errno != 0) || (*endptr != ':'))
continue;
irq_value = 0;
for (i = 1; i < fields_num; i++)
{
- errno = 0;
- value = strtoull (fields[i], &endptr, 10);
+ /* Per-CPU value */
+ value_t v;
+ int status;
- if ((*endptr != '\0') || (errno != 0))
+ status = parse_value (fields[i], &v, DS_TYPE_DERIVE);
+ if (status != 0)
break;
- irq_value += value;
+ irq_value += v.derive;
} /* for (i) */
- /* Force 32bit wrap-around */
- irq_submit (irq, irq_value % 4294967296ULL);
+ if (i < fields_num)
+ continue;
+
+ irq_submit (irq, irq_value);
}
fclose (fh);
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);
#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)
{
}
/* 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)
{
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)
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");
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)
ud.data = (void *) cbi;
ud.free_func = cjni_callback_info_destroy;
- plugin_register_complex_read (cbi->name, cjni_read,
+ plugin_register_complex_read (/* group = */ NULL, cbi->name, cjni_read,
/* interval = */ NULL, &ud);
(*jvm_env)->DeleteLocalRef (jvm_env, o_read);
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:
api_class_ptr = (*jvm_env)->FindClass (jvm_env, "org/collectd/api/Collectd");
if (api_class_ptr == NULL)
{
- ERROR ("cjni_init_native: Cannot find API class `org/collectd/api/Collectd'.");
+ ERROR ("cjni_init_native: Cannot find the API class \"org.collectd.api"
+ ".Collectd\". Please set the correct class path "
+ "using 'JVMArg \"-Djava.class.path=...\"'.");
return (-1);
}
} /* }}} 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;
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)
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;
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);
if (values_names != NULL)
{
- values_names[i] = lcc_strdup (key);
+ values_names[i] = strdup (key);
if (values_names[i] == NULL)
BAIL_OUT (ENOMEM);
}
SSTRCATF (command, " interval=%i", vl->interval);
if (vl->time > 0)
- SSTRCATF (command, "%u", (unsigned int) vl->time);
+ SSTRCATF (command, " %u", (unsigned int) vl->time);
else
- SSTRCAT (command, "N");
+ SSTRCAT (command, " N");
for (i = 0; i < vl->values_len; i++)
{
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);
}
char *type;
char *type_instance;
- string_copy = lcc_strdup (string);
+ string_copy = strdup (string);
if (string_copy == NULL)
{
lcc_set_errno (c, ENOMEM);
/*
* Includes (for data types)
*/
-#include <stdint.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
#include <inttypes.h>
#include <time.h>
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;
"IgnoreSelected",
"HostnameFormat",
+ "InterfaceFormat",
NULL
};
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
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; \
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];
+ char *host_ptr;
+ size_t host_len;
+
+ vl->interval = interval_g;
+
+ sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
+
+ vl->host[0] = '\0';
+ host_ptr = vl->host;
+ host_len = sizeof (vl->host);
+
+ /* 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)
{
n = strsplit (value_copy, fields, HF_MAX_FIELDS);
if (n < 1) {
- free (value_copy);
+ sfree (value_copy);
ERROR ("HostnameFormat: no fields");
return -1;
}
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;
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;
}
if (virDomainGetInfo (domains[i], &info) != 0)
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) {
if (virDomainGetVcpus (domains[i], vinfo, info.nrVirtCpu,
NULL, 0) != 0) {
- free (vinfo);
+ sfree (vinfo);
continue;
}
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. */
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,
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;
n = virConnectListDomains (conn, domids, n);
if (n < 0) {
VIRT_ERROR (conn, "reading list of domains");
- free (domids);
+ sfree (domids);
return -1;
}
/* 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;
if (domains) {
for (i = 0; i < nr_domains; ++i)
virDomainFree (domains[i]);
- free (domains);
+ sfree (domains);
}
domains = NULL;
nr_domains = 0;
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;
new_ptr = malloc (new_size);
if (new_ptr == NULL) {
- free (path_copy);
+ sfree (path_copy);
return -1;
}
block_devices = new_ptr;
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++;
}
}
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];
- char *host_ptr;
- size_t host_len;
-
- vl->time = t;
- vl->interval = interval_g;
-
- sstrncpy (vl->plugin, "libvirt", sizeof (vl->plugin));
-
- vl->host[0] = '\0';
- host_ptr = vl->host;
- host_len = sizeof (vl->host);
-
- /* 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)
{
static char *log_file = NULL;
static int print_timestamp = 1;
+static int print_severity = 0;
static const char *config_keys[] =
{
"LogLevel",
"File",
- "Timestamp"
+ "Timestamp",
+ "PrintSeverity"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
print_timestamp = 0;
else
print_timestamp = 1;
+ } else if (0 == strcasecmp(key, "PrintSeverity")) {
+ if (IS_FALSE (value))
+ print_severity = 0;
+ else
+ print_severity = 1;
}
else {
return -1;
return 0;
} /* int logfile_config (const char *, const char *) */
-static void logfile_print (const char *msg, time_t timestamp_time)
+static void logfile_print (const char *msg, int severity,
+ cdtime_t timestamp_time)
{
FILE *fh;
int do_close = 0;
struct tm timestamp_tm;
char timestamp_str[64];
+ char level_str[16] = "";
+
+ if (print_severity)
+ {
+ switch (severity)
+ {
+ case LOG_ERR:
+ snprintf(level_str, sizeof (level_str), "[error] ");
+ break;
+ case LOG_WARNING:
+ snprintf(level_str, sizeof (level_str), "[warning] ");
+ break;
+ case LOG_NOTICE:
+ snprintf(level_str, sizeof (level_str), "[notice] ");
+ break;
+ case LOG_INFO:
+ snprintf(level_str, sizeof (level_str), "[info] ");
+ break;
+ case LOG_DEBUG:
+ snprintf(level_str, sizeof (level_str), "[debug] ");
+ break;
+ default:
+ break;
+ }
+ }
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);
else
{
if (print_timestamp)
- fprintf (fh, "[%s] %s\n", timestamp_str, msg);
+ fprintf (fh, "[%s] %s%s\n", timestamp_str, level_str, msg);
else
- fprintf (fh, "%s\n", msg);
+ fprintf (fh, "%s%s\n", level_str, msg);
if (do_close != 0)
fclose (fh);
if (severity > log_level)
return;
- logfile_print (msg, time (NULL));
+ logfile_print (msg, severity, cdtime ());
} /* void logfile_log (int, const char *) */
static int logfile_notification (const notification_t *n,
buf[sizeof (buf) - 1] = '\0';
- logfile_print (buf,
- (n->time > 0) ? n->time : time (NULL));
+ logfile_print (buf, LOG_INFO,
+ (n->time != 0) ? n->time : cdtime ());
return (0);
} /* int logfile_notification */
--- /dev/null
+/**
+ * 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 <collectd at wattapower.net>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <sys/protosw.h>
+#include <libperfstat.h>
+#include <sys/utsname.h>
+
+/* XINTFRAC was defined in libperfstat.h somewhere between AIX 5.3 and 6.1 */
+#ifndef XINTFRAC
+# include <sys/systemcfg.h>
+# 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 : */
+
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);
}
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]);
}
}
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);
}
/* 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 */
mr_regex_t *plugin_instance;
mr_regex_t *type;
mr_regex_t *type_instance;
+ _Bool invert;
};
/*
return (-ENOMEM);
}
memset (m, 0, sizeof (*m));
+
+ m->invert = 0;
status = 0;
for (i = 0; i < ci->children_num; i++)
status = mr_config_add_regex (&m->type, child);
else if (strcasecmp ("TypeInstance", child->key) == 0)
status = mr_config_add_regex (&m->type_instance, child);
+ else if (strcasecmp ("Invert", child->key) == 0)
+ status = cf_util_get_boolean(child, &m->invert);
else
{
log_err ("The `%s' configuration option is not understood and "
void **user_data)
{
mr_match_t *m;
+ int match_value = FC_MATCH_MATCHES;
+ int nomatch_value = FC_MATCH_NO_MATCH;
if ((user_data == NULL) || (*user_data == NULL))
return (-1);
m = *user_data;
+ if (m->invert)
+ {
+ match_value = FC_MATCH_NO_MATCH;
+ nomatch_value = FC_MATCH_MATCHES;
+ }
+
if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
if (mr_match_regexen (m->plugin_instance,
vl->plugin_instance) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
if (mr_match_regexen (m->type_instance,
vl->type_instance) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
- return (FC_MATCH_MATCHES);
+ return (match_value);
} /* }}} int mr_match */
void module_register (void)
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;
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 "
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)
{
*/
static void mv_free_match (mv_match_t *m) /* {{{ */
{
+ int i;
+
if (m == NULL)
return;
+ if (m->data_sources != NULL)
+ {
+ for (i = 0; i < m->data_sources_num; ++i)
+ free(m->data_sources[i]);
+ free(m->data_sources);
+ }
+
free (m);
} /* }}} void mv_free_match */
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));
/**
* 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
*
*
* Authors:
* Antony Dovgal <tony at daylessday dot org>
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Doug MacEachern <dougm at hyperic.com>
* Franck Lombardi
**/
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
{
}
/* }}} */
-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;
} /* 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));
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));
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));
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) {
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]);
}
*/
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]));
}
/*
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))
{
#include <pthread.h>
/*
- * Defines
- */
-#define MD_TYPE_STRING 1
-#define MD_TYPE_SIGNED_INT 2
-#define MD_TYPE_UNSIGNED_INT 3
-#define MD_TYPE_DOUBLE 4
-#define MD_TYPE_BOOLEAN 5
-
-/*
* Data types
*/
union meta_value_u
return (0);
} /* }}} int meta_data_exists */
+int meta_data_type (meta_data_t *md, const char *key) /* {{{ */
+{
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock (&md->lock);
+
+ for (e = md->head; e != NULL; e = e->next)
+ {
+ if (strcasecmp (key, e->key) == 0)
+ {
+ pthread_mutex_unlock (&md->lock);
+ return e->type;
+ }
+ }
+
+ pthread_mutex_unlock (&md->lock);
+ return 0;
+} /* }}} int meta_data_type */
+
+int meta_data_toc (meta_data_t *md, char ***toc) /* {{{ */
+{
+ int i = 0, count = 0;
+ meta_entry_t *e;
+
+ if ((md == NULL) || (toc == NULL))
+ return -EINVAL;
+
+ pthread_mutex_lock (&md->lock);
+
+ for (e = md->head; e != NULL; e = e->next)
+ ++count;
+
+ *toc = malloc(count * sizeof(**toc));
+ for (e = md->head; e != NULL; e = e->next)
+ (*toc)[i++] = strdup(e->key);
+
+ pthread_mutex_unlock (&md->lock);
+ return count;
+} /* }}} int meta_data_toc */
+
int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */
{
meta_entry_t *this;
#include "collectd.h"
+/*
+ * Defines
+ */
+#define MD_TYPE_STRING 1
+#define MD_TYPE_SIGNED_INT 2
+#define MD_TYPE_UNSIGNED_INT 3
+#define MD_TYPE_DOUBLE 4
+#define MD_TYPE_BOOLEAN 5
+
struct meta_data_s;
typedef struct meta_data_s meta_data_t;
void meta_data_destroy (meta_data_t *md);
int meta_data_exists (meta_data_t *md, const char *key);
+int meta_data_type (meta_data_t *md, const char *key);
+int meta_data_toc (meta_data_t *md, char ***toc);
int meta_data_delete (meta_data_t *md, const char *key);
int meta_data_add_string (meta_data_t *md,
--- /dev/null
+/**
+ * collectd - src/modbus.c
+ * 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 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 <octo at noris.net>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <netdb.h>
+
+#include <modbus/modbus.h>
+
+#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
+# else
+# define MODBUS_TCP_DEFAULT_PORT 502
+# endif
+#endif
+
+/*
+ * <Data "data_name">
+ * RegisterBase 1234
+ * RegisterType float
+ * Type gauge
+ * Instance "..."
+ * </Data>
+ *
+ * <Host "name">
+ * Address "addr"
+ * Port "1234"
+ * Interval 60
+ *
+ * <Slave 1>
+ * Instance "foobar" # optional
+ * Collect "data_name"
+ * </Slave>
+ * </Host>
+ */
+
+/*
+ * Data structures
+ */
+enum mb_register_type_e /* {{{ */
+{
+ REG_TYPE_UINT16,
+ REG_TYPE_UINT32,
+ REG_TYPE_FLOAT
+}; /* }}} */
+typedef enum mb_register_type_e mb_register_type_t;
+
+struct mb_data_s;
+typedef struct mb_data_s mb_data_t;
+struct mb_data_s /* {{{ */
+{
+ char *name;
+ int register_base;
+ mb_register_type_t register_type;
+ char type[DATA_MAX_NAME_LEN];
+ char instance[DATA_MAX_NAME_LEN];
+
+ mb_data_t *next;
+}; /* }}} */
+
+struct mb_slave_s /* {{{ */
+{
+ int id;
+ char instance[DATA_MAX_NAME_LEN];
+ mb_data_t *collect;
+}; /* }}} */
+typedef struct mb_slave_s mb_slave_t;
+
+struct mb_host_s /* {{{ */
+{
+ char host[DATA_MAX_NAME_LEN];
+ char node[NI_MAXHOST];
+ /* char service[NI_MAXSERV]; */
+ int port;
+ 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;
+}; /* }}} */
+typedef struct mb_host_s mb_host_t;
+
+struct mb_data_group_s;
+typedef struct mb_data_group_s mb_data_group_t;
+struct mb_data_group_s /* {{{ */
+{
+ mb_data_t *registers;
+ size_t registers_num;
+
+ mb_data_group_t *next;
+}; /* }}} */
+
+/*
+ * Global variables
+ */
+static mb_data_t *data_definitions = NULL;
+
+/*
+ * Functions
+ */
+static mb_data_t *data_get_by_name (mb_data_t *src, /* {{{ */
+ const char *name)
+{
+ mb_data_t *ptr;
+
+ if (name == NULL)
+ return (NULL);
+
+ for (ptr = src; ptr != NULL; ptr = ptr->next)
+ if (strcasecmp (ptr->name, name) == 0)
+ return (ptr);
+
+ return (NULL);
+} /* }}} mb_data_t *data_get_by_name */
+
+static int data_append (mb_data_t **dst, mb_data_t *src) /* {{{ */
+{
+ mb_data_t *ptr;
+
+ if ((dst == NULL) || (src == NULL))
+ return (EINVAL);
+
+ ptr = *dst;
+
+ if (ptr == NULL)
+ {
+ *dst = src;
+ return (0);
+ }
+
+ while (ptr->next != NULL)
+ ptr = ptr->next;
+
+ ptr->next = src;
+
+ return (0);
+} /* }}} int data_append */
+
+/* Copy a single mb_data_t and append it to another list. */
+static int data_copy (mb_data_t **dst, const mb_data_t *src) /* {{{ */
+{
+ mb_data_t *tmp;
+ int status;
+
+ if ((dst == NULL) || (src == NULL))
+ return (EINVAL);
+
+ tmp = malloc (sizeof (*tmp));
+ if (tmp == NULL)
+ return (ENOMEM);
+ memcpy (tmp, src, sizeof (*tmp));
+ tmp->name = NULL;
+ tmp->next = NULL;
+
+ tmp->name = strdup (src->name);
+ if (tmp->name == NULL)
+ {
+ sfree (tmp);
+ return (ENOMEM);
+ }
+
+ status = data_append (dst, tmp);
+ if (status != 0)
+ {
+ sfree (tmp->name);
+ sfree (tmp);
+ return (status);
+ }
+
+ return (0);
+} /* }}} int data_copy */
+
+/* Lookup a single mb_data_t instance, copy it and append the copy to another
+ * list. */
+static int data_copy_by_name (mb_data_t **dst, mb_data_t *src, /* {{{ */
+ const char *name)
+{
+ mb_data_t *ptr;
+
+ if ((dst == NULL) || (src == NULL) || (name == NULL))
+ return (EINVAL);
+
+ ptr = data_get_by_name (src, name);
+ if (ptr == NULL)
+ return (ENOENT);
+
+ return (data_copy (dst, ptr));
+} /* }}} int data_copy_by_name */
+
+/* Read functions */
+
+static int mb_submit (mb_host_t *host, mb_slave_t *slave, /* {{{ */
+ mb_data_t *data, value_t value)
+{
+ value_list_t vl = VALUE_LIST_INIT;
+
+ if ((host == NULL) || (slave == NULL) || (data == NULL))
+ return (EINVAL);
+
+ if (host->interval <= 0)
+ host->interval = interval_g;
+
+ if (slave->instance[0] == 0)
+ ssnprintf (slave->instance, sizeof (slave->instance), "slave_%i",
+ slave->id);
+
+ vl.values = &value;
+ vl.values_len = 1;
+ vl.interval = host->interval;
+ sstrncpy (vl.host, host->host, sizeof (vl.host));
+ sstrncpy (vl.plugin, "modbus", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, slave->instance, sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, data->type, sizeof (vl.type));
+ sstrncpy (vl.type_instance, data->instance, sizeof (vl.type_instance));
+
+ return (plugin_dispatch_values (&vl));
+} /* }}} int mb_submit */
+
+static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */
+{
+ union
+ {
+ uint8_t b[4];
+ float f;
+ } conv;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* little endian */
+ conv.b[0] = lo & 0x00ff;
+ conv.b[1] = (lo >> 8) & 0x00ff;
+ conv.b[2] = hi & 0x00ff;
+ conv.b[3] = (hi >> 8) & 0x00ff;
+#else
+ conv.b[3] = lo & 0x00ff;
+ conv.b[2] = (lo >> 8) & 0x00ff;
+ conv.b[1] = hi & 0x00ff;
+ conv.b[0] = (hi >> 8) & 0x00ff;
+#endif
+
+ 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;
+
+ if (host == NULL)
+ return (EINVAL);
+
+ if (host->is_connected)
+ return (0);
+
+ /* Only reconnect once per interval. */
+ if (host->have_reconnected)
+ return (-1);
+
+ modbus_set_debug (&host->connection, 1);
+
+ /* We'll do the error handling ourselves. */
+ modbus_set_error_handling (&host->connection, NOP_ON_ERROR);
+
+ 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);
+
+ modbus_init_tcp (&host->connection,
+ /* host = */ host->node,
+ /* port = */ host->port);
+
+ status = modbus_connect (&host->connection);
+ if (status != 0)
+ {
+ ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.",
+ host->node, host->port, status);
+ return (status);
+ }
+
+ host->is_connected = 1;
+ 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) \
+ (vt).counter = (counter_t) (raw); \
+ else if ((ds)->ds[0].type == DS_TYPE_GAUGE) \
+ (vt).gauge = (gauge_t) (raw); \
+ else if ((ds)->ds[0].type == DS_TYPE_DERIVE) \
+ (vt).derive = (derive_t) (raw); \
+ else /* if (ds->ds[0].type == DS_TYPE_ABSOLUTE) */ \
+ (vt).absolute = (absolute_t) (raw); \
+} while (0)
+
+static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
+ mb_data_t *data)
+{
+ uint16_t values[2];
+ int values_num;
+ const data_set_t *ds;
+ int status;
+ int i;
+
+ if ((host == NULL) || (slave == NULL) || (data == NULL))
+ return (EINVAL);
+
+ ds = plugin_get_ds (data->type);
+ if (ds == NULL)
+ {
+ ERROR ("Modbus plugin: Type \"%s\" is not defined.", data->type);
+ return (-1);
+ }
+
+ if (ds->ds_num != 1)
+ {
+ ERROR ("Modbus plugin: The type \"%s\" has %i data sources. "
+ "I can only handle data sets with only one data source.",
+ data->type, ds->ds_num);
+ return (-1);
+ }
+
+ if ((ds->ds[0].type != DS_TYPE_GAUGE)
+ && (data->register_type != REG_TYPE_UINT32))
+ {
+ NOTICE ("Modbus plugin: The data source of type \"%s\" is %s, not gauge. "
+ "This will most likely result in problems, because the register type "
+ "is not UINT32.", data->type, DS_TYPE_TO_STRING (ds->ds[0].type));
+ }
+
+ memset (values, 0, sizeof (values));
+ if ((data->register_type == REG_TYPE_UINT32)
+ || (data->register_type == REG_TYPE_FLOAT))
+ values_num = 2;
+ 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 = 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;
+#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: modbus_read_registers (%s) failed. "
+ "Reconnecting has already been tried. Giving up.", host->host);
+ return (-1);
+ }
+
+ /* Maybe the device closed the connection during the waiting interval.
+ * Try re-establishing the connection. */
+ status = mb_init_connection (host);
+ if (status != 0)
+ {
+ ERROR ("Modbus plugin: modbus_read_registers (%s) failed. "
+ "While trying to reconnect, connecting to \"%s\" failed. "
+ "Giving up.",
+ host->host, host->node);
+ return (-1);
+ }
+
+ DEBUG ("Modbus plugin: Re-established connection to %s", host->host);
+
+ /* try again */
+ continue;
+ } /* for (i = 0, 1) */
+
+ DEBUG ("Modbus plugin: mb_read_data: Success! "
+ "modbus_read_registers returned with status %i.", status);
+
+ if (data->register_type == REG_TYPE_FLOAT)
+ {
+ float float_value;
+ value_t vt;
+
+ float_value = mb_register_to_float (values[0], values[1]);
+ DEBUG ("Modbus plugin: mb_read_data: "
+ "Returned float value is %g", (double) float_value);
+
+ CAST_TO_VALUE_T (ds, vt, float_value);
+ mb_submit (host, slave, data, vt);
+ }
+ else if (data->register_type == REG_TYPE_UINT32)
+ {
+ uint32_t v32;
+ value_t vt;
+
+ v32 = (values[0] << 16) | values[1];
+ DEBUG ("Modbus plugin: mb_read_data: "
+ "Returned uint32 value is %"PRIu32, v32);
+
+ CAST_TO_VALUE_T (ds, vt, v32);
+ mb_submit (host, slave, data, vt);
+ }
+ else /* if (data->register_type == REG_TYPE_UINT16) */
+ {
+ value_t vt;
+
+ DEBUG ("Modbus plugin: mb_read_data: "
+ "Returned uint16 value is %"PRIu16, values[0]);
+
+ CAST_TO_VALUE_T (ds, vt, values[0]);
+ mb_submit (host, slave, data, vt);
+ }
+
+ return (0);
+} /* }}} int mb_read_data */
+
+static int mb_read_slave (mb_host_t *host, mb_slave_t *slave) /* {{{ */
+{
+ mb_data_t *data;
+ int success;
+ int status;
+
+ if ((host == NULL) || (slave == NULL))
+ return (EINVAL);
+
+ success = 0;
+ for (data = slave->collect; data != NULL; data = data->next)
+ {
+ status = mb_read_data (host, slave, data);
+ if (status == 0)
+ success++;
+ }
+
+ if (success == 0)
+ return (-1);
+ else
+ return (0);
+} /* }}} int mb_read_slave */
+
+static int mb_read (user_data_t *user_data) /* {{{ */
+{
+ mb_host_t *host;
+ size_t i;
+ int success;
+ int status;
+
+ if ((user_data == NULL) || (user_data->data == NULL))
+ return (EINVAL);
+
+ host = user_data->data;
+
+ /* Clear the reconnect flag. */
+ host->have_reconnected = 0;
+
+ success = 0;
+ for (i = 0; i < host->slaves_num; i++)
+ {
+ status = mb_read_slave (host, host->slaves + i);
+ if (status == 0)
+ success++;
+ }
+
+ if (success == 0)
+ return (-1);
+ else
+ return (0);
+} /* }}} int mb_read */
+
+/* Free functions */
+
+static void data_free_one (mb_data_t *data) /* {{{ */
+{
+ if (data == NULL)
+ return;
+
+ sfree (data->name);
+ sfree (data);
+} /* }}} void data_free_one */
+
+static void data_free_all (mb_data_t *data) /* {{{ */
+{
+ mb_data_t *next;
+
+ if (data == NULL)
+ return;
+
+ next = data->next;
+ data_free_one (data);
+
+ data_free_all (next);
+} /* }}} void data_free_all */
+
+static void slaves_free_all (mb_slave_t *slaves, size_t slaves_num) /* {{{ */
+{
+ size_t i;
+
+ if (slaves == NULL)
+ return;
+
+ for (i = 0; i < slaves_num; i++)
+ data_free_all (slaves[i].collect);
+ sfree (slaves);
+} /* }}} void slaves_free_all */
+
+static void host_free (void *void_host) /* {{{ */
+{
+ mb_host_t *host = void_host;
+
+ if (host == NULL)
+ return;
+
+ slaves_free_all (host->slaves, host->slaves_num);
+ sfree (host);
+} /* }}} void host_free */
+
+/* Config functions */
+
+static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */
+{
+ mb_data_t data;
+ int status;
+ int i;
+
+ memset (&data, 0, sizeof (data));
+ data.name = NULL;
+ data.register_type = REG_TYPE_UINT16;
+ data.next = NULL;
+
+ status = cf_util_get_string (ci, &data.name);
+ if (status != 0)
+ return (status);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ status = 0;
+
+ if (strcasecmp ("Type", child->key) == 0)
+ status = cf_util_get_string_buffer (child,
+ data.type, sizeof (data.type));
+ else if (strcasecmp ("Instance", child->key) == 0)
+ status = cf_util_get_string_buffer (child,
+ data.instance, sizeof (data.instance));
+ else if (strcasecmp ("RegisterBase", child->key) == 0)
+ status = cf_util_get_int (child, &data.register_base);
+ else if (strcasecmp ("RegisterType", child->key) == 0)
+ {
+ char tmp[16];
+ status = cf_util_get_string_buffer (child, tmp, sizeof (tmp));
+ if (status != 0)
+ /* do nothing */;
+ else if (strcasecmp ("Uint16", tmp) == 0)
+ data.register_type = REG_TYPE_UINT16;
+ else if (strcasecmp ("Uint32", tmp) == 0)
+ data.register_type = REG_TYPE_UINT32;
+ else if (strcasecmp ("Float", tmp) == 0)
+ data.register_type = REG_TYPE_FLOAT;
+ else
+ {
+ ERROR ("Modbus plugin: The register type \"%s\" is unknown.", tmp);
+ status = -1;
+ }
+ }
+ else
+ {
+ ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ assert (data.name != NULL);
+ if (data.type[0] == 0)
+ {
+ ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
+ data.name);
+ status = -1;
+ }
+
+ if (status == 0)
+ data_copy (&data_definitions, &data);
+
+ sfree (data.name);
+
+ return (status);
+} /* }}} int mb_config_add_data */
+
+static int mb_config_set_host_address (mb_host_t *host, /* {{{ */
+ const char *address)
+{
+ struct addrinfo *ai_list;
+ struct addrinfo *ai_ptr;
+ struct addrinfo ai_hints;
+ int status;
+
+ if ((host == NULL) || (address == NULL))
+ return (EINVAL);
+
+ memset (&ai_hints, 0, sizeof (ai_hints));
+#if AI_ADDRCONFIG
+ ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ /* XXX: libmodbus can only handle IPv4 addresses. */
+ ai_hints.ai_family = AF_INET;
+ ai_hints.ai_addr = NULL;
+ ai_hints.ai_canonname = NULL;
+ ai_hints.ai_next = NULL;
+
+ ai_list = NULL;
+ status = getaddrinfo (address, /* service = */ NULL,
+ &ai_hints, &ai_list);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("Modbus plugin: getaddrinfo failed: %s",
+ (status == EAI_SYSTEM)
+ ? sstrerror (errno, errbuf, sizeof (errbuf))
+ : gai_strerror (status));
+ return (status);
+ }
+
+ for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+ {
+ status = getnameinfo (ai_ptr->ai_addr, ai_ptr->ai_addrlen,
+ host->node, sizeof (host->node),
+ /* service = */ NULL, /* length = */ 0,
+ /* flags = */ NI_NUMERICHOST);
+ if (status == 0)
+ break;
+ } /* for (ai_ptr) */
+
+ freeaddrinfo (ai_list);
+
+ if (status != 0)
+ ERROR ("Modbus plugin: Unable to translate node name: \"%s\"", address);
+ else /* if (status == 0) */
+ {
+ DEBUG ("Modbus plugin: mb_config_set_host_address: %s -> %s",
+ address, host->node);
+ }
+
+ return (status);
+} /* }}} int mb_config_set_host_address */
+
+static int mb_config_add_slave (mb_host_t *host, oconfig_item_t *ci) /* {{{ */
+{
+ mb_slave_t *slave;
+ int status;
+ int i;
+
+ if ((host == NULL) || (ci == NULL))
+ return (EINVAL);
+
+ slave = realloc (host->slaves, sizeof (*slave) * (host->slaves_num + 1));
+ if (slave == NULL)
+ return (ENOMEM);
+ host->slaves = slave;
+ slave = host->slaves + host->slaves_num;
+ memset (slave, 0, sizeof (*slave));
+ slave->collect = NULL;
+
+ status = cf_util_get_int (ci, &slave->id);
+ if (status != 0)
+ return (status);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ status = 0;
+
+ if (strcasecmp ("Instance", child->key) == 0)
+ status = cf_util_get_string_buffer (child,
+ slave->instance, sizeof (slave->instance));
+ else if (strcasecmp ("Collect", child->key) == 0)
+ {
+ char buffer[1024];
+ status = cf_util_get_string_buffer (child, buffer, sizeof (buffer));
+ if (status == 0)
+ data_copy_by_name (&slave->collect, data_definitions, buffer);
+ status = 0; /* continue after failure. */
+ }
+ else
+ {
+ ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if ((status == 0) && (slave->collect == NULL))
+ status = EINVAL;
+
+ if (slave->id < 0)
+ status = EINVAL;
+
+ if (status == 0)
+ host->slaves_num++;
+ else /* if (status != 0) */
+ data_free_all (slave->collect);
+
+ return (status);
+} /* }}} int mb_config_add_slave */
+
+static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */
+{
+ mb_host_t *host;
+ int status;
+ int i;
+
+ host = malloc (sizeof (*host));
+ if (host == NULL)
+ return (ENOMEM);
+ memset (host, 0, sizeof (*host));
+ host->slaves = NULL;
+
+ status = cf_util_get_string_buffer (ci, host->host, sizeof (host->host));
+ if (status != 0)
+ return (status);
+ if (host->host[0] == 0)
+ return (EINVAL);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ status = 0;
+
+ if (strcasecmp ("Address", child->key) == 0)
+ {
+ char buffer[NI_MAXHOST];
+ status = cf_util_get_string_buffer (child, buffer, sizeof (buffer));
+ if (status == 0)
+ status = mb_config_set_host_address (host, buffer);
+ }
+ else if (strcasecmp ("Port", child->key) == 0)
+ {
+ host->port = cf_util_get_port_number (child);
+ if (host->port <= 0)
+ status = -1;
+ }
+ else if (strcasecmp ("Interval", child->key) == 0)
+ 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);
+ else
+ {
+ ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ assert (host->host[0] != 0);
+ if (host->host[0] == 0)
+ {
+ ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
+ host->host);
+ status = -1;
+ }
+
+ if (status == 0)
+ {
+ user_data_t ud;
+ char name[1024];
+ struct timespec interval = { 0, 0 };
+
+ ud.data = host;
+ ud.free_func = host_free;
+
+ ssnprintf (name, sizeof (name), "modbus-%s", host->host);
+
+ CDTIME_T_TO_TIMESPEC (host->interval, &interval);
+
+ plugin_register_complex_read (/* group = */ NULL, name,
+ /* callback = */ mb_read,
+ /* interval = */ (host->interval > 0) ? &interval : NULL,
+ &ud);
+ }
+ else
+ {
+ host_free (host);
+ }
+
+ return (status);
+} /* }}} int mb_config_add_host */
+
+static int mb_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ if (ci == NULL)
+ return (EINVAL);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Data", child->key) == 0)
+ mb_config_add_data (child);
+ else if (strcasecmp ("Host", child->key) == 0)
+ mb_config_add_host (child);
+ else
+ ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+ }
+
+ return (0);
+} /* }}} int mb_config */
+
+/* ========= */
+
+static int mb_shutdown (void) /* {{{ */
+{
+ data_free_all (data_definitions);
+ data_definitions = NULL;
+
+ return (0);
+} /* }}} int mb_shutdown */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("modbus", mb_config);
+ plugin_register_shutdown ("modbus", mb_shutdown);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Mirko Buffoni <briareos at eswat.org>
* Doug MacEachern <dougm at hyperic.com>
* Sebastian tokkee Harl <sh at tokkee.org>
struct mysql_database_s /* {{{ */
{
- /* instance == NULL => legacy mode */
char *instance;
char *host;
char *user;
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;
* </Database>
* </Plugin>
*/
-
-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;
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++)
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);
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 <Database> "
- "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
- : "<legacy>",
- db->port);
- status = -1;
- }
- break;
- } /* while (status == 0) */
-
/* If all went well, register this database for reading */
if (status == 0)
{
else
sstrncpy (cb_name, "mysql", sizeof (cb_name));
- plugin_register_complex_read (cb_name, mysql_read,
+ plugin_register_complex_read (/* group = */ NULL, cb_name,
+ mysql_read,
/* interval = */ NULL, &ud);
}
else
}
return (0);
+} /* }}} int mysql_config_database */
+
+static int mysql_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ if (ci == NULL)
+ return (EINVAL);
+
+ /* Fill the `mysql_database_t' structure.. */
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Database", child->key) == 0)
+ mysql_config_database (child);
+ else
+ WARNING ("mysql plugin: Option \"%s\" not allowed here.",
+ child->key);
+ }
+
+ return (0);
} /* }}} int mysql_config */
/* }}} End of configuration handling functions */
int err;
if ((err = mysql_ping (db->con)) != 0)
{
- WARNING ("mysql_ping failed for %s: %s",
- (db->instance != NULL)
- ? db->instance
- : "<legacy>",
+ /* 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;
}
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)
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)
} /* 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 */
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 */
if (db->slave_notif)
{
- notification_t n = { 0, time (NULL), "", "",
+ notification_t n = { 0, cdtime (), "", "",
"mysql", "", "time_offset", "", NULL };
char *io, *sql;
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))
char *query;
int field_num;
- unsigned long long qcache_hits = 0ULL;
- unsigned long long qcache_inserts = 0ULL;
- unsigned long long qcache_not_cached = 0ULL;
- unsigned long long qcache_lowmem_prunes = 0ULL;
- int qcache_queries_in_cache = -1;
+ derive_t qcache_hits = 0;
+ derive_t qcache_inserts = 0;
+ derive_t qcache_not_cached = 0;
+ derive_t qcache_lowmem_prunes = 0;
+ gauge_t qcache_queries_in_cache = NAN;
- int threads_running = -1;
- int threads_connected = -1;
- int threads_cached = -1;
- unsigned long long threads_created = 0ULL;
+ gauge_t threads_running = NAN;
+ gauge_t threads_connected = NAN;
+ gauge_t threads_cached = NAN;
+ derive_t threads_created = 0;
unsigned long long traffic_incoming = 0ULL;
unsigned long long traffic_outgoing = 0ULL;
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)
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)
}
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);
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;
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;
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;
struct data_volume_perf_s {
char *name;
uint32_t flags;
- time_t timestamp;
+ cdtime_t timestamp;
uint64_t read_bytes;
uint64_t write_bytes;
int port;
char *username;
char *password;
- int interval;
+ cdtime_t interval;
na_server_t *srv;
cfg_wafl_t *cfg_wafl;
const char *plugin_inst,
const char *type, const char *type_inst,
value_t *values, int values_len,
- time_t timestamp)
+ cdtime_t timestamp, cdtime_t interval)
{
value_list_t vl = VALUE_LIST_INIT;
if (timestamp > 0)
vl.time = timestamp;
+ if (interval > 0)
+ vl.interval = interval;
+
if (host != NULL)
sstrncpy (vl.host, host, sizeof (vl.host));
else
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)
+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));
-} /* }}} int submit_two_counters */
+ values, 2, timestamp, interval));
+} /* }}} 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)
+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));
-} /* }}} int submit_counter */
+ &v, 1, timestamp, interval));
+} /* }}} 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)
+ cdtime_t timestamp, cdtime_t interval)
{
value_t values[2];
values[1].gauge = val1;
return (submit_values (host, plugin_inst, type, type_inst,
- values, 2, timestamp));
+ values, 2, timestamp, interval));
} /* }}} 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)
+ const char *type, const char *type_inst, double d,
+ cdtime_t timestamp, cdtime_t interval)
{
value_t v;
v.gauge = (gauge_t) d;
return (submit_values (host, plugin_inst, type, type_inst,
- &v, 1, timestamp));
+ &v, 1, timestamp, interval));
} /* }}} int submit_uint64 */
/* Calculate hit ratio from old and new counters and submit the resulting
uint64_t new_misses,
uint64_t old_hits,
uint64_t old_misses,
- time_t timestamp)
+ cdtime_t timestamp,
+ cdtime_t interval)
{
value_t v;
}
return (submit_values (host, plugin_inst, "cache_ratio", type_inst,
- &v, 1, timestamp));
+ &v, 1, timestamp, interval));
} /* }}} int submit_cache_ratio */
/* Submits all the caches used by WAFL. Uses "submit_cache_ratio". */
static int submit_wafl_data (const char *hostname, const char *instance, /* {{{ */
- cfg_wafl_t *old_data, const cfg_wafl_t *new_data)
+ cfg_wafl_t *old_data, const cfg_wafl_t *new_data, int interval)
{
/* Submit requested counters */
if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_NAME_CACHE | HAVE_WAFL_NAME_CACHE)
submit_cache_ratio (hostname, instance, "name_cache_hit",
new_data->name_cache_hit, new_data->name_cache_miss,
old_data->name_cache_hit, old_data->name_cache_miss,
- new_data->timestamp);
+ new_data->timestamp, interval);
if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_DIR_CACHE | HAVE_WAFL_FIND_DIR)
&& HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_FIND_DIR))
submit_cache_ratio (hostname, instance, "find_dir_hit",
new_data->find_dir_hit, new_data->find_dir_miss,
old_data->find_dir_hit, old_data->find_dir_miss,
- new_data->timestamp);
+ new_data->timestamp, interval);
if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_BUF_CACHE | HAVE_WAFL_BUF_HASH)
&& HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_BUF_HASH))
submit_cache_ratio (hostname, instance, "buf_hash_hit",
new_data->buf_hash_hit, new_data->buf_hash_miss,
old_data->buf_hash_hit, old_data->buf_hash_miss,
- new_data->timestamp);
+ new_data->timestamp, interval);
if (HAS_ALL_FLAGS (old_data->flags, CFG_WAFL_INODE_CACHE | HAVE_WAFL_INODE_CACHE)
&& HAS_ALL_FLAGS (new_data->flags, HAVE_WAFL_INODE_CACHE))
submit_cache_ratio (hostname, instance, "inode_cache_hit",
new_data->inode_cache_hit, new_data->inode_cache_miss,
old_data->inode_cache_hit, old_data->inode_cache_miss,
- new_data->timestamp);
+ new_data->timestamp, interval);
/* Clear old HAVE_* flags */
old_data->flags &= ~HAVE_WAFL_ALL;
* update flags appropriately. */
static int submit_volume_perf_data (const char *hostname, /* {{{ */
data_volume_perf_t *old_data,
- const data_volume_perf_t *new_data)
+ const data_volume_perf_t *new_data, int interval)
{
char plugin_instance[DATA_MAX_NAME_LEN];
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);
+ 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);
+ 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 */
}
submit_two_gauge (hostname, plugin_instance, "disk_latency", /* type instance = */ NULL,
- latency_per_op_read, latency_per_op_write, new_data->timestamp);
+ latency_per_op_read, latency_per_op_write, new_data->timestamp, interval);
}
/* Clear all HAVE_* flags. */
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
*
*/
/* Data corresponding to <WAFL /> */
static int cna_handle_wafl_data (const char *hostname, cfg_wafl_t *cfg_wafl, /* {{{ */
- na_elem_t *data)
+ na_elem_t *data, int interval)
{
cfg_wafl_t perf_data;
const char *plugin_inst;
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)
}
}
- return (submit_wafl_data (hostname, plugin_inst, cfg_wafl, &perf_data));
+ return (submit_wafl_data (hostname, plugin_inst, cfg_wafl, &perf_data, interval));
} /* }}} void cna_handle_wafl_data */
static int cna_setup_wafl (cfg_wafl_t *cw) /* {{{ */
{
na_elem_t *data;
int status;
- time_t now;
+ cdtime_t now;
if (host == NULL)
return (EINVAL);
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);
return (-1);
}
- status = cna_handle_wafl_data (host->name, host->cfg_wafl, data);
+ status = cna_handle_wafl_data (host->name, host->cfg_wafl, data, host->interval);
if (status == 0)
host->cfg_wafl->interval.last_read = now;
/* Data corresponding to <Disks /> */
static int cna_handle_disk_data (const char *hostname, /* {{{ */
- cfg_disk_t *cfg_disk, na_elem_t *data)
+ 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;
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)
if ((cfg_disk->flags & CFG_DISK_BUSIEST) && (worst_disk != NULL))
submit_double (hostname, "system", "percent", "disk_busy",
- worst_disk->disk_busy_percent, timestamp);
+ worst_disk->disk_busy_percent, timestamp, interval);
return (0);
} /* }}} int cna_handle_disk_data */
{
na_elem_t *data;
int status;
- time_t now;
+ cdtime_t now;
if (host == NULL)
return (EINVAL);
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);
return (-1);
}
- status = cna_handle_disk_data (host->name, host->cfg_disk, data);
+ status = cna_handle_disk_data (host->name, host->cfg_disk, data, host->interval);
if (status == 0)
host->cfg_disk->interval.last_read = now;
/* Data corresponding to <VolumePerf /> */
static int cna_handle_volume_perf_data (const char *hostname, /* {{{ */
- cfg_volume_perf_t *cvp, na_elem_t *data)
+ 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)
}
} /* for (elem_counter) */
- submit_volume_perf_data (hostname, v, &perf_data);
+ submit_volume_perf_data (hostname, v, &perf_data, interval);
} /* for (volume) */
return (0);
{
na_elem_t *data;
int status;
- time_t now;
+ cdtime_t now;
if (host == NULL)
return (EINVAL);
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);
return (-1);
}
- status = cna_handle_volume_perf_data (host->name, host->cfg_volume_perf, data);
+ status = cna_handle_volume_perf_data (host->name, host->cfg_volume_perf, data, host->interval);
if (status == 0)
host->cfg_volume_perf->interval.last_read = now;
/* Data corresponding to <VolumeUsage /> */
static int cna_submit_volume_usage_data (const char *hostname, /* {{{ */
- cfg_volume_usage_t *cfg_volume)
+ cfg_volume_usage_t *cfg_volume, int interval)
{
data_volume_usage_t *v;
if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_FREE))
submit_double (hostname, /* plugin instance = */ plugin_instance,
"df_complex", "free",
- (double) norm_free, /* timestamp = */ 0);
+ (double) norm_free, /* timestamp = */ 0, interval);
if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SIS_SAVED))
submit_double (hostname, /* plugin instance = */ plugin_instance,
"df_complex", "sis_saved",
- (double) sis_saved, /* timestamp = */ 0);
+ (double) sis_saved, /* timestamp = */ 0, interval);
if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_NORM_USED))
submit_double (hostname, /* plugin instance = */ plugin_instance,
"df_complex", "used",
- (double) norm_used, /* timestamp = */ 0);
+ (double) norm_used, /* timestamp = */ 0, interval);
if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_RSVD))
submit_double (hostname, /* plugin instance = */ plugin_instance,
"df_complex", "snap_reserved",
- (double) snap_reserve_free, /* timestamp = */ 0);
+ (double) snap_reserve_free, /* timestamp = */ 0, interval);
if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED | HAVE_VOLUME_USAGE_SNAP_RSVD))
submit_double (hostname, /* plugin instance = */ plugin_instance,
"df_complex", "snap_reserve_used",
- (double) snap_reserve_used, /* timestamp = */ 0);
+ (double) snap_reserve_used, /* timestamp = */ 0, interval);
if (HAS_ALL_FLAGS (v->flags, HAVE_VOLUME_USAGE_SNAP_USED))
submit_double (hostname, /* plugin instance = */ plugin_instance,
"df_complex", "snap_normal_used",
- (double) snap_norm_used, /* timestamp = */ 0);
+ (double) snap_norm_used, /* timestamp = */ 0, interval);
/* Clear all the HAVE_* flags */
v->flags &= ~HAVE_VOLUME_USAGE_ALL;
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));
if (sis == NULL)
continue;
+ if (na_elem_child(sis, "sis-info"))
+ sis = na_elem_child(sis, "sis-info");
+
sis_state = na_child_get_string(sis, "state");
if (sis_state == NULL)
continue;
} /* }}} end of 32-bit workaround */
} /* for (elem_volume) */
- return (cna_submit_volume_usage_data (host->name, cfg_volume));
+ return (cna_submit_volume_usage_data (host->name, cfg_volume, host->interval));
} /* }}} int cna_handle_volume_usage_data */
static int cna_setup_volume_usage (cfg_volume_usage_t *cvu) /* {{{ */
{
na_elem_t *data;
int status;
- time_t now;
+ cdtime_t now;
if (host == NULL)
return (EINVAL);
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);
/* Data corresponding to <System /> */
static int cna_handle_system_data (const char *hostname, /* {{{ */
- cfg_system_t *cfg_system, na_elem_t *data)
+ cfg_system_t *cfg_system, na_elem_t *data, int interval)
{
na_elem_t *instances;
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)
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);
+ 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,
- disk_read, disk_written, timestamp);
+ 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,
- net_recv, net_sent, timestamp);
+ 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",
- cpu_busy, timestamp);
- submit_counter (hostname, instance, "cpu", "idle",
- cpu_total - cpu_busy, timestamp);
+ submit_derive (hostname, instance, "cpu", "system",
+ cpu_busy, timestamp, interval);
+ submit_derive (hostname, instance, "cpu", "idle",
+ cpu_total - cpu_busy, timestamp, interval);
}
return (0);
{
na_elem_t *data;
int status;
- time_t now;
+ cdtime_t now;
if (host == NULL)
return (EINVAL);
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);
return (-1);
}
- status = cna_handle_system_data (host->name, host->cfg_system, data);
+ status = cna_handle_system_data (host->name, host->cfg_system, data, host->interval);
if (status == 0)
host->cfg_system->interval.last_read = now;
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;
} 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")) {
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;
ud.free_func = (void (*) (void *)) free_host_config;
- plugin_register_complex_read (cb_name,
+ plugin_register_complex_read (/* group = */ NULL, cb_name,
/* callback = */ cna_read,
/* interval = */ (host->interval > 0) ? &interval : NULL,
/* user data = */ &ud);
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
**/
#include "collectd.h"
} /* 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;
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;
/**
* 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
#include "utils_fbhash.h"
#include "utils_avltree.h"
#include "utils_cache.h"
+#include "utils_complain.h"
#include "network.h"
#if HAVE_POLL_H
# include <poll.h>
#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
#if HAVE_LIBGCRYPT
# include <gcrypt.h>
char *node;
char *service;
+ int interface;
union
{
* Private variables
*/
static int network_config_ttl = 0;
-static size_t network_config_packet_size = 1024;
+static size_t network_config_packet_size = 1452;
static int network_config_forward = 0;
static int network_config_stats = 0;
* 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;
/*
/* 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
return (!received);
} /* }}} _Bool check_send_okay */
-static int network_dispatch_values (value_list_t *vl) /* {{{ */
+static int network_dispatch_values (value_list_t *vl, /* {{{ */
+ const char *username)
{
int status;
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.");
return (status);
}
+ if (username != NULL)
+ {
+ status = meta_data_add_string (vl->meta, "network:username", username);
+ if (status != 0)
+ {
+ ERROR ("network plugin: meta_data_add_string failed.");
+ meta_data_destroy (vl->meta);
+ vl->meta = NULL;
+ return (status);
+ }
+ }
+
plugin_dispatch_values (vl);
stats_values_dispatched++;
break;
default:
- sfree (pkg_types);
- sfree (pkg_values);
NOTICE ("network plugin: parse_part_values: "
"Don't know how to handle data source type %"PRIu8,
pkg_types[i]);
+ sfree (pkg_types);
+ sfree (pkg_values);
return (-1);
} /* switch (pkg_types[i]) */
}
#define PP_SIGNED 0x01
#define PP_ENCRYPTED 0x02
static int parse_packet (sockent_t *se,
- void *buffer, size_t buffer_size, int flags);
+ void *buffer, size_t buffer_size, int flags,
+ const char *username);
#define BUFFER_READ(p,s) do { \
memcpy ((p), buffer + buffer_offset, (s)); \
static int parse_part_sign_sha256 (sockent_t *se, /* {{{ */
void **ret_buffer, size_t *ret_buffer_len, int flags)
{
+ static c_complain_t complain_no_users = C_COMPLAIN_INIT_STATIC;
+
char *buffer;
size_t buffer_len;
size_t buffer_offset;
if (se->data.server.userdb == NULL)
{
- NOTICE ("network plugin: Received signed network packet but can't verify "
- "it because no user DB has been configured. Will accept it.");
+ c_complain (LOG_NOTICE, &complain_no_users,
+ "network plugin: Received signed network packet but can't verify it "
+ "because no user DB has been configured. Will accept it.");
return (0);
}
{
ERROR ("network plugin: gcry_md_setkey failed: %s", gcry_strerror (err));
gcry_md_close (hd);
+ sfree (secret);
+ sfree (pss.username);
return (-1);
}
gcry_md_close (hd);
hd = NULL;
- sfree (secret);
- sfree (pss.username);
-
if (memcmp (pss.hash, hash, sizeof (pss.hash)) != 0)
{
WARNING ("network plugin: Verifying HMAC-SHA-256 signature failed: "
else
{
parse_packet (se, buffer + buffer_offset, buffer_len - buffer_offset,
- flags | PP_SIGNED);
+ flags | PP_SIGNED, pss.username);
}
+ sfree (secret);
+ sfree (pss.username);
+
*ret_buffer = buffer + buffer_len;
*ret_buffer_len = 0;
warning_has_been_printed = 1;
}
- parse_packet (se, buffer + part_len, buffer_size - part_len, flags);
+ parse_packet (se, buffer + part_len, buffer_size - part_len, flags,
+ /* username = */ NULL);
*ret_buffer = buffer + buffer_size;
*ret_buffer_size = 0;
cypher = network_get_aes256_cypher (se, pea.iv, sizeof (pea.iv),
pea.username);
if (cypher == NULL)
+ {
+ sfree (pea.username);
return (-1);
+ }
payload_len = part_size - (PART_ENCRYPTION_AES256_SIZE + username_len);
assert (payload_len > 0);
/* in = */ NULL, /* in len = */ 0);
if (err != 0)
{
+ sfree (pea.username);
ERROR ("network plugin: gcry_cipher_decrypt returned: %s",
gcry_strerror (err));
return (-1);
buffer + buffer_offset, payload_len);
if (memcmp (hash, pea.hash, sizeof (hash)) != 0)
{
+ sfree (pea.username);
ERROR ("network plugin: Decryption failed: Checksum mismatch.");
return (-1);
}
parse_packet (se, buffer + buffer_offset, payload_len,
- flags | PP_ENCRYPTED);
+ flags | PP_ENCRYPTED, pea.username);
+
+ /* XXX: Free pea.username?!? */
/* Update return values */
*ret_buffer = buffer + part_size;
*ret_buffer_len = buffer_len - part_size;
+ sfree (pea.username);
+
return (0);
} /* }}} int parse_part_encr_aes256 */
/* #endif HAVE_LIBGCRYPT */
#undef BUFFER_READ
static int parse_packet (sockent_t *se, /* {{{ */
- void *buffer, size_t buffer_size, int flags)
+ void *buffer, size_t buffer_size, int flags,
+ const char *username)
{
int status;
if (status != 0)
break;
- network_dispatch_values (&vl);
+ network_dispatch_values (&vl, username);
sfree (vl.values);
}
&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)
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)
{
if (setsockopt (se->data.client.fd, IPPROTO_IP, optname,
&network_config_ttl,
- sizeof (network_config_ttl)) == -1)
+ sizeof (network_config_ttl)) != 0)
{
char errbuf[1024];
ERROR ("setsockopt: %s",
if (setsockopt (se->data.client.fd, IPPROTO_IPV6, optname,
&network_config_ttl,
- sizeof (network_config_ttl)) == -1)
+ sizeof (network_config_ttl)) != 0)
{
char errbuf[1024];
ERROR ("setsockopt: %s",
return (0);
} /* int network_set_ttl */
-static int network_bind_socket (int fd, const struct addrinfo *ai)
+static int network_set_interface (const sockent_t *se, const struct addrinfo *ai) /* {{{ */
+{
+ DEBUG ("network plugin: network_set_interface: interface index = %i;",
+ se->interface);
+
+ assert (se->type == SOCKENT_TYPE_CLIENT);
+
+ if (ai->ai_family == AF_INET)
+ {
+ struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
+
+ if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+ {
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ /* If possible, use the "ip_mreqn" structure which has
+ * an "interface index" member. Using the interface
+ * index is preferred here, because of its similarity
+ * to the way IPv6 handles this. Unfortunately, it
+ * appears not to be portable. */
+ struct ip_mreqn mreq;
+
+ memset (&mreq, 0, sizeof (mreq));
+ mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+ mreq.imr_address.s_addr = ntohl (INADDR_ANY);
+ mreq.imr_ifindex = se->interface;
+#else
+ struct ip_mreq mreq;
+
+ memset (&mreq, 0, sizeof (mreq));
+ mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+ mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
+#endif
+
+ if (setsockopt (se->data.client.fd, IPPROTO_IP, IP_MULTICAST_IF,
+ &mreq, sizeof (mreq)) != 0)
+ {
+ char errbuf[1024];
+ ERROR ("setsockopt: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ return (0);
+ }
+ }
+ else if (ai->ai_family == AF_INET6)
+ {
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
+
+ if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+ {
+ if (setsockopt (se->data.client.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &se->interface,
+ sizeof (se->interface)) != 0)
+ {
+ char errbuf[1024];
+ ERROR ("setsockopt: %s",
+ sstrerror (errno, errbuf,
+ sizeof (errbuf)));
+ return (-1);
+ }
+
+ return (0);
+ }
+ }
+
+ /* else: Not a multicast interface. */
+#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
+ if (se->interface != 0)
+ {
+ char interface_name[IFNAMSIZ];
+
+ if (if_indextoname (se->interface, interface_name) == NULL)
+ return (-1);
+
+ DEBUG ("network plugin: Binding socket to interface %s", interface_name);
+
+ if (setsockopt (se->data.client.fd, SOL_SOCKET, SO_BINDTODEVICE,
+ interface_name,
+ sizeof(interface_name)) == -1 )
+ {
+ char errbuf[1024];
+ ERROR ("setsockopt: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ }
+/* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
+
+#else
+ WARNING ("network plugin: Cannot set the interface on a unicast "
+ "socket because "
+# if !defined(SO_BINDTODEVICE)
+ "the the \"SO_BINDTODEVICE\" socket option "
+# else
+ "the \"if_indextoname\" function "
+# endif
+ "is not available on your system.");
+#endif
+
+ return (0);
+} /* }}} network_set_interface */
+
+static int network_bind_socket (int fd, const struct addrinfo *ai, const int interface_idx)
{
int loop = 0;
int yes = 1;
struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
{
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ struct ip_mreqn mreq;
+#else
struct ip_mreq mreq;
+#endif
DEBUG ("fd = %i; IPv4 multicast address found", fd);
mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
- mreq.imr_interface.s_addr = htonl (INADDR_ANY);
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ /* Set the interface using the interface index if
+ * possible (available). Unfortunately, the struct
+ * ip_mreqn is not portable. */
+ mreq.imr_address.s_addr = ntohl (INADDR_ANY);
+ mreq.imr_ifindex = interface_idx;
+#else
+ mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
+#endif
if (setsockopt (fd, IPPROTO_IP, IP_MULTICAST_LOOP,
&loop, sizeof (loop)) == -1)
sizeof (errbuf)));
return (-1);
}
+
+ return (0);
}
}
else if (ai->ai_family == AF_INET6)
* single interface; programs running on
* multihomed hosts may need to join the same
* group on more than one interface.*/
- mreq.ipv6mr_interface = 0;
+ mreq.ipv6mr_interface = interface_idx;
if (setsockopt (fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&loop, sizeof (loop)) == -1)
sizeof (errbuf)));
return (-1);
}
+
+ return (0);
+ }
+ }
+
+#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
+ /* if a specific interface was set, bind the socket to it. But to avoid
+ * possible problems with multicast routing, only do that for non-multicast
+ * addresses */
+ if (interface_idx != 0)
+ {
+ char interface_name[IFNAMSIZ];
+
+ if (if_indextoname (interface_idx, interface_name) == NULL)
+ return (-1);
+
+ DEBUG ("fd = %i; Binding socket to interface %s", fd, interface_name);
+
+ if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE,
+ interface_name,
+ sizeof(interface_name)) == -1 )
+ {
+ char errbuf[1024];
+ ERROR ("setsockopt: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
}
}
+#endif /* HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
return (0);
} /* int network_bind_socket */
se->type = SOCKENT_TYPE_CLIENT;
se->node = NULL;
se->service = NULL;
+ se->interface = 0;
se->next = NULL;
if (type == SOCKENT_TYPE_SERVER)
continue;
}
- status = network_bind_socket (*tmp, ai_ptr);
+ status = network_bind_socket (*tmp, ai_ptr, se->interface);
if (status != 0)
{
close (*tmp);
se->data.client.addrlen = ai_ptr->ai_addrlen;
network_set_ttl (se, ai_ptr);
+ network_set_interface (se, ai_ptr);
/* We don't open more than one write-socket per
* node/service pair.. */
continue;
}
- parse_packet (se, ent->data, ent->data_len, /* flags = */ 0);
+ parse_packet (se, ent->data, ent->data_len, /* flags = */ 0,
+ /* username = */ NULL);
sfree (ent->data);
sfree (ent);
} /* while (42) */
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;
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;
return (0);
} /* }}} int network_config_set_ttl */
+static int network_config_set_interface (const oconfig_item_t *ci, /* {{{ */
+ int *interface)
+{
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("network plugin: The `Interface' config option needs exactly "
+ "one string argument.");
+ return (-1);
+ }
+
+ if (interface == NULL)
+ return (-1);
+
+ *interface = if_nametoindex (ci->values[0].value.string);
+
+ return (0);
+} /* }}} int network_config_set_interface */
+
static int network_config_set_buffer_size (const oconfig_item_t *ci) /* {{{ */
{
int tmp;
&se->data.server.security_level);
else
#endif /* HAVE_LIBGCRYPT */
+ if (strcasecmp ("Interface", child->key) == 0)
+ network_config_set_interface (child,
+ &se->interface);
+ else
{
WARNING ("network plugin: Option `%s' is not allowed here.",
child->key);
&se->data.client.security_level);
else
#endif /* HAVE_LIBGCRYPT */
+ if (strcasecmp ("Interface", child->key) == 0)
+ network_config_set_interface (child,
+ &se->interface);
+ else
{
WARNING ("network plugin: Option `%s' is not allowed here.",
child->key);
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.",
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);
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];
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 (&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 (&vl);
static int network_init (void)
{
- static _Bool have_init = false;
+ static _Bool have_init = 0;
/* Check if we were already initialized. If so, just return - there's
* nothing more to do (for now, that is). */
if (have_init)
return (0);
- have_init = true;
+ have_init = 1;
#if HAVE_LIBGCRYPT
gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
* 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);
#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
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",
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Sebastian Harl <sh at tokkee.org>
**/
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[] =
{
{
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);
}
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;
/**
* collectd - src/notify_email.c
* Copyright (C) 2008 Oleg King
+ * 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
*
* Authors:
* Oleg King <king2 at kaluga.ru>
+ * Florian Forster <octo at collectd.org>
**/
#include "collectd.h"
#include <auth-client.h>
#include <libesmtp.h>
+#include <pthread.h>
#define MAXSTRING 256
static int recipients_len = 0;
static smtp_session_t session;
+static pthread_mutex_t session_lock = PTHREAD_MUTEX_INITIALIZER;
static smtp_message_t message;
static auth_context_t authctx = NULL;
{
char server[MAXSTRING];
+ ssnprintf(server, sizeof (server), "%s:%i",
+ (smtp_host == NULL) ? DEFAULT_SMTP_HOST : smtp_host,
+ smtp_port);
+
+ pthread_mutex_lock (&session_lock);
+
auth_client_init();
- if (!(session = smtp_create_session ())) {
+
+ session = smtp_create_session ();
+ if (session == NULL) {
+ pthread_mutex_unlock (&session_lock);
ERROR ("notify_email plugin: cannot create SMTP session");
return (-1);
}
smtp_set_monitorcb (session, monitor_cb, NULL, 1);
smtp_set_hostname (session, hostname_g);
- ssnprintf(server, sizeof (server), "%s:%i",
- (smtp_host == NULL) ? DEFAULT_SMTP_HOST : smtp_host,
- smtp_port);
smtp_set_server (session, server);
if (smtp_user && smtp_password) {
}
if ( !smtp_auth_set_context (session, authctx)) {
+ pthread_mutex_unlock (&session_lock);
ERROR ("notify_email plugin: cannot set SMTP auth context");
return (-1);
}
+ pthread_mutex_unlock (&session_lock);
return (0);
} /* int notify_email_init */
static int notify_email_shutdown (void)
{
- smtp_destroy_session (session);
- auth_destroy_context (authctx);
+ pthread_mutex_lock (&session_lock);
+
+ if (session != NULL)
+ smtp_destroy_session (session);
+ session = NULL;
+
+ if (authctx != NULL)
+ auth_destroy_context (authctx);
+ authctx = NULL;
+
auth_client_exit();
+
+ pthread_mutex_unlock (&session_lock);
return (0);
} /* int notify_email_shutdown */
{
smtp_recipient_t recipient;
+ time_t tt;
struct tm timestamp_tm;
char timestamp_str[64];
(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';
n->host,
n->message);
+ pthread_mutex_lock (&session_lock);
+
+ if (session == NULL) {
+ /* Initialization failed or we're in the process of shutting down. */
+ pthread_mutex_unlock (&session_lock);
+ return (-1);
+ }
+
if (!(message = smtp_add_message (session))) {
+ pthread_mutex_unlock (&session_lock);
ERROR ("notify_email plugin: cannot set SMTP message");
return (-1);
}
char buf[MAXSTRING];
ERROR ("notify_email plugin: SMTP server problem: %s",
smtp_strerror (smtp_errno (), buf, sizeof buf));
+ pthread_mutex_unlock (&session_lock);
return (-1);
} else {
const smtp_status_t *status;
smtp_enumerate_recipients (message, print_recipient_status, NULL);
}
+ pthread_mutex_unlock (&session_lock);
return (0);
} /* int notify_email_notification */
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[] =
{
}
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);
}
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 ("onewire", cow_read,
- &cb_interval, /* user data = */ NULL);
+ plugin_register_complex_read (/* group = */ NULL, "onewire", cow_read,
+ (ow_interval != 0) ? &cb_interval : NULL,
+ /* user data = */ NULL);
plugin_register_shutdown ("onewire", cow_shutdown);
return (0);
/**
* collectd - src/openvpn.c
- * Copyright (C) 2008 Doug MacEachern
- * Copyright (C) 2009 Florian octo Forster
- * Copyright (C) 2009 Marco Chiappero
+ * 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
*
* Authors:
* Doug MacEachern <dougm at hyperic.com>
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Marco Chiappero <marco at absence.it>
+ * Fabian Schuh <mail at xeroc.org>
**/
#include "collectd.h"
static vpn_status_t **vpn_list = NULL;
static int vpn_num = 0;
-static int store_compression = 1;
-static int new_naming_schema = 0;
+static _Bool new_naming_schema = 0;
+static _Bool collect_compression = 1;
+static _Bool collect_user_count = 0;
+static _Bool collect_individual_users = 1;
static const char *config_keys[] =
{
"StatusFile",
- "Compression",
- "ImprovedNamingSchema"
+ "Compression", /* old, deprecated name */
+ "ImprovedNamingSchema",
+ "CollectCompression",
+ "CollectUserCount",
+ "CollectIndividualUsers"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
return (i);
} /* int openvpn_strsplit */
+/* dispatches number of users */
+static void numusers_submit (char *pinst, char *tinst, gauge_t value)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].gauge = value;
+
+ vl.values = values;
+ vl.values_len = STATIC_ARRAY_SIZE (values);
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "openvpn", sizeof (vl.plugin));
+ sstrncpy (vl.type, "users", sizeof (vl.type));
+ if (pinst != NULL)
+ sstrncpy (vl.plugin_instance, pinst, sizeof (vl.plugin_instance));
+ if (tinst != NULL)
+ sstrncpy (vl.type_instance, tinst, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+} /* 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
/* 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);
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;
iostats_submit (name, "overhead", overhead_rx, overhead_tx);
- if (store_compression)
+ if (collect_compression)
{
compression_submit (name, "data_in", post_decompress, pre_decompress);
compression_submit (name, "data_out", pre_compress, post_compress);
char buffer[1024];
char *fields[10];
int fields_num, read = 0, found_header = 0;
+ long long sum_users = 0;
/* read the file until the "ROUTING TABLE" line is found (no more info after) */
while (fgets (buffer, sizeof (buffer), fh) != NULL)
if (fields_num < 4)
continue;
- if (new_naming_schema)
+ if (collect_user_count)
+ /* If so, sum all users, ignore the individuals*/
{
- iostats_submit (name, /* vpn instance */
- fields[0], /* "Common Name" */
- atoll (fields[2]), /* "Bytes Received" */
- atoll (fields[3])); /* "Bytes Sent" */
+ sum_users += 1;
}
- else
+ if (collect_individual_users)
{
- iostats_submit (fields[0], /* "Common Name" */
- NULL, /* unused when in multimode */
- atoll (fields[2]), /* "Bytes Received" */
- atoll (fields[3])); /* "Bytes Sent" */
+ if (new_naming_schema)
+ {
+ iostats_submit (name, /* vpn instance */
+ fields[0], /* "Common Name" */
+ atoll (fields[2]), /* "Bytes Received" */
+ atoll (fields[3])); /* "Bytes Sent" */
+ }
+ else
+ {
+ iostats_submit (fields[0], /* "Common Name" */
+ NULL, /* unused when in multimode */
+ atoll (fields[2]), /* "Bytes Received" */
+ atoll (fields[3])); /* "Bytes Sent" */
+ }
}
read = 1;
}
+ if (collect_user_count)
+ {
+ numusers_submit(name, name, sum_users);
+ read = 1;
+ }
+
return (read);
} /* int multi1_read */
char *fields[10];
const int max_fields = STATIC_ARRAY_SIZE (fields);
int fields_num, read = 0;
+ long long sum_users = 0;
while (fgets (buffer, sizeof (buffer), fh) != NULL)
{
if (strcmp (fields[0], "CLIENT_LIST") != 0)
continue;
- if (new_naming_schema)
+ if (collect_user_count)
+ /* If so, sum all users, ignore the individuals*/
{
- /* plugin inst = file name, type inst = fields[1] */
- iostats_submit (name, /* vpn instance */
- fields[1], /* "Common Name" */
- atoll (fields[4]), /* "Bytes Received" */
- atoll (fields[5])); /* "Bytes Sent" */
+ sum_users += 1;
}
- else
+ if (collect_individual_users)
{
- /* plugin inst = fields[1], type inst = "" */
- iostats_submit (fields[1], /* "Common Name" */
- NULL, /* unused when in multimode */
- atoll (fields[4]), /* "Bytes Received" */
- atoll (fields[5])); /* "Bytes Sent" */
+ if (new_naming_schema)
+ {
+ /* plugin inst = file name, type inst = fields[1] */
+ iostats_submit (name, /* vpn instance */
+ fields[1], /* "Common Name" */
+ atoll (fields[4]), /* "Bytes Received" */
+ atoll (fields[5])); /* "Bytes Sent" */
+ }
+ else
+ {
+ /* plugin inst = fields[1], type inst = "" */
+ iostats_submit (fields[1], /* "Common Name" */
+ NULL, /* unused when in multimode */
+ atoll (fields[4]), /* "Bytes Received" */
+ atoll (fields[5])); /* "Bytes Sent" */
+ }
}
read = 1;
}
+ if (collect_user_count)
+ {
+ numusers_submit(name, name, sum_users);
+ read = 1;
+ }
+
return (read);
} /* int multi2_read */
char *fields[15];
const int max_fields = STATIC_ARRAY_SIZE (fields);
int fields_num, read = 0;
+ long long sum_users = 0;
while (fgets (buffer, sizeof (buffer), fh) != NULL)
{
if (strcmp (fields[0], "CLIENT_LIST") != 0)
continue;
- if (new_naming_schema)
+ if (collect_user_count)
+ /* If so, sum all users, ignore the individuals*/
{
- iostats_submit (name, /* vpn instance */
- fields[1], /* "Common Name" */
- atoll (fields[4]), /* "Bytes Received" */
- atoll (fields[5])); /* "Bytes Sent" */
+ sum_users += 1;
}
- else
+
+ if (collect_individual_users)
{
- iostats_submit (fields[1], /* "Common Name" */
- NULL, /* unused when in multimode */
- atoll (fields[4]), /* "Bytes Received" */
- atoll (fields[5])); /* "Bytes Sent" */
+ if (new_naming_schema)
+ {
+ iostats_submit (name, /* vpn instance */
+ fields[1], /* "Common Name" */
+ atoll (fields[4]), /* "Bytes Received" */
+ atoll (fields[5])); /* "Bytes Sent" */
+ }
+ else
+ {
+ iostats_submit (fields[1], /* "Common Name" */
+ NULL, /* unused when in multimode */
+ atoll (fields[4]), /* "Bytes Received" */
+ atoll (fields[5])); /* "Bytes Sent" */
+ }
}
read = 1;
}
}
+ if (collect_user_count)
+ {
+ numusers_submit(name, name, sum_users);
+ read = 1;
+ }
+
return (read);
} /* int multi3_read */
{
char errbuf[1024];
WARNING ("openvpn plugin: fopen(%s) failed: %s", vpn_list[i]->file,
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
continue;
}
if (status_version == 0)
{
WARNING ("openvpn plugin: unable to detect status version, \
- discarding status file \"%s\".", value);
+ discarding status file \"%s\".", value);
return (1);
}
{
char errbuf[1024];
WARNING ("openvpn plugin: sstrdup failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
return (1);
}
{
char errbuf[1024];
ERROR ("openvpn plugin: malloc failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
sfree (temp->file);
sfree (temp);
DEBUG ("openvpn plugin: status file \"%s\" added", temp->file);
} /* if (strcasecmp ("StatusFile", key) == 0) */
- else if (strcasecmp ("Compression", key) == 0)
+ else if ((strcasecmp ("CollectCompression", key) == 0)
+ || (strcasecmp ("Compression", key) == 0)) /* old, deprecated name */
{
- if (IS_TRUE (value))
- store_compression = 1;
+ if (IS_FALSE (value))
+ collect_compression = 0;
else
- {
- store_compression = 0;
- DEBUG ("openvpn plugin: no 'compression statistcs' collected");
- }
- } /* if (strcasecmp ("Compression", key) == 0) */
+ collect_compression = 1;
+ } /* if (strcasecmp ("CollectCompression", key) == 0) */
else if (strcasecmp ("ImprovedNamingSchema", key) == 0)
{
if (IS_TRUE (value))
new_naming_schema = 0;
}
} /* if (strcasecmp ("ImprovedNamingSchema", key) == 0) */
+ else if (strcasecmp("CollectUserCount", key) == 0)
+ {
+ if (IS_TRUE(value))
+ collect_user_count = 1;
+ else
+ collect_user_count = 0;
+ } /* if (strcasecmp("CollectUserCount", key) == 0) */
+ else if (strcasecmp("CollectIndividualUsers", key) == 0)
+ {
+ if (IS_FALSE (value))
+ collect_individual_users = 0;
+ else
+ collect_individual_users = 1;
+ } /* if (strcasecmp("CollectIndividualUsers", key) == 0) */
else
{
return (-1);
return (0);
} /* int openvpn_shutdown */
+static int openvpn_init (void)
+{
+ if (!collect_individual_users
+ && !collect_compression
+ && !collect_user_count)
+ {
+ WARNING ("OpenVPN plugin: Neither `CollectIndividualUsers', "
+ "`CollectCompression', nor `CollectUserCount' is true. There's no "
+ "data left to collect.");
+ return (-1);
+ }
+
+ plugin_register_read ("openvpn", openvpn_read);
+ plugin_register_shutdown ("openvpn", openvpn_shutdown);
+
+ return (0);
+} /* int openvpn_init */
+
void module_register (void)
{
plugin_register_config ("openvpn", openvpn_config,
config_keys, config_keys_num);
- plugin_register_read ("openvpn", openvpn_read);
- plugin_register_shutdown ("openvpn", openvpn_shutdown);
+ plugin_register_init ("openvpn", openvpn_init);
} /* void module_register */
/* vim: set sw=2 ts=2 : */
char *username;
char *password;
+ udb_query_preparation_area_t **q_prep_areas;
udb_query_t **queries;
size_t queries_num;
static void o_database_free (o_database_t *db) /* {{{ */
{
+ size_t i;
+
if (db == NULL)
return;
sfree (db->password);
sfree (db->queries);
+ if (db->q_prep_areas != NULL)
+ for (i = 0; i < db->queries_num; ++i)
+ udb_query_delete_preparation_area (db->q_prep_areas[i]);
+ free (db->q_prep_areas);
+
sfree (db);
} /* }}} void o_database_free */
break;
} /* while (status == 0) */
+ while ((status == 0) && (db->queries_num > 0))
+ {
+ db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+ db->queries_num, sizeof (*db->q_prep_areas));
+
+ if (db->q_prep_areas == NULL)
+ {
+ WARNING ("oracle plugin: malloc failed");
+ status = -1;
+ break;
+ }
+
+ for (i = 0; i < db->queries_num; ++i)
+ {
+ db->q_prep_areas[i]
+ = udb_query_allocate_preparation_area (db->queries[i]);
+
+ if (db->q_prep_areas[i] == NULL)
+ {
+ WARNING ("oracle plugin: udb_query_allocate_preparation_area failed");
+ status = -1;
+ break;
+ }
+ }
+
+ break;
+ }
+
/* If all went well, add this query to the list of queries within the
* database structure. */
if (status == 0)
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
} /* }}} int o_init */
static int o_read_database_query (o_database_t *db, /* {{{ */
- udb_query_t *q)
+ udb_query_t *q, udb_query_preparation_area_t *prep_area)
{
char **column_names;
char **column_values;
} /* for (j = 1; j <= param_counter; j++) */
/* }}} End of the ``define'' stuff. */
- status = udb_query_prepare_result (q, hostname_g, /* plugin = */ "oracle",
- db->name, column_names, column_num);
+ status = udb_query_prepare_result (q, prep_area, hostname_g,
+ /* plugin = */ "oracle", db->name, column_names, column_num,
+ /* interval = */ 0);
if (status != 0)
{
ERROR ("oracle plugin: o_read_database_query (%s, %s): "
break;
}
- status = udb_query_handle_result (q, column_values);
+ status = udb_query_handle_result (q, prep_area, column_values);
if (status != 0)
{
WARNING ("oracle plugin: o_read_database_query (%s, %s): "
db->connect_id, db->oci_service_context);
for (i = 0; i < db->queries_num; i++)
- o_read_database_query (db, db->queries[i]);
+ o_read_database_query (db, db->queries[i], db->q_prep_areas[i]);
return (0);
} /* }}} int o_read_database */
#include "configfile.h"
+#if HAVE_STDBOOL_H
+# include <stdbool.h>
+#endif
+
#include <EXTERN.h>
#include <perl.h>
{ "", NULL }
};
-struct {
- char name[64];
- int *var;
-} g_integers[] =
-{
- { "Collectd::interval_g", &interval_g },
- { "", NULL }
-};
-
/*
* Helper functions for data type conversion.
*/
}
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));
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));
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))
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))
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)));
}
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));
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;
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
, 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
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) */
--- /dev/null
+/**
+ * collectd - src/pinba.c (based on code from pinba_engine 0.0.5)
+ * Copyright (c) 2007-2009 Antony Dovgal
+ * Copyright (C) 2010 Phoenix Kayo
+ * 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:
+ * Antony Dovgal <tony at daylessday.org>
+ * Phoenix Kayo <kayo.k11.4 at gmail.com>
+ * Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <pthread.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <poll.h>
+
+#include "pinba.pb-c.h"
+
+/*
+ * Defines
+ */
+#ifndef PINBA_UDP_BUFFER_SIZE
+# define PINBA_UDP_BUFFER_SIZE 65536
+#endif
+
+#ifndef PINBA_DEFAULT_NODE
+# define PINBA_DEFAULT_NODE "::0"
+#endif
+
+#ifndef PINBA_DEFAULT_SERVICE
+# define PINBA_DEFAULT_SERVICE "30002"
+#endif
+
+#ifndef PINBA_MAX_SOCKETS
+# define PINBA_MAX_SOCKETS 16
+#endif
+
+/*
+ * Private data structures
+ */
+/* {{{ */
+struct pinba_socket_s
+{
+ struct pollfd fd[PINBA_MAX_SOCKETS];
+ nfds_t fd_num;
+};
+typedef struct pinba_socket_s pinba_socket_t;
+
+/* Fixed point counter value. n is the decimal part multiplied by 10^9. */
+struct float_counter_s
+{
+ uint64_t i;
+ uint64_t n; /* nanos */
+};
+typedef struct float_counter_s float_counter_t;
+
+struct pinba_statnode_s
+{
+ /* collector name, used as plugin instance */
+ char *name;
+
+ /* query data */
+ char *host;
+ char *server;
+ char *script;
+
+ derive_t req_count;
+
+ float_counter_t req_time;
+ float_counter_t ru_utime;
+ float_counter_t ru_stime;
+
+ derive_t doc_size;
+ gauge_t mem_peak;
+};
+typedef struct pinba_statnode_s pinba_statnode_t;
+/* }}} */
+
+/*
+ * Module global variables
+ */
+/* {{{ */
+static pinba_statnode_t *stat_nodes = NULL;
+static unsigned int stat_nodes_num = 0;
+static pthread_mutex_t stat_nodes_lock;
+
+static char *conf_node = NULL;
+static char *conf_service = NULL;
+
+static _Bool collector_thread_running = 0;
+static _Bool collector_thread_do_shutdown = 0;
+static pthread_t collector_thread_id;
+/* }}} */
+
+/*
+ * Functions
+ */
+static void float_counter_add (float_counter_t *fc, float val) /* {{{ */
+{
+ uint64_t tmp;
+
+ if (val < 0.0)
+ return;
+
+ tmp = (uint64_t) val;
+ val -= (double) tmp;
+
+ fc->i += tmp;
+ fc->n += (uint64_t) ((val * 1000000000.0) + .5);
+
+ if (fc->n >= 1000000000)
+ {
+ fc->i += 1;
+ fc->n -= 1000000000;
+ assert (fc->n < 1000000000);
+ }
+} /* }}} void float_counter_add */
+
+static derive_t float_counter_get (const float_counter_t *fc, /* {{{ */
+ uint64_t factor)
+{
+ derive_t ret;
+
+ ret = (derive_t) (fc->i * factor);
+ ret += (derive_t) (fc->n / (1000000000 / factor));
+
+ return (ret);
+} /* }}} derive_t float_counter_get */
+
+static void strset (char **str, const char *new) /* {{{ */
+{
+ char *tmp;
+
+ if (!str || !new)
+ return;
+
+ tmp = strdup (new);
+ if (tmp == NULL)
+ return;
+
+ sfree (*str);
+ *str = tmp;
+} /* }}} void strset */
+
+static void service_statnode_add(const char *name, /* {{{ */
+ const char *host,
+ const char *server,
+ const char *script)
+{
+ pinba_statnode_t *node;
+
+ node = realloc (stat_nodes,
+ sizeof (*stat_nodes) * (stat_nodes_num + 1));
+ if (node == NULL)
+ {
+ ERROR ("pinba plugin: realloc failed");
+ return;
+ }
+ stat_nodes = node;
+
+ node = stat_nodes + stat_nodes_num;
+ memset (node, 0, sizeof (*node));
+
+ /* reset strings */
+ node->name = NULL;
+ node->host = NULL;
+ node->server = NULL;
+ node->script = NULL;
+
+ node->mem_peak = NAN;
+
+ /* fill query data */
+ strset (&node->name, name);
+ strset (&node->host, host);
+ strset (&node->server, server);
+ strset (&node->script, script);
+
+ /* increment counter */
+ stat_nodes_num++;
+} /* }}} void service_statnode_add */
+
+/* Copy the data from the global "stat_nodes" list into the buffer pointed to
+ * by "res", doing the derivation in the process. Returns the next index or
+ * zero if the end of the list has been reached. */
+static unsigned int service_statnode_collect (pinba_statnode_t *res, /* {{{ */
+ unsigned int index)
+{
+ pinba_statnode_t *node;
+
+ if (stat_nodes_num == 0)
+ return 0;
+
+ /* begin collecting */
+ if (index == 0)
+ pthread_mutex_lock (&stat_nodes_lock);
+
+ /* end collecting */
+ if (index >= stat_nodes_num)
+ {
+ pthread_mutex_unlock (&stat_nodes_lock);
+ return 0;
+ }
+
+ node = stat_nodes + index;
+ memcpy (res, node, sizeof (*res));
+
+ /* reset node */
+ node->mem_peak = NAN;
+
+ return (index + 1);
+} /* }}} unsigned int service_statnode_collect */
+
+static void service_statnode_process (pinba_statnode_t *node, /* {{{ */
+ Pinba__Request* request)
+{
+ node->req_count++;
+
+ float_counter_add (&node->req_time, request->request_time);
+ float_counter_add (&node->ru_utime, request->ru_utime);
+ float_counter_add (&node->ru_stime, request->ru_stime);
+
+ node->doc_size += request->document_size;
+
+ if (isnan (node->mem_peak)
+ || (node->mem_peak < ((gauge_t) request->memory_peak)))
+ node->mem_peak = (gauge_t) request->memory_peak;
+
+} /* }}} void service_statnode_process */
+
+static void service_process_request (Pinba__Request *request) /* {{{ */
+{
+ unsigned int i;
+
+ pthread_mutex_lock (&stat_nodes_lock);
+
+ for (i = 0; i < stat_nodes_num; i++)
+ {
+ if ((stat_nodes[i].host != NULL)
+ && (strcmp (request->hostname, stat_nodes[i].host) != 0))
+ continue;
+
+ if ((stat_nodes[i].server != NULL)
+ && (strcmp (request->server_name, stat_nodes[i].server) != 0))
+ continue;
+
+ if ((stat_nodes[i].script != NULL)
+ && (strcmp (request->script_name, stat_nodes[i].script) != 0))
+ continue;
+
+ service_statnode_process(&stat_nodes[i], request);
+ }
+
+ pthread_mutex_unlock(&stat_nodes_lock);
+} /* }}} void service_process_request */
+
+static int pb_del_socket (pinba_socket_t *s, /* {{{ */
+ nfds_t index)
+{
+ if (index >= s->fd_num)
+ return (EINVAL);
+
+ close (s->fd[index].fd);
+ s->fd[index].fd = -1;
+
+ /* When deleting the last element in the list, no memmove is necessary. */
+ if (index < (s->fd_num - 1))
+ {
+ memmove (&s->fd[index], &s->fd[index + 1],
+ sizeof (s->fd[0]) * (s->fd_num - (index + 1)));
+ }
+
+ s->fd_num--;
+ return (0);
+} /* }}} int pb_del_socket */
+
+static int pb_add_socket (pinba_socket_t *s, /* {{{ */
+ const struct addrinfo *ai)
+{
+ int fd;
+ int tmp;
+ int status;
+
+ if (s->fd_num == PINBA_MAX_SOCKETS)
+ {
+ WARNING ("pinba plugin: Sorry, you have hit the built-in limit of "
+ "%i sockets. Please complain to the collectd developers so we can "
+ "raise the limit.", PINBA_MAX_SOCKETS);
+ return (-1);
+ }
+
+ fd = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (fd < 0)
+ {
+ char errbuf[1024];
+ ERROR ("pinba plugin: socket(2) failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (0);
+ }
+
+ tmp = 1;
+ status = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof (tmp));
+ if (status != 0)
+ {
+ char errbuf[1024];
+ WARNING ("pinba plugin: setsockopt(SO_REUSEADDR) failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ }
+
+ status = bind (fd, ai->ai_addr, ai->ai_addrlen);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("pinba plugin: bind(2) failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (0);
+ }
+
+ s->fd[s->fd_num].fd = fd;
+ s->fd[s->fd_num].events = POLLIN | POLLPRI;
+ s->fd[s->fd_num].revents = 0;
+ s->fd_num++;
+
+ return (0);
+} /* }}} int pb_add_socket */
+
+static pinba_socket_t *pinba_socket_open (const char *node, /* {{{ */
+ const char *service)
+{
+ pinba_socket_t *s;
+ struct addrinfo *ai_list;
+ struct addrinfo *ai_ptr;
+ struct addrinfo ai_hints;
+ int status;
+
+ memset (&ai_hints, 0, sizeof (ai_hints));
+ ai_hints.ai_flags = AI_PASSIVE;
+ ai_hints.ai_family = AF_UNSPEC;
+ ai_hints.ai_socktype = SOCK_DGRAM;
+ ai_hints.ai_addr = NULL;
+ ai_hints.ai_canonname = NULL;
+ ai_hints.ai_next = NULL;
+
+ if (node == NULL)
+ node = PINBA_DEFAULT_NODE;
+
+ if (service == NULL)
+ service = PINBA_DEFAULT_SERVICE;
+
+ ai_list = NULL;
+ status = getaddrinfo (node, service,
+ &ai_hints, &ai_list);
+ if (status != 0)
+ {
+ ERROR ("pinba plugin: getaddrinfo(3) failed: %s",
+ gai_strerror (status));
+ return (NULL);
+ }
+ assert (ai_list != NULL);
+
+ s = malloc (sizeof (*s));
+ if (s == NULL)
+ {
+ freeaddrinfo (ai_list);
+ ERROR ("pinba plugin: malloc failed.");
+ return (NULL);
+ }
+ memset (s, 0, sizeof (*s));
+
+ for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+ {
+ status = pb_add_socket (s, ai_ptr);
+ if (status != 0)
+ break;
+ } /* for (ai_list) */
+
+ freeaddrinfo (ai_list);
+
+ if (s->fd_num < 1)
+ {
+ WARNING ("pinba plugin: Unable to open socket for address %s.", node);
+ sfree (s);
+ s = NULL;
+ }
+
+ return (s);
+} /* }}} pinba_socket_open */
+
+static void pinba_socket_free (pinba_socket_t *socket) /* {{{ */
+{
+ nfds_t i;
+
+ if (!socket)
+ return;
+
+ for (i = 0; i < socket->fd_num; i++)
+ {
+ if (socket->fd[i].fd < 0)
+ continue;
+ close (socket->fd[i].fd);
+ socket->fd[i].fd = -1;
+ }
+
+ sfree(socket);
+} /* }}} void pinba_socket_free */
+
+static int pinba_process_stats_packet (const uint8_t *buffer, /* {{{ */
+ size_t buffer_size)
+{
+ Pinba__Request *request;
+
+ request = pinba__request__unpack (NULL, buffer_size, buffer);
+
+ if (!request)
+ return (-1);
+
+ service_process_request(request);
+ pinba__request__free_unpacked (request, NULL);
+
+ return (0);
+} /* }}} int pinba_process_stats_packet */
+
+static int pinba_udp_read_callback_fn (int sock) /* {{{ */
+{
+ uint8_t buffer[PINBA_UDP_BUFFER_SIZE];
+ size_t buffer_size;
+ int status;
+
+ while (42)
+ {
+ buffer_size = sizeof (buffer);
+ status = recvfrom (sock, buffer, buffer_size - 1, MSG_DONTWAIT, /* from = */ NULL, /* from len = */ 0);
+ if (status < 0)
+ {
+ char errbuf[1024];
+
+ if ((errno == EINTR)
+#ifdef EWOULDBLOCK
+ || (errno == EWOULDBLOCK)
+#endif
+ || (errno == EAGAIN))
+ {
+ continue;
+ }
+
+ WARNING("pinba plugin: recvfrom(2) failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ else if (status == 0)
+ {
+ DEBUG ("pinba plugin: recvfrom(2) returned unexpected status zero.");
+ return (-1);
+ }
+ else /* if (status > 0) */
+ {
+ assert (((size_t) status) < buffer_size);
+ buffer_size = (size_t) status;
+ buffer[buffer_size] = 0;
+
+ status = pinba_process_stats_packet (buffer, buffer_size);
+ if (status != 0)
+ DEBUG("pinba plugin: Parsing packet failed.");
+ return (status);
+ }
+ } /* while (42) */
+
+ /* not reached */
+ assert (23 == 42);
+ return (-1);
+} /* }}} void pinba_udp_read_callback_fn */
+
+static int receive_loop (void) /* {{{ */
+{
+ pinba_socket_t *s;
+
+ s = pinba_socket_open (conf_node, conf_service);
+ if (s == NULL)
+ {
+ ERROR ("pinba plugin: Collector thread is exiting prematurely.");
+ return (-1);
+ }
+
+ while (!collector_thread_do_shutdown)
+ {
+ int status;
+ nfds_t i;
+
+ if (s->fd_num < 1)
+ break;
+
+ status = poll (s->fd, s->fd_num, /* timeout = */ 1000);
+ if (status == 0) /* timeout */
+ {
+ continue;
+ }
+ else if (status < 0)
+ {
+ char errbuf[1024];
+
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+
+ ERROR ("pinba plugin: poll(2) failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ pinba_socket_free (s);
+ return (-1);
+ }
+
+ for (i = 0; i < s->fd_num; i++)
+ {
+ if (s->fd[i].revents & (POLLERR | POLLHUP | POLLNVAL))
+ {
+ pb_del_socket (s, i);
+ i--;
+ }
+ else if (s->fd[i].revents & (POLLIN | POLLPRI))
+ {
+ pinba_udp_read_callback_fn (s->fd[i].fd);
+ }
+ } /* for (s->fd) */
+ } /* while (!collector_thread_do_shutdown) */
+
+ pinba_socket_free (s);
+ s = NULL;
+
+ return (0);
+} /* }}} int receive_loop */
+
+static void *collector_thread (void *arg) /* {{{ */
+{
+ receive_loop ();
+
+ memset (&collector_thread_id, 0, sizeof (collector_thread_id));
+ collector_thread_running = 0;
+ pthread_exit (NULL);
+ return (NULL);
+} /* }}} void *collector_thread */
+
+/*
+ * Plugin declaration section
+ */
+static int pinba_config_view (const oconfig_item_t *ci) /* {{{ */
+{
+ char *name = NULL;
+ char *host = NULL;
+ char *server = NULL;
+ char *script = NULL;
+ int status;
+ int i;
+
+ status = cf_util_get_string (ci, &name);
+ if (status != 0)
+ 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, &host);
+ else if (strcasecmp ("Server", child->key) == 0)
+ status = cf_util_get_string (child, &server);
+ else if (strcasecmp ("Script", child->key) == 0)
+ status = cf_util_get_string (child, &script);
+ else
+ {
+ WARNING ("pinba plugin: Unknown config option: %s", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if (status == 0)
+ service_statnode_add (name, host, server, script);
+
+ sfree (name);
+ sfree (host);
+ sfree (server);
+ sfree (script);
+
+ return (status);
+} /* }}} int pinba_config_view */
+
+static int plugin_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ /* The lock should not be necessary in the config callback, but let's be
+ * sure.. */
+ pthread_mutex_lock (&stat_nodes_lock);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Address", child->key) == 0)
+ cf_util_get_string (child, &conf_node);
+ else if (strcasecmp ("Port", child->key) == 0)
+ cf_util_get_string (child, &conf_service);
+ else if (strcasecmp ("View", child->key) == 0)
+ pinba_config_view (child);
+ else
+ WARNING ("pinba plugin: Unknown config option: %s", child->key);
+ }
+
+ pthread_mutex_unlock(&stat_nodes_lock);
+
+ return (0);
+} /* }}} int pinba_config */
+
+static int plugin_init (void) /* {{{ */
+{
+ int status;
+
+ if (stat_nodes == NULL)
+ {
+ /* Collect the "total" data by default. */
+ service_statnode_add ("total",
+ /* host = */ NULL,
+ /* server = */ NULL,
+ /* script = */ NULL);
+ }
+
+ if (collector_thread_running)
+ return (0);
+
+ status = pthread_create (&collector_thread_id,
+ /* attrs = */ NULL,
+ collector_thread,
+ /* args = */ NULL);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("pinba plugin: pthread_create(3) failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ collector_thread_running = 1;
+
+ return (0);
+} /* }}} */
+
+static int plugin_shutdown (void) /* {{{ */
+{
+ if (collector_thread_running)
+ {
+ int status;
+
+ DEBUG ("pinba plugin: Shutting down collector thread.");
+ collector_thread_do_shutdown = 1;
+
+ status = pthread_join (collector_thread_id, /* retval = */ NULL);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("pinba plugin: pthread_join(3) failed: %s",
+ sstrerror (status, errbuf, sizeof (errbuf)));
+ }
+
+ collector_thread_running = 0;
+ collector_thread_do_shutdown = 0;
+ } /* if (collector_thread_running) */
+
+ return (0);
+} /* }}} int plugin_shutdown */
+
+static int plugin_submit (const pinba_statnode_t *res) /* {{{ */
+{
+ 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, "pinba", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, res->name, sizeof (vl.plugin_instance));
+
+ value.derive = res->req_count;
+ sstrncpy (vl.type, "total_requests", sizeof (vl.type));
+ plugin_dispatch_values (&vl);
+
+ value.derive = float_counter_get (&res->req_time, /* factor = */ 1000);
+ sstrncpy (vl.type, "total_time_in_ms", sizeof (vl.type));
+ plugin_dispatch_values (&vl);
+
+ value.derive = res->doc_size;
+ sstrncpy (vl.type, "total_bytes", sizeof (vl.type));
+ plugin_dispatch_values (&vl);
+
+ value.derive = float_counter_get (&res->ru_utime, /* factor = */ 100);
+ sstrncpy (vl.type, "cpu", sizeof (vl.type));
+ sstrncpy (vl.type_instance, "user", sizeof (vl.type_instance));
+ plugin_dispatch_values (&vl);
+
+ value.derive = float_counter_get (&res->ru_stime, /* factor = */ 100);
+ sstrncpy (vl.type, "cpu", sizeof (vl.type));
+ sstrncpy (vl.type_instance, "system", sizeof (vl.type_instance));
+ plugin_dispatch_values (&vl);
+
+ value.gauge = res->mem_peak;
+ sstrncpy (vl.type, "memory", sizeof (vl.type));
+ sstrncpy (vl.type_instance, "peak", sizeof (vl.type_instance));
+ plugin_dispatch_values (&vl);
+
+ return (0);
+} /* }}} int plugin_submit */
+
+static int plugin_read (void) /* {{{ */
+{
+ unsigned int i=0;
+ pinba_statnode_t data;
+
+ while ((i = service_statnode_collect (&data, i)) != 0)
+ {
+ plugin_submit (&data);
+ }
+
+ return 0;
+} /* }}} int plugin_read */
+
+void module_register (void) /* {{{ */
+{
+ plugin_register_complex_config ("pinba", plugin_config);
+ plugin_register_init ("pinba", plugin_init);
+ plugin_register_read ("pinba", plugin_read);
+ plugin_register_shutdown ("pinba", plugin_shutdown);
+} /* }}} void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
--- /dev/null
+package Pinba;
+option optimize_for = SPEED;
+
+message Request {
+ required string hostname = 1;
+ required string server_name = 2;
+ required string script_name = 3;
+ required uint32 request_count = 4;
+ required uint32 document_size = 5;
+ required uint32 memory_peak = 6;
+ required float request_time = 7;
+ required float ru_utime = 8;
+ required float ru_stime = 9;
+
+ repeated uint32 timer_hit_count = 10;
+ repeated float timer_value = 11;
+ repeated uint32 timer_tag_count = 12;
+ repeated uint32 timer_tag_name = 13;
+ repeated uint32 timer_tag_value = 14;
+ repeated string dictionary = 15;
+ optional uint32 status = 16;
+}
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Sebastian Harl <sh at tokkee.org>
**/
#include "utils_llist.h"
#include "utils_heap.h"
#include "utils_cache.h"
-#include "utils_threshold.h"
#include "filter_chain.h"
/*
#define rf_callback rf_super.cf_callback
#define rf_udata rf_super.cf_udata
callback_func_t rf_super;
+ char rf_group[DATA_MAX_NAME_LEN];
char rf_name[DATA_MAX_NAME_LEN];
int rf_type;
struct timespec rf_interval;
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;
while (read_loop != 0)
{
read_func_t *rf;
- struct timeval now;
+ cdtime_t now;
int status;
int rf_type;
int rc;
{
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,
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,
* we need to re-evaluate the condition every time
* pthread_cond_timedwait returns. */
rc = 0;
- while (!timeout_reached(rf->rf_next_read) && rc == 0) {
+ while ((read_loop != 0)
+ && !timeout_reached(rf->rf_next_read)
+ && rc == 0)
+ {
rc = pthread_cond_timedwait (&read_cond, &read_lock,
&rf->rf_next_read);
}
- /* Must hold `real_lock' when accessing `rf->rf_type'. */
+ /* Must hold `read_lock' when accessing `rf->rf_type'. */
rf_type = rf->rf_type;
pthread_mutex_unlock (&read_lock);
}
/* 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.",
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.",
}
}
+ 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)
{
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));
rf->rf_callback = (void *) callback;
rf->rf_udata.data = NULL;
rf->rf_udata.free_func = NULL;
+ rf->rf_group[0] = '\0';
sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
rf->rf_type = RF_SIMPLE;
rf->rf_interval.tv_sec = 0;
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 *name,
+int plugin_register_complex_read (const char *group, const char *name,
plugin_read_cb callback,
const struct timespec *interval,
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));
rf->rf_callback = (void *) callback;
+ if (group != NULL)
+ sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
+ else
+ rf->rf_group[0] = '\0';
sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
rf->rf_type = RF_COMPLEX;
if (interval != NULL)
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,
(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 (char *name,
int (*callback) (void))
{
return (0);
} /* }}} int plugin_unregister_read */
+static int compare_read_func_group (llentry_t *e, void *ud) /* {{{ */
+{
+ read_func_t *rf = e->value;
+ char *group = ud;
+
+ return strcmp (rf->rf_group, (const char *)group);
+} /* }}} int compare_read_func_group */
+
+int plugin_unregister_read_group (const char *group) /* {{{ */
+{
+ llentry_t *le;
+ read_func_t *rf;
+
+ int found = 0;
+
+ if (group == NULL)
+ return (-ENOENT);
+
+ pthread_mutex_lock (&read_lock);
+
+ if (read_list == NULL)
+ {
+ pthread_mutex_unlock (&read_lock);
+ return (-ENOENT);
+ }
+
+ while (42)
+ {
+ le = llist_search_custom (read_list,
+ compare_read_func_group, (void *)group);
+
+ if (le == NULL)
+ break;
+
+ ++found;
+
+ llist_remove (read_list, le);
+
+ rf = le->value;
+ assert (rf != NULL);
+ rf->rf_type = RF_REMOVE;
+
+ llentry_destroy (le);
+
+ DEBUG ("plugin_unregister_read_group: "
+ "Marked `%s' (group `%s') for removal.",
+ rf->rf_name, group);
+ }
+
+ pthread_mutex_unlock (&read_lock);
+
+ if (found == 0)
+ {
+ WARNING ("plugin_unregister_read_group: No such "
+ "group of read function: %s", group);
+ return (-ENOENT);
+ }
+
+ return (0);
+} /* }}} int plugin_unregister_read_group */
+
int plugin_unregister_write (const char *name)
{
return (plugin_unregister (list_write, 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));
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;
destroy_read_heap ();
- plugin_flush (/* plugin = */ NULL, /* timeout = */ -1,
+ plugin_flush (/* plugin = */ NULL,
+ /* timeout = */ 0,
/* identifier = */ NULL);
le = NULL;
* 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);
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;
if (c_avl_get (data_sets, vl->type, (void *) &ds) != 0)
{
- INFO ("plugin_dispatch_values: Dataset not found: %s", vl->type);
+ char ident[6 * DATA_MAX_NAME_LEN];
+
+ FORMAT_VL (ident, sizeof (ident), vl);
+ INFO ("plugin_dispatch_values: Dataset not found: %s "
+ "(from \"%s\"), check your types.db!",
+ vl->type, ident);
return (-1);
}
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);
/* 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);
/* 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)
va_list ap;
llentry_t *le;
- if (list_log == NULL)
- {
- va_start (ap, format);
- vfprintf (stderr, format, ap);
- va_end (ap);
- return;
- }
-
#if !COLLECT_DEBUG
if (level >= LOG_DEBUG)
return;
msg[sizeof (msg) - 1] = '\0';
va_end (ap);
+ if (list_log == NULL)
+ {
+ fprintf (stderr, "%s\n", msg);
+ return;
+ }
+
le = llist_head (list_log);
while (le != NULL)
{
}
case NM_TYPE_BOOLEAN:
{
- meta->nm_value.nm_boolean = *((bool *) value);
+ meta->nm_value.nm_boolean = *((_Bool *) value);
break;
}
default:
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));
}
#define PLUGIN_H
/**
* collectd - src/plugin.h
- * Copyright (C) 2005-2008 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Sebastian Harl <sh at tokkee.org>
**/
#include "collectd.h"
#include "configfile.h"
#include "meta_data.h"
+#include "utils_time.h"
#define PLUGIN_FLAGS_GLOBAL 0x0001
{
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];
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;
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];
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);
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',
plugin_init_cb callback);
int plugin_register_read (const char *name,
int (*callback) (void));
-int plugin_register_complex_read (const char *name,
+/* "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,
user_data_t *user_data);
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 (char *name,
plugin_shutdown_cb callback);
int plugin_register_data_set (const data_set_t *ds);
int plugin_unregister_complex_config (const char *name);
int plugin_unregister_init (const char *name);
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);
* function.
*/
int plugin_dispatch_values (value_list_t *vl);
+int plugin_dispatch_missing (const value_list_t *vl);
int plugin_dispatch_notification (const notification_t *notif);
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);
* collectd - src/postgresql.c
* Copyright (C) 2008, 2009 Sebastian Harl
* Copyright (C) 2009 Florian Forster
+ * All rights reserved.
*
- * 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * 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.
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
*
- * 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
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*
* Authors:
* Sebastian Harl <sh at tokkee.org>
int max_params_num;
/* user configuration */
+ udb_query_preparation_area_t **q_prep_areas;
udb_query_t **queries;
size_t queries_num;
+ cdtime_t interval;
+
char *host;
char *port;
char *database;
static udb_query_t **queries = NULL;
static size_t queries_num = 0;
-static c_psql_database_t *databases = NULL;
-static int databases_num = 0;
-
static c_psql_database_t *c_psql_database_new (const char *name)
{
c_psql_database_t *db;
- ++databases_num;
- if (NULL == (databases = (c_psql_database_t *)realloc (databases,
- databases_num * sizeof (*databases)))) {
+ db = (c_psql_database_t *)malloc (sizeof (*db));
+ if (NULL == db) {
log_err ("Out of memory.");
- exit (5);
+ return NULL;
}
- db = databases + (databases_num - 1);
-
db->conn = NULL;
C_COMPLAIN_INIT (&db->conn_complaint);
db->max_params_num = 0;
+ db->q_prep_areas = NULL;
db->queries = NULL;
db->queries_num = 0;
+ db->interval = 0;
+
db->database = sstrdup (name);
db->host = NULL;
db->port = NULL;
return db;
} /* c_psql_database_new */
-static void c_psql_database_delete (c_psql_database_t *db)
+static void c_psql_database_delete (void *data)
{
+ size_t i;
+
+ c_psql_database_t *db = data;
+
PQfinish (db->conn);
db->conn = NULL;
+ if (db->q_prep_areas)
+ for (i = 0; i < db->queries_num; ++i)
+ udb_query_delete_preparation_area (db->q_prep_areas[i]);
+ free (db->q_prep_areas);
+
sfree (db->queries);
db->queries_num = 0;
return;
} /* c_psql_database_delete */
+static int c_psql_connect (c_psql_database_t *db)
+{
+ char conninfo[4096];
+ char *buf = conninfo;
+ int buf_len = sizeof (conninfo);
+ int status;
+
+ if (! db)
+ return -1;
+
+ status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
+ if (0 < status) {
+ buf += status;
+ buf_len -= status;
+ }
+
+ C_PSQL_PAR_APPEND (buf, buf_len, "host", db->host);
+ C_PSQL_PAR_APPEND (buf, buf_len, "port", db->port);
+ C_PSQL_PAR_APPEND (buf, buf_len, "user", db->user);
+ C_PSQL_PAR_APPEND (buf, buf_len, "password", db->password);
+ C_PSQL_PAR_APPEND (buf, buf_len, "sslmode", db->sslmode);
+ C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
+ C_PSQL_PAR_APPEND (buf, buf_len, "service", db->service);
+
+ db->conn = PQconnectdb (conninfo);
+ db->proto_version = PQprotocolVersion (db->conn);
+ return 0;
+} /* c_psql_connect */
+
static int c_psql_check_connection (c_psql_database_t *db)
{
+ _Bool init = 0;
+
+ if (! db->conn) {
+ init = 1;
+
+ /* trigger c_release() */
+ if (0 == db->conn_complaint.interval)
+ db->conn_complaint.interval = 1;
+
+ c_psql_connect (db);
+ }
+
/* "ping" */
PQclear (PQexec (db->conn, "SELECT 42;"));
}
db->proto_version = PQprotocolVersion (db->conn);
- if (3 > db->proto_version)
- log_warn ("Protocol version %d does not support parameters.",
- db->proto_version);
}
db->server_version = PQserverVersion (db->conn);
- c_release (LOG_INFO, &db->conn_complaint,
- "Successfully reconnected to database %s", PQdb (db->conn));
+ if (c_would_release (&db->conn_complaint)) {
+ char *server_host;
+ int server_version;
+
+ server_host = PQhost (db->conn);
+ server_version = PQserverVersion (db->conn);
+
+ c_do_release (LOG_INFO, &db->conn_complaint,
+ "Successfully %sconnected to database %s (user %s) "
+ "at server %s%s%s (server version: %d.%d.%d, "
+ "protocol version: %d, pid: %d)", init ? "" : "re",
+ PQdb (db->conn), PQuser (db->conn),
+ C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
+ C_PSQL_SERVER_VERSION3 (server_version),
+ db->proto_version, PQbackendPID (db->conn));
+
+ if (3 > db->proto_version)
+ log_warn ("Protocol version %d does not support parameters.",
+ db->proto_version);
+ }
return 0;
} /* c_psql_check_connection */
params[i] = db->user;
break;
case C_PSQL_PARAM_INTERVAL:
- ssnprintf (interval, sizeof (interval), "%i", interval_g);
+ ssnprintf (interval, sizeof (interval), "%.3f",
+ (db->interval > 0)
+ ? CDTIME_T_TO_DOUBLE (db->interval) : interval_g);
params[i] = interval;
break;
default:
NULL, NULL, /* return text data */ 0);
} /* c_psql_exec_query_params */
-static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q)
+static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
+ udb_query_preparation_area_t *prep_area)
{
PGresult *res;
else
host = db->host;
- status = udb_query_prepare_result (q, host, "postgresql",
- db->database, column_names, (size_t) column_num);
+ status = udb_query_prepare_result (q, prep_area, host, "postgresql",
+ db->database, column_names, (size_t) column_num, db->interval);
if (0 != status) {
log_err ("udb_query_prepare_result failed with status %i.",
status);
if (col < column_num)
continue;
- status = udb_query_handle_result (q, column_values);
+ status = udb_query_handle_result (q, prep_area, column_values);
if (status != 0) {
log_err ("udb_query_handle_result failed with status %i.",
status);
}
} /* for (row = 0; row < rows_num; ++row) */
+ udb_query_finish_result (q, prep_area);
+
BAIL_OUT (0);
#undef BAIL_OUT
} /* c_psql_exec_query */
-static int c_psql_read (void)
+static int c_psql_read (user_data_t *ud)
{
+ c_psql_database_t *db;
+
int success = 0;
int i;
- for (i = 0; i < databases_num; ++i) {
- c_psql_database_t *db = databases + i;
+ if ((ud == NULL) || (ud->data == NULL)) {
+ log_err ("c_psql_read: Invalid user data.");
+ return -1;
+ }
- int j;
+ db = ud->data;
- assert (NULL != db->database);
+ assert (NULL != db->database);
- if (0 != c_psql_check_connection (db))
- continue;
+ if (0 != c_psql_check_connection (db))
+ return -1;
- for (j = 0; j < db->queries_num; ++j)
- {
- udb_query_t *q;
+ for (i = 0; i < db->queries_num; ++i)
+ {
+ udb_query_preparation_area_t *prep_area;
+ udb_query_t *q;
- q = db->queries[j];
+ prep_area = db->q_prep_areas[i];
+ q = db->queries[i];
- if ((0 != db->server_version)
+ if ((0 != db->server_version)
&& (udb_query_check_version (q, db->server_version) <= 0))
- continue;
-
- c_psql_exec_query (db, q);
- }
+ continue;
- ++success;
+ if (0 == c_psql_exec_query (db, q, prep_area))
+ success = 1;
}
if (! success)
static int c_psql_shutdown (void)
{
- int i;
-
- if ((NULL == databases) || (0 == databases_num))
- return 0;
-
- plugin_unregister_read ("postgresql");
- plugin_unregister_shutdown ("postgresql");
-
- for (i = 0; i < databases_num; ++i)
- c_psql_database_delete (databases + i);
-
- sfree (databases);
- databases_num = 0;
+ plugin_unregister_read_group ("postgresql");
udb_query_free (queries, queries_num);
queries = NULL;
return 0;
} /* c_psql_shutdown */
-static int c_psql_init (void)
-{
- int i;
-
- if ((NULL == databases) || (0 == databases_num))
- return 0;
-
- for (i = 0; i < databases_num; ++i) {
- c_psql_database_t *db = databases + i;
-
- char conninfo[4096];
- char *buf = conninfo;
- int buf_len = sizeof (conninfo);
- int status;
-
- char *server_host;
- int server_version;
-
- /* this will happen during reinitialization */
- if (NULL != db->conn) {
- c_psql_check_connection (db);
- continue;
- }
-
- status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
- if (0 < status) {
- buf += status;
- buf_len -= status;
- }
-
- C_PSQL_PAR_APPEND (buf, buf_len, "host", db->host);
- C_PSQL_PAR_APPEND (buf, buf_len, "port", db->port);
- C_PSQL_PAR_APPEND (buf, buf_len, "user", db->user);
- C_PSQL_PAR_APPEND (buf, buf_len, "password", db->password);
- C_PSQL_PAR_APPEND (buf, buf_len, "sslmode", db->sslmode);
- C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
- C_PSQL_PAR_APPEND (buf, buf_len, "service", db->service);
-
- db->conn = PQconnectdb (conninfo);
- if (0 != c_psql_check_connection (db))
- continue;
-
- db->proto_version = PQprotocolVersion (db->conn);
-
- server_host = PQhost (db->conn);
- server_version = PQserverVersion (db->conn);
- log_info ("Successfully connected to database %s (user %s) "
- "at server %s%s%s (server version: %d.%d.%d, "
- "protocol version: %d, pid: %d)",
- PQdb (db->conn), PQuser (db->conn),
- C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
- C_PSQL_SERVER_VERSION3 (server_version),
- db->proto_version, PQbackendPID (db->conn));
-
- if (3 > db->proto_version)
- log_warn ("Protocol version %d does not support parameters.",
- db->proto_version);
- }
-
- plugin_register_read ("postgresql", c_psql_read);
- plugin_register_shutdown ("postgresql", c_psql_shutdown);
- return 0;
-} /* c_psql_init */
-
static int config_set_s (char *name, char **var, const oconfig_item_t *ci)
{
if ((0 != ci->children_num) || (1 != ci->values_num)
{
c_psql_database_t *db;
+ char cb_name[DATA_MAX_NAME_LEN];
+ struct timespec cb_interval = { 0, 0 };
+ user_data_t ud;
+
int i;
if ((1 != ci->values_num)
return 1;
}
+ memset (&ud, 0, sizeof (ud));
+
db = c_psql_database_new (ci->values[0].value.string);
+ if (db == NULL)
+ return -1;
for (i = 0; i < ci->children_num; ++i) {
oconfig_item_t *c = ci->children + i;
else if (0 == strcasecmp (c->key, "Query"))
udb_query_pick_from_list (c, queries, queries_num,
&db->queries, &db->queries_num);
+ else if (0 == strcasecmp (c->key, "Interval"))
+ cf_util_get_cdtime (c, &db->interval);
else
log_warn ("Ignoring unknown config key \"%s\".", c->key);
}
/* If no `Query' options were given, add the default queries.. */
- if (db->queries_num == 0)
- {
+ if (db->queries_num == 0) {
for (i = 0; i < def_queries_num; i++)
udb_query_pick_from_list_by_name (def_queries[i],
queries, queries_num,
&db->queries, &db->queries_num);
}
+ if (db->queries_num > 0) {
+ db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+ db->queries_num, sizeof (*db->q_prep_areas));
+
+ if (db->q_prep_areas == NULL) {
+ log_err ("Out of memory.");
+ c_psql_database_delete (db);
+ return -1;
+ }
+ }
+
for (i = 0; (size_t)i < db->queries_num; ++i) {
c_psql_user_data_t *data;
data = udb_query_get_user_data (db->queries[i]);
if ((data != NULL) && (data->params_num > db->max_params_num))
db->max_params_num = data->params_num;
+
+ db->q_prep_areas[i]
+ = udb_query_allocate_preparation_area (db->queries[i]);
+
+ if (db->q_prep_areas[i] == NULL) {
+ log_err ("Out of memory.");
+ c_psql_database_delete (db);
+ return -1;
+ }
}
+
+ ud.data = db;
+ ud.free_func = c_psql_database_delete;
+
+ ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->database);
+
+ CDTIME_T_TO_TIMESPEC (db->interval, &cb_interval);
+
+ plugin_register_complex_read ("postgresql", cb_name, c_psql_read,
+ /* interval = */ (db->interval > 0) ? &cb_interval : NULL,
+ &ud);
return 0;
} /* c_psql_config_database */
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
void module_register (void)
{
plugin_register_complex_config ("postgresql", c_psql_config);
- plugin_register_init ("postgresql", c_psql_init);
+ plugin_register_shutdown ("postgresql", c_psql_shutdown);
} /* module_register */
/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
#define FUNC_ERROR(func) do { char errbuf[1024]; ERROR ("powerdns plugin: %s failed: %s", func, sstrerror (errno, errbuf, sizeof (errbuf))); } while (0)
#define SERVER_SOCKET LOCALSTATEDIR"/run/pdns.controlsocket"
-#define SERVER_COMMAND "SHOW *"
+#define SERVER_COMMAND "SHOW * \n"
#define RECURSOR_SOCKET LOCALSTATEDIR"/run/pdns_recursor.controlsocket"
#define RECURSOR_COMMAND "get noerror-answers nxdomain-answers " \
"servfail-answers sys-msec user-msec qa-latency cache-entries cache-hits " \
- "cache-misses questions"
+ "cache-misses questions\n"
struct list_item_s;
typedef struct list_item_s list_item_t;
struct sockaddr_un sa_unix;
+ struct timeval stv_timeout;
+ cdtime_t cdt_timeout;
+
sd = socket (PF_UNIX, item->socktype, 0);
if (sd < 0)
{
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");
return (-1);
}
+ struct timeval timeout;
+ timeout.tv_sec=5;
+ timeout.tv_usec=0;
+ status = setsockopt (sd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof (timeout));
+
status = connect (sd, (struct sockaddr *) &item->sockaddr,
sizeof (item->sockaddr));
if (status != 0)
int fields_num;
if (item->command == NULL)
- item->command = strdup ("SHOW *");
+ item->command = strdup (SERVER_COMMAND);
if (item->command == NULL)
{
ERROR ("powerdns plugin: strdup failed.");
else
{
sstrncpy (buffer, "get ", sizeof (buffer));
- status = strjoin (&buffer[4], sizeof (buffer) - strlen ("get "),
+ status = strjoin (&buffer[strlen("get ")], sizeof (buffer) - strlen ("get "),
li->fields, li->fields_num,
/* seperator = */ " ");
if (status < 0)
ERROR ("powerdns plugin: strjoin failed.");
return (-1);
}
+ buffer[sizeof (buffer) - 1] = 0;
+ int i = strlen (buffer);
+ if (i < sizeof (buffer) - 2)
+ {
+ buffer[i++] = ' ';
+ buffer[i++] = '\n';
+ buffer[i++] = '\0';
+ }
}
buffer[sizeof (buffer) - 1] = 0;
/**
* 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
* Copyright (C) 2009 Manuel Sanmartin
+ * Copyright (C) 2010 Clément Stenac
*
* 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
* Sebastian Harl <sh at tokkee.org>
* Andrés J. Díaz <ajdiaz at connectical.com>
* Manuel Sanmartin
+ * Clément Stenac <clement.stenac at diwi.org>
**/
#include "collectd.h"
# define ARG_MAX 4096
#endif
-#define BUFSIZE 256
-
static const char *config_keys[] =
{
"Process",
unsigned long num_lwp;
unsigned long vmem_size;
unsigned long vmem_rss;
+ unsigned long vmem_data;
+ unsigned long vmem_code;
unsigned long stack_size;
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;
unsigned long num_lwp;
unsigned long vmem_size;
unsigned long vmem_rss;
+ unsigned long vmem_data;
+ 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;
pse->num_lwp = entry->num_lwp;
pse->vmem_size = entry->vmem_size;
pse->vmem_rss = entry->vmem_rss;
+ pse->vmem_data = entry->vmem_data;
+ pse->vmem_code = entry->vmem_code;
pse->stack_size = entry->stack_size;
pse->io_rchar = entry->io_rchar;
pse->io_wchar = entry->io_wchar;
ps->num_lwp += pse->num_lwp;
ps->vmem_size += pse->vmem_size;
ps->vmem_rss += pse->vmem_rss;
+ ps->vmem_data += pse->vmem_data;
+ ps->vmem_code += pse->vmem_code;
ps->stack_size += pse->stack_size;
ps->io_rchar += ((pse->io_rchar == -1)?0:pse->io_rchar);
ps->num_lwp = 0;
ps->vmem_size = 0;
ps->vmem_rss = 0;
+ ps->vmem_data = 0;
+ ps->vmem_code = 0;
ps->stack_size = 0;
ps->io_rchar = -1;
ps->io_wchar = -1;
vl.values_len = 1;
plugin_dispatch_values (&vl);
+ sstrncpy (vl.type, "ps_data", sizeof (vl.type));
+ vl.values[0].gauge = ps->vmem_data;
+ vl.values_len = 1;
+ plugin_dispatch_values (&vl);
+
+ sstrncpy (vl.type, "ps_code", sizeof (vl.type));
+ vl.values[0].gauge = ps->vmem_code;
+ vl.values_len = 1;
+ plugin_dispatch_values (&vl);
+
sstrncpy (vl.type, "ps_stacksize", sizeof (vl.type));
vl.values[0].gauge = ps->stack_size;
vl.values_len = 1;
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);
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);
plugin_dispatch_values (&vl);
}
- DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
- "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
- "cpu_user_counter = %lu; cpu_system_counter = %lu; "
+ DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
+ "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
+ "vmem_code = %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, ps->vmem_rss,
+ ps->name, ps->num_proc, ps->num_lwp,
+ ps->vmem_size, ps->vmem_rss,
+ ps->vmem_data, ps->vmem_code,
ps->vmem_minflt_counter, ps->vmem_majflt_counter,
ps->cpu_user_counter, ps->cpu_system_counter,
ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
return ((count >= 1) ? count : 1);
} /* int *ps_read_tasks */
+/* Read advanced virtual memory data from /proc/pid/status */
+static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
+{
+ FILE *fh;
+ char buffer[1024];
+ char filename[64];
+ unsigned long long lib = 0;
+ unsigned long long exe = 0;
+ unsigned long long data = 0;
+ char *fields[8];
+ int numfields;
+
+ ssnprintf (filename, sizeof (filename), "/proc/%i/status", pid);
+ if ((fh = fopen (filename, "r")) == NULL)
+ return (NULL);
+
+ while (fgets (buffer, sizeof(buffer), fh) != NULL)
+ {
+ long long tmp;
+ char *endptr;
+
+ if (strncmp (buffer, "Vm", 2) != 0)
+ continue;
+
+ numfields = strsplit (buffer, fields,
+ STATIC_ARRAY_SIZE (fields));
+
+ if (numfields < 2)
+ continue;
+
+ errno = 0;
+ endptr = NULL;
+ tmp = strtoll (fields[1], &endptr, /* base = */ 10);
+ if ((errno == 0) && (endptr != fields[1]))
+ {
+ if (strncmp (buffer, "VmData", 6) == 0)
+ {
+ data = tmp;
+ }
+ else if (strncmp (buffer, "VmLib", 5) == 0)
+ {
+ lib = tmp;
+ }
+ else if (strncmp(buffer, "VmExe", 5) == 0)
+ {
+ exe = tmp;
+ }
+ }
+ } /* while (fgets) */
+
+ if (fclose (fh))
+ {
+ char errbuf[1024];
+ WARNING ("processes: fclose: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ }
+
+ ps->vmem_data = data * 1024;
+ ps->vmem_code = (exe + lib) * 1024;
+
+ return (ps);
+} /* procstat_t *ps_read_vmem */
+
static procstat_t *ps_read_io (int pid, procstat_t *ps)
{
FILE *fh;
if ((fh = fopen (filename, "r")) == NULL)
return (NULL);
- while (fgets (buffer, 1024, fh) != NULL)
+ while (fgets (buffer, sizeof (buffer), fh) != NULL)
{
derive_t *val = NULL;
long long tmp;
else
continue;
- numfields = strsplit (buffer, fields, 8);
+ numfields = strsplit (buffer, fields,
+ STATIC_ARRAY_SIZE (fields));
if (numfields < 2)
continue;
int ppid;
int 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;
return (-1);
buffer[i] = 0;
- fields_len = strsplit (buffer, fields, 64);
+ fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
if (fields_len < 24)
{
DEBUG ("processes plugin: ps_read_process (pid = %i):"
cpu_system_counter = atoll (fields[14]);
vmem_size = atoll (fields[22]);
vmem_rss = atoll (fields[23]);
- ps->vmem_minflt_counter = atol (fields[9]);
- ps->vmem_majflt_counter = atol (fields[11]);
+ ps->vmem_minflt_counter = atoll (fields[9]);
+ ps->vmem_majflt_counter = atoll (fields[11]);
{
unsigned long long stack_start = atoll (fields[27]);
cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
vmem_rss = vmem_rss * pagesize_g;
- ps->cpu_user_counter = (unsigned long) cpu_user_counter;
- ps->cpu_system_counter = (unsigned long) cpu_system_counter;
+ if ( (ps_read_vmem(pid, ps)) == NULL)
+ {
+ /* No VMem data */
+ ps->vmem_data = -1;
+ ps->vmem_code = -1;
+ DEBUG("ps_read_process: did not get vmem data for pid %i",pid);
+ }
+
+ 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;
if ((pid < 1) || (NULL == buf) || (buf_len < 2))
return NULL;
- ssnprintf (file, sizeof (file), "/proc/%u/cmdline", pid);
+ ssnprintf (file, sizeof (file), "/proc/%u/cmdline",
+ (unsigned int) pid);
+ errno = 0;
fd = open (file, O_RDONLY);
if (fd < 0) {
char errbuf[4096];
- WARNING ("processes plugin: Failed to open `%s': %s.", file,
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ /* ENOENT means the process exited while we were handling it.
+ * Don't complain about this, it only fills the logs. */
+ if (errno != ENOENT)
+ WARNING ("processes plugin: Failed to open `%s': %s.", file,
+ sstrerror (errno, errbuf, sizeof (errbuf)));
return NULL;
}
status = read (fd, (void *)buf_ptr, len);
if (status < 0) {
- char errbuf[4096];
+ char errbuf[1024];
if ((EAGAIN == errno) || (EINTR == errno))
continue;
errno = 0;
endptr = NULL;
- result = strtoul(fields[1], &endptr, 10);
+ result = strtoul(fields[1], &endptr, /* base = */ 10);
if ((endptr == fields[1]) || (errno != 0)) {
ERROR ("processes plugin: Cannot parse fork rate: %s",
fields[1]);
}
pse.num_proc++;
+ pse.vmem_size = task_basic_info.virtual_size;
pse.vmem_rss = task_basic_info.resident_size;
+ /* Does not seem to be easily exposed */
+ pse.vmem_data = 0;
+ pse.vmem_code = 0;
pse.vmem_minflt_counter = task_events_info.cow_faults;
pse.vmem_majflt_counter = task_events_info.faults;
pse.num_lwp = ps.num_lwp;
pse.vmem_size = ps.vmem_size;
pse.vmem_rss = ps.vmem_rss;
+ pse.vmem_data = ps.vmem_data;
+ pse.vmem_code = ps.vmem_code;
pse.stack_size = ps.stack_size;
pse.vmem_minflt = 0;
pse.vmem_size = procs[i].ki_size;
pse.vmem_rss = procs[i].ki_rssize * getpagesize();
+ pse.vmem_data = procs[i].ki_dsize * getpagesize();
+ pse.vmem_code = procs[i].ki_tsize * getpagesize();
pse.stack_size = procs[i].ki_ssize * getpagesize();
pse.vmem_minflt = 0;
pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
+ /* Not supported */
+ pse.vmem_data = 0;
+ pse.vmem_code = 0;
pse.stack_size = 0;
+ pse.io_rchar = -1;
+ pse.io_wchar = -1;
+ pse.io_syscr = -1;
+ pse.io_syscw = -1;
+
ps_list_add (cmdline, cargs, &pse);
} /* for (i = 0 .. nprocs) */
/**
* 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
{
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);
Config *self = (Config *) s;
static char *kwlist[] = {"key", "parent", "values", "children", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|OOO", kwlist,
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOO", kwlist,
&key, &parent, &values, &children))
return -1;
+ if (!IS_BYTES_OR_UNICODE(key)) {
+ PyErr_SetString(PyExc_TypeError, "argument 1 must be str");
+ Py_XDECREF(parent);
+ Py_XDECREF(values);
+ Py_XDECREF(children);
+ return -1;
+ }
if (values == NULL) {
values = PyTuple_New(0);
PyErr_Clear();
static PyObject *Config_repr(PyObject *s) {
Config *self = (Config *) s;
+ PyObject *ret = NULL;
+ static PyObject *node_prefix = NULL, *root_prefix = NULL, *ending = NULL;
+
+ /* This is ok because we have the GIL, so this is thread-save by default. */
+ if (node_prefix == NULL)
+ node_prefix = cpy_string_to_unicode_or_bytes("<collectd.Config node ");
+ if (root_prefix == NULL)
+ root_prefix = cpy_string_to_unicode_or_bytes("<collectd.Config root node ");
+ if (ending == NULL)
+ ending = cpy_string_to_unicode_or_bytes(">");
+ if (node_prefix == NULL || root_prefix == NULL || ending == NULL)
+ return NULL;
- return PyString_FromFormat("<collectd.Config %snode %s>", self->parent == Py_None ? "root " : "", PyString_AsString(PyObject_Str(self->key)));
+ ret = PyObject_Str(self->key);
+ CPY_SUBSTITUTE(PyObject_Repr, ret, ret);
+ if (self->parent == NULL || self->parent == Py_None)
+ CPY_STRCAT(&ret, root_prefix);
+ else
+ CPY_STRCAT(&ret, node_prefix);
+ CPY_STRCAT(&ret, ending);
+
+ return ret;
}
static int Config_traverse(PyObject *self, visitproc visit, void *arg) {
Py_VISIT(c->key);
Py_VISIT(c->values);
Py_VISIT(c->children);
- return 0;
-}
+ return 0;}
static int Config_clear(PyObject *self) {
Config *c = (Config *) self;
};
PyTypeObject ConfigType = {
- PyObject_HEAD_INIT(NULL)
- 0, /* Always 0 */
+ CPY_INIT_TYPE
"collectd.Config", /* tp_name */
sizeof(Config), /* tp_basicsize */
0, /* Will be filled in later */
mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
if (mod != NULL)
- module = PyString_AsString(mod);
+ module = cpy_unicode_or_bytes_to_string(&mod);
if (module != NULL) {
snprintf(buf, size, "python.%s", module);
PyErr_Clear();
}
-static void cpy_log_exception(const char *context) {
+void cpy_log_exception(const char *context) {
int l = 0, i;
const char *typename = NULL, *message = NULL;
PyObject *type, *value, *traceback, *tn, *m, *list;
PyErr_NormalizeException(&type, &value, &traceback);
if (type == NULL) return;
tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
- m = PyObject_GetAttrString(value, "message"); /* New reference. */
+ m = PyObject_Str(value); /* New reference. */
if (tn != NULL)
- typename = PyString_AsString(tn);
+ typename = cpy_unicode_or_bytes_to_string(&tn);
if (m != NULL)
- message = PyString_AsString(m);
+ message = cpy_unicode_or_bytes_to_string(&m);
if (typename == NULL)
typename = "NamelessException";
if (message == NULL)
PyObject *line;
line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
- s = strdup(PyString_AsString(line));
+ Py_INCREF(line);
+ s = strdup(cpy_unicode_or_bytes_to_string(&line));
+ Py_DECREF(line);
if (s[strlen(s) - 1] == '\n')
s[strlen(s) - 1] = 0;
Py_BEGIN_ALLOW_THREADS
static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
int i;
cpy_callback_t *c = data->data;
- PyObject *ret, *v, *list;
+ PyObject *ret, *list, *temp, *dict = NULL, *val;
+ Values *v;
CPY_LOCK_THREADS
list = PyList_New(value_list->values_len); /* New reference. */
}
if (PyErr_Occurred() != NULL) {
cpy_log_exception("value building for write callback");
+ Py_DECREF(list);
CPY_RETURN_FROM_THREADS 0;
}
}
- v = PyObject_CallFunction((void *) &ValuesType, "sOssssdi", value_list->type, list,
- value_list->plugin_instance, value_list->type_instance, value_list->plugin,
- value_list->host, (double) value_list->time, value_list->interval);
- Py_DECREF(list);
+ dict = PyDict_New();
+ if (value_list->meta) {
+ int i, num;
+ char **table;
+ meta_data_t *meta = value_list->meta;
+
+ num = meta_data_toc(meta, &table);
+ for (i = 0; i < num; ++i) {
+ int type;
+ char *string;
+ int64_t si;
+ uint64_t ui;
+ double d;
+ _Bool b;
+
+ type = meta_data_type(meta, table[i]);
+ if (type == MD_TYPE_STRING) {
+ if (meta_data_get_string(meta, table[i], &string))
+ continue;
+ temp = cpy_string_to_unicode_or_bytes(string);
+ free(string);
+ PyDict_SetItemString(dict, table[i], temp);
+ Py_XDECREF(temp);
+ } else if (type == MD_TYPE_SIGNED_INT) {
+ if (meta_data_get_signed_int(meta, table[i], &si))
+ continue;
+ temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0);
+ PyDict_SetItemString(dict, table[i], temp);
+ Py_XDECREF(temp);
+ } else if (type == MD_TYPE_UNSIGNED_INT) {
+ if (meta_data_get_unsigned_int(meta, table[i], &ui))
+ continue;
+ temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0);
+ PyDict_SetItemString(dict, table[i], temp);
+ Py_XDECREF(temp);
+ } else if (type == MD_TYPE_DOUBLE) {
+ if (meta_data_get_double(meta, table[i], &d))
+ continue;
+ temp = PyFloat_FromDouble(d);
+ PyDict_SetItemString(dict, table[i], temp);
+ Py_XDECREF(temp);
+ } else if (type == MD_TYPE_BOOLEAN) {
+ if (meta_data_get_boolean(meta, table[i], &b))
+ continue;
+ if (b)
+ PyDict_SetItemString(dict, table[i], Py_True);
+ else
+ PyDict_SetItemString(dict, table[i], Py_False);
+ }
+ free(table[i]);
+ }
+ free(table);
+ }
+ val = Values_New(); /* New reference. */
+ v = (Values *) val;
+ sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
+ sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
+ 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 = 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);
+ v->meta = dict;
ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
+ Py_XDECREF(val);
if (ret == NULL) {
cpy_log_exception("write callback");
} else {
static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
cpy_callback_t *c = data->data;
- PyObject *ret, *n;
+ PyObject *ret, *notify;
+ Notification *n;
CPY_LOCK_THREADS
- n = PyObject_CallFunction((void *) &NotificationType, "ssssssdi", notification->type, notification->message,
- notification->plugin_instance, notification->type_instance, notification->plugin,
- notification->host, (double) notification->time, notification->severity);
+ notify = Notification_New(); /* New reference. */
+ n = (Notification *) notify;
+ sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
+ sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
+ 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 = 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. */
+ Py_XDECREF(notify);
if (ret == NULL) {
cpy_log_exception("notification callback");
} else {
static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
cpy_callback_t * c = data->data;
- PyObject *ret;
+ PyObject *ret, *text;
CPY_LOCK_THREADS
+ text = cpy_string_to_unicode_or_bytes(message);
if (c->data == NULL)
- ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
+ ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. */
else
- ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
+ ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. */
if (ret == NULL) {
/* FIXME */
static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
cpy_callback_t * c = data->data;
- PyObject *ret;
+ PyObject *ret, *text;
CPY_LOCK_THREADS
+ text = cpy_string_to_unicode_or_bytes(id);
if (c->data == NULL)
- ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
+ ret = PyObject_CallFunction(c->callback, "iN", timeout, text); /* New reference. */
else
- ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
+ ret = PyObject_CallFunction(c->callback, "iNO", timeout, text, c->data); /* New reference. */
if (ret == NULL) {
cpy_log_exception("flush callback");
PyObject *callback = NULL, *data = NULL, *mod = NULL;
static char *kwlist[] = {"callback", "data", "name", NULL};
- if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
if (PyCallable_Check(callback) == 0) {
PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
return NULL;
c->next = *list_head;
*list_head = c;
Py_XDECREF(mod);
- return PyString_FromString(buf);
+ return cpy_string_to_unicode_or_bytes(buf);
}
static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
const char *plugin = NULL, *identifier = NULL;
static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
- if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL;
Py_BEGIN_ALLOW_THREADS
plugin_flush(plugin, timeout, identifier);
Py_END_ALLOW_THREADS
PyObject *callback = NULL, *data = NULL;
static char *kwlist[] = {"callback", "data", "name", NULL};
- if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
if (PyCallable_Check(callback) == 0) {
PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
return NULL;
user_data->free_func = cpy_destroy_user_data;
user_data->data = c;
register_function(buf, handler, user_data);
- return PyString_FromString(buf);
+ return cpy_string_to_unicode_or_bytes(buf);
}
static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
struct timespec ts;
static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
- if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
if (PyCallable_Check(callback) == 0) {
PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
return NULL;
user_data->data = c;
ts.tv_sec = interval;
ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
- plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
- return PyString_FromString(buf);
+ plugin_register_complex_read(/* group = */ NULL, buf,
+ cpy_read_callback, &ts, user_data);
+ return cpy_string_to_unicode_or_bytes(buf);
}
static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
static PyObject *cpy_error(PyObject *self, PyObject *args) {
const char *text;
- if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
Py_BEGIN_ALLOW_THREADS
plugin_log(LOG_ERR, "%s", text);
Py_END_ALLOW_THREADS
static PyObject *cpy_warning(PyObject *self, PyObject *args) {
const char *text;
- if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
Py_BEGIN_ALLOW_THREADS
plugin_log(LOG_WARNING, "%s", text);
Py_END_ALLOW_THREADS
static PyObject *cpy_notice(PyObject *self, PyObject *args) {
const char *text;
- if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
Py_BEGIN_ALLOW_THREADS
plugin_log(LOG_NOTICE, "%s", text);
Py_END_ALLOW_THREADS
static PyObject *cpy_info(PyObject *self, PyObject *args) {
const char *text;
- if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
Py_BEGIN_ALLOW_THREADS
plugin_log(LOG_INFO, "%s", text);
Py_END_ALLOW_THREADS
static PyObject *cpy_debug(PyObject *self, PyObject *args) {
#ifdef COLLECT_DEBUG
const char *text;
- if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
Py_BEGIN_ALLOW_THREADS
plugin_log(LOG_DEBUG, "%s", text);
Py_END_ALLOW_THREADS
const char *name;
cpy_callback_t *prev = NULL, *tmp;
- if (PyUnicode_Check(arg)) {
- arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
- if (arg == NULL)
- return NULL;
- name = PyString_AsString(arg);
- Py_DECREF(arg);
- } else if (PyString_Check(arg)) {
- name = PyString_AsString(arg);
- } else {
+ Py_INCREF(arg);
+ name = cpy_unicode_or_bytes_to_string(&arg);
+ if (name == NULL) {
+ PyErr_Clear();
if (!PyCallable_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
+ Py_DECREF(arg);
return NULL;
}
cpy_build_name(buf, sizeof(buf), arg, NULL);
if (strcmp(name, tmp->name) == 0)
break;
+ Py_DECREF(arg);
if (tmp == NULL) {
PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
return NULL;
char buf[512];
const char *name;
- if (PyUnicode_Check(arg)) {
- arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
- if (arg == NULL)
- return NULL;
- name = PyString_AsString(arg);
- Py_DECREF(arg);
- } else if (PyString_Check(arg)) {
- name = PyString_AsString(arg);
- } else {
+ Py_INCREF(arg);
+ name = cpy_unicode_or_bytes_to_string(&arg);
+ if (name == NULL) {
+ PyErr_Clear();
if (!PyCallable_Check(arg)) {
PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
+ Py_DECREF(arg);
return NULL;
}
cpy_build_name(buf, sizeof(buf), arg, NULL);
name = buf;
}
- if (unreg(name) == 0)
+ if (unreg(name) == 0) {
+ Py_DECREF(arg);
Py_RETURN_NONE;
+ }
PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
+ Py_DECREF(arg);
return NULL;
}
static pthread_t thread;
sigset_t sigset;
+ if (!Py_IsInitialized()) {
+ WARNING("python: Plugin loaded but not configured.");
+ plugin_unregister_shutdown("python");
+ return 0;
+ }
PyEval_InitThreads();
/* Now it's finally OK to use python threads. */
for (c = cpy_init_callbacks; c; c = c->next) {
values = PyTuple_New(ci->values_num); /* New reference. */
for (i = 0; i < ci->values_num; ++i) {
if (ci->values[i].type == OCONFIG_TYPE_STRING) {
- PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
+ PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
} else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
} else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
}
}
- item = PyObject_CallFunction((void *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
+ tmp = cpy_string_to_unicode_or_bytes(ci->key);
+ item = PyObject_CallFunction((void *) &ConfigType, "NONO", tmp, parent, values, Py_None);
if (item == NULL)
return NULL;
children = PyTuple_New(ci->children_num); /* New reference. */
return item;
}
+#ifdef IS_PY3K
+static struct PyModuleDef collectdmodule = {
+ PyModuleDef_HEAD_INIT,
+ "collectd", /* name of module */
+ "The python interface to collectd", /* module documentation, may be NULL */
+ -1,
+ cpy_methods
+};
+
+PyMODINIT_FUNC PyInit_collectd(void) {
+ return PyModule_Create(&collectdmodule);
+}
+#endif
+
static int cpy_config(oconfig_item_t *ci) {
int i;
+ char *argv = "";
PyObject *sys, *tb;
PyObject *sys_path;
PyObject *module;
* python code during the config callback so we have to start
* the interpreter here. */
/* Do *not* use the python "thread" module at this point! */
+
+#ifdef IS_PY3K
+ /* Add a builtin module, before Py_Initialize */
+ PyImport_AppendInittab("collectd", PyInit_collectd);
+#endif
+
Py_Initialize();
PyType_Ready(&ConfigType);
PyType_Ready(&ValuesType);
NotificationType.tp_base = &PluginDataType;
PyType_Ready(&NotificationType);
+ SignedType.tp_base = &PyLong_Type;
+ PyType_Ready(&SignedType);
+ UnsignedType.tp_base = &PyLong_Type;
+ PyType_Ready(&UnsignedType);
sys = PyImport_ImportModule("sys"); /* New reference. */
if (sys == NULL) {
cpy_log_exception("python initialization");
cpy_log_exception("python initialization");
return 1;
}
+ PySys_SetArgv(1, &argv);
+ PyList_SetSlice(sys_path, 0, 1, NULL);
+
+#ifdef IS_PY3K
+ module = PyImport_ImportModule("collectd");
+#else
module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
+#endif
PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
+ PyModule_AddObject(module, "Signed", (void *) &SignedType); /* Steals a reference. */
+ PyModule_AddObject(module, "Unsigned", (void *) &UnsignedType); /* Steals a reference. */
PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
if (cf_util_get_string(item, &dir) != 0)
continue;
- dir_object = PyString_FromString(dir); /* New reference. */
+ dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
if (dir_object == NULL) {
ERROR("python plugin: Unable to convert \"%s\" to "
"a python object.", dir);
if (module == NULL) {
ERROR("python plugin: Error importing module \"%s\".", module_name);
cpy_log_exception("importing module");
- PyErr_Print();
}
free(module_name);
Py_XDECREF(module);
#include "cpython.h"
+static PyObject *cpy_common_repr(PyObject *s) {
+ PyObject *ret, *tmp;
+ static PyObject *l_type = NULL, *l_type_instance = NULL, *l_plugin = NULL, *l_plugin_instance = NULL;
+ static PyObject *l_host = NULL, *l_time = NULL;
+ PluginData *self = (PluginData *) s;
+
+ if (l_type == NULL)
+ l_type = cpy_string_to_unicode_or_bytes("(type=");
+ if (l_type_instance == NULL)
+ l_type_instance = cpy_string_to_unicode_or_bytes(",type_instance=");
+ if (l_plugin == NULL)
+ l_plugin = cpy_string_to_unicode_or_bytes(",plugin=");
+ if (l_plugin_instance == NULL)
+ l_plugin_instance = cpy_string_to_unicode_or_bytes(",plugin_instance=");
+ if (l_host == NULL)
+ l_host = cpy_string_to_unicode_or_bytes(",host=");
+ if (l_time == NULL)
+ l_time = cpy_string_to_unicode_or_bytes(",time=");
+
+ if (!l_type || !l_type_instance || !l_plugin || !l_plugin_instance || !l_host || !l_time)
+ return NULL;
+
+ ret = cpy_string_to_unicode_or_bytes(s->ob_type->tp_name);
+
+ CPY_STRCAT(&ret, l_type);
+ tmp = cpy_string_to_unicode_or_bytes(self->type);
+ CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+
+ if (self->type_instance[0] != 0) {
+ CPY_STRCAT(&ret, l_type_instance);
+ tmp = cpy_string_to_unicode_or_bytes(self->type_instance);
+ CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+
+ if (self->plugin[0] != 0) {
+ CPY_STRCAT(&ret, l_plugin);
+ tmp = cpy_string_to_unicode_or_bytes(self->plugin);
+ CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+
+ if (self->plugin_instance[0] != 0) {
+ CPY_STRCAT(&ret, l_plugin_instance);
+ tmp = cpy_string_to_unicode_or_bytes(self->plugin_instance);
+ CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+
+ if (self->host[0] != 0) {
+ CPY_STRCAT(&ret, l_host);
+ tmp = cpy_string_to_unicode_or_bytes(self->host);
+ CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+
+ if (self->time != 0) {
+ CPY_STRCAT(&ret, l_time);
+ tmp = PyFloat_FromDouble(self->time);
+ CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+ return ret;
+}
+
static char time_doc[] = "This is the Unix timestap of the time this value was read.\n"
"For dispatching values this can be set to 0 which means \"now\".\n"
"This means the time the value is actually dispatched, not the time\n"
static char *kwlist[] = {"type", "plugin_instance", "type_instance",
"plugin", "host", "time", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sssssd", kwlist, &type,
- &plugin_instance, &type_instance, &plugin, &host, &time))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetd", kwlist, NULL, &type,
+ NULL, &plugin_instance, NULL, &type_instance, NULL, &plugin, NULL, &host, &time))
return -1;
if (type[0] != 0 && plugin_get_ds(type) == NULL) {
}
static PyObject *PluginData_repr(PyObject *s) {
- PluginData *self = (PluginData *) s;
+ PyObject *ret;
+ static PyObject *l_closing = NULL;
- return PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu)", self->type,
- *self->type_instance ? "',type_instance='" : "", self->type_instance,
- *self->plugin ? "',plugin='" : "", self->plugin,
- *self->plugin_instance ? "',plugin_instance='" : "", self->plugin_instance,
- *self->host ? "',host='" : "", self->host,
- (long unsigned) self->time);
+ if (l_closing == NULL)
+ l_closing = cpy_string_to_unicode_or_bytes(")");
+
+ if (l_closing == NULL)
+ return NULL;
+
+ ret = cpy_common_repr(s);
+ CPY_STRCAT(&ret, l_closing);
+ return ret;
}
static PyMemberDef PluginData_members[] = {
static PyObject *PluginData_getstring(PyObject *self, void *data) {
const char *value = ((char *) self) + (intptr_t) data;
- return PyString_FromString(value);
+ return cpy_string_to_unicode_or_bytes(value);
}
static int PluginData_setstring(PyObject *self, PyObject *value, void *data) {
PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
return -1;
}
- new = PyString_AsString(value);
- if (new == NULL) return -1;
+ Py_INCREF(value);
+ new = cpy_unicode_or_bytes_to_string(&value);
+ if (new == NULL) {
+ Py_DECREF(value);
+ return -1;
+ }
old = ((char *) self) + (intptr_t) data;
sstrncpy(old, new, DATA_MAX_NAME_LEN);
+ Py_DECREF(value);
return 0;
}
PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
return -1;
}
- new = PyString_AsString(value);
- if (new == NULL) return -1;
+ Py_INCREF(value);
+ new = cpy_unicode_or_bytes_to_string(&value);
+ if (new == NULL) {
+ Py_DECREF(value);
+ return -1;
+ }
if (plugin_get_ds(new) == NULL) {
PyErr_Format(PyExc_TypeError, "Dataset %s not found", new);
+ Py_DECREF(value);
return -1;
}
old = ((char *) self) + (intptr_t) data;
sstrncpy(old, new, DATA_MAX_NAME_LEN);
+ Py_DECREF(value);
return 0;
}
};
PyTypeObject PluginDataType = {
- PyObject_HEAD_INIT(NULL)
- 0, /* Always 0 */
+ CPY_INIT_TYPE
"collectd.PluginData", /* tp_name */
sizeof(PluginData), /* tp_basicsize */
0, /* Will be filled in later */
"exception will be raised. If the content of the sequence is not a number,\n"
"a TypeError exception will be raised.";
+static char meta_doc[] = "These are the meta data for this Value object.\n"
+ "It has to be a dictionary of numbers, strings or bools. All keys must be\n"
+ "strings. int and long objects will be dispatched as signed integers unless\n"
+ "they are between 2**63 and 2**64-1, which will result in a unsigned integer.\n"
+ "You can force one of these storage classes by using the classes\n"
+ "collectd.Signed and collectd.Unsigned. A meta object received by a write\n"
+ "callback will always contain Signed or Unsigned objects.";
+
static char dispatch_doc[] = "dispatch([type][, values][, plugin_instance][, type_instance]"
"[, plugin][, host][, time][, interval]) -> None. Dispatch a value list.\n"
"\n"
return NULL;
self->values = PyList_New(0);
+ self->meta = PyDict_New();
self->interval = 0;
return (PyObject *) self;
}
static int Values_init(PyObject *s, PyObject *args, PyObject *kwds) {
Values *self = (Values *) s;
- int interval = 0, ret;
- double time = 0;
- PyObject *values = NULL, *tmp;
+ double interval = 0, time = 0;
+ PyObject *values = NULL, *meta = NULL, *tmp;
const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
- "plugin", "host", "time", "interval", NULL};
+ "plugin", "host", "time", "interval", "meta", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
- &type, &values, &plugin_instance, &type_instance,
- &plugin, &host, &time, &interval))
+ 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;
- tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
- if (tmp == NULL)
- return -1;
- ret = PluginDataType.tp_init(s, tmp, NULL);
- Py_DECREF(tmp);
- if (ret != 0)
+ if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+ PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
return -1;
-
+ }
+
+ sstrncpy(self->data.host, host, sizeof(self->data.host));
+ sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
+ sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
+ sstrncpy(self->data.type, type, sizeof(self->data.type));
+ sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
+ self->data.time = time;
+
if (values == NULL) {
values = PyList_New(0);
PyErr_Clear();
Py_INCREF(values);
}
+ if (meta == NULL) {
+ meta = PyDict_New();
+ PyErr_Clear();
+ } else {
+ Py_INCREF(meta);
+ }
+
tmp = self->values;
self->values = values;
Py_XDECREF(tmp);
+ tmp = self->meta;
+ self->meta = meta;
+ Py_XDECREF(tmp);
+
self->interval = interval;
return 0;
}
+static meta_data_t *cpy_build_meta(PyObject *meta) {
+ int i, s;
+ meta_data_t *m = NULL;
+ PyObject *l;
+
+ if (!meta)
+ return NULL;
+
+ m = meta_data_create();
+ l = PyDict_Items(meta);
+ s = PyList_Size(l);
+ for (i = 0; i < s; ++i) {
+ const char *string, *keystring;
+ PyObject *key, *value, *item, *tmp;
+
+ item = PyList_GET_ITEM(l, i);
+ key = PyTuple_GET_ITEM(item, 0);
+ Py_INCREF(key);
+ keystring = cpy_unicode_or_bytes_to_string(&key);
+ if (!keystring) {
+ PyErr_Clear();
+ Py_XDECREF(key);
+ continue;
+ }
+ value = PyTuple_GET_ITEM(item, 1);
+ Py_INCREF(value);
+ if (value == Py_True) {
+ meta_data_add_boolean(m, keystring, 1);
+ } else if (value == Py_False) {
+ meta_data_add_boolean(m, keystring, 0);
+ } else if (PyFloat_Check(value)) {
+ meta_data_add_double(m, keystring, PyFloat_AsDouble(value));
+ } else if (PyObject_TypeCheck(value, &SignedType)) {
+ long long int lli;
+ lli = PyLong_AsLongLong(value);
+ if (!PyErr_Occurred() && (lli == (int64_t) lli))
+ meta_data_add_signed_int(m, keystring, lli);
+ } else if (PyObject_TypeCheck(value, &UnsignedType)) {
+ long long unsigned llu;
+ llu = PyLong_AsUnsignedLongLong(value);
+ if (!PyErr_Occurred() && (llu == (uint64_t) llu))
+ meta_data_add_unsigned_int(m, keystring, llu);
+ } else if (PyNumber_Check(value)) {
+ long long int lli;
+ long long unsigned llu;
+ tmp = PyNumber_Long(value);
+ lli = PyLong_AsLongLong(tmp);
+ if (!PyErr_Occurred() && (lli == (int64_t) lli)) {
+ meta_data_add_signed_int(m, keystring, lli);
+ } else {
+ PyErr_Clear();
+ llu = PyLong_AsUnsignedLongLong(tmp);
+ if (!PyErr_Occurred() && (llu == (uint64_t) llu))
+ meta_data_add_unsigned_int(m, keystring, llu);
+ }
+ Py_XDECREF(tmp);
+ } else {
+ string = cpy_unicode_or_bytes_to_string(&value);
+ if (string) {
+ meta_data_add_string(m, keystring, string);
+ } else {
+ PyErr_Clear();
+ tmp = PyObject_Str(value);
+ string = cpy_unicode_or_bytes_to_string(&tmp);
+ if (string)
+ meta_data_add_string(m, keystring, string);
+ Py_XDECREF(tmp);
+ }
+ }
+ if (PyErr_Occurred())
+ cpy_log_exception("building meta data");
+ Py_XDECREF(value);
+ Py_DECREF(key);
+ }
+ return m;
+}
+
static PyObject *Values_dispatch(Values *self, PyObject *args, PyObject *kwds) {
int i, ret;
const data_set_t *ds;
int size;
value_t *value;
value_list_t value_list = VALUE_LIST_INIT;
- PyObject *values = self->values;
- double time = self->data.time;
- int interval = self->interval;
+ PyObject *values = self->values, *meta = self->meta;
+ double time = self->data.time, interval = self->interval;
const char *host = self->data.host;
const char *plugin = self->data.plugin;
const char *plugin_instance = self->data.plugin_instance;
const char *type_instance = self->data.type_instance;
static char *kwlist[] = {"type", "values", "plugin_instance", "type_instance",
- "plugin", "host", "time", "interval", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
- &type, &values, &plugin_instance, &type_instance,
- &plugin, &host, &time, &interval))
+ "plugin", "host", "time", "interval", "meta", NULL};
+ 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;
if (type[0] == 0) {
PyErr_Format(PyExc_TypeError, "values must be list or tuple");
return NULL;
}
+ if (meta != NULL && meta != Py_None && !PyDict_Check(meta)) {
+ PyErr_Format(PyExc_TypeError, "meta must be a dict");
+ return NULL;
+ }
size = (int) PySequence_Length(values);
if (size != ds->ds_num) {
PyErr_Format(PyExc_RuntimeError, "type %s needs %d values, got %i", type, ds->ds_num, size);
}
}
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);
sstrncpy(value_list.host, host, sizeof(value_list.host));
sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
sstrncpy(value_list.type, type, sizeof(value_list.type));
sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
- value_list.meta = NULL;
if (value_list.host[0] == 0)
sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
if (value_list.plugin[0] == 0)
int size;
value_t *value;
value_list_t value_list = VALUE_LIST_INIT;
- PyObject *values = self->values;
- double time = self->data.time;
- int interval = self->interval;
+ PyObject *values = self->values, *meta = self->meta;
+ double time = self->data.time, interval = self->interval;
const char *host = self->data.host;
const char *plugin = self->data.plugin;
const char *plugin_instance = self->data.plugin_instance;
const char *dest = NULL;
static char *kwlist[] = {"destination", "type", "values", "plugin_instance", "type_instance",
- "plugin", "host", "time", "interval", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|sOssssdi", kwlist,
- &type, &values, &plugin_instance, &type_instance,
- &plugin, &host, &time, &interval))
+ "plugin", "host", "time", "interval", "meta", NULL};
+ 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;
if (type[0] == 0) {
}
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);
sstrncpy(value_list.host, host, sizeof(value_list.host));
sstrncpy(value_list.plugin, plugin, sizeof(value_list.plugin));
sstrncpy(value_list.plugin_instance, plugin_instance, sizeof(value_list.plugin_instance));
sstrncpy(value_list.type, type, sizeof(value_list.type));
sstrncpy(value_list.type_instance, type_instance, sizeof(value_list.type_instance));
- value_list.meta = NULL;
+ value_list.meta = cpy_build_meta(meta);;
if (value_list.host[0] == 0)
sstrncpy(value_list.host, hostname_g, sizeof(value_list.host));
if (value_list.plugin[0] == 0)
}
static PyObject *Values_repr(PyObject *s) {
- PyObject *ret, *valuestring = NULL;
+ PyObject *ret, *tmp;
+ static PyObject *l_interval = NULL, *l_values = NULL, *l_meta = NULL, *l_closing = NULL;
Values *self = (Values *) s;
- if (self->values != NULL)
- valuestring = PyObject_Repr(self->values);
- if (valuestring == NULL)
+ if (l_interval == NULL)
+ l_interval = cpy_string_to_unicode_or_bytes(",interval=");
+ if (l_values == NULL)
+ l_values = cpy_string_to_unicode_or_bytes(",values=");
+ if (l_meta == NULL)
+ l_meta = cpy_string_to_unicode_or_bytes(",meta=");
+ if (l_closing == NULL)
+ l_closing = cpy_string_to_unicode_or_bytes(")");
+
+ if (l_interval == NULL || l_values == NULL || l_meta == NULL || l_closing == NULL)
return NULL;
- ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i,values=%s)", self->data.type,
- *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
- *self->data.plugin ? "',plugin='" : "", self->data.plugin,
- *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
- *self->data.host ? "',host='" : "", self->data.host,
- (long unsigned) self->data.time, self->interval,
- valuestring ? PyString_AsString(valuestring) : "[]");
- Py_XDECREF(valuestring);
+ ret = cpy_common_repr(s);
+ if (self->interval != 0) {
+ CPY_STRCAT(&ret, l_interval);
+ tmp = PyFloat_FromDouble(self->interval);
+ CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+ if (self->values && (!PyList_Check(self->values) || PySequence_Length(self->values) > 0)) {
+ CPY_STRCAT(&ret, l_values);
+ tmp = PyObject_Repr(self->values);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+ if (self->meta && (!PyDict_Check(self->meta) || PyDict_Size(self->meta) > 0)) {
+ CPY_STRCAT(&ret, l_meta);
+ tmp = PyObject_Repr(self->meta);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+ CPY_STRCAT(&ret, l_closing);
return ret;
}
static int Values_traverse(PyObject *self, visitproc visit, void *arg) {
Values *v = (Values *) self;
Py_VISIT(v->values);
+ Py_VISIT(v->meta);
return 0;
}
static int Values_clear(PyObject *self) {
Values *v = (Values *) self;
Py_CLEAR(v->values);
+ Py_CLEAR(v->meta);
return 0;
}
static PyMemberDef Values_members[] = {
{"interval", T_INT, offsetof(Values, interval), 0, interval_doc},
{"values", T_OBJECT_EX, offsetof(Values, values), 0, values_doc},
+ {"meta", T_OBJECT_EX, offsetof(Values, meta), 0, meta_doc},
{NULL}
};
};
PyTypeObject ValuesType = {
- PyObject_HEAD_INIT(NULL)
- 0, /* Always 0 */
+ CPY_INIT_TYPE
"collectd.Values", /* tp_name */
sizeof(Values), /* tp_basicsize */
0, /* Will be filled in later */
static int Notification_init(PyObject *s, PyObject *args, PyObject *kwds) {
Notification *self = (Notification *) s;
- PyObject *tmp;
- int severity = 0, ret;
+ int severity = 0;
double time = 0;
const char *message = "";
const char *type = "", *plugin_instance = "", *type_instance = "", *plugin = "", *host = "";
static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
"plugin", "host", "time", "severity", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
- &type, &message, &plugin_instance, &type_instance,
- &plugin, &host, &time, &severity))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
+ NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
+ NULL, &plugin, NULL, &host, &time, &severity))
return -1;
- tmp = Py_BuildValue("sssssd", type, plugin_instance, type_instance, plugin, host, time);
- if (tmp == NULL)
- return -1;
- ret = PluginDataType.tp_init(s, tmp, NULL);
- Py_DECREF(tmp);
- if (ret != 0)
+ if (type[0] != 0 && plugin_get_ds(type) == NULL) {
+ PyErr_Format(PyExc_TypeError, "Dataset %s not found", type);
return -1;
-
+ }
+
+ sstrncpy(self->data.host, host, sizeof(self->data.host));
+ sstrncpy(self->data.plugin, plugin, sizeof(self->data.plugin));
+ sstrncpy(self->data.plugin_instance, plugin_instance, sizeof(self->data.plugin_instance));
+ sstrncpy(self->data.type, type, sizeof(self->data.type));
+ sstrncpy(self->data.type_instance, type_instance, sizeof(self->data.type_instance));
+ self->data.time = time;
+
sstrncpy(self->message, message, sizeof(self->message));
self->severity = severity;
return 0;
static char *kwlist[] = {"type", "message", "plugin_instance", "type_instance",
"plugin", "host", "time", "severity", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssssssdi", kwlist,
- &type, &message, &plugin_instance, &type_instance,
- &plugin, &host, &t, &severity))
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|etetetetetetdi", kwlist,
+ NULL, &type, NULL, &message, NULL, &plugin_instance, NULL, &type_instance,
+ NULL, &plugin, NULL, &host, &t, &severity))
return NULL;
if (type[0] == 0) {
return NULL;
}
- notification.time = t;
+ notification.time = DOUBLE_TO_CDTIME_T(t);
notification.severity = severity;
sstrncpy(notification.message, message, sizeof(notification.message));
sstrncpy(notification.host, host, sizeof(notification.host));
sstrncpy(notification.type, type, sizeof(notification.type));
sstrncpy(notification.type_instance, type_instance, sizeof(notification.type_instance));
notification.meta = 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)
PyErr_SetString(PyExc_TypeError, "Cannot delete this attribute");
return -1;
}
- new = PyString_AsString(value);
- if (new == NULL) return -1;
+ Py_INCREF(value);
+ new = cpy_unicode_or_bytes_to_string(&value);
+ if (new == NULL) {
+ Py_DECREF(value);
+ return -1;
+ }
old = ((char *) self) + (intptr_t) data;
sstrncpy(old, new, NOTIF_MAX_MSG_LEN);
+ Py_DECREF(value);
return 0;
}
static PyObject *Notification_repr(PyObject *s) {
- PyObject *ret;
+ PyObject *ret, *tmp;
+ static PyObject *l_severity = NULL, *l_message = NULL, *l_closing = NULL;
Notification *self = (Notification *) s;
- ret = PyString_FromFormat("collectd.Values(type='%s%s%s%s%s%s%s%s%s%s%s',time=%lu,interval=%i)", self->data.type,
- *self->data.type_instance ? "',type_instance='" : "", self->data.type_instance,
- *self->data.plugin ? "',plugin='" : "", self->data.plugin,
- *self->data.plugin_instance ? "',plugin_instance='" : "", self->data.plugin_instance,
- *self->data.host ? "',host='" : "", self->data.host,
- *self->message ? "',message='" : "", self->message,
- (long unsigned) self->data.time, self->severity);
+ if (l_severity == NULL)
+ l_severity = cpy_string_to_unicode_or_bytes(",severity=");
+ if (l_message == NULL)
+ l_message = cpy_string_to_unicode_or_bytes(",message=");
+ if (l_closing == NULL)
+ l_closing = cpy_string_to_unicode_or_bytes(")");
+
+ if (l_severity == NULL || l_message == NULL || l_closing == NULL)
+ return NULL;
+
+ ret = cpy_common_repr(s);
+ if (self->severity != 0) {
+ CPY_STRCAT(&ret, l_severity);
+ tmp = PyInt_FromLong(self->severity);
+ CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+ if (self->message[0] != 0) {
+ CPY_STRCAT(&ret, l_message);
+ tmp = cpy_string_to_unicode_or_bytes(self->message);
+ CPY_SUBSTITUTE(PyObject_Repr, tmp, tmp);
+ CPY_STRCAT_AND_DEL(&ret, tmp);
+ }
+ CPY_STRCAT(&ret, l_closing);
return ret;
}
};
PyTypeObject NotificationType = {
- PyObject_HEAD_INIT(NULL)
- 0, /* Always 0 */
+ CPY_INIT_TYPE
"collectd.Notification", /* tp_name */
sizeof(Notification), /* tp_basicsize */
0, /* Will be filled in later */
0, /* tp_alloc */
Notification_new /* tp_new */
};
+
+static char Signed_doc[] = "This is a long by another name. Use it in meta data dicts\n"
+ "to choose the way it is stored in the meta data.";
+
+PyTypeObject SignedType = {
+ CPY_INIT_TYPE
+ "collectd.Signed", /* tp_name */
+ sizeof(Signed), /* tp_basicsize */
+ 0, /* Will be filled in later */
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Signed_doc /* tp_doc */
+};
+
+static char Unsigned_doc[] = "This is a long by another name. Use it in meta data dicts\n"
+ "to choose the way it is stored in the meta data.";
+
+PyTypeObject UnsignedType = {
+ CPY_INIT_TYPE
+ "collectd.Unsigned", /* tp_name */
+ sizeof(Unsigned), /* tp_basicsize */
+ 0, /* Will be filled in later */
+ 0, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ 0, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ 0, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ Unsigned_doc /* tp_doc */
+};
--- /dev/null
+/**
+ * collectd - src/redis.c, based on src/memcached.c
+ * Copyright (C) 2010 Andrés J. Díaz <ajdiaz@connectical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Andrés J. Díaz <ajdiaz@connectical.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <pthread.h>
+#include <credis.h>
+
+#define REDIS_DEF_HOST "localhost"
+#define REDIS_DEF_PORT 6379
+#define REDIS_DEF_TIMEOUT 2000
+#define MAX_REDIS_NODE_NAME 64
+
+/* Redis plugin configuration example:
+ *
+ * <Plugin redis>
+ * <Node "mynode">
+ * Host "localhost"
+ * Port "6379"
+ * Timeout 2000
+ * </Node>
+ * </Plugin>
+ */
+
+struct redis_node_s;
+typedef struct redis_node_s redis_node_t;
+struct redis_node_s
+{
+ char name[MAX_REDIS_NODE_NAME];
+ char host[HOST_NAME_MAX];
+ int port;
+ int timeout;
+
+ redis_node_t *next;
+};
+
+static redis_node_t *nodes_head = NULL;
+
+static int redis_node_add (const redis_node_t *rn) /* {{{ */
+{
+ redis_node_t *rn_copy;
+ redis_node_t *rn_ptr;
+
+ /* Check for duplicates first */
+ for (rn_ptr = nodes_head; rn_ptr != NULL; rn_ptr = rn_ptr->next)
+ if (strcmp (rn->name, rn_ptr->name) == 0)
+ break;
+
+ if (rn_ptr != NULL)
+ {
+ ERROR ("redis plugin: A node with the name `%s' already exists.",
+ rn->name);
+ return (-1);
+ }
+
+ rn_copy = malloc (sizeof (*rn_copy));
+ if (rn_copy == NULL)
+ {
+ ERROR ("redis plugin: malloc failed adding redis_node to the tree.");
+ return (-1);
+ }
+
+ memcpy (rn_copy, rn, sizeof (*rn_copy));
+ rn_copy->next = NULL;
+
+ DEBUG ("redis plugin: Adding node \"%s\".", rn->name);
+
+ if (nodes_head == NULL)
+ nodes_head = rn_copy;
+ else
+ {
+ rn_ptr = nodes_head;
+ while (rn_ptr->next != NULL)
+ rn_ptr = rn_ptr->next;
+ rn_ptr->next = rn_copy;
+ }
+
+ return (0);
+} /* }}} */
+
+static int redis_config_node (oconfig_item_t *ci) /* {{{ */
+{
+ redis_node_t rn;
+ int i;
+ int status;
+
+ memset (&rn, 0, sizeof (rn));
+ sstrncpy (rn.host, REDIS_DEF_HOST, sizeof (rn.host));
+ rn.port = REDIS_DEF_PORT;
+ rn.timeout = REDIS_DEF_TIMEOUT;
+
+ status = cf_util_get_string_buffer (ci, rn.name, sizeof (rn.name));
+ if (status != 0)
+ return (status);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("Host", option->key) == 0)
+ status = cf_util_get_string_buffer (option, rn.host, sizeof (rn.host));
+ else if (strcasecmp ("Port", option->key) == 0)
+ {
+ status = cf_util_get_port_number (option);
+ if (status > 0)
+ {
+ rn.port = status;
+ status = 0;
+ }
+ }
+ else if (strcasecmp ("Timeout", option->key) == 0)
+ status = cf_util_get_int (option, &rn.timeout);
+ else
+ WARNING ("redis plugin: Option `%s' not allowed inside a `Node' "
+ "block. I'll ignore this option.", option->key);
+
+ if (status != 0)
+ break;
+ }
+
+ if (status != 0)
+ return (status);
+
+ return (redis_node_add (&rn));
+} /* }}} int redis_config_node */
+
+static int redis_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *option = ci->children + i;
+
+ if (strcasecmp ("Node", option->key) == 0)
+ redis_config_node (option);
+ else
+ WARNING ("redis plugin: Option `%s' not allowed in redis"
+ " configuration. It will be ignored.", option->key);
+ }
+
+ if (nodes_head == NULL)
+ {
+ ERROR ("redis plugin: No valid node configuration could be found.");
+ return (ENOENT);
+ }
+
+ return (0);
+} /* }}} */
+
+ __attribute__ ((nonnull(2)))
+static void redis_submit_g (char *plugin_instance,
+ const char *type, const char *type_instance,
+ gauge_t value) /* {{{ */
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].gauge = value;
+
+ vl.values = values;
+ vl.values_len = 1;
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "redis", sizeof (vl.plugin));
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance,
+ sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance,
+ sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+} /* }}} */
+
+ __attribute__ ((nonnull(2)))
+static void redis_submit_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 : */
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
**/
#include "collectd.h"
_Bool collect_interface;
_Bool collect_regtable;
+ _Bool collect_cpu_load;
+ _Bool collect_memory;
+ _Bool collect_df;
+ _Bool collect_disk;
};
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);
}
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 */
plugin_dispatch_values (&vl);
} /* }}} void cr_submit_gauge */
+#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, derive_t value)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].derive = value;
+
+ vl.values = values;
+ vl.values_len = STATIC_ARRAY_SIZE (values);
+ sstrncpy (vl.host, rd->node, sizeof (vl.host));
+ sstrncpy (vl.plugin, "routeros", sizeof (vl.plugin));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+ sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+} /* }}} void cr_submit_gauge */
+#endif
+
static void submit_regtable (cr_data_t *rd, /* {{{ */
const ros_registration_table_t *r)
{
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);
return (0);
} /* }}} int handle_regtable */
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0)
+static int handle_system_resource (__attribute__((unused)) ros_connection_t *c, /* {{{ */
+ const ros_system_resource_t *r,
+ __attribute__((unused)) void *user_data)
+{
+ cr_data_t *rd;
+
+ if ((r == NULL) || (user_data == NULL))
+ return (EINVAL);
+ rd = user_data;
+
+ if (rd->collect_cpu_load)
+ cr_submit_gauge (rd, "gauge", "cpu_load", (gauge_t) r->cpu_load);
+
+ if (rd->collect_memory)
+ {
+ cr_submit_gauge (rd, "memory", "used",
+ (gauge_t) (r->total_memory - r->free_memory));
+ cr_submit_gauge (rd, "memory", "free", (gauge_t) r->free_memory);
+ }
+
+ if (rd->collect_df)
+ {
+ cr_submit_gauge (rd, "df_complex", "used",
+ (gauge_t) (r->total_memory - r->free_memory));
+ cr_submit_gauge (rd, "df_complex", "free", (gauge_t) r->free_memory);
+ }
+
+ if (rd->collect_disk)
+ {
+ 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);
+ }
+
+ return (0);
+} /* }}} int handle_system_resource */
+#endif
+
static int cr_read (user_data_t *user_data) /* {{{ */
{
int status;
}
}
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0)
+ if (rd->collect_cpu_load
+ || rd->collect_memory
+ || rd->collect_df
+ || rd->collect_disk)
+ {
+ status = ros_system_resource (rd->connection, handle_system_resource,
+ /* user data = */ rd);
+ if (status != 0)
+ {
+ char errbuf[128];
+ ERROR ("routeros plugin: ros_system_resource failed: %s",
+ sstrerror (status, errbuf, sizeof (errbuf)));
+ ros_disconnect (rd->connection);
+ rd->connection = NULL;
+ return (-1);
+ }
+ }
+#endif
+
return (0);
} /* }}} int cr_read */
router_data->service = NULL;
router_data->username = NULL;
router_data->password = NULL;
- router_data->collect_interface = false;
- router_data->collect_regtable = false;
status = 0;
for (i = 0; i < ci->children_num; i++)
cf_util_get_boolean (child, &router_data->collect_interface);
else if (strcasecmp ("CollectRegistrationTable", child->key) == 0)
cf_util_get_boolean (child, &router_data->collect_regtable);
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0)
+ else if (strcasecmp ("CollectCPULoad", child->key) == 0)
+ cf_util_get_boolean (child, &router_data->collect_cpu_load);
+ else if (strcasecmp ("CollectMemory", child->key) == 0)
+ cf_util_get_boolean (child, &router_data->collect_memory);
+ else if (strcasecmp ("CollectDF", child->key) == 0)
+ cf_util_get_boolean (child, &router_data->collect_df);
+ else if (strcasecmp ("CollectDisk", child->key) == 0)
+ cf_util_get_boolean (child, &router_data->collect_disk);
+#endif
else
{
WARNING ("routeros plugin: Unknown config option `%s'.", child->key);
user_data.data = router_data;
user_data.free_func = (void *) cr_free_data;
if (status == 0)
- status = plugin_register_complex_read (read_name, cr_read,
- /* interval = */ NULL, &user_data);
+ status = plugin_register_complex_read (/* group = */ NULL, read_name,
+ cr_read, /* interval = */ NULL, &user_data);
if (status != 0)
cr_free_data (router_data);
#include "utils_rrdcreate.h"
#undef HAVE_CONFIG_H
+#include <rrd.h>
#include <rrd_client.h>
/*
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;
*/
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,
/* 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;
{
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;
pthread_mutex_lock (&queue_lock);
/* Wait for values to arrive */
- while (true)
+ while (42)
{
struct timespec ts_wait;
&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! */
sfree (queue_entry);
} /* while (42) */
- pthread_mutex_lock (&cache_lock);
- c_avl_destroy (cache);
- cache = NULL;
- pthread_mutex_unlock (&cache_lock);
-
pthread_exit ((void *) 0);
return ((void *) 0);
} /* void *rrd_queue_thread */
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;
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);
{
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)
{
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];
return (0);
}
- now = time (NULL);
+ now = cdtime ();
if (datadir == NULL)
snprintf (key, sizeof (key), "%s.rrd",
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;
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;
}
if (rc->last_value >= value_time)
{
pthread_mutex_unlock (&cache_lock);
- WARNING ("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);
}
}
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! */
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
{
}
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);
return (0);
} /* int rrd_cache_insert */
+static int rrd_cache_destroy (void) /* {{{ */
+{
+ void *key = NULL;
+ void *value = NULL;
+
+ int non_empty = 0;
+
+ pthread_mutex_lock (&cache_lock);
+
+ if (cache == NULL)
+ {
+ pthread_mutex_unlock (&cache_lock);
+ return (0);
+ }
+
+ while (c_avl_pick (cache, &key, &value) == 0)
+ {
+ rrd_cache_t *rc;
+ int i;
+
+ sfree (key);
+ key = NULL;
+
+ rc = value;
+ value = NULL;
+
+ if (rc->values_num > 0)
+ non_empty++;
+
+ for (i = 0; i < rc->values_num; i++)
+ sfree (rc->values[i]);
+ sfree (rc->values);
+ sfree (rc);
+ }
+
+ c_avl_destroy (cache);
+ cache = NULL;
+
+ if (non_empty > 0)
+ {
+ INFO ("rrdtool plugin: %i cache %s had values when destroying the cache.",
+ non_empty, (non_empty == 1) ? "entry" : "entries");
+ }
+ else
+ {
+ DEBUG ("rrdtool plugin: No values have been lost "
+ "when destroying the cache.");
+ }
+
+ pthread_mutex_unlock (&cache_lock);
+ return (0);
+} /* }}} int rrd_cache_destroy */
+
static int rrd_compare_numeric (const void *a_ptr, const void *b_ptr)
{
int a = *((int *) a_ptr);
char values[512];
int status;
+ if (do_shutdown)
+ return (0);
+
if (0 != strcmp (ds->type, vl->type)) {
ERROR ("rrdtool plugin: DS type does not match value list type");
return -1;
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);
{
if (strcasecmp ("CacheTimeout", key) == 0)
{
- int tmp = atoi (value);
+ double tmp = atof (value);
if (tmp < 0)
{
fprintf (stderr, "rrdtool: `CacheTimeout' must "
"be greater than 0.\n");
return (1);
}
- cache_timeout = tmp;
+ cache_timeout = DOUBLE_TO_CDTIME_T (tmp);
}
else if (strcasecmp ("CacheFlush", key) == 0)
{
}
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");
}
else
{
- random_timeout = tmp;
+ random_timeout = DOUBLE_TO_CDTIME_T (tmp);
}
}
else
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);
DEBUG ("rrdtool plugin: queue_thread exited.");
}
- /* TODO: Maybe it'd be a good idea to free the cache here.. */
+ rrd_cache_destroy ();
return (0);
} /* int rrd_shutdown */
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.");
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)
#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;
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;
int version;
void *sess_handle;
c_complain_t complaint;
- uint32_t interval;
+ cdtime_t interval;
data_definition_t **data_list;
int data_list_len;
};
* +-> 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)
{
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;
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);
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 (cb_name, csnmp_read_host,
- /* interval = */ &cb_interval, /* user_data = */ &cb_data);
+ status = plugin_register_complex_read (/* group = */ NULL, cb_name,
+ csnmp_read_host, /* interval = */ &cb_interval,
+ /* user_data = */ &cb_data);
if (status != 0)
{
ERROR ("snmp plugin: Registering complex read function failed.");
}
else
{
- WARNING ("snmp plugin: I don't know the ASN type `%i'", (int) vl->type);
+ char oid_buffer[1024];
+
+ memset (oid_buffer, 0, sizeof (oid_buffer));
+ snprint_objid (oid_buffer, sizeof (oid_buffer) - 1,
+ vl->name, vl->name_length);
+
+#ifdef ASN_NULL
+ if (vl->type == ASN_NULL)
+ INFO ("snmp plugin: OID \"%s\" is undefined (type ASN_NULL)",
+ oid_buffer);
+ else
+#endif
+ WARNING ("snmp plugin: I don't know the ASN type \"%i\" (OID: %s)",
+ (int) vl->type, oid_buffer);
+
defined = 0;
}
for (i = 0; i < num_chars; i++)
{
/* Check for control characters. */
- if ((src[i] >= 0) && (src[i] < 32))
+ if ((unsigned char)src[i] < 32)
return (csnmp_strvbcopy_hexstring (dst, vb, dst_size));
dst[i] = src[i];
}
char temp[DATA_MAX_NAME_LEN];
if (instance_list_ptr == NULL)
- ssnprintf (temp, sizeof (temp), "%u", (uint32_t) subid);
+ ssnprintf (temp, sizeof (temp), "%"PRIu32, (uint32_t) subid);
else
sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
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;
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);
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)
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
* Manuel Sanmartin
+ * Aurélien Reynaud <collectd at wattapower.net>
**/
#if HAVE_CONFIG_H
/* 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 */
#elif HAVE_PERFSTAT
static int pagesize;
static perfstat_memory_total_t pmemory;
-/*# endif HAVE_PERFSTAT */
+/*# endif HAVE_PERFSTAT */
#else
# error "No applicable input method."
/* 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)
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;
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];
char *fields[8];
int numfields;
+ _Bool old_kernel=0;
+
derive_t swap_used = 0;
derive_t swap_cached = 0;
derive_t swap_free = 0;
return (-1);
}
- while (fgets (buffer, 1024, fh) != NULL)
+ while (fgets (buffer, sizeof (buffer), fh) != NULL)
{
- derive_t *val = NULL;
-
- if (strncasecmp (buffer, "SwapTotal:", 10) == 0)
- val = &swap_total;
- else if (strncasecmp (buffer, "SwapFree:", 9) == 0)
- val = &swap_free;
- else if (strncasecmp (buffer, "SwapCached:", 11) == 0)
- val = &swap_cached;
- else
- continue;
-
- numfields = strsplit (buffer, fields, 8);
-
+ numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
if (numfields < 2)
continue;
- *val = (derive_t) atoll (fields[1]) * 1024LL;
+ if (strcasecmp (fields[0], "SwapTotal:") == 0)
+ strtoderive (fields[1], &swap_total);
+ else if (strcasecmp (fields[0], "SwapFree:") == 0)
+ strtoderive (fields[1], &swap_free);
+ else if (strcasecmp (fields[0], "SwapCached:") == 0)
+ strtoderive (fields[1], &swap_cached);
}
if (fclose (fh))
if ((fh = fopen ("/proc/vmstat", "r")) == NULL)
{
- char errbuf[1024];
- WARNING ("swap: fopen: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- return (-1);
+ // /proc/vmstat does not exist in kernels <2.6
+ if ((fh = fopen ("/proc/stat", "r")) == NULL )
+ {
+ char errbuf[1024];
+ WARNING ("swap: fopen: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ else
+ old_kernel = 1;
}
- while (fgets (buffer, 1024, fh) != NULL)
+ while (fgets (buffer, sizeof (buffer), fh) != NULL)
{
- derive_t *val = NULL;
-
- if (strncasecmp (buffer, "pswpin", 6) == 0)
- val = &swap_in;
- else if (strncasecmp (buffer, "pswpout", 7) == 0)
- val = &swap_out;
- else
- continue;
-
- numfields = strsplit (buffer, fields, 8);
-
- if (numfields < 2)
- continue;
-
- *val = (derive_t) atoll (fields[1]);
- }
+ numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+
+ if (!old_kernel)
+ {
+ if (numfields != 2)
+ continue;
+
+ if (strcasecmp ("pswpin", fields[0]) == 0)
+ strtoderive (fields[1], &swap_in);
+ else if (strcasecmp ("pswpout", fields[0]) == 0)
+ strtoderive (fields[1], &swap_out);
+ }
+ else /* if (old_kernel) */
+ {
+ if (numfields != 3)
+ continue;
+
+ if (strcasecmp ("page", fields[0]) == 0)
+ {
+ strtoderive (fields[1], &swap_in);
+ strtoderive (fields[2], &swap_out);
+ }
+ }
+ } /* while (fgets) */
if (fclose (fh))
{
sstrerror (errno, errbuf, sizeof (errbuf)));
}
- swap_submit ("used", swap_used, DS_TYPE_GAUGE);
- swap_submit ("free", swap_free, DS_TYPE_GAUGE);
- swap_submit ("cached", swap_cached, DS_TYPE_GAUGE);
- swap_submit ("in", swap_in, DS_TYPE_DERIVE);
+ swap_submit ("used", 1024 * swap_used, DS_TYPE_GAUGE);
+ swap_submit ("free", 1024 * swap_free, DS_TYPE_GAUGE);
+ 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;
* However, Solaris does not allow to allocated/reserved more than the
* available swap (physical memory + disk swap), so the pedant may
* prefer: allocated + unallocated = reserved, available
- *
+ *
* We map the above to: used + resv = n/a, free
*
* Does your brain hurt yet? - Christophe Kalt
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;
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)
{
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;
/* 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;
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 ();
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];
}
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 : */
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;
/* 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));
{
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))
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;
/* 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))
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
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))
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);
static int ts_destroy (void **user_data) /* {{{ */
{
+ ts_data_t **data;
+
if (user_data == NULL)
return (-EINVAL);
- free (*user_data);
- *user_data = NULL;
+ data = (ts_data_t **) user_data;
+
+ free (*data);
+ *data = NULL;
return (0);
} /* }}} int ts_destroy */
--- /dev/null
+/**
+ * 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 <octo at verplant.org>
+ **/
+
+#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 : */
+
#include "common.h"
#include "plugin.h"
-#include <arpa/inet.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
} /* 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
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;
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;
values[0].gauge = value;
- vl.time = time (NULL);
vl.values = values;
vl.values_len = 1;
sstrncpy (vl.host, hostname_g, sizeof (vl.host));
# 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,
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);
char filename[256];
char data[1024];
int len;
- int ok = 0;
+ _Bool success = 0;
if (device_list && ignorelist_match (device_list, name))
return -1;
if (endptr == data + len && errno == 0) {
thermal_submit(name, TEMP, temp);
- ++ok;
+ success = 1;
}
}
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,
&& (! 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;
++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);
return -1;
}
-static const char *config_keys[] = {
- "Device",
- "IgnoreSelected",
- "ForceUseProcfs"
-};
-
static int thermal_config (const char *key, const char *value)
{
if (device_list == NULL)
static int thermal_sysfs_read (void)
{
return walk_directory (dirname_sysfs, thermal_sysfs_device_read,
- /* user_data = */ NULL);
+ /* user_data = */ NULL, /* include hidden */ 0);
}
static int thermal_procfs_read (void)
{
return walk_directory (dirname_procfs, thermal_procfs_device_read,
- /* user_data = */ NULL);
+ /* user_data = */ NULL, /* include hidden */ 0);
}
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;
}
--- /dev/null
+/**
+ * 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 <octo at collectd.org>
+ * Sebastian Harl <sh at tokkee.org>
+ * Andrés J. Díaz <ajdiaz at connectical.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_avltree.h"
+#include "utils_cache.h"
+
+#include <assert.h>
+#include <pthread.h>
+
+/*
+ * 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 (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;
+ 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 /* is not inverted */
+ {
+ 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],
+ (values[ds_index] < min) ? "below" : "above",
+ (state == STATE_ERROR) ? "failure" : "warning",
+ (values[ds_index] < min) ? min : max,
+ ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
+ }
+ 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 : */
-absolute count:ABSOLUTE:0:U
-apache_bytes count:COUNTER:0:134217728
-apache_connections 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_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
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;
#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"
{
"SocketFile",
"SocketGroup",
- "SocketPerms"
+ "SocketPerms",
+ "DeleteSocket"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
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;
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)
{
{
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);
{
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);
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Author:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
**/
#include "collectd.h"
#include "plugin.h"
#include "utils_avltree.h"
#include "utils_cache.h"
-#include "utils_threshold.h"
#include "meta_data.h"
#include <assert.h>
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;
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) < (2 * 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;
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;
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;
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) >= (2 * 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 */
{
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;
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)
}
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;
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;
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 */
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;
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;
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);
int success = 0;
int error = 0;
- int timeout = -1;
+ double timeout = 0.0;
char **plugins = NULL;
int plugins_num = 0;
char **identifiers = NULL;
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);
sfree (identifiers);
return (-1);
}
- else if (timeout <= 0)
- timeout = -1;
+ else if (timeout < 0.0)
+ {
+ timeout = 0.0;
+ }
}
else
{
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
{
char *command;
char **names = NULL;
- time_t *times = NULL;
+ cdtime_t *times = NULL;
size_t number = 0;
size_t i;
int status;
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 */
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);
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 */
#include <stdio.h>
+#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 */
char **values;
size_t values_num;
- /* Preparation area */
- const data_set_t *ds;
- size_t *instances_pos;
- size_t *values_pos;
- char **instances_buffer;
- char **values_buffer;
-
- /* 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;
}; /* }}} */
char *statement;
void *user_data;
- int legacy_mode;
-
unsigned int min_version;
unsigned int max_version;
- /* Preparation area */
+ udb_result_t *results;
+}; /* }}} */
+
+struct udb_result_preparation_area_s /* {{{ */
+{
+ const data_set_t *ds;
+ size_t *instances_pos;
+ size_t *values_pos;
+ char **instances_buffer;
+ char **values_buffer;
+
+ struct udb_result_preparation_area_s *next;
+}; /* }}} */
+typedef struct udb_result_preparation_area_s udb_result_preparation_area_t;
+
+struct udb_query_preparation_area_s /* {{{ */
+{
size_t column_num;
char *host;
char *plugin;
char *db_name;
- udb_result_t *results;
+ cdtime_t interval;
+
+ udb_result_preparation_area_t *result_prep_areas;
}; /* }}} */
/*
} /* }}} int udb_config_set_uint */
/*
- * Legacy result private functions
- */
-static void udb_legacy_result_finish_result (udb_result_t *r) /* {{{ */
-{
- if (r == NULL)
- return;
-
- assert (r->legacy_mode == 1);
-
- r->ds = NULL;
-} /* }}} void udb_legacy_result_finish_result */
-
-static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
- udb_query_t *q, char **column_values)
-{
- value_list_t vl = VALUE_LIST_INIT;
- value_t value;
- char *value_str;
-
- assert (r->legacy_mode == 1);
- assert (r->ds != NULL);
- assert (r->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->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->ds->ds[0].type));
- errno = EINVAL;
- return (-1);
- }
-
- sstrncpy (vl.host, q->host, sizeof (vl.host));
- sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
- sstrncpy (vl.type, r->type, sizeof (vl.type));
-
- if (r->instance_prefix != NULL)
- sstrncpy (vl.type_instance, r->instance_prefix,
- sizeof (vl.type_instance));
-
- plugin_dispatch_values (&vl);
-
- return (0);
-} /* }}} int udb_legacy_result_handle_result */
-
-static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
- 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);
-
- 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 {{{ */
- r->ds = plugin_get_ds (r->type);
- if (r->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 (r->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,
- r->ds->ds_num);
- return (-1);
- }
- /* }}} */
-
- return (0);
-} /* }}} int udb_legacy_result_prepare_result */
-
-static int udb_legacy_result_create (const char *query_name, /* {{{ */
- udb_result_t **r_head, oconfig_item_t *ci, size_t position)
-{
- udb_result_t *r;
-
- if ((ci->values_num < 1) || (ci->values_num > 2)
- || (ci->values[0].type != OCONFIG_TYPE_STRING)
- || ((ci->values_num == 2)
- && (ci->values[1].type != OCONFIG_TYPE_STRING)))
- {
- WARNING ("db query utils: The `Column' block needs either one or two "
- "string arguments.");
- return (-1);
- }
-
- r = (udb_result_t *) malloc (sizeof (*r));
- if (r == NULL)
- {
- ERROR ("db query utils: malloc failed.");
- return (-1);
- }
- memset (r, 0, sizeof (*r));
-
- r->legacy_mode = 1;
- r->legacy_position = position;
-
- r->type = strdup (ci->values[0].value.string);
- if (r->type == NULL)
- {
- ERROR ("db query utils: strdup failed.");
- free (r);
- return (-1);
- }
-
- r->instance_prefix = NULL;
- if (ci->values_num == 2)
- {
- r->instance_prefix = strdup (ci->values[1].value.string);
- if (r->instance_prefix == NULL)
- {
- ERROR ("db query utils: strdup failed.");
- free (r->type);
- free (r);
- return (-1);
- }
- }
-
- /* If all went well, add this result to the list of results. */
- if (*r_head == NULL)
- {
- *r_head = r;
- }
- else
- {
- udb_result_t *last;
-
- last = *r_head;
- while (last->next != NULL)
- last = last->next;
-
- last->next = r;
- }
-
- return (0);
-} /* }}} int udb_legacy_result_create */
-
-/*
* Result private functions
*/
-static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
+static int udb_result_submit (udb_result_t *r, /* {{{ */
+ udb_result_preparation_area_t *r_area,
+ const udb_query_t const *q, udb_query_preparation_area_t *q_area)
{
value_list_t vl = VALUE_LIST_INIT;
size_t i;
assert (r != NULL);
- assert (r->legacy_mode == 0);
- assert (r->ds != NULL);
- assert (((size_t) r->ds->ds_num) == r->values_num);
+ assert (r_area->ds != NULL);
+ assert (((size_t) r_area->ds->ds_num) == r->values_num);
- vl.values = (value_t *) calloc (r->ds->ds_num, sizeof (value_t));
+ vl.values = (value_t *) calloc (r_area->ds->ds_num, sizeof (value_t));
if (vl.values == NULL)
{
ERROR ("db query utils: malloc failed.");
return (-1);
}
- vl.values_len = r->ds->ds_num;
+ vl.values_len = r_area->ds->ds_num;
for (i = 0; i < r->values_num; i++)
{
- char *value_str = r->values_buffer[i];
+ char *value_str = r_area->values_buffer[i];
- if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i].type))
+ if (0 != parse_value (value_str, &vl.values[i], r_area->ds->ds[i].type))
{
ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
- value_str, DS_TYPE_TO_STRING (r->ds->ds[i].type));
+ value_str, DS_TYPE_TO_STRING (r_area->ds->ds[i].type));
errno = EINVAL;
return (-1);
}
}
- sstrncpy (vl.host, q->host, sizeof (vl.host));
- sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
+ if (q_area->interval > 0)
+ vl.interval = q_area->interval;
+
+ sstrncpy (vl.host, q_area->host, sizeof (vl.host));
+ sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
sstrncpy (vl.type, r->type, sizeof (vl.type));
/* Set vl.type_instance {{{ */
if (r->instance_prefix == NULL)
{
strjoin (vl.type_instance, sizeof (vl.type_instance),
- r->instances_buffer, r->instances_num, "-");
+ r_area->instances_buffer, r->instances_num, "-");
}
else
{
char tmp[DATA_MAX_NAME_LEN];
- strjoin (tmp, sizeof (tmp), r->instances_buffer, r->instances_num, "-");
+ strjoin (tmp, sizeof (tmp), r_area->instances_buffer,
+ r->instances_num, "-");
tmp[sizeof (tmp) - 1] = 0;
snprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
return (0);
} /* }}} void udb_result_submit */
-static void udb_result_finish_result (udb_result_t *r) /* {{{ */
+static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */
+ udb_result_preparation_area_t *prep_area)
{
- if (r == NULL)
- return;
-
- if (r->legacy_mode == 1)
- {
- udb_legacy_result_finish_result (r);
+ if ((r == NULL) || (prep_area == NULL))
return;
- }
-
- assert (r->legacy_mode == 0);
- r->ds = NULL;
- sfree (r->instances_pos);
- sfree (r->values_pos);
- sfree (r->instances_buffer);
- sfree (r->values_buffer);
+ prep_area->ds = NULL;
+ sfree (prep_area->instances_pos);
+ sfree (prep_area->values_pos);
+ sfree (prep_area->instances_buffer);
+ sfree (prep_area->values_buffer);
} /* }}} void udb_result_finish_result */
static int udb_result_handle_result (udb_result_t *r, /* {{{ */
- udb_query_t *q, char **column_values)
+ udb_query_preparation_area_t *q_area,
+ udb_result_preparation_area_t *r_area,
+ const udb_query_t const *q, char **column_values)
{
size_t i;
- if (r->legacy_mode == 1)
- return (udb_legacy_result_handle_result (r, q, column_values));
-
- assert (r->legacy_mode == 0);
+ assert (r && q_area && r_area);
for (i = 0; i < r->instances_num; i++)
- r->instances_buffer[i] = column_values[r->instances_pos[i]];
+ r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]];
for (i = 0; i < r->values_num; i++)
- r->values_buffer[i] = column_values[r->values_pos[i]];
+ r_area->values_buffer[i] = column_values[r_area->values_pos[i]];
- return udb_result_submit (r, q);
+ return udb_result_submit (r, r_area, q, q_area);
} /* }}} int udb_result_handle_result */
-static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
+static int udb_result_prepare_result (const udb_result_t const *r, /* {{{ */
+ udb_result_preparation_area_t *prep_area,
char **column_names, size_t column_num)
{
size_t i;
- if (r == NULL)
+ if ((r == NULL) || (prep_area == NULL))
return (-EINVAL);
- if (r->legacy_mode == 1)
- return (udb_legacy_result_prepare_result (r, column_names, column_num));
-
- assert (r->legacy_mode == 0);
-
#define BAIL_OUT(status) \
- r->ds = NULL; \
- sfree (r->instances_pos); \
- sfree (r->values_pos); \
- sfree (r->instances_buffer); \
- sfree (r->values_buffer); \
+ prep_area->ds = NULL; \
+ sfree (prep_area->instances_pos); \
+ sfree (prep_area->values_pos); \
+ sfree (prep_area->instances_buffer); \
+ sfree (prep_area->values_buffer); \
return (status)
/* Make sure previous preparations are cleaned up. */
- udb_result_finish_result (r);
- r->instances_pos = NULL;
- r->values_pos = NULL;
+ udb_result_finish_result (r, prep_area);
+ prep_area->instances_pos = NULL;
+ prep_area->values_pos = NULL;
/* Read `ds' and check number of values {{{ */
- r->ds = plugin_get_ds (r->type);
- if (r->ds == NULL)
+ 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.",
BAIL_OUT (-1);
}
- if (((size_t) r->ds->ds_num) != r->values_num)
+ if (((size_t) prep_area->ds->ds_num) != r->values_num)
{
ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
"requires exactly %i value%s, but the configuration specifies %zu.",
r->type,
- r->ds->ds_num, (r->ds->ds_num == 1) ? "" : "s",
+ prep_area->ds->ds_num, (prep_area->ds->ds_num == 1) ? "" : "s",
r->values_num);
BAIL_OUT (-1);
}
* r->values_buffer {{{ */
if (r->instances_num > 0)
{
- r->instances_pos = (size_t *) calloc (r->instances_num, sizeof (size_t));
- if (r->instances_pos == NULL)
+ prep_area->instances_pos
+ = (size_t *) calloc (r->instances_num, sizeof (size_t));
+ if (prep_area->instances_pos == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
BAIL_OUT (-ENOMEM);
}
- r->instances_buffer = (char **) calloc (r->instances_num, sizeof (char *));
- if (r->instances_buffer == NULL)
+ prep_area->instances_buffer
+ = (char **) calloc (r->instances_num, sizeof (char *));
+ if (prep_area->instances_buffer == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
BAIL_OUT (-ENOMEM);
}
} /* if (r->instances_num > 0) */
- r->values_pos = (size_t *) calloc (r->values_num, sizeof (size_t));
- if (r->values_pos == NULL)
+ prep_area->values_pos
+ = (size_t *) calloc (r->values_num, sizeof (size_t));
+ if (prep_area->values_pos == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
BAIL_OUT (-ENOMEM);
}
- r->values_buffer = (char **) calloc (r->values_num, sizeof (char *));
- if (r->values_buffer == NULL)
+ prep_area->values_buffer
+ = (char **) calloc (r->values_num, sizeof (char *));
+ if (prep_area->values_buffer == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
BAIL_OUT (-ENOMEM);
{
if (strcasecmp (r->instances[i], column_names[j]) == 0)
{
- r->instances_pos[i] = j;
+ prep_area->instances_pos[i] = j;
break;
}
}
{
if (strcasecmp (r->values[i], column_names[j]) == 0)
{
- r->values_pos[i] = j;
+ prep_area->values_pos[i] = j;
break;
}
}
*/
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;
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;
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)
{
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)
{
return (1);
} /* }}} int udb_query_check_version */
-void udb_query_finish_result (udb_query_t *q) /* {{{ */
+void udb_query_finish_result (const udb_query_t const *q, /* {{{ */
+ udb_query_preparation_area_t *prep_area)
{
+ udb_result_preparation_area_t *r_area;
udb_result_t *r;
- if (q == NULL)
+ if ((q == NULL) || (prep_area == NULL))
return;
- q->column_num = 0;
- sfree (q->host);
- sfree (q->plugin);
- sfree (q->db_name);
+ prep_area->column_num = 0;
+ sfree (prep_area->host);
+ sfree (prep_area->plugin);
+ sfree (prep_area->db_name);
- for (r = q->results; r != NULL; r = r->next)
- udb_result_finish_result (r);
+ 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)
+ {
+ /* this may happen during error conditions of the caller */
+ if (r_area == NULL)
+ break;
+ udb_result_finish_result (r, r_area);
+ }
} /* }}} void udb_query_finish_result */
-int udb_query_handle_result (udb_query_t *q, char **column_values) /* {{{ */
+int udb_query_handle_result (const udb_query_t const *q, /* {{{ */
+ udb_query_preparation_area_t *prep_area, char **column_values)
{
+ udb_result_preparation_area_t *r_area;
udb_result_t *r;
int success;
int status;
- if (q == NULL)
+ if ((q == NULL) || (prep_area == NULL))
return (-EINVAL);
- if ((q->column_num < 1) || (q->host == NULL) || (q->plugin == NULL)
- || (q->db_name == NULL))
+ if ((prep_area->column_num < 1) || (prep_area->host == NULL)
+ || (prep_area->plugin == NULL) || (prep_area->db_name == NULL))
{
ERROR ("db query utils: Query `%s': Query is not prepared; "
"can't handle result.", q->name);
{
size_t i;
- for (i = 0; i < q->column_num; i++)
+ for (i = 0; i < prep_area->column_num; i++)
{
DEBUG ("db query utils: udb_query_handle_result (%s, %s): "
"column[%zu] = %s;",
- q->db_name, q->name, i, column_values[i]);
+ prep_area->db_name, q->name, i, column_values[i]);
}
} while (0);
#endif /* }}} */
success = 0;
- for (r = q->results; r != NULL; r = r->next)
+ for (r = q->results, r_area = prep_area->result_prep_areas;
+ r != NULL; r = r->next, r_area = r_area->next)
{
- status = udb_result_handle_result (r, q, column_values);
+ status = udb_result_handle_result (r, prep_area, r_area,
+ q, column_values);
if (status == 0)
success++;
}
if (success == 0)
{
ERROR ("db query utils: udb_query_handle_result (%s, %s): "
- "All results failed.", q->db_name, q->name);
+ "All results failed.", prep_area->db_name, q->name);
return (-1);
}
return (0);
} /* }}} int udb_query_handle_result */
-int udb_query_prepare_result (udb_query_t *q, /* {{{ */
+int udb_query_prepare_result (const 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)
+ char **column_names, size_t column_num, cdtime_t interval)
{
+ udb_result_preparation_area_t *r_area;
udb_result_t *r;
int status;
- if (q == NULL)
+ if ((q == NULL) || (prep_area == NULL))
return (-EINVAL);
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
- q->column_num = column_num;
- q->host = strdup (host);
- q->plugin = strdup (plugin);
- q->db_name = strdup (db_name);
+ prep_area->column_num = column_num;
+ prep_area->host = strdup (host);
+ prep_area->plugin = strdup (plugin);
+ prep_area->db_name = strdup (db_name);
- if ((q->host == NULL) || (q->plugin == NULL) || (q->db_name == NULL))
+ prep_area->interval = interval;
+
+ if ((prep_area->host == NULL) || (prep_area->plugin == NULL)
+ || (prep_area->db_name == NULL))
{
ERROR ("db query utils: Query `%s': Prepare failed: Out of memory.", q->name);
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
return (-ENOMEM);
}
} while (0);
#endif
- for (r = q->results; r != NULL; r = r->next)
+ for (r = q->results, r_area = prep_area->result_prep_areas;
+ r != NULL; r = r->next, r_area = r_area->next)
{
- status = udb_result_prepare_result (r, column_names, column_num);
+ if (! r_area)
+ {
+ ERROR ("db query utils: Query `%s': Invalid number of result "
+ "preparation areas.", q->name);
+ udb_query_finish_result (q, prep_area);
+ return (-EINVAL);
+ }
+
+ status = udb_result_prepare_result (r, r_area, column_names, column_num);
if (status != 0)
{
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
return (status);
}
}
return (0);
} /* }}} int udb_query_prepare_result */
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area (udb_query_t *q) /* {{{ */
+{
+ udb_query_preparation_area_t *q_area;
+ udb_result_preparation_area_t **next_r_area;
+ udb_result_t *r;
+
+ q_area = (udb_query_preparation_area_t *)malloc (sizeof (*q_area));
+ if (q_area == NULL)
+ return NULL;
+
+ memset (q_area, 0, sizeof (*q_area));
+
+ next_r_area = &q_area->result_prep_areas;
+ for (r = q->results; r != NULL; r = r->next)
+ {
+ udb_result_preparation_area_t *r_area;
+
+ r_area = (udb_result_preparation_area_t *)malloc (sizeof (*r_area));
+ if (r_area == NULL)
+ {
+ for (r_area = q_area->result_prep_areas;
+ r_area != NULL; r_area = r_area->next)
+ {
+ free (r_area);
+ }
+ free (q_area);
+ return NULL;
+ }
+
+ memset (r_area, 0, sizeof (*r_area));
+
+ *next_r_area = r_area;
+ next_r_area = &r_area->next;
+ }
+
+ return (q_area);
+} /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */
+
+void
+udb_query_delete_preparation_area (udb_query_preparation_area_t *q_area) /* {{{ */
+{
+ udb_result_preparation_area_t *r_area;
+
+ if (q_area == NULL)
+ return;
+
+ r_area = q_area->result_prep_areas;
+ while (r_area != NULL)
+ {
+ udb_result_preparation_area_t *area = r_area;
+
+ r_area = r_area->next;
+
+ sfree (area->instances_pos);
+ sfree (area->values_pos);
+ sfree (area->instances_buffer);
+ sfree (area->values_buffer);
+ free (area);
+ }
+
+ sfree (q_area->host);
+ sfree (q_area->plugin);
+ sfree (q_area->db_name);
+
+ free (q_area);
+} /* }}} void udb_query_delete_preparation_area */
+
/* vim: set sw=2 sts=2 et fdm=marker : */
struct udb_query_s;
typedef struct udb_query_s udb_query_t;
+struct udb_query_preparation_area_s;
+typedef struct udb_query_preparation_area_s udb_query_preparation_area_t;
+
typedef int (*udb_query_create_callback_t) (udb_query_t *q,
oconfig_item_t *ci);
*/
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,
*/
int udb_query_check_version (udb_query_t *q, unsigned int version);
-int udb_query_prepare_result (udb_query_t *q,
+int udb_query_prepare_result (const 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 udb_query_handle_result (udb_query_t *q, char **column_values);
-void udb_query_finish_result (udb_query_t *q);
+ char **column_names, size_t column_num, cdtime_t interval);
+int udb_query_handle_result (const udb_query_t const *q,
+ udb_query_preparation_area_t *prep_area, char **column_values);
+void udb_query_finish_result (const udb_query_t const *q,
+ udb_query_preparation_area_t *prep_area);
+
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area (udb_query_t *q);
+void
+udb_query_delete_preparation_area (udb_query_preparation_area_t *q_area);
#endif /* UTILS_DB_QUERY_H */
/* vim: set sw=2 sts=2 et : */
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));
(*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;
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;
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);
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')))
return 1;
}
+#if HAVE_NETINET_IP6_H
static int
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)
struct in6_addr d_addr;
if (ip->ip_v == 6)
- return (handle_ipv6 ((struct ip6_hdr *) ip, len));
+ return (handle_ipv6 ((void *) ip, len));
in6_addr_from_buffer (&s_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET);
in6_addr_from_buffer (&d_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET);
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);
}
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);
}
{
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;
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;
# include <pcap.h>
#endif
+#define DNS_MSG_HDR_SZ 12
+
#define T_MAX 65536
#define OP_MAX 16
#define C_MAX 65536
#include "plugin.h"
#include "common.h"
+#include "utils_cache.h"
#include "utils_format_json.h"
static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
} /* }}} int buffer_add_string */
static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
- const data_set_t *ds, const value_list_t *vl)
+ const data_set_t *ds, const value_list_t *vl, int store_rates)
{
size_t offset = 0;
int i;
+ gauge_t *rates = NULL;
memset (buffer, 0, buffer_size);
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 (-ENOMEM); \
+ } \
else \
- offset += ((size_t) status); \
+ offset += ((size_t) status); \
} while (0)
BUFFER_ADD ("[");
else
BUFFER_ADD ("null");
}
+ else if (store_rates)
+ {
+ if (rates == NULL)
+ rates = uc_get_rate (ds, vl);
+ if (rates == NULL)
+ {
+ WARNING ("utils_format_json: uc_get_rate failed.");
+ sfree(rates);
+ return (-1);
+ }
+
+ if(isfinite (rates[i]))
+ BUFFER_ADD ("%g", rates[i]);
+ else
+ BUFFER_ADD ("null");
+ }
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)
{
ERROR ("format_json: Unknown data source type: %i",
ds->ds[i].type);
+ sfree (rates);
return (-1);
}
} /* for ds->ds_num */
#undef BUFFER_ADD
DEBUG ("format_json: values_to_json: buffer = %s;", buffer);
-
+ sfree(rates);
return (0);
} /* }}} int values_to_json */
-static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
+static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl)
+{
+ size_t offset = 0;
+ int i;
+
+ memset (buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) do { \
+ int status; \
+ status = ssnprintf (buffer + offset, buffer_size - offset, \
+ __VA_ARGS__); \
+ if (status < 1) \
+ return (-1); \
+ else if (((size_t) status) >= (buffer_size - offset)) \
+ return (-ENOMEM); \
+ else \
+ offset += ((size_t) status); \
+} while (0)
+
+ BUFFER_ADD ("[");
+ for (i = 0; i < ds->ds_num; i++)
+ {
+ if (i > 0)
+ BUFFER_ADD (",");
+
+ BUFFER_ADD ("\"%s\"", DS_TYPE_TO_STRING (ds->ds[i].type));
+ } /* for ds->ds_num */
+ BUFFER_ADD ("]");
+
+#undef BUFFER_ADD
+
+ DEBUG ("format_json: dstypes_to_json: buffer = %s;", buffer);
+
+ return (0);
+} /* }}} int dstypes_to_json */
+
+static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */
const data_set_t *ds, const value_list_t *vl)
{
+ size_t offset = 0;
+ int i;
+
+ memset (buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) do { \
+ int status; \
+ status = ssnprintf (buffer + offset, buffer_size - offset, \
+ __VA_ARGS__); \
+ if (status < 1) \
+ return (-1); \
+ else if (((size_t) status) >= (buffer_size - offset)) \
+ return (-ENOMEM); \
+ else \
+ offset += ((size_t) status); \
+} while (0)
+
+ BUFFER_ADD ("[");
+ for (i = 0; i < ds->ds_num; i++)
+ {
+ if (i > 0)
+ BUFFER_ADD (",");
+
+ BUFFER_ADD ("\"%s\"", ds->ds[i].name);
+ } /* for ds->ds_num */
+ BUFFER_ADD ("]");
+
+#undef BUFFER_ADD
+
+ DEBUG ("format_json: dsnames_to_json: buffer = %s;", buffer);
+
+ return (0);
+} /* }}} int dsnames_to_json */
+
+static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl, int store_rates)
+{
char temp[512];
size_t offset = 0;
int status;
* a square bracket in `format_json_finalize'. */
BUFFER_ADD (",{");
- status = values_to_json (temp, sizeof (temp), ds, vl);
+ status = values_to_json (temp, sizeof (temp), ds, vl, store_rates);
if (status != 0)
return (status);
BUFFER_ADD ("\"values\":%s", temp);
+ status = dstypes_to_json (temp, sizeof (temp), ds, vl);
+ if (status != 0)
+ return (status);
+ BUFFER_ADD (",\"dstypes\":%s", temp);
+
+ status = dsnames_to_json (temp, sizeof (temp), ds, vl);
+ if (status != 0)
+ return (status);
+ BUFFER_ADD (",\"dsnames\":%s", temp);
+
BUFFER_ADD (",\"time\":%lu", (unsigned long) vl->time);
- BUFFER_ADD (",\"interval\":%i", vl->interval);
+ BUFFER_ADD (",\"interval\":%.3f", CDTIME_T_TO_DOUBLE (vl->interval));
#define BUFFER_ADD_KEYVAL(key, value) do { \
status = escape_string (temp, sizeof (temp), (value)); \
static int format_json_value_list_nocheck (char *buffer, /* {{{ */
size_t *ret_buffer_fill, size_t *ret_buffer_free,
const data_set_t *ds, const value_list_t *vl,
- size_t temp_size)
+ int store_rates, size_t temp_size)
{
char temp[temp_size];
int status;
- status = value_list_to_json (temp, sizeof (temp), ds, vl);
+ status = value_list_to_json (temp, sizeof (temp), ds, vl, store_rates);
if (status != 0)
return (status);
temp_size = strlen (temp);
int format_json_value_list (char *buffer, /* {{{ */
size_t *ret_buffer_fill, size_t *ret_buffer_free,
- const data_set_t *ds, const value_list_t *vl)
+ const data_set_t *ds, const value_list_t *vl, int store_rates)
{
if ((buffer == NULL)
|| (ret_buffer_fill == NULL) || (ret_buffer_free == NULL)
return (format_json_value_list_nocheck (buffer,
ret_buffer_fill, ret_buffer_free, ds, vl,
- (*ret_buffer_free) - 2));
+ store_rates, (*ret_buffer_free) - 2));
} /* }}} int format_json_value_list */
/* vim: set sw=2 sts=2 et fdm=marker : */
size_t *ret_buffer_fill, size_t *ret_buffer_free);
int format_json_value_list (char *buffer,
size_t *ret_buffer_fill, size_t *ret_buffer_free,
- const data_set_t *ds, const value_list_t *vl);
+ const data_set_t *ds, const value_list_t *vl, int store_rates);
int format_json_finalize (char *buffer,
size_t *ret_buffer_fill, size_t *ret_buffer_free);
return;
if (dir == DIR_UP)
- reheap (h, root / 2, dir);
+ reheap (h, (root - 1) / 2, dir);
else if (dir == DIR_DOWN)
reheap (h, min, dir);
} /* void reheap */
int c_heap_insert (c_heap_t *h, void *ptr)
{
+ size_t index;
+
if ((h == NULL) || (ptr == NULL))
return (-EINVAL);
}
/* Insert the new node as a leaf. */
- h->list[h->list_len] = ptr;
+ index = h->list_len;
+ h->list[index] = ptr;
h->list_len++;
/* Reorganize the heap from bottom up. */
- reheap (h, /* parent of this node = */ (h->list_len - 1) / 2, DIR_UP);
+ reheap (h, /* parent of this node = */ (index - 1) / 2, DIR_UP);
pthread_mutex_unlock (&h->lock);
return (0);
return (l ? l->size : 0);
}
+static int llist_strcmp (llentry_t *e, void *ud)
+{
+ if ((e == NULL) || (ud == NULL))
+ return (-1);
+ return (strcmp (e->key, (const char *)ud));
+}
+
llentry_t *llist_search (llist_t *l, const char *key)
{
+ return (llist_search_custom (l, llist_strcmp, (void *)key));
+}
+
+llentry_t *llist_search_custom (llist_t *l,
+ int (*compare) (llentry_t *, void *), void *user_data)
+{
llentry_t *e;
if (l == NULL)
return (NULL);
- for (e = l->head; e != NULL; e = e->next)
- if (strcmp (key, e->key) == 0)
+ e = l->head;
+ while (e != NULL) {
+ llentry_t *next = e->next;
+
+ if (compare (e, user_data) == 0)
break;
+ e = next;
+ }
+
return (e);
}
int llist_size (llist_t *l);
llentry_t *llist_search (llist_t *l, const char *key);
+llentry_t *llist_search_custom (llist_t *l,
+ int (*compare) (llentry_t *, void *), void *user_data);
llentry_t *llist_head (llist_t *l);
llentry_t *llist_tail (llist_t *l);
if (matches_num < 2)
return (-1);
- value = strtod (matches[1], &endptr);
+ value = (gauge_t) strtod (matches[1], &endptr);
if (matches[1] == endptr)
return (-1);
if (matches_num < 2)
return (-1);
- value = strtoll (matches[1], &endptr, 0);
+ value = (counter_t) strtoull (matches[1], &endptr, 0);
if (matches[1] == endptr)
return (-1);
if (matches_num < 2)
return (-1);
- value = strtoll (matches[1], &endptr, 0);
+ value = (derive_t) strtoll (matches[1], &endptr, 0);
if (matches[1] == endptr)
return (-1);
if (matches_num < 2)
return (-1);
- value = strtoll (matches[1], &endptr, 0);
+ value = (absolute_t) strtoull (matches[1], &endptr, 0);
if (matches[1] == endptr)
return (-1);
return (NULL);
memset (obj, '\0', sizeof (cu_match_t));
- status = regcomp (&obj->regex, regex, REG_EXTENDED);
+ status = regcomp (&obj->regex, regex, REG_EXTENDED | REG_NEWLINE);
if (status != 0)
{
ERROR ("Compiling the regular expression \"%s\" failed.", regex);
/*
* Defines
*/
-#define UTILS_MATCH_DS_TYPE_GAUGE 0x10
-#define UTILS_MATCH_DS_TYPE_COUNTER 0x20
-#define UTILS_MATCH_DS_TYPE_DERIVE 0x30
-#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x40
+#define UTILS_MATCH_DS_TYPE_GAUGE 0x10
+#define UTILS_MATCH_DS_TYPE_COUNTER 0x20
+#define UTILS_MATCH_DS_TYPE_DERIVE 0x40
+#define UTILS_MATCH_DS_TYPE_ABSOLUTE 0x80
#define UTILS_MATCH_CF_GAUGE_AVERAGE 0x01
#define UTILS_MATCH_CF_GAUGE_MIN 0x02
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;
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;
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;
char **ds_def;
int ds_num;
int status = 0;
+ time_t last_up;
+ int stepsize;
if (check_create_dir (filename))
return (-1);
memcpy (argv + ds_num, rra_def, rra_num * sizeof (char *));
argv[ds_num + rra_num] = NULL;
- assert (vl->time > 10);
+ last_up = CDTIME_T_TO_TIME_T (vl->time);
+ if (last_up <= 10)
+ last_up = time (NULL);
+ last_up -= 10;
+
+ if (cfg->stepsize > 0)
+ stepsize = cfg->stepsize;
+ else
+ stepsize = (int) CDTIME_T_TO_TIME_T (vl->interval);
+
status = srrd_create (filename,
- (cfg->stepsize > 0) ? cfg->stepsize : vl->interval,
- vl->time - 10,
+ (cfg->stepsize > 0) ? cfg->stepsize : CDTIME_T_TO_TIME_T (vl->interval),
+ last_up,
argc, (const char **) argv);
free (argv);
while (42)
{
+ size_t len;
+
status = cu_tail_readline (obj, buf, buflen);
if (status != 0)
{
if (buf[0] == 0)
break;
+ len = strlen (buf);
+ while (len > 0) {
+ if (buf[len - 1] != '\n')
+ break;
+ buf[len - 1] = '\0';
+ }
+
status = callback (data, buf, buflen);
if (status != 0)
{
+++ /dev/null
-/**
- * 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 <octo at verplant.org>
- * Sebastian Harl <sh at tokkee.org>
- * Andrés J. Díaz <ajdiaz at connectical.com>
- **/
-
-#include "collectd.h"
-#include "common.h"
-#include "plugin.h"
-#include "utils_avltree.h"
-#include "utils_cache.h"
-#include "utils_threshold.h"
-
-#include <assert.h>
-#include <pthread.h>
-
-/*
- * 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 /* is not inverted */
- {
- 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],
- (values[ds_index] < min) ? "below" : "above",
- (state == STATE_ERROR) ? "failure" : "warning",
- (values[ds_index] < min) ? min : max,
- ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
- }
- 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 : */
+++ /dev/null
-/**
- * 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 <octo at verplant.org>
- **/
-
-#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 */
--- /dev/null
+/**
+ * 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 <ff at octo.it>
+ **/
+
+#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 : */
--- /dev/null
+/**
+ * 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 <ff at octo.it>
+ **/
+
+#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 : */
--- /dev/null
+/**
+ * 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 <jerome.renard at gmail.com>
+ * Marc Fournier <marc.fournier at camptocamp.com>
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+/**
+ * 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 <varnish/varnishapi.h>
+
+/* {{{ user_config_s */
+struct user_config_s {
+ char *instance;
+
+ _Bool collect_cache;
+ _Bool collect_connections;
+ _Bool collect_esi;
+ _Bool collect_backend;
+ _Bool collect_fetch;
+ _Bool collect_hcb;
+ _Bool collect_shm;
+ _Bool collect_sma;
+ _Bool collect_sms;
+ _Bool collect_sm;
+ _Bool collect_totals;
+ _Bool collect_workers;
+};
+typedef struct user_config_s user_config_t; /* }}} */
+
+static _Bool have_instance = 0;
+
+static int varnish_submit (const char *plugin_instance, /* {{{ */
+ const char *category, const char *type, const char *type_instance, value_t value)
+{
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = &value;
+ vl.values_len = 1;
+
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+
+ sstrncpy (vl.plugin, "varnish", sizeof (vl.plugin));
+
+ if (plugin_instance == NULL)
+ plugin_instance = "default";
+
+ ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+ "%s-%s", plugin_instance, category);
+
+ sstrncpy (vl.type, type, sizeof (vl.type));
+
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance,
+ sizeof (vl.type_instance));
+
+ return (plugin_dispatch_values (&vl));
+} /* }}} int varnish_submit */
+
+static int varnish_submit_gauge (const char *plugin_instance, /* {{{ */
+ const char *category, const char *type, const char *type_instance,
+ uint64_t gauge_value)
+{
+ value_t value;
+
+ value.gauge = (gauge_t) gauge_value;
+
+ return (varnish_submit (plugin_instance, category, type, type_instance, value));
+} /* }}} int varnish_submit_gauge */
+
+static int varnish_submit_derive (const char *plugin_instance, /* {{{ */
+ const char *category, const char *type, const char *type_instance,
+ uint64_t derive_value)
+{
+ value_t value;
+
+ value.derive = (derive_t) derive_value;
+
+ return (varnish_submit (plugin_instance, category, type, type_instance, value));
+} /* }}} int varnish_submit_derive */
+
+static void varnish_monitor (const user_config_t *conf, struct varnish_stats *VSL_stats) /* {{{ */
+{
+ if (conf->collect_cache)
+ {
+ /* Cache hits */
+ varnish_submit_derive (conf->instance, "cache", "cache_result", "hit", VSL_stats->cache_hit);
+ /* Cache misses */
+ varnish_submit_derive (conf->instance, "cache", "cache_result", "miss", VSL_stats->cache_miss);
+ /* Cache hits for pass */
+ varnish_submit_derive (conf->instance, "cache", "cache_result", "hitpass", VSL_stats->cache_hitpass);
+ }
+
+ if (conf->collect_connections)
+ {
+ /* Client connections accepted */
+ varnish_submit_derive (conf->instance, "connections", "connections", "accepted", VSL_stats->client_conn);
+ /* Connection dropped, no sess */
+ varnish_submit_derive (conf->instance, "connections", "connections", "dropped" , VSL_stats->client_drop);
+ /* Client requests received */
+ varnish_submit_derive (conf->instance, "connections", "connections", "received", VSL_stats->client_req);
+ }
+
+ if (conf->collect_esi)
+ {
+ /* Objects ESI parsed (unlock) */
+ varnish_submit_derive (conf->instance, "esi", "total_operations", "parsed", VSL_stats->esi_parse);
+ /* ESI parse errors (unlock) */
+ varnish_submit_derive (conf->instance, "esi", "total_operations", "error", VSL_stats->esi_errors);
+ }
+
+ if (conf->collect_backend)
+ {
+ /* Backend conn. success */
+ varnish_submit_derive (conf->instance, "backend", "connections", "success" , VSL_stats->backend_conn);
+ /* Backend conn. not attempted */
+ varnish_submit_derive (conf->instance, "backend", "connections", "not-attempted", VSL_stats->backend_unhealthy);
+ /* Backend conn. too many */
+ varnish_submit_derive (conf->instance, "backend", "connections", "too-many" , VSL_stats->backend_busy);
+ /* Backend conn. failures */
+ varnish_submit_derive (conf->instance, "backend", "connections", "failures" , VSL_stats->backend_fail);
+ /* Backend conn. reuses */
+ varnish_submit_derive (conf->instance, "backend", "connections", "reuses" , VSL_stats->backend_reuse);
+ /* Backend conn. was closed */
+ varnish_submit_derive (conf->instance, "backend", "connections", "was-closed" , VSL_stats->backend_toolate);
+ /* Backend conn. recycles */
+ varnish_submit_derive (conf->instance, "backend", "connections", "recycled" , VSL_stats->backend_recycle);
+ /* Backend conn. unused */
+ varnish_submit_derive (conf->instance, "backend", "connections", "unused" , VSL_stats->backend_unused);
+ }
+
+ if (conf->collect_fetch)
+ {
+ /* Fetch head */
+ varnish_submit_derive (conf->instance, "fetch", "http_requests", "head" , VSL_stats->fetch_head);
+ /* Fetch with length */
+ varnish_submit_derive (conf->instance, "fetch", "http_requests", "length" , VSL_stats->fetch_length);
+ /* Fetch chunked */
+ varnish_submit_derive (conf->instance, "fetch", "http_requests", "chunked" , VSL_stats->fetch_chunked);
+ /* Fetch EOF */
+ varnish_submit_derive (conf->instance, "fetch", "http_requests", "eof" , VSL_stats->fetch_eof);
+ /* Fetch bad headers */
+ varnish_submit_derive (conf->instance, "fetch", "http_requests", "bad_headers", VSL_stats->fetch_bad);
+ /* Fetch wanted close */
+ varnish_submit_derive (conf->instance, "fetch", "http_requests", "close" , VSL_stats->fetch_close);
+ /* Fetch pre HTTP/1.1 closed */
+ varnish_submit_derive (conf->instance, "fetch", "http_requests", "oldhttp" , VSL_stats->fetch_oldhttp);
+ /* Fetch zero len */
+ varnish_submit_derive (conf->instance, "fetch", "http_requests", "zero" , VSL_stats->fetch_zero);
+ /* Fetch failed */
+ varnish_submit_derive (conf->instance, "fetch", "http_requests", "failed" , VSL_stats->fetch_failed);
+ }
+
+ if (conf->collect_hcb)
+ {
+ /* HCB Lookups without lock */
+ varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_nolock", VSL_stats->hcb_nolock);
+ /* HCB Lookups with lock */
+ varnish_submit_derive (conf->instance, "hcb", "cache_operation", "lookup_lock", VSL_stats->hcb_lock);
+ /* HCB Inserts */
+ varnish_submit_derive (conf->instance, "hcb", "cache_operation", "insert", VSL_stats->hcb_insert);
+ }
+
+ if (conf->collect_shm)
+ {
+ /* SHM records */
+ varnish_submit_derive (conf->instance, "shm", "total_operations", "records" , VSL_stats->shm_records);
+ /* SHM writes */
+ varnish_submit_derive (conf->instance, "shm", "total_operations", "writes" , VSL_stats->shm_writes);
+ /* SHM flushes due to overflow */
+ varnish_submit_derive (conf->instance, "shm", "total_operations", "flushes" , VSL_stats->shm_flushes);
+ /* SHM MTX contention */
+ varnish_submit_derive (conf->instance, "shm", "total_operations", "contention", VSL_stats->shm_cont);
+ /* SHM cycles through buffer */
+ varnish_submit_derive (conf->instance, "shm", "total_operations", "cycles" , VSL_stats->shm_cycles);
+ }
+
+ if (conf->collect_sm)
+ {
+ /* allocator requests */
+ varnish_submit_derive (conf->instance, "sm", "total_requests", "nreq", VSL_stats->sm_nreq);
+ /* outstanding allocations */
+ varnish_submit_gauge (conf->instance, "sm", "requests", "outstanding", VSL_stats->sm_nobj);
+ /* bytes allocated */
+ varnish_submit_gauge (conf->instance, "sm", "total_bytes", "allocated", VSL_stats->sm_balloc);
+ /* bytes free */
+ varnish_submit_gauge (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_gauge (conf->instance, "sma", "total_bytes", "allocated", VSL_stats->sma_balloc);
+ /* SMA bytes free */
+ varnish_submit_gauge (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_gauge (conf->instance, "sms", "total_bytes", "allocated", VSL_stats->sms_balloc);
+ /* SMS bytes freed */
+ varnish_submit_gauge (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_gauge (conf->instance, "workers", "total_threads", "created", VSL_stats->n_wrk_create);
+ /* worker threads not created */
+ varnish_submit_gauge (conf->instance, "workers", "total_threads", "failed", VSL_stats->n_wrk_failed);
+ /* worker threads limited */
+ varnish_submit_gauge (conf->instance, "workers", "total_threads", "limited", VSL_stats->n_wrk_max);
+ /* queued work requests */
+ varnish_submit_gauge (conf->instance, "workers", "total_requests", "queued", VSL_stats->n_wrk_queue);
+ /* overflowed work requests */
+ varnish_submit_gauge (conf->instance, "workers", "total_requests", "overflowed", VSL_stats->n_wrk_overflow);
+ /* dropped work requests */
+ varnish_submit_gauge (conf->instance, "workers", "total_requests", "dropped", VSL_stats->n_wrk_drop);
+ }
+} /* }}} void varnish_monitor */
+
+static int varnish_read (user_data_t *ud) /* {{{ */
+{
+ struct varnish_stats *VSL_stats;
+ user_config_t *conf;
+
+ if ((ud == NULL) || (ud->data == NULL))
+ return (EINVAL);
+
+ conf = ud->data;
+
+ VSL_stats = VSL_OpenStats (conf->instance);
+ if (VSL_stats == NULL)
+ {
+ ERROR ("Varnish plugin : unable to load statistics");
+
+ return (-1);
+ }
+
+ varnish_monitor (conf, VSL_stats);
+
+ return (0);
+} /* }}} */
+
+static void varnish_config_free (void *ptr) /* {{{ */
+{
+ user_config_t *conf = ptr;
+
+ if (conf == NULL)
+ return;
+
+ sfree (conf->instance);
+ sfree (conf);
+} /* }}} */
+
+static int varnish_config_apply_default (user_config_t *conf) /* {{{ */
+{
+ if (conf == NULL)
+ return (EINVAL);
+
+ conf->collect_backend = 1;
+ conf->collect_cache = 1;
+ conf->collect_connections = 1;
+ conf->collect_esi = 0;
+ conf->collect_fetch = 0;
+ conf->collect_hcb = 0;
+ conf->collect_shm = 1;
+ conf->collect_sm = 0;
+ conf->collect_sma = 0;
+ conf->collect_sms = 0;
+ conf->collect_totals = 0;
+
+ return (0);
+} /* }}} int varnish_config_apply_default */
+
+static int varnish_init (void) /* {{{ */
+{
+ user_config_t *conf;
+ user_data_t ud;
+
+ if (have_instance)
+ return (0);
+
+ conf = malloc (sizeof (*conf));
+ if (conf == NULL)
+ return (ENOMEM);
+ memset (conf, 0, sizeof (*conf));
+
+ /* Default settings: */
+ conf->instance = NULL;
+
+ varnish_config_apply_default (conf);
+
+ ud.data = conf;
+ ud.free_func = varnish_config_free;
+
+ plugin_register_complex_read (/* group = */ "varnish",
+ /* name = */ "varnish/localhost",
+ /* callback = */ varnish_read,
+ /* interval = */ NULL,
+ /* user data = */ &ud);
+
+ return (0);
+} /* }}} int varnish_init */
+
+static int varnish_config_instance (const oconfig_item_t *ci) /* {{{ */
+{
+ user_config_t *conf;
+ user_data_t ud;
+ char callback_name[DATA_MAX_NAME_LEN];
+ int i;
+
+ conf = malloc (sizeof (*conf));
+ if (conf == NULL)
+ return (ENOMEM);
+ memset (conf, 0, sizeof (*conf));
+ conf->instance = NULL;
+
+ varnish_config_apply_default (conf);
+
+ if (ci->values_num == 1)
+ {
+ int status;
+
+ status = cf_util_get_string (ci, &conf->instance);
+ if (status != 0)
+ {
+ sfree (conf);
+ return (status);
+ }
+ assert (conf->instance != NULL);
+
+ if (strcmp ("localhost", conf->instance) == 0)
+ {
+ sfree (conf->instance);
+ conf->instance = NULL;
+ }
+ }
+ else if (ci->values_num > 1)
+ {
+ WARNING ("Varnish plugin: \"Instance\" blocks accept only "
+ "one argument.");
+ return (EINVAL);
+ }
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("CollectCache", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_cache);
+ else if (strcasecmp ("CollectConnections", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_connections);
+ else if (strcasecmp ("CollectESI", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_esi);
+ else if (strcasecmp ("CollectBackend", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_backend);
+ else if (strcasecmp ("CollectFetch", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_fetch);
+ else if (strcasecmp ("CollectHCB", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_hcb);
+ else if (strcasecmp ("CollectSHM", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_shm);
+ else if (strcasecmp ("CollectSMA", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_sma);
+ else if (strcasecmp ("CollectSMS", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_sms);
+ else if (strcasecmp ("CollectSM", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_sm);
+ else if (strcasecmp ("CollectTotals", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_totals);
+ else if (strcasecmp ("CollectWorkers", child->key) == 0)
+ cf_util_get_boolean (child, &conf->collect_workers);
+ else
+ {
+ WARNING ("Varnish plugin: Ignoring unknown "
+ "configuration option: \"%s\"",
+ child->key);
+ }
+ }
+
+ if (!conf->collect_cache
+ && !conf->collect_connections
+ && !conf->collect_esi
+ && !conf->collect_backend
+ && !conf->collect_fetch
+ && !conf->collect_hcb
+ && !conf->collect_shm
+ && !conf->collect_sma
+ && !conf->collect_sms
+ && !conf->collect_sm
+ && !conf->collect_totals
+ && !conf->collect_workers)
+ {
+ WARNING ("Varnish plugin: No metric has been configured for "
+ "instance \"%s\". Disabling this instance.",
+ (conf->instance == NULL) ? "localhost" : conf->instance);
+ return (EINVAL);
+ }
+
+ ssnprintf (callback_name, sizeof (callback_name), "varnish/%s",
+ (conf->instance == NULL) ? "localhost" : conf->instance);
+
+ ud.data = conf;
+ ud.free_func = varnish_config_free;
+
+ plugin_register_complex_read (/* group = */ "varnish",
+ /* name = */ callback_name,
+ /* callback = */ varnish_read,
+ /* interval = */ NULL,
+ /* user data = */ &ud);
+
+ have_instance = 1;
+
+ return (0);
+} /* }}} int varnish_config_instance */
+
+static int varnish_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Instance", child->key) == 0)
+ varnish_config_instance (child);
+ else
+ {
+ WARNING ("Varnish plugin: Ignoring unknown "
+ "configuration option: \"%s\"",
+ child->key);
+ }
+ }
+
+ return (0);
+} /* }}} int varnish_config */
+
+void module_register (void) /* {{{ */
+{
+ plugin_register_complex_config ("varnish", varnish_config);
+ plugin_register_init ("varnish", varnish_init);
+} /* }}} */
+
+/* vim: set sw=8 noet fdm=marker : */
/**
* 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
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
- * Florian octo Forster <octo at verplant.org>
+ * Florian octo Forster <octo at collectd.org>
**/
#include "collectd.h"
} /* 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 */
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;
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));
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);
}
*/
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) */
/**
* 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
} /* 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);
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)
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)
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);
int verify_peer;
int verify_host;
char *cacert;
+ int store_rates;
#define WH_FORMAT_COMMAND 0
#define WH_FORMAT_JSON 1
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;
};
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)
{
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);
}
{
if (cb->send_buffer_fill <= 0)
{
- cb->send_buffer_init_time = time (NULL);
+ cb->send_buffer_init_time = cdtime ();
return (0);
}
{
if (cb->send_buffer_fill <= 2)
{
- cb->send_buffer_init_time = time (NULL);
+ cb->send_buffer_init_time = cdtime ();
return (0);
}
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)
{
cb = data;
- wh_flush_nolock (/* timeout = */ -1, cb);
+ wh_flush_nolock (/* timeout = */ 0, cb);
curl_easy_cleanup (cb->curl);
sfree (cb->location);
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)
-{
- size_t offset = 0;
- int status;
- int i;
-
- 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) \
- return (-1); \
- else if (((size_t) status) >= (buffer_size - offset)) \
- 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 (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);
- return (-1);
- }
-} /* for ds->ds_num */
-
-#undef BUFFER_ADD
-
-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)
{
/* Convert the values to an ASCII representation and put that into
* `values'. */
- status = wh_value_list_to_string (values, sizeof (values), ds, vl);
+ 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");
}
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);
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);
status = format_json_value_list (cb->send_buffer,
&cb->send_buffer_fill,
&cb->send_buffer_free,
- ds, 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);
status = format_json_value_list (cb->send_buffer,
&cb->send_buffer_fill,
&cb->send_buffer_free,
- ds, vl);
+ ds, vl, cb->store_rates);
}
if (status != 0)
{
config_set_string (&cb->cacert, child);
else if (strcasecmp ("Format", child->key) == 0)
config_set_format (cb, child);
+ else if (strcasecmp ("StoreRates", child->key) == 0)
+ config_set_boolean (&cb->store_rates, child);
else
{
ERROR ("write_http plugin: Invalid configuration "
--- /dev/null
+/**
+ * collectd - src/write_redis.c
+ * Copyright (C) 2010 Florian Forster
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Florian Forster <ff at octo.it>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "configfile.h"
+
+#include <pthread.h>
+#include <credis.h>
+
+struct wr_node_s
+{
+ char name[DATA_MAX_NAME_LEN];
+
+ char *host;
+ int port;
+ int timeout;
+
+ REDIS conn;
+ pthread_mutex_t lock;
+};
+typedef struct wr_node_s wr_node_t;
+
+/*
+ * Functions
+ */
+static int wr_write (const data_set_t *ds, /* {{{ */
+ const value_list_t *vl,
+ user_data_t *ud)
+{
+ wr_node_t *node = ud->data;
+ char ident[512];
+ char key[512];
+ char value[512];
+ size_t value_size;
+ char *value_ptr;
+ int status;
+ int i;
+
+ status = FORMAT_VL (ident, sizeof (ident), vl);
+ if (status != 0)
+ return (status);
+ ssnprintf (key, sizeof (key), "collectd/%s", ident);
+
+ memset (value, 0, sizeof (value));
+ value_size = sizeof (value);
+ value_ptr = &value[0];
+
+#define APPEND(...) do { \
+ status = snprintf (value_ptr, value_size, __VA_ARGS__); \
+ if (((size_t) status) > value_size) \
+ { \
+ value_ptr += value_size; \
+ value_size = 0; \
+ } \
+ else \
+ { \
+ value_ptr += status; \
+ value_size -= status; \
+ } \
+} while (0)
+
+ APPEND ("%lu", (unsigned long) vl->time);
+ for (i = 0; i < ds->ds_num; i++)
+ {
+ if (ds->ds[i].type == DS_TYPE_COUNTER)
+ APPEND ("%llu", vl->values[i].counter);
+ else if (ds->ds[i].type == DS_TYPE_GAUGE)
+ APPEND ("%g", vl->values[i].gauge);
+ else if (ds->ds[i].type == DS_TYPE_DERIVE)
+ APPEND ("%"PRIi64, vl->values[i].derive);
+ else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+ APPEND ("%"PRIu64, vl->values[i].absolute);
+ else
+ assert (23 == 42);
+ }
+
+#undef APPEND
+
+ pthread_mutex_lock (&node->lock);
+
+ if (node->conn == NULL)
+ {
+ node->conn = credis_connect (node->host, node->port, node->timeout);
+ if (node->conn == NULL)
+ {
+ ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed.",
+ (node->host != NULL) ? node->host : "localhost",
+ (node->port != 0) ? node->port : 6379);
+ pthread_mutex_unlock (&node->lock);
+ return (-1);
+ }
+ }
+
+ /* "credis_zadd" doesn't handle a NULL pointer gracefully, so I'd rather
+ * have a meaningful assertion message than a normal segmentation fault. */
+ assert (node->conn != NULL);
+ status = credis_zadd (node->conn, key, (double) vl->time, value);
+
+ credis_sadd (node->conn, "collectd/values", ident);
+
+ pthread_mutex_unlock (&node->lock);
+
+ return (0);
+} /* }}} int wr_write */
+
+static void wr_config_free (void *ptr) /* {{{ */
+{
+ wr_node_t *node = ptr;
+
+ if (node == NULL)
+ return;
+
+ if (node->conn != NULL)
+ {
+ credis_close (node->conn);
+ node->conn = NULL;
+ }
+
+ sfree (node->host);
+ sfree (node);
+} /* }}} void wr_config_free */
+
+static int wr_config_node (oconfig_item_t *ci) /* {{{ */
+{
+ wr_node_t *node;
+ int status;
+ int i;
+
+ node = malloc (sizeof (*node));
+ if (node == NULL)
+ return (ENOMEM);
+ memset (node, 0, sizeof (*node));
+ node->host = NULL;
+ node->port = 0;
+ node->timeout = 1000;
+ node->conn = NULL;
+ pthread_mutex_init (&node->lock, /* attr = */ NULL);
+
+ status = cf_util_get_string_buffer (ci, node->name, sizeof (node->name));
+ if (status != 0)
+ {
+ sfree (node);
+ return (status);
+ }
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Host", child->key) == 0)
+ status = cf_util_get_string (child, &node->host);
+ else if (strcasecmp ("Port", child->key) == 0)
+ {
+ status = cf_util_get_port_number (child);
+ if (status > 0)
+ {
+ node->port = status;
+ status = 0;
+ }
+ }
+ else if (strcasecmp ("Timeout", child->key) == 0)
+ status = cf_util_get_int (child, &node->timeout);
+ else
+ WARNING ("write_redis plugin: Ignoring unknown config option \"%s\".",
+ child->key);
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ if (status == 0)
+ {
+ char cb_name[DATA_MAX_NAME_LEN];
+ user_data_t ud;
+
+ ssnprintf (cb_name, sizeof (cb_name), "write_redis/%s", node->name);
+
+ ud.data = node;
+ ud.free_func = wr_config_free;
+
+ status = plugin_register_write (cb_name, wr_write, &ud);
+ }
+
+ if (status != 0)
+ wr_config_free (node);
+
+ return (status);
+} /* }}} int wr_config_node */
+
+static int wr_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Node", child->key) == 0)
+ wr_config_node (child);
+ else
+ WARNING ("write_redis plugin: Ignoring unknown "
+ "configuration option \"%s\" at top level.", child->key);
+ }
+
+ return (0);
+} /* }}} int wr_config */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("write_redis", wr_config);
+}
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
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];
get_kstat (&ksp, "zfs", 0, "arcstats");
if (ksp == NULL)
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");
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) /* {{{ */
{
#!/bin/sh
-DEFAULT_VERSION="4.8.2.git"
+DEFAULT_VERSION="5.0.0.git"
VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"
VERSION="`echo \"$VERSION\" | sed -e 's/-/./g'`"
-if test "x`uname -s`" = "xAIX" ; then
+if test "x`uname -s`" = "xAIX" || test "x`uname -s`" = "xSunOS" ; then
echo "$VERSION\c"
else
echo -n "$VERSION"