Merge branch 'collectd-4.7' into collectd-4.8
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Thu, 1 Oct 2009 19:33:37 +0000 (21:33 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Thu, 1 Oct 2009 19:33:37 +0000 (21:33 +0200)
71 files changed:
.gitignore
AUTHORS
ChangeLog
README
bindings/java/Makefile.am
bindings/java/org/collectd/api/DataSource.java
bindings/java/org/collectd/java/GenericJMX.java [new file with mode: 0644]
bindings/java/org/collectd/java/GenericJMXConfConnection.java [new file with mode: 0644]
bindings/java/org/collectd/java/GenericJMXConfMBean.java [new file with mode: 0644]
bindings/java/org/collectd/java/GenericJMXConfValue.java [new file with mode: 0644]
bindings/java/org/collectd/java/JMXMemory.java
bindings/perl/Collectd/Unixsock.pm
configure.in
contrib/GenericJMX.conf [new file with mode: 0644]
contrib/README
contrib/collection3/etc/collection.conf
contrib/collection3/lib/Collectd/Graph/Type/ArcCounts.pm [new file with mode: 0644]
contrib/cussh.pl
src/Makefile.am
src/collectd-java.pod
src/collectd-perl.pod
src/collectd.conf.in
src/collectd.conf.pod
src/common.c
src/common.h
src/csv.c
src/curl.c
src/curl_json.c [new file with mode: 0644]
src/df.c
src/dns.c
src/gmond.c
src/java.c
src/libcollectdclient/client.c
src/libcollectdclient/client.h
src/madwifi.c [new file with mode: 0644]
src/madwifi.h [new file with mode: 0644]
src/match_empty_counter.c [new file with mode: 0644]
src/meta_data.c
src/meta_data.h
src/network.c
src/olsrd.c [new file with mode: 0644]
src/perl.c
src/plugin.c
src/plugin.h
src/powerdns.c
src/rrdcached.c
src/rrdtool.c
src/snmp.c
src/swap.c
src/table.c
src/tail.c
src/tokyotyrant.c [new file with mode: 0644]
src/types.db
src/types_list.c
src/unixsock.c
src/utils_cache.c
src/utils_cache.h
src/utils_cmd_getthreshold.c [new file with mode: 0644]
src/utils_cmd_getthreshold.h [new file with mode: 0644]
src/utils_cmd_putval.c
src/utils_db_query.c
src/utils_format_json.c [new file with mode: 0644]
src/utils_format_json.h [new file with mode: 0644]
src/utils_match.c
src/utils_match.h
src/utils_rrdcreate.c
src/utils_threshold.c
src/utils_threshold.h
src/write_http.c [new file with mode: 0644]
src/zfs_arc.c [new file with mode: 0644]
version-gen.sh

index 1f491f3..46c8c04 100644 (file)
@@ -1,29 +1,19 @@
-# 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
@@ -37,30 +27,20 @@ src/libcollectdclient/libcollectdclient.pc
 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
@@ -68,12 +48,16 @@ src/liboconfig/parser.c
 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
diff --git a/AUTHORS b/AUTHORS
index 9f82ee1..8d0a022 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -24,6 +24,9 @@ Alvaro Barcellos <alvaro.barcellos at gmail.com>
 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.
 
@@ -51,6 +54,7 @@ Doug MacEachern <dougm at hyperic.com>
  - 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.
@@ -103,6 +107,14 @@ Oleg King <king2 at kaluga.ru>
    + 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.
index f88135f..8398083 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,52 @@
+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.
diff --git a/README b/README
index 7412765..ea53dba 100644 (file)
--- a/README
+++ b/README
@@ -43,6 +43,10 @@ Features
     - 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.
@@ -117,6 +121,10 @@ Features
     - 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).
@@ -165,6 +173,10 @@ Features
       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.
@@ -238,6 +250,10 @@ Features
     - 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.
 
@@ -258,6 +274,9 @@ Features
     - 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:
 
@@ -292,6 +311,11 @@ Features
       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:
 
@@ -334,6 +358,9 @@ Features
   * 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.
 
@@ -432,7 +459,8 @@ Prerequisites
     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)
@@ -543,6 +571,9 @@ Prerequisites
   * libxmms (optional)
     <http://www.xmms.org/>
 
+  * libyajl (optional)
+    Parse JSON data. This is needed for the `curl_json' plugin.
+    <http://github.com/lloyd/yajl>
 
 Configuring / Compiling / Installing
 ------------------------------------
index af5e480..d3315ac 100644 (file)
@@ -17,10 +17,16 @@ EXTRA_DIST = org/collectd/api/CollectdConfigInterface.java \
             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
@@ -29,7 +35,11 @@ install-exec-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"
index bfe8e2d..ba132d3 100644 (file)
@@ -22,14 +22,18 @@ package org.collectd.api;
  * 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;
@@ -41,6 +45,10 @@ public class DataSource {
         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;
     }
diff --git a/bindings/java/org/collectd/java/GenericJMX.java b/bindings/java/org/collectd/java/GenericJMX.java
new file mode 100644 (file)
index 0000000..319615c
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * 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 : */
diff --git a/bindings/java/org/collectd/java/GenericJMXConfConnection.java b/bindings/java/org/collectd/java/GenericJMXConfConnection.java
new file mode 100644 (file)
index 0000000..7214fd7
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * 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 : */
diff --git a/bindings/java/org/collectd/java/GenericJMXConfMBean.java b/bindings/java/org/collectd/java/GenericJMXConfMBean.java
new file mode 100644 (file)
index 0000000..27e9e32
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * 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 : */
diff --git a/bindings/java/org/collectd/java/GenericJMXConfValue.java b/bindings/java/org/collectd/java/GenericJMXConfValue.java
new file mode 100644 (file)
index 0000000..0eb0d5f
--- /dev/null
@@ -0,0 +1,587 @@
+/*
+ * 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 &lt;value&nbsp;/&gt; block and query functionality.
+ *
+ * This class represents a &lt;value&nbsp;/&gt; 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 : */
index d1666c0..6e6a2fb 100644 (file)
@@ -47,7 +47,7 @@ import org.collectd.api.CollectdShutdownInterface;
 import org.collectd.api.OConfigValue;
 import org.collectd.api.OConfigItem;
 
-public class JMXMemory implements CollectdConfigInterface, /* {{{ */
+public class JMXMemory implements CollectdConfigInterface,
        CollectdInitInterface,
        CollectdReadInterface,
        CollectdShutdownInterface
@@ -227,6 +227,6 @@ public class JMXMemory implements CollectdConfigInterface, /* {{{ */
     _mbean = null;
     return (0);
   } /* }}} int shutdown */
-} /* }}} class JMXMemory */
+} /* class JMXMemory */
 
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 4403178..199a47c 100644 (file)
@@ -196,7 +196,7 @@ value. On error false is returned.
 
 =cut
 
-sub getval
+sub getval # {{{
 {
        my $obj = shift;
        my %args = @_;
@@ -242,7 +242,64 @@ sub getval
        }
 
        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> [...]);
 
@@ -595,3 +652,5 @@ L<collectd-unixsock(5)>
 Florian octo Forster E<lt>octo@verplant.orgE<gt>
 
 =cut
+
+# vim: set fdm=marker :
index c076e77..a898d74 100644 (file)
@@ -277,6 +277,20 @@ AC_CHECK_HEADERS(IOKit/ps/IOPowerSources.h, [], [],
 ])
 
 # 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"],
 [
@@ -2806,6 +2820,79 @@ then
 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=""
@@ -3009,6 +3096,57 @@ AC_DEFINE_UNQUOTED(HAVE_LIBXMMS, [$with_libxmms_numeric], [Define to 1 if you ha
 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=""
@@ -3343,6 +3481,7 @@ plugin_bind="no"
 plugin_conntrack="no"
 plugin_cpu="no"
 plugin_cpufreq="no"
+plugin_curl_json="no"
 plugin_df="no"
 plugin_disk="no"
 plugin_entropy="no"
@@ -3370,6 +3509,7 @@ plugin_uptime="no"
 plugin_vmem="no"
 plugin_vserver="no"
 plugin_wireless="no"
+plugin_zfs_arc="no"
 
 # Linux
 if test "x$ac_system" = "xLinux"
@@ -3419,6 +3559,7 @@ fi
 if test "x$with_kstat" = "xyes"
 then
        plugin_uptime="yes"
+       plugin_zfs_arc="yes"
 fi
 
 if test "x$with_devinfo$with_kstat" = "xyesyes"
@@ -3458,6 +3599,11 @@ then
        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"
@@ -3609,6 +3755,7 @@ AC_PLUGIN([cpufreq],     [$plugin_cpufreq],    [CPU frequency statistics])
 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])
@@ -3629,6 +3776,8 @@ AC_PLUGIN([java],        [$with_java],         [Embed the Java Virtual Machine])
 AC_PLUGIN([libvirt],     [$plugin_libvirt],    [Virtual machine statistics])
 AC_PLUGIN([load],        [$plugin_load],       [System load])
 AC_PLUGIN([logfile],     [yes],                [File logging plugin])
+AC_PLUGIN([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])
@@ -3646,6 +3795,7 @@ AC_PLUGIN([notify_desktop], [$with_libnotify], [Desktop notifications])
 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])
@@ -3672,6 +3822,7 @@ AC_PLUGIN([tcpconns],    [$plugin_tcpconns],   [TCP connection statistics])
 AC_PLUGIN([teamspeak2],  [yes],                [TeamSpeak2 server statistics])
 AC_PLUGIN([ted],         [$plugin_ted],        [Read The Energy Detective values])
 AC_PLUGIN([thermal],     [$plugin_thermal],    [Linux ACPI thermal zone statistics])
+AC_PLUGIN([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])
@@ -3679,7 +3830,9 @@ AC_PLUGIN([uuid],        [yes],                [UUID as hostname plugin])
 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
@@ -3875,10 +4028,12 @@ Configuration:
     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:
@@ -3900,6 +4055,7 @@ Configuration:
     cpufreq . . . . . . . $enable_cpufreq
     csv . . . . . . . . . $enable_csv
     curl  . . . . . . . . $enable_curl
+    curl_json . . . . . . $enable_curl_json
     dbi . . . . . . . . . $enable_dbi
     df  . . . . . . . . . $enable_df
     disk  . . . . . . . . $enable_disk
@@ -3920,6 +4076,8 @@ Configuration:
     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
@@ -3937,6 +4095,7 @@ Configuration:
     notify_email  . . . . $enable_notify_email
     ntpd  . . . . . . . . $enable_ntpd
     nut . . . . . . . . . $enable_nut
+    olsrd . . . . . . . . $enable_olsrd
     onewire . . . . . . . $enable_onewire
     openvpn . . . . . . . $enable_openvpn
     oracle  . . . . . . . $enable_oracle
@@ -3963,6 +4122,7 @@ Configuration:
     teamspeak2  . . . . . $enable_teamspeak2
     ted . . . . . . . . . $enable_ted
     thermal . . . . . . . $enable_thermal
+    tokyotyrant . . . . . $enable_tokyotyrant
     unixsock  . . . . . . $enable_unixsock
     uptime  . . . . . . . $enable_uptime
     users . . . . . . . . $enable_users
@@ -3970,7 +4130,9 @@ Configuration:
     vmem  . . . . . . . . $enable_vmem
     vserver . . . . . . . $enable_vserver
     wireless  . . . . . . $enable_wireless
+    write_http  . . . . . $enable_write_http
     xmms  . . . . . . . . $enable_xmms
+    zfs_arc . . . . . . . $enable_zfs_arc
 
 EOF
 
diff --git a/contrib/GenericJMX.conf b/contrib/GenericJMX.conf
new file mode 100644 (file)
index 0000000..f4dd8f0
--- /dev/null
@@ -0,0 +1,238 @@
+# 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>
index 119d866..bc1fe9f 100644 (file)
@@ -13,20 +13,18 @@ can seriously fuck up your RRD files if you don't know what you're doing.
 
 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.
 
@@ -72,6 +70,11 @@ fedora/
   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
index 6baad5b..1322d0d 100644 (file)
@@ -47,6 +47,52 @@ GraphWidth 400
   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
diff --git a/contrib/collection3/lib/Collectd/Graph/Type/ArcCounts.pm b/contrib/collection3/lib/Collectd/Graph/Type/ArcCounts.pm
new file mode 100644 (file)
index 0000000..7a8946e
--- /dev/null
@@ -0,0 +1,110 @@
+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 :
index 320bf73..ae758d1 100755 (executable)
@@ -60,6 +60,7 @@ use Collectd::Unixsock();
                HELP    => \&cmd_help,
                PUTVAL  => \&putval,
                GETVAL  => \&getval,
+               GETTHRESHOLD  => \&getthreshold,
                FLUSH   => \&flush,
                LISTVAL => \&listval,
                PUTNOTIF => \&putnotif,
@@ -198,6 +199,7 @@ Available commands:
   HELP
   PUTVAL
   GETVAL
+  GETTHRESHOLD
   FLUSH
   LISTVAL
   PUTNOTIF
@@ -327,6 +329,48 @@ sub getval {
        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
index 7b13029..2a83659 100644 (file)
@@ -223,6 +223,21 @@ endif
 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 \
@@ -439,6 +454,22 @@ collectd_LDADD += "-dlopen" logfile.la
 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
@@ -639,6 +670,18 @@ collectd_LDADD += "-dlopen" nut.la
 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
@@ -912,11 +955,25 @@ collectd_LDADD += "-dlopen" thermal.la
 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
@@ -987,6 +1044,21 @@ collectd_LDADD += "-dlopen" wireless.la
 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
@@ -997,6 +1069,16 @@ collectd_LDADD += "-dlopen" xmms.la
 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 \
index f441c82..9c0c6eb 100644 (file)
@@ -542,6 +542,138 @@ daemon:
     }
   }
 
+=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)>,
index 0f48ba5..b8d72f0 100644 (file)
@@ -182,7 +182,7 @@ structure. The general layout looks like this:
 
   [{
     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
   }, ...]
@@ -569,6 +569,10 @@ available (B<:all> will export all of them):
 
 =item B<DS_TYPE_GAUGE>
 
+=item B<DS_TYPE_DERIVE>
+
+=item B<DS_TYPE_ABSOLUTE>
+
 =back
 
 =item B<:log>
index 0ef71b5..8f28f8f 100644 (file)
@@ -61,6 +61,7 @@ FQDNLookup   true
 #@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
@@ -94,6 +95,7 @@ FQDNLookup   true
 #@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
@@ -116,6 +118,7 @@ FQDNLookup   true
 #@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
@@ -123,7 +126,9 @@ FQDNLookup   true
 #@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                                                       #
@@ -189,6 +194,37 @@ FQDNLookup   true
 #  </Page>
 #</Plugin>
 
+#<Plugin curl_json>
+## 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 dbi>
 #      <Query "num_of_customers">
 #              Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl"
@@ -216,6 +252,7 @@ FQDNLookup   true
 #      MountPoint "/home"
 #      FSType "ext3"
 #      IgnoreSelected false
+#      ReportByDevice false
 #</Plugin>
 
 #<Plugin disk>
@@ -226,6 +263,7 @@ FQDNLookup   true
 #<Plugin dns>
 #      Interface "eth0"
 #      IgnoreSource "192.168.0.1"
+#      SelectNumericQueryTypes true
 #</Plugin>
 
 #<Plugin email>
@@ -303,7 +341,7 @@ FQDNLookup   true
 
 #<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">
@@ -415,6 +453,14 @@ FQDNLookup   true
 #      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"
@@ -567,7 +613,7 @@ FQDNLookup   true
 #       Version 1
 #       Community "community_string"
 #       Collect "std_traffic"
-#       Inverval 120
+#       Interval 120
 #   </Host>
 #   <Host "some.server.mydomain.org">
 #       Address "192.168.0.42"
@@ -644,6 +690,11 @@ FQDNLookup   true
 #      IgnoreSelected false
 #</Plugin>
 
+#<Plugin tokyotyrant>
+#      Host "localhost"
+#      Port "1978"
+#</Plugin>
+
 #<Plugin unixsock>
 #      SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock"
 #      SocketGroup "collectd"
@@ -658,6 +709,13 @@ FQDNLookup   true
 #      Verbose false
 #</Plugin>
 
+#<Plugin write_http>
+#      <URL "http://example.com/collectd-post">
+#              User "collectd"
+#              Password "weCh3ik0"
+#      </URL>
+#</Plugin>
+
 ##############################################################################
 # Filter configuration                                                       #
 #----------------------------------------------------------------------------#
index 32d877a..43a322b 100644 (file)
@@ -510,6 +510,109 @@ plugin below on how matches are defined.
 
 =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
@@ -770,6 +873,12 @@ match any one of the criteria are collected. By default only selected
 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>
@@ -821,6 +930,10 @@ may not work on certain platforms, such as MacE<nbsp>OSE<nbsp>X.
 
 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>
@@ -962,6 +1075,11 @@ Controls whether or not to recurse into subdirectories. Enabled by default.
 
 =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
@@ -1722,6 +1840,11 @@ multicast, and IPv4 and IPv6 packets. The default is to not change this value.
 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
@@ -1902,6 +2025,55 @@ L<upsc(8)>.
 
 =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.
@@ -2775,6 +2947,14 @@ updates per second, writing all values to disk will take approximately
 "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>
@@ -3163,6 +3343,26 @@ selection is configured at all, B<all> devices are selected.
 
 =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
@@ -3259,6 +3459,64 @@ traffic (e.E<nbsp>g. due to headers and retransmission). If you want to
 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
@@ -3384,6 +3642,13 @@ This applies to missing values, too: If set to B<true> a notification about a
 missing value is generated once every B<Interval> seconds. If set to B<false>
 only one such notification is generated until the value appears again.
 
+=item B<Percentage> B<true>|B<false>
+
+If set to B<true>, the minimum and maximum values given are interpreted as
+percentage value, relative to the other data sources. This is helpful for
+example for the "df" type, where you may want to issue a warning when less than
+5E<nbsp>% of the total space is available. Defaults to B<false>.
+
 =back
 
 =head1 FILTER CONFIGURATION
@@ -3877,6 +4142,19 @@ Example:
    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
index 8f6cad2..7c2c30e 100644 (file)
@@ -837,28 +837,41 @@ int parse_identifier (char *str, char **ret_host,
        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)
@@ -893,7 +906,7 @@ 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;
                }
 
index d0cc4e8..6682e1c 100644 (file)
@@ -253,14 +253,14 @@ int format_name (char *ret, int ret_len,
                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
index 5c43b8a..78037a9 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -61,37 +61,50 @@ static int value_list_to_string (char *buffer, int buffer_len,
        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)))
index d3c307e..a43e7ed 100644 (file)
@@ -222,6 +222,29 @@ static int cc_config_add_match_dstype (int *dstype_ret, /* {{{ */
     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;
diff --git a/src/curl_json.c b/src/curl_json.c
new file mode 100644 (file)
index 0000000..53e8abd
--- /dev/null
@@ -0,0 +1,822 @@
+/**
+ * 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 : */
index 2c59b01..194c257 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -1,6 +1,7 @@
 /**
  * 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
@@ -17,6 +18,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Paul Sadauskas <psadauskas at gmail.com>
  **/
 
 #include "collectd.h"
@@ -48,14 +50,16 @@ static const char *config_keys[] =
        "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)
@@ -92,9 +96,7 @@ static int df_config (const char *key, const char *value)
        }
        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);
@@ -108,6 +110,13 @@ static int df_config (const char *key, const char *value)
                }
                return (0);
        }
+       else if (strcasecmp (key, "ReportByDevice") == 0)
+       {
+               if (IS_TRUE (value))
+                       by_device = true;
+
+               return (0);
+       }
 
        return (-1);
 }
@@ -147,7 +156,7 @@ static int df_read (void)
        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)
@@ -155,22 +164,6 @@ static int df_read (void)
 
        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
@@ -197,7 +190,40 @@ static int df_read (void)
                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);
index 3de7c34..f3280c0 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -1,6 +1,7 @@
 /**
  * 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
@@ -17,6 +18,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Mirko Buffoni <briareos at eswat.org>
  **/
 
 #define _BSD_SOURCE
@@ -49,9 +51,10 @@ static const char *config_keys[] =
 {
        "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;
@@ -158,6 +161,13 @@ static int dns_config (const char *key, const char *value)
                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);
@@ -171,13 +181,24 @@ static void dns_child_callback (const rfc1035_header_t *dns)
        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
        {
index 006f5b8..3357ea0 100644 (file)
@@ -541,6 +541,11 @@ static int staging_entry_update (const char *host, const char *name, /* {{{ */
     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. */
@@ -565,6 +570,7 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
 
   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. */
@@ -580,6 +586,7 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
       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;
     }
 
@@ -606,6 +613,13 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
       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;
     }
 
@@ -619,6 +633,7 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
       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;
     }
 
@@ -632,6 +647,7 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
       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:
@@ -644,10 +660,20 @@ static int mc_handle_value_msg (Ganglia_value_msg *msg) /* {{{ */
 
   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);
index 73fd566..87b189f 100644 (file)
@@ -286,6 +286,10 @@ static jobject ctoj_value_to_number (JNIEnv *jvm_env, /* {{{ */
     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 */
@@ -1044,33 +1048,39 @@ static int jtoc_value (JNIEnv *jvm_env, /* {{{ */
 
   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);
index 407493c..63d4e9d 100644 (file)
@@ -792,6 +792,11 @@ int lcc_putval (lcc_connection_t *c, const lcc_value_list_t *vl) /* {{{ */
       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);
index d5371fb..b0d092d 100644 (file)
  */
 #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;
 
diff --git a/src/madwifi.c b/src/madwifi.c
new file mode 100644 (file)
index 0000000..8b3266d
--- /dev/null
@@ -0,0 +1,975 @@
+/**
+ * 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);
+}
diff --git a/src/madwifi.h b/src/madwifi.h
new file mode 100644 (file)
index 0000000..d6a5e35
--- /dev/null
@@ -0,0 +1,307 @@
+/**
+ * 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
diff --git a/src/match_empty_counter.c b/src/match_empty_counter.c
new file mode 100644 (file)
index 0000000..b9afd81
--- /dev/null
@@ -0,0 +1,118 @@
+/**
+ * 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 : */
index 10ccd4b..3a3f5e7 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * 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
@@ -32,6 +32,7 @@
 #define MD_TYPE_SIGNED_INT   2
 #define MD_TYPE_UNSIGNED_INT 3
 #define MD_TYPE_DOUBLE       4
+#define MD_TYPE_BOOLEAN      5
 
 /*
  * Data types
@@ -42,6 +43,7 @@ union meta_value_u
   int64_t  mv_signed_int;
   uint64_t mv_unsigned_int;
   double   mv_double;
+  _Bool    mv_boolean;
 };
 typedef union meta_value_u meta_value_t;
 
@@ -287,6 +289,9 @@ int meta_data_delete (meta_data_t *md, const char *key) /* {{{ */
   return (0);
 } /* }}} int meta_data_delete */
 
+/*
+ * Add functions
+ */
 int meta_data_add_string (meta_data_t *md, /* {{{ */
     const char *key, const char *value)
 {
@@ -365,6 +370,27 @@ int meta_data_add_double (meta_data_t *md, /* {{{ */
   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)
 {
@@ -495,4 +521,34 @@ int meta_data_get_double (meta_data_t *md, /* {{{ */
   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 : */
index 30e7285..8e5a785 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * 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
@@ -45,6 +45,9 @@ int meta_data_add_unsigned_int (meta_data_t *md,
 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,
@@ -58,6 +61,9 @@ int meta_data_get_unsigned_int (meta_data_t *md,
 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 : */
index cf67c2b..109289e 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * 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
@@ -17,6 +18,7 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Aman Gupta <aman at tmm1.net>
  **/
 
 #define _BSD_SOURCE /* For struct ip_mreq */
@@ -27,6 +29,7 @@
 #include "configfile.h"
 #include "utils_fbhash.h"
 #include "utils_avltree.h"
+#include "utils_cache.h"
 
 #include "network.h"
 
@@ -54,9 +57,6 @@
 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
@@ -65,9 +65,6 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL;
 # endif
 #endif /* !IP_ADD_MEMBERSHIP */
 
-/* Buffer size to allocate. */
-#define BUFF_SIZE 1024
-
 /*
  * Maximum size required for encryption / signing:
  *
@@ -245,7 +242,7 @@ typedef struct part_encryption_aes256_s part_encryption_aes256_t;
 
 struct receive_list_entry_s
 {
-  char data[BUFF_SIZE];
+  char *data;
   int  data_len;
   int  fd;
   struct receive_list_entry_s *next;
@@ -256,6 +253,7 @@ typedef struct receive_list_entry_s receive_list_entry_t;
  * Private variables
  */
 static int network_config_ttl = 0;
+static size_t network_config_packet_size = 1024;
 static int network_config_forward = 0;
 
 static sockent_t *sending_sockets = NULL;
@@ -278,133 +276,104 @@ static int       dispatch_thread_running = 0;
 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, /* {{{ */
@@ -528,17 +497,34 @@ static int write_part_values (char **ret_buffer, int *ret_buffer_len,
 
        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
@@ -714,10 +700,32 @@ static int parse_part_values (void **ret_buffer, size_t *ret_buffer_len,
 
        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;
@@ -1303,23 +1311,10 @@ static int parse_packet (sockent_t *se, /* {{{ */
                {
                        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);
                }
@@ -1434,6 +1429,10 @@ static int parse_packet (sockent_t *se, /* {{{ */
                }
        } /* 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 */
 
@@ -1962,7 +1961,7 @@ static void *dispatch_thread (void __attribute__((unused)) *arg) /* {{{ */
     /* 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 */
@@ -1994,14 +1993,16 @@ static void *dispatch_thread (void __attribute__((unused)) *arg) /* {{{ */
 
     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) */
 
@@ -2010,7 +2011,7 @@ static void *dispatch_thread (void __attribute__((unused)) *arg) /* {{{ */
 
 static int network_receive (void) /* {{{ */
 {
-       char buffer[BUFF_SIZE];
+       char buffer[network_config_packet_size];
        int  buffer_len;
 
        int i;
@@ -2070,14 +2071,15 @@ static int network_receive (void) /* {{{ */
                                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;
 
@@ -2134,7 +2136,7 @@ static void *receive_thread (void __attribute__((unused)) *arg)
 
 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;
 
@@ -2435,18 +2437,25 @@ static int network_write (const data_set_t *ds, const value_list_t *vl,
 {
        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)
@@ -2460,7 +2469,7 @@ static int network_write (const data_set_t *ds, const value_list_t *vl,
                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);
 
@@ -2476,7 +2485,7 @@ static int network_write (const data_set_t *ds, const value_list_t *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 ();
        }
@@ -2547,6 +2556,24 @@ static int network_config_set_ttl (const oconfig_item_t *ci) /* {{{ */
   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)
@@ -2756,24 +2783,6 @@ static int network_config_add_server (const oconfig_item_t *ci) /* {{{ */
   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;
@@ -2788,10 +2797,12 @@ static int network_config (oconfig_item_t *ci) /* {{{ */
       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.",
@@ -2805,7 +2816,7 @@ static int network_config (oconfig_item_t *ci) /* {{{ */
 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;
@@ -2904,19 +2915,7 @@ static int network_shutdown (void)
        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' */
 
@@ -2925,18 +2924,18 @@ static int network_shutdown (void)
        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);
@@ -2946,11 +2945,14 @@ static int network_init (void)
 
        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)
        {
@@ -3022,11 +3024,8 @@ static int network_flush (int timeout,
 {
        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);
 
diff --git a/src/olsrd.c b/src/olsrd.c
new file mode 100644 (file)
index 0000000..be422ab
--- /dev/null
@@ -0,0 +1,709 @@
+/**
+ * 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 : */
index cc1e974..a2f5da2 100644 (file)
@@ -199,6 +199,8 @@ struct {
        { "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 },
@@ -272,7 +274,10 @@ static int hv2data_source (pTHX_ HV *hash, data_source_t *ds)
        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;
                }
@@ -325,8 +330,12 @@ static int av2value (pTHX_ char *name, AV *array, value_t *value, int len)
                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;
@@ -642,8 +651,12 @@ static int value_list2hv (pTHX_ value_list_t *vl, data_set_t *ds, HV *hash)
 
                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);
index 6fb7520..5d882e6 100644 (file)
@@ -1213,8 +1213,14 @@ void plugin_shutdown_all (void)
                (*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);
@@ -1230,6 +1236,8 @@ int plugin_dispatch_values (value_list_t *vl)
 
        data_set_t *ds;
 
+       int free_meta_data = 0;
+
        if ((vl == NULL) || (vl->type[0] == 0)
                        || (vl->values == NULL) || (vl->values_len < 1))
        {
@@ -1237,6 +1245,12 @@ int plugin_dispatch_values (value_list_t *vl)
                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 "
@@ -1377,6 +1391,12 @@ int plugin_dispatch_values (value_list_t *vl)
                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 */
 
index e54b27d..b35fcf1 100644 (file)
 
 #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;
 
@@ -76,11 +90,12 @@ struct value_list_s
        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
 {
index 37fceed..3e7ebb3 100644 (file)
@@ -289,7 +289,7 @@ static void submit (const char *plugin_instance, /* {{{ */
     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);
index 326a889..728ada4 100644 (file)
@@ -75,7 +75,9 @@ static int value_list_to_string (char *buffer, int buffer_len,
   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)
@@ -83,10 +85,19 @@ static int value_list_to_string (char *buffer, int buffer_len,
       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)))
index 243a8c8..ee5d70c 100644 (file)
@@ -1,6 +1,8 @@
 /**
  * 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
@@ -17,6 +19,8 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Sebastian Harl <sh at tokkee.org>
+ *   Mariusz Gronczewski <xani666 at gmail.com>
  **/
 
 #include "collectd.h"
@@ -40,6 +44,7 @@ struct rrd_cache_s
        char **values;
        time_t first_value;
        time_t last_value;
+       int random_variation;
        enum
        {
                FLAG_NONE   = 0x00,
@@ -76,7 +81,8 @@ static const char *config_keys[] =
        "RRARows",
        "RRATimespan",
        "XFF",
-       "WritesPerSecond"
+       "WritesPerSecond",
+       "RandomTimeout"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -103,6 +109,7 @@ static rrdcreate_config_t rrdcreate_config =
  * 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;
@@ -204,15 +211,23 @@ static int value_list_to_string (char *buffer, int buffer_len,
        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);
@@ -731,7 +746,7 @@ static int rrd_cache_insert (const char *filename,
                        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! */
@@ -742,6 +757,18 @@ static int rrd_cache_insert (const char *filename,
                        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
                {
@@ -980,6 +1007,23 @@ static int rrd_config (const char *key, const char *value)
                        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);
index 23e199e..7568cf6 100644 (file)
@@ -757,9 +757,8 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
 
   if (vl->type == ASN_OCTET_STR)
   {
-    char *endptr;
+    int status = -1;
 
-    endptr = NULL;
     if (vl->val.string != NULL)
     {
       char string[64];
@@ -770,45 +769,59 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
        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 */
index de9b5f3..22eda1f 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * 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
@@ -64,7 +65,7 @@
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
-static unsigned long long pagesize;
+static derive_t pagesize;
 static kstat_t *ksp;
 /* #endif HAVE_LIBKSTAT */
 
@@ -97,7 +98,7 @@ static int swap_init (void)
 
 #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 */
@@ -138,18 +139,30 @@ static int swap_init (void)
        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);
@@ -164,10 +177,12 @@ static int swap_read (void)
        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)
        {
@@ -179,7 +194,7 @@ static int swap_read (void)
 
        while (fgets (buffer, 1024, fh) != NULL)
        {
-               unsigned long long *val = NULL;
+               derive_t *val = NULL;
 
                if (strncasecmp (buffer, "SwapTotal:", 10) == 0)
                        val = &swap_total;
@@ -195,7 +210,7 @@ static int swap_read (void)
                if (numfields < 2)
                        continue;
 
-               *val = atoll (fields[1]) * 1024LL;
+               *val = (derive_t) atoll (fields[1]) * 1024LL;
        }
 
        if (fclose (fh))
@@ -210,15 +225,52 @@ static int swap_read (void)
 
        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;
 
@@ -251,16 +303,14 @@ static int swap_read (void)
         * 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
@@ -269,8 +319,8 @@ static int swap_read (void)
        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
@@ -311,9 +361,9 @@ static int swap_read (void)
        }
 
 #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++)
@@ -321,9 +371,9 @@ static int swap_read (void)
                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;
        }
 
@@ -335,8 +385,8 @@ static int swap_read (void)
                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 */
@@ -357,17 +407,17 @@ static int swap_read (void)
                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);
@@ -377,16 +427,16 @@ static int swap_read (void)
        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
@@ -397,8 +447,8 @@ static int swap_read (void)
        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);
index 2911bf0..9641c75 100644 (file)
@@ -383,7 +383,7 @@ static int tbl_result_dispatch (tbl_t *tbl, tbl_result_t *res,
                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;
        }
 
index 02afd79..8becc05 100644 (file)
@@ -101,6 +101,26 @@ static int ctail_config_add_match_dstype (ctail_config_match_t *cm,
     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;
diff --git a/src/tokyotyrant.c b/src/tokyotyrant.c
new file mode 100644 (file)
index 0000000..26366c9
--- /dev/null
@@ -0,0 +1,234 @@
+/**
+ * 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 : */
index a92153c..8ac9dd4 100644 (file)
@@ -1,7 +1,15 @@
+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
@@ -15,6 +23,7 @@ cpufreq                       value:GAUGE:0:U
 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
@@ -24,8 +33,8 @@ dns_answer            value:COUNTER:0:65535
 dns_notify             value:COUNTER:0:65535
 dns_octets             queries:COUNTER:0:125000000, responses:COUNTER:0:125000000
 dns_opcode             value:COUNTER:0:65535
-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
@@ -41,10 +50,15 @@ email_count         value:GAUGE:0:U
 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
@@ -54,12 +68,14 @@ if_octets           rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
 if_packets             rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
 if_rx_errors           value:COUNTER:0:4294967295
 if_tx_errors           value:COUNTER:0:4294967295
+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
@@ -76,9 +92,12 @@ mysql_octets         rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
 mysql_qcache           hits:COUNTER:0:U, inserts:COUNTER:0:U, not_cached:COUNTER:0:U, lowmem_prunes:COUNTER:0:U, queries_in_cache:GAUGE:0:U
 mysql_threads          running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created:COUNTER:0:U
 nfs_procedure          value:COUNTER:0:4294967295
-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
@@ -88,8 +107,8 @@ pg_n_tup_g           value:GAUGE:0:U
 pg_numbackends         value:GAUGE:0:U
 pg_scan                        value:COUNTER:0:U
 pg_xact                        value:COUNTER:0:U
-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
@@ -102,18 +121,26 @@ ps_stacksize              value:GAUGE:0:9223372036854775807
 ps_state               value:GAUGE:0:65535
 ps_vm                  value:GAUGE:0:9223372036854775807
 queue_length           value:GAUGE:0:U
+records                        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
index ed832fe..4971417 100644 (file)
@@ -68,9 +68,13 @@ static int parse_ds (data_source_t *dsrc, char *buf, size_t buf_len)
     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);
   }
 
index 38f9025..0b89748 100644 (file)
@@ -26,6 +26,7 @@
 
 #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"
@@ -255,6 +256,10 @@ static void *us_handle_client (void *arg)
                {
                        handle_getval (fhout, buffer);
                }
+               else if (strcasecmp (fields[0], "getthreshold") == 0)
+               {
+                       handle_getthreshold (fhout, buffer);
+               }
                else if (strcasecmp (fields[0], "putval") == 0)
                {
                        handle_putval (fhout, buffer);
index 170e2c5..648c54d 100644 (file)
@@ -25,6 +25,7 @@
 #include "utils_avltree.h"
 #include "utils_cache.h"
 #include "utils_threshold.h"
+#include "meta_data.h"
 
 #include <assert.h>
 #include <pthread.h>
@@ -34,7 +35,7 @@ typedef struct cache_entry_s
        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;
@@ -45,6 +46,22 @@ typedef struct cache_entry_s
         * (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;
@@ -69,17 +86,21 @@ static cache_entry_t *cache_alloc (int values_num)
   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 */
 
@@ -89,7 +110,13 @@ static void cache_free (cache_entry_t *ce)
     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 */
 
@@ -167,6 +194,21 @@ static int uc_send_notification (const char *name)
   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)
 {
@@ -195,17 +237,42 @@ static int uc_insert (const data_set_t *ds, const value_list_t *vl,
 
   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;
@@ -386,7 +453,7 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
   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);
@@ -425,36 +492,84 @@ int uc_update (const data_set_t *ds, const value_list_t *vl)
 
   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;
@@ -543,7 +658,7 @@ gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl)
   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);
@@ -653,7 +768,7 @@ int uc_get_state (const data_set_t *ds, const value_list_t *vl)
   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);
@@ -678,7 +793,7 @@ int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state)
   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);
@@ -697,4 +812,273 @@ int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state)
 
   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 : */
index a965feb..f8059ec 100644 (file)
@@ -39,6 +39,52 @@ int uc_get_names (char ***ret_names, time_t **ret_times, size_t *ret_number);
 
 int uc_get_state (const data_set_t *ds, const value_list_t *vl);
 int uc_set_state (const data_set_t *ds, const value_list_t *vl, int state);
+int 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 */
diff --git a/src/utils_cmd_getthreshold.c b/src/utils_cmd_getthreshold.c
new file mode 100644 (file)
index 0000000..e8c29fa
--- /dev/null
@@ -0,0 +1,178 @@
+/**
+ * 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 : */
diff --git a/src/utils_cmd_getthreshold.h b/src/utils_cmd_getthreshold.h
new file mode 100644 (file)
index 0000000..5481cfd
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ * 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 : */
index 826e1b0..ec2b5f8 100644 (file)
 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);
        }
 
index 5531b25..5f892a4 100644 (file)
@@ -207,11 +207,11 @@ static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
   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);
   }
@@ -364,11 +364,10 @@ static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
   {
     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);
     }
diff --git a/src/utils_format_json.c b/src/utils_format_json.c
new file mode 100644 (file)
index 0000000..a919316
--- /dev/null
@@ -0,0 +1,268 @@
+/**
+ * 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 : */
diff --git a/src/utils_format_json.h b/src/utils_format_json.h
new file mode 100644 (file)
index 0000000..880a79d
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * 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 */
index c19c5ff..bdbad3f 100644 (file)
@@ -145,6 +145,59 @@ static int default_callback (const char __attribute__((unused)) *str,
 
     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!");
index a32a6fe..5a0c033 100644 (file)
@@ -30,6 +30,8 @@
  */
 #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
  */
index c4e9d8b..4ecec59 100644 (file)
@@ -213,6 +213,10 @@ static int ds_get (char ***ret, /* {{{ */
       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",
@@ -225,14 +229,14 @@ static int ds_get (char ***ret, /* {{{ */
       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",
index 03a3f2d..090cc75 100644 (file)
@@ -1,6 +1,9 @@
 /**
  * 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
@@ -17,6 +20,8 @@
  *
  * 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"
@@ -24,6 +29,7 @@
 #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
 /* }}} */
 
 /*
@@ -262,6 +253,54 @@ static int ut_config_type_persist (threshold_t *th, oconfig_item_t *ci)
   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;
@@ -289,6 +328,8 @@ static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
   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++)
   {
@@ -309,6 +350,12 @@ static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
       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' "
@@ -467,6 +514,9 @@ int ut_config (const oconfig_item_t *ci)
   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++)
   {
@@ -562,6 +612,22 @@ static int ut_report_state (const data_set_t *ds,
 
   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
@@ -643,30 +709,33 @@ static int ut_report_state (const data_set_t *ds,
     {
       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;
@@ -697,12 +766,16 @@ static int ut_check_one_data_source (const data_set_t *ds,
   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)
   {
@@ -710,15 +783,39 @@ static int ut_check_one_data_source (const data_set_t *ds,
     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);
 
@@ -742,12 +839,49 @@ static int ut_check_one_threshold (const data_set_t *ds,
   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;
@@ -899,4 +1033,22 @@ int ut_check_interesting (const char *name)
   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 : */
index a42c412..8aaf34c 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * 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
  *
@@ -54,4 +72,16 @@ int ut_check_threshold (const data_set_t *ds, const value_list_t *vl);
  */
 int ut_check_interesting (const char *name);
 
+/* 
+ * Given an identifier in form of a `value_list_t', searches for the best
+ * matching threshold configuration. `ret_threshold' may be NULL.
+ *
+ * Returns:
+ *        0: Success. Threshold configuration has been copied to
+ *           `ret_threshold' (if it is non-NULL).
+ *   ENOENT: No configuration for this identifier found.
+ *     else: Error.
+ */
+int ut_search_threshold (const value_list_t *vl, threshold_t *ret_threshold);
+
 #endif /* UTILS_THRESHOLD_H */
diff --git a/src/write_http.c b/src/write_http.c
new file mode 100644 (file)
index 0000000..f14636b
--- /dev/null
@@ -0,0 +1,638 @@
+/**
+ * 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 : */
diff --git a/src/zfs_arc.c b/src/zfs_arc.c
new file mode 100644 (file)
index 0000000..5f14e90
--- /dev/null
@@ -0,0 +1,166 @@
+/**
+ * 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 : */
index bfe8771..f2c61f5 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-DEFAULT_VERSION="4.7.3.git"
+DEFAULT_VERSION="4.8.0.git"
 
 VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"