-# svn stuff:
-.svn
-contrib/.svn
-debian/.svn
-src/.svn
-src/libconfig/.svn
-src/liboping/.svn
-
# build.sh stuff:
-INSTALL
Makefile.in
-aclocal.m4
-autom4te.cache
-compile
-config.guess
-config.sub
-configure
-depcomp
-install-sh
-libltdl
-ltmain.sh
-missing
-src/Makefile.in
+/INSTALL
+/aclocal.m4
+/autom4te.cache
+/autom4te.cache
+/compile
+/config.guess
+/config.sub
+/configure
+/depcomp
+/install-sh
+/libltdl/
+/ltmain.sh
+/missing
src/config.h.in
-src/libconfig/Makefile.in
-src/liboping/Makefile.in
# configure stuff:
Makefile
src/stamp-h1
# make stuff:
-src/.libs
-src/*.la
-src/*.lo
-src/*.o
+*.la
+*.lo
+*.o
+.libs/
src/collectd
src/collectd-nagios
src/collectdmon
-src/collectd*.1
-src/collectd*.5
-src/types.db.5
-src/config.h.in~
-src/libcollectdclient/.libs
-src/libcollectdclient/*.la
-src/libcollectdclient/*.lo
+src/*.1
+src/*.5
src/libcollectdclient/lcc_features.h
-src/libiptc/.libs
-src/libiptc/*.la
-src/libiptc/*.lo
-src/liboconfig/.libs
-src/liboconfig/*.la
-src/liboconfig/*.lo
-src/liboping/.libs
-src/liboping/*.la
-src/liboping/*.lo
+
+# patch stuff
+*.rej
+*.orig
# lex / yacc stuff:
ylwrap
src/liboconfig/parser.h
src/liboconfig/scanner.c
+# make dist stuff:
+/collectd-*.tar.gz
+/collectd-*.tar.bz2
+
# perl stuff:
bindings/.perl-directory-stamp
bindings/perl/Collectd/pm_to_blib
bindings/perl/blib/
bindings/perl/pm_to_blib
-# make dist stuff:
-collectd-*.tar.gz
-collectd-*.tar.bz2
+# java stuff
+bindings/java/java-build-stamp
+bindings/java/org/collectd/api/*.class
Amit Gupta <amit.gupta221 at gmail.com>
- Multiple servers in the apache plugin.
+Anthony Dewhurst <dewhurst at gmail.com>
+ - zfs_arc plugin.
+
Anthony Gialluca <tonyabg at charter.net>
- apcups plugin.
- OpenVPN plugin.
- jcollectd (two-way JMX integration).
- A few other patches to various plugins.
+ - curl_json plugin.
Edward “Koko” Konetzko <konetzed at quixoticagony.com>
- fscache plugin.
+ the disk plugin, and
+ the users plugin.
+Ondrej Zajicek <santiago at crfreenet.org>
+ - madwifi plugin.
+
+Paul Sadauskas <psadauskas at gmail.com>
+ - tokyotyrant plugin.
+ - `ReportByDevice' option of the df plugin.
+ - write_http plugin.
+
Peter Holik <peter at holik.at>
- cpufreq plugin.
- multimeter plugin.
+2009-09-13, Version 4.8.0
+ * collectd: Two new data source types, “DERIVE” and “ABSOLUTE”, have
+ been added. “DERIVE” can be used for counters that are reset
+ occasionally. Thanks to Mariusz Gronczewski for implementing this.
+ * thresholds: The advanced threshold options “Percentage”, “Hits”, and
+ “Hysteresis” have been added. Thanks to Andrés J. Díaz for hit
+ patches.
+ * curl_json plugin: The new cURL-JSON plugin reads JSON files using
+ the cURL library and parses the contents according to user
+ specification. Among other things, this allows to read statistics
+ from a CouchDB instance. Thanks to Doug MacEachern for the patch.
+ * df plugin: Using the new “ReportByDevice” option the device rather
+ than the mount point can be used to identify partitions. Thanks to
+ Paul Sadauskas for the patch.
+ * dns plugin: The possibility to ignore numeric QTypes has been added.
+ Thanks to Mirko Buffoni for the patch.
+ * GenericJMX plugin: The new, Java-based GenericJMX plugin allows to
+ query arbitrary data from a Java process using the “Java Management
+ Extensions” (JMX).
+ * madwifi plugin: The new MadWifi plugin collects information about
+ Atheros wireless LAN chipsets from the MadWifi driver. Thanks to
+ Ondrej Zajicek for his patches.
+ * network plugin: The receive- and send-buffer-sizes have been made
+ configurable, allowing for bigger and smaller packets. Thanks to
+ Aman Gupta for the patch.
+ * olsrd plugin: The new OLSRd plugin queries routing information from
+ the “Optimized Link State Routing” daemon.
+ * rrdtool plugin: A new configuration option allows to define a random
+ write delay when writing RRD files. This spreads the load created by
+ writing RRD files more evenly. Thanks to Mariusz Gronczewski for the
+ patch.
+ * swap plugin: The possibility to collect swapped in/out pages has
+ been added to the Swap plugin. Thanks to Stefan Völkel for the
+ patch.
+ * tokyotyrant plugin: The new TokyoTyrant plugin reads the number of
+ records and file size from a running Tokyo Tyrant server. Thanks to
+ Paul Sadauskas for the patch.
+ * unixsock plugin: Add the “GETTHRESHOLD” command. This command can be
+ used to query the thresholds configured for a particular identifier.
+ * write_http plugin: The new Write HTTP plugin sends the values
+ collected by collectd to a web-server using HTTP POST requests.
+ Thanks to Paul Sadauskas for the patch.
+ * zfs_arc plugin: The new ZFS ARC plugin collects information about
+ the “Adaptive Replacement Cache” (ARC) of the “Zeta File-System”
+ (ZFS). Thanks to Anthony Dewhurst for the patch.
+ * empty_counter match: The new Empty Counter match matches value
+ lists, where at least one data source is of type COUNTER and the
+ counter value of all counter data sources is zero.
+
2009-09-13, Version 4.7.3
* collectd: Fix a possible but very rare invalid “free” in the caching
code. Thanks to Sebastian Harl for the patch.
- conntrack
Number of nf_conntrack entries.
+ - curl_json
+ Retrieves JSON data via cURL and parses it according to user
+ configuration.
+
- cpu
CPU utilization: Time spent in the system, user, nice, idle, and related
states.
- libvirt
CPU, disk and network I/O statistics from virtual machines.
+ - madwifi
+ Queries very detailed usage statistics from wireless LAN adapters and
+ interfaces that use the Atheros chipset and the MadWifi driver.
+
- mbmon
Motherboard sensors: temperature, fanspeed and voltage information,
using mbmon(1).
Network UPS tools: UPS current, voltage, power, charge, utilisation,
temperature, etc. See upsd(8).
+ - olsr
+ Queries routing information from the “Optimized Link State Routing”
+ daemon.
+
- onewire (EXPERIMENTAL!)
Read onewire sensors using the owcapu library of the owfs project.
Please read in collectd.conf(5) why this plugin is experimental.
- thermal
Linux ACPI thermal zone information.
+ - tokyotyrant
+ Reads the number of records and file size from a running Tokyo Tyrant
+ server.
+
- uptime
System uptime statistics.
- xmms
Bitrate and frequency of music played with XMMS.
+ - zfs_arc
+ Statistics for ZFS' “Adaptive Replacement Cache” (ARC).
+
* Output can be written or send to various destinations by the following
plugins:
needed. Please read collectd-unixsock(5) for a description on how that's
done.
+ - write_http
+ Sends the values collected by collectd to a web-server using HTTP POST
+ requests. The transmitted data is either in a form understood by the
+ Exec plugin or formatted in JSON.
+
* Logging is, as everything in collectd, provided by plugins. The following
plugins keep up informed about what's going on:
* Value processing can be controlled using the "filter chain" infrastructure
and "matches" and "targets". The following plugins are available:
+ - match_empty_counter
+ Match counter values which are currently zero.
+
- match_regex
Match values by their identifier based on regular expressions.
Used by the `oracle' plugin.
* libcurl (optional)
- If you want to use the `apache', `ascent', `curl' or `nginx' plugin.
+ If you want to use the `apache', `ascent', `curl', `nginx', or `write_http'
+ plugin.
<http://curl.haxx.se/>
* libdbi (optional)
* libxmms (optional)
<http://www.xmms.org/>
+ * libyajl (optional)
+ Parse JSON data. This is needed for the `curl_json' plugin.
+ <http://www.lloydforge.org/projects/yajl/>
Configuring / Compiling / Installing
------------------------------------
org/collectd/api/OConfigItem.java \
org/collectd/api/OConfigValue.java \
org/collectd/api/PluginData.java \
- org/collectd/api/ValueList.java
+ org/collectd/api/ValueList.java \
+ org/collectd/java/GenericJMXConfConnection.java \
+ org/collectd/java/GenericJMXConfMBean.java \
+ org/collectd/java/GenericJMXConfValue.java \
+ org/collectd/java/GenericJMX.java \
+ org/collectd/java/JMXMemory.java
java-build-stamp: org/collectd/api/*.java
$(JAVAC) -d "." "$(srcdir)/org/collectd/api"/*.java
+ $(JAVAC) -d "." "$(srcdir)/org/collectd/java"/*.java
touch "$@"
all-local: java-build-stamp
mkdir -p "$(DESTDIR)$(pkgdatadir)/java/org/collectd/api"
$(INSTALL) -m 644 "org/collectd/api"/*.class \
"$(DESTDIR)$(pkgdatadir)/java/org/collectd/api/"
+ mkdir -p "$(DESTDIR)$(pkgdatadir)/java/org/collectd/java"
+ $(INSTALL) -m 644 "org/collectd/java"/*.class \
+ "$(DESTDIR)$(pkgdatadir)/java/org/collectd/java/"
clean-local:
rm -f "org/collectd/api"/*.class
+ rm -f "org/collectd/java"/*.class
rm -f "java-build-stamp"
* Java representation of collectd/src/plugin.h:data_source_t structure.
*/
public class DataSource {
- public static final int TYPE_COUNTER = 0;
- public static final int TYPE_GAUGE = 1;
+ public static final int TYPE_COUNTER = 0;
+ public static final int TYPE_GAUGE = 1;
+ public static final int TYPE_DERIVE = 2;
+ public static final int TYPE_ABSOLUTE = 3;
- static final String COUNTER = "COUNTER";
- static final String GAUGE = "GAUGE";
+ static final String COUNTER = "COUNTER";
+ static final String GAUGE = "GAUGE";
+ static final String DERIVE = "DERIVE";
+ static final String ABSOLUTE = "ABSOLUTE";
static final String NAN = "U";
- private static final String[] TYPES = { COUNTER, GAUGE };
+ private static final String[] TYPES = { COUNTER, GAUGE, DERIVE, ABSOLUTE };
String _name;
int _type;
this._type = TYPE_GAUGE;
if (type == TYPE_COUNTER)
this._type = TYPE_COUNTER;
+ else if (type == TYPE_DERIVE)
+ this._type = TYPE_DERIVE;
+ else if (type == TYPE_ABSOLUTE)
+ this._type = TYPE_ABSOLUTE;
this._min = min;
this._max = max;
}
--- /dev/null
+/*
+ * collectd/java - org/collectd/java/GenericJMX.java
+ * Copyright (C) 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
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ */
+
+package org.collectd.java;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.collectd.api.Collectd;
+import org.collectd.api.CollectdConfigInterface;
+import org.collectd.api.CollectdInitInterface;
+import org.collectd.api.CollectdReadInterface;
+import org.collectd.api.CollectdShutdownInterface;
+import org.collectd.api.OConfigValue;
+import org.collectd.api.OConfigItem;
+
+public class GenericJMX implements CollectdConfigInterface,
+ CollectdReadInterface,
+ CollectdShutdownInterface
+{
+ static private Map<String,GenericJMXConfMBean> _mbeans
+ = new TreeMap<String,GenericJMXConfMBean> ();
+
+ private List<GenericJMXConfConnection> _connections = null;
+
+ public GenericJMX ()
+ {
+ Collectd.registerConfig ("GenericJMX", this);
+ Collectd.registerRead ("GenericJMX", this);
+ Collectd.registerShutdown ("GenericJMX", this);
+
+ this._connections = new ArrayList<GenericJMXConfConnection> ();
+ }
+
+ public int config (OConfigItem ci) /* {{{ */
+ {
+ List<OConfigItem> children;
+ int i;
+
+ Collectd.logDebug ("GenericJMX plugin: config: ci = " + ci + ";");
+
+ children = ci.getChildren ();
+ for (i = 0; i < children.size (); i++)
+ {
+ OConfigItem child;
+ String key;
+
+ child = children.get (i);
+ key = child.getKey ();
+ if (key.equalsIgnoreCase ("MBean"))
+ {
+ try
+ {
+ GenericJMXConfMBean mbean = new GenericJMXConfMBean (child);
+ putMBean (mbean);
+ }
+ catch (IllegalArgumentException e)
+ {
+ Collectd.logError ("GenericJMX plugin: "
+ + "Evaluating `MBean' block failed: " + e);
+ }
+ }
+ else if (key.equalsIgnoreCase ("Connection"))
+ {
+ try
+ {
+ GenericJMXConfConnection conn = new GenericJMXConfConnection (child);
+ this._connections.add (conn);
+ }
+ catch (IllegalArgumentException e)
+ {
+ Collectd.logError ("GenericJMX plugin: "
+ + "Evaluating `Connection' block failed: " + e);
+ }
+ }
+ else
+ {
+ Collectd.logError ("GenericJMX plugin: Unknown config option: " + key);
+ }
+ } /* for (i = 0; i < children.size (); i++) */
+
+ return (0);
+ } /* }}} int config */
+
+ public int read () /* {{{ */
+ {
+ for (int i = 0; i < this._connections.size (); i++)
+ {
+ try
+ {
+ this._connections.get (i).query ();
+ }
+ catch (Exception e)
+ {
+ Collectd.logError ("GenericJMX: Caught unexpected exception: " + e);
+ e.printStackTrace ();
+ }
+ }
+
+ return (0);
+ } /* }}} int read */
+
+ public int shutdown () /* {{{ */
+ {
+ System.out.print ("org.collectd.java.GenericJMX.Shutdown ();\n");
+ this._connections = null;
+ return (0);
+ } /* }}} int shutdown */
+
+ /*
+ * static functions
+ */
+ static public GenericJMXConfMBean getMBean (String alias)
+ {
+ return (_mbeans.get (alias));
+ }
+
+ static private void putMBean (GenericJMXConfMBean mbean)
+ {
+ Collectd.logDebug ("GenericJMX.putMBean: Adding " + mbean.getName ());
+ _mbeans.put (mbean.getName (), mbean);
+ }
+} /* class GenericJMX */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
--- /dev/null
+/*
+ * collectd/java - org/collectd/java/GenericJMXConfConnection.java
+ * Copyright (C) 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
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ */
+
+package org.collectd.java;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+
+import javax.management.remote.JMXServiceURL;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+
+import org.collectd.api.Collectd;
+import org.collectd.api.PluginData;
+import org.collectd.api.OConfigValue;
+import org.collectd.api.OConfigItem;
+
+class GenericJMXConfConnection
+{
+ private String _username = null;
+ private String _password = null;
+ private String _host = null;
+ private String _service_url = null;
+ private MBeanServerConnection _jmx_connection = null;
+ private List<GenericJMXConfMBean> _mbeans = null;
+
+ /*
+ * private methods
+ */
+ private String getConfigString (OConfigItem ci) /* {{{ */
+ {
+ List<OConfigValue> values;
+ OConfigValue v;
+
+ values = ci.getValues ();
+ if (values.size () != 1)
+ {
+ Collectd.logError ("GenericJMXConfConnection: The " + ci.getKey ()
+ + " configuration option needs exactly one string argument.");
+ return (null);
+ }
+
+ v = values.get (0);
+ if (v.getType () != OConfigValue.OCONFIG_TYPE_STRING)
+ {
+ Collectd.logError ("GenericJMXConfConnection: The " + ci.getKey ()
+ + " configuration option needs exactly one string argument.");
+ return (null);
+ }
+
+ return (v.getString ());
+ } /* }}} String getConfigString */
+
+private void connect () /* {{{ */
+{
+ JMXServiceURL service_url;
+ JMXConnector connector;
+ Map environment;
+
+ if (_jmx_connection != null)
+ return;
+
+ environment = null;
+ if (this._password != null)
+ {
+ String[] credentials;
+
+ if (this._username == null)
+ this._username = new String ("monitorRole");
+
+ credentials = new String[] { this._username, this._password };
+
+ environment = new HashMap ();
+ environment.put (JMXConnector.CREDENTIALS, credentials);
+ }
+
+ try
+ {
+ service_url = new JMXServiceURL (this._service_url);
+ connector = JMXConnectorFactory.connect (service_url, environment);
+ _jmx_connection = connector.getMBeanServerConnection ();
+ }
+ catch (Exception e)
+ {
+ Collectd.logError ("GenericJMXConfConnection: "
+ + "Creating MBean server connection failed: " + e);
+ return;
+ }
+} /* }}} void connect */
+
+/*
+ * public methods
+ *
+ * <Connection>
+ * Host "tomcat0.mycompany"
+ * ServiceURL "service:jmx:rmi:///jndi/rmi://localhost:17264/jmxrmi"
+ * Collect "java.lang:type=GarbageCollector,name=Copy"
+ * Collect "java.lang:type=Memory"
+ * </Connection>
+ *
+ */
+ public GenericJMXConfConnection (OConfigItem ci) /* {{{ */
+ throws IllegalArgumentException
+ {
+ List<OConfigItem> children;
+ Iterator<OConfigItem> iter;
+
+ this._mbeans = new ArrayList<GenericJMXConfMBean> ();
+
+ children = ci.getChildren ();
+ iter = children.iterator ();
+ while (iter.hasNext ())
+ {
+ OConfigItem child = iter.next ();
+
+ if (child.getKey ().equalsIgnoreCase ("Host"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._host = tmp;
+ }
+ else if (child.getKey ().equalsIgnoreCase ("User"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._username = tmp;
+ }
+ else if (child.getKey ().equalsIgnoreCase ("Password"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._password = tmp;
+ }
+ else if (child.getKey ().equalsIgnoreCase ("ServiceURL"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._service_url = tmp;
+ }
+ else if (child.getKey ().equalsIgnoreCase ("Collect"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ {
+ GenericJMXConfMBean mbean;
+
+ mbean = GenericJMX.getMBean (tmp);
+ if (mbean == null)
+ throw (new IllegalArgumentException ("No such MBean defined: "
+ + tmp + ". Please make sure all `MBean' blocks appear "
+ + "before (above) all `Connection' blocks."));
+ Collectd.logDebug ("GenericJMXConfConnection: " + this._host + ": Add " + tmp);
+ this._mbeans.add (mbean);
+ }
+ }
+ else
+ throw (new IllegalArgumentException ("Unknown option: "
+ + child.getKey ()));
+ }
+
+ if (this._service_url == null)
+ throw (new IllegalArgumentException ("No service URL was defined."));
+ if (this._mbeans.size () == 0)
+ throw (new IllegalArgumentException ("No valid collect statement "
+ + "present."));
+ } /* }}} GenericJMXConfConnection (OConfigItem ci) */
+
+ public void query () /* {{{ */
+ {
+ PluginData pd;
+
+ connect ();
+
+ if (this._jmx_connection == null)
+ return;
+
+ Collectd.logDebug ("GenericJMXConfConnection.query: "
+ + "Reading " + this._mbeans.size () + " mbeans from "
+ + ((this._host != null) ? this._host : "(null)"));
+
+ pd = new PluginData ();
+ pd.setHost ((this._host != null) ? this._host : "localhost");
+ pd.setPlugin ("GenericJMX");
+
+ for (int i = 0; i < this._mbeans.size (); i++)
+ this._mbeans.get (i).query (this._jmx_connection, pd);
+ } /* }}} void query */
+
+ public String toString ()
+ {
+ return (new String ("host = " + this._host + "; "
+ + "url = " + this._service_url));
+ }
+}
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
--- /dev/null
+/*
+ * collectd/java - org/collectd/java/GenericJMXConfMBean.java
+ * Copyright (C) 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
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ */
+
+package org.collectd.java;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+
+import org.collectd.api.Collectd;
+import org.collectd.api.PluginData;
+import org.collectd.api.OConfigValue;
+import org.collectd.api.OConfigItem;
+
+class GenericJMXConfMBean
+{
+ private String _name; /* name by which this mapping is referenced */
+ private ObjectName _obj_name;
+ private String _instance_prefix;
+ private List<String> _instance_from;
+ private List<GenericJMXConfValue> _values;
+
+ private String getConfigString (OConfigItem ci) /* {{{ */
+ {
+ List<OConfigValue> values;
+ OConfigValue v;
+
+ values = ci.getValues ();
+ if (values.size () != 1)
+ {
+ Collectd.logError ("GenericJMXConfMBean: The " + ci.getKey ()
+ + " configuration option needs exactly one string argument.");
+ return (null);
+ }
+
+ v = values.get (0);
+ if (v.getType () != OConfigValue.OCONFIG_TYPE_STRING)
+ {
+ Collectd.logError ("GenericJMXConfMBean: The " + ci.getKey ()
+ + " configuration option needs exactly one string argument.");
+ return (null);
+ }
+
+ 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"
+ * InstancePrefix "foobar"
+ * InstanceFrom "name"
+ * <Value />
+ * <Value />
+ * :
+ * </MBean>
+ */
+ public GenericJMXConfMBean (OConfigItem ci) /* {{{ */
+ throws IllegalArgumentException
+ {
+ List<OConfigItem> children;
+ Iterator<OConfigItem> iter;
+
+ this._name = getConfigString (ci);
+ if (this._name == null)
+ throw (new IllegalArgumentException ("No alias name was defined. "
+ + "MBean blocks need exactly one string argument."));
+
+ this._obj_name = null;
+ this._instance_prefix = null;
+ this._instance_from = new ArrayList<String> ();
+ this._values = new ArrayList<GenericJMXConfValue> ();
+
+ children = ci.getChildren ();
+ iter = children.iterator ();
+ while (iter.hasNext ())
+ {
+ OConfigItem child = iter.next ();
+
+ Collectd.logDebug ("GenericJMXConfMBean: child.getKey () = "
+ + child.getKey ());
+ if (child.getKey ().equalsIgnoreCase ("ObjectName"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp == null)
+ continue;
+
+ try
+ {
+ this._obj_name = new ObjectName (tmp);
+ }
+ catch (MalformedObjectNameException e)
+ {
+ throw (new IllegalArgumentException ("Not a valid object name: "
+ + tmp, e));
+ }
+ }
+ else if (child.getKey ().equalsIgnoreCase ("InstancePrefix"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._instance_prefix = tmp;
+ }
+ else if (child.getKey ().equalsIgnoreCase ("InstanceFrom"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._instance_from.add (tmp);
+ }
+ else if (child.getKey ().equalsIgnoreCase ("Value"))
+ {
+ GenericJMXConfValue cv;
+
+ cv = new GenericJMXConfValue (child);
+ this._values.add (cv);
+ }
+ else
+ throw (new IllegalArgumentException ("Unknown option: "
+ + child.getKey ()));
+ }
+
+ if (this._obj_name == null)
+ throw (new IllegalArgumentException ("No object name was defined."));
+
+ if (this._values.size () == 0)
+ throw (new IllegalArgumentException ("No value block was defined."));
+
+ } /* }}} GenericJMXConfMBean (OConfigItem ci) */
+
+ public String getName () /* {{{ */
+ {
+ return (this._name);
+ } /* }}} */
+
+ public void query (MBeanServerConnection conn, PluginData pd) /* {{{ */
+ {
+ Set<ObjectName> names;
+ Iterator<ObjectName> iter;
+
+ try
+ {
+ names = conn.queryNames (this._obj_name, /* query = */ null);
+ }
+ catch (Exception e)
+ {
+ Collectd.logError ("GenericJMXConfMBean: queryNames failed: " + e);
+ return;
+ }
+
+ if (names.size () == 0)
+ {
+ Collectd.logWarning ("GenericJMXConfMBean: No MBean matched "
+ + "the ObjectName " + this._obj_name);
+ }
+
+ iter = names.iterator ();
+ while (iter.hasNext ())
+ {
+ ObjectName objName;
+ PluginData pd_tmp;
+ List<String> instanceList;
+ String instance;
+
+ objName = iter.next ();
+ pd_tmp = new PluginData (pd);
+ instanceList = new ArrayList<String> ();
+
+ Collectd.logDebug ("GenericJMXConfMBean: objName = "
+ + objName.toString ());
+
+ for (int i = 0; i < this._instance_from.size (); i++)
+ {
+ String propertyName;
+ String propertyValue;
+
+ propertyName = this._instance_from.get (i);
+ propertyValue = objName.getKeyProperty (propertyName);
+ if (propertyValue == null)
+ {
+ Collectd.logError ("GenericJMXConfMBean: "
+ + "No such property in object name: " + propertyName);
+ }
+ else
+ {
+ instanceList.add (propertyValue);
+ }
+ }
+
+ if (this._instance_prefix != null)
+ instance = new String (this._instance_prefix
+ + join ("-", instanceList));
+ else
+ instance = join ("-", instanceList);
+ pd_tmp.setPluginInstance (instance);
+
+ Collectd.logDebug ("GenericJMXConfMBean: instance = " + instance);
+
+ for (int i = 0; i < this._values.size (); i++)
+ this._values.get (i).query (conn, objName, pd_tmp);
+ }
+ } /* }}} void query */
+}
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
--- /dev/null
+/*
+ * collectd/java - org/collectd/java/GenericJMXConfValue.java
+ * Copyright (C) 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
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ */
+
+package org.collectd.java;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.InvalidKeyException;
+
+import org.collectd.api.Collectd;
+import org.collectd.api.DataSet;
+import org.collectd.api.DataSource;
+import org.collectd.api.ValueList;
+import org.collectd.api.PluginData;
+import org.collectd.api.OConfigValue;
+import org.collectd.api.OConfigItem;
+
+/**
+ * Representation of a <value /> block and query functionality.
+ *
+ * This class represents a <value /> block in the configuration. As
+ * such, the constructor takes an {@link org.collectd.api.OConfigValue} to
+ * construct an object of this class.
+ *
+ * The object can then be asked to query data from JMX and dispatch it to
+ * collectd.
+ *
+ * @see GenericJMXConfMBean
+ */
+class GenericJMXConfValue
+{
+ private String _ds_name;
+ private DataSet _ds;
+ private List<String> _attributes;
+ private String _instance_prefix;
+ private List<String> _instance_from;
+ private boolean _is_table;
+
+ /**
+ * Converts a generic (OpenType) object to a number.
+ *
+ * Returns null if a conversion is not possible or not implemented.
+ */
+ private Number genericObjectToNumber (Object obj, int ds_type) /* {{{ */
+ {
+ if (obj instanceof String)
+ {
+ String str = (String) obj;
+
+ try
+ {
+ if (ds_type == DataSource.TYPE_GAUGE)
+ return (new Double (str));
+ else
+ return (new Long (str));
+ }
+ catch (NumberFormatException e)
+ {
+ return (null);
+ }
+ }
+ else if (obj instanceof Byte)
+ {
+ return (new Byte ((Byte) obj));
+ }
+ else if (obj instanceof Short)
+ {
+ return (new Short ((Short) obj));
+ }
+ else if (obj instanceof Integer)
+ {
+ return (new Integer ((Integer) obj));
+ }
+ else if (obj instanceof Long)
+ {
+ return (new Long ((Long) obj));
+ }
+ else if (obj instanceof Float)
+ {
+ return (new Float ((Float) obj));
+ }
+ else if (obj instanceof Double)
+ {
+ return (new Double ((Double) obj));
+ }
+ else if (obj instanceof BigDecimal)
+ {
+ return (BigDecimal.ZERO.add ((BigDecimal) obj));
+ }
+ else if (obj instanceof BigInteger)
+ {
+ return (BigInteger.ZERO.add ((BigInteger) obj));
+ }
+
+ return (null);
+ } /* }}} Number genericObjectToNumber */
+
+ /**
+ * Converts a generic list to a list of numbers.
+ *
+ * Returns null if one or more objects could not be converted.
+ */
+ private List<Number> genericListToNumber (List<Object> objects) /* {{{ */
+ {
+ List<Number> ret = new ArrayList<Number> ();
+ List<DataSource> dsrc = this._ds.getDataSources ();
+
+ assert (objects.size () == dsrc.size ());
+
+ for (int i = 0; i < objects.size (); i++)
+ {
+ Number n;
+
+ n = genericObjectToNumber (objects.get (i), dsrc.get (i).getType ());
+ if (n == null)
+ return (null);
+ ret.add (n);
+ }
+
+ return (ret);
+ } /* }}} List<Number> genericListToNumber */
+
+ /**
+ * Converts a list of CompositeData to a list of numbers.
+ *
+ * From each <em>CompositeData </em> the key <em>key</em> is received and all
+ * those values are converted to a number. If one of the
+ * <em>CompositeData</em> doesn't have the specified key or one returned
+ * object cannot converted to a number then the function will return null.
+ */
+ private List<Number> genericCompositeToNumber (List<CompositeData> cdlist, /* {{{ */
+ String key)
+ {
+ List<Object> objects = new ArrayList<Object> ();
+
+ for (int i = 0; i < cdlist.size (); i++)
+ {
+ CompositeData cd;
+ Object value;
+
+ cd = cdlist.get (i);
+ try
+ {
+ value = cd.get (key);
+ }
+ catch (InvalidKeyException e)
+ {
+ return (null);
+ }
+ objects.add (value);
+ }
+
+ return (genericListToNumber (objects));
+ } /* }}} List<Number> genericCompositeToNumber */
+
+ private void submitTable (List<Object> objects, ValueList vl, /* {{{ */
+ String instancePrefix)
+ {
+ List<CompositeData> cdlist;
+ Set<String> keySet = null;
+ Iterator<String> keyIter;
+
+ cdlist = new ArrayList<CompositeData> ();
+ for (int i = 0; i < objects.size (); i++)
+ {
+ Object obj;
+
+ obj = objects.get (i);
+ if (obj instanceof CompositeData)
+ {
+ CompositeData cd;
+
+ cd = (CompositeData) obj;
+
+ if (i == 0)
+ keySet = cd.getCompositeType ().keySet ();
+
+ cdlist.add (cd);
+ }
+ else
+ {
+ Collectd.logError ("GenericJMXConfValue: At least one of the "
+ + "attributes was not of type `CompositeData', as required "
+ + "when table is set to `true'.");
+ return;
+ }
+ }
+
+ assert (keySet != null);
+
+ keyIter = keySet.iterator ();
+ while (keyIter.hasNext ())
+ {
+ String key;
+ List<Number> values;
+
+ key = keyIter.next ();
+ values = genericCompositeToNumber (cdlist, key);
+ if (values == null)
+ {
+ Collectd.logError ("GenericJMXConfValue: Cannot build a list of "
+ + "numbers for key " + key + ". Most likely not all attributes "
+ + "have this key.");
+ continue;
+ }
+
+ if (instancePrefix == null)
+ vl.setTypeInstance (key);
+ else
+ vl.setTypeInstance (instancePrefix + key);
+ vl.setValues (values);
+
+ Collectd.dispatchValues (vl);
+ }
+ } /* }}} void submitTable */
+
+ private void submitScalar (List<Object> objects, ValueList vl, /* {{{ */
+ String instancePrefix)
+ {
+ List<Number> values;
+
+ values = genericListToNumber (objects);
+ if (values == null)
+ {
+ Collectd.logError ("GenericJMXConfValue: Cannot convert list of "
+ + "objects to numbers.");
+ return;
+ }
+
+ if (instancePrefix == null)
+ vl.setTypeInstance ("");
+ else
+ vl.setTypeInstance (instancePrefix);
+ vl.setValues (values);
+
+ Collectd.dispatchValues (vl);
+ } /* }}} void submitScalar */
+
+ private Object queryAttributeRecursive (CompositeData parent, /* {{{ */
+ List<String> attrName)
+ {
+ String key;
+ Object value;
+
+ key = attrName.remove (0);
+
+ try
+ {
+ value = parent.get (key);
+ }
+ catch (InvalidKeyException e)
+ {
+ return (null);
+ }
+
+ if (attrName.size () == 0)
+ {
+ return (value);
+ }
+ else
+ {
+ if (value instanceof CompositeData)
+ return (queryAttributeRecursive ((CompositeData) value, attrName));
+ else
+ return (null);
+ }
+ } /* }}} queryAttributeRecursive */
+
+ private Object queryAttribute (MBeanServerConnection conn, /* {{{ */
+ ObjectName objName, String attrName)
+ {
+ List<String> attrNameList;
+ String key;
+ Object value;
+ String[] attrNameArray;
+
+ attrNameList = new ArrayList<String> ();
+
+ attrNameArray = attrName.split ("\\.");
+ key = attrNameArray[0];
+ for (int i = 1; i < attrNameArray.length; i++)
+ attrNameList.add (attrNameArray[i]);
+
+ try
+ {
+ value = conn.getAttribute (objName, key);
+ }
+ catch (Exception e)
+ {
+ Collectd.logError ("GenericJMXConfValue.query: getAttribute failed: "
+ + e);
+ return (null);
+ }
+
+ if (attrNameList.size () == 0)
+ {
+ return (value);
+ }
+ else
+ {
+ if (value instanceof CompositeData)
+ return (queryAttributeRecursive((CompositeData) value, attrNameList));
+ else if (value instanceof OpenType)
+ {
+ OpenType ot = (OpenType) value;
+ Collectd.logNotice ("GenericJMXConfValue: Handling of OpenType \""
+ + ot.getTypeName () + "\" is not yet implemented.");
+ return (null);
+ }
+ else
+ {
+ Collectd.logError ("GenericJMXConfValue: Received object of "
+ + "unknown class.");
+ return (null);
+ }
+ }
+ } /* }}} Object queryAttribute */
+
+ 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 */
+
+ private String getConfigString (OConfigItem ci) /* {{{ */
+ {
+ List<OConfigValue> values;
+ OConfigValue v;
+
+ values = ci.getValues ();
+ if (values.size () != 1)
+ {
+ Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
+ + " configuration option needs exactly one string argument.");
+ return (null);
+ }
+
+ v = values.get (0);
+ if (v.getType () != OConfigValue.OCONFIG_TYPE_STRING)
+ {
+ Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
+ + " configuration option needs exactly one string argument.");
+ return (null);
+ }
+
+ return (v.getString ());
+ } /* }}} String getConfigString */
+
+ private Boolean getConfigBoolean (OConfigItem ci) /* {{{ */
+ {
+ List<OConfigValue> values;
+ OConfigValue v;
+ Boolean b;
+
+ values = ci.getValues ();
+ if (values.size () != 1)
+ {
+ Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
+ + " configuration option needs exactly one boolean argument.");
+ return (null);
+ }
+
+ v = values.get (0);
+ if (v.getType () != OConfigValue.OCONFIG_TYPE_BOOLEAN)
+ {
+ Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
+ + " configuration option needs exactly one boolean argument.");
+ return (null);
+ }
+
+ return (new Boolean (v.getBoolean ()));
+ } /* }}} String getConfigBoolean */
+
+ /**
+ * Constructs a new value with the configured properties.
+ */
+ public GenericJMXConfValue (OConfigItem ci) /* {{{ */
+ throws IllegalArgumentException
+ {
+ List<OConfigItem> children;
+ Iterator<OConfigItem> iter;
+
+ this._ds_name = null;
+ this._ds = null;
+ this._attributes = new ArrayList<String> ();
+ this._instance_prefix = null;
+ this._instance_from = new ArrayList<String> ();
+ this._is_table = false;
+
+ /*
+ * <Value>
+ * Type "memory"
+ * Table true|false
+ * Attribute "HeapMemoryUsage"
+ * Attribute "..."
+ * :
+ * # Type instance:
+ * InstancePrefix "heap-"
+ * </Value>
+ */
+ children = ci.getChildren ();
+ iter = children.iterator ();
+ while (iter.hasNext ())
+ {
+ OConfigItem child = iter.next ();
+
+ if (child.getKey ().equalsIgnoreCase ("Type"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._ds_name = tmp;
+ }
+ else if (child.getKey ().equalsIgnoreCase ("Table"))
+ {
+ Boolean tmp = getConfigBoolean (child);
+ if (tmp != null)
+ this._is_table = tmp.booleanValue ();
+ }
+ else if (child.getKey ().equalsIgnoreCase ("Attribute"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._attributes.add (tmp);
+ }
+ else if (child.getKey ().equalsIgnoreCase ("InstancePrefix"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._instance_prefix = tmp;
+ }
+ else if (child.getKey ().equalsIgnoreCase ("InstanceFrom"))
+ {
+ String tmp = getConfigString (child);
+ if (tmp != null)
+ this._instance_from.add (tmp);
+ }
+ else
+ throw (new IllegalArgumentException ("Unknown option: "
+ + child.getKey ()));
+ }
+
+ if (this._ds_name == null)
+ throw (new IllegalArgumentException ("No data set was defined."));
+ else if (this._attributes.size () == 0)
+ throw (new IllegalArgumentException ("No attribute was defined."));
+ } /* }}} GenericJMXConfValue (OConfigItem ci) */
+
+ /**
+ * Query values via JMX according to the object's configuration and dispatch
+ * them to collectd.
+ *
+ * @param conn Connection to the MBeanServer.
+ * @param objName Object name of the MBean to query.
+ * @param pd Preset naming components. The members host, plugin and
+ * plugin instance will be used.
+ */
+ public void query (MBeanServerConnection conn, ObjectName objName, /* {{{ */
+ PluginData pd)
+ {
+ ValueList vl;
+ List<DataSource> dsrc;
+ List<Object> values;
+ List<String> instanceList;
+ String instancePrefix;
+
+ if (this._ds == null)
+ {
+ this._ds = Collectd.getDS (this._ds_name);
+ if (this._ds == null)
+ {
+ Collectd.logError ("GenericJMXConfValue: Unknown type: "
+ + this._ds_name);
+ return;
+ }
+ }
+
+ dsrc = this._ds.getDataSources ();
+ if (dsrc.size () != this._attributes.size ())
+ {
+ Collectd.logError ("GenericJMXConfValue.query: The data set "
+ + this._ds_name + " has " + this._ds.getDataSources ().size ()
+ + " data sources, but there were " + this._attributes.size ()
+ + " attributes configured. This doesn't match!");
+ this._ds = null;
+ return;
+ }
+
+ vl = new ValueList (pd);
+ vl.setType (this._ds_name);
+
+ /*
+ * Build the instnace prefix from the fixed string prefix and the
+ * properties of the objName.
+ */
+ instanceList = new ArrayList<String> ();
+ for (int i = 0; i < this._instance_from.size (); i++)
+ {
+ String propertyName;
+ String propertyValue;
+
+ propertyName = this._instance_from.get (i);
+ propertyValue = objName.getKeyProperty (propertyName);
+ if (propertyValue == null)
+ {
+ Collectd.logError ("GenericJMXConfMBean: "
+ + "No such property in object name: " + propertyName);
+ }
+ else
+ {
+ instanceList.add (propertyValue);
+ }
+ }
+
+ if (this._instance_prefix != null)
+ instancePrefix = new String (this._instance_prefix
+ + join ("-", instanceList));
+ else
+ instancePrefix = join ("-", instanceList);
+
+ /*
+ * Build a list of `Object's which is then passed to `submitTable' and
+ * `submitScalar'.
+ */
+ values = new ArrayList<Object> ();
+ assert (dsrc.size () == this._attributes.size ());
+ for (int i = 0; i < this._attributes.size (); i++)
+ {
+ Object v;
+
+ v = queryAttribute (conn, objName, this._attributes.get (i));
+ if (v == null)
+ {
+ Collectd.logError ("GenericJMXConfValue.query: "
+ + "Querying attribute " + this._attributes.get (i) + " failed.");
+ return;
+ }
+
+ values.add (v);
+ }
+
+ if (this._is_table)
+ submitTable (values, vl, instancePrefix);
+ else
+ submitScalar (values, vl, instancePrefix);
+ } /* }}} void query */
+} /* class GenericJMXConfValue */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
import org.collectd.api.OConfigValue;
import org.collectd.api.OConfigItem;
-public class JMXMemory implements CollectdConfigInterface, /* {{{ */
+public class JMXMemory implements CollectdConfigInterface,
CollectdInitInterface,
CollectdReadInterface,
CollectdShutdownInterface
_mbean = null;
return (0);
} /* }}} int shutdown */
-} /* }}} class JMXMemory */
+} /* class JMXMemory */
/* vim: set sw=2 sts=2 et fdm=marker : */
=cut
-sub getval
+sub getval # {{{
{
my $obj = shift;
my %args = @_;
}
return ($ret);
-} # getval
+} # }}} sub getval
+
+=item I<$res> = I<$obj>-E<gt>B<getthreshold> (I<%identifier>);
+
+Requests a threshold from the daemon. On success a hash-ref is returned with
+the threshold data. On error false is returned.
+
+=cut
+
+sub getthreshold # {{{
+{
+ my $obj = shift;
+ my %args = @_;
+
+ my $status;
+ my $fh = $obj->{'sock'} or confess ('object has no filehandle');
+ my $msg;
+ my $identifier;
+
+ my $ret = {};
+
+ $identifier = _create_identifier (\%args) or return;
+
+ $msg = 'GETTHRESHOLD ' . _escape_argument ($identifier) . "\n";
+ _debug "-> $msg";
+ print $fh $msg;
+
+ $msg = <$fh>;
+ chomp ($msg);
+ _debug "<- $msg\n";
+
+ ($status, $msg) = split (' ', $msg, 2);
+ if ($status <= 0)
+ {
+ $obj->{'error'} = $msg;
+ return;
+ }
+
+ for (my $i = 0; $i < $status; $i++)
+ {
+ my $entry = <$fh>;
+ chomp ($entry);
+ _debug "<- $entry\n";
+
+ if ($entry =~ m/^([^:]+):\s*(\S.*)$/)
+ {
+ my $key = $1;
+ my $value = $2;
+
+ $key =~ s/^\s+//;
+ $key =~ s/\s+$//;
+
+ $ret->{$key} = $value;
+ }
+ }
+
+ return ($ret);
+} # }}} sub getthreshold
=item I<$obj>-E<gt>B<putval> (I<%identifier>, B<time> =E<gt> I<$time>, B<values> =E<gt> [...]);
Florian octo Forster E<lt>octo@verplant.orgE<gt>
=cut
+
+# vim: set fdm=marker :
])
# For the swap module
+have_linux_wireless_h="no"
+if test "x$ac_system" = "xLinux"
+then
+ AC_CHECK_HEADERS(linux/wireless.h,
+ [have_linux_wireless_h="yes"],
+ [have_linux_wireless_h="no"],
+[
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+])
+fi
+
+# For the swap module
have_sys_swap_h="yes"
AC_CHECK_HEADERS(sys/swap.h vm/anon.h, [], [have_sys_swap_h="no"],
[
fi
# }}}
+# --with-libtokyotyrant {{{
+with_libtokyotyrant_cppflags=""
+with_libtokyotyrant_ldflags=""
+with_libtokyotyrant_libs=""
+AC_ARG_WITH(libtokyotyrant, [AS_HELP_STRING([--with-libtokyotyrant@<:@=PREFIX@:>@], [Path to libtokyotyrant.])],
+[
+ if test "x$withval" = "xno"
+ then
+ with_libtokyotyrant="no"
+ else if test "x$withval" = "xyes"
+ then
+ with_libtokyotyrant="yes"
+ else
+ with_libtokyotyrant_cppflags="-I$withval/include"
+ with_libtokyotyrant_ldflags="-L$withval/include"
+ with_libtokyotyrant_libs="-ltokyotyrant"
+ with_libtokyotyrant="yes"
+ fi; fi
+],
+[
+ with_libtokyotyrant="yes"
+])
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+ if $PKG_CONFIG --exists tokyotyrant
+ then
+ with_libtokyotyrant_cppflags="$with_libtokyotyrant_cppflags `$PKG_CONFIG --cflags tokyotyrant`"
+ with_libtokyotyrant_ldflags="$with_libtokyotyrant_ldflags `pkg-config --libs-only-L tokyotyrant`"
+ with_libtokyotyrant_libs="$with_libtokyotyrant_libs `pkg-config --libs-only-l tokyotyrant`"
+ fi
+fi
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+CPPFLAGS="$CPPFLAGS $with_libtokyotyrant_cppflags"
+LDFLAGS="$LDFLAGS $with_libtokyotyrant_ldflags"
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+ AC_CHECK_HEADERS(tcrdb.h,
+ [
+ AC_DEFINE(HAVE_TCRDB_H, 1,
+ [Define to 1 if you have the <tcrdb.h> header file.])
+ ], [with_libtokyotyrant="no (tcrdb.h not found)"])
+fi
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+ AC_CHECK_LIB(tokyotyrant, tcrdbrnum,
+ [
+ AC_DEFINE(HAVE_LIBTOKYOTYRANT, 1,
+ [Define to 1 if you have the tokyotyrant library (-ltokyotyrant).])
+ ],
+ [with_libtokyotyrant="no (symbol tcrdbrnum not found)"],
+ [$with_libtokyotyrant_libs])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_libtokyotyrant" = "xyes"
+then
+ BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS="$with_libtokyotyrant_cppflags"
+ BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS="$with_libtokyotyrant_ldflags"
+ BUILD_WITH_LIBTOKYOTYRANT_LIBS="$with_libtokyotyrant_libs"
+ AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS)
+ AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS)
+ AC_SUBST(BUILD_WITH_LIBTOKYOTYRANT_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBTOKYOTYRANT, test "x$with_libtokyotyrant" = "xyes")
+# }}}
+
# --with-libupsclient {{{
with_libupsclient_config=""
with_libupsclient_cflags=""
AM_CONDITIONAL(BUILD_WITH_LIBXMMS, test "x$with_libxmms" = "xyes")
# }}}
+# --with-libyajl {{{
+with_libyajl_cppflags=""
+with_libyajl_ldflags=""
+AC_ARG_WITH(libyajl, [AS_HELP_STRING([--with-libyajl@<:@=PREFIX@:>@], [Path to libyajl.])],
+[
+ if test "x$withval" != "xno" && test "x$withval" != "xyes"
+ then
+ with_libyajl_cppflags="-I$withval/include"
+ with_libyajl_ldflags="-L$withval/lib"
+ with_libyajl="yes"
+ else
+ with_libyajl="$withval"
+ fi
+],
+[
+ with_libyajl="yes"
+])
+if test "x$with_libyajl" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libyajl_cppflags"
+
+ AC_CHECK_HEADERS(yajl/yajl_parse.h, [with_libyajl="yes"], [with_libyajl="no (yajl/yajl_parse.h not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libyajl" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ SAVE_LDFLAGS="$LDFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libyajl_cppflags"
+ LDFLAGS="$LDFLAGS $with_libyajl_ldflags"
+
+ AC_CHECK_LIB(yajl, yajl_alloc, [with_libyajl="yes"], [with_libyajl="no (Symbol 'yajl_alloc' not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libyajl" = "xyes"
+then
+ BUILD_WITH_LIBYAJL_CPPFLAGS="$with_libyajl_cppflags"
+ BUILD_WITH_LIBYAJL_LDFLAGS="$with_libyajl_ldflags"
+ BUILD_WITH_LIBYAJL_LIBS="-lyajl"
+ AC_SUBST(BUILD_WITH_LIBYAJL_CPPFLAGS)
+ AC_SUBST(BUILD_WITH_LIBYAJL_LDFLAGS)
+ AC_SUBST(BUILD_WITH_LIBYAJL_LIBS)
+ AC_DEFINE(HAVE_LIBYAJL, 1, [Define if libyajl is present and usable.])
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBYAJL, test "x$with_libyajl" = "xyes")
+# }}}
+
# pkg-config --exists 'libxml-2.0'; pkg-config --exists libvirt {{{
with_libxml2="no (pkg-config isn't available)"
with_libxml2_cflags=""
plugin_conntrack="no"
plugin_cpu="no"
plugin_cpufreq="no"
+plugin_curl_json="no"
plugin_df="no"
plugin_disk="no"
plugin_entropy="no"
plugin_vmem="no"
plugin_vserver="no"
plugin_wireless="no"
+plugin_zfs_arc="no"
# Linux
if test "x$ac_system" = "xLinux"
if test "x$with_kstat" = "xyes"
then
plugin_uptime="yes"
+ plugin_zfs_arc="yes"
fi
if test "x$with_devinfo$with_kstat" = "xyesyes"
plugin_ipmi="yes"
fi
+if test "x$with_libcurl" = "xyes" && test "x$with_libyajl" = "xyes"
+then
+ plugin_curl_json="yes"
+fi
+
if test "x$have_processor_info" = "xyes"
then
plugin_cpu="yes"
AC_PLUGIN([cpu], [$plugin_cpu], [CPU usage statistics])
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([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([madwifi], [$have_linux_wireless_h], [Madwifi wireless statistics])
+AC_PLUGIN([match_empty_counter], [yes], [The empty counter match])
AC_PLUGIN([match_regex], [yes], [The regex match])
AC_PLUGIN([match_timediff], [yes], [The timediff match])
AC_PLUGIN([match_value], [yes], [The value match])
AC_PLUGIN([notify_email], [$with_libesmtp], [Email notifier])
AC_PLUGIN([ntpd], [yes], [NTPd statistics])
AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics])
+AC_PLUGIN([olsrd], [yes], [olsrd statistics])
AC_PLUGIN([onewire], [$with_libowcapi], [OneWire sensor statistics])
AC_PLUGIN([openvpn], [yes], [OpenVPN client statistics])
AC_PLUGIN([oracle], [$with_oracle], [Oracle plugin])
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([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([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([xmms], [$with_libxmms], [XMMS statistics])
+AC_PLUGIN([zfs_arc], [$plugin_zfs_arc], [ZFS ARC statistics])
dnl Default configuration file
# Load either syslog or logfile
librrd . . . . . . . $with_librrd
libsensors . . . . . $with_libsensors
libstatgrab . . . . . $with_libstatgrab
+ libtokyotyrant . . . $with_libtokyotyrant
libupsclient . . . . $with_libupsclient
libvirt . . . . . . . $with_libvirt
libxml2 . . . . . . . $with_libxml2
libxmms . . . . . . . $with_libxmms
+ libyajl . . . . . . . $with_libyajl
oracle . . . . . . . $with_oracle
Features:
cpufreq . . . . . . . $enable_cpufreq
csv . . . . . . . . . $enable_csv
curl . . . . . . . . $enable_curl
+ curl_json . . . . . . $enable_curl_json
dbi . . . . . . . . . $enable_dbi
df . . . . . . . . . $enable_df
disk . . . . . . . . $enable_disk
libvirt . . . . . . . $enable_libvirt
load . . . . . . . . $enable_load
logfile . . . . . . . $enable_logfile
+ madwifi . . . . . . . $enable_madwifi
+ match_empty_counter . $enable_match_empty_counter
match_regex . . . . . $enable_match_regex
match_timediff . . . $enable_match_timediff
match_value . . . . . $enable_match_value
notify_email . . . . $enable_notify_email
ntpd . . . . . . . . $enable_ntpd
nut . . . . . . . . . $enable_nut
+ olsrd . . . . . . . . $enable_olsrd
onewire . . . . . . . $enable_onewire
openvpn . . . . . . . $enable_openvpn
oracle . . . . . . . $enable_oracle
teamspeak2 . . . . . $enable_teamspeak2
ted . . . . . . . . . $enable_ted
thermal . . . . . . . $enable_thermal
+ tokyotyrant . . . . . $enable_tokyotyrant
unixsock . . . . . . $enable_unixsock
uptime . . . . . . . $enable_uptime
users . . . . . . . . $enable_users
vmem . . . . . . . . $enable_vmem
vserver . . . . . . . $enable_vserver
wireless . . . . . . $enable_wireless
+ write_http . . . . . $enable_write_http
xmms . . . . . . . . $enable_xmms
+ zfs_arc . . . . . . . $enable_zfs_arc
EOF
--- /dev/null
+# contrib/GenericJMX.conf
+# -----------------------
+#
+# This is an example config file for the ‘GenericJMX’ plugin, a plugin written
+# in Java to receive values via the “Java Management Extensions” (JMX). The
+# plugin can be found in the
+# bindings/java/org/collectd/java/
+# directory of the source distribution.
+#
+# This sample config defines a couple of <MBean /> blocks which query MBeans
+# provided by the JVM itself, i. e. which should be available for all Java
+# processes. The following MBean blocks are defined:
+#
+# +-------------------+------------------------------------------------+
+# ! Name ! Description !
+# +-------------------+------------------------------------------------+
+# ! classes ! Number of classes being loaded. !
+# ! compilation ! Time spent by the JVM compiling or optimizing. !
+# ! garbage_collector ! Number of garbage collections and time spent. !
+# ! memory ! Generic heap/nonheap memory usage. !
+# ! memory_pool ! Memory usage by memory pool. !
+# +-------------------+------------------------------------------------+
+#
+<Plugin "java">
+ LoadPlugin "org.collectd.java.GenericJMX"
+
+ <Plugin "GenericJMX">
+ ################
+ # MBean blocks #
+ ################
+ # Number of classes being loaded.
+ <MBean "classes">
+ ObjectName "java.lang:type=ClassLoading"
+ #InstancePrefix ""
+ #InstanceFrom ""
+
+ <Value>
+ Type "gauge"
+ InstancePrefix "loaded_classes"
+ #InstanceFrom ""
+ Table false
+ Attribute "LoadedClassCount"
+ </Value>
+ </MBean>
+
+ # Time spent by the JVM compiling or optimizing.
+ <MBean "compilation">
+ ObjectName "java.lang:type=Compilation"
+ #InstancePrefix ""
+ #InstanceFrom ""
+
+ <Value>
+ Type "total_time_in_ms"
+ InstancePrefix "compilation_time"
+ #InstanceFrom ""
+ Table false
+ Attribute "TotalCompilationTime"
+ </Value>
+ </MBean>
+
+ # Garbage collector information
+ <MBean "garbage_collector">
+ ObjectName "java.lang:type=GarbageCollector,*"
+ InstancePrefix "gc-"
+ InstanceFrom "name"
+
+ <Value>
+ Type "invocations"
+ #InstancePrefix ""
+ #InstanceFrom ""
+ Table false
+ Attribute "CollectionCount"
+ </Value>
+
+ <Value>
+ Type "total_time_in_ms"
+ InstancePrefix "collection_time"
+ #InstanceFrom ""
+ Table false
+ Attribute "CollectionTime"
+ </Value>
+
+# # Not that useful, therefore commented out.
+# <Value>
+# Type "threads"
+# #InstancePrefix ""
+# #InstanceFrom ""
+# Table false
+# # Demonstration how to access composite types
+# Attribute "LastGcInfo.GcThreadCount"
+# </Value>
+ </MBean>
+
+ # Generic heap/nonheap memory usage.
+ <MBean "memory">
+ ObjectName "java.lang:type=Memory"
+ #InstanceFrom ""
+ InstancePrefix "memory"
+
+ # Creates four values: committed, init, max, used
+ <Value>
+ Type "memory"
+ #InstancePrefix ""
+ #InstanceFrom ""
+ Table true
+ Attribute "HeapMemoryUsage"
+ InstancePrefix "heap-"
+ </Value>
+
+ # Creates four values: committed, init, max, used
+ <Value>
+ Type "memory"
+ #InstancePrefix ""
+ #InstanceFrom ""
+ Table true
+ Attribute "NonHeapMemoryUsage"
+ </Value>
+ </MBean>
+
+ # Memory usage by memory pool.
+ <MBean "memory_pool">
+ ObjectName "java.lang:type=MemoryPool,*"
+ InstancePrefix "memory_pool-"
+ InstanceFrom "name"
+
+ <Value>
+ Type "memory"
+ #InstancePrefix ""
+ #InstanceFrom ""
+ Table true
+ Attribute "Usage"
+ </Value>
+ </MBean>
+
+ ### MBeans by Catalina / Tomcat ###
+ # The global request processor (summary for each request processor)
+ <MBean "catalina/global_request_processor">
+ ObjectName "Catalina:type=GlobalRequestProcessor,*"
+ InstancePrefix "request_processor-"
+ InstanceFrom "name"
+
+ <Value>
+ Type "io_octets"
+ InstancePrefix "global"
+ #InstanceFrom ""
+ Table false
+ Attribute "bytesReceived"
+ Attribute "bytesSent"
+ </Value>
+
+ <Value>
+ Type "total_requests"
+ InstancePrefix "global"
+ #InstanceFrom ""
+ Table false
+ Attribute "requestCount"
+ </Value>
+
+ <Value>
+ Type "total_time_in_ms"
+ InstancePrefix "global-processing"
+ #InstanceFrom ""
+ Table false
+ Attribute "processingTime"
+ </Value>
+ </MBean>
+
+ # Details for each request processor
+ <MBean "catalina/detailed_request_processor">
+ ObjectName "Catalina:type=RequestProcessor,*"
+ InstancePrefix "request_processor-"
+ InstanceFrom "worker"
+
+ <Value>
+ Type "io_octets"
+ #InstancePrefix ""
+ InstanceFrom "name"
+ Table false
+ Attribute "bytesReceived"
+ Attribute "bytesSent"
+ </Value>
+
+ <Value>
+ Type "total_requests"
+ #InstancePrefix ""
+ InstanceFrom "name"
+ Table false
+ Attribute "requestCount"
+ </Value>
+
+ <Value>
+ Type "total_time_in_ms"
+ InstancePrefix "processing-"
+ InstanceFrom "name"
+ Table false
+ Attribute "processingTime"
+ </Value>
+ </MBean>
+
+ # Thread pool
+ <MBean "catalina/thread_pool">
+ ObjectName "Catalina:type=ThreadPool,*"
+ InstancePrefix "request_processor-"
+ InstanceFrom "name"
+
+ <Value>
+ Type "threads"
+ InstancePrefix "total"
+ #InstanceFrom ""
+ Table false
+ Attribute "currentThreadCount"
+ </Value>
+
+ <Value>
+ Type "threads"
+ InstancePrefix "running"
+ #InstanceFrom ""
+ Table false
+ Attribute "currentThreadsBusy"
+ </Value>
+ </MBean>
+
+ #####################
+ # Connection blocks #
+ #####################
+ <Connection>
+ ServiceURL "service:jmx:rmi:///jndi/rmi://localhost:17264/jmxrmi"
+ User "monitorRole"
+ Password "queeZie1"
+ Host "localhost"
+ Collect "classes"
+ Collect "compilation"
+ Collect "garbage_collector"
+ Collect "memory"
+ Collect "memory_pool"
+ </Connection>
+ </Plugin>
+</Plugin>
collectd-network.py
-------------------
-
This Python module by Adrian Perez implements the collectd network protocol
in pure Python. It currently supports to receive data and notifications from
collectd.
collectd-unixsock.py
--------------------
-
This Python module by Clay Loveless provides an interface to collect's
unixsock plugin.
collectd2html.pl
----------------
- This script by Vincent Stehlé will search for RRD files in
+ This script by Vincent Stehlé will search for RRD files in
`/var/lib/collectd/' and generate an HTML file and a directory containing
several PNG files which are graphs of the RRD files found.
Init-script and Spec-file that can be used when creating RPM-packages for
Fedora.
+GenericJMX.conf
+---------------
+ Example configuration file for the ‘GenericJMX’ Java plugin. Please read the
+documentation at the beginning of the file for more details.
+
migrate-3-4.px
--------------
Migration-script to ease the switch from version 3 to version 4. Many
Color starting ff00ff
Color waiting ffb000
</Type>
+<Type arc_counts>
+ Module ArcCounts
+ RRDTitle "ARC {type_instance} on {hostname}"
+# RRDOptions ...
+</Type>
+<Type arc_l2_bytes>
+ Module GenericIO
+ DataSources read write
+ DSName "read Read "
+ DSName "write Written"
+ RRDTitle "L2ARC traffic"
+ RRDVerticalLabel "Bytes per second"
+# RRDOptions ...
+ RRDFormat "%5.1lf%s"
+</Type>
+<Type arc_l2_size>
+ RRDTitle "L2ARC size on {hostname}"
+ RRDVerticalLabel "Size"
+ RRDFormat "%4.0lf%s"
+ RRDOptions -b 1024
+ DSName "value Current size"
+ Color value 00e000
+</Type>
+<Type arc_size>
+ DataSources "current target minlimit maxlimit"
+ RRDTitle "ARC size on {hostname}"
+ RRDVerticalLabel "Size"
+ RRDFormat "%4.0lf%s"
+ RRDOptions -b 1024
+ DSName "current Current size"
+ DSName "target Target size "
+ DSName "maxlimit Max size "
+ DSName "minlimit Min size "
+ Color current 00e000
+ Color target 0000ff
+ Color minlimit ff0000
+ Color maxlimit ff00ff
+</Type>
+<Type arc_ratio>
+ DataSources value
+ RRDTitle "{type_instance}ARC ratio on {hostname}"
+ RRDVerticalLabel "Ratio"
+ RRDFormat "%4.1lf"
+ RRDOptions -l 0
+ DSName "value Hit ratio"
+</Type>
<Type cache_ratio>
DataSources value
DSName value Percent
--- /dev/null
+package Collectd::Graph::Type::ArcCounts;
+
+# Copyright (C) 2009 Anthony Dewhurst <dewhurst at gmail>
+#
+# This program is available software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the available 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 available Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+use strict;
+use warnings;
+use base ('Collectd::Graph::Type');
+
+use Collectd::Graph::Common (qw(ident_to_filename get_faded_color));
+
+return (1);
+
+sub getDataSources
+{
+ return ([qw(available usedbydatasetbydataset usedbysnapshots usedbyrefres usedbychildren)]);
+} # getDataSources
+
+sub new
+{
+ my $pkg = shift;
+ my $obj = Collectd::Graph::Type->new (@_);
+ $obj->{'data_sources'} = [qw(demand_data demand_metadata prefetch_data prefetch_metadata)];
+ $obj->{'rrd_opts'} = [];
+ $obj->{'rrd_title'} = 'ARC {type_instance} on {hostname}';
+
+ return (bless ($obj, $pkg));
+} # new
+
+sub getRRDArgs
+{
+ my $obj = shift;
+ my $index = shift;
+
+ my $ident = $obj->{'files'}[$index];
+ if (!$ident)
+ {
+ cluck ("Invalid index: $index");
+ return;
+ }
+ my $filename = ident_to_filename ($ident);
+ $filename =~ s#:#\\:#g;
+
+ my $legend = $ident->{'type_instance'};
+
+
+ my $faded_green = get_faded_color ('00ff00');
+ my $faded_blue = get_faded_color ('0000ff');
+ my $faded_red = get_faded_color ('ff0000');
+ my $faded_cyan = get_faded_color ('00ffff');
+
+ my @ret = @{$obj->{'rrd_opts'}};
+
+ push @ret, '-t', $obj->getTitle($ident);
+ push @ret, '-v', ucfirst($ident->{'type_instance'});
+
+ my $ds = {
+ demand_data => { legend => "Demand data ", color => "00ff00" },
+ demand_metadata => { legend => "Demand metadata", color => "0000ff" },
+ prefetch_data => { legend => "Prefetch data ", color => "ff0000" },
+ prefetch_metadata => { legend => "Prefetch meta ", color => "ff00ff" },
+ };
+
+ foreach (qw(demand_data demand_metadata prefetch_data prefetch_metadata))
+ {
+ push @ret,
+ "DEF:${_}_min=${filename}:${_}:MIN",
+ "DEF:${_}_avg=${filename}:${_}:AVERAGE",
+ "DEF:${_}_max=${filename}:${_}:MAX";
+ }
+
+ {
+ push @ret,
+ "CDEF:stack_prefetch_metadata=prefetch_metadata_avg",
+ "CDEF:stack_prefetch_data=prefetch_data_avg,stack_prefetch_metadata,+",
+ "CDEF:stack_demand_metadata=demand_metadata_avg,stack_prefetch_data,+",
+ "CDEF:stack_demand_data=demand_data_avg,stack_demand_metadata,+",
+ "AREA:stack_demand_data#${faded_green}",
+ "AREA:stack_demand_metadata#${faded_blue}",
+ "AREA:stack_prefetch_data#${faded_red}",
+ "AREA:stack_prefetch_metadata#${faded_cyan}",
+ }
+
+ foreach (qw(demand_data demand_metadata prefetch_data prefetch_metadata))
+ {
+ push @ret,
+ "LINE1:stack_${_}#" . $ds->{$_}->{color} . ":" . $ds->{$_}->{legend},
+ "GPRINT:${_}_min:MIN:%5.1lf Min,",
+ "GPRINT:${_}_avg:AVERAGE:%5.1lf Avg,",
+ "GPRINT:${_}_max:MAX:%5.1lf Max,",
+ "GPRINT:${_}_avg:LAST:%5.1lf Last\l";
+ }
+
+ return \@ret;
+
+} # getRRDArgs
+
+# vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
HELP => \&cmd_help,
PUTVAL => \&putval,
GETVAL => \&getval,
+ GETTHRESHOLD => \&getthreshold,
FLUSH => \&flush,
LISTVAL => \&listval,
PUTNOTIF => \&putnotif,
HELP
PUTVAL
GETVAL
+ GETTHRESHOLD
FLUSH
LISTVAL
PUTNOTIF
return 1;
}
+=item B<GETTHRESHOLD> I<Identifier>
+
+=cut
+
+sub getthreshold {
+ my $sock = shift || return;
+ my $line = shift || return;
+
+ my @line = tokenize($line);
+
+ my $id;
+ my $vals;
+
+ if (! @line) {
+ return;
+ }
+
+ if (scalar(@line) < 1) {
+ print STDERR "Synopsis: GETTHRESHOLD <id>" . $/;
+ return;
+ }
+
+ $id = getid($line[0]);
+
+ if (! $id) {
+ print STDERR "Invalid id \"$line[0]\"." . $/;
+ return;
+ }
+
+ $vals = $sock->getthreshold(%$id);
+
+ if (! $vals) {
+ print STDERR "socket error: " . $sock->{'error'} . $/;
+ return;
+ }
+
+ foreach my $key (keys %$vals) {
+ print "\t$key: $vals->{$key}\n";
+ }
+ return 1;
+}
+
=item B<FLUSH> [B<timeout>=I<$timeout>] [B<plugin>=I<$plugin>[ ...]]
=cut
collectd_DEPENDENCIES += curl.la
endif
+if BUILD_PLUGIN_CURL_JSON
+pkglib_LTLIBRARIES += curl_json.la
+curl_json_la_SOURCES = curl_json.c
+curl_json_la_CFLAGS = $(AM_CFLAGS)
+curl_json_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBYAJL_LDFLAGS)
+curl_json_la_CPPFLAGS = $(BUILD_WITH_LIBYAJL_CPPFLAGS)
+curl_json_la_LIBADD = $(BUILD_WITH_LIBYAJL_LIBS)
+if BUILD_WITH_LIBCURL
+curl_json_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
+curl_json_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
+endif
+collectd_LDADD += "-dlopen" curl_json.la
+collectd_DEPENDENCIES += curl_json.la
+endif
+
if BUILD_PLUGIN_DBI
pkglib_LTLIBRARIES += dbi.la
dbi_la_SOURCES = dbi.c \
collectd_DEPENDENCIES += logfile.la
endif
+if BUILD_PLUGIN_MADWIFI
+pkglib_LTLIBRARIES += madwifi.la
+madwifi_la_SOURCES = madwifi.c madwifi.h
+madwifi_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" madwifi.la
+collectd_DEPENDENCIES += madwifi.la
+endif
+
+if BUILD_PLUGIN_MATCH_EMPTY_COUNTER
+pkglib_LTLIBRARIES += match_empty_counter.la
+match_empty_counter_la_SOURCES = match_empty_counter.c
+match_empty_counter_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" match_empty_counter.la
+collectd_DEPENDENCIES += match_empty_counter.la
+endif
+
if BUILD_PLUGIN_MATCH_REGEX
pkglib_LTLIBRARIES += match_regex.la
match_regex_la_SOURCES = match_regex.c
collectd_DEPENDENCIES += nut.la
endif
+if BUILD_PLUGIN_OLSRD
+pkglib_LTLIBRARIES += olsrd.la
+olsrd_la_SOURCES = olsrd.c
+olsrd_la_LDFLAGS = -module -avoid-version
+olsrd_la_LIBADD =
+if BUILD_WITH_LIBSOCKET
+olsrd_la_LIBADD += -lsocket
+endif
+collectd_LDADD += "-dlopen" olsrd.la
+collectd_DEPENDENCIES += olsrd.la
+endif
+
if BUILD_PLUGIN_ONEWIRE
pkglib_LTLIBRARIES += onewire.la
onewire_la_SOURCES = onewire.c
collectd_DEPENDENCIES += thermal.la
endif
+if BUILD_PLUGIN_TOKYOTYRANT
+pkglib_LTLIBRARIES += tokyotyrant.la
+tokyotyrant_la_SOURCES = tokyotyrant.c
+tokyotyrant_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBTOKYOTYRANT_CPPFLAGS)
+tokyotyrant_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBTOKYOTYRANT_LDFLAGS)
+tokyotyrant_la_LIBADD = $(BUILD_WITH_LIBTOKYOTYRANT_LIBS)
+if BUILD_WITH_LIBSOCKET
+tokyotyrant_la_LIBADD += -lsocket
+endif
+collectd_LDADD += "-dlopen" tokyotyrant.la
+collectd_DEPENDENCIES += tokyotyrant.la
+endif
+
if BUILD_PLUGIN_UNIXSOCK
pkglib_LTLIBRARIES += unixsock.la
unixsock_la_SOURCES = unixsock.c \
utils_cmd_flush.h utils_cmd_flush.c \
utils_cmd_getval.h utils_cmd_getval.c \
+ utils_cmd_getthreshold.h utils_cmd_getthreshold.c \
utils_cmd_listval.h utils_cmd_listval.c \
utils_cmd_putval.h utils_cmd_putval.c \
utils_cmd_putnotif.h utils_cmd_putnotif.c
collectd_DEPENDENCIES += wireless.la
endif
+if BUILD_PLUGIN_WRITE_HTTP
+pkglib_LTLIBRARIES += write_http.la
+write_http_la_SOURCES = write_http.c \
+ utils_format_json.c utils_format_json.h
+write_http_la_LDFLAGS = -module -avoid-version
+write_http_la_CFLAGS = $(AM_CFLAGS)
+write_http_la_LIBADD =
+collectd_LDADD += "-dlopen" write_http.la
+if BUILD_WITH_LIBCURL
+write_http_la_CFLAGS += $(BUILD_WITH_LIBCURL_CFLAGS)
+write_http_la_LIBADD += $(BUILD_WITH_LIBCURL_LIBS)
+endif
+collectd_DEPENDENCIES += write_http.la
+endif
+
if BUILD_PLUGIN_XMMS
pkglib_LTLIBRARIES += xmms.la
xmms_la_SOURCES = xmms.c
collectd_DEPENDENCIES += xmms.la
endif
+if BUILD_PLUGIN_ZFS_ARC
+pkglib_LTLIBRARIES += zfs_arc.la
+zfs_arc_la_SOURCES = zfs_arc.c
+zfs_arc_la_CFLAGS = $(AM_CFLAGS)
+zfs_arc_la_LDFLAGS = -module -avoid-version
+zfs_arc_la_LIBADD = -lkstat
+collectd_LDADD += "-dlopen" zfs_arc.la
+collectd_DEPENDENCIES += zfs_arc.la
+endif
+
dist_man_MANS = collectd.1 \
collectd.conf.5 \
}
}
+=head1 PLUGINS
+
+The following plugins are implemented in I<Java>. Both, the B<LoadPlugin>
+option and the B<Plugin> block must be inside the
+B<E<lt>PluginE<nbsp>javaE<gt>> block (see above).
+
+=head2 GenericJMX plugin
+
+The GenericJMX plugin reads I<Managed Beans> (MBeans) from an I<MBeanServer>
+using JMX. JMX is a generic framework to provide and query various management
+information. The interface is used by Java processes to provide internal
+statistics as well as by the I<Java Virtual Machine> (JVM) to provide
+information about the memory used, threads and so on.
+
+The configuration of the I<GenericJMX plugin> consists of two blocks: I<MBean>
+blocks that define a mapping of MBean attributes to the “types” used by
+I<collectd>, and I<Connection> blocks which define the parameters needed to
+connect to an I<MBeanServer> and what data to collect. The configuration of the
+I<SNMP plugin> is similar in nature, in case you know it.
+
+=head3 MBean blocks
+
+I<MBean> blocks specify what data is retrieved from I<MBeans> and how that data
+is mapped on the I<collectd> data types. The block requires one string
+argument, a name. This name is used in the I<Connection> blocks (see below) to
+refer to a specific I<MBean> block. Therefore, the names must be unique.
+
+The following options are recognized within I<MBean> blocks:
+
+=over 4
+
+=item B<ObjectName> I<pattern>
+
+Sets the pattern which is used to retrieve I<MBeans> from the I<MBeanServer>.
+If more than one MBean is returned you should use the B<InstanceFrom> option
+(see below) to make the identifiers unique.
+
+See also:
+L<http://java.sun.com/javase/6/docs/api/javax/management/ObjectName.html>
+
+=item B<InstancePrefix> I<prefix>
+
+Prefixes the generated I<plugin instance> with I<prefix>. I<(optional)>
+
+=item B<InstanceFrom> I<property>
+
+The I<object names> used by JMX to identify I<MBeans> include so called
+I<“properties”> which are basically key-value-pairs. If the given object name
+is not unique and multiple MBeans are returned, the values of those properties
+usually differ. You can use this option to build the I<plugin instance> from
+the appropriate property values. This option is optional and may be repeated to
+generate the I<plugin instance> from multiple property values.
+
+=item B<E<lt>value /E<gt>> blocks
+
+The I<value> blocks map one or more attributes of an I<MBean> to a value list
+in I<collectd>. There must be at least one Value block within each I<MBean>
+block.
+
+=over 4
+
+=item B<Type> type
+
+Sets the data set used within I<collectd> to handle the values of the I<MBean>
+attribute.
+
+=item B<InstancePrefix> I<prefix>
+
+Works like the option of the same name directly beneath the I<MBean> block, but
+sets the type instance instead. I<(optional)>
+
+=item B<InstanceFrom> I<prefix>
+
+Works like the option of the same name directly beneath the I<MBean> block, but
+sets the type instance instead. I<(optional)>
+
+=item B<Table> B<true>|B<false>
+
+Set this to true if the returned attribute is a I<composite type>. If set to
+true, the keys within the I<composite type> is appended to the
+I<type instance>.
+
+=item B<Attribute> I<path>
+
+Sets the name of the attribute from which to read the value. You can access the
+keys of composite types by using a dot to concatenate the key name to the
+attribute name. For example: “attrib0.key42”. If B<Table> is set to B<true>
+I<path> must point to a I<composite type>, otherwise it must point to a numeric
+type.
+
+=back
+
+=back
+
+=head3 Connection blocks
+
+Connection blocks specify I<how> to connect to an I<MBeanServer> and what data
+to retrieve. The following configuration options are available:
+
+=over 4
+
+=item B<Host> I<name>
+
+Host name used when dispatching the values to I<collectd>. The option sets this
+field only, it is I<not> used to connect to anything and doesn't need to be a
+real, resolvable name.
+
+=item B<ServiceURL> I<URL>
+
+Specifies how the I<MBeanServer> can be reached. Any string accepted by the
+I<JMXServiceURL> is valid.
+
+See also:
+L<http://java.sun.com/javase/6/docs/api/javax/management/remote/JMXServiceURL.html>
+
+=item B<User> I<name>
+
+Use I<name> to authenticate to the server. If not configured, “monitorRole”
+will be used.
+
+=item B<Password> I<password>
+
+Use I<password> to authenticate to the server. If not given, unauthenticated
+access is used.
+
+=item B<Collect> I<mbean_block_name>
+
+Configures which of the I<MBean> blocks to use with this connection. May be
+repeated to collect multiple I<MBeans> from this server.
+
+=back
+
=head1 SEE ALSO
L<collectd(1)>,
[{
name => 'data_source_name',
- type => DS_TYPE_COUNTER || DS_TYPE_GAUGE,
+ type => DS_TYPE_COUNTER || DS_TYPE_GAUGE || DS_TYPE_DERIVE || DS_TYPE_ABSOLUTE,
min => value || undef,
max => value || undef
}, ...]
=item B<DS_TYPE_GAUGE>
+=item B<DS_TYPE_DERIVE>
+
+=item B<DS_TYPE_ABSOLUTE>
+
=back
=item B<:log>
#@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
@LOAD_PLUGIN_CSV@LoadPlugin csv
#@BUILD_PLUGIN_CURL_TRUE@LoadPlugin curl
+#@BUILD_PLUGIN_CURL_JSON_TRUE@LoadPlugin curl_json
#@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi
#@BUILD_PLUGIN_DF_TRUE@LoadPlugin df
#@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk
#@BUILD_PLUGIN_NOTIFY_EMAIL_TRUE@LoadPlugin notify_email
#@BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd
#@BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut
+#@BUILD_PLUGIN_OLSRD_TRUE@LoadPlugin olsrd
#@BUILD_PLUGIN_ONEWIRE_TRUE@LoadPlugin onewire
#@BUILD_PLUGIN_OPENVPN_TRUE@LoadPlugin openvpn
#@BUILD_PLUGIN_ORACLE_TRUE@LoadPlugin oracle
#@BUILD_PLUGIN_TEAMSPEAK2_TRUE@LoadPlugin teamspeak2
#@BUILD_PLUGIN_TED_TRUE@LoadPlugin ted
#@BUILD_PLUGIN_THERMAL_TRUE@LoadPlugin thermal
+#@BUILD_PLUGIN_TOKYOTYRANT_TRUE@LoadPlugin tokyotyrant
#@BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock
#@BUILD_PLUGIN_UPTIME_TRUE@LoadPlugin uptime
#@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users
#@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_XMMS_TRUE@LoadPlugin xmms
+#@BUILD_PLUGIN_ZFS_ARC_TRUE@LoadPlugin zfs_arc
##############################################################################
# Plugin configuration #
# </View>
#</Plugin>
+#<Plugin couchdb>
+## See: http://wiki.apache.org/couchdb/Runtime_Statistics
+# <URL "http://localhost:5984/_stats">
+# Instance "httpd"
+# <Key "httpd/requests/count">
+# Type "http_requests"
+# </Key>
+#
+# <Key "httpd_request_methods/*/count">
+# Type "http_request_methods"
+# </Key>
+#
+# <Key "httpd_status_codes/*/count">
+# Type "http_response_codes"
+# </Key>
+# </URL>
+## Database status metrics:
+# <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>
+#</Plugin>
+
#<Plugin csv>
# DataDir "@prefix@/var/lib/@PACKAGE_NAME@/csv"
# StoreRates false
# MountPoint "/home"
# FSType "ext3"
# IgnoreSelected false
+# ReportByDevice false
#</Plugin>
#<Plugin disk>
# TranslateDevicename false
#</Plugin>
+#<Plugin http>
+# URL "http://example.com/collectd-import"
+# User "www-user"
+# Password "secret"
+#</Plugin>
+
#<Plugin interface>
# Interface "eth0"
# IgnoreSelected false
#<Plugin "java">
# JVMArg "-verbose:jni"
-# JVMArg "-Djava.class.path=/opt/collectd/lib/collectd/bindings/java"
+# JVMArg "-Djava.class.path=@prefix@/share/collectd/java"
#
# LoadPlugin "org.collectd.java.Foobar"
# <Plugin "org.collectd.java.Foobar">
# UPS "upsname@hostname:port"
#</Plugin>
+#<Plugin olsrd>
+# Host "127.0.0.1"
+# Port "2006"
+# CollectLinks "Summary"
+# CollectRoutes "Summary"
+# CollectTopology "Summary"
+#</Plugin>
+
#<Plugin onewire>
# Device "-s localhost:4304"
# Sensor "F10FCA000800"
# Version 1
# Community "community_string"
# Collect "std_traffic"
-# Inverval 120
+# Interval 120
# </Host>
# <Host "some.server.mydomain.org">
# Address "192.168.0.42"
# IgnoreSelected false
#</Plugin>
+#<Plugin tokyotyrant>
+# Host "localhost"
+# Port "1978"
+#</Plugin>
+
#<Plugin unixsock>
# SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock"
# SocketGroup "collectd"
# Verbose false
#</Plugin>
+#<Plugin write_http>
+# <URL "http://example.com/collectd-post">
+# User "collectd"
+# Password "weCh3ik0"
+# </URL>
+#</Plugin>
+
##############################################################################
# Filter configuration #
#----------------------------------------------------------------------------#
=back
+=head2 Plugin C<curl_json>
+
+The B<curl_json plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and
+B<libyajl> (L<http://www.lloydforge.org/projects/yajl/>) to retrieve JSON data
+via cURL. This can be used to collect values from CouchDB documents (which are
+stored JSON notation), for example.
+
+The following example will collect several values from the built-in `_stats'
+runtime statistics module of CouchDB
+(L<http://wiki.apache.org/couchdb/Runtime_Statistics>).
+
+ <Plugin curl_json>
+ <URL "http://localhost:5984/_stats">
+ Instance "httpd"
+ <Key "httpd/requests/count">
+ Type "http_requests"
+ </Key>
+
+ <Key "httpd_request_methods/*/count">
+ Type "http_request_methods"
+ </Key>
+
+ <Key "httpd_status_codes/*/count">
+ Type "http_response_codes"
+ </Key>
+ </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, of which is used to collect
+a value from a JSON map object. If a B<Key> path element is that of a I<*> wildcard,
+the values for all keys will be collectd.
+
+The following options are valid within B<URL> blocks:
+
+=over 4
+
+=item B<Instance> I<Instance>
+
+Sets the plugin instance to I<Instance>.
+
+=item B<User> I<Name>
+
+Username to use if authorization is required to read the page.
+
+=item B<Password> I<Password>
+
+Password to use if authorization is required to read the page.
+
+=item B<VerifyPeer> B<true>|B<false>
+
+Enable or disable peer SSL certificate verification. See
+L<http://curl.haxx.se/docs/sslcerts.html> for details. Enabled by default.
+
+=item B<VerifyHost> B<true>|B<false>
+
+Enable or disable peer host name verification. If enabled, the plugin checks if
+the C<Common Name> or a C<Subject Alternate Name> field of the SSL certificate
+matches the host name provided by the B<URL> option. If this identity check
+fails, the connection is aborted. Obviously, only works when connecting to a
+SSL enabled server. Enabled by default.
+
+=item B<CACert> I<file>
+
+File that holds one or more SSL certificates. If you want to use HTTPS you will
+possibly need this option. What CA certificates come bundled with C<libcurl>
+and are checked by default depends on the distribution you use.
+
+=back
+
+The following options are valid within B<Key> blocks:
+
+=over 4
+
+=item B<Type> I<Type>
+
+Sets the type used to dispatch the values to the daemon. Detailed information
+about types and their configuration can be found in L<types.db(5)>. This
+option is mandatory.
+
+=item B<Instance> I<Instance>
+
+Type-instance to use. Defaults to the current map key or current string array element value.
+
+=back
+
=head2 Plugin C<dbi>
This plugin uses the B<dbi> library (L<http://libdbi.sourceforge.net/>) to
partitions are collected if a selection is made. If no selection is configured
at all, B<all> partitions are selected.
+=item B<ReportByDevice> I<true>|I<false>
+
+Report using the device name rather than the mountpoint. i.e. with this I<false>,
+(the default), it will report a disk as "root", but with it I<true>, it will be
+"sda1" (or whichever).
+
=back
=head2 Plugin C<disk>
Ignore packets that originate from this address.
+=item B<SelectNumericQueryTypes> B<true>|B<false>
+
+Enabled by default, collects unknown (and thus presented as numeric only) query types.
+
=back
=head2 Plugin C<email>
=back
+=head2 Plugin C<GenericJMX>
+
+The I<GenericJMX plugin> is written in I<Java> and therefore documented in
+L<collectd-java(5)>.
+
=head2 Plugin C<gmond>
The I<gmond> plugin received the multicast traffic sent by B<gmond>, the
That means that multicast packets will be sent with a TTL of C<1> (one) on most
operating systems.
+=item B<MaxPacketSize> I<1024-65535>
+
+Set the maximum size for datagrams received over the network. Packets larger
+than this will be truncated.
+
=item B<Forward> I<true|false>
If set to I<true>, write packets that were received via the network plugin to
=back
+=head2 Plugin C<olsrd>
+
+The I<olsrd> plugin connects to the TCP port opened by the I<txtinfo> plugin of
+the Optimized Link State Routing daemon and reads information about the current
+state of the meshed network.
+
+The following configuration options are understood:
+
+=over 4
+
+=item B<Host> I<Host>
+
+Connect to I<Host>. Defaults to B<"localhost">.
+
+=item B<Port> I<Port>
+
+Specifies the port to connect to. This must be a string, even if you give the
+port as a number rather than a service name. Defaults to B<"2006">.
+
+=item B<CollectLinks> B<No>|B<Summary>|B<Detail>
+
+Specifies what information to collect about links, i.E<nbsp>e. direct
+connections of the daemon queried. If set to B<No>, no information is
+collected. If set to B<Summary>, the number of links and the average of all
+I<link quality> (LQ) and I<neighbor link quality> (NLQ) values is calculated.
+If set to B<Detail> LQ and NLQ are collected per link.
+
+Defaults to B<Detail>.
+
+=item B<CollectRoutes> B<No>|B<Summary>|B<Detail>
+
+Specifies what information to collect about routes of the daemon queried. If
+set to B<No>, no information is collected. If set to B<Summary>, the number of
+routes and the average I<metric> and I<ETX> is calculated. If set to B<Detail>
+metric and ETX are collected per route.
+
+Defaults to B<Summary>.
+
+=item B<CollectTopology> B<No>|B<Summary>|B<Detail>
+
+Specifies what information to collect about the global topology. If set to
+B<No>, no information is collected. If set to B<Summary>, the number of links
+in the entire topology and the average I<link quality> (LQ) is calculated.
+If set to B<Detail> LQ and NLQ are collected for each link in the entire topology.
+
+Defaults to B<Summary>.
+
+=back
+
=head2 Plugin C<onewire>
B<EXPERIMENTAL!> See notes below.
"collection3" you'll end up with a responsive and fast system, up to date
graphs and basically a "backup" of your values every hour.
+=item B<RandomTimeout> I<Seconds>
+
+When set, the actual timeout for each value is chosen randomly between
+I<CacheTimeout>-I<RandomTimeout> and I<CacheTimeout>+I<RandomTimeout>. The
+intention is to avoid high load situations that appear when many values timeout
+at the same time. This is especially a problem shortly after the daemon starts,
+because all values were added to the internal cache at roughly the same time.
+
=back
=head2 Plugin C<sensors>
=back
+=head2 Plugin C<tokyotyrant>
+
+The C<tokyotyrant plugin> connects to a TokyoTyrant server and collects a
+couple metrics: number of records, and database size on disk.
+
+=over 4
+
+=item B<Host> I<Hostname/IP>
+
+The hostname or ip which identifies the server.
+Default: B<127.0.0.1>
+
+=item B<Port> I<Service/Port>
+
+The query port of the server. This needs to be a string, even if the port is
+given in its numeric form.
+Default: B<1978>
+
+=back
+
=head2 Plugin C<unixsock>
=over 4
collect on-wire traffic you could, for example, use the logging facilities of
iptables to feed data for the guest IPs into the iptables plugin.
+=head2 Plugin C<write_http>
+
+This output plugin submits values to an http server by POST them using the
+PUTVAL plain-text protocol. Each destination you want to post data to needs to
+have one B<URL> block, within which the destination can be configured further,
+for example by specifying authentication data.
+
+Synopsis:
+
+ <Plugin "write_http">
+ <URL "http://example.com/post-collectd">
+ User "collectd"
+ Password "weCh3ik0"
+ </URL>
+ </Plugin>
+
+B<URL> blocks need one string argument which is used as the URL to which data
+is posted. The following options are understood within B<URL> blocks.
+
+=over 4
+
+=item B<User> I<Username>
+
+Optional user name needed for authentication.
+
+=item B<Password> I<Password>
+
+Optional password needed for authentication.
+
+=item B<VerifyPeer> B<true>|B<false>
+
+Enable or disable peer SSL certificate verification. See
+L<http://curl.haxx.se/docs/sslcerts.html> for details. Enabled by default.
+
+=item B<VerifyHost> B<true|false>
+
+Enable or disable peer host name verification. If enabled, the plugin checks if
+the C<Common Name> or a C<Subject Alternate Name> field of the SSL certificate
+matches the host name provided by the B<URL> option. If this identity check
+fails, the connection is aborted. Obviously, only works when connecting to a
+SSL enabled server. Enabled by default.
+
+=item B<CACert> I<File>
+
+File that holds one or more SSL certificates. If you want to use HTTPS you will
+possibly need this option. What CA certificates come bundled with C<libcurl>
+and are checked by default depends on the distribution you use.
+
+=item B<Format> B<Command>|B<JSON>
+
+Format of the output to generate. If set to B<Command>, will create output that
+is understood by the I<Exec> and I<UnixSock> plugins. When set to B<JSON>, will
+create output in the I<JavaScript Object Notation> (JSON).
+
+Defaults to B<Command>.
+
+=back
+
=head1 THRESHOLD CONFIGURATION
Starting with version C<4.3.0> collectd has support for B<monitoring>. By that
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>.
+
=back
=head1 FILTER CONFIGURATION
Satisfy "Any"
</Match>
+=item B<empty_counter>
+
+Matches all values with one or more data sources of type B<COUNTER> and where
+all counter values are zero. These counters usually I<never> increased since
+they started existing (and are therefore uninteresting), or got reset recently
+or overflowed and you had really, I<really> bad luck.
+
+Please keep in mind that ignoring such counters can result in confusing
+behavior: Counters which hardly ever increase will be zero for long periods of
+time. If the counter is reset for some reason (machine or service restarted,
+usually), the graph will be empty (NAN) for a long time. People may not
+understand why.
+
=back
=head2 Available targets
return (0);
} /* int parse_identifier */
-int parse_value (const char *value, value_t *ret_value, const data_source_t ds)
+int parse_value (const char *value, value_t *ret_value, int ds_type)
{
- char *endptr = NULL;
-
- if (DS_TYPE_COUNTER == ds.type)
- ret_value->counter = (counter_t)strtoll (value, &endptr, 0);
- else if (DS_TYPE_GAUGE == ds.type)
- ret_value->gauge = (gauge_t)strtod (value, &endptr);
- else {
- ERROR ("parse_value: Invalid data source \"%s\" "
- "(type = %i).", ds.name, ds.type);
- return -1;
- }
-
- if (value == endptr) {
- ERROR ("parse_value: Failed to parse string as number: %s.", value);
- return -1;
- }
- else if ((NULL != endptr) && ('\0' != *endptr))
- WARNING ("parse_value: Ignoring trailing garbage after number: %s.",
- endptr);
- return 0;
+ char *endptr = NULL;
+
+ switch (ds_type)
+ {
+ case DS_TYPE_COUNTER:
+ ret_value->counter = (counter_t) strtoull (value, &endptr, 0);
+ break;
+
+ case DS_TYPE_GAUGE:
+ ret_value->gauge = (gauge_t) strtod (value, &endptr);
+ break;
+
+ case DS_TYPE_DERIVE:
+ ret_value->counter = (derive_t) strtoll (value, &endptr, 0);
+ break;
+
+ case DS_TYPE_ABSOLUTE:
+ ret_value->counter = (absolute_t) strtoull (value, &endptr, 0);
+ break;
+
+ default:
+ ERROR ("parse_value: Invalid data source type: %i.", ds_type);
+ return -1;
+ }
+
+ if (value == endptr) {
+ ERROR ("parse_value: Failed to parse string as number: %s.", value);
+ return -1;
+ }
+ else if ((NULL != endptr) && ('\0' != *endptr))
+ WARNING ("parse_value: Ignoring trailing garbage after number: %s.",
+ endptr);
+ return 0;
} /* int parse_value */
int parse_values (char *buffer, value_list_t *vl, const data_set_t *ds)
{
if ((strcmp ("U", ptr) == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
vl->values[i].gauge = NAN;
- else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i]))
+ else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i].type))
return -1;
}
const char *hostname,
const char *plugin, const char *plugin_instance,
const char *type, const char *type_instance);
-#define FORMAT_VL(ret, ret_len, vl, ds) \
+#define FORMAT_VL(ret, ret_len, vl) \
format_name (ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
- (ds)->type, (vl)->type_instance)
+ (vl)->type, (vl)->type_instance)
int parse_identifier (char *str, char **ret_host,
char **ret_plugin, char **ret_plugin_instance,
char **ret_type, char **ret_type_instance);
-int parse_value (const char *value, value_t *ret_value, const data_source_t ds);
+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);
#if !HAVE_GETPWNAM_R
for (i = 0; i < ds->ds_num; i++)
{
if ((ds->ds[i].type != DS_TYPE_COUNTER)
- && (ds->ds[i].type != DS_TYPE_GAUGE))
+ && (ds->ds[i].type != DS_TYPE_GAUGE)
+ && (ds->ds[i].type != DS_TYPE_DERIVE)
+ && (ds->ds[i].type != DS_TYPE_ABSOLUTE))
return (-1);
- if (ds->ds[i].type == DS_TYPE_COUNTER)
+ if (ds->ds[i].type == DS_TYPE_GAUGE)
{
- if (store_rates == 0)
- {
- status = ssnprintf (buffer + offset,
- buffer_len - offset,
- ",%llu",
- vl->values[i].counter);
- }
- else /* if (store_rates == 1) */
+ status = ssnprintf (buffer + offset, buffer_len - offset,
+ ",%lf", vl->values[i].gauge);
+ }
+ else if (store_rates != 0)
+ {
+ if (rates == NULL)
+ rates = uc_get_rate (ds, vl);
+ if (rates == NULL)
{
- if (rates == NULL)
- rates = uc_get_rate (ds, vl);
- if (rates == NULL)
- {
- WARNING ("csv plugin: "
- "uc_get_rate failed.");
- return (-1);
- }
- status = ssnprintf (buffer + offset,
- buffer_len - offset,
- ",%lf", rates[i]);
+ WARNING ("csv plugin: "
+ "uc_get_rate failed.");
+ return (-1);
}
+ status = ssnprintf (buffer + offset,
+ buffer_len - offset,
+ ",%lf", rates[i]);
}
- else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+ else if (ds->ds[i].type == DS_TYPE_COUNTER)
{
- status = ssnprintf (buffer + offset, buffer_len - offset,
- ",%lf", vl->values[i].gauge);
+ status = ssnprintf (buffer + offset,
+ buffer_len - offset,
+ ",%llu",
+ vl->values[i].counter);
+ }
+ else if (ds->ds[i].type == DS_TYPE_DERIVE)
+ {
+ status = ssnprintf (buffer + offset,
+ buffer_len - offset,
+ ",%"PRIi64,
+ vl->values[i].derive);
+ }
+ else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+ {
+ status = ssnprintf (buffer + offset,
+ buffer_len - offset,
+ ",%"PRIu64,
+ vl->values[i].absolute);
}
if ((status < 1) || (status >= (buffer_len - offset)))
else
dstype = 0;
}
+else if (strncasecmp ("Derive", ci->values[0].value.string,
+ strlen ("Derive")) == 0)
+ {
+ dstype = UTILS_MATCH_DS_TYPE_DERIVE;
+ if (strcasecmp ("DeriveSet", ci->values[0].value.string) == 0)
+ dstype |= UTILS_MATCH_CF_DERIVE_SET;
+ else if (strcasecmp ("DeriveAdd", ci->values[0].value.string) == 0)
+ dstype |= UTILS_MATCH_CF_DERIVE_ADD;
+ else if (strcasecmp ("DeriveInc", ci->values[0].value.string) == 0)
+ dstype |= UTILS_MATCH_CF_DERIVE_INC;
+ else
+ dstype = 0;
+ }
+else if (strncasecmp ("Absolute", ci->values[0].value.string,
+ strlen ("Absolute")) == 0)
+ {
+ dstype = UTILS_MATCH_DS_TYPE_ABSOLUTE;
+ if (strcasecmp ("AbsoluteSet", ci->values[0].value.string) == 0) /* Absolute DS is reset-on-read so no sense doin anything else but set */
+ dstype |= UTILS_MATCH_CF_ABSOLUTE_SET;
+ else
+ dstype = 0;
+ }
+
else
{
dstype = 0;
--- /dev/null
+/**
+ * collectd - src/curl_json.c
+ * Copyright (C) 2009 Doug MacEachern
+ * Copyright (C) 2006-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
+ *
+ * Authors:
+ * Doug MacEachern <dougm at hyperic.com>
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_avltree.h"
+
+#include <curl/curl.h>
+#include <yajl/yajl_parse.h>
+
+#define CJ_DEFAULT_HOST "localhost"
+#define CJ_KEY_MAGIC 0x43484b59UL /* CHKY */
+#define CJ_IS_KEY(key) (key)->magic == CJ_KEY_MAGIC
+#define CJ_ANY "*"
+#define COUCH_MIN(x,y) ((x) < (y) ? (x) : (y))
+
+struct cj_key_s;
+typedef struct cj_key_s cj_key_t;
+struct cj_key_s /* {{{ */
+{
+ char *path;
+ char *type;
+ char *instance;
+ unsigned long magic;
+};
+/* }}} */
+
+struct cj_s /* {{{ */
+{
+ char *instance;
+ char *host;
+
+ char *url;
+ char *user;
+ char *pass;
+ char *credentials;
+ int verify_peer;
+ int verify_host;
+ char *cacert;
+
+ CURL *curl;
+ char curl_errbuf[CURL_ERROR_SIZE];
+
+ yajl_handle yajl;
+ c_avl_tree_t *tree;
+ cj_key_t *key;
+ int depth;
+ struct {
+ union {
+ c_avl_tree_t *tree;
+ cj_key_t *key;
+ };
+ char name[DATA_MAX_NAME_LEN];
+ } state[YAJL_MAX_DEPTH];
+};
+typedef struct cj_s cj_t; /* }}} */
+
+static int cj_read (user_data_t *ud);
+static int cj_curl_perform (cj_t *db, CURL *curl);
+static void cj_submit (cj_t *db, cj_key_t *key, value_t *value);
+
+static size_t cj_curl_callback (void *buf, /* {{{ */
+ size_t size, size_t nmemb, void *user_data)
+{
+ cj_t *db;
+ size_t len;
+ yajl_status status;
+
+ len = size * nmemb;
+
+ if (len <= 0)
+ return (len);
+
+ db = user_data;
+ if (db == NULL)
+ 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)
+ {
+ unsigned char *msg =
+ yajl_get_error(db->yajl, 1, (unsigned char *)buf, len);
+ ERROR ("curl_json plugin: yajl_parse failed: %s", msg);
+ yajl_free_error(db->yajl, msg);
+ return (0); /* abort write callback */
+ }
+
+ return (len);
+} /* }}} size_t cj_curl_callback */
+
+static int cj_get_type (cj_key_t *key)
+{
+ const data_set_t *ds;
+
+ ds = plugin_get_ds (key->type);
+ if (ds == NULL)
+ return -1; /* let plugin_write do the complaining */
+ else
+ return ds->ds[0].type; /* XXX support ds->ds_len > 1 */
+}
+
+/* yajl callbacks */
+static int cj_cb_integer (void *ctx, long val)
+{
+ cj_t *db = (cj_t *)ctx;
+ cj_key_t *key = db->state[db->depth].key;
+
+ 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;
+
+ cj_submit (db, key, &vt);
+ }
+ return 1;
+}
+
+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)
+ {
+ 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);
+ }
+ return 1;
+}
+
+static int cj_cb_map_key (void *ctx, const unsigned char *val,
+ unsigned int len)
+{
+ cj_t *db = (cj_t *)ctx;
+ c_avl_tree_t *tree;
+
+ tree = db->state[db->depth-1].tree;
+
+ if (tree != NULL)
+ {
+ cj_key_t *value;
+ char *name;
+
+ name = db->state[db->depth].name;
+ len = COUCH_MIN(len, sizeof (db->state[db->depth].name)-1);
+ sstrncpy (name, (char *)val, len+1);
+
+ if (c_avl_get (tree, name, (void *) &value) == 0)
+ db->state[db->depth].key = value;
+ else if (c_avl_get (tree, CJ_ANY, (void *) &value) == 0)
+ db->state[db->depth].key = value;
+ else
+ db->state[db->depth].key = NULL;
+ }
+
+ return 1;
+}
+
+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;
+
+ cj_cb_map_key (ctx, val, len); /* same logic */
+
+ tree = db->state[db->depth].tree;
+
+ if ((tree != NULL) && (ptr = rindex (db->url, '/')))
+ {
+ 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);
+ }
+ return 1;
+}
+
+static int cj_cb_start (void *ctx)
+{
+ cj_t *db = (cj_t *)ctx;
+ if (++db->depth >= YAJL_MAX_DEPTH)
+ {
+ ERROR ("curl_json plugin: %s depth exceeds max, aborting.", db->url);
+ return 0;
+ }
+ return 1;
+}
+
+static int cj_cb_end (void *ctx)
+{
+ cj_t *db = (cj_t *)ctx;
+ db->state[db->depth].tree = NULL;
+ --db->depth;
+ return 1;
+}
+
+static int cj_cb_start_map (void *ctx)
+{
+ return cj_cb_start (ctx);
+}
+
+static int cj_cb_end_map (void *ctx)
+{
+ return cj_cb_end (ctx);
+}
+
+static int cj_cb_start_array (void * ctx)
+{
+ return cj_cb_start (ctx);
+}
+
+static int cj_cb_end_array (void * ctx)
+{
+ return cj_cb_start (ctx);
+}
+
+static yajl_callbacks ycallbacks = {
+ NULL, /* null */
+ NULL, /* boolean */
+ cj_cb_integer,
+ cj_cb_double,
+ NULL, /* number */
+ cj_cb_string,
+ cj_cb_start_map,
+ cj_cb_map_key,
+ cj_cb_end_map,
+ cj_cb_start_array,
+ cj_cb_end_array
+};
+
+/* end yajl callbacks */
+
+static void cj_key_free (cj_key_t *key) /* {{{ */
+{
+ if (key == NULL)
+ return;
+
+ sfree (key->path);
+ sfree (key->type);
+ sfree (key->instance);
+
+ sfree (key);
+} /* }}} void cj_key_free */
+
+static void cj_tree_free (c_avl_tree_t *tree) /* {{{ */
+{
+ char *name;
+ void *value;
+
+ while (c_avl_pick (tree, (void *) &name, (void *) &value) == 0)
+ {
+ cj_key_t *key = (cj_key_t *)value;
+
+ if (CJ_IS_KEY(key))
+ cj_key_free (key);
+ else
+ cj_tree_free ((c_avl_tree_t *)value);
+
+ sfree (name);
+ }
+
+ c_avl_destroy (tree);
+} /* }}} void cj_tree_free */
+
+static void cj_free (void *arg) /* {{{ */
+{
+ cj_t *db;
+
+ DEBUG ("curl_json plugin: cj_free (arg = %p);", arg);
+
+ db = (cj_t *) arg;
+
+ if (db == NULL)
+ return;
+
+ if (db->curl != NULL)
+ curl_easy_cleanup (db->curl);
+ db->curl = NULL;
+
+ if (db->tree != NULL)
+ cj_tree_free (db->tree);
+ db->tree = NULL;
+
+ sfree (db->instance);
+ sfree (db->host);
+
+ sfree (db->url);
+ sfree (db->user);
+ sfree (db->pass);
+ sfree (db->credentials);
+ sfree (db->cacert);
+
+ sfree (db);
+} /* }}} void cj_free */
+
+/* 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);
+}
+
+static int cj_config_add_key (cj_t *db, /* {{{ */
+ oconfig_item_t *ci)
+{
+ cj_key_t *key;
+ int status;
+ int i;
+
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("curl_json plugin: The `Key' block "
+ "needs exactly one string argument.");
+ return (-1);
+ }
+
+ key = (cj_key_t *) malloc (sizeof (*key));
+ if (key == NULL)
+ {
+ ERROR ("curl_json plugin: malloc failed.");
+ return (-1);
+ }
+ memset (key, 0, sizeof (*key));
+ key->magic = CJ_KEY_MAGIC;
+
+ if (strcasecmp ("Key", ci->key) == 0)
+ {
+ status = cj_config_add_string ("Key", &key->path, ci);
+ if (status != 0)
+ {
+ sfree (key);
+ return (status);
+ }
+ }
+ else
+ {
+ ERROR ("curl_json plugin: cj_config: "
+ "Invalid key: %s", ci->key);
+ 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 = cj_config_add_string ("Type", &key->type, child);
+ else if (strcasecmp ("Instance", child->key) == 0)
+ status = cj_config_add_string ("Instance", &key->instance, child);
+ else
+ {
+ WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ while (status == 0)
+ {
+ if (key->type == NULL)
+ {
+ WARNING ("curl_json plugin: `Type' missing in `Key' block.");
+ status = -1;
+ }
+
+ break;
+ } /* while (status == 0) */
+
+ /* store path in a tree that will match the json map structure, example:
+ * "httpd/requests/count",
+ * "httpd/requests/current" ->
+ * { "httpd": { "requests": { "count": $key, "current": $key } } }
+ */
+ if (status == 0)
+ {
+ char *ptr;
+ char *name;
+ char ent[PATH_MAX];
+ c_avl_tree_t *tree;
+
+ if (db->tree == NULL)
+ db->tree = cj_avl_create();
+
+ tree = db->tree;
+ name = key->path;
+ ptr = key->path;
+ if (*ptr == '/')
+ ++ptr;
+
+ name = ptr;
+ while (*ptr)
+ {
+ if (*ptr == '/')
+ {
+ c_avl_tree_t *value;
+ int len;
+
+ len = ptr-name;
+ if (len == 0)
+ break;
+ sstrncpy (ent, name, len+1);
+
+ if (c_avl_get (tree, ent, (void *) &value) != 0)
+ {
+ value = cj_avl_create ();
+ c_avl_insert (tree, strdup (ent), value);
+ }
+
+ tree = value;
+ name = ptr+1;
+ }
+ ++ptr;
+ }
+ if (*name)
+ c_avl_insert (tree, strdup(name), key);
+ else
+ {
+ ERROR ("curl_json plugin: invalid key: %s", key->path);
+ status = -1;
+ }
+ }
+
+ return (status);
+} /* }}} int cj_config_add_key */
+
+static int cj_init_curl (cj_t *db) /* {{{ */
+{
+ db->curl = curl_easy_init ();
+ if (db->curl == NULL)
+ {
+ ERROR ("curl_json plugin: curl_easy_init failed.");
+ return (-1);
+ }
+
+ curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cj_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_json 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);
+ curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
+ db->verify_host ? 2 : 0);
+ if (db->cacert != NULL)
+ curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
+
+ return (0);
+} /* }}} int cj_init_curl */
+
+static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
+{
+ cj_t *db;
+ int status = 0;
+ int i;
+
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("curl_json plugin: The `URL' block "
+ "needs exactly one string argument.");
+ return (-1);
+ }
+
+ db = (cj_t *) malloc (sizeof (*db));
+ if (db == NULL)
+ {
+ ERROR ("curl_json plugin: malloc failed.");
+ return (-1);
+ }
+ memset (db, 0, sizeof (*db));
+
+ if (strcasecmp ("URL", ci->key) == 0)
+ {
+ status = cj_config_add_string ("URL", &db->url, ci);
+ if (status != 0)
+ {
+ sfree (db);
+ return (status);
+ }
+ }
+ else
+ {
+ ERROR ("curl_json plugin: cj_config: "
+ "Invalid key: %s", ci->key);
+ return (-1);
+ }
+
+ /* Fill the `cj_t' structure.. */
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Instance", child->key) == 0)
+ status = cj_config_add_string ("Instance", &db->instance, child);
+ else if (strcasecmp ("Host", child->key) == 0)
+ status = cj_config_add_string ("Host", &db->host, child);
+ else if (strcasecmp ("User", child->key) == 0)
+ status = cj_config_add_string ("User", &db->user, child);
+ else if (strcasecmp ("Password", child->key) == 0)
+ status = cj_config_add_string ("Password", &db->pass, child);
+ else if (strcasecmp ("VerifyPeer", child->key) == 0)
+ status = cj_config_set_boolean ("VerifyPeer", &db->verify_peer, child);
+ else if (strcasecmp ("VerifyHost", child->key) == 0)
+ status = cj_config_set_boolean ("VerifyHost", &db->verify_host, child);
+ else if (strcasecmp ("CACert", child->key) == 0)
+ status = cj_config_add_string ("CACert", &db->cacert, child);
+ else if (strcasecmp ("Key", child->key) == 0)
+ status = cj_config_add_key (db, child);
+ else
+ {
+ WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if (status == 0)
+ {
+ if (db->tree == NULL)
+ {
+ WARNING ("curl_json plugin: No (valid) `Key' block "
+ "within `URL' block `%s'.", db->url);
+ status = -1;
+ }
+ if (status == 0)
+ status = cj_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_json plugin: Registering new read callback: %s",
+ db->instance);
+
+ memset (&ud, 0, sizeof (ud));
+ ud.data = (void *) db;
+ ud.free_func = cj_free;
+
+ ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
+ db->instance, db->url);
+
+ plugin_register_complex_read (cb_name, cj_read,
+ /* interval = */ NULL, &ud);
+ }
+ else
+ {
+ cj_free (db);
+ return (-1);
+ }
+
+ return (0);
+}
+ /* }}} int cj_config_add_database */
+
+static int cj_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 = cj_config_add_url (child);
+ if (status == 0)
+ success++;
+ else
+ errors++;
+ }
+ else
+ {
+ WARNING ("curl_json plugin: Option `%s' not allowed here.", child->key);
+ errors++;
+ }
+ }
+
+ if ((success == 0) && (errors > 0))
+ {
+ ERROR ("curl_json plugin: All statements failed.");
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int cj_config */
+
+/* }}} End of configuration handling functions */
+
+static void cj_submit (cj_t *db, cj_key_t *key, value_t *value) /* {{{ */
+{
+ value_list_t vl = VALUE_LIST_INIT;
+ char *host;
+
+ vl.values = value;
+ vl.values_len = 1;
+
+ if ((db->host == NULL)
+ || (strcmp ("", db->host) == 0)
+ || (strcmp (CJ_DEFAULT_HOST, db->host) == 0))
+ host = hostname_g;
+ else
+ host = db->host;
+
+ if (key->instance == NULL)
+ ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
+ db->state[db->depth-1].name, db->state[db->depth].name);
+ else
+ sstrncpy (vl.type_instance, key->instance, sizeof (vl.type_instance));
+
+ sstrncpy (vl.host, host, sizeof (vl.host));
+ sstrncpy (vl.plugin, "curl_json", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, db->instance, sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, key->type, sizeof (vl.type));
+
+ plugin_dispatch_values (&vl);
+} /* }}} int cj_submit */
+
+static int cj_curl_perform (cj_t *db, CURL *curl) /* {{{ */
+{
+ int status;
+ long rc;
+ char *url;
+ yajl_handle yprev = db->yajl;
+
+ db->yajl = yajl_alloc (&ycallbacks, NULL, NULL, (void *)db);
+ if (db->yajl == NULL)
+ {
+ ERROR ("curl_json plugin: yajl_alloc failed.");
+ return (-1);
+ }
+
+ status = curl_easy_perform (curl);
+
+ yajl_free (db->yajl);
+ db->yajl = yprev;
+
+ curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+
+ if (rc != 200)
+ {
+ ERROR ("curl_json plugin: curl_easy_perform failed with response code %ld (%s)",
+ rc, url);
+ return (-1);
+ }
+
+ if (status != 0)
+ {
+ ERROR ("curl_json plugin: curl_easy_perform failed with status %i: %s (%s)",
+ status, db->curl_errbuf, url);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int cj_curl_perform */
+
+static int cj_read (user_data_t *ud) /* {{{ */
+{
+ cj_t *db;
+
+ if ((ud == NULL) || (ud->data == NULL))
+ {
+ ERROR ("curl_json plugin: cj_read: Invalid user data.");
+ return (-1);
+ }
+
+ db = (cj_t *) ud->data;
+
+ db->depth = 0;
+ memset (&db->state, 0, sizeof(db->state));
+ db->state[db->depth].tree = db->tree;
+ db->key = NULL;
+
+ return cj_curl_perform (db, db->curl);
+} /* }}} int cj_read */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("curl_json", cj_config);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
/**
* collectd - src/df.c
* Copyright (C) 2005-2007 Florian octo Forster
+ * Copyright (C) 2009 Paul Sadauskas
*
* 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:
* Florian octo Forster <octo at verplant.org>
+ * Paul Sadauskas <psadauskas at gmail.com>
**/
#include "collectd.h"
"MountPoint",
"FSType",
"IgnoreSelected",
- NULL
+ "ReportByDevice"
};
-static int config_keys_num = 4;
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
static ignorelist_t *il_device = NULL;
static ignorelist_t *il_mountpoint = NULL;
static ignorelist_t *il_fstype = NULL;
+static _Bool by_device = false;
+
static int df_init (void)
{
if (il_device == NULL)
}
else if (strcasecmp (key, "IgnoreSelected") == 0)
{
- if ((strcasecmp (value, "True") == 0)
- || (strcasecmp (value, "Yes") == 0)
- || (strcasecmp (value, "On") == 0))
+ if (IS_TRUE (value))
{
ignorelist_set_invert (il_device, 0);
ignorelist_set_invert (il_mountpoint, 0);
}
return (0);
}
+ else if (strcasecmp (key, "ReportByDevice") == 0)
+ {
+ if (IS_TRUE (value))
+ by_device = true;
+
+ return (0);
+ }
return (-1);
}
unsigned long long blocksize;
gauge_t df_free;
gauge_t df_used;
- char mnt_name[256];
+ char disk_name[256];
mnt_list = NULL;
if (cu_mount_getlist (&mnt_list) == NULL)
for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next)
{
- if (strcmp (mnt_ptr->dir, "/") == 0)
- {
- sstrncpy (mnt_name, "root", sizeof (mnt_name));
- }
- else
- {
- int i, len;
-
- sstrncpy (mnt_name, mnt_ptr->dir + 1, sizeof (mnt_name));
- len = strlen (mnt_name);
-
- for (i = 0; i < len; i++)
- if (mnt_name[i] == '/')
- mnt_name[i] = '-';
- }
-
if (ignorelist_match (il_device,
(mnt_ptr->spec_device != NULL)
? mnt_ptr->spec_device
df_free = statbuf.f_bfree * blocksize;
df_used = (statbuf.f_blocks - statbuf.f_bfree) * blocksize;
- df_submit (mnt_name, df_used, df_free);
+ if (by_device)
+ {
+ /* eg, /dev/hda1 -- strip off the "/dev/" */
+ if (strncmp (mnt_ptr->spec_device, "/dev/", strlen ("/dev/")) == 0)
+ sstrncpy (disk_name, mnt_ptr->spec_device + strlen ("/dev/"), sizeof (disk_name));
+ else
+ sstrncpy (disk_name, mnt_ptr->spec_device, sizeof (disk_name));
+
+ if (strlen(disk_name) < 1)
+ {
+ DEBUG("df: no device name name for mountpoint %s, skipping", mnt_ptr->dir);
+ continue;
+ }
+ }
+ else
+ {
+ if (strcmp (mnt_ptr->dir, "/") == 0)
+ {
+ sstrncpy (disk_name, "root", sizeof (disk_name));
+ }
+ else
+ {
+ int i, len;
+
+ sstrncpy (disk_name, mnt_ptr->dir + 1, sizeof (disk_name));
+ len = strlen (disk_name);
+
+ for (i = 0; i < len; i++)
+ if (disk_name[i] == '/')
+ disk_name[i] = '-';
+ }
+ }
+
+ df_submit (disk_name, df_used, df_free);
}
cu_mount_freelist (mnt_list);
/**
* collectd - src/dns.c
* Copyright (C) 2006,2007 Florian octo Forster
+ * Copyright (C) 2009 Mirko Buffoni
*
* 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:
* Florian octo Forster <octo at verplant.org>
+ * Mirko Buffoni <briareos at eswat.org>
**/
#define _BSD_SOURCE
{
"Interface",
"IgnoreSource",
- NULL
+ "SelectNumericQueryTypes"
};
-static int config_keys_num = 2;
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+static int select_numeric_qtype = 1;
#define PCAP_SNAPLEN 1460
static char *pcap_device = NULL;
if (value != NULL)
ignore_list_add_name (value);
}
+ else if (strcasecmp (key, "SelectNumericQueryTypes") == 0)
+ {
+ if ((value != NULL) && IS_FALSE (value))
+ select_numeric_qtype = 0;
+ else
+ select_numeric_qtype = 1;
+ }
else
{
return (-1);
if (dns->qr == 0)
{
/* This is a query */
+ int skip = 0;
+ if (!select_numeric_qtype)
+ {
+ const char *str = qtype_str(dns->qtype);
+ if ((str == NULL) || (str[0] == '#'))
+ skip = 1;
+ }
+
pthread_mutex_lock (&traffic_mutex);
tr_queries += dns->length;
pthread_mutex_unlock (&traffic_mutex);
- pthread_mutex_lock (&qtype_mutex);
- counter_list_add (&qtype_list, dns->qtype, 1);
- pthread_mutex_unlock (&qtype_mutex);
+ if (skip == 0)
+ {
+ pthread_mutex_lock (&qtype_mutex);
+ counter_list_add (&qtype_list, dns->qtype, 1);
+ pthread_mutex_unlock (&qtype_mutex);
+ }
}
else
{
se->vl.values[ds_index].counter += value.counter;
else if (ds_type == DS_TYPE_GAUGE)
se->vl.values[ds_index].gauge = value.gauge;
+ else if (ds_type == DS_TYPE_DERIVE)
+ se->vl.values[ds_index].DERIVE += value.derive;
+ else if (ds_type == DS_TYPE_ABSOLUTE)
+ se->vl.values[ds_index].ABSOLUTE = value.absolute;
+
se->flags |= (0x01 << ds_index);
/* Check if all values have been set and submit if so. */
value_t value_counter;
value_t value_gauge;
+ value_t value_derive;
/* Fill in `host', `name', `value_counter', and `value_gauge' according to
* the value type, or return with an error. */
name = msg_uint.metric_id.name;
value_counter.counter = (counter_t) msg_uint.ui;
value_gauge.gauge = (gauge_t) msg_uint.ui;
+ value_derive.derive = (derive_t) msg_uint.ui;
break;
}
if ((endptr == msg_string.str) || (errno != 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;
+
break;
}
name = msg_float.metric_id.name;
value_counter.counter = (counter_t) msg_float.f;
value_gauge.gauge = (gauge_t) msg_float.f;
+ value_derive.derive = (derive_t) msg_float.f;
break;
}
name = msg_double.metric_id.name;
value_counter.counter = (counter_t) msg_double.d;
value_gauge.gauge = (gauge_t) msg_double.d;
+ value_derive.derive = (derive_t) msg_double.d;
break;
}
default:
map = metric_lookup (name);
if (map != NULL)
+ {
+ value_t val_copy;
+
+ 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;
+
return (staging_entry_update (host, name,
map->type, map->type_instance,
map->ds_index, map->ds_type,
- (map->ds_type == DS_TYPE_COUNTER) ? value_counter : value_gauge));
+ val_copy));
+ }
DEBUG ("gmond plugin: Cannot find a translation for %s.", name);
return (-1);
return (ctoj_jlong_to_number (jvm_env, (jlong) value.counter));
else if (ds_type == DS_TYPE_GAUGE)
return (ctoj_jdouble_to_number (jvm_env, (jdouble) value.gauge));
+ if (ds_type == DS_TYPE_DERIVE)
+ return (ctoj_jlong_to_number (jvm_env, (jlong) value.derive));
+ if (ds_type == DS_TYPE_ABSOLUTE)
+ return (ctoj_jlong_to_number (jvm_env, (jlong) value.absolute));
else
return (NULL);
} /* }}} jobject ctoj_value_to_number */
class_ptr = (*jvm_env)->GetObjectClass (jvm_env, object_ptr);
- if (ds_type == DS_TYPE_COUNTER)
+ if (ds_type == DS_TYPE_GAUGE)
{
- jlong tmp_long;
+ jdouble tmp_double;
- status = jtoc_long (jvm_env, &tmp_long,
- class_ptr, object_ptr, "longValue");
+ status = jtoc_double (jvm_env, &tmp_double,
+ class_ptr, object_ptr, "doubleValue");
if (status != 0)
{
ERROR ("java plugin: jtoc_value: "
- "jtoc_long failed.");
+ "jtoc_double failed.");
return (-1);
}
- (*ret_value).counter = (counter_t) tmp_long;
+ (*ret_value).gauge = (gauge_t) tmp_double;
}
else
{
- jdouble tmp_double;
+ jlong tmp_long;
- status = jtoc_double (jvm_env, &tmp_double,
- class_ptr, object_ptr, "doubleValue");
+ status = jtoc_long (jvm_env, &tmp_long,
+ class_ptr, object_ptr, "longValue");
if (status != 0)
{
ERROR ("java plugin: jtoc_value: "
- "jtoc_double failed.");
+ "jtoc_long failed.");
return (-1);
}
- (*ret_value).gauge = (gauge_t) tmp_double;
+
+ if (ds_type == DS_TYPE_DERIVE)
+ (*ret_value).derive = (derive_t) tmp_long;
+ else if (ds_type == DS_TYPE_ABSOLUTE)
+ (*ret_value).absolute = (absolute_t) tmp_long;
+ else
+ (*ret_value).counter = (counter_t) tmp_long;
}
return (0);
else
SSTRCATF (command, ":%g", vl->values[i].gauge);
}
+ else if (vl->values_types[i] == LCC_TYPE_DERIVE)
+ SSTRCATF (command, ":%"PRIu64, vl->values[i].derive);
+ else if (vl->values_types[i] == LCC_TYPE_ABSOLUTE)
+ SSTRCATF (command, ":%"PRIu64, vl->values[i].absolute);
+
} /* for (i = 0; i < vl->values_len; i++) */
status = lcc_sendreceive (c, command, &res);
*/
#define LCC_TYPE_COUNTER 0
#define LCC_TYPE_GAUGE 1
+#define LCC_TYPE_DERIVE 2
+#define LCC_TYPE_ABSOLUTE 3
LCC_BEGIN_DECLS
typedef uint64_t counter_t;
typedef double gauge_t;
+typedef uint64_t derive_t;
+typedef uint64_t absolute_t;
union value_u
{
counter_t counter;
gauge_t gauge;
+ derive_t derive;
+ absolute_t absolute;
};
typedef union value_u value_t;
--- /dev/null
+/**
+ * collectd - src/madwifi.c
+ * Copyright (C) 2009 Ondrej 'SanTiago' Zajicek
+ *
+ * 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:
+ * Ondrej 'SanTiago' Zajicek <santiago@crfreenet.org>
+ *
+ * based on some code from interfaces.c (collectd) and Madwifi driver
+ **/
+
+
+/**
+ * There are several data streams provided by Madwifi plugin, some are
+ * connected to network interface, some are connected to each node
+ * associated to that interface. Nodes represents other sides in
+ * wireless communication, for example on network interface in AP mode,
+ * there is one node for each associated station. Node data streams
+ * contain MAC address of the node as the last part of the type_instance
+ * field.
+ *
+ * Inteface data streams:
+ * ath_nodes The number of associated nodes
+ * ath_stat Device statistic counters
+ *
+ * Node data streams:
+ * node_octets RX and TX data count (octets/bytes)
+ * node_rssi Received RSSI of the node
+ * node_tx_rate Reported TX rate to that node
+ * node_stat Node statistic counters
+ *
+ * Both statistic counters have type instances for each counter returned
+ * by Madwifi. See madwifi.h for content of ieee80211_nodestats,
+ * ieee80211_stats and ath_stats structures. Type instances use the same
+ * name as fields in these structures (like ns_rx_dup). Some fields are
+ * not reported, because they are not counters (like ns_tx_deauth_code
+ * or ast_tx_rssi). Fields ns_rx_bytes and ns_tx_bytes are reported as
+ * node_octets data stream instead of type instance of node_stat.
+ * Statistics are not logged when they are zero.
+ *
+ * There are two sets of these counters - the first 'WatchList' is a
+ * set of counters that are individually logged. The second 'MiscList'
+ * is a set of counters that are summed together and the sum is logged.
+ * By default, the most important statistics are in the WatchList and
+ * many error statistics are in MiscList. There are also many statistics
+ * that are not in any of these sets, so they are not monitored by default.
+ * It is possible to alter these lists using configuration options:
+ *
+ * WatchAdd X Adds X to WachList
+ * WatchRemove X Removes X from WachList
+ * WatchSet All Adds all statistics to WatchList
+ * WatchSet None Removes all statistics from WachList
+ *
+ * There are also Misc* variants fo these options, they modifies MiscList
+ * instead of WatchList.
+ *
+ * Example:
+ *
+ * WatchSet None
+ * WatchAdd node_octets
+ * WatchAdd node_rssi
+ * WatchAdd is_rx_acl
+ * WatchAdd is_scan_active
+ *
+ * That causes that just the four mentioned data streams are logged.
+ *
+ *
+ * By default, madwifi plugin enumerates network interfaces using /sys
+ * filesystem. Configuration option `Source' can change this to use
+ * /proc filesystem (which is useful for example when running on Linux
+ * 2.4). But without /sys filesystem, Madwifi plugin cannot check whether
+ * given interface is madwifi interface and there are private ioctls used,
+ * which may do something completely different on non-madwifi devices.
+ * Therefore, the /proc filesystem should always be used together with option
+ * `Interface', to limit found interfaces to madwifi interfaces only.
+ **/
+
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_ignorelist.h"
+
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#if !KERNEL_LINUX
+# error "No applicable input method."
+#endif
+
+#include <linux/wireless.h>
+#include "madwifi.h"
+
+
+
+struct stat_spec {
+ uint16_t flags;
+ uint16_t offset;
+ const char *name;
+};
+
+
+#define OFFSETOF(s, i) ((size_t)&((s *)0)->i)
+
+#define FLAG(i) (((uint32_t) 1) << ((i) % 32))
+
+#define SPC_STAT 0
+#define NOD_STAT 1
+#define IFA_STAT 2
+#define ATH_STAT 3
+#define SRC_MASK 3
+
+/* By default, the item is disabled */
+#define D 0
+
+/* By default, the item is logged */
+#define LOG 4
+
+/* By default, the item is summed with other such items and logged together */
+#define SU 8
+
+#define SS_STAT(flags, name) { flags | SPC_STAT, 0, #name }
+#define NS_STAT(flags, name) { flags | NOD_STAT, OFFSETOF(struct ieee80211_nodestats, name), #name }
+#define IS_STAT(flags, name) { flags | IFA_STAT, OFFSETOF(struct ieee80211_stats, name), #name }
+#define AS_STAT(flags, name) { flags | ATH_STAT, OFFSETOF(struct ath_stats, name), #name }
+
+
+/*
+ * (Module-)Global variables
+ */
+
+/* Indices of special stats in specs array */
+#define STAT_NODE_OCTETS 0
+#define STAT_NODE_RSSI 1
+#define STAT_NODE_TX_RATE 2
+#define STAT_ATH_NODES 3
+#define STAT_NS_RX_BEACONS 4
+#define STAT_AST_ANT_RX 5
+#define STAT_AST_ANT_TX 6
+
+static struct stat_spec specs[] = {
+
+/* Special statistics */
+SS_STAT(LOG, node_octets), /* rx and tx data count (bytes) */
+SS_STAT(LOG, node_rssi), /* received RSSI of the node */
+SS_STAT(LOG, node_tx_rate), /* used tx rate to the node */
+SS_STAT(LOG, ath_nodes), /* the number of associated nodes */
+SS_STAT(D, ns_rx_beacons), /* rx beacon frames */
+SS_STAT(LOG, ast_ant_rx), /* rx frames with antenna */
+SS_STAT(LOG, ast_ant_tx), /* tx frames with antenna */
+
+/* Node statistics */
+NS_STAT(LOG, ns_rx_data), /* rx data frames */
+NS_STAT(LOG, ns_rx_mgmt), /* rx management frames */
+NS_STAT(LOG, ns_rx_ctrl), /* rx control frames */
+NS_STAT(D, ns_rx_ucast), /* rx unicast frames */
+NS_STAT(D, ns_rx_mcast), /* rx multi/broadcast frames */
+NS_STAT(D, ns_rx_proberesp), /* rx probe response frames */
+NS_STAT(LOG, ns_rx_dup), /* rx discard because it's a dup */
+NS_STAT(SU, ns_rx_noprivacy), /* rx w/ wep but privacy off */
+NS_STAT(SU, ns_rx_wepfail), /* rx wep processing failed */
+NS_STAT(SU, ns_rx_demicfail), /* rx demic failed */
+NS_STAT(SU, ns_rx_decap), /* rx decapsulation failed */
+NS_STAT(SU, ns_rx_defrag), /* rx defragmentation failed */
+NS_STAT(D, ns_rx_disassoc), /* rx disassociation */
+NS_STAT(D, ns_rx_deauth), /* rx deauthentication */
+NS_STAT(SU, ns_rx_decryptcrc), /* rx decrypt failed on crc */
+NS_STAT(SU, ns_rx_unauth), /* rx on unauthorized port */
+NS_STAT(SU, ns_rx_unencrypted), /* rx unecrypted w/ privacy */
+NS_STAT(LOG, ns_tx_data), /* tx data frames */
+NS_STAT(LOG, ns_tx_mgmt), /* tx management frames */
+NS_STAT(D, ns_tx_ucast), /* tx unicast frames */
+NS_STAT(D, ns_tx_mcast), /* tx multi/broadcast frames */
+NS_STAT(D, ns_tx_probereq), /* tx probe request frames */
+NS_STAT(D, ns_tx_uapsd), /* tx on uapsd queue */
+NS_STAT(SU, ns_tx_novlantag), /* tx discard due to no tag */
+NS_STAT(SU, ns_tx_vlanmismatch), /* tx discard due to of bad tag */
+NS_STAT(D, ns_tx_eosplost), /* uapsd EOSP retried out */
+NS_STAT(D, ns_ps_discard), /* ps discard due to of age */
+NS_STAT(D, ns_uapsd_triggers), /* uapsd triggers */
+NS_STAT(LOG, ns_tx_assoc), /* [re]associations */
+NS_STAT(LOG, ns_tx_auth), /* [re]authentications */
+NS_STAT(D, ns_tx_deauth), /* deauthentications */
+NS_STAT(D, ns_tx_disassoc), /* disassociations */
+NS_STAT(D, ns_psq_drops), /* power save queue drops */
+
+/* Iface statistics */
+IS_STAT(SU, is_rx_badversion), /* rx frame with bad version */
+IS_STAT(SU, is_rx_tooshort), /* rx frame too short */
+IS_STAT(LOG, is_rx_wrongbss), /* rx from wrong bssid */
+IS_STAT(LOG, is_rx_dup), /* rx discard due to it's a dup */
+IS_STAT(SU, is_rx_wrongdir), /* rx w/ wrong direction */
+IS_STAT(D, is_rx_mcastecho), /* rx discard due to of mcast echo */
+IS_STAT(SU, is_rx_notassoc), /* rx discard due to sta !assoc */
+IS_STAT(SU, is_rx_noprivacy), /* rx w/ wep but privacy off */
+IS_STAT(SU, is_rx_unencrypted), /* rx w/o wep and privacy on */
+IS_STAT(SU, is_rx_wepfail), /* rx wep processing failed */
+IS_STAT(SU, is_rx_decap), /* rx decapsulation failed */
+IS_STAT(D, is_rx_mgtdiscard), /* rx discard mgt frames */
+IS_STAT(D, is_rx_ctl), /* rx discard ctrl frames */
+IS_STAT(D, is_rx_beacon), /* rx beacon frames */
+IS_STAT(D, is_rx_rstoobig), /* rx rate set truncated */
+IS_STAT(SU, is_rx_elem_missing), /* rx required element missing*/
+IS_STAT(SU, is_rx_elem_toobig), /* rx element too big */
+IS_STAT(SU, is_rx_elem_toosmall), /* rx element too small */
+IS_STAT(LOG, is_rx_elem_unknown), /* rx element unknown */
+IS_STAT(SU, is_rx_badchan), /* rx frame w/ invalid chan */
+IS_STAT(SU, is_rx_chanmismatch), /* rx frame chan mismatch */
+IS_STAT(SU, is_rx_nodealloc), /* rx frame dropped */
+IS_STAT(LOG, is_rx_ssidmismatch), /* rx frame ssid mismatch */
+IS_STAT(SU, is_rx_auth_unsupported), /* rx w/ unsupported auth alg */
+IS_STAT(SU, is_rx_auth_fail), /* rx sta auth failure */
+IS_STAT(SU, is_rx_auth_countermeasures),/* rx auth discard due to CM */
+IS_STAT(SU, is_rx_assoc_bss), /* rx assoc from wrong bssid */
+IS_STAT(SU, is_rx_assoc_notauth), /* rx assoc w/o auth */
+IS_STAT(SU, is_rx_assoc_capmismatch), /* rx assoc w/ cap mismatch */
+IS_STAT(SU, is_rx_assoc_norate), /* rx assoc w/ no rate match */
+IS_STAT(SU, is_rx_assoc_badwpaie), /* rx assoc w/ bad WPA IE */
+IS_STAT(LOG, is_rx_deauth), /* rx deauthentication */
+IS_STAT(LOG, is_rx_disassoc), /* rx disassociation */
+IS_STAT(SU, is_rx_badsubtype), /* rx frame w/ unknown subtype*/
+IS_STAT(SU, is_rx_nobuf), /* rx failed for lack of buf */
+IS_STAT(SU, is_rx_decryptcrc), /* rx decrypt failed on crc */
+IS_STAT(D, is_rx_ahdemo_mgt), /* rx discard ahdemo mgt frame*/
+IS_STAT(SU, is_rx_bad_auth), /* rx bad auth request */
+IS_STAT(SU, is_rx_unauth), /* rx on unauthorized port */
+IS_STAT(SU, is_rx_badkeyid), /* rx w/ incorrect keyid */
+IS_STAT(D, is_rx_ccmpreplay), /* rx seq# violation (CCMP), */
+IS_STAT(D, is_rx_ccmpformat), /* rx format bad (CCMP), */
+IS_STAT(D, is_rx_ccmpmic), /* rx MIC check failed (CCMP), */
+IS_STAT(D, is_rx_tkipreplay), /* rx seq# violation (TKIP), */
+IS_STAT(D, is_rx_tkipformat), /* rx format bad (TKIP), */
+IS_STAT(D, is_rx_tkipmic), /* rx MIC check failed (TKIP), */
+IS_STAT(D, is_rx_tkipicv), /* rx ICV check failed (TKIP), */
+IS_STAT(D, is_rx_badcipher), /* rx failed due to of key type */
+IS_STAT(D, is_rx_nocipherctx), /* rx failed due to key !setup */
+IS_STAT(D, is_rx_acl), /* rx discard due to of acl policy */
+IS_STAT(D, is_rx_ffcnt), /* rx fast frames */
+IS_STAT(SU, is_rx_badathtnl), /* driver key alloc failed */
+IS_STAT(SU, is_tx_nobuf), /* tx failed for lack of buf */
+IS_STAT(SU, is_tx_nonode), /* tx failed for no node */
+IS_STAT(SU, is_tx_unknownmgt), /* tx of unknown mgt frame */
+IS_STAT(SU, is_tx_badcipher), /* tx failed due to of key type */
+IS_STAT(SU, is_tx_nodefkey), /* tx failed due to no defkey */
+IS_STAT(SU, is_tx_noheadroom), /* tx failed due to no space */
+IS_STAT(D, is_tx_ffokcnt), /* tx fast frames sent success */
+IS_STAT(D, is_tx_fferrcnt), /* tx fast frames sent success */
+IS_STAT(D, is_scan_active), /* active scans started */
+IS_STAT(D, is_scan_passive), /* passive scans started */
+IS_STAT(D, is_node_timeout), /* nodes timed out inactivity */
+IS_STAT(D, is_crypto_nomem), /* no memory for crypto ctx */
+IS_STAT(D, is_crypto_tkip), /* tkip crypto done in s/w */
+IS_STAT(D, is_crypto_tkipenmic), /* tkip en-MIC done in s/w */
+IS_STAT(D, is_crypto_tkipdemic), /* tkip de-MIC done in s/w */
+IS_STAT(D, is_crypto_tkipcm), /* tkip counter measures */
+IS_STAT(D, is_crypto_ccmp), /* ccmp crypto done in s/w */
+IS_STAT(D, is_crypto_wep), /* wep crypto done in s/w */
+IS_STAT(D, is_crypto_setkey_cipher), /* cipher rejected key */
+IS_STAT(D, is_crypto_setkey_nokey), /* no key index for setkey */
+IS_STAT(D, is_crypto_delkey), /* driver key delete failed */
+IS_STAT(D, is_crypto_badcipher), /* unknown cipher */
+IS_STAT(D, is_crypto_nocipher), /* cipher not available */
+IS_STAT(D, is_crypto_attachfail), /* cipher attach failed */
+IS_STAT(D, is_crypto_swfallback), /* cipher fallback to s/w */
+IS_STAT(D, is_crypto_keyfail), /* driver key alloc failed */
+IS_STAT(D, is_crypto_enmicfail), /* en-MIC failed */
+IS_STAT(SU, is_ibss_capmismatch), /* merge failed-cap mismatch */
+IS_STAT(SU, is_ibss_norate), /* merge failed-rate mismatch */
+IS_STAT(D, is_ps_unassoc), /* ps-poll for unassoc. sta */
+IS_STAT(D, is_ps_badaid), /* ps-poll w/ incorrect aid */
+IS_STAT(D, is_ps_qempty), /* ps-poll w/ nothing to send */
+
+/* Atheros statistics */
+AS_STAT(D, ast_watchdog), /* device reset by watchdog */
+AS_STAT(D, ast_hardware), /* fatal hardware error interrupts */
+AS_STAT(D, ast_bmiss), /* beacon miss interrupts */
+AS_STAT(D, ast_rxorn), /* rx overrun interrupts */
+AS_STAT(D, ast_rxeol), /* rx eol interrupts */
+AS_STAT(D, ast_txurn), /* tx underrun interrupts */
+AS_STAT(D, ast_mib), /* mib interrupts */
+AS_STAT(D, ast_tx_packets), /* packet sent on the interface */
+AS_STAT(D, ast_tx_mgmt), /* management frames transmitted */
+AS_STAT(LOG, ast_tx_discard), /* frames discarded prior to assoc */
+AS_STAT(SU, ast_tx_invalid), /* frames discarded due to is device gone */
+AS_STAT(SU, ast_tx_qstop), /* tx queue stopped because it's full */
+AS_STAT(SU, ast_tx_encap), /* tx encapsulation failed */
+AS_STAT(SU, ast_tx_nonode), /* tx failed due to of no node */
+AS_STAT(SU, ast_tx_nobuf), /* tx failed due to of no tx buffer (data), */
+AS_STAT(SU, ast_tx_nobufmgt), /* tx failed due to of no tx buffer (mgmt),*/
+AS_STAT(LOG, ast_tx_xretries), /* tx failed due to of too many retries */
+AS_STAT(SU, ast_tx_fifoerr), /* tx failed due to of FIFO underrun */
+AS_STAT(SU, ast_tx_filtered), /* tx failed due to xmit filtered */
+AS_STAT(LOG, ast_tx_shortretry), /* tx on-chip retries (short), */
+AS_STAT(LOG, ast_tx_longretry), /* tx on-chip retries (long), */
+AS_STAT(SU, ast_tx_badrate), /* tx failed due to of bogus xmit rate */
+AS_STAT(D, ast_tx_noack), /* tx frames with no ack marked */
+AS_STAT(D, ast_tx_rts), /* tx frames with rts enabled */
+AS_STAT(D, ast_tx_cts), /* tx frames with cts enabled */
+AS_STAT(D, ast_tx_shortpre), /* tx frames with short preamble */
+AS_STAT(LOG, ast_tx_altrate), /* tx frames with alternate rate */
+AS_STAT(D, ast_tx_protect), /* tx frames with protection */
+AS_STAT(SU, ast_rx_orn), /* rx failed due to of desc overrun */
+AS_STAT(LOG, ast_rx_crcerr), /* rx failed due to of bad CRC */
+AS_STAT(SU, ast_rx_fifoerr), /* rx failed due to of FIFO overrun */
+AS_STAT(SU, ast_rx_badcrypt), /* rx failed due to of decryption */
+AS_STAT(SU, ast_rx_badmic), /* rx failed due to of MIC failure */
+AS_STAT(LOG, ast_rx_phyerr), /* rx PHY error summary count */
+AS_STAT(SU, ast_rx_tooshort), /* rx discarded due to frame too short */
+AS_STAT(SU, ast_rx_toobig), /* rx discarded due to frame too large */
+AS_STAT(SU, ast_rx_nobuf), /* rx setup failed due to of no skbuff */
+AS_STAT(D, ast_rx_packets), /* packet recv on the interface */
+AS_STAT(D, ast_rx_mgt), /* management frames received */
+AS_STAT(D, ast_rx_ctl), /* control frames received */
+AS_STAT(D, ast_be_xmit), /* beacons transmitted */
+AS_STAT(SU, ast_be_nobuf), /* no skbuff available for beacon */
+AS_STAT(D, ast_per_cal), /* periodic calibration calls */
+AS_STAT(D, ast_per_calfail), /* periodic calibration failed */
+AS_STAT(D, ast_per_rfgain), /* periodic calibration rfgain reset */
+AS_STAT(D, ast_rate_calls), /* rate control checks */
+AS_STAT(D, ast_rate_raise), /* rate control raised xmit rate */
+AS_STAT(D, ast_rate_drop), /* rate control dropped xmit rate */
+AS_STAT(D, ast_ant_defswitch), /* rx/default antenna switches */
+AS_STAT(D, ast_ant_txswitch) /* tx antenna switches */
+};
+
+/* Bounds between SS, NS, IS and AS stats in stats array */
+static int bounds[4];
+
+#define WL_LEN 6
+/* Bitmasks for logged and error items */
+static uint32_t watch_items[WL_LEN];
+static uint32_t misc_items[WL_LEN];
+
+
+static const char *config_keys[] =
+{
+ "Interface",
+ "IgnoreSelected",
+ "Source",
+ "WatchAdd",
+ "WatchRemove",
+ "WatchSet",
+ "MiscAdd",
+ "MiscRemove",
+ "MiscSet"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *ignorelist = NULL;
+
+static int use_sysfs = 1;
+static int init_state = 0;
+
+static inline int item_watched(int i)
+{
+ assert (i >= 0);
+ assert (i < ((STATIC_ARRAY_SIZE (watch_items) + 1) * 32));
+ return watch_items[i / 32] & FLAG (i);
+}
+
+static inline int item_summed(int i)
+{
+ assert (i >= 0);
+ assert (i < ((STATIC_ARRAY_SIZE (misc_items) + 1) * 32));
+ return misc_items[i / 32] & FLAG (i);
+}
+
+static inline void watchlist_add (uint32_t *wl, int item)
+{
+ assert (item >= 0);
+ assert (item < ((WL_LEN + 1) * 32));
+ wl[item / 32] |= FLAG (item);
+}
+
+static inline void watchlist_remove (uint32_t *wl, int item)
+{
+ assert (item >= 0);
+ assert (item < ((WL_LEN + 1) * 32));
+ wl[item / 32] &= ~FLAG (item);
+}
+
+static inline void watchlist_set (uint32_t *wl, uint32_t val)
+{
+ int i;
+ for (i = 0; i < WL_LEN; i++)
+ wl[i] = val;
+}
+
+/* This is horribly inefficient, but it is called only during configuration */
+static int watchitem_find (const char *name)
+{
+ int max = STATIC_ARRAY_SIZE (specs);
+ int i;
+
+ for (i = 0; i < max; i++)
+ if (strcasecmp (name, specs[i].name) == 0)
+ return i;
+
+ return -1;
+}
+
+
+/* Collectd hooks */
+
+/* We need init function called before madwifi_config */
+
+static int madwifi_real_init (void)
+{
+ int max = STATIC_ARRAY_SIZE (specs);
+ int i;
+
+ for (i = 0; i < STATIC_ARRAY_SIZE (bounds); i++)
+ bounds[i] = 0;
+
+ watchlist_set(watch_items, 0);
+ watchlist_set(misc_items, 0);
+
+ for (i = 0; i < max; i++)
+ {
+ bounds[specs[i].flags & SRC_MASK] = i;
+
+ if (specs[i].flags & LOG)
+ watch_items[i / 32] |= FLAG (i);
+
+ if (specs[i].flags & SU)
+ misc_items[i / 32] |= FLAG (i);
+ }
+
+ for (i = 0; i < STATIC_ARRAY_SIZE (bounds); i++)
+ bounds[i]++;
+
+ return (0);
+}
+
+static int madwifi_config (const char *key, const char *value)
+{
+ if (init_state != 1)
+ madwifi_real_init();
+ init_state = 1;
+
+ if (ignorelist == NULL)
+ ignorelist = ignorelist_create (/* invert = */ 1);
+
+ if (strcasecmp (key, "Interface") == 0)
+ ignorelist_add (ignorelist, value);
+
+ else if (strcasecmp (key, "IgnoreSelected") == 0)
+ ignorelist_set_invert (ignorelist, IS_TRUE (value) ? 0 : 1);
+
+ else if (strcasecmp (key, "Source") == 0)
+ {
+ if (strcasecmp (value, "ProcFS") == 0)
+ use_sysfs = 0;
+ else if (strcasecmp (value, "SysFS") == 0)
+ use_sysfs = 1;
+ else
+ {
+ ERROR ("madwifi plugin: The argument of the `Source' "
+ "option must either be `SysFS' or "
+ "`ProcFS'.");
+ return -1;
+ }
+ }
+
+ else if (strcasecmp (key, "WatchSet") == 0)
+ {
+ if (strcasecmp (value, "All") == 0)
+ watchlist_set (watch_items, 0xFFFFFFFF);
+ else if (strcasecmp (value, "None") == 0)
+ watchlist_set (watch_items, 0);
+ else return -1;
+ }
+
+ else if (strcasecmp (key, "WatchAdd") == 0)
+ {
+ int id = watchitem_find (value);
+
+ if (id < 0)
+ return (-1);
+ else
+ watchlist_add (watch_items, id);
+ }
+
+ else if (strcasecmp (key, "WatchRemove") == 0)
+ {
+ int id = watchitem_find (value);
+
+ if (id < 0)
+ return (-1);
+ else
+ watchlist_remove (watch_items, id);
+ }
+
+ else if (strcasecmp (key, "MiscSet") == 0)
+ {
+ if (strcasecmp (value, "All") == 0)
+ watchlist_set (misc_items, 0xFFFFFFFF);
+ else if (strcasecmp (value, "None") == 0)
+ watchlist_set (misc_items, 0);
+ else return -1;
+ }
+
+ else if (strcasecmp (key, "MiscAdd") == 0)
+ {
+ int id = watchitem_find (value);
+
+ if (id < 0)
+ return (-1);
+ else
+ watchlist_add (misc_items, id);
+ }
+
+ else if (strcasecmp (key, "MiscRemove") == 0)
+ {
+ int id = watchitem_find (value);
+
+ if (id < 0)
+ return (-1);
+ else
+ watchlist_remove (misc_items, id);
+ }
+
+ else
+ return (-1);
+
+ return (0);
+}
+
+
+static void submit (const char *dev, const char *type, const char *ti1,
+ const char *ti2, value_t *val, int len)
+{
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = val;
+ vl.values_len = len;
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "madwifi", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, dev, sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+
+ if ((ti1 != NULL) && (ti2 != NULL))
+ ssnprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s", ti1, ti2);
+ else if ((ti1 != NULL) && (ti2 == NULL))
+ sstrncpy (vl.type_instance, ti1, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+}
+
+static void submit_counter (const char *dev, const char *type, const char *ti1,
+ const char *ti2, counter_t val)
+{
+ value_t item;
+ item.counter = 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)
+{
+ value_t items[2];
+ items[0].counter = val1;
+ items[1].counter = val2;
+ submit (dev, type, ti1, ti2, items, 2);
+}
+
+static void submit_gauge (const char *dev, const char *type, const char *ti1,
+ const char *ti2, gauge_t val)
+{
+ value_t item;
+ item.gauge = val;
+ submit (dev, type, ti1, ti2, &item, 1);
+}
+
+static void submit_antx (const char *dev, const char *name,
+ u_int32_t *vals, int vals_num)
+{
+ char ti2[16];
+ int i;
+
+ for (i = 0; i < vals_num; i++)
+ {
+ if (vals[i] == 0)
+ continue;
+
+ ssnprintf (ti2, sizeof (ti2), "%i", i);
+ submit_counter (dev, "ath_stat", name, ti2,
+ (counter_t) vals[i]);
+ }
+}
+
+static inline void
+macaddr_to_str (char *buf, size_t bufsize, const uint8_t mac[IEEE80211_ADDR_LEN])
+{
+ ssnprintf (buf, bufsize, "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static void
+process_stat_struct (int which, const void *ptr, const char *dev, const char *mac,
+ const char *type_name, const char *misc_name)
+{
+ uint32_t misc = 0;
+ int i;
+
+ assert (which >= 1);
+ assert (which < STATIC_ARRAY_SIZE (bounds));
+
+ for (i = bounds[which - 1]; i < bounds[which]; 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);
+
+ if (item_summed (i))
+ misc += val;
+ }
+
+ if (misc != 0)
+ submit_counter (dev, type_name, misc_name, mac, misc);
+
+}
+
+static int
+process_athstats (int sk, const char *dev)
+{
+ struct ifreq ifr;
+ struct ath_stats stats;
+ int status;
+
+ sstrncpy (ifr.ifr_name, dev, sizeof (ifr.ifr_name));
+ ifr.ifr_data = (void *) &stats;
+ status = ioctl (sk, SIOCGATHSTATS, &ifr);
+ if (status < 0)
+ {
+ /* Silent, because not all interfaces support all ioctls. */
+ DEBUG ("madwifi plugin: Sending IO-control "
+ "SIOCGATHSTATS to device %s "
+ "failed with status %i.",
+ dev, status);
+ return (status);
+ }
+
+ /* These stats are handled as a special case, because they are
+ eight values each */
+
+ if (item_watched (STAT_AST_ANT_RX))
+ submit_antx (dev, "ast_ant_rx", stats.ast_ant_rx,
+ STATIC_ARRAY_SIZE (stats.ast_ant_rx));
+
+ if (item_watched (STAT_AST_ANT_TX))
+ submit_antx (dev, "ast_ant_tx", stats.ast_ant_tx,
+ STATIC_ARRAY_SIZE (stats.ast_ant_tx));
+
+ /* All other ath statistics */
+ process_stat_struct (ATH_STAT, &stats, dev, NULL, "ath_stat", "ast_misc");
+ return (0);
+}
+
+static int
+process_80211stats (int sk, const char *dev)
+{
+ struct ifreq ifr;
+ struct ieee80211_stats stats;
+ int status;
+
+ sstrncpy (ifr.ifr_name, dev, sizeof (ifr.ifr_name));
+ ifr.ifr_data = (void *) &stats;
+ status = ioctl(sk, SIOCG80211STATS, &ifr);
+ if (status < 0)
+ {
+ /* Silent, because not all interfaces support all ioctls. */
+ DEBUG ("madwifi plugin: Sending IO-control "
+ "SIOCG80211STATS to device %s "
+ "failed with status %i.",
+ dev, status);
+ return (status);
+ }
+
+ process_stat_struct (IFA_STAT, &stats, dev, NULL, "ath_stat", "is_misc");
+ return (0);
+}
+
+
+static int
+process_station (int sk, const char *dev, struct ieee80211req_sta_info *si)
+{
+ struct iwreq iwr;
+ static char mac[DATA_MAX_NAME_LEN];
+ struct ieee80211req_sta_stats stats;
+ const struct ieee80211_nodestats *ns = &stats.is_stats;
+ int status;
+
+ macaddr_to_str (mac, sizeof (mac), si->isi_macaddr);
+
+ if (item_watched (STAT_NODE_TX_RATE))
+ submit_gauge (dev, "node_tx_rate", mac, NULL,
+ (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL) / 2);
+
+ if (item_watched (STAT_NODE_RSSI))
+ submit_gauge (dev, "node_rssi", mac, NULL, si->isi_rssi);
+
+ memset (&iwr, 0, sizeof (iwr));
+ sstrncpy(iwr.ifr_name, dev, sizeof (iwr.ifr_name));
+ iwr.u.data.pointer = (void *) &stats;
+ iwr.u.data.length = sizeof (stats);
+ memcpy(stats.is_u.macaddr, si->isi_macaddr, IEEE80211_ADDR_LEN);
+ status = ioctl(sk, IEEE80211_IOCTL_STA_STATS, &iwr);
+ if (status < 0)
+ {
+ /* Silent, because not all interfaces support all ioctls. */
+ DEBUG ("madwifi plugin: Sending IO-control "
+ "IEEE80211_IOCTL_STA_STATS to device %s "
+ "failed with status %i.",
+ dev, status);
+ return (status);
+ }
+
+ /* 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,
+ 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,
+ (ns->ns_rx_beacons & 0xFFFFFFFF));
+
+ /* All other node statistics */
+ process_stat_struct (NOD_STAT, ns, dev, mac, "node_stat", "ns_misc");
+ return (0);
+}
+
+static int
+process_stations (int sk, const char *dev)
+{
+ uint8_t buf[24*1024];
+ struct iwreq iwr;
+ uint8_t *cp;
+ int len, nodes;
+ int status;
+
+ memset (&iwr, 0, sizeof (iwr));
+ sstrncpy (iwr.ifr_name, dev, sizeof (iwr.ifr_name));
+ iwr.u.data.pointer = (void *) buf;
+ iwr.u.data.length = sizeof (buf);
+
+ status = ioctl (sk, IEEE80211_IOCTL_STA_INFO, &iwr);
+ if (status < 0)
+ {
+ /* Silent, because not all interfaces support all ioctls. */
+ DEBUG ("madwifi plugin: Sending IO-control "
+ "IEEE80211_IOCTL_STA_INFO to device %s "
+ "failed with status %i.",
+ dev, status);
+ return (status);
+ }
+
+ len = iwr.u.data.length;
+
+ cp = buf;
+ nodes = 0;
+ while (len >= sizeof (struct ieee80211req_sta_info))
+ {
+ struct ieee80211req_sta_info *si = (void *) cp;
+ process_station(sk, dev, si);
+ cp += si->isi_len;
+ len -= si->isi_len;
+ nodes++;
+ }
+
+ if (item_watched (STAT_ATH_NODES))
+ submit_gauge (dev, "ath_nodes", NULL, NULL, nodes);
+ return (0);
+}
+
+static int
+process_device (int sk, const char *dev)
+{
+ int num_success = 0;
+ int status;
+
+ status = process_athstats (sk, dev);
+ if (status == 0)
+ num_success++;
+
+ status = process_80211stats (sk, dev);
+ if (status == 0)
+ num_success++;
+
+ status = process_stations (sk, dev);
+ if (status == 0)
+ num_success++;
+
+ return ((num_success == 0) ? -1 : 0);
+}
+
+static int
+check_devname (const char *dev)
+{
+ char buf[PATH_MAX];
+ char buf2[PATH_MAX];
+ int i;
+
+ if (dev[0] == '.')
+ return 0;
+
+ ssnprintf (buf, sizeof (buf), "/sys/class/net/%s/device/driver", dev);
+ buf[sizeof (buf) - 1] = 0;
+
+ memset (buf2, 0, sizeof (buf2));
+ i = readlink (buf, buf2, sizeof (buf2) - 1);
+ if (i < 0)
+ return 0;
+
+ if (strstr (buf2, "/drivers/ath_") == NULL)
+ return 0;
+ return 1;
+}
+
+static int
+sysfs_iterate(int sk)
+{
+ struct dirent *de;
+ DIR *nets;
+ int status;
+ int num_success;
+ int num_fail;
+
+ nets = opendir ("/sys/class/net/");
+ if (nets == NULL)
+ {
+ WARNING ("madwifi plugin: opening /sys/class/net failed");
+ return (-1);
+ }
+
+ num_success = 0;
+ num_fail = 0;
+ while ((de = readdir (nets)))
+ {
+ if (check_devname (de->d_name) == 0)
+ continue;
+
+ if (ignorelist_match (ignorelist, de->d_name) != 0)
+ continue;
+
+ status = process_device (sk, de->d_name);
+ if (status != 0)
+ {
+ ERROR ("madwifi plugin: Processing interface "
+ "%s failed.", de->d_name);
+ num_fail++;
+ }
+ else
+ {
+ num_success++;
+ }
+ } /* while (readdir) */
+
+ closedir(nets);
+
+ if ((num_success == 0) && (num_fail != 0))
+ return (-1);
+ return (0);
+}
+
+static int
+procfs_iterate(int sk)
+{
+ char buffer[1024];
+ char *device, *dummy;
+ FILE *fh;
+ int status;
+ int num_success;
+ int num_fail;
+
+ if ((fh = fopen ("/proc/net/dev", "r")) == NULL)
+ {
+ WARNING ("madwifi plugin: opening /proc/net/dev failed");
+ return (-1);
+ }
+
+ num_success = 0;
+ num_fail = 0;
+ while (fgets (buffer, sizeof (buffer), fh) != NULL)
+ {
+ dummy = strchr(buffer, ':');
+ if (dummy == NULL)
+ continue;
+ dummy[0] = 0;
+
+ device = buffer;
+ while (device[0] == ' ')
+ device++;
+
+ if (device[0] == 0)
+ continue;
+
+ if (ignorelist_match (ignorelist, device) != 0)
+ continue;
+
+ status = process_device (sk, device);
+ if (status != 0)
+ {
+ ERROR ("madwifi plugin: Processing interface "
+ "%s failed.", device);
+ num_fail++;
+ }
+ else
+ {
+ num_success++;
+ }
+ } /* while (fgets) */
+
+ fclose(fh);
+
+ if ((num_success == 0) && (num_fail != 0))
+ return (-1);
+ return 0;
+}
+
+static int madwifi_read (void)
+{
+ int rv;
+ int sk;
+
+ if (init_state == 0)
+ madwifi_real_init();
+ init_state = 2;
+
+ sk = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sk < 0)
+ return (-1);
+
+
+/* procfs iteration is not safe because it does not check whether given
+ interface is madwifi interface and there are private ioctls used, which
+ may do something completely different on non-madwifi devices.
+ Therefore, it is not used unless explicitly enabled (and should be used
+ together with ignorelist). */
+
+ if (use_sysfs)
+ rv = sysfs_iterate(sk);
+ else
+ rv = procfs_iterate(sk);
+
+ close(sk);
+
+ return rv;
+}
+
+void module_register (void)
+{
+ plugin_register_config ("madwifi", madwifi_config,
+ config_keys, config_keys_num);
+
+ plugin_register_read ("madwifi", madwifi_read);
+}
--- /dev/null
+/**
+ * This file is compiled from several Madwifi header files.
+ * Original copyright is:
+ *
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef MADWIFI_H
+#define MADWIFI_H
+
+#define IEEE80211_ADDR_LEN 6 /* size of 802.11 address */
+#define IEEE80211_RATE_VAL 0x7f
+#define IEEE80211_RATE_SIZE 8 /* 802.11 standard */
+#define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */
+
+
+/*
+ * Per/node (station) statistics available when operating as an AP.
+ */
+struct ieee80211_nodestats {
+ u_int32_t ns_rx_data; /* rx data frames */
+ u_int32_t ns_rx_mgmt; /* rx management frames */
+ u_int32_t ns_rx_ctrl; /* rx control frames */
+ u_int32_t ns_rx_ucast; /* rx unicast frames */
+ u_int32_t ns_rx_mcast; /* rx multi/broadcast frames */
+ u_int64_t ns_rx_bytes; /* rx data count (bytes) */
+ u_int64_t ns_rx_beacons; /* rx beacon frames */
+ u_int32_t ns_rx_proberesp; /* rx probe response frames */
+
+ u_int32_t ns_rx_dup; /* rx discard because it's a dup */
+ u_int32_t ns_rx_noprivacy; /* rx w/ wep but privacy off */
+ u_int32_t ns_rx_wepfail; /* rx wep processing failed */
+ u_int32_t ns_rx_demicfail; /* rx demic failed */
+ u_int32_t ns_rx_decap; /* rx decapsulation failed */
+ u_int32_t ns_rx_defrag; /* rx defragmentation failed */
+ u_int32_t ns_rx_disassoc; /* rx disassociation */
+ u_int32_t ns_rx_deauth; /* rx deauthentication */
+ u_int32_t ns_rx_decryptcrc; /* rx decrypt failed on crc */
+ u_int32_t ns_rx_unauth; /* rx on unauthorized port */
+ u_int32_t ns_rx_unencrypted; /* rx unecrypted w/ privacy */
+
+ u_int32_t ns_tx_data; /* tx data frames */
+ u_int32_t ns_tx_mgmt; /* tx management frames */
+ u_int32_t ns_tx_ucast; /* tx unicast frames */
+ u_int32_t ns_tx_mcast; /* tx multi/broadcast frames */
+ u_int64_t ns_tx_bytes; /* tx data count (bytes) */
+ u_int32_t ns_tx_probereq; /* tx probe request frames */
+ u_int32_t ns_tx_uapsd; /* tx on uapsd queue */
+
+ u_int32_t ns_tx_novlantag; /* tx discard due to no tag */
+ u_int32_t ns_tx_vlanmismatch; /* tx discard due to of bad tag */
+
+ u_int32_t ns_tx_eosplost; /* uapsd EOSP retried out */
+
+ u_int32_t ns_ps_discard; /* ps discard due to of age */
+
+ u_int32_t ns_uapsd_triggers; /* uapsd triggers */
+
+ /* MIB-related state */
+ u_int32_t ns_tx_assoc; /* [re]associations */
+ u_int32_t ns_tx_assoc_fail; /* [re]association failures */
+ u_int32_t ns_tx_auth; /* [re]authentications */
+ u_int32_t ns_tx_auth_fail; /* [re]authentication failures*/
+ u_int32_t ns_tx_deauth; /* deauthentications */
+ u_int32_t ns_tx_deauth_code; /* last deauth reason */
+ u_int32_t ns_tx_disassoc; /* disassociations */
+ u_int32_t ns_tx_disassoc_code; /* last disassociation reason */
+ u_int32_t ns_psq_drops; /* power save queue drops */
+};
+
+/*
+ * Summary statistics.
+ */
+struct ieee80211_stats {
+ u_int32_t is_rx_badversion; /* rx frame with bad version */
+ u_int32_t is_rx_tooshort; /* rx frame too short */
+ u_int32_t is_rx_wrongbss; /* rx from wrong bssid */
+ u_int32_t is_rx_dup; /* rx discard due to it's a dup */
+ u_int32_t is_rx_wrongdir; /* rx w/ wrong direction */
+ u_int32_t is_rx_mcastecho; /* rx discard due to of mcast echo */
+ u_int32_t is_rx_notassoc; /* rx discard due to sta !assoc */
+ u_int32_t is_rx_noprivacy; /* rx w/ wep but privacy off */
+ u_int32_t is_rx_unencrypted; /* rx w/o wep and privacy on */
+ u_int32_t is_rx_wepfail; /* rx wep processing failed */
+ u_int32_t is_rx_decap; /* rx decapsulation failed */
+ u_int32_t is_rx_mgtdiscard; /* rx discard mgt frames */
+ u_int32_t is_rx_ctl; /* rx discard ctrl frames */
+ u_int32_t is_rx_beacon; /* rx beacon frames */
+ u_int32_t is_rx_rstoobig; /* rx rate set truncated */
+ u_int32_t is_rx_elem_missing; /* rx required element missing*/
+ u_int32_t is_rx_elem_toobig; /* rx element too big */
+ u_int32_t is_rx_elem_toosmall; /* rx element too small */
+ u_int32_t is_rx_elem_unknown; /* rx element unknown */
+ u_int32_t is_rx_badchan; /* rx frame w/ invalid chan */
+ u_int32_t is_rx_chanmismatch; /* rx frame chan mismatch */
+ u_int32_t is_rx_nodealloc; /* rx frame dropped */
+ u_int32_t is_rx_ssidmismatch; /* rx frame ssid mismatch */
+ u_int32_t is_rx_auth_unsupported;/* rx w/ unsupported auth alg */
+ u_int32_t is_rx_auth_fail; /* rx sta auth failure */
+ u_int32_t is_rx_auth_countermeasures;/* rx auth discard due to CM */
+ u_int32_t is_rx_assoc_bss; /* rx assoc from wrong bssid */
+ u_int32_t is_rx_assoc_notauth; /* rx assoc w/o auth */
+ u_int32_t is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
+ u_int32_t is_rx_assoc_norate; /* rx assoc w/ no rate match */
+ u_int32_t is_rx_assoc_badwpaie; /* rx assoc w/ bad WPA IE */
+ u_int32_t is_rx_deauth; /* rx deauthentication */
+ u_int32_t is_rx_disassoc; /* rx disassociation */
+ u_int32_t is_rx_badsubtype; /* rx frame w/ unknown subtype*/
+ u_int32_t is_rx_nobuf; /* rx failed for lack of buf */
+ u_int32_t is_rx_decryptcrc; /* rx decrypt failed on crc */
+ u_int32_t is_rx_ahdemo_mgt; /* rx discard ahdemo mgt frame*/
+ u_int32_t is_rx_bad_auth; /* rx bad auth request */
+ u_int32_t is_rx_unauth; /* rx on unauthorized port */
+ u_int32_t is_rx_badkeyid; /* rx w/ incorrect keyid */
+ u_int32_t is_rx_ccmpreplay; /* rx seq# violation (CCMP) */
+ u_int32_t is_rx_ccmpformat; /* rx format bad (CCMP) */
+ u_int32_t is_rx_ccmpmic; /* rx MIC check failed (CCMP) */
+ u_int32_t is_rx_tkipreplay; /* rx seq# violation (TKIP) */
+ u_int32_t is_rx_tkipformat; /* rx format bad (TKIP) */
+ u_int32_t is_rx_tkipmic; /* rx MIC check failed (TKIP) */
+ u_int32_t is_rx_tkipicv; /* rx ICV check failed (TKIP) */
+ u_int32_t is_rx_badcipher; /* rx failed due to of key type */
+ u_int32_t is_rx_nocipherctx; /* rx failed due to key !setup */
+ u_int32_t is_rx_acl; /* rx discard due to of acl policy */
+ u_int32_t is_rx_ffcnt; /* rx fast frames */
+ u_int32_t is_rx_badathtnl; /* driver key alloc failed */
+ u_int32_t is_tx_nobuf; /* tx failed for lack of buf */
+ u_int32_t is_tx_nonode; /* tx failed for no node */
+ u_int32_t is_tx_unknownmgt; /* tx of unknown mgt frame */
+ u_int32_t is_tx_badcipher; /* tx failed due to of key type */
+ u_int32_t is_tx_nodefkey; /* tx failed due to no defkey */
+ u_int32_t is_tx_noheadroom; /* tx failed due to no space */
+ u_int32_t is_tx_ffokcnt; /* tx fast frames sent success */
+ u_int32_t is_tx_fferrcnt; /* tx fast frames sent success */
+ u_int32_t is_scan_active; /* active scans started */
+ u_int32_t is_scan_passive; /* passive scans started */
+ u_int32_t is_node_timeout; /* nodes timed out inactivity */
+ u_int32_t is_crypto_nomem; /* no memory for crypto ctx */
+ u_int32_t is_crypto_tkip; /* tkip crypto done in s/w */
+ u_int32_t is_crypto_tkipenmic; /* tkip en-MIC done in s/w */
+ u_int32_t is_crypto_tkipdemic; /* tkip de-MIC done in s/w */
+ u_int32_t is_crypto_tkipcm; /* tkip counter measures */
+ u_int32_t is_crypto_ccmp; /* ccmp crypto done in s/w */
+ u_int32_t is_crypto_wep; /* wep crypto done in s/w */
+ u_int32_t is_crypto_setkey_cipher;/* cipher rejected key */
+ u_int32_t is_crypto_setkey_nokey;/* no key index for setkey */
+ u_int32_t is_crypto_delkey; /* driver key delete failed */
+ u_int32_t is_crypto_badcipher; /* unknown cipher */
+ u_int32_t is_crypto_nocipher; /* cipher not available */
+ u_int32_t is_crypto_attachfail; /* cipher attach failed */
+ u_int32_t is_crypto_swfallback; /* cipher fallback to s/w */
+ u_int32_t is_crypto_keyfail; /* driver key alloc failed */
+ u_int32_t is_crypto_enmicfail; /* en-MIC failed */
+ u_int32_t is_ibss_capmismatch; /* merge failed-cap mismatch */
+ u_int32_t is_ibss_norate; /* merge failed-rate mismatch */
+ u_int32_t is_ps_unassoc; /* ps-poll for unassoc. sta */
+ u_int32_t is_ps_badaid; /* ps-poll w/ incorrect aid */
+ u_int32_t is_ps_qempty; /* ps-poll w/ nothing to send */
+};
+
+/*
+ * Retrieve per-node statistics.
+ */
+struct ieee80211req_sta_stats {
+ union {
+ /* NB: explicitly force 64-bit alignment */
+ u_int8_t macaddr[IEEE80211_ADDR_LEN];
+ u_int64_t pad;
+ } is_u;
+ struct ieee80211_nodestats is_stats;
+};
+
+/*
+ * Station information block; the mac address is used
+ * to retrieve other data like stats, unicast key, etc.
+ */
+struct ieee80211req_sta_info {
+ u_int16_t isi_len; /* length (mult of 4) */
+ u_int16_t isi_freq; /* MHz */
+ u_int16_t isi_flags; /* channel flags */
+ u_int16_t isi_state; /* state flags */
+ u_int8_t isi_authmode; /* authentication algorithm */
+ u_int8_t isi_rssi;
+ u_int16_t isi_capinfo; /* capabilities */
+ u_int8_t isi_athflags; /* Atheros capabilities */
+ u_int8_t isi_erp; /* ERP element */
+ u_int8_t isi_macaddr[IEEE80211_ADDR_LEN];
+ u_int8_t isi_nrates; /* negotiated rates */
+ u_int8_t isi_rates[IEEE80211_RATE_MAXSIZE];
+ u_int8_t isi_txrate; /* index to isi_rates[] */
+ u_int16_t isi_ie_len; /* IE length */
+ u_int16_t isi_associd; /* assoc response */
+ u_int16_t isi_txpower; /* current tx power */
+ u_int16_t isi_vlan; /* vlan tag */
+ u_int16_t isi_txseqs[17]; /* seq to be transmitted */
+ u_int16_t isi_rxseqs[17]; /* seq previous for qos frames*/
+ u_int16_t isi_inact; /* inactivity timer */
+ u_int8_t isi_uapsd; /* UAPSD queues */
+ u_int8_t isi_opmode; /* sta operating mode */
+
+ /* XXX frag state? */
+ /* variable length IE data */
+};
+
+
+struct ath_stats {
+ u_int32_t ast_watchdog; /* device reset by watchdog */
+ u_int32_t ast_hardware; /* fatal hardware error interrupts */
+ u_int32_t ast_bmiss; /* beacon miss interrupts */
+ u_int32_t ast_rxorn; /* rx overrun interrupts */
+ u_int32_t ast_rxeol; /* rx eol interrupts */
+ u_int32_t ast_txurn; /* tx underrun interrupts */
+ u_int32_t ast_mib; /* mib interrupts */
+ u_int32_t ast_tx_packets; /* packet sent on the interface */
+ u_int32_t ast_tx_mgmt; /* management frames transmitted */
+ u_int32_t ast_tx_discard; /* frames discarded prior to assoc */
+ u_int32_t ast_tx_invalid; /* frames discarded due to is device gone */
+ u_int32_t ast_tx_qstop; /* tx queue stopped because it's full */
+ u_int32_t ast_tx_encap; /* tx encapsulation failed */
+ u_int32_t ast_tx_nonode; /* tx failed due to of no node */
+ u_int32_t ast_tx_nobuf; /* tx failed due to of no tx buffer (data) */
+ u_int32_t ast_tx_nobufmgt; /* tx failed due to of no tx buffer (mgmt)*/
+ u_int32_t ast_tx_xretries; /* tx failed due to of too many retries */
+ u_int32_t ast_tx_fifoerr; /* tx failed due to of FIFO underrun */
+ u_int32_t ast_tx_filtered; /* tx failed due to xmit filtered */
+ u_int32_t ast_tx_shortretry; /* tx on-chip retries (short) */
+ u_int32_t ast_tx_longretry; /* tx on-chip retries (long) */
+ u_int32_t ast_tx_badrate; /* tx failed due to of bogus xmit rate */
+ u_int32_t ast_tx_noack; /* tx frames with no ack marked */
+ u_int32_t ast_tx_rts; /* tx frames with rts enabled */
+ u_int32_t ast_tx_cts; /* tx frames with cts enabled */
+ u_int32_t ast_tx_shortpre; /* tx frames with short preamble */
+ u_int32_t ast_tx_altrate; /* tx frames with alternate rate */
+ u_int32_t ast_tx_protect; /* tx frames with protection */
+ u_int32_t ast_rx_orn; /* rx failed due to of desc overrun */
+ u_int32_t ast_rx_crcerr; /* rx failed due to of bad CRC */
+ u_int32_t ast_rx_fifoerr; /* rx failed due to of FIFO overrun */
+ u_int32_t ast_rx_badcrypt; /* rx failed due to of decryption */
+ u_int32_t ast_rx_badmic; /* rx failed due to of MIC failure */
+ u_int32_t ast_rx_phyerr; /* rx PHY error summary count */
+ u_int32_t ast_rx_phy[32]; /* rx PHY error per-code counts */
+ u_int32_t ast_rx_tooshort; /* rx discarded due to frame too short */
+ u_int32_t ast_rx_toobig; /* rx discarded due to frame too large */
+ u_int32_t ast_rx_nobuf; /* rx setup failed due to of no skbuff */
+ u_int32_t ast_rx_packets; /* packet recv on the interface */
+ u_int32_t ast_rx_mgt; /* management frames received */
+ u_int32_t ast_rx_ctl; /* control frames received */
+ int8_t ast_tx_rssi; /* tx rssi of last ack */
+ int8_t ast_rx_rssi; /* rx rssi from histogram */
+ u_int32_t ast_be_xmit; /* beacons transmitted */
+ u_int32_t ast_be_nobuf; /* no skbuff available for beacon */
+ u_int32_t ast_per_cal; /* periodic calibration calls */
+ u_int32_t ast_per_calfail; /* periodic calibration failed */
+ u_int32_t ast_per_rfgain; /* periodic calibration rfgain reset */
+ u_int32_t ast_rate_calls; /* rate control checks */
+ u_int32_t ast_rate_raise; /* rate control raised xmit rate */
+ u_int32_t ast_rate_drop; /* rate control dropped xmit rate */
+ u_int32_t ast_ant_defswitch; /* rx/default antenna switches */
+ u_int32_t ast_ant_txswitch; /* tx antenna switches */
+ u_int32_t ast_ant_rx[8]; /* rx frames with antenna */
+ u_int32_t ast_ant_tx[8]; /* tx frames with antenna */
+};
+
+#define SIOCGATHSTATS (SIOCDEVPRIVATE+0)
+#define SIOCGATHDIAG (SIOCDEVPRIVATE+1)
+#define SIOCGATHRADARSIG (SIOCDEVPRIVATE+2)
+#define SIOCGATHHALDIAG (SIOCDEVPRIVATE+3)
+#define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+/* NB: require in+out parameters so cannot use wireless extensions, yech */
+#define IEEE80211_IOCTL_GETKEY (SIOCDEVPRIVATE+3)
+#define IEEE80211_IOCTL_GETWPAIE (SIOCDEVPRIVATE+4)
+#define IEEE80211_IOCTL_STA_STATS (SIOCDEVPRIVATE+5)
+#define IEEE80211_IOCTL_STA_INFO (SIOCDEVPRIVATE+6)
+#define SIOC80211IFCREATE (SIOCDEVPRIVATE+7)
+#define SIOC80211IFDESTROY (SIOCDEVPRIVATE+8)
+#define IEEE80211_IOCTL_SCAN_RESULTS (SIOCDEVPRIVATE+9)
+
+
+#endif
--- /dev/null
+/**
+ * collectd - src/match_empty_counter.c
+ * Copyright (C) 2009 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:
+ * Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "utils_cache.h"
+#include "filter_chain.h"
+
+/*
+ * private data types
+ */
+struct mec_match_s;
+typedef struct mec_match_s mec_match_t;
+struct mec_match_s
+{
+ int dummy;
+};
+
+/*
+ * internal helper functions
+ */
+static int mec_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+ mec_match_t *m;
+
+ m = (mec_match_t *) malloc (sizeof (*m));
+ if (m == NULL)
+ {
+ ERROR ("mec_create: malloc failed.");
+ return (-ENOMEM);
+ }
+ memset (m, 0, sizeof (*m));
+
+ if (ci->children_num != 0)
+ {
+ ERROR ("empty_counter match: This match does not take any additional "
+ "configuration.");
+ }
+
+ *user_data = m;
+ return (0);
+} /* }}} int mec_create */
+
+static int mec_destroy (void **user_data) /* {{{ */
+{
+ if (user_data != NULL)
+ {
+ sfree (*user_data);
+ }
+
+ return (0);
+} /* }}} int mec_destroy */
+
+static int mec_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
+ const value_list_t *vl,
+ notification_meta_t __attribute__((unused)) **meta, void **user_data)
+{
+ mec_match_t *m;
+ int num_counters;
+ int num_empty;
+ int i;
+
+ if ((user_data == NULL) || (*user_data == NULL))
+ return (-1);
+
+ m = *user_data;
+
+ num_counters = 0;
+ num_empty = 0;
+
+ for (i = 0; i < ds->ds_num; i++)
+ {
+ if (ds->ds[i].type != DS_TYPE_COUNTER)
+ continue;
+
+ num_counters++;
+ if (vl->values[i].counter == 0)
+ num_empty++;
+ }
+
+ if (num_counters == 0)
+ return (FC_MATCH_NO_MATCH);
+ else if (num_counters == num_empty)
+ return (FC_MATCH_MATCHES);
+ else
+ return (FC_MATCH_NO_MATCH);
+} /* }}} int mec_match */
+
+void module_register (void)
+{
+ match_proc_t mproc;
+
+ memset (&mproc, 0, sizeof (mproc));
+ mproc.create = mec_create;
+ mproc.destroy = mec_destroy;
+ mproc.match = mec_match;
+ fc_register_match ("empty_counter", mproc);
+} /* module_register */
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
/**
* collectd - src/meta_data.c
- * Copyright (C) 2008 Florian octo Forster
+ * Copyright (C) 2008,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
#define MD_TYPE_SIGNED_INT 2
#define MD_TYPE_UNSIGNED_INT 3
#define MD_TYPE_DOUBLE 4
+#define MD_TYPE_BOOLEAN 5
/*
* Data types
int64_t mv_signed_int;
uint64_t mv_unsigned_int;
double mv_double;
+ _Bool mv_boolean;
};
typedef union meta_value_u meta_value_t;
return (0);
} /* }}} int meta_data_delete */
+/*
+ * Add functions
+ */
int meta_data_add_string (meta_data_t *md, /* {{{ */
const char *key, const char *value)
{
return (md_entry_insert (md, e));
} /* }}} int meta_data_add_double */
+int meta_data_add_boolean (meta_data_t *md, /* {{{ */
+ const char *key, _Bool value)
+{
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL))
+ return (-EINVAL);
+
+ e = md_entry_alloc (key);
+ if (e == NULL)
+ return (-ENOMEM);
+
+ e->value.mv_boolean = value;
+ e->type = MD_TYPE_BOOLEAN;
+
+ return (md_entry_insert (md, e));
+} /* }}} int meta_data_add_boolean */
+
+/*
+ * Get functions
+ */
int meta_data_get_string (meta_data_t *md, /* {{{ */
const char *key, char **value)
{
return (0);
} /* }}} int meta_data_get_double */
+int meta_data_get_boolean (meta_data_t *md, /* {{{ */
+ const char *key, _Bool *value)
+{
+ meta_entry_t *e;
+
+ if ((md == NULL) || (key == NULL) || (value == NULL))
+ return (-EINVAL);
+
+ pthread_mutex_lock (&md->lock);
+
+ e = md_entry_lookup (md, key);
+ if (e == NULL)
+ {
+ pthread_mutex_unlock (&md->lock);
+ return (-ENOENT);
+ }
+
+ if (e->type != MD_TYPE_BOOLEAN)
+ {
+ ERROR ("meta_data_get_boolean: Type mismatch for key `%s'", e->key);
+ pthread_mutex_unlock (&md->lock);
+ return (-ENOENT);
+ }
+
+ *value = e->value.mv_boolean;
+
+ pthread_mutex_unlock (&md->lock);
+ return (0);
+} /* }}} int meta_data_get_boolean */
+
/* vim: set sw=2 sts=2 et fdm=marker : */
/**
* collectd - src/meta_data.h
- * Copyright (C) 2008 Florian octo Forster
+ * Copyright (C) 2008,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
int meta_data_add_double (meta_data_t *md,
const char *key,
double value);
+int meta_data_add_boolean (meta_data_t *md,
+ const char *key,
+ _Bool value);
int meta_data_get_string (meta_data_t *md,
const char *key,
int meta_data_get_double (meta_data_t *md,
const char *key,
double *value);
+int meta_data_get_boolean (meta_data_t *md,
+ const char *key,
+ _Bool *value);
#endif /* META_DATA_H */
/* vim: set sw=2 sts=2 et : */
/**
* collectd - src/network.c
* Copyright (C) 2005-2009 Florian octo Forster
+ * Copyright (C) 2009 Aman 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
*
* Authors:
* Florian octo Forster <octo at verplant.org>
+ * Aman Gupta <aman at tmm1.net>
**/
#define _BSD_SOURCE /* For struct ip_mreq */
#include "configfile.h"
#include "utils_fbhash.h"
#include "utils_avltree.h"
+#include "utils_cache.h"
#include "network.h"
GCRY_THREAD_OPTION_PTHREAD_IMPL;
#endif
-/* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */
-/* #define BUFF_SIZE 1452 */
-
#ifndef IPV6_ADD_MEMBERSHIP
# ifdef IPV6_JOIN_GROUP
# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
# endif
#endif /* !IP_ADD_MEMBERSHIP */
-/* Buffer size to allocate. */
-#define BUFF_SIZE 1024
-
/*
* Maximum size required for encryption / signing:
*
struct receive_list_entry_s
{
- char data[BUFF_SIZE];
+ char *data;
int data_len;
int fd;
struct receive_list_entry_s *next;
* Private variables
*/
static int network_config_ttl = 0;
+static size_t network_config_packet_size = 1024;
static int network_config_forward = 0;
static sockent_t *sending_sockets = NULL;
static pthread_t dispatch_thread_id;
/* Buffer in which to-be-sent network packets are constructed. */
-static char send_buffer[BUFF_SIZE];
+static char *send_buffer;
static char *send_buffer_ptr;
static int send_buffer_fill;
static value_list_t send_buffer_vl = VALUE_LIST_STATIC;
static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
-/* In this cache we store all the values we received, so we can send out only
- * those values which were *not* received via the network plugin, too. This is
- * used for the `Forward false' option. */
-static c_avl_tree_t *cache_tree = NULL;
-static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
-static time_t cache_flush_last = 0;
-static int cache_flush_interval = 1800;
-
/*
* Private functions
*/
-static int cache_flush (void)
+static _Bool check_receive_okay (const value_list_t *vl) /* {{{ */
{
- char **keys = NULL;
- int keys_num = 0;
+ uint64_t time_sent = 0;
+ int status;
- char **tmp;
- int i;
+ status = uc_meta_data_get_unsigned_int (vl,
+ "network:time_sent", &time_sent);
- char *key;
- time_t *value;
- c_avl_iterator_t *iter;
+ /* 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);
- time_t curtime = time (NULL);
+ return (true);
+} /* }}} _Bool check_receive_okay */
- iter = c_avl_get_iterator (cache_tree);
- while (c_avl_iterator_next (iter, (void *) &key, (void *) &value) == 0)
- {
- if ((curtime - *value) <= cache_flush_interval)
- continue;
- tmp = (char **) realloc (keys,
- (keys_num + 1) * sizeof (char *));
- if (tmp == NULL)
- {
- sfree (keys);
- c_avl_iterator_destroy (iter);
- ERROR ("network plugin: cache_flush: realloc"
- " failed.");
- return (-1);
- }
- keys = tmp;
- keys[keys_num] = key;
- keys_num++;
- } /* while (c_avl_iterator_next) */
- c_avl_iterator_destroy (iter);
+static _Bool check_send_okay (const value_list_t *vl) /* {{{ */
+{
+ _Bool received = false;
+ int status;
- for (i = 0; i < keys_num; i++)
- {
- if (c_avl_remove (cache_tree, keys[i], (void *) &key,
- (void *) &value) != 0)
- {
- WARNING ("network plugin: cache_flush: c_avl_remove"
- " (%s) failed.", keys[i]);
- continue;
- }
+ if (network_config_forward != 0)
+ return (true);
- sfree (key);
- sfree (value);
- }
+ if (vl->meta == NULL)
+ return (true);
- sfree (keys);
+ status = meta_data_get_boolean (vl->meta, "network:received", &received);
+ if (status == -ENOENT)
+ return (true);
+ else if (status != 0)
+ {
+ ERROR ("network plugin: check_send_okay: meta_data_get_boolean failed "
+ "with status %i.", status);
+ return (true);
+ }
- DEBUG ("network plugin: cache_flush: Removed %i %s",
- keys_num, (keys_num == 1) ? "entry" : "entries");
- cache_flush_last = curtime;
- return (0);
-} /* int cache_flush */
+ /* By default, only *send* value lists that were not *received* by the
+ * network plugin. */
+ return (!received);
+} /* }}} _Bool check_send_okay */
-static int cache_check (const value_list_t *vl)
+static int network_dispatch_values (value_list_t *vl) /* {{{ */
{
- char key[1024];
- time_t *value = NULL;
- int retval = -1;
+ int status;
- if (cache_tree == NULL)
- return (-1);
+ if ((vl->time <= 0)
+ || (strlen (vl->host) <= 0)
+ || (strlen (vl->plugin) <= 0)
+ || (strlen (vl->type) <= 0))
+ return (-EINVAL);
- if (format_name (key, sizeof (key), vl->host, vl->plugin,
- vl->plugin_instance, vl->type, vl->type_instance))
- return (-1);
+ if (!check_receive_okay (vl))
+ {
+#if COLLECT_DEBUG
+ char name[6*DATA_MAX_NAME_LEN];
+ FORMAT_VL (name, sizeof (name), vl);
+ name[sizeof (name) - 1] = 0;
+ DEBUG ("network plugin: network_dispatch_values: "
+ "NOT dispatching %s.", name);
+#endif
+ return (0);
+ }
- pthread_mutex_lock (&cache_lock);
+ assert (vl->meta == NULL);
- if (c_avl_get (cache_tree, key, (void *) &value) == 0)
- {
- if (*value < vl->time)
- {
- *value = vl->time;
- retval = 0;
- }
- else
- {
- DEBUG ("network plugin: cache_check: *value = %i >= vl->time = %i",
- (int) *value, (int) vl->time);
- retval = 1;
- }
- }
- else
- {
- char *key_copy = strdup (key);
- value = malloc (sizeof (time_t));
- if ((key_copy != NULL) && (value != NULL))
- {
- *value = vl->time;
- c_avl_insert (cache_tree, key_copy, value);
- retval = 0;
- }
- else
- {
- sfree (key_copy);
- sfree (value);
- }
- }
+ vl->meta = meta_data_create ();
+ if (vl->meta == NULL)
+ {
+ ERROR ("network plugin: meta_data_create failed.");
+ return (-ENOMEM);
+ }
- if ((time (NULL) - cache_flush_last) > cache_flush_interval)
- cache_flush ();
+ status = meta_data_add_boolean (vl->meta, "network:received", true);
+ if (status != 0)
+ {
+ ERROR ("network plugin: meta_data_add_boolean failed.");
+ meta_data_destroy (vl->meta);
+ vl->meta = NULL;
+ return (status);
+ }
- pthread_mutex_unlock (&cache_lock);
+ plugin_dispatch_values (vl);
- return (retval);
-} /* int cache_check */
+ meta_data_destroy (vl->meta);
+ vl->meta = NULL;
+
+ return (0);
+} /* }}} int network_dispatch_values */
#if HAVE_LIBGCRYPT
static gcry_cipher_hd_t network_get_aes256_cypher (sockent_t *se, /* {{{ */
for (i = 0; i < num_values; i++)
{
- if (ds->ds[i].type == DS_TYPE_COUNTER)
+ pkg_values_types[i] = (uint8_t) ds->ds[i].type;
+ switch (ds->ds[i].type)
{
- pkg_values_types[i] = DS_TYPE_COUNTER;
- pkg_values[i].counter = htonll (vl->values[i].counter);
- }
- else
- {
- pkg_values_types[i] = DS_TYPE_GAUGE;
- pkg_values[i].gauge = htond (vl->values[i].gauge);
- }
- }
+ case DS_TYPE_COUNTER:
+ pkg_values[i].counter = htonll (vl->values[i].counter);
+ break;
+
+ case DS_TYPE_GAUGE:
+ pkg_values[i].gauge = htond (vl->values[i].gauge);
+ break;
+
+ case DS_TYPE_DERIVE:
+ pkg_values[i].derive = htonll (vl->values[i].derive);
+ break;
+
+ case DS_TYPE_ABSOLUTE:
+ pkg_values[i].absolute = htonll (vl->values[i].absolute);
+ break;
+
+ default:
+ free (pkg_values_types);
+ free (pkg_values);
+ ERROR ("network plugin: write_part_values: "
+ "Unknown data source type: %i",
+ ds->ds[i].type);
+ return (-1);
+ } /* switch (ds->ds[i].type) */
+ } /* for (num_values) */
/*
* Use `memcpy' to write everything to the buffer, because the pointer
for (i = 0; i < pkg_numval; i++)
{
- if (pkg_types[i] == DS_TYPE_COUNTER)
- pkg_values[i].counter = ntohll (pkg_values[i].counter);
- else if (pkg_types[i] == DS_TYPE_GAUGE)
- pkg_values[i].gauge = ntohd (pkg_values[i].gauge);
+ switch (pkg_types[i])
+ {
+ case DS_TYPE_COUNTER:
+ pkg_values[i].counter = (counter_t) ntohll (pkg_values[i].counter);
+ break;
+
+ case DS_TYPE_GAUGE:
+ pkg_values[i].gauge = (gauge_t) ntohd (pkg_values[i].gauge);
+ break;
+
+ case DS_TYPE_DERIVE:
+ pkg_values[i].derive = (derive_t) ntohll (pkg_values[i].derive);
+ break;
+
+ case DS_TYPE_ABSOLUTE:
+ pkg_values[i].absolute = (absolute_t) ntohll (pkg_values[i].absolute);
+ 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]);
+ return (-1);
+ } /* switch (pkg_types[i]) */
}
*ret_buffer = buffer;
{
status = parse_part_values (&buffer, &buffer_size,
&vl.values, &vl.values_len);
-
if (status != 0)
break;
- if ((vl.time > 0)
- && (strlen (vl.host) > 0)
- && (strlen (vl.plugin) > 0)
- && (strlen (vl.type) > 0)
- && (cache_check (&vl) == 0))
- {
- plugin_dispatch_values (&vl);
- }
- else
- {
- DEBUG ("network plugin: parse_packet:"
- " NOT dispatching values");
- }
+ network_dispatch_values (&vl);
sfree (vl.values);
}
}
} /* while (buffer_size > sizeof (part_header_t)) */
+ if (status == 0 && buffer_size > 0)
+ WARNING ("network plugin: parse_packet: Received truncated "
+ "packet, try increasing `MaxPacketSize'");
+
return (status);
} /* }}} int parse_packet */
/* Lock and wait for more data to come in */
pthread_mutex_lock (&receive_list_lock);
while ((listen_loop == 0)
- && (receive_list_head == NULL))
+ && (receive_list_head == NULL))
pthread_cond_wait (&receive_list_cond, &receive_list_lock);
/* Remove the head entry and unlock */
if (se == NULL)
{
- ERROR ("network plugin: Got packet from FD %i, but can't "
- "find an appropriate socket entry.",
- ent->fd);
- sfree (ent);
- continue;
+ ERROR ("network plugin: Got packet from FD %i, but can't "
+ "find an appropriate socket entry.",
+ ent->fd);
+ sfree (ent->data);
+ sfree (ent);
+ continue;
}
parse_packet (se, ent->data, ent->data_len, /* flags = */ 0);
+ sfree (ent->data);
sfree (ent);
} /* while (42) */
static int network_receive (void) /* {{{ */
{
- char buffer[BUFF_SIZE];
+ char buffer[network_config_packet_size];
int buffer_len;
int i;
return (-1);
}
memset (ent, 0, sizeof (receive_list_entry_t));
+ ent->data = malloc (network_config_packet_size);
+ if (ent->data == NULL)
+ {
+ ERROR ("network plugin: malloc failed.");
+ return (-1);
+ }
ent->fd = listen_sockets_pollfd[i].fd;
ent->next = NULL;
- /* Hopefully this be optimized out by the compiler. It
- * might help prevent stupid bugs in the future though.
- */
- assert (sizeof (ent->data) == sizeof (buffer));
-
memcpy (ent->data, buffer, buffer_len);
ent->data_len = buffer_len;
static void network_init_buffer (void)
{
- memset (send_buffer, 0, sizeof (send_buffer));
+ memset (send_buffer, 0, network_config_packet_size);
send_buffer_ptr = send_buffer;
send_buffer_fill = 0;
{
int status;
- /* If the value is already in the cache, we have received it via the
- * network. We write it again if forwarding is activated. It's then in
- * the cache and should we receive it again we will ignore it. */
- status = cache_check (vl);
- if ((network_config_forward == 0)
- && (status != 0))
- return (0);
+ if (!check_send_okay (vl))
+ {
+#if COLLECT_DEBUG
+ char name[6*DATA_MAX_NAME_LEN];
+ FORMAT_VL (name, sizeof (name), vl);
+ name[sizeof (name) - 1] = 0;
+ DEBUG ("network plugin: network_write: "
+ "NOT sending %s.", name);
+#endif
+ return (0);
+ }
+
+ uc_meta_data_add_unsigned_int (vl,
+ "network:time_sent", (uint64_t) vl->time);
pthread_mutex_lock (&send_buffer_lock);
status = add_to_buffer (send_buffer_ptr,
- sizeof (send_buffer) - (send_buffer_fill + BUFF_SIG_SIZE),
+ network_config_packet_size - (send_buffer_fill + BUFF_SIG_SIZE),
&send_buffer_vl,
ds, vl);
if (status >= 0)
flush_buffer ();
status = add_to_buffer (send_buffer_ptr,
- sizeof (send_buffer) - (send_buffer_fill + BUFF_SIG_SIZE),
+ network_config_packet_size - (send_buffer_fill + BUFF_SIG_SIZE),
&send_buffer_vl,
ds, vl);
ERROR ("network plugin: Unable to append to the "
"buffer for some weird reason");
}
- else if ((sizeof (send_buffer) - send_buffer_fill) < 15)
+ else if ((network_config_packet_size - send_buffer_fill) < 15)
{
flush_buffer ();
}
return (0);
} /* }}} int network_config_set_ttl */
+static int network_config_set_buffer_size (const oconfig_item_t *ci) /* {{{ */
+{
+ int tmp;
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+ {
+ WARNING ("network plugin: The `MaxPacketSize' config option needs exactly "
+ "one numeric argument.");
+ return (-1);
+ }
+
+ tmp = (int) ci->values[0].value.number;
+ if ((tmp >= 1024) && (tmp <= 65535))
+ network_config_packet_size = tmp;
+
+ return (0);
+} /* }}} int network_config_set_buffer_size */
+
#if HAVE_LIBGCRYPT
static int network_config_set_string (const oconfig_item_t *ci, /* {{{ */
char **ret_string)
return (0);
} /* }}} int network_config_add_server */
-static int network_config_set_cache_flush (const oconfig_item_t *ci) /* {{{ */
-{
- int tmp;
- if ((ci->values_num != 1)
- || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
- {
- WARNING ("network plugin: The `CacheFlush' config option needs exactly "
- "one numeric argument.");
- return (-1);
- }
-
- tmp = (int) ci->values[0].value.number;
- if (tmp > 0)
- cache_flush_interval = tmp;
-
- return (0);
-} /* }}} int network_config_set_cache_flush */
-
static int network_config (oconfig_item_t *ci) /* {{{ */
{
int i;
network_config_add_server (child);
else if (strcasecmp ("TimeToLive", child->key) == 0)
network_config_set_ttl (child);
+ else if (strcasecmp ("MaxPacketSize", child->key) == 0)
+ network_config_set_buffer_size (child);
else if (strcasecmp ("Forward", child->key) == 0)
network_config_set_boolean (child, &network_config_forward);
else if (strcasecmp ("CacheFlush", child->key) == 0)
- network_config_set_cache_flush (child);
+ /* no op for backwards compatibility only */;
else
{
WARNING ("network plugin: Option `%s' is not allowed here.",
static int network_notification (const notification_t *n,
user_data_t __attribute__((unused)) *user_data)
{
- char buffer[BUFF_SIZE];
+ char buffer[network_config_packet_size];
char *buffer_ptr = buffer;
int buffer_free = sizeof (buffer);
int status;
if (send_buffer_fill > 0)
flush_buffer ();
- if (cache_tree != NULL)
- {
- void *key;
- void *value;
-
- while (c_avl_pick (cache_tree, &key, &value) == 0)
- {
- sfree (key);
- sfree (value);
- }
- c_avl_destroy (cache_tree);
- cache_tree = NULL;
- }
+ sfree (send_buffer);
/* TODO: Close `sending_sockets' */
plugin_unregister_write ("network");
plugin_unregister_shutdown ("network");
- /* Let the init function do it's move again ;) */
- cache_flush_last = 0;
-
return (0);
} /* int network_shutdown */
static int network_init (void)
{
+ static _Bool have_init = false;
+
/* Check if we were already initialized. If so, just return - there's
* nothing more to do (for now, that is). */
- if (cache_flush_last != 0)
+ if (have_init)
return (0);
+ have_init = true;
#if HAVE_LIBGCRYPT
gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
plugin_register_shutdown ("network", network_shutdown);
+ send_buffer = malloc (network_config_packet_size);
+ if (send_buffer == NULL)
+ {
+ ERROR ("network plugin: malloc failed.");
+ return (-1);
+ }
network_init_buffer ();
- cache_tree = c_avl_create ((int (*) (const void *, const void *)) strcmp);
- cache_flush_last = time (NULL);
-
/* setup socket(s) and so on */
if (sending_sockets != NULL)
{
{
pthread_mutex_lock (&send_buffer_lock);
- if (((time (NULL) - cache_flush_last) >= timeout)
- && (send_buffer_fill > 0))
- {
- flush_buffer ();
- }
+ if (send_buffer_fill > 0)
+ flush_buffer ();
pthread_mutex_unlock (&send_buffer_lock);
--- /dev/null
+/**
+ * collectd - src/olsrd.c
+ * Copyright (C) 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
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#define OLSRD_DEFAULT_NODE "localhost"
+#define OLSRD_DEFAULT_SERVICE "2006"
+
+static const char *config_keys[] =
+{
+ "Host",
+ "Port",
+ "CollectLinks",
+ "CollectRoutes",
+ "CollectTopology"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static char *config_node = NULL;
+static char *config_service = NULL;
+
+#define OLSRD_WANT_NOT 0
+#define OLSRD_WANT_SUMMARY 1
+#define OLSRD_WANT_DETAIL 2
+static int config_want_links = OLSRD_WANT_DETAIL;
+static int config_want_routes = OLSRD_WANT_SUMMARY;
+static int config_want_topology = OLSRD_WANT_SUMMARY;
+
+static const char *olsrd_get_node (void) /* {{{ */
+{
+ if (config_node != NULL)
+ return (config_node);
+ return (OLSRD_DEFAULT_NODE);
+} /* }}} const char *olsrd_get_node */
+
+static const char *olsrd_get_service (void) /* {{{ */
+{
+ if (config_service != NULL)
+ return (config_service);
+ return (OLSRD_DEFAULT_SERVICE);
+} /* }}} const char *olsrd_get_service */
+
+static void olsrd_set_node (const char *node) /* {{{ */
+{
+ char *tmp;
+ if (node == NULL)
+ return;
+ tmp = strdup (node);
+ if (tmp == NULL)
+ return;
+ config_node = tmp;
+} /* }}} void olsrd_set_node */
+
+static void olsrd_set_service (const char *service) /* {{{ */
+{
+ char *tmp;
+ if (service == NULL)
+ return;
+ tmp = strdup (service);
+ if (tmp == NULL)
+ return;
+ config_service = tmp;
+} /* }}} void olsrd_set_service */
+
+static void olsrd_set_detail (int *varptr, const char *detail, /* {{{ */
+ const char *key)
+{
+ if (strcasecmp ("No", detail) == 0)
+ *varptr = OLSRD_WANT_NOT;
+ else if (strcasecmp ("Summary", detail) == 0)
+ *varptr = OLSRD_WANT_SUMMARY;
+ else if (strcasecmp ("Detail", detail) == 0)
+ *varptr = OLSRD_WANT_DETAIL;
+ else
+ {
+ ERROR ("olsrd plugin: Invalid argument given to the `%s' configuration "
+ "option: `%s'. Expected: `No', `Summary', or `Detail'.",
+ key, detail);
+ }
+} /* }}} void olsrd_set_detail */
+
+/* Strip trailing newline characters. Returns length of string. */
+static size_t strchomp (char *buffer) /* {{{ */
+{
+ size_t buffer_len;
+
+ buffer_len = strlen (buffer);
+ while ((buffer_len > 0)
+ && ((buffer[buffer_len - 1] == '\r')
+ || (buffer[buffer_len - 1] == '\n')))
+ {
+ buffer_len--;
+ buffer[buffer_len] = 0;
+ }
+
+ return (buffer_len);
+} /* }}} size_t strchomp */
+
+static size_t strtabsplit (char *string, char **fields, size_t size) /* {{{ */
+{
+ size_t i;
+ char *ptr;
+ char *saveptr;
+
+ i = 0;
+ ptr = string;
+ saveptr = NULL;
+ while ((fields[i] = strtok_r (ptr, " \t\r\n", &saveptr)) != NULL)
+ {
+ ptr = NULL;
+ i++;
+
+ if (i >= size)
+ break;
+ }
+
+ return (i);
+} /* }}} size_t strtabsplit */
+
+static FILE *olsrd_connect (void) /* {{{ */
+{
+ struct addrinfo ai_hints;
+ struct addrinfo *ai_list, *ai_ptr;
+ int ai_return;
+
+ FILE *fh;
+
+ memset (&ai_hints, 0, sizeof (ai_hints));
+ ai_hints.ai_flags = 0;
+#ifdef AI_ADDRCONFIG
+ ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ ai_hints.ai_family = PF_UNSPEC;
+ ai_hints.ai_socktype = SOCK_STREAM;
+ ai_hints.ai_protocol = IPPROTO_TCP;
+
+ ai_list = NULL;
+ ai_return = getaddrinfo (olsrd_get_node (), olsrd_get_service (),
+ &ai_hints, &ai_list);
+ if (ai_return != 0)
+ {
+ ERROR ("olsrd plugin: getaddrinfo (%s, %s) failed: %s",
+ olsrd_get_node (), olsrd_get_service (),
+ gai_strerror (ai_return));
+ return (NULL);
+ }
+
+ fh = NULL;
+ for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+ {
+ int fd;
+ int status;
+ char errbuf[1024];
+
+ fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+ if (fd < 0)
+ {
+ ERROR ("olsrd plugin: socket failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ continue;
+ }
+
+ status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
+ if (status != 0)
+ {
+ ERROR ("olsrd plugin: connect failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ close (fd);
+ continue;
+ }
+
+ fh = fdopen (fd, "r+");
+ if (fh == NULL)
+ {
+ ERROR ("olsrd plugin: fdopen failed.");
+ close (fd);
+ continue;
+ }
+
+ break;
+ } /* for (ai_ptr) */
+
+ freeaddrinfo (ai_list);
+
+ return (fh);
+} /* }}} FILE *olsrd_connect */
+
+__attribute__ ((nonnull(2)))
+static void olsrd_submit (const 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, "olsrd", 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);
+} /* }}} void olsrd_submit */
+
+static int olsrd_cb_ignore (int lineno, /* {{{ */
+ size_t fields_num, char **fields)
+{
+ return (0);
+} /* }}} int olsrd_cb_ignore */
+
+static int olsrd_cb_links (int lineno, /* {{{ */
+ size_t fields_num, char **fields)
+{
+ /* Fields:
+ * 0 = Local IP
+ * 1 = Remote IP
+ * 2 = Hyst.
+ * 3 = LQ
+ * 4 = NLQ
+ * 5 = Cost */
+
+ static uint32_t links_num;
+ static double lq_sum;
+ static uint32_t lq_num;
+ static double nlq_sum;
+ static uint32_t nlq_num;
+
+ double lq;
+ double nlq;
+
+ char *endptr;
+
+ if (config_want_links == OLSRD_WANT_NOT)
+ return (0);
+
+ /* Special handling of the first line. */
+ if (lineno <= 0)
+ {
+ links_num = 0;
+ lq_sum = 0.0;
+ lq_num = 0;
+ nlq_sum = 0.0;
+ nlq_num = 0;
+
+ return (0);
+ }
+
+ /* Special handling of the last line. */
+ if (fields_num == 0)
+ {
+ DEBUG ("olsrd plugin: Number of links: %"PRIu32, links_num);
+ olsrd_submit (/* p.-inst = */ "links", /* type = */ "links",
+ /* t.-inst = */ NULL, (gauge_t) links_num);
+
+ lq = NAN;
+ if (lq_num > 0)
+ lq = lq_sum / ((double) lq_num);
+ DEBUG ("olsrd plugin: Average LQ: %g", lq);
+ olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
+ "average-lq", lq);
+
+ nlq = NAN;
+ if (nlq_num > 0)
+ nlq = nlq_sum / ((double) nlq_num);
+ DEBUG ("olsrd plugin: Average NLQ: %g", nlq);
+ olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
+ "average-nlq", nlq);
+
+ return (0);
+ }
+
+ if (fields_num != 6)
+ return (-1);
+
+ links_num++;
+
+ errno = 0;
+ endptr = NULL;
+ lq = strtod (fields[3], &endptr);
+ if ((errno != 0) || (endptr == fields[3]))
+ {
+ ERROR ("olsrd plugin: Cannot parse link quality: %s", fields[3]);
+ }
+ else
+ {
+ if (!isnan (lq))
+ {
+ lq_sum += lq;
+ lq_num++;
+ }
+
+ if (config_want_links == OLSRD_WANT_DETAIL)
+ {
+ char type_instance[DATA_MAX_NAME_LEN];
+
+ ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
+ fields[0], fields[1]);
+
+ DEBUG ("olsrd plugin: links: type_instance = %s; lq = %g;",
+ type_instance, lq);
+ olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
+ type_instance, lq);
+ }
+ }
+
+ errno = 0;
+ endptr = NULL;
+ nlq = strtod (fields[4], &endptr);
+ if ((errno != 0) || (endptr == fields[4]))
+ {
+ ERROR ("olsrd plugin: Cannot parse neighbor link quality: %s", fields[4]);
+ }
+ else
+ {
+ if (!isnan (nlq))
+ {
+ nlq_sum += nlq;
+ nlq_num++;
+ }
+
+ if (config_want_links == OLSRD_WANT_DETAIL)
+ {
+ char type_instance[DATA_MAX_NAME_LEN];
+
+ ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
+ fields[0], fields[1]);
+
+ DEBUG ("olsrd plugin: links: type_instance = %s; nlq = %g;",
+ type_instance, lq);
+ olsrd_submit (/* p.-inst = */ "links", /* type = */ "signal_quality",
+ type_instance, nlq);
+ }
+ }
+
+ return (0);
+} /* }}} int olsrd_cb_links */
+
+static int olsrd_cb_routes (int lineno, /* {{{ */
+ size_t fields_num, char **fields)
+{
+ /* Fields:
+ * 0 = Destination
+ * 1 = Gateway IP
+ * 2 = Metric
+ * 3 = ETX
+ * 4 = Interface */
+
+ static uint32_t routes_num;
+ static uint32_t metric_sum;
+ static uint32_t metric_num;
+ static double etx_sum;
+ static uint32_t etx_num;
+
+ uint32_t metric;
+ double etx;
+ char *endptr;
+
+ if (config_want_routes == OLSRD_WANT_NOT)
+ return (0);
+
+ /* Special handling of the first line */
+ if (lineno <= 0)
+ {
+ routes_num = 0;
+ metric_num = 0;
+ metric_sum = 0;
+ etx_sum = 0.0;
+ etx_num = 0;
+
+ return (0);
+ }
+
+ /* Special handling after the last line */
+ if (fields_num == 0)
+ {
+ double metric_avg;
+
+ DEBUG ("olsrd plugin: Number of routes: %"PRIu32, routes_num);
+ olsrd_submit (/* p.-inst = */ "routes", /* type = */ "routes",
+ /* t.-inst = */ NULL, (gauge_t) routes_num);
+
+ metric_avg = NAN;
+ if (metric_num > 0)
+ metric_avg = ((double) metric_sum) / ((double) metric_num);
+ DEBUG ("olsrd plugin: Average metric: %g", metric_avg);
+ olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
+ "average", metric_avg);
+
+ etx = NAN;
+ if (etx_num > 0)
+ etx = etx_sum / ((double) etx_sum);
+ DEBUG ("olsrd plugin: Average ETX: %g", etx);
+ olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
+ "average", etx);
+
+ return (0);
+ }
+
+ if (fields_num != 5)
+ return (-1);
+
+ routes_num++;
+
+ errno = 0;
+ endptr = NULL;
+ metric = (uint32_t) strtoul (fields[2], &endptr, 0);
+ if ((errno != 0) || (endptr == fields[2]))
+ {
+ ERROR ("olsrd plugin: Unable to parse metric: %s", fields[2]);
+ }
+ else
+ {
+ metric_num++;
+ metric_sum += metric;
+
+ if (config_want_routes == OLSRD_WANT_DETAIL)
+ {
+ DEBUG ("olsrd plugin: destination = %s; metric = %"PRIu32";",
+ fields[0], metric);
+ olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_metric",
+ /* t.-inst = */ fields[0], (gauge_t) metric);
+ }
+ }
+
+ errno = 0;
+ endptr = NULL;
+ etx = strtod (fields[3], &endptr);
+ if ((errno != 0) || (endptr == fields[3]))
+ {
+ ERROR ("olsrd plugin: Unable to parse ETX: %s", fields[3]);
+ }
+ else
+ {
+ if (!isnan (etx))
+ {
+ etx_sum += etx;
+ etx_num++;
+ }
+
+ if (config_want_routes == OLSRD_WANT_DETAIL)
+ {
+ DEBUG ("olsrd plugin: destination = %s; etx = %g;",
+ fields[0], etx);
+ olsrd_submit (/* p.-inst = */ "routes", /* type = */ "route_etx",
+ /* t.-inst = */ fields[0], etx);
+ }
+ }
+
+ return (0);
+} /* }}} int olsrd_cb_routes */
+
+static int olsrd_cb_topology (int lineno, /* {{{ */
+ size_t fields_num, char **fields)
+{
+ /* Fields:
+ * 0 = Dest. IP
+ * 1 = Last hop IP
+ * 2 = LQ
+ * 3 = NLQ
+ * 4 = Cost */
+
+ static double lq_sum;
+ static uint32_t lq_num;
+
+ static uint32_t links_num;
+
+ double lq;
+ char *endptr;
+
+ if (config_want_topology == OLSRD_WANT_NOT)
+ return (0);
+
+ /* Special handling of the first line */
+ if (lineno <= 0)
+ {
+ lq_sum = 0.0;
+ lq_num = 0;
+ links_num = 0;
+
+ return (0);
+ }
+
+ /* Special handling after the last line */
+ if (fields_num == 0)
+ {
+ DEBUG ("olsrd plugin: topology: Number of links: %"PRIu32, links_num);
+ olsrd_submit (/* p.-inst = */ "topology", /* type = */ "links",
+ /* t.-inst = */ NULL, (gauge_t) links_num);
+
+ lq = NAN;
+ if (lq_num > 0)
+ lq = lq_sum / ((double) lq_sum);
+ DEBUG ("olsrd plugin: topology: Average link quality: %g", lq);
+ olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
+ /* t.-inst = */ "average", lq);
+
+ return (0);
+ }
+
+ if (fields_num != 5)
+ return (-1);
+
+ links_num++;
+
+ errno = 0;
+ endptr = NULL;
+ lq = strtod (fields[2], &endptr);
+ if ((errno != 0) || (endptr == fields[2]))
+ {
+ ERROR ("olsrd plugin: Unable to parse LQ: %s", fields[2]);
+ }
+ else
+ {
+ if (!isnan (lq))
+ {
+ lq_sum += lq;
+ lq_num++;
+ }
+
+ if (config_want_topology == OLSRD_WANT_DETAIL)
+ {
+ char type_instance[DATA_MAX_NAME_LEN];
+
+ memset (type_instance, 0, sizeof (type_instance));
+ ssnprintf (type_instance, sizeof (type_instance), "%s-%s-lq",
+ fields[0], fields[1]);
+ DEBUG ("olsrd plugin: type_instance = %s; lq = %g;", type_instance, lq);
+ olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
+ type_instance, lq);
+ }
+ }
+
+ if (config_want_topology == OLSRD_WANT_DETAIL)
+ {
+ double nlq;
+
+ errno = 0;
+ endptr = NULL;
+ nlq = strtod (fields[3], &endptr);
+ if ((errno != 0) || (endptr == fields[3]))
+ {
+ ERROR ("olsrd plugin: Unable to parse NLQ: %s", fields[3]);
+ }
+ else
+ {
+ char type_instance[DATA_MAX_NAME_LEN];
+
+ memset (type_instance, 0, sizeof (type_instance));
+ ssnprintf (type_instance, sizeof (type_instance), "%s-%s-nlq",
+ fields[0], fields[1]);
+ DEBUG ("olsrd plugin: type_instance = %s; nlq = %g;", type_instance, nlq);
+ olsrd_submit (/* p.-inst = */ "topology", /* type = */ "signal_quality",
+ type_instance, nlq);
+ }
+ }
+
+ return (0);
+} /* }}} int olsrd_cb_topology */
+
+static int olsrd_read_table (FILE *fh, /* {{{ */
+ int (*callback) (int lineno, size_t fields_num, char **fields))
+{
+ char buffer[1024];
+ size_t buffer_len;
+
+ char *fields[32];
+ size_t fields_num;
+
+ int lineno;
+
+ lineno = 0;
+ while (fgets (buffer, sizeof (buffer), fh) != NULL)
+ {
+ /* An empty line ends the table. */
+ buffer_len = strchomp (buffer);
+ if (buffer_len <= 0)
+ {
+ (*callback) (lineno, /* fields_num = */ 0, /* fields = */ NULL);
+ break;
+ }
+
+ fields_num = strtabsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+
+ (*callback) (lineno, fields_num, fields);
+ lineno++;
+ } /* while (fgets) */
+
+ return (0);
+} /* }}} int olsrd_read_table */
+
+static int olsrd_config (const char *key, const char *value) /* {{{ */
+{
+ if (strcasecmp ("Host", key) == 0)
+ olsrd_set_node (value);
+ else if (strcasecmp ("Port", key) == 0)
+ olsrd_set_service (value);
+ else if (strcasecmp ("CollectLinks", key) == 0)
+ olsrd_set_detail (&config_want_links, value, key);
+ else if (strcasecmp ("CollectRoutes", key) == 0)
+ olsrd_set_detail (&config_want_routes, value, key);
+ else if (strcasecmp ("CollectTopology", key) == 0)
+ olsrd_set_detail (&config_want_topology, value, key);
+ else
+ {
+ ERROR ("olsrd plugin: Unknown configuration option given: %s", key);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int olsrd_config */
+
+static int olsrd_read (void) /* {{{ */
+{
+ FILE *fh;
+ char buffer[1024];
+ size_t buffer_len;
+
+ fh = olsrd_connect ();
+ if (fh == NULL)
+ return (-1);
+
+ fputs ("\r\n", fh);
+ fflush (fh);
+
+ while (fgets (buffer, sizeof (buffer), fh) != NULL)
+ {
+ buffer_len = strchomp (buffer);
+ if (buffer_len <= 0)
+ continue;
+
+ if (strcmp ("Table: Links", buffer) == 0)
+ olsrd_read_table (fh, olsrd_cb_links);
+ else if (strcmp ("Table: Neighbors", buffer) == 0)
+ olsrd_read_table (fh, olsrd_cb_ignore);
+ else if (strcmp ("Table: Topology", buffer) == 0)
+ olsrd_read_table (fh, olsrd_cb_topology);
+ else if (strcmp ("Table: HNA", buffer) == 0)
+ olsrd_read_table (fh, olsrd_cb_ignore);
+ else if (strcmp ("Table: MID", buffer) == 0)
+ olsrd_read_table (fh, olsrd_cb_ignore);
+ else if (strcmp ("Table: Routes", buffer) == 0)
+ olsrd_read_table (fh, olsrd_cb_routes);
+ else if ((strcmp ("HTTP/1.0 200 OK", buffer) == 0)
+ || (strcmp ("Content-type: text/plain", buffer) == 0))
+ {
+ /* ignore */
+ }
+ else
+ {
+ DEBUG ("olsrd plugin: Unable to handle line: %s", buffer);
+ }
+ } /* while (fgets) */
+
+ fclose (fh);
+
+ return (0);
+} /* }}} int olsrd_read */
+
+static int olsrd_shutdown (void) /* {{{ */
+{
+ sfree (config_node);
+ sfree (config_service);
+
+ return (0);
+} /* }}} int olsrd_shutdown */
+
+void module_register (void)
+{
+ plugin_register_config ("olsrd", olsrd_config,
+ config_keys, config_keys_num);
+ plugin_register_read ("olsrd", olsrd_read);
+ plugin_register_shutdown ("olsrd", olsrd_shutdown);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
{ "Collectd::TYPE_DATASET", PLUGIN_DATASET },
{ "Collectd::DS_TYPE_COUNTER", DS_TYPE_COUNTER },
{ "Collectd::DS_TYPE_GAUGE", DS_TYPE_GAUGE },
+ { "Collectd::DS_TYPE_DERIVE", DS_TYPE_DERIVE },
+ { "Collectd::DS_TYPE_ABSOLUTE", DS_TYPE_ABSOLUTE },
{ "Collectd::LOG_ERR", LOG_ERR },
{ "Collectd::LOG_WARNING", LOG_WARNING },
{ "Collectd::LOG_NOTICE", LOG_NOTICE },
if (NULL != (tmp = hv_fetch (hash, "type", 4, 0))) {
ds->type = SvIV (*tmp);
- if ((DS_TYPE_COUNTER != ds->type) && (DS_TYPE_GAUGE != ds->type)) {
+ if ((DS_TYPE_COUNTER != ds->type)
+ && (DS_TYPE_GAUGE != ds->type)
+ && (DS_TYPE_DERIVE != ds->type)
+ && (DS_TYPE_ABSOLUTE != ds->type)) {
log_err ("hv2data_source: Invalid DS type.");
return -1;
}
if (NULL != tmp) {
if (DS_TYPE_COUNTER == ds->ds[i].type)
value[i].counter = SvIV (*tmp);
- else
+ else if (DS_TYPE_GAUGE == ds->ds[i].type)
value[i].gauge = SvNV (*tmp);
+ else if (DS_TYPE_DERIVE == ds->ds[i].type)
+ value[i].derive = SvIV (*tmp);
+ else if (DS_TYPE_ABSOLUTE == ds->ds[i].type)
+ value[i].absolute = SvIV (*tmp);
}
else {
return -1;
if (DS_TYPE_COUNTER == ds->ds[i].type)
val = newSViv (vl->values[i].counter);
- else
+ else if (DS_TYPE_GAUGE == ds->ds[i].type)
val = newSVnv (vl->values[i].gauge);
+ else if (DS_TYPE_DERIVE == ds->ds[i].type)
+ val = newSViv (vl->values[i].derive);
+ else if (DS_TYPE_ABSOLUTE == ds->ds[i].type)
+ val = newSViv (vl->values[i].absolute);
if (NULL == av_store (values, i, val)) {
av_undef (values);
(*callback) ();
}
- destroy_all_callbacks (&list_write);
+ /* Write plugins which use the `user_data' pointer usually need the
+ * same data available to the flush callback. If this is the case, set
+ * the free_function to NULL when registering the flush callback and to
+ * 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_write);
+
destroy_all_callbacks (&list_notification);
destroy_all_callbacks (&list_shutdown);
destroy_all_callbacks (&list_log);
data_set_t *ds;
+ int free_meta_data = 0;
+
if ((vl == NULL) || (vl->type[0] == 0)
|| (vl->values == NULL) || (vl->values_len < 1))
{
return (-1);
}
+ /* Free meta data only if the calling function didn't specify any. In
+ * this case matches and targets may add some and the calling function
+ * may not expect (and therefore free) that data. */
+ if (vl->meta == NULL)
+ free_meta_data = 1;
+
if (list_write == NULL)
c_complain_once (LOG_WARNING, &no_write_complaint,
"plugin_dispatch_values: No write callback has been "
vl->values_len = saved_values_len;
}
+ if ((free_meta_data != 0) && (vl->meta != NULL))
+ {
+ meta_data_destroy (vl->meta);
+ vl->meta = NULL;
+ }
+
return (0);
} /* int plugin_dispatch_values */
#include "collectd.h"
#include "configfile.h"
+#include "meta_data.h"
#define DATA_MAX_NAME_LEN 64
-#define DS_TYPE_COUNTER 0
-#define DS_TYPE_GAUGE 1
+#define DS_TYPE_COUNTER 0
+#define DS_TYPE_GAUGE 1
+#define DS_TYPE_DERIVE 2
+#define DS_TYPE_ABSOLUTE 3
+
+#define DS_TYPE_TO_STRING(t) (t == DS_TYPE_COUNTER) ? "counter" : \
+ (t == DS_TYPE_GAUGE) ? "gauge" : \
+ (t == DS_TYPE_DERIVE) ? "derive" : \
+ (t == DS_TYPE_ABSOLUTE) ? "absolute" : \
+ "unknown"
+
#ifndef LOG_ERR
# define LOG_ERR 3
*/
typedef unsigned long long counter_t;
typedef double gauge_t;
+typedef int64_t derive_t;
+typedef uint64_t absolute_t;
union value_u
{
- counter_t counter;
- gauge_t gauge;
+ counter_t counter;
+ gauge_t gauge;
+ derive_t derive;
+ absolute_t absolute;
};
typedef union value_u value_t;
char plugin_instance[DATA_MAX_NAME_LEN];
char type[DATA_MAX_NAME_LEN];
char type_instance[DATA_MAX_NAME_LEN];
+ meta_data_t *meta;
};
typedef struct value_list_s value_list_t;
-#define VALUE_LIST_INIT { NULL, 0, 0, interval_g, "localhost", "", "", "", "" }
-#define VALUE_LIST_STATIC { NULL, 0, 0, 0, "localhost", "", "", "", "" }
+#define VALUE_LIST_INIT { NULL, 0, 0, interval_g, "localhost", "", "", "", "", NULL }
+#define VALUE_LIST_STATIC { NULL, 0, 0, 0, "localhost", "", "", "", "", NULL }
struct data_source_s
{
return;
}
- if (0 != parse_value (value, &values[0], ds->ds[0]))
+ if (0 != parse_value (value, &values[0], ds->ds[0].type))
{
ERROR ("powerdns plugin: Cannot convert `%s' "
"to a number.", value);
for (i = 0; i < ds->ds_num; i++)
{
if ((ds->ds[i].type != DS_TYPE_COUNTER)
- && (ds->ds[i].type != DS_TYPE_GAUGE))
+ && (ds->ds[i].type != DS_TYPE_GAUGE)
+ && (ds->ds[i].type != DS_TYPE_DERIVE)
+ && (ds->ds[i].type != DS_TYPE_ABSOLUTE))
return (-1);
if (ds->ds[i].type == DS_TYPE_COUNTER)
status = ssnprintf (buffer + offset, buffer_len - offset,
":%llu", vl->values[i].counter);
}
- else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+ else if (ds->ds[i].type == DS_TYPE_GAUGE)
{
status = ssnprintf (buffer + offset, buffer_len - offset,
- ":%lf", vl->values[i].gauge);
+ ":%f", vl->values[i].gauge);
+ }
+ else if (ds->ds[i].type == DS_TYPE_DERIVE) {
+ status = ssnprintf (buffer + offset, buffer_len - offset,
+ ":%"PRIi64, vl->values[i].derive);
+ }
+ else /* if (ds->ds[i].type == DS_TYPE_ABSOLUTE) */ {
+ status = ssnprintf (buffer + offset, buffer_len - offset,
+ ":%"PRIu64, vl->values[i].absolute);
+
}
if ((status < 1) || (status >= (buffer_len - offset)))
/**
* collectd - src/rrdtool.c
* Copyright (C) 2006-2008 Florian octo Forster
+ * Copyright (C) 2008-2008 Sebastian Harl
+ * Copyright (C) 2009 Mariusz Gronczewski
*
* 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:
* Florian octo Forster <octo at verplant.org>
+ * Sebastian Harl <sh at tokkee.org>
+ * Mariusz Gronczewski <xani666 at gmail.com>
**/
#include "collectd.h"
char **values;
time_t first_value;
time_t last_value;
+ int random_variation;
enum
{
FLAG_NONE = 0x00,
"RRARows",
"RRATimespan",
"XFF",
- "WritesPerSecond"
+ "WritesPerSecond",
+ "RandomTimeout"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
* 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 c_avl_tree_t *cache = NULL;
static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
for (i = 0; i < ds->ds_num; i++)
{
if ((ds->ds[i].type != DS_TYPE_COUNTER)
- && (ds->ds[i].type != DS_TYPE_GAUGE))
+ && (ds->ds[i].type != DS_TYPE_GAUGE)
+ && (ds->ds[i].type != DS_TYPE_DERIVE)
+ && (ds->ds[i].type != DS_TYPE_ABSOLUTE))
return (-1);
if (ds->ds[i].type == DS_TYPE_COUNTER)
status = ssnprintf (buffer + offset, buffer_len - offset,
":%llu", vl->values[i].counter);
- else
+ else if (ds->ds[i].type == DS_TYPE_GAUGE)
status = ssnprintf (buffer + offset, buffer_len - offset,
":%lf", vl->values[i].gauge);
+ else if (ds->ds[i].type == DS_TYPE_DERIVE)
+ status = ssnprintf (buffer + offset, buffer_len - offset,
+ ":%"PRIi64, vl->values[i].derive);
+ else /*if (ds->ds[i].type == DS_TYPE_ABSOLUTE) */
+ status = ssnprintf (buffer + offset, buffer_len - offset,
+ ":%"PRIu64, vl->values[i].absolute);
if ((status < 1) || (status >= (buffer_len - offset)))
return (-1);
filename, rc->values_num,
(unsigned long)(rc->last_value - rc->first_value));
- if ((rc->last_value - rc->first_value) >= cache_timeout)
+ if ((rc->last_value + rc->random_variation - rc->first_value) >= cache_timeout)
{
/* XXX: If you need to lock both, cache_lock and queue_lock, at
* the same time, ALWAYS lock `cache_lock' first! */
status = rrd_queue_enqueue (filename, &queue_head, &queue_tail);
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;
+ }
}
else
{
write_rate = 1.0 / wps;
}
}
+ else if (strcasecmp ("RandomTimeout", key) == 0)
+ {
+ int tmp;
+
+ tmp = atoi (value);
+ if (tmp < 0)
+ {
+ fprintf (stderr, "rrdtool: `RandomTimeout' must "
+ "be greater than or equal to zero.\n");
+ ERROR ("rrdtool: `RandomTimeout' must "
+ "be greater then or equal to zero.");
+ }
+ else
+ {
+ random_timeout = tmp;
+ }
+ }
else
{
return (-1);
if (vl->type == ASN_OCTET_STR)
{
- char *endptr;
+ int status = -1;
- endptr = NULL;
if (vl->val.string != NULL)
{
char string[64];
string_length = vl->val_len;
/* The strings we get from the Net-SNMP library may not be null
- * terminated. That is why we're using `membpy' here and not `strcpy'.
+ * terminated. That is why we're using `memcpy' here and not `strcpy'.
* `string_length' is set to `vl->val_len' which holds the length of the
* string. -octo */
memcpy (string, vl->val.string, string_length);
string[string_length] = 0;
- if (type == DS_TYPE_COUNTER)
+ status = parse_value (string, &ret, type);
+ if (status != 0)
{
- ret.counter = (counter_t) strtoll (string, &endptr, /* base = */ 0);
- DEBUG ("snmp plugin: csnmp_value_list_to_value: String to counter: %s -> %llu",
- string, (unsigned long long) ret.counter);
- }
- else if (type == DS_TYPE_GAUGE)
- {
- ret.gauge = (gauge_t) strtod (string, &endptr);
- DEBUG ("snmp plugin: csnmp_value_list_to_value: String to gauge: %s -> %g",
- string, (double) ret.gauge);
+ ERROR ("snmp plugin: csnmp_value_list_to_value: Parsing string as %s failed: %s",
+ DS_TYPE_TO_STRING (type), string);
}
}
- /* Check if an error occurred */
- if ((vl->val.string == NULL) || (endptr == (char *) vl->val.string))
+ if (status != 0)
{
- if (type == DS_TYPE_COUNTER)
- ret.counter = 0;
- else if (type == DS_TYPE_GAUGE)
- ret.gauge = NAN;
+ switch (type)
+ {
+ case DS_TYPE_COUNTER:
+ case DS_TYPE_DERIVE:
+ case DS_TYPE_ABSOLUTE:
+ memset (&ret, 0, sizeof (ret));
+ break;
+
+ case DS_TYPE_GAUGE:
+ ret.gauge = NAN;
+ break;
+
+ default:
+ ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown "
+ "data source type: %i.", type);
+ ret.gauge = NAN;
+ }
}
- }
+ } /* if (vl->type == ASN_OCTET_STR) */
else if (type == DS_TYPE_COUNTER)
- {
ret.counter = temp;
- }
else if (type == DS_TYPE_GAUGE)
{
ret.gauge = NAN;
if (defined != 0)
ret.gauge = (scale * temp) + shift;
}
+ else if (type == DS_TYPE_DERIVE)
+ ret.derive = (derive_t) temp;
+ else if (type == DS_TYPE_ABSOLUTE)
+ ret.absolute = (absolute_t) temp;
+ else
+ {
+ ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown data source "
+ "type: %i.", type);
+ ret.gauge = NAN;
+ }
return (ret);
} /* value_t csnmp_value_list_to_value */
/**
* collectd - src/swap.c
* Copyright (C) 2005-2009 Florian octo Forster
+ * Copyright (C) 2009 Stefan Völkel
*
* 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
/* #endif KERNEL_LINUX */
#elif HAVE_LIBKSTAT
-static unsigned long long pagesize;
+static derive_t pagesize;
static kstat_t *ksp;
/* #endif HAVE_LIBKSTAT */
#elif HAVE_LIBKSTAT
/* getpagesize(3C) tells me this does not fail.. */
- pagesize = (unsigned long long) getpagesize ();
+ pagesize = (derive_t) getpagesize ();
if (get_kstat (&ksp, "unix", 0, "system_pages"))
ksp = NULL;
/* #endif HAVE_LIBKSTAT */
return (0);
}
-static void swap_submit (const char *type_instance, double value)
+static void swap_submit (const char *type_instance, derive_t value, unsigned type)
{
value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
+ switch (type)
+ {
+ case DS_TYPE_GAUGE:
+ values[0].gauge = (gauge_t) value;
+ sstrncpy (vl.type, "swap", sizeof (vl.type));
+ break;
+ case DS_TYPE_DERIVE:
+ values[0].derive = value;
+ sstrncpy (vl.type, "swap_io", sizeof (vl.type));
+ break;
+ default:
+ ERROR ("swap plugin: swap_submit called with wrong"
+ " type");
+ }
vl.values = values;
vl.values_len = 1;
sstrncpy (vl.host, hostname_g, sizeof (vl.host));
sstrncpy (vl.plugin, "swap", sizeof (vl.plugin));
- sstrncpy (vl.type, "swap", sizeof (vl.type));
sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
plugin_dispatch_values (&vl);
char *fields[8];
int numfields;
- unsigned long long swap_used = 0LL;
- unsigned long long swap_cached = 0LL;
- unsigned long long swap_free = 0LL;
- unsigned long long swap_total = 0LL;
+ derive_t swap_used = 0;
+ derive_t swap_cached = 0;
+ derive_t swap_free = 0;
+ derive_t swap_total = 0;
+ derive_t swap_in = 0;
+ derive_t swap_out = 0;
if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
{
while (fgets (buffer, 1024, fh) != NULL)
{
- unsigned long long *val = NULL;
+ derive_t *val = NULL;
if (strncasecmp (buffer, "SwapTotal:", 10) == 0)
val = &swap_total;
if (numfields < 2)
continue;
- *val = atoll (fields[1]) * 1024LL;
+ *val = (derive_t) atoll (fields[1]) * 1024LL;
}
if (fclose (fh))
swap_used = swap_total - (swap_free + swap_cached);
- swap_submit ("used", swap_used);
- swap_submit ("free", swap_free);
- swap_submit ("cached", swap_cached);
+ if ((fh = fopen ("/proc/vmstat", "r")) == NULL)
+ {
+ char errbuf[1024];
+ WARNING ("swap: fopen: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ while (fgets (buffer, 1024, 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]);
+ }
+
+ if (fclose (fh))
+ {
+ char errbuf[1024];
+ WARNING ("swap: fclose: %s",
+ 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 ("out", swap_out, DS_TYPE_DERIVE);
+
/* #endif KERNEL_LINUX */
#elif HAVE_LIBKSTAT
- unsigned long long swap_alloc;
- unsigned long long swap_resv;
- unsigned long long swap_avail;
+ derive_t swap_alloc;
+ derive_t swap_resv;
+ derive_t swap_avail;
struct anoninfo ai;
* swap_alloc = pagesize * ( ai.ani_max - ai.ani_free );
* can suffer from a 32bit overflow.
*/
- swap_alloc = ai.ani_max - ai.ani_free;
- swap_alloc *= pagesize;
- swap_resv = ai.ani_resv + ai.ani_free - ai.ani_max;
- swap_resv *= pagesize;
- swap_avail = ai.ani_max - ai.ani_resv;
- swap_avail *= pagesize;
-
- swap_submit ("used", swap_alloc);
- swap_submit ("free", swap_avail);
- swap_submit ("reserved", swap_resv);
+ swap_alloc = (derive_t) ((ai.ani_max - ai.ani_free) * pagesize);
+ swap_resv = (derive_t) ((ai.ani_resv + ai.ani_free - ai.ani_max)
+ * pagesize);
+ swap_avail = (derive_t) ((ai.ani_max - ai.ani_resv) * pagesize);
+
+ 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
int status;
int i;
- uint64_t used = 0;
- uint64_t total = 0;
+ derive_t used = 0;
+ derive_t total = 0;
/*
* XXX: This is the syntax for the *BSD `swapctl', which has the
}
#if defined(DEV_BSIZE) && (DEV_BSIZE > 0)
-# define C_SWAP_BLOCK_SIZE ((uint64_t) DEV_BSIZE)
+# define C_SWAP_BLOCK_SIZE ((derive_t) DEV_BSIZE)
#else
-# define C_SWAP_BLOCK_SIZE ((uint64_t) 512)
+# define C_SWAP_BLOCK_SIZE ((derive_t) 512)
#endif
for (i = 0; i < swap_num; i++)
if ((swap_entries[i].se_flags & SWF_ENABLE) == 0)
continue;
- used += ((uint64_t) swap_entries[i].se_inuse)
+ used += ((derive_t) swap_entries[i].se_inuse)
* C_SWAP_BLOCK_SIZE;
- total += ((uint64_t) swap_entries[i].se_nblks)
+ total += ((derive_t) swap_entries[i].se_nblks)
* C_SWAP_BLOCK_SIZE;
}
return (-1);
}
- swap_submit ("used", (gauge_t) used);
- swap_submit ("free", (gauge_t) (total - used));
+ swap_submit ("used", used, DS_TYPE_GAUGE);
+ swap_submit ("free", total - used, DS_TYPE_GAUGE);
sfree (swap_entries);
/* #endif HAVE_SWAPCTL */
return (-1);
/* The returned values are bytes. */
- swap_submit ("used", sw_usage.xsu_used);
- swap_submit ("free", sw_usage.xsu_avail);
+ swap_submit ("used", (derive_t) sw_usage.xsu_used, DS_TYPE_GAUGE);
+ swap_submit ("free", (derive_t) sw_usage.xsu_avail, DS_TYPE_GAUGE);
/* #endif VM_SWAPUSAGE */
#elif HAVE_LIBKVM_GETSWAPINFO
struct kvm_swap data_s;
int status;
- unsigned long long used;
- unsigned long long free;
- unsigned long long total;
+ derive_t used;
+ derive_t free;
+ derive_t total;
if (kvm_obj == NULL)
return (-1);
if (status == -1)
return (-1);
- total = (unsigned long long) data_s.ksw_total;
- used = (unsigned long long) data_s.ksw_used;
+ total = (derive_t) data_s.ksw_total;
+ used = (derive_t) data_s.ksw_used;
- total *= (unsigned long long) kvm_pagesize;
- used *= (unsigned long long) kvm_pagesize;
+ total *= (derive_t) kvm_pagesize;
+ used *= (derive_t) kvm_pagesize;
free = total - used;
- swap_submit ("used", used);
- swap_submit ("free", free);
+ swap_submit ("used", used, DS_TYPE_GAUGE);
+ swap_submit ("free", free, DS_TYPE_GAUGE);
/* #endif HAVE_LIBKVM_GETSWAPINFO */
#elif HAVE_LIBSTATGRAB
if (swap == NULL)
return (-1);
- swap_submit ("used", swap->used);
- swap_submit ("free", swap->free);
+ swap_submit ("used", (derive_t) swap->used, DS_TYPE_GAUGE);
+ swap_submit ("free", (derive_t) swap->free, DS_TYPE_GAUGE);
#endif /* HAVE_LIBSTATGRAB */
return (0);
assert (res->values[i] < fields_num);
value = fields[res->values[i]];
- if (0 != parse_value (value, &values[i], res->ds->ds[i]))
+ if (0 != parse_value (value, &values[i], res->ds->ds[i].type))
return -1;
}
else
cm->flags = 0;
}
+ else if (strncasecmp ("Derive", ci->values[0].value.string, strlen ("Derive")) == 0)
+ {
+ cm->flags = UTILS_MATCH_DS_TYPE_DERIVE;
+ if (strcasecmp ("DeriveSet", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_DERIVE_SET;
+ else if (strcasecmp ("DeriveAdd", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_DERIVE_ADD;
+ else if (strcasecmp ("DeriveInc", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_DERIVE_INC;
+ else
+ cm->flags = 0;
+ }
+ else if (strncasecmp ("Absolute", ci->values[0].value.string, strlen ("Absolute")) == 0)
+ {
+ cm->flags = UTILS_MATCH_DS_TYPE_ABSOLUTE;
+ if (strcasecmp ("AbsoluteSet", ci->values[0].value.string) == 0)
+ cm->flags |= UTILS_MATCH_CF_ABSOLUTE_SET;
+ else
+ cm->flags = 0;
+ }
else
{
cm->flags = 0;
--- /dev/null
+/**
+ * collectd - src/tokyotyrant.c
+ * Copyright (C) 2009 Paul Sadauskas
+ *
+ * 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:
+ * Paul Sadauskas <psadauskas@gmail.com>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "utils_cache.h"
+#include "utils_parse_option.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <tcrdb.h>
+
+#define DEFAULT_HOST "127.0.0.1"
+#define DEFAULT_PORT 1978
+
+static const char *config_keys[] =
+{
+ "Host",
+ "Port"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static char *config_host = NULL;
+static char *config_port = NULL;
+
+static TCRDB *rdb = NULL;
+
+static int parse_service_name (const char *service_name)
+{
+ struct addrinfo *ai_list;
+ struct addrinfo *ai_ptr;
+ struct addrinfo ai_hints;
+ int status;
+ int service_number;
+
+ ai_list = NULL;
+ memset (&ai_hints, 0, sizeof (ai_hints));
+ ai_hints.ai_family = AF_UNSPEC;
+
+ status = getaddrinfo (/* node = */ NULL, service_name,
+ &ai_hints, &ai_list);
+ if (status != 0)
+ {
+ ERROR ("tokyotyrant plugin: getaddrinfo failed: %s",
+ gai_strerror (status));
+ return (-1);
+ }
+
+ service_number = -1;
+ for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+ {
+ if (ai_ptr->ai_family == AF_INET)
+ {
+ struct sockaddr_in *sa;
+
+ sa = (void *) ai_ptr->ai_addr;
+ service_number = (int) ntohs (sa->sin_port);
+ }
+ else if (ai_ptr->ai_family == AF_INET6)
+ {
+ struct sockaddr_in6 *sa;
+
+ sa = (void *) ai_ptr->ai_addr;
+ service_number = (int) ntohs (sa->sin6_port);
+ }
+
+ if ((service_number > 0) && (service_number <= 65535))
+ break;
+ }
+
+ freeaddrinfo (ai_list);
+
+ if ((service_number > 0) && (service_number <= 65535))
+ return (service_number);
+ return (-1);
+} /* int parse_service_name */
+
+static int tt_config (const char *key, const char *value)
+{
+ if (strcasecmp ("Host", key) == 0)
+ {
+ char *temp;
+
+ temp = strdup (value);
+ if (temp == NULL)
+ {
+ ERROR("tokyotyrant plugin: Host strdup failed.");
+ return (1);
+ }
+ sfree (config_host);
+ config_host = temp;
+ }
+ else if (strcasecmp ("Port", key) == 0)
+ {
+ char *temp;
+
+ temp = strdup (value);
+ if (temp == NULL)
+ {
+ ERROR("tokyotyrant plugin: Port strdup failed.");
+ return (1);
+ }
+ sfree (config_port);
+ config_port = temp;
+ }
+ else
+ {
+ ERROR ("tokyotyrant plugin: error: unrecognized configuration key %s", key);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void printerr()
+{
+ int ecode = tcrdbecode(rdb);
+ ERROR ("tokyotyrant plugin: error: %d, %s",
+ ecode, tcrdberrmsg(ecode));
+}
+
+static void tt_submit (gauge_t val, const char* type)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].gauge = val;
+
+ vl.values = values;
+ vl.values_len = STATIC_ARRAY_SIZE (values);
+
+ sstrncpy (vl.host, config_host, sizeof (vl.host));
+ sstrncpy (vl.plugin, "tokyotyrant", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, config_port,
+ sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+
+ plugin_dispatch_values (&vl);
+}
+
+static void tt_open_db (void)
+{
+ char* host = NULL;
+ int port = DEFAULT_PORT;
+
+ if (rdb != NULL)
+ return;
+
+ host = ((config_host != NULL) ? config_host : DEFAULT_HOST);
+
+ if (config_port != NULL)
+ {
+ port = parse_service_name (config_port);
+ if (port <= 0)
+ return;
+ }
+
+ rdb = tcrdbnew ();
+ if (rdb == NULL)
+ return;
+ else if (!tcrdbopen(rdb, host, port))
+ {
+ printerr ();
+ tcrdbdel (rdb);
+ rdb = NULL;
+ }
+} /* void tt_open_db */
+
+static int tt_read (void) {
+ gauge_t rnum, size;
+
+ tt_open_db ();
+ if (rdb == NULL)
+ return (-1);
+
+ rnum = tcrdbrnum(rdb);
+ tt_submit (rnum, "records");
+
+ size = tcrdbsize(rdb);
+ tt_submit (size, "file_size");
+
+ return (0);
+}
+
+static int tt_shutdown(void)
+{
+ sfree(config_host);
+ sfree(config_port);
+
+ if (rdb != NULL)
+ {
+ if (!tcrdbclose(rdb))
+ {
+ printerr ();
+ tcrdbdel (rdb);
+ return (1);
+ }
+ tcrdbdel (rdb);
+ rdb = NULL;
+ }
+
+ return(0);
+}
+
+void module_register (void)
+{
+ plugin_register_config("tokyotyrant", tt_config,
+ config_keys, config_keys_num);
+ plugin_register_read("tokyotyrant", tt_read);
+ plugin_register_shutdown("tokyotyrant", tt_shutdown);
+}
+
+/* vim: set sw=8 ts=8 tw=78 : */
+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
+ath_nodes value:GAUGE:0:65535
+ath_stat value:COUNTER:0:4294967295
bitrate value:GAUGE:0:4294967295
bytes value:GAUGE:0:U
cache_result value:COUNTER:0:4294967295
cpu value:COUNTER:0:4294967295
current value:GAUGE:U:U
delay seconds:GAUGE:-1000000:1000000
+derive value:DERIVE:0:U
df used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623
disk_merged read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
disk_octets read:COUNTER:0:17179869183, write:COUNTER:0:17179869183
dns_notify value:COUNTER:0:65535
dns_octets queries:COUNTER:0:125000000, responses:COUNTER:0:125000000
dns_opcode value:COUNTER:0:65535
-dns_qtype value:COUNTER:0:65535
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
email_size value:GAUGE:0:U
entropy entropy:GAUGE:0:4294967295
fanspeed value:GAUGE:0:U
+file_size bytes: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
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
humidity value:GAUGE:0:100
if_collisions value:COUNTER:0:4294967295
if_dropped 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
+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
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_connections value: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
nfs_procedure value:COUNTER:0:4294967295
-fscache_stat value:COUNTER:0:4294967295
nginx_connections value:GAUGE:0:U
nginx_requests value:COUNTER:0:134217728
+node_octets rx:COUNTER:0:U, tx:COUNTER:0:U
+node_rssi value:GAUGE:0:255
+node_stat value:COUNTER:0:4294967295
+node_tx_rate value:GAUGE:0:127
operations value:COUNTER:0:4294967295
percent percent:GAUGE:0:100.1
pg_blks value:COUNTER:0:U
pg_numbackends value:GAUGE:0:U
pg_scan value:COUNTER:0:U
pg_xact value:COUNTER:0:U
-ping ping:GAUGE:0:65535
ping_droprate value:GAUGE:0:100
+ping ping:GAUGE:0:65535
ping_stddev value:GAUGE:0:65535
players value:GAUGE:0:1000000
power value:GAUGE:0:U
ps_state value:GAUGE:0:65535
ps_vm value:GAUGE:0:9223372036854775807
queue_length 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
signal_noise value:GAUGE:U:0
signal_power value:GAUGE:U:0
signal_quality value:GAUGE:0:U
spam_check value:GAUGE:0:U
spam_score value:GAUGE:U:U
+swap_io value:DERIVE:0:1099511627776
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
+total_requests value:DERIVE:0:U
+total_time_in_ms value:DERIVE:0:U
uptime value:GAUGE:0:4294967295
users users:GAUGE:0:65535
virt_cpu_total ns:COUNTER:0:256000000000
dsrc->type = DS_TYPE_GAUGE;
else if (strcasecmp (fields[1], "COUNTER") == 0)
dsrc->type = DS_TYPE_COUNTER;
+ else if (strcasecmp (fields[1], "DERIVE") == 0)
+ dsrc->type = DS_TYPE_DERIVE;
+ else if (strcasecmp (fields[1], "ABSOLUTE") == 0)
+ dsrc->type = DS_TYPE_ABSOLUTE;
else
{
- ERROR ("(fields[1] = %s) != (GAUGE || COUNTER)", fields[1]);
+ ERROR ("(fields[1] = %s) != (GAUGE || COUNTER || DERIVE || ABSOLUTE)", fields[1]);
return (-1);
}
#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"
{
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);
#include "utils_avltree.h"
#include "utils_cache.h"
#include "utils_threshold.h"
+#include "meta_data.h"
#include <assert.h>
#include <pthread.h>
char name[6 * DATA_MAX_NAME_LEN];
int values_num;
gauge_t *values_gauge;
- counter_t *values_counter;
+ value_t *values_raw;
/* Time contained in the package
* (for calculating rates) */
time_t last_time;
* (for purding old entries) */
int interval;
int state;
+ int hits;
+
+ /*
+ * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+ * ! 0 ! 1 ! 2 ! 3 ! 4 ! 5 ! 6 ! 7 ! 8 ! ...
+ * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+ * ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ds0 ! ds1 ! ds2 ! ...
+ * +-----+-----+-----+-----+-----+-----+-----+-----+-----+----
+ * ! t = 0 ! t = 1 ! t = 2 ! ...
+ * +-----------------+-----------------+-----------------+----
+ */
+ gauge_t *history;
+ size_t history_index; /* points to the next position to write to. */
+ size_t history_length;
+
+ meta_data_t *meta;
} cache_entry_t;
static c_avl_tree_t *cache_tree = NULL;
memset (ce, '\0', sizeof (cache_entry_t));
ce->values_num = values_num;
- ce->values_gauge = (gauge_t *) calloc (values_num, sizeof (gauge_t));
- ce->values_counter = (counter_t *) calloc (values_num, sizeof (counter_t));
- if ((ce->values_gauge == NULL) || (ce->values_counter == NULL))
+ ce->values_gauge = calloc (values_num, sizeof (*ce->values_gauge));
+ ce->values_raw = calloc (values_num, sizeof (*ce->values_raw));
+ if ((ce->values_gauge == NULL) || (ce->values_raw == NULL))
{
sfree (ce->values_gauge);
- sfree (ce->values_counter);
+ sfree (ce->values_raw);
sfree (ce);
ERROR ("utils_cache: cache_alloc: calloc failed.");
return (NULL);
}
+ ce->history = NULL;
+ ce->history_length = 0;
+ ce->meta = NULL;
+
return (ce);
} /* cache_entry_t *cache_alloc */
return;
sfree (ce->values_gauge);
- sfree (ce->values_counter);
+ sfree (ce->values_raw);
+ sfree (ce->history);
+ if (ce->meta != NULL)
+ {
+ meta_data_destroy (ce->meta);
+ ce->meta = NULL;
+ }
sfree (ce);
} /* void cache_free */
return (0);
} /* int uc_send_notification */
+static void uc_check_range (const data_set_t *ds, cache_entry_t *ce)
+{
+ int i;
+
+ for (i = 0; i < ds->ds_num; i++)
+ {
+ if (isnan (ce->values_gauge[i]))
+ continue;
+ else if (ce->values_gauge[i] < ds->ds[i].min)
+ ce->values_gauge[i] = NAN;
+ else if (ce->values_gauge[i] > ds->ds[i].max)
+ ce->values_gauge[i] = NAN;
+ }
+} /* void uc_check_range */
+
static int uc_insert (const data_set_t *ds, const value_list_t *vl,
const char *key)
{
for (i = 0; i < ds->ds_num; i++)
{
- if (ds->ds[i].type == DS_TYPE_COUNTER)
- {
- ce->values_gauge[i] = NAN;
- ce->values_counter[i] = vl->values[i].counter;
- }
- else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
+ switch (ds->ds[i].type)
{
- ce->values_gauge[i] = vl->values[i].gauge;
- }
+ case DS_TYPE_COUNTER:
+ ce->values_gauge[i] = NAN;
+ ce->values_raw[i].counter = vl->values[i].counter;
+ break;
+
+ case DS_TYPE_GAUGE:
+ ce->values_gauge[i] = vl->values[i].gauge;
+ ce->values_raw[i].gauge = vl->values[i].gauge;
+ break;
+
+ case DS_TYPE_DERIVE:
+ ce->values_gauge[i] = NAN;
+ ce->values_raw[i].derive = vl->values[i].derive;
+ break;
+
+ case DS_TYPE_ABSOLUTE:
+ ce->values_gauge[i] = NAN;
+ if (vl->interval > 0)
+ ce->values_gauge[i] = ((double) vl->values[i].absolute)
+ / ((double) vl->interval);
+ ce->values_raw[i].absolute = vl->values[i].absolute;
+ break;
+
+ default:
+ /* This shouldn't happen. */
+ ERROR ("uc_insert: Don't know how to handle data source type %i.",
+ ds->ds[i].type);
+ return (-1);
+ } /* switch (ds->ds[i].type) */
} /* for (i) */
+ /* Prune invalid gauge data */
+ uc_check_range (ds, ce);
+
ce->last_time = vl->time;
ce->last_update = time (NULL);
ce->interval = vl->interval;
int status;
int i;
- if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+ if (FORMAT_VL (name, sizeof (name), vl) != 0)
{
ERROR ("uc_update: FORMAT_VL failed.");
return (-1);
for (i = 0; i < ds->ds_num; i++)
{
- if (ds->ds[i].type == DS_TYPE_COUNTER)
+ switch (ds->ds[i].type)
{
- counter_t diff;
+ case DS_TYPE_COUNTER:
+ {
+ counter_t diff;
+
+ /* check if the counter has wrapped around */
+ if (vl->values[i].counter < ce->values_raw[i].counter)
+ {
+ if (ce->values_raw[i].counter <= 4294967295U)
+ diff = (4294967295U - ce->values_raw[i].counter)
+ + vl->values[i].counter;
+ else
+ diff = (18446744073709551615ULL - ce->values_raw[i].counter)
+ + vl->values[i].counter;
+ }
+ else /* counter has NOT wrapped around */
+ {
+ diff = vl->values[i].counter - ce->values_raw[i].counter;
+ }
+
+ ce->values_gauge[i] = ((double) diff)
+ / ((double) (vl->time - ce->last_time));
+ ce->values_raw[i].counter = vl->values[i].counter;
+ }
+ break;
- /* check if the counter has wrapped around */
- if (vl->values[i].counter < ce->values_counter[i])
- {
- if (ce->values_counter[i] <= 4294967295U)
- diff = (4294967295U - ce->values_counter[i])
- + vl->values[i].counter;
- else
- diff = (18446744073709551615ULL - ce->values_counter[i])
- + vl->values[i].counter;
- }
- else /* counter has NOT wrapped around */
- {
- diff = vl->values[i].counter - ce->values_counter[i];
- }
+ case DS_TYPE_GAUGE:
+ ce->values_raw[i].gauge = vl->values[i].gauge;
+ ce->values_gauge[i] = vl->values[i].gauge;
+ break;
+
+ case DS_TYPE_DERIVE:
+ {
+ derive_t diff;
+
+ diff = vl->values[i].derive - ce->values_raw[i].derive;
+
+ ce->values_gauge[i] = ((double) diff)
+ / ((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));
+ ce->values_raw[i].absolute = vl->values[i].absolute;
+ break;
+
+ default:
+ /* This shouldn't happen. */
+ pthread_mutex_unlock (&cache_lock);
+ ERROR ("uc_update: Don't know how to handle data source type %i.",
+ ds->ds[i].type);
+ return (-1);
+ } /* switch (ds->ds[i].type) */
- ce->values_gauge[i] = ((double) diff)
- / ((double) (vl->time - ce->last_time));
- ce->values_counter[i] = vl->values[i].counter;
- }
- else /* if (ds->ds[i].type == DS_TYPE_GAUGE) */
- {
- ce->values_gauge[i] = vl->values[i].gauge;
- }
DEBUG ("uc_update: %s: ds[%i] = %lf", name, i, ce->values_gauge[i]);
} /* for (i) */
+ /* Update the history if it exists. */
+ if (ce->history != NULL)
+ {
+ assert (ce->history_index < ce->history_length);
+ for (i = 0; i < ce->values_num; i++)
+ {
+ size_t hist_idx = (ce->values_num * ce->history_index) + i;
+ ce->history[hist_idx] = ce->values_gauge[i];
+ }
+
+ assert (ce->history_length > 0);
+ ce->history_index = (ce->history_index + 1) % ce->history_length;
+ }
+
+ /* Prune invalid gauge data */
+ uc_check_range (ds, ce);
+
ce->last_time = vl->time;
ce->last_update = time (NULL);
ce->interval = vl->interval;
size_t ret_num = 0;
int status;
- if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+ if (FORMAT_VL (name, sizeof (name), vl) != 0)
{
ERROR ("utils_cache: uc_get_rate: FORMAT_VL failed.");
return (NULL);
cache_entry_t *ce = NULL;
int ret = STATE_ERROR;
- if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+ if (FORMAT_VL (name, sizeof (name), vl) != 0)
{
ERROR ("uc_get_state: FORMAT_VL failed.");
return (STATE_ERROR);
cache_entry_t *ce = NULL;
int ret = -1;
- if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+ if (FORMAT_VL (name, sizeof (name), vl) != 0)
{
ERROR ("uc_get_state: FORMAT_VL failed.");
return (STATE_ERROR);
return (ret);
} /* int uc_set_state */
+
+int uc_get_history_by_name (const char *name,
+ gauge_t *ret_history, size_t num_steps, size_t num_ds)
+{
+ cache_entry_t *ce = NULL;
+ size_t i;
+ int status = 0;
+
+ pthread_mutex_lock (&cache_lock);
+
+ status = c_avl_get (cache_tree, name, (void *) &ce);
+ if (status != 0)
+ {
+ pthread_mutex_unlock (&cache_lock);
+ return (-ENOENT);
+ }
+
+ if (((size_t) ce->values_num) != num_ds)
+ {
+ pthread_mutex_unlock (&cache_lock);
+ return (-EINVAL);
+ }
+
+ /* Check if there are enough values available. If not, increase the buffer
+ * size. */
+ if (ce->history_length < num_steps)
+ {
+ gauge_t *tmp;
+ size_t i;
+
+ tmp = realloc (ce->history, sizeof (*ce->history)
+ * num_steps * ce->values_num);
+ if (tmp == NULL)
+ {
+ pthread_mutex_unlock (&cache_lock);
+ return (-ENOMEM);
+ }
+
+ for (i = ce->history_length * ce->values_num;
+ i < (num_steps * ce->values_num);
+ i++)
+ tmp[i] = NAN;
+
+ ce->history = tmp;
+ ce->history_length = num_steps;
+ } /* if (ce->history_length < num_steps) */
+
+ /* Copy the values to the output buffer. */
+ for (i = 0; i < num_steps; i++)
+ {
+ size_t src_index;
+ size_t dst_index;
+
+ if (i < ce->history_index)
+ src_index = ce->history_index - (i + 1);
+ else
+ src_index = ce->history_length + ce->history_index - (i + 1);
+ src_index = src_index * num_ds;
+
+ dst_index = i * num_ds;
+
+ memcpy (ret_history + dst_index, ce->history + src_index,
+ sizeof (*ret_history) * num_ds);
+ }
+
+ pthread_mutex_unlock (&cache_lock);
+
+ return (0);
+} /* int uc_get_history_by_name */
+
+int uc_get_history (const data_set_t *ds, const value_list_t *vl,
+ gauge_t *ret_history, size_t num_steps, size_t num_ds)
+{
+ char name[6 * DATA_MAX_NAME_LEN];
+
+ if (FORMAT_VL (name, sizeof (name), vl) != 0)
+ {
+ ERROR ("utils_cache: uc_get_history: FORMAT_VL failed.");
+ return (-1);
+ }
+
+ return (uc_get_history_by_name (name, ret_history, num_steps, num_ds));
+} /* int uc_get_history */
+
+int uc_get_hits (const data_set_t *ds, const value_list_t *vl)
+{
+ char name[6 * DATA_MAX_NAME_LEN];
+ cache_entry_t *ce = NULL;
+ int ret = STATE_ERROR;
+
+ if (FORMAT_VL (name, sizeof (name), vl) != 0)
+ {
+ ERROR ("uc_get_state: FORMAT_VL failed.");
+ return (STATE_ERROR);
+ }
+
+ pthread_mutex_lock (&cache_lock);
+
+ if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+ {
+ assert (ce != NULL);
+ ret = ce->hits;
+ }
+
+ pthread_mutex_unlock (&cache_lock);
+
+ return (ret);
+} /* int uc_get_hits */
+
+int uc_set_hits (const data_set_t *ds, const value_list_t *vl, int hits)
+{
+ char name[6 * DATA_MAX_NAME_LEN];
+ cache_entry_t *ce = NULL;
+ int ret = -1;
+
+ if (FORMAT_VL (name, sizeof (name), vl) != 0)
+ {
+ ERROR ("uc_get_state: FORMAT_VL failed.");
+ return (STATE_ERROR);
+ }
+
+ pthread_mutex_lock (&cache_lock);
+
+ if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+ {
+ assert (ce != NULL);
+ ret = ce->hits;
+ ce->hits = hits;
+ }
+
+ pthread_mutex_unlock (&cache_lock);
+
+ return (ret);
+} /* int uc_set_hits */
+
+int uc_inc_hits (const data_set_t *ds, const value_list_t *vl, int step)
+{
+ char name[6 * DATA_MAX_NAME_LEN];
+ cache_entry_t *ce = NULL;
+ int ret = -1;
+
+ if (FORMAT_VL (name, sizeof (name), vl) != 0)
+ {
+ ERROR ("uc_get_state: FORMAT_VL failed.");
+ return (STATE_ERROR);
+ }
+
+ pthread_mutex_lock (&cache_lock);
+
+ if (c_avl_get (cache_tree, name, (void *) &ce) == 0)
+ {
+ assert (ce != NULL);
+ ret = ce->hits;
+ ce->hits = ret + step;
+ }
+
+ pthread_mutex_unlock (&cache_lock);
+
+ return (ret);
+} /* int uc_inc_hits */
+
+/*
+ * Meta data interface
+ */
+/* XXX: This function will acquire `cache_lock' but will not free it! */
+static meta_data_t *uc_get_meta (const value_list_t *vl) /* {{{ */
+{
+ char name[6 * DATA_MAX_NAME_LEN];
+ cache_entry_t *ce = NULL;
+ int status;
+
+ status = FORMAT_VL (name, sizeof (name), vl);
+ if (status != 0)
+ {
+ ERROR ("utils_cache: uc_get_meta: FORMAT_VL failed.");
+ return (NULL);
+ }
+
+ pthread_mutex_lock (&cache_lock);
+
+ status = c_avl_get (cache_tree, name, (void *) &ce);
+ if (status != 0)
+ {
+ pthread_mutex_unlock (&cache_lock);
+ return (NULL);
+ }
+ assert (ce != NULL);
+
+ if (ce->meta == NULL)
+ ce->meta = meta_data_create ();
+
+ if (ce->meta == NULL)
+ pthread_mutex_unlock (&cache_lock);
+
+ return (ce->meta);
+} /* }}} meta_data_t *uc_get_meta */
+
+/* Sorry about this preprocessor magic, but it really makes this file much
+ * shorter.. */
+#define UC_WRAP(wrap_function) { \
+ meta_data_t *meta; \
+ int status; \
+ meta = uc_get_meta (vl); \
+ if (meta == NULL) return (-1); \
+ status = wrap_function (meta, key); \
+ pthread_mutex_unlock (&cache_lock); \
+ return (status); \
+}
+int uc_meta_data_exists (const value_list_t *vl, const char *key)
+ UC_WRAP (meta_data_exists)
+
+int uc_meta_data_delete (const value_list_t *vl, const char *key)
+ UC_WRAP (meta_data_delete)
+#undef UC_WRAP
+
+/* We need a new version of this macro because the following functions take
+ * two argumetns. */
+#define UC_WRAP(wrap_function) { \
+ meta_data_t *meta; \
+ int status; \
+ meta = uc_get_meta (vl); \
+ if (meta == NULL) return (-1); \
+ status = wrap_function (meta, key, value); \
+ pthread_mutex_unlock (&cache_lock); \
+ return (status); \
+}
+int uc_meta_data_add_string (const value_list_t *vl,
+ const char *key,
+ const char *value)
+ UC_WRAP(meta_data_add_string)
+int uc_meta_data_add_signed_int (const value_list_t *vl,
+ const char *key,
+ int64_t value)
+ UC_WRAP(meta_data_add_signed_int)
+int uc_meta_data_add_unsigned_int (const value_list_t *vl,
+ const char *key,
+ uint64_t value)
+ UC_WRAP(meta_data_add_unsigned_int)
+int uc_meta_data_add_double (const value_list_t *vl,
+ const char *key,
+ double value)
+ UC_WRAP(meta_data_add_double)
+int uc_meta_data_add_boolean (const value_list_t *vl,
+ const char *key,
+ _Bool value)
+ UC_WRAP(meta_data_add_boolean)
+
+int uc_meta_data_get_string (const value_list_t *vl,
+ const char *key,
+ char **value)
+ UC_WRAP(meta_data_get_string)
+int uc_meta_data_get_signed_int (const value_list_t *vl,
+ const char *key,
+ int64_t *value)
+ UC_WRAP(meta_data_get_signed_int)
+int uc_meta_data_get_unsigned_int (const value_list_t *vl,
+ const char *key,
+ uint64_t *value)
+ UC_WRAP(meta_data_get_unsigned_int)
+int uc_meta_data_get_double (const value_list_t *vl,
+ const char *key,
+ double *value)
+ UC_WRAP(meta_data_get_double)
+int uc_meta_data_get_boolean (const value_list_t *vl,
+ const char *key,
+ _Bool *value)
+ UC_WRAP(meta_data_get_boolean)
+#undef UC_WRAP
+
/* vim: set sw=2 ts=8 sts=2 tw=78 : */
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 uc_get_hits (const data_set_t *ds, const value_list_t *vl);
+int uc_set_hits (const data_set_t *ds, const value_list_t *vl, int hits);
+int uc_inc_hits (const data_set_t *ds, const value_list_t *vl, int step);
+
+int uc_get_history (const data_set_t *ds, const value_list_t *vl,
+ gauge_t *ret_history, size_t num_steps, size_t num_ds);
+int uc_get_history_by_name (const char *name,
+ gauge_t *ret_history, size_t num_steps, size_t num_ds);
+
+/*
+ * Meta data interface
+ */
+int uc_meta_data_exists (const value_list_t *vl, const char *key);
+int uc_meta_data_delete (const value_list_t *vl, const char *key);
+
+int uc_meta_data_add_string (const value_list_t *vl,
+ const char *key,
+ const char *value);
+int uc_meta_data_add_signed_int (const value_list_t *vl,
+ const char *key,
+ int64_t value);
+int uc_meta_data_add_unsigned_int (const value_list_t *vl,
+ const char *key,
+ uint64_t value);
+int uc_meta_data_add_double (const value_list_t *vl,
+ const char *key,
+ double value);
+int uc_meta_data_add_boolean (const value_list_t *vl,
+ const char *key,
+ _Bool value);
+
+int uc_meta_data_get_string (const value_list_t *vl,
+ const char *key,
+ char **value);
+int uc_meta_data_get_signed_int (const value_list_t *vl,
+ const char *key,
+ int64_t *value);
+int uc_meta_data_get_unsigned_int (const value_list_t *vl,
+ const char *key,
+ uint64_t *value);
+int uc_meta_data_get_double (const value_list_t *vl,
+ const char *key,
+ double *value);
+int uc_meta_data_get_boolean (const value_list_t *vl,
+ const char *key,
+ _Bool *value);
/* vim: set shiftwidth=2 softtabstop=2 tabstop=8 : */
#endif /* !UTILS_CACHE_H */
--- /dev/null
+/**
+ * collectd - src/utils_cms_getthreshold.c
+ * Copyright (C) 2008,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>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include "utils_threshold.h"
+#include "utils_parse_option.h" /* for `parse_string' */
+#include "utils_cmd_getthreshold.h"
+
+#define print_to_socket(fh, ...) \
+ if (fprintf (fh, __VA_ARGS__) < 0) { \
+ char errbuf[1024]; \
+ WARNING ("handle_getthreshold: failed to write to socket #%i: %s", \
+ fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
+ return -1; \
+ }
+
+int handle_getthreshold (FILE *fh, char *buffer)
+{
+ char *command;
+ char *identifier;
+ char *identifier_copy;
+
+ char *host;
+ char *plugin;
+ char *plugin_instance;
+ char *type;
+ char *type_instance;
+
+ value_list_t vl;
+ threshold_t threshold;
+
+ int status;
+ size_t i;
+
+ if ((fh == NULL) || (buffer == NULL))
+ return (-1);
+
+ DEBUG ("utils_cmd_getthreshold: handle_getthreshold (fh = %p, buffer = %s);",
+ (void *) fh, buffer);
+
+ command = NULL;
+ status = parse_string (&buffer, &command);
+ if (status != 0)
+ {
+ print_to_socket (fh, "-1 Cannot parse command.\n");
+ return (-1);
+ }
+ assert (command != NULL);
+
+ if (strcasecmp ("GETTHRESHOLD", command) != 0)
+ {
+ print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
+ return (-1);
+ }
+
+ identifier = NULL;
+ status = parse_string (&buffer, &identifier);
+ if (status != 0)
+ {
+ print_to_socket (fh, "-1 Cannot parse identifier.\n");
+ return (-1);
+ }
+ assert (identifier != NULL);
+
+ if (*buffer != 0)
+ {
+ print_to_socket (fh, "-1 Garbage after end of command: %s\n", buffer);
+ return (-1);
+ }
+
+ /* parse_identifier() modifies its first argument,
+ * returning pointers into it */
+ identifier_copy = sstrdup (identifier);
+
+ status = parse_identifier (identifier_copy, &host,
+ &plugin, &plugin_instance,
+ &type, &type_instance);
+ if (status != 0)
+ {
+ DEBUG ("handle_getthreshold: Cannot parse identifier `%s'.", identifier);
+ print_to_socket (fh, "-1 Cannot parse identifier `%s'.\n", identifier);
+ sfree (identifier_copy);
+ return (-1);
+ }
+
+ 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 (vl.type, type, sizeof (vl.type));
+ if (type_instance != NULL)
+ sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+ sfree (identifier_copy);
+
+ memset (&threshold, 0, sizeof (threshold));
+ status = ut_search_threshold (&vl, &threshold);
+ if (status == ENOENT)
+ {
+ print_to_socket (fh, "-1 No threshold found for identifier %s\n",
+ identifier);
+ return (0);
+ }
+ else if (status != 0)
+ {
+ print_to_socket (fh, "-1 Error while looking up threshold: %i\n",
+ status);
+ return (-1);
+ }
+
+ /* Lets count the number of lines we'll return. */
+ i = 0;
+ if (threshold.host[0] != 0) i++;
+ if (threshold.plugin[0] != 0) i++;
+ if (threshold.plugin_instance[0] != 0) i++;
+ if (threshold.type[0] != 0) i++;
+ if (threshold.type_instance[0] != 0) i++;
+ if (threshold.data_source[0] != 0) i++;
+ if (!isnan (threshold.warning_min)) i++;
+ if (!isnan (threshold.warning_max)) i++;
+ if (!isnan (threshold.failure_min)) i++;
+ if (!isnan (threshold.failure_max)) i++;
+ if (threshold.hysteresis > 0.0) i++;
+ if (threshold.hits > 1) i++;
+
+ /* Print the response */
+ print_to_socket (fh, "%zu Threshold found\n", i);
+
+ if (threshold.host[0] != 0)
+ print_to_socket (fh, "Host: %s\n", threshold.host)
+ if (threshold.plugin[0] != 0)
+ print_to_socket (fh, "Plugin: %s\n", threshold.plugin)
+ if (threshold.plugin_instance[0] != 0)
+ print_to_socket (fh, "Plugin Instance: %s\n", threshold.plugin_instance)
+ if (threshold.type[0] != 0)
+ print_to_socket (fh, "Type: %s\n", threshold.type)
+ if (threshold.type_instance[0] != 0)
+ print_to_socket (fh, "Type Instance: %s\n", threshold.type_instance)
+ if (threshold.data_source[0] != 0)
+ print_to_socket (fh, "Data Source: %s\n", threshold.data_source)
+ if (!isnan (threshold.warning_min))
+ print_to_socket (fh, "Warning Min: %g\n", threshold.warning_min)
+ if (!isnan (threshold.warning_max))
+ print_to_socket (fh, "Warning Max: %g\n", threshold.warning_max)
+ if (!isnan (threshold.failure_min))
+ print_to_socket (fh, "Failure Min: %g\n", threshold.failure_min)
+ if (!isnan (threshold.failure_max))
+ print_to_socket (fh, "Failure Max: %g\n", threshold.failure_max)
+ if (threshold.hysteresis > 0.0)
+ print_to_socket (fh, "Hysteresis: %g\n", threshold.hysteresis)
+ if (threshold.hits > 1)
+ print_to_socket (fh, "Hits: %i\n", threshold.hits)
+
+ return (0);
+} /* int handle_getthreshold */
+
+/* vim: set sw=2 sts=2 ts=8 et : */
--- /dev/null
+/**
+ * collectd - src/utils_cmd_getthreshold.h
+ * Copyright (C) 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_CMD_GETTHRESHOLD_H
+#define UTILS_CMD_GETTHRESHOLD_H 1
+
+#include <stdio.h>
+
+int handle_getthreshold (FILE *fh, char *buffer);
+
+#endif /* UTILS_CMD_GETTHRESHOLD_H */
+
+/* vim: set sw=2 sts=2 ts=8 : */
static int dispatch_values (const data_set_t *ds, value_list_t *vl,
FILE *fh, char *buffer)
{
- char *dummy;
- char *ptr;
- char *saveptr;
- int i;
-
- char *time_str = buffer;
- char *value_str = strchr (time_str, ':');
- if (value_str == NULL)
- {
- print_to_socket (fh, "-1 No time found.\n");
- return (-1);
- }
- *value_str = '\0'; value_str++;
-
- vl->time = (time_t) atoi (time_str);
+ int status;
- i = 0;
- dummy = value_str;
- saveptr = NULL;
- while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
- {
- dummy = NULL;
-
- if (i >= vl->values_len)
- {
- i = vl->values_len + 1;
- break;
- }
-
- if ((strcmp (ptr, "U") == 0) && (ds->ds[i].type == DS_TYPE_GAUGE))
- vl->values[i].gauge = NAN;
- else if (0 != parse_value (ptr, &vl->values[i], ds->ds[i]))
- {
- print_to_socket (fh, "-1 Failed to parse value `%s'.\n", ptr);
- return (-1);
- }
-
- i++;
- } /* while (strtok_r) */
-
- if (i != vl->values_len)
+ status = parse_values (buffer, vl, ds);
+ if (status != 0)
{
- char identifier[128];
- FORMAT_VL (identifier, sizeof (identifier), vl, ds);
- ERROR ("cmd putval: dispatch_values: "
- "Number of values incorrect: "
- "Got %i, expected %i. Identifier is `%s'.",
- i, vl->values_len, identifier);
- print_to_socket (fh, "-1 Number of values incorrect: "
- "Got %i, expected %i.\n",
- i, vl->values_len);
+ print_to_socket (fh, "-1 Parsing the values string failed.\n");
return (-1);
}
vl.values_len = 1;
value_str = column_values[r->legacy_position];
- if (0 != parse_value (value_str, &vl.values[0], r->ds->ds[0]))
+ 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,
- (r->ds->ds[0].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
+ DS_TYPE_TO_STRING (r->ds->ds[0].type));
errno = EINVAL;
return (-1);
}
{
char *value_str = r->values_buffer[i];
- if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i]))
+ if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i].type))
{
ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
- value_str,
- (r->ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
+ value_str, DS_TYPE_TO_STRING (r->ds->ds[i].type));
errno = EINVAL;
return (-1);
}
--- /dev/null
+/**
+ * collectd - src/utils_format_json.c
+ * Copyright (C) 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
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+
+#include "utils_format_json.h"
+
+static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
+ const char *string)
+{
+ size_t src_pos;
+ size_t dst_pos;
+
+ if ((buffer == NULL) || (string == NULL))
+ return (-EINVAL);
+
+ if (buffer_size < 3)
+ return (-ENOMEM);
+
+ dst_pos = 0;
+
+#define BUFFER_ADD(c) do { \
+ if (dst_pos >= (buffer_size - 1)) { \
+ buffer[buffer_size - 1] = 0; \
+ return (-ENOMEM); \
+ } \
+ buffer[dst_pos] = (c); \
+ dst_pos++; \
+} while (0)
+
+ /* Escape special characters */
+ BUFFER_ADD ('"');
+ for (src_pos = 0; string[src_pos] != 0; src_pos++)
+ {
+ if ((string[src_pos] == '"')
+ || (string[src_pos] == '\\'))
+ {
+ BUFFER_ADD ('\\');
+ BUFFER_ADD (string[src_pos]);
+ }
+ else if (string[src_pos] <= 0x001F)
+ BUFFER_ADD ('?');
+ else
+ BUFFER_ADD (string[src_pos]);
+ } /* for */
+ BUFFER_ADD ('"');
+ buffer[dst_pos] = 0;
+
+#undef BUFFER_ADD
+
+ return (0);
+} /* }}} 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)
+{
+ 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 (",");
+
+ if (ds->ds[i].type == DS_TYPE_GAUGE)
+ BUFFER_ADD ("%g", 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 ("format_json: Unknown data source type: %i",
+ ds->ds[i].type);
+ return (-1);
+ }
+ } /* for ds->ds_num */
+ BUFFER_ADD ("]");
+
+#undef BUFFER_ADD
+
+ DEBUG ("format_json: values_to_json: buffer = %s;", buffer);
+
+ return (0);
+} /* }}} int values_to_json */
+
+static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl)
+{
+ char temp[512];
+ size_t offset = 0;
+ int status;
+
+ 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 (-ENOMEM); \
+ else \
+ offset += ((size_t) status); \
+} while (0)
+
+ /* All value lists have a leading comma. The first one will be replaced with
+ * a square bracket in `format_json_finalize'. */
+ BUFFER_ADD (",{");
+
+ status = values_to_json (temp, sizeof (temp), ds, vl);
+ if (status != 0)
+ return (status);
+ BUFFER_ADD ("\"values\":%s", temp);
+
+ BUFFER_ADD (",\"time\":%lu", (unsigned long) vl->time);
+ BUFFER_ADD (",\"interval\":%i", vl->interval);
+
+#define BUFFER_ADD_KEYVAL(key, value) do { \
+ status = escape_string (temp, sizeof (temp), (value)); \
+ if (status != 0) \
+ return (status); \
+ BUFFER_ADD (",\"%s\":%s", (key), temp); \
+} while (0)
+
+ BUFFER_ADD_KEYVAL ("host", vl->host);
+ BUFFER_ADD_KEYVAL ("plugin", vl->plugin);
+ BUFFER_ADD_KEYVAL ("plugin_instance", vl->plugin_instance);
+ BUFFER_ADD_KEYVAL ("type", vl->type);
+ BUFFER_ADD_KEYVAL ("type_instance", vl->type_instance);
+
+ BUFFER_ADD ("}");
+
+#undef BUFFER_ADD_KEYVAL
+#undef BUFFER_ADD
+
+ DEBUG ("format_json: value_list_to_json: buffer = %s;", buffer);
+
+ return (0);
+} /* }}} int value_list_to_json */
+
+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)
+{
+ char temp[temp_size];
+ int status;
+
+ status = value_list_to_json (temp, sizeof (temp), ds, vl);
+ if (status != 0)
+ return (status);
+ temp_size = strlen (temp);
+
+ memcpy (buffer + (*ret_buffer_fill), temp, temp_size + 1);
+ (*ret_buffer_fill) += temp_size;
+ (*ret_buffer_free) -= temp_size;
+
+ return (0);
+} /* }}} int format_json_value_list_nocheck */
+
+int format_json_initialize (char *buffer, /* {{{ */
+ size_t *ret_buffer_fill, size_t *ret_buffer_free)
+{
+ size_t buffer_fill;
+ size_t buffer_free;
+
+ if ((buffer == NULL) || (ret_buffer_fill == NULL) || (ret_buffer_free == NULL))
+ return (-EINVAL);
+
+ buffer_fill = *ret_buffer_fill;
+ buffer_free = *ret_buffer_free;
+
+ buffer_free = buffer_fill + buffer_free;
+ buffer_fill = 0;
+
+ if (buffer_free < 3)
+ return (-ENOMEM);
+
+ memset (buffer, 0, buffer_free);
+ *ret_buffer_fill = buffer_fill;
+ *ret_buffer_free = buffer_free;
+
+ return (0);
+} /* }}} int format_json_initialize */
+
+int format_json_finalize (char *buffer, /* {{{ */
+ size_t *ret_buffer_fill, size_t *ret_buffer_free)
+{
+ size_t pos;
+
+ if ((buffer == NULL) || (ret_buffer_fill == NULL) || (ret_buffer_free == NULL))
+ return (-EINVAL);
+
+ if (*ret_buffer_free < 2)
+ return (-ENOMEM);
+
+ /* Replace the leading comma added in `value_list_to_json' with a square
+ * bracket. */
+ if (buffer[0] != ',')
+ return (-EINVAL);
+ buffer[0] = '[';
+
+ pos = *ret_buffer_fill;
+ buffer[pos] = ']';
+ buffer[pos+1] = 0;
+
+ (*ret_buffer_fill)++;
+ (*ret_buffer_free)--;
+
+ return (0);
+} /* }}} int format_json_finalize */
+
+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)
+{
+ if ((buffer == NULL)
+ || (ret_buffer_fill == NULL) || (ret_buffer_free == NULL)
+ || (ds == NULL) || (vl == NULL))
+ return (-EINVAL);
+
+ if (*ret_buffer_free < 3)
+ return (-ENOMEM);
+
+ return (format_json_value_list_nocheck (buffer,
+ ret_buffer_fill, ret_buffer_free, ds, vl,
+ (*ret_buffer_free) - 2));
+} /* }}} int format_json_value_list */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
--- /dev/null
+/**
+ * collectd - src/utils_format_json.c
+ * Copyright (C) 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
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ **/
+
+#ifndef UTILS_FORMAT_JSON_H
+#define UTILS_FORMAT_JSON_H 1
+
+#include "collectd.h"
+#include "plugin.h"
+
+int format_json_initialize (char *buffer,
+ 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);
+int format_json_finalize (char *buffer,
+ size_t *ret_buffer_fill, size_t *ret_buffer_free);
+
+#endif /* UTILS_FORMAT_JSON_H */
data->values_num++;
}
+ else if (data->ds_type & UTILS_MATCH_DS_TYPE_DERIVE)
+ {
+ derive_t value;
+ char *endptr = NULL;
+
+ if (data->ds_type & UTILS_MATCH_CF_DERIVE_INC)
+ {
+ data->value.counter++;
+ data->values_num++;
+ return (0);
+ }
+
+ if (matches_num < 2)
+ return (-1);
+
+ value = strtoll (matches[1], &endptr, 0);
+ if (matches[1] == endptr)
+ return (-1);
+
+ if (data->ds_type & UTILS_MATCH_CF_DERIVE_SET)
+ data->value.derive = value;
+ else if (data->ds_type & UTILS_MATCH_CF_DERIVE_ADD)
+ data->value.derive += value;
+ else
+ {
+ ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ data->values_num++;
+ }
+ else if (data->ds_type & UTILS_MATCH_DS_TYPE_ABSOLUTE)
+ {
+ absolute_t value;
+ char *endptr = NULL;
+
+ if (matches_num < 2)
+ return (-1);
+
+ value = strtoll (matches[1], &endptr, 0);
+ if (matches[1] == endptr)
+ return (-1);
+
+ if (data->ds_type & UTILS_MATCH_CF_ABSOLUTE_SET)
+ data->value.absolute = value;
+ else
+ {
+ ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
+ return (-1);
+ }
+
+ data->values_num++;
+ }
else
{
ERROR ("utils_match: default_callback: obj->ds_type is invalid!");
*/
#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_CF_GAUGE_AVERAGE 0x01
#define UTILS_MATCH_CF_GAUGE_MIN 0x02
#define UTILS_MATCH_CF_COUNTER_ADD 0x02
#define UTILS_MATCH_CF_COUNTER_INC 0x04
+#define UTILS_MATCH_CF_DERIVE_SET 0x01
+#define UTILS_MATCH_CF_DERIVE_ADD 0x02
+#define UTILS_MATCH_CF_DERIVE_INC 0x04
+
+#define UTILS_MATCH_CF_ABSOLUTE_SET 0x01
+#define UTILS_MATCH_CF_ABSOLUTE_ADD 0x02
+#define UTILS_MATCH_CF_ABSOLUTE_INC 0x04
+
/*
* Data types
*/
type = "COUNTER";
else if (d->type == DS_TYPE_GAUGE)
type = "GAUGE";
+ else if (d->type == DS_TYPE_DERIVE)
+ type = "DERIVE";
+ else if (d->type == DS_TYPE_ABSOLUTE)
+ type = "ABSOLUTE";
else
{
ERROR ("rrdtool plugin: Unknown DS type: %i",
sstrncpy (min, "U", sizeof (min));
}
else
- ssnprintf (min, sizeof (min), "%lf", d->min);
+ ssnprintf (min, sizeof (min), "%f", d->min);
if (isnan (d->max))
{
sstrncpy (max, "U", sizeof (max));
}
else
- ssnprintf (max, sizeof (max), "%lf", d->max);
+ ssnprintf (max, sizeof (max), "%f", d->max);
status = ssnprintf (buffer, sizeof (buffer),
"DS:%s:%s:%i:%s:%s",
/**
* collectd - src/utils_threshold.c
- * Copyright (C) 2007,2008 Florian octo Forster
+ * 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
*
* 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 "plugin.h"
#include "utils_avltree.h"
#include "utils_cache.h"
+#include "utils_threshold.h"
#include <assert.h>
#include <pthread.h>
* {{{ */
#define UT_FLAG_INVERT 0x01
#define UT_FLAG_PERSIST 0x02
-
-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;
- int flags;
- struct threshold_s *next;
-} threshold_t;
+#define UT_FLAG_PERCENTAGE 0x04
/* }}} */
/*
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;
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++)
{
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' "
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++)
{
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
{
if (!isnan (min) && !isnan (max))
{
- status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
- "%f. That is within the %s region of %f and %f.",
- ds->ds[ds_index].name, values[ds_index],
- (state == STATE_ERROR) ? "failure" : "warning",
- min, 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.",
+ "%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);
+ 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.",
+ "%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);
+ (values[ds_index] < min) ? min : max,
+ ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
}
buf += status;
bufsize -= status;
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 */
- ds_name = ds->ds[ds_index].name;
- if ((th->data_source[0] != 0)
- && (strcmp (ds_name, th->data_source) != 0))
- return (STATE_OKAY);
+ 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_failure--;
}
- if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
- || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
- 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 ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
- || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
- is_warning++;
if (is_warning != 0)
return (STATE_WARNING);
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, i);
+ status = ut_check_one_data_source (ds, vl, th, values_copy, i);
if (ret < status)
{
ret = status;
return (2);
} /* }}} int ut_check_interesting */
-/* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */
+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 : */
/**
* collectd - src/utils_threshold.h
- * Copyright (C) 2007,2008 Florian octo Forster
+ * 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
#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
*
*/
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/write_http.c
+ * Copyright (C) 2009 Paul Sadauskas
+ * Copyright (C) 2009 Doug MacEachern
+ * 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
+ *
+ * Authors:
+ * Florian octo Forster <octo at verplant.org>
+ * Doug MacEachern <dougm@hyperic.com>
+ * Paul Sadauskas <psadauskas@gmail.com>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "utils_cache.h"
+#include "utils_parse_option.h"
+#include "utils_format_json.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#include <curl/curl.h>
+
+/*
+ * Private variables
+ */
+struct wh_callback_s
+{
+ char *location;
+
+ char *user;
+ char *pass;
+ char *credentials;
+ int verify_peer;
+ int verify_host;
+ char *cacert;
+
+#define WH_FORMAT_COMMAND 0
+#define WH_FORMAT_JSON 1
+ int format;
+
+ CURL *curl;
+ char curl_errbuf[CURL_ERROR_SIZE];
+
+ char send_buffer[4096];
+ size_t send_buffer_free;
+ size_t send_buffer_fill;
+ time_t send_buffer_init_time;
+
+ pthread_mutex_t send_lock;
+};
+typedef struct wh_callback_s wh_callback_t;
+
+static void wh_reset_buffer (wh_callback_t *cb) /* {{{ */
+{
+ memset (cb->send_buffer, 0, sizeof (cb->send_buffer));
+ cb->send_buffer_free = sizeof (cb->send_buffer);
+ cb->send_buffer_fill = 0;
+ cb->send_buffer_init_time = time (NULL);
+
+ if (cb->format == WH_FORMAT_JSON)
+ {
+ format_json_initialize (cb->send_buffer,
+ &cb->send_buffer_fill,
+ &cb->send_buffer_free);
+ }
+} /* }}} wh_reset_buffer */
+
+static int wh_send_buffer (wh_callback_t *cb) /* {{{ */
+{
+ int status = 0;
+
+ curl_easy_setopt (cb->curl, CURLOPT_POSTFIELDS, cb->send_buffer);
+ status = curl_easy_perform (cb->curl);
+ if (status != 0)
+ {
+ ERROR ("write_http plugin: curl_easy_perform failed with "
+ "status %i: %s",
+ status, cb->curl_errbuf);
+ }
+ return (status);
+} /* }}} wh_send_buffer */
+
+static int wh_callback_init (wh_callback_t *cb) /* {{{ */
+{
+ struct curl_slist *headers;
+
+ if (cb->curl != NULL)
+ return (0);
+
+ cb->curl = curl_easy_init ();
+ if (cb->curl == NULL)
+ {
+ ERROR ("curl plugin: curl_easy_init failed.");
+ return (-1);
+ }
+
+ curl_easy_setopt (cb->curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+
+ headers = NULL;
+ headers = curl_slist_append (headers, "Accept: */*");
+ if (cb->format == WH_FORMAT_JSON)
+ headers = curl_slist_append (headers, "Content-Type: application/json");
+ else
+ headers = curl_slist_append (headers, "Content-Type: text/plain");
+ headers = curl_slist_append (headers, "Expect:");
+ curl_easy_setopt (cb->curl, CURLOPT_HTTPHEADER, headers);
+
+ curl_easy_setopt (cb->curl, CURLOPT_ERRORBUFFER, cb->curl_errbuf);
+ curl_easy_setopt (cb->curl, CURLOPT_URL, cb->location);
+
+ if (cb->user != NULL)
+ {
+ size_t credentials_size;
+
+ credentials_size = strlen (cb->user) + 2;
+ if (cb->pass != NULL)
+ credentials_size += strlen (cb->pass);
+
+ cb->credentials = (char *) malloc (credentials_size);
+ if (cb->credentials == NULL)
+ {
+ ERROR ("curl plugin: malloc failed.");
+ return (-1);
+ }
+
+ ssnprintf (cb->credentials, credentials_size, "%s:%s",
+ cb->user, (cb->pass == NULL) ? "" : cb->pass);
+ curl_easy_setopt (cb->curl, CURLOPT_USERPWD, cb->credentials);
+ curl_easy_setopt (cb->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+ }
+
+ curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYPEER, cb->verify_peer);
+ curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYHOST,
+ cb->verify_host ? 2 : 0);
+ if (cb->cacert != NULL)
+ curl_easy_setopt (cb->curl, CURLOPT_CAINFO, cb->cacert);
+
+ wh_reset_buffer (cb);
+
+ return (0);
+} /* }}} int wh_callback_init */
+
+static int wh_flush_nolock (int timeout, wh_callback_t *cb) /* {{{ */
+{
+ int status;
+
+ DEBUG ("write_http plugin: wh_flush_nolock: timeout = %i; "
+ "send_buffer_fill = %zu;",
+ timeout, cb->send_buffer_fill);
+
+ if (timeout > 0)
+ {
+ time_t now;
+
+ now = time (NULL);
+ if ((cb->send_buffer_init_time + timeout) > now)
+ return (0);
+ }
+
+ if (cb->format == WH_FORMAT_COMMAND)
+ {
+ if (cb->send_buffer_fill <= 0)
+ {
+ cb->send_buffer_init_time = time (NULL);
+ return (0);
+ }
+
+ status = wh_send_buffer (cb);
+ wh_reset_buffer (cb);
+ }
+ else if (cb->format == WH_FORMAT_JSON)
+ {
+ if (cb->send_buffer_fill <= 2)
+ {
+ cb->send_buffer_init_time = time (NULL);
+ return (0);
+ }
+
+ status = format_json_finalize (cb->send_buffer,
+ &cb->send_buffer_fill,
+ &cb->send_buffer_free);
+ if (status != 0)
+ {
+ ERROR ("write_http: wh_flush_nolock: "
+ "format_json_finalize failed.");
+ wh_reset_buffer (cb);
+ return (status);
+ }
+
+ status = wh_send_buffer (cb);
+ wh_reset_buffer (cb);
+ }
+ else
+ {
+ ERROR ("write_http: wh_flush_nolock: "
+ "Unknown format: %i",
+ cb->format);
+ return (-1);
+ }
+
+ return (status);
+} /* }}} wh_flush_nolock */
+
+static int wh_flush (int timeout, /* {{{ */
+ const char *identifier __attribute__((unused)),
+ user_data_t *user_data)
+{
+ wh_callback_t *cb;
+ int status;
+
+ if (user_data == NULL)
+ return (-EINVAL);
+
+ cb = user_data->data;
+
+ pthread_mutex_lock (&cb->send_lock);
+
+ if (cb->curl == NULL)
+ {
+ status = wh_callback_init (cb);
+ if (status != 0)
+ {
+ ERROR ("write_http plugin: wh_callback_init failed.");
+ pthread_mutex_unlock (&cb->send_lock);
+ return (-1);
+ }
+ }
+
+ status = wh_flush_nolock (timeout, cb);
+ pthread_mutex_unlock (&cb->send_lock);
+
+ return (status);
+} /* }}} int wh_flush */
+
+static void wh_callback_free (void *data) /* {{{ */
+{
+ wh_callback_t *cb;
+
+ if (data == NULL)
+ return;
+
+ cb = data;
+
+ wh_flush_nolock (/* timeout = */ -1, cb);
+
+ curl_easy_cleanup (cb->curl);
+ sfree (cb->location);
+ sfree (cb->user);
+ sfree (cb->pass);
+ sfree (cb->credentials);
+ sfree (cb->cacert);
+
+ 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)
+{
+ char key[10*DATA_MAX_NAME_LEN];
+ char values[512];
+ char command[1024];
+ size_t command_len;
+
+ int status;
+
+ if (0 != strcmp (ds->type, vl->type)) {
+ ERROR ("write_http plugin: DS type does not match "
+ "value list type");
+ return -1;
+ }
+
+ /* Copy the identifier to `key' and escape it. */
+ status = FORMAT_VL (key, sizeof (key), vl);
+ if (status != 0) {
+ ERROR ("write_http plugin: error with format_name");
+ return (status);
+ }
+ escape_string (key, sizeof (key));
+
+ /* Convert the values to an ASCII representation and put that into
+ * `values'. */
+ status = wh_value_list_to_string (values, sizeof (values), ds, vl);
+ if (status != 0) {
+ ERROR ("write_http plugin: error with "
+ "wh_value_list_to_string");
+ return (status);
+ }
+
+ command_len = (size_t) ssnprintf (command, sizeof (command),
+ "PUTVAL %s interval=%i %s\r\n",
+ key, vl->interval, values);
+ if (command_len >= sizeof (command)) {
+ ERROR ("write_http plugin: Command buffer too small: "
+ "Need %zu bytes.", command_len + 1);
+ return (-1);
+ }
+
+ pthread_mutex_lock (&cb->send_lock);
+
+ if (cb->curl == NULL)
+ {
+ status = wh_callback_init (cb);
+ if (status != 0)
+ {
+ ERROR ("write_http plugin: wh_callback_init failed.");
+ pthread_mutex_unlock (&cb->send_lock);
+ return (-1);
+ }
+ }
+
+ if (command_len >= cb->send_buffer_free)
+ {
+ status = wh_flush_nolock (/* timeout = */ -1, cb);
+ if (status != 0)
+ {
+ pthread_mutex_unlock (&cb->send_lock);
+ return (status);
+ }
+ }
+ assert (command_len < cb->send_buffer_free);
+
+ /* `command_len + 1' because `command_len' does not include the
+ * trailing null byte. Neither does `send_buffer_fill'. */
+ memcpy (cb->send_buffer + cb->send_buffer_fill,
+ command, command_len + 1);
+ cb->send_buffer_fill += command_len;
+ cb->send_buffer_free -= command_len;
+
+ DEBUG ("write_http plugin: <%s> buffer %zu/%zu (%g%%) \"%s\"",
+ cb->location,
+ cb->send_buffer_fill, sizeof (cb->send_buffer),
+ 100.0 * ((double) cb->send_buffer_fill) / ((double) sizeof (cb->send_buffer)),
+ command);
+
+ /* Check if we have enough space for this command. */
+ pthread_mutex_unlock (&cb->send_lock);
+
+ return (0);
+} /* }}} int wh_write_command */
+
+static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ */
+ wh_callback_t *cb)
+{
+ int status;
+
+ pthread_mutex_lock (&cb->send_lock);
+
+ if (cb->curl == NULL)
+ {
+ status = wh_callback_init (cb);
+ if (status != 0)
+ {
+ ERROR ("write_http plugin: wh_callback_init failed.");
+ pthread_mutex_unlock (&cb->send_lock);
+ return (-1);
+ }
+ }
+
+ status = format_json_value_list (cb->send_buffer,
+ &cb->send_buffer_fill,
+ &cb->send_buffer_free,
+ ds, vl);
+ if (status == (-ENOMEM))
+ {
+ status = wh_flush_nolock (/* timeout = */ -1, cb);
+ if (status != 0)
+ {
+ wh_reset_buffer (cb);
+ pthread_mutex_unlock (&cb->send_lock);
+ return (status);
+ }
+
+ status = format_json_value_list (cb->send_buffer,
+ &cb->send_buffer_fill,
+ &cb->send_buffer_free,
+ ds, vl);
+ }
+ if (status != 0)
+ {
+ pthread_mutex_unlock (&cb->send_lock);
+ return (status);
+ }
+
+ DEBUG ("write_http plugin: <%s> buffer %zu/%zu (%g%%)",
+ cb->location,
+ cb->send_buffer_fill, sizeof (cb->send_buffer),
+ 100.0 * ((double) cb->send_buffer_fill) / ((double) sizeof (cb->send_buffer)));
+
+ /* Check if we have enough space for this command. */
+ pthread_mutex_unlock (&cb->send_lock);
+
+ return (0);
+} /* }}} int wh_write_json */
+
+static int wh_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */
+ user_data_t *user_data)
+{
+ wh_callback_t *cb;
+ int status;
+
+ if (user_data == NULL)
+ return (-EINVAL);
+
+ cb = user_data->data;
+
+ if (cb->format == WH_FORMAT_JSON)
+ status = wh_write_json (ds, vl, cb);
+ else
+ status = wh_write_command (ds, vl, cb);
+
+ return (status);
+} /* }}} int wh_write */
+
+static int 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 ("write_http 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 ("write_http plugin: strdup failed.");
+ return (-1);
+ }
+
+ if (*ret_string != NULL)
+ free (*ret_string);
+ *ret_string = string;
+
+ return (0);
+} /* }}} int config_set_string */
+
+static int config_set_boolean (int *dest, oconfig_item_t *ci) /* {{{ */
+{
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+ {
+ WARNING ("write_http plugin: The `%s' config option "
+ "needs exactly one boolean argument.", ci->key);
+ return (-1);
+ }
+
+ *dest = ci->values[0].value.boolean ? 1 : 0;
+
+ return (0);
+} /* }}} int config_set_boolean */
+
+static int config_set_format (wh_callback_t *cb, /* {{{ */
+ oconfig_item_t *ci)
+{
+ char *string;
+
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("write_http plugin: The `%s' config option "
+ "needs exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ string = ci->values[0].value.string;
+ if (strcasecmp ("Command", string) == 0)
+ cb->format = WH_FORMAT_COMMAND;
+ else if (strcasecmp ("JSON", string) == 0)
+ cb->format = WH_FORMAT_JSON;
+ else
+ {
+ ERROR ("write_http plugin: Invalid format string: %s",
+ string);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int config_set_string */
+
+static int wh_config_url (oconfig_item_t *ci) /* {{{ */
+{
+ wh_callback_t *cb;
+ user_data_t user_data;
+ int i;
+
+ cb = malloc (sizeof (*cb));
+ if (cb == NULL)
+ {
+ ERROR ("write_http plugin: malloc failed.");
+ return (-1);
+ }
+ memset (cb, 0, sizeof (*cb));
+ cb->location = NULL;
+ cb->user = NULL;
+ cb->pass = NULL;
+ cb->credentials = NULL;
+ cb->verify_peer = 1;
+ cb->verify_host = 1;
+ cb->cacert = NULL;
+ cb->format = WH_FORMAT_COMMAND;
+ cb->curl = NULL;
+
+ pthread_mutex_init (&cb->send_lock, /* attr = */ NULL);
+
+ config_set_string (&cb->location, ci);
+ if (cb->location == NULL)
+ return (-1);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("User", child->key) == 0)
+ config_set_string (&cb->user, child);
+ else if (strcasecmp ("Password", child->key) == 0)
+ config_set_string (&cb->pass, child);
+ else if (strcasecmp ("VerifyPeer", child->key) == 0)
+ config_set_boolean (&cb->verify_peer, child);
+ else if (strcasecmp ("VerifyHost", child->key) == 0)
+ config_set_boolean (&cb->verify_host, child);
+ else if (strcasecmp ("CACert", child->key) == 0)
+ config_set_string (&cb->cacert, child);
+ else if (strcasecmp ("Format", child->key) == 0)
+ config_set_format (cb, child);
+ else
+ {
+ ERROR ("write_http plugin: Invalid configuration "
+ "option: %s.", child->key);
+ }
+ }
+
+ DEBUG ("write_http: Registering write callback with URL %s",
+ cb->location);
+
+ memset (&user_data, 0, sizeof (user_data));
+ user_data.data = cb;
+ user_data.free_func = NULL;
+ plugin_register_flush ("write_http", wh_flush, &user_data);
+
+ user_data.free_func = wh_callback_free;
+ plugin_register_write ("write_http", wh_write, &user_data);
+
+ return (0);
+} /* }}} int wh_config_url */
+
+static int wh_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("URL", child->key) == 0)
+ wh_config_url (child);
+ else
+ {
+ ERROR ("write_http plugin: Invalid configuration "
+ "option: %s.", child->key);
+ }
+ }
+
+ return (0);
+} /* }}} int wh_config */
+
+void module_register (void) /* {{{ */
+{
+ plugin_register_complex_config ("write_http", wh_config);
+} /* }}} void module_register */
+
+/* vim: set fdm=marker sw=8 ts=8 tw=78 et : */
--- /dev/null
+/**
+ * collectd - src/zfs_arc.c
+ * Copyright (C) 2009 Anthony Dewhurst
+ *
+ * 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:
+ * Anthony Dewhurst <dewhurst at gmail>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+/*
+ * Global variables
+ */
+static kstat_t *ksp;
+extern kstat_ctl_t *kc;
+
+static void za_submit (const char* type, const char* type_instance, value_t* values, int values_len)
+{
+ value_list_t vl = VALUE_LIST_INIT;
+
+ vl.values = values;
+ vl.values_len = values_len;
+
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "zfs_arc", sizeof (vl.plugin));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+ sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+}
+
+static void za_submit_gauge (const char* type, const char* type_instance, gauge_t value)
+{
+ value_t values[1];
+
+ values[0].gauge = value;
+
+ za_submit (type, type_instance, values, STATIC_ARRAY_SIZE(values));
+}
+
+static void za_submit_size (gauge_t size, gauge_t size_target, gauge_t limit_min, gauge_t limit_max)
+{
+ value_t values[4];
+
+ values[0].gauge = size;
+ values[1].gauge = size_target;
+ values[2].gauge = limit_min;
+ values[3].gauge = limit_max;
+
+ za_submit ("arc_size", "", values, STATIC_ARRAY_SIZE(values));
+}
+
+static void za_submit_bytes (counter_t read, counter_t write)
+{
+ value_t values[2];
+
+ values[0].counter = read;
+ values[1].counter = write;
+
+ 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];
+
+ 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));
+}
+
+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;
+
+ get_kstat (&ksp, "zfs", 0, "arcstats");
+ if (ksp == NULL)
+ {
+ ERROR ("zfs_arc plugin: Cannot find zfs:0:arcstats kstat.");
+ 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");
+
+ 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_metadata_hits = get_kstat_value(ksp, "prefetch_metadata_hits");
+
+ demand_data_misses = get_kstat_value(ksp, "demand_data_misses");
+ demand_metadata_misses = get_kstat_value(ksp, "demand_metadata_misses");
+ 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");
+
+ 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_size (arcsize, targetsize, minlimit, maxlimit);
+ za_submit_gauge ("arc_l2_size", "", l2_size);
+
+ 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);
+
+ 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);
+
+ return (0);
+}
+
+static int za_init (void) /* {{{ */
+{
+ ksp = NULL;
+
+ /* kstats chain already opened by update_kstat (using *kc), verify everything went fine. */
+ if (kc == NULL)
+ {
+ ERROR ("zfs_arc plugin: kstat chain control structure not available.");
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int za_init */
+
+void module_register (void)
+{
+ plugin_register_init ("zfs_arc", za_init);
+ plugin_register_read ("zfs_arc", za_read);
+} /* void module_register */
+
+/* vmi: set sw=8 noexpandtab fdm=marker : */
#!/bin/sh
-DEFAULT_VERSION="4.7.3.git"
+DEFAULT_VERSION="4.8.0.git"
VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"