Merge branch 'st/python'
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Tue, 8 Dec 2009 12:28:10 +0000 (13:28 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Tue, 8 Dec 2009 12:28:10 +0000 (13:28 +0100)
60 files changed:
.gitignore
AUTHORS
ChangeLog
README
bindings/java/org/collectd/java/GenericJMXConfConnection.java
bindings/java/org/collectd/java/GenericJMXConfMBean.java
configure.in
contrib/collectd-network.py [deleted file]
contrib/collectd-unixsock.py [deleted file]
contrib/collectd_network.py [new file with mode: 0644]
contrib/collectd_unixsock.py [new file with mode: 0644]
contrib/collection3/etc/collection.conf
contrib/snmp-data.conf
src/Makefile.am
src/apache.c
src/ascent.c
src/collectd-python.pod
src/collectd-snmp.pod
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/configfile.c
src/configfile.h
src/contextswitch.c [new file with mode: 0644]
src/cpu.c
src/csv.c
src/df.c
src/disk.c
src/gmond.c
src/hddtemp.c
src/interface.c
src/ipmi.c
src/irq.c
src/libvirt.c
src/load.c
src/logfile.c
src/match_hashed.c [new file with mode: 0644]
src/memory.c
src/netapp.c
src/netlink.c
src/network.c
src/nginx.c
src/ntpd.c
src/onewire.c
src/openvpn.c
src/owniptc/libiptc.c
src/plugin.c
src/processes.c
src/protocols.c
src/routeros.c [new file with mode: 0644]
src/rrdcached.c
src/sensors.c
src/snmp.c
src/swap.c
src/tcpconns.c
src/thermal.c
src/types.db
src/unixsock.c
src/vmem.c
version-gen.sh

index 46c8c04..e8f9af6 100644 (file)
@@ -61,3 +61,7 @@ bindings/perl/pm_to_blib
 # java stuff
 bindings/java/java-build-stamp
 bindings/java/org/collectd/api/*.class
+bindings/java/org/collectd/java/*.class
+
+# python stuff
+*.pyc
diff --git a/AUTHORS b/AUTHORS
index 41c9bb0..5abbfa3 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -85,6 +85,7 @@ Lyonel Vincent <lyonel at ezix.org>
 Marco Chiappero <marco at absence.it>
  - uptime plugin.
  - ip6tables support in the iptables plugin.
+ - openvpn plugin (support for more status file formats)
 
 Michael Stapelberg <michael+git at stapelberg.de>
  - OpenBSD port of the tcpconns plugin.
@@ -110,6 +111,11 @@ Oleg King <king2 at kaluga.ru>
 Ondrej Zajicek <santiago at crfreenet.org>
  - madwifi plugin.
 
+Patrik Weiskircher <weiskircher at inqnet.at>
+ - Contextswitch plugin.
+ - Forkrate counter in the processes plugin.
+ - INode count in the DF plugin.
+
 Paul Sadauskas <psadauskas at gmail.com>
  - tokyotyrant plugin.
  - `ReportByDevice' option of the df plugin.
index 8398083..ecc3869 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2009-10-04, Version 4.8.1
+       * Build system: Issues when building the iptables plugin have been
+         fixed.
+       * exec plugin: Clear the signal block mask before calling exec(2).
+       * perl plugin: Declare the “environ” variable. This solves build
+         issues on some platforms.
+       * processes plugin: Remove unnecessary call of realloc(3). Thanks to
+         Andrés J. Díaz for the patch.
+       * unixsock plugin: Fix a (well hidden) race condition related to file
+         descriptor handling.
+
 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
          lists, where at least one data source is of type COUNTER and the
          counter value of all counter data sources is zero.
 
+2009-10-03, Version 4.7.4
+       * Build system: Issues when building the iptables plugin have been
+         fixed.
+       * exec plugin: Clear the signal block mask before calling exec(2).
+       * perl plugin: Declare the “environ” variable. This solves build
+         issues on some platforms.
+       * processes plugin: Remove unnecessary call of realloc(3). Thanks to
+         Andrés J. Díaz for the patch.
+       * unixsock plugin: Fix a (well hidden) race condition related to file
+         descriptor handling.
+
 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 70118ad..e95bae6 100644 (file)
--- a/README
+++ b/README
@@ -33,9 +33,6 @@ Features
       Batterycharge, -current and voltage of ACPI and PMU based laptop
       batteries.
 
-    - curl
-      Parse statistics from websites using regular expressions.
-
     - bind
       Name server and resolver statistics from the `statistics-channel'
       interface of BIND 9.5, 9,6 and later.
@@ -43,9 +40,8 @@ Features
     - conntrack
       Number of nf_conntrack entries.
 
-    - curl_json
-      Retrieves JSON data via cURL and parses it according to user
-      configuration.
+    - contextswitch
+      Number of context switches done by the operating system.
 
     - cpu
       CPU utilization: Time spent in the system, user, nice, idle, and related
@@ -54,6 +50,13 @@ Features
     - cpufreq
       CPU frequency (For laptops with speed step or a similar technology)
 
+    - curl
+      Parse statistics from websites using regular expressions.
+
+    - curl_json
+      Retrieves JSON data via cURL and parses it according to user
+      configuration.
+
     - dbi
       Executes SQL statements on various databases and interprets the returned
       data.
@@ -177,7 +180,7 @@ Features
       Network UPS tools: UPS current, voltage, power, charge, utilisation,
       temperature, etc. See upsd(8).
 
-    - olsr
+    - olsrd
       Queries routing information from the “Optimized Link State Routing”
       daemon.
 
@@ -495,9 +498,11 @@ Prerequisites
 
   * libganglia (optional)
     Used by the `gmond' plugin to process data received from Ganglia.
+    <http://ganglia.info/>
 
   * libgcrypt (optional)
     Used by the `network' plugin for encryption and authentication.
+    <http://www.gnupg.org/>
 
   * libhal (optional)
     If present, the uuid plugin will check for UUID from HAL.
@@ -517,9 +522,11 @@ Prerequisites
     Library that encapsulates the `Java Virtual Machine' (JVM). This library is
     used by the Java plugin to execute Java bytecode. See “Configuring with
     libjvm” below.
+    <http://openjdk.java.net/> (and others)
 
   * libmemcached (optional)
     Used by the `memcachec' plugin to connect to a memcache daemon.
+    <http://tangent.org/552/libmemcached.html>
 
   * libmysqlclient (optional)
     Unsurprisingly used by the `mysql' plugin.
@@ -567,6 +574,10 @@ Prerequisites
     Used by the `python' plugin. Currently, only 2.3 ≦ Python < 3 is supported.
     <http://www.python.org/>
 
+  * librouteros (optional)
+    Used by the `routeros' plugin to connect to a device running `RouterOS'.
+    <http://verplant.org/librouteros/>
+
   * librrd (optional)
     Used by the `rrdtool' and `rrdcached' plugins. The latter requires RRDtool
     client support which was added after version 1.3 of RRDtool. Versions 1.0,
@@ -586,6 +597,10 @@ Prerequisites
     and/or Solaris.
     <http://www.i-scream.org/libstatgrab/>
 
+  * libtokyotyrant (optional)
+    Used by the tokyotyrant plugin.
+    <http://1978th.net/tokyotyrant/>
+
   * libupsclient/nut (optional)
     For the `nut' plugin which queries nut's `upsd'.
     <http://networkupstools.org/>
@@ -603,7 +618,7 @@ Prerequisites
 
   * libyajl (optional)
     Parse JSON data. This is needed for the `curl_json' plugin.
-    <http://www.lloydforge.org/projects/yajl/>
+    <http://github.com/lloyd/yajl>
 
 Configuring / Compiling / Installing
 ------------------------------------
index 7214fd7..ffa9ded 100644 (file)
@@ -208,7 +208,16 @@ private void connect () /* {{{ */
     pd.setPlugin ("GenericJMX");
 
     for (int i = 0; i < this._mbeans.size (); i++)
-      this._mbeans.get (i).query (this._jmx_connection, pd);
+    {
+      int status;
+
+      status = this._mbeans.get (i).query (this._jmx_connection, pd);
+      if (status != 0)
+      {
+        this._jmx_connection = null;
+        return;
+      }
+    } /* for */
   } /* }}} void query */
 
   public String toString ()
index 27e9e32..1587bd5 100644 (file)
@@ -170,7 +170,7 @@ class GenericJMXConfMBean
     return (this._name);
   } /* }}} */
 
-  public void query (MBeanServerConnection conn, PluginData pd) /* {{{ */
+  public int query (MBeanServerConnection conn, PluginData pd) /* {{{ */
   {
     Set<ObjectName> names;
     Iterator<ObjectName> iter;
@@ -182,7 +182,7 @@ class GenericJMXConfMBean
     catch (Exception e)
     {
       Collectd.logError ("GenericJMXConfMBean: queryNames failed: " + e);
-      return;
+      return (-1);
     }
 
     if (names.size () == 0)
@@ -236,6 +236,8 @@ class GenericJMXConfMBean
       for (int i = 0; i < this._values.size (); i++)
         this._values.get (i).query (conn, objName, pd_tmp);
     }
+
+    return (0);
   } /* }}} void query */
 }
 
index 67f837e..ea1e40e 100644 (file)
@@ -1591,7 +1591,7 @@ then
        if test -d "$with_java_home"
        then
                AC_MSG_CHECKING([for jni.h])
-               TMPDIR=`find -L "$with_java_home" -name jni.h -type f -exec 'dirname' '{}' ';' | head -n 1`
+               TMPDIR=`find "$with_java_home" -name jni.h -type f -exec 'dirname' '{}' ';' | head -n 1`
                if test "x$TMPDIR" != "x"
                then
                        AC_MSG_RESULT([found in $TMPDIR])
@@ -1601,7 +1601,7 @@ then
                fi
 
                AC_MSG_CHECKING([for jni_md.h])
-               TMPDIR=`find -L "$with_java_home" -name jni_md.h -type f -exec 'dirname' '{}' ';' | head -n 1`
+               TMPDIR=`find "$with_java_home" -name jni_md.h -type f -exec 'dirname' '{}' ';' | head -n 1`
                if test "x$TMPDIR" != "x"
                then
                        AC_MSG_RESULT([found in $TMPDIR])
@@ -1611,7 +1611,7 @@ then
                fi
 
                AC_MSG_CHECKING([for libjvm.so])
-               TMPDIR=`find -L "$with_java_home" -name libjvm.so -type f -exec 'dirname' '{}' ';' | head -n 1`
+               TMPDIR=`find "$with_java_home" -name libjvm.so -type f -exec 'dirname' '{}' ';' | head -n 1`
                if test "x$TMPDIR" != "x"
                then
                        AC_MSG_RESULT([found in $TMPDIR])
@@ -1623,7 +1623,7 @@ then
                if test "x$JAVAC" = "x"
                then
                        AC_MSG_CHECKING([for javac])
-                       TMPDIR=`find -L "$with_java_home" -name javac -type f | head -n 1`
+                       TMPDIR=`find "$with_java_home" -name javac -type f | head -n 1`
                        if test "x$TMPDIR" != "x"
                        then
                                JAVAC="$TMPDIR"
@@ -1993,7 +1993,7 @@ then
 
        if test "x$LIBNETAPP_LIBS" = "x"
        then
-               LIBNETAPP_LIBS="-lpthread -lxml -ladt -lssl -lm"
+               LIBNETAPP_LIBS="-lpthread -lxml -ladt -lssl -lm -lcrypto -lz"
        fi
        AC_MSG_NOTICE([netapp LIBS: $LIBNETAPP_LIBS])
 
@@ -2751,6 +2751,63 @@ then
 fi
 # }}} --with-python
 
+# --with-librouteros {{{
+AC_ARG_WITH(librouteros, [AS_HELP_STRING([--with-librouteros@<:@=PREFIX@:>@], [Path to librouteros.])],
+[
+ if test "x$withval" = "xyes"
+ then
+        with_librouteros="yes"
+ else if test "x$withval" = "xno"
+ then
+        with_librouteros="no"
+ else
+        with_librouteros="yes"
+        LIBROUTEROS_CPPFLAGS="$LIBROUTEROS_CPPFLAGS -I$withval/include"
+        LIBROUTEROS_LDFLAGS="$LIBROUTEROS_LDFLAGS -L$withval/lib"
+ fi; fi
+],
+[with_librouteros="yes"])
+
+SAVE_CPPFLAGS="$CPPFLAGS"
+SAVE_LDFLAGS="$LDFLAGS"
+
+CPPFLAGS="$CPPFLAGS $LIBROUTEROS_CPPFLAGS"
+LDFLAGS="$LDFLAGS $LIBROUTEROS_LDFLAGS"
+
+if test "x$with_librouteros" = "xyes"
+then
+       if test "x$LIBROUTEROS_CPPFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([librouteros CPPFLAGS: $LIBROUTEROS_CPPFLAGS])
+       fi
+       AC_CHECK_HEADERS(routeros_api.h,
+       [with_librouteros="yes"],
+       [with_librouteros="no ('routeros_api.h' not found)"])
+fi
+if test "x$with_librouteros" = "xyes"
+then
+       if test "x$LIBROUTEROS_LDFLAGS" != "x"
+       then
+               AC_MSG_NOTICE([librouteros LDFLAGS: $LIBROUTEROS_LDFLAGS])
+       fi
+       AC_CHECK_LIB(routeros, ros_interface,
+       [with_librouteros="yes"],
+       [with_librouteros="no (symbol 'ros_interface' not found)"])
+fi
+
+CPPFLAGS="$SAVE_CPPFLAGS"
+LDFLAGS="$SAVE_LDFLAGS"
+
+if test "x$with_librouteros" = "xyes"
+then
+       BUILD_WITH_LIBROUTEROS_CPPFLAGS="$LIBROUTEROS_CPPFLAGS"
+       BUILD_WITH_LIBROUTEROS_LDFLAGS="$LIBROUTEROS_LDFLAGS"
+       AC_SUBST(BUILD_WITH_LIBROUTEROS_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBROUTEROS_LDFLAGS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBROUTEROS, test "x$with_librouteros" = "xyes")
+# }}}
+
 # --with-librrd {{{
 # AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given])
 librrd_cflags=""
@@ -3665,6 +3722,7 @@ plugin_ascent="no"
 plugin_battery="no"
 plugin_bind="no"
 plugin_conntrack="no"
+plugin_contextswitch="no"
 plugin_cpu="no"
 plugin_cpufreq="no"
 plugin_curl_json="no"
@@ -3702,6 +3760,7 @@ if test "x$ac_system" = "xLinux"
 then
        plugin_battery="yes"
        plugin_conntrack="yes"
+       plugin_contextswitch="yes"
        plugin_cpu="yes"
        plugin_cpufreq="yes"
        plugin_disk="yes"
@@ -3937,6 +3996,7 @@ AC_PLUGIN([ascent],      [$plugin_ascent],     [AscentEmu player statistics])
 AC_PLUGIN([battery],     [$plugin_battery],    [Battery statistics])
 AC_PLUGIN([bind],        [$plugin_bind],       [ISC Bind nameserver statistics])
 AC_PLUGIN([conntrack],   [$plugin_conntrack],  [nf_conntrack statistics])
+AC_PLUGIN([contextswitch], [$plugin_contextswitch], [context switch statistics])
 AC_PLUGIN([cpufreq],     [$plugin_cpufreq],    [CPU frequency statistics])
 AC_PLUGIN([cpu],         [$plugin_cpu],        [CPU usage statistics])
 AC_PLUGIN([csv],         [yes],                [CSV output plugin])
@@ -3964,6 +4024,7 @@ 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_hashed], [yes],               [The hashed 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])
@@ -3993,6 +4054,7 @@ AC_PLUGIN([powerdns],    [yes],                [PowerDNS statistics])
 AC_PLUGIN([processes],   [$plugin_processes],  [Process statistics])
 AC_PLUGIN([protocols],   [$plugin_protocols],  [Protocol (IP, TCP, ...) statistics])
 AC_PLUGIN([python],      [$with_python],       [Embed a Python interpreter])
+AC_PLUGIN([routeros],    [$with_librouteros],  [RouterOS plugin])
 AC_PLUGIN([rrdcached],   [$librrd_rrdc_update], [RRDTool output plugin])
 AC_PLUGIN([rrdtool],     [$with_librrd],       [RRDTool output plugin])
 AC_PLUGIN([sensors],     [$with_libsensors],   [lm_sensors statistics])
@@ -4215,6 +4277,7 @@ Configuration:
     libperl . . . . . . . $with_libperl
     libpq . . . . . . . . $with_libpq
     libpthread  . . . . . $with_libpthread
+    librouteros . . . . . $with_librouteros
     librrd  . . . . . . . $with_librrd
     libsensors  . . . . . $with_libsensors
     libstatgrab . . . . . $with_libstatgrab
@@ -4242,6 +4305,7 @@ Configuration:
     battery . . . . . . . $enable_battery
     bind  . . . . . . . . $enable_bind
     conntrack . . . . . . $enable_conntrack
+    contextswitch . . . . $enable_contextswitch
     cpu . . . . . . . . . $enable_cpu
     cpufreq . . . . . . . $enable_cpufreq
     csv . . . . . . . . . $enable_csv
@@ -4269,6 +4333,7 @@ Configuration:
     logfile . . . . . . . $enable_logfile
     madwifi . . . . . . . $enable_madwifi
     match_empty_counter . $enable_match_empty_counter
+    match_hashed  . . . . $enable_match_hashed
     match_regex . . . . . $enable_match_regex
     match_timediff  . . . $enable_match_timediff
     match_value . . . . . $enable_match_value
@@ -4298,6 +4363,7 @@ Configuration:
     processes . . . . . . $enable_processes
     protocols . . . . . . $enable_protocols
     python  . . . . . . . $enable_python
+    routeros  . . . . . . $enable_routeros
     rrdcached . . . . . . $enable_rrdcached
     rrdtool . . . . . . . $enable_rrdtool
     sensors . . . . . . . $enable_sensors
diff --git a/contrib/collectd-network.py b/contrib/collectd-network.py
deleted file mode 100644 (file)
index 445b183..0000000
+++ /dev/null
@@ -1,318 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: utf-8 -*-
-# vim: fileencoding=utf-8
-#
-# Copyright © 2009 Adrian Perez <aperez@igalia.com>
-#
-# Distributed under terms of the GPLv2 license.
-
-"""
-Collectd network protocol implementation.
-"""
-
-import socket
-import struct
-
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-
-from datetime import datetime
-from copy import deepcopy
-
-
-DEFAULT_PORT = 25826
-"""Default port"""
-
-DEFAULT_IPv4_GROUP = "239.192.74.66"
-"""Default IPv4 multicast group"""
-
-DEFAULT_IPv6_GROUP = "ff18::efc0:4a42"
-"""Default IPv6 multicast group"""
-
-
-
-# Message kinds
-TYPE_HOST            = 0x0000
-TYPE_TIME            = 0x0001
-TYPE_PLUGIN          = 0x0002
-TYPE_PLUGIN_INSTANCE = 0x0003
-TYPE_TYPE            = 0x0004
-TYPE_TYPE_INSTANCE   = 0x0005
-TYPE_VALUES          = 0x0006
-TYPE_INTERVAL        = 0x0007
-
-# For notifications
-TYPE_MESSAGE         = 0x0100
-TYPE_SEVERITY        = 0x0101
-
-# DS kinds
-DS_TYPE_COUNTER      = 0
-DS_TYPE_GAUGE        = 1
-
-
-header = struct.Struct("!2H")
-number = struct.Struct("!Q")
-short  = struct.Struct("!H")
-double = struct.Struct("<d")
-
-
-def decode_network_values(ptype, plen, buf):
-    """Decodes a list of DS values in collectd network format
-    """
-    nvalues = short.unpack_from(buf, header.size)[0]
-    off = header.size + short.size + nvalues
-    valskip = double.size
-
-    # Check whether our expected packet size is the reported one
-    assert ((valskip + 1) * nvalues + short.size + header.size) == plen
-    assert double.size == number.size
-
-    result = []
-    for dstype in map(ord, buf[header.size+short.size:off]):
-        if dstype == DS_TYPE_COUNTER:
-            result.append((dstype, number.unpack_from(buf, off)[0]))
-            off += valskip
-        elif dstype == DS_TYPE_GAUGE:
-            result.append((dstype, double.unpack_from(buf, off)[0]))
-            off += valskip
-        else:
-            raise ValueError("DS type %i unsupported" % dstype)
-
-    return result
-
-
-def decode_network_number(ptype, plen, buf):
-    """Decodes a number (64-bit unsigned) in collectd network format.
-    """
-    return number.unpack_from(buf, header.size)[0]
-
-
-def decode_network_string(msgtype, plen, buf):
-    """Decodes a floating point number (64-bit) in collectd network format.
-    """
-    return buf[header.size:plen-1]
-
-
-# Mapping of message types to decoding functions.
-_decoders = {
-    TYPE_VALUES         : decode_network_values,
-    TYPE_TIME           : decode_network_number,
-    TYPE_INTERVAL       : decode_network_number,
-    TYPE_HOST           : decode_network_string,
-    TYPE_PLUGIN         : decode_network_string,
-    TYPE_PLUGIN_INSTANCE: decode_network_string,
-    TYPE_TYPE           : decode_network_string,
-    TYPE_TYPE_INSTANCE  : decode_network_string,
-    TYPE_MESSAGE        : decode_network_string,
-    TYPE_SEVERITY       : decode_network_number,
-}
-
-
-def decode_network_packet(buf):
-    """Decodes a network packet in collectd format.
-    """
-    off = 0
-    blen = len(buf)
-    while off < blen:
-        ptype, plen = header.unpack_from(buf, off)
-
-        if plen > blen - off:
-            raise ValueError("Packet longer than amount of data in buffer")
-
-        if ptype not in _decoders:
-            raise ValueError("Message type %i not recognized" % ptype)
-
-        yield ptype, _decoders[ptype](ptype, plen, buf[off:])
-        off += plen
-
-
-
-
-
-class Data(object):
-    time = 0
-    host = None
-    plugin = None
-    plugininstance = None
-    type = None
-    typeinstance = None
-
-    def __init__(self, **kw):
-        [setattr(self, k, v) for k, v in kw.iteritems()]
-
-    @property
-    def datetime(self):
-        return datetime.fromtimestamp(self.time)
-
-    @property
-    def source(self):
-        buf = StringIO()
-        if self.host:
-            buf.write(self.host)
-        if self.plugin:
-            buf.write("/")
-            buf.write(self.plugin)
-        if self.plugininstance:
-            buf.write("/")
-            buf.write(self.plugininstance)
-        if self.type:
-            buf.write("/")
-            buf.write(self.type)
-        if self.typeinstance:
-            buf.write("/")
-            buf.write(self.typeinstance)
-        return buf.getvalue()
-
-    def __str__(self):
-        return "[%i] %s" % (self.time, self.source)
-
-
-
-class Notification(Data):
-    FAILURE  = 1
-    WARNING  = 2
-    OKAY     = 4
-
-    SEVERITY = {
-        FAILURE: "FAILURE",
-        WARNING: "WARNING",
-        OKAY   : "OKAY",
-    }
-
-    __severity = 0
-    message  = ""
-
-    def __set_severity(self, value):
-        if value in (self.FAILURE, self.WARNING, self.OKAY):
-            self.__severity = value
-
-    severity = property(lambda self: self.__severity, __set_severity)
-
-    @property
-    def severitystring(self):
-        return self.SEVERITY.get(self.severity, "UNKNOWN")
-
-    def __str__(self):
-        return "%s [%s] %s" % (
-                super(Notification, self).__str__(),
-                self.severitystring,
-                self.message)
-
-
-
-class Values(Data, list):
-    def __str__(self):
-        return "%s %s" % (Data.__str__(self), list.__str__(self))
-
-
-
-def interpret_opcodes(iterable):
-    vl = Values()
-    nt = Notification()
-
-    for kind, data in iterable:
-        if kind == TYPE_TIME:
-            vl.time = nt.time = data
-        elif kind == TYPE_INTERVAL:
-            vl.interval = data
-        elif kind == TYPE_HOST:
-            vl.host = nt.host = data
-        elif kind == TYPE_PLUGIN:
-            vl.plugin = nt.plugin = data
-        elif kind == TYPE_PLUGIN_INSTANCE:
-            vl.plugininstance = nt.plugininstance = data
-        elif kind == TYPE_TYPE:
-            vl.type = nt.type = data
-        elif kind == TYPE_TYPE_INSTANCE:
-            vl.typeinstance = nt.typeinstance = data
-        elif kind == TYPE_SEVERITY:
-            nt.severity = data
-        elif kind == TYPE_MESSAGE:
-            nt.message = data
-            yield deepcopy(nt)
-        elif kind == TYPE_VALUES:
-            vl[:] = data
-            yield deepcopy(vl)
-
-
-
-class Reader(object):
-    """Network reader for collectd data.
-
-    Listens on the network in a given address, which can be a multicast
-    group address, and handles reading data when it arrives.
-    """
-    addr = None
-    host = None
-    port = DEFAULT_PORT
-
-    BUFFER_SIZE = 1024
-
-
-    def __init__(self, host=None, port=DEFAULT_PORT, multicast=False):
-        if host is None:
-            multicast = True
-            host = DEFAULT_IPv4_GROUP
-
-        self.host, self.port = host, port
-        self.ipv6 = ":" in self.host
-
-        family, socktype, proto, canonname, sockaddr = socket.getaddrinfo(
-                None if multicast else self.host, self.port,
-                socket.AF_INET6 if self.ipv6 else socket.AF_UNSPEC,
-                socket.SOCK_DGRAM, 0, socket.AI_PASSIVE)[0]
-
-        self._sock = socket.socket(family, socktype, proto)
-        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        self._sock.bind(sockaddr)
-
-        if multicast:
-            if hasattr(socket, "SO_REUSEPORT"):
-                self._sock.setsockopt(
-                        socket.SOL_SOCKET,
-                        socket.SO_REUSEPORT, 1)
-
-            val = None
-            if family == socket.AF_INET:
-                assert "." in self.host
-                val = struct.pack("4sl",
-                        socket.inet_aton(self.host), socket.INADDR_ANY)
-            elif family == socket.AF_INET6:
-                raise NotImplementedError("IPv6 support not ready yet")
-            else:
-                raise ValueError("Unsupported network address family")
-
-            self._sock.setsockopt(
-                    socket.IPPROTO_IPV6 if self.ipv6 else socket.IPPROTO_IP,
-                    socket.IP_ADD_MEMBERSHIP, val)
-            self._sock.setsockopt(
-                    socket.IPPROTO_IPV6 if self.ipv6 else socket.IPPROTO_IP,
-                    socket.IP_MULTICAST_LOOP, 0)
-
-
-    def receive(self):
-        """Receives a single raw collect network packet.
-        """
-        return self._sock.recv(self.BUFFER_SIZE)
-
-
-    def decode(self, buf=None):
-        """Decodes a given buffer or the next received packet.
-        """
-        if buf is None:
-            buf = self.receive()
-        return decode_network_packet(buf)
-
-
-    def interpret(self, iterable=None):
-        """Interprets a sequence
-        """
-        if iterable is None:
-            iterable = self.decode()
-        if isinstance(iterable, basestring):
-            iterable = self.decode(iterable)
-        return interpret_opcodes(iterable)
-
-
diff --git a/contrib/collectd-unixsock.py b/contrib/collectd-unixsock.py
deleted file mode 100644 (file)
index 2d7430d..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#-*- coding: ISO-8859-1 -*-
-# collect.py: the python collectd-unixsock module.
-#
-# Requires collectd to be configured with the unixsock plugin, like so:
-#
-# LoadPlugin unixsock
-# <Plugin unixsock>
-#   SocketFile "/var/run/collectd-unixsock"
-#   SocketPerms "0775"
-# </Plugin>
-#
-# Copyright (C) 2008 Clay Loveless <clay@killersoft.com>
-#
-# This software is provided 'as-is', without any express or implied
-# warranty.  In no event will the author be held liable for any damages
-# arising from the use of this software.
-#
-# Permission is granted to anyone to use this software for any purpose,
-# including commercial applications, and to alter it and redistribute it
-# freely, subject to the following restrictions:
-#
-# 1. The origin of this software must not be misrepresented; you must not
-#    claim that you wrote the original software. If you use this software
-#    in a product, an acknowledgment in the product documentation would be
-#    appreciated but is not required.
-# 2. Altered source versions must be plainly marked as such, and must not be
-#    misrepresented as being the original software.
-# 3. This notice may not be removed or altered from any source distribution.
-
-import socket, string
-
-class Collect(object):
-
-    def __init__(self, path='/var/run/collectd-unixsock'):
-        self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-        self._path =  path
-        self._sock.connect(self._path)
-        
-    def list(self):
-        numvalues = self._cmd('LISTVAL')
-        lines = []
-        if numvalues:
-            lines = self._readlines(numvalues)
-        return lines
-        
-    def get(self, val, flush=True):
-        numvalues = self._cmd('GETVAL "' + val + '"')
-        lines = []
-        if numvalues:
-            lines = self._readlines(numvalues)
-        if flush:
-            self._cmd('FLUSH identifier="' + val + '"')
-        return lines
-            
-    def _cmd(self, c):
-        self._sock.send(c + "\n")
-        stat = string.split(self._readline())
-        status = int(stat[0])
-        if status:
-            return status
-        return False
-    
-    '''
-    _readline and _readlines methods borrowed from the _fileobject class 
-    in sockets.py, tweaked a little bit for use in the collectd context.
-    '''
-    def _readline(self):
-        data = ''
-        buf = []
-        recv = self._sock.recv
-        while data != "\n":
-            data = recv(1)
-            if not data:
-                break
-            if data != "\n":
-                buf.append(data)
-        return ''.join(buf)
-        
-    def _readlines(self, sizehint=0):
-        total = 0
-        list = []
-        while True:
-            line = self._readline()
-            if not line:
-                break
-            list.append(line)
-            total = len(list)
-            if sizehint and total >= sizehint:
-                break
-        return list
-    
-    def __del__(self):
-        self._sock.close()    
-
-
-
-if __name__ == '__main__':
-    
-    '''
-    Example usage:
-    Collect values from socket and dump to STDOUT.
-    '''
-    
-    c = Collect('/var/run/collectd-unixsock')
-    list = c.list()
-
-    for val in list:
-        stamp, key = string.split(val)
-        glines = c.get(key)
-        print stamp + ' ' + key + ' ' + ', '.join(glines)
-    
diff --git a/contrib/collectd_network.py b/contrib/collectd_network.py
new file mode 100644 (file)
index 0000000..445b183
--- /dev/null
@@ -0,0 +1,318 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+# vim: fileencoding=utf-8
+#
+# Copyright © 2009 Adrian Perez <aperez@igalia.com>
+#
+# Distributed under terms of the GPLv2 license.
+
+"""
+Collectd network protocol implementation.
+"""
+
+import socket
+import struct
+
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
+from datetime import datetime
+from copy import deepcopy
+
+
+DEFAULT_PORT = 25826
+"""Default port"""
+
+DEFAULT_IPv4_GROUP = "239.192.74.66"
+"""Default IPv4 multicast group"""
+
+DEFAULT_IPv6_GROUP = "ff18::efc0:4a42"
+"""Default IPv6 multicast group"""
+
+
+
+# Message kinds
+TYPE_HOST            = 0x0000
+TYPE_TIME            = 0x0001
+TYPE_PLUGIN          = 0x0002
+TYPE_PLUGIN_INSTANCE = 0x0003
+TYPE_TYPE            = 0x0004
+TYPE_TYPE_INSTANCE   = 0x0005
+TYPE_VALUES          = 0x0006
+TYPE_INTERVAL        = 0x0007
+
+# For notifications
+TYPE_MESSAGE         = 0x0100
+TYPE_SEVERITY        = 0x0101
+
+# DS kinds
+DS_TYPE_COUNTER      = 0
+DS_TYPE_GAUGE        = 1
+
+
+header = struct.Struct("!2H")
+number = struct.Struct("!Q")
+short  = struct.Struct("!H")
+double = struct.Struct("<d")
+
+
+def decode_network_values(ptype, plen, buf):
+    """Decodes a list of DS values in collectd network format
+    """
+    nvalues = short.unpack_from(buf, header.size)[0]
+    off = header.size + short.size + nvalues
+    valskip = double.size
+
+    # Check whether our expected packet size is the reported one
+    assert ((valskip + 1) * nvalues + short.size + header.size) == plen
+    assert double.size == number.size
+
+    result = []
+    for dstype in map(ord, buf[header.size+short.size:off]):
+        if dstype == DS_TYPE_COUNTER:
+            result.append((dstype, number.unpack_from(buf, off)[0]))
+            off += valskip
+        elif dstype == DS_TYPE_GAUGE:
+            result.append((dstype, double.unpack_from(buf, off)[0]))
+            off += valskip
+        else:
+            raise ValueError("DS type %i unsupported" % dstype)
+
+    return result
+
+
+def decode_network_number(ptype, plen, buf):
+    """Decodes a number (64-bit unsigned) in collectd network format.
+    """
+    return number.unpack_from(buf, header.size)[0]
+
+
+def decode_network_string(msgtype, plen, buf):
+    """Decodes a floating point number (64-bit) in collectd network format.
+    """
+    return buf[header.size:plen-1]
+
+
+# Mapping of message types to decoding functions.
+_decoders = {
+    TYPE_VALUES         : decode_network_values,
+    TYPE_TIME           : decode_network_number,
+    TYPE_INTERVAL       : decode_network_number,
+    TYPE_HOST           : decode_network_string,
+    TYPE_PLUGIN         : decode_network_string,
+    TYPE_PLUGIN_INSTANCE: decode_network_string,
+    TYPE_TYPE           : decode_network_string,
+    TYPE_TYPE_INSTANCE  : decode_network_string,
+    TYPE_MESSAGE        : decode_network_string,
+    TYPE_SEVERITY       : decode_network_number,
+}
+
+
+def decode_network_packet(buf):
+    """Decodes a network packet in collectd format.
+    """
+    off = 0
+    blen = len(buf)
+    while off < blen:
+        ptype, plen = header.unpack_from(buf, off)
+
+        if plen > blen - off:
+            raise ValueError("Packet longer than amount of data in buffer")
+
+        if ptype not in _decoders:
+            raise ValueError("Message type %i not recognized" % ptype)
+
+        yield ptype, _decoders[ptype](ptype, plen, buf[off:])
+        off += plen
+
+
+
+
+
+class Data(object):
+    time = 0
+    host = None
+    plugin = None
+    plugininstance = None
+    type = None
+    typeinstance = None
+
+    def __init__(self, **kw):
+        [setattr(self, k, v) for k, v in kw.iteritems()]
+
+    @property
+    def datetime(self):
+        return datetime.fromtimestamp(self.time)
+
+    @property
+    def source(self):
+        buf = StringIO()
+        if self.host:
+            buf.write(self.host)
+        if self.plugin:
+            buf.write("/")
+            buf.write(self.plugin)
+        if self.plugininstance:
+            buf.write("/")
+            buf.write(self.plugininstance)
+        if self.type:
+            buf.write("/")
+            buf.write(self.type)
+        if self.typeinstance:
+            buf.write("/")
+            buf.write(self.typeinstance)
+        return buf.getvalue()
+
+    def __str__(self):
+        return "[%i] %s" % (self.time, self.source)
+
+
+
+class Notification(Data):
+    FAILURE  = 1
+    WARNING  = 2
+    OKAY     = 4
+
+    SEVERITY = {
+        FAILURE: "FAILURE",
+        WARNING: "WARNING",
+        OKAY   : "OKAY",
+    }
+
+    __severity = 0
+    message  = ""
+
+    def __set_severity(self, value):
+        if value in (self.FAILURE, self.WARNING, self.OKAY):
+            self.__severity = value
+
+    severity = property(lambda self: self.__severity, __set_severity)
+
+    @property
+    def severitystring(self):
+        return self.SEVERITY.get(self.severity, "UNKNOWN")
+
+    def __str__(self):
+        return "%s [%s] %s" % (
+                super(Notification, self).__str__(),
+                self.severitystring,
+                self.message)
+
+
+
+class Values(Data, list):
+    def __str__(self):
+        return "%s %s" % (Data.__str__(self), list.__str__(self))
+
+
+
+def interpret_opcodes(iterable):
+    vl = Values()
+    nt = Notification()
+
+    for kind, data in iterable:
+        if kind == TYPE_TIME:
+            vl.time = nt.time = data
+        elif kind == TYPE_INTERVAL:
+            vl.interval = data
+        elif kind == TYPE_HOST:
+            vl.host = nt.host = data
+        elif kind == TYPE_PLUGIN:
+            vl.plugin = nt.plugin = data
+        elif kind == TYPE_PLUGIN_INSTANCE:
+            vl.plugininstance = nt.plugininstance = data
+        elif kind == TYPE_TYPE:
+            vl.type = nt.type = data
+        elif kind == TYPE_TYPE_INSTANCE:
+            vl.typeinstance = nt.typeinstance = data
+        elif kind == TYPE_SEVERITY:
+            nt.severity = data
+        elif kind == TYPE_MESSAGE:
+            nt.message = data
+            yield deepcopy(nt)
+        elif kind == TYPE_VALUES:
+            vl[:] = data
+            yield deepcopy(vl)
+
+
+
+class Reader(object):
+    """Network reader for collectd data.
+
+    Listens on the network in a given address, which can be a multicast
+    group address, and handles reading data when it arrives.
+    """
+    addr = None
+    host = None
+    port = DEFAULT_PORT
+
+    BUFFER_SIZE = 1024
+
+
+    def __init__(self, host=None, port=DEFAULT_PORT, multicast=False):
+        if host is None:
+            multicast = True
+            host = DEFAULT_IPv4_GROUP
+
+        self.host, self.port = host, port
+        self.ipv6 = ":" in self.host
+
+        family, socktype, proto, canonname, sockaddr = socket.getaddrinfo(
+                None if multicast else self.host, self.port,
+                socket.AF_INET6 if self.ipv6 else socket.AF_UNSPEC,
+                socket.SOCK_DGRAM, 0, socket.AI_PASSIVE)[0]
+
+        self._sock = socket.socket(family, socktype, proto)
+        self._sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self._sock.bind(sockaddr)
+
+        if multicast:
+            if hasattr(socket, "SO_REUSEPORT"):
+                self._sock.setsockopt(
+                        socket.SOL_SOCKET,
+                        socket.SO_REUSEPORT, 1)
+
+            val = None
+            if family == socket.AF_INET:
+                assert "." in self.host
+                val = struct.pack("4sl",
+                        socket.inet_aton(self.host), socket.INADDR_ANY)
+            elif family == socket.AF_INET6:
+                raise NotImplementedError("IPv6 support not ready yet")
+            else:
+                raise ValueError("Unsupported network address family")
+
+            self._sock.setsockopt(
+                    socket.IPPROTO_IPV6 if self.ipv6 else socket.IPPROTO_IP,
+                    socket.IP_ADD_MEMBERSHIP, val)
+            self._sock.setsockopt(
+                    socket.IPPROTO_IPV6 if self.ipv6 else socket.IPPROTO_IP,
+                    socket.IP_MULTICAST_LOOP, 0)
+
+
+    def receive(self):
+        """Receives a single raw collect network packet.
+        """
+        return self._sock.recv(self.BUFFER_SIZE)
+
+
+    def decode(self, buf=None):
+        """Decodes a given buffer or the next received packet.
+        """
+        if buf is None:
+            buf = self.receive()
+        return decode_network_packet(buf)
+
+
+    def interpret(self, iterable=None):
+        """Interprets a sequence
+        """
+        if iterable is None:
+            iterable = self.decode()
+        if isinstance(iterable, basestring):
+            iterable = self.decode(iterable)
+        return interpret_opcodes(iterable)
+
+
diff --git a/contrib/collectd_unixsock.py b/contrib/collectd_unixsock.py
new file mode 100644 (file)
index 0000000..ebe549c
--- /dev/null
@@ -0,0 +1,201 @@
+#-*- coding: ISO-8859-1 -*-
+# collect.py: the python collectd-unixsock module.
+#
+# Requires collectd to be configured with the unixsock plugin, like so:
+#
+# LoadPlugin unixsock
+# <Plugin unixsock>
+#   SocketFile "/var/run/collectd-unixsock"
+#   SocketPerms "0775"
+# </Plugin>
+#
+# Copyright (C) 2008 Clay Loveless <clay@killersoft.com>
+#
+# This software is provided 'as-is', without any express or implied
+# warranty.  In no event will the author be held liable for any damages
+# arising from the use of this software.
+#
+# Permission is granted to anyone to use this software for any purpose,
+# including commercial applications, and to alter it and redistribute it
+# freely, subject to the following restrictions:
+#
+# 1. The origin of this software must not be misrepresented; you must not
+#    claim that you wrote the original software. If you use this software
+#    in a product, an acknowledgment in the product documentation would be
+#    appreciated but is not required.
+# 2. Altered source versions must be plainly marked as such, and must not be
+#    misrepresented as being the original software.
+# 3. This notice may not be removed or altered from any source distribution.
+
+import socket
+
+
+class Collectd():
+
+    def __init__(self, path='/var/run/collectd-unixsock', noisy=False):
+        self.noisy = noisy
+        self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        self._sock.connect(path)
+
+    def flush(self, timeout=None, plugins=[], identifiers=[]):
+        """Send a FLUSH command.
+
+        Full documentation:
+            http://collectd.org/wiki/index.php/Plain_text_protocol#FLUSH
+
+        """
+        # have to pass at least one plugin or identifier
+        if not plugins and not identifiers:
+            return None
+        args = []
+        if timeout:
+            args.append("timeout=%s" % timeout)
+        if plugins:
+            plugin_args = map(lambda x: "plugin=%s" % x, plugins)
+            args.extend(plugin_args)
+        if identifiers:
+            identifier_args = map(lambda x: "identifier=%s" % x, identifiers)
+            args.extend(identifier_args)
+        return self._cmd('FLUSH %s' % ' '.join(args))
+
+    def getthreshold(self, identifier):
+        """Send a GETTHRESHOLD command.
+
+        Full documentation:
+            http://collectd.org/wiki/index.php/Plain_text_protocol#GETTHRESHOLD
+
+        """
+        numvalues = self._cmd('GETTHRESHOLD "%s"' % identifier)
+        lines = []
+        if numvalues:
+            lines = self._readlines(numvalues)
+        return lines
+
+    def getval(self, identifier, flush_after=True):
+        """Send a GETVAL command.
+
+        Also flushes the identifier if flush_after is True.
+
+        Full documentation:
+            http://collectd.org/wiki/index.php/Plain_text_protocol#GETVAL
+
+        """
+        numvalues = self._cmd('GETVAL "%s"' % identifier)
+        lines = []
+        if numvalues:
+            lines = self._readlines(numvalues)
+        if flush_after:
+            self.flush(identifiers=[identifier])
+        return lines
+
+    def listval(self):
+        """Send a LISTVAL command.
+
+        Full documentation:
+            http://collectd.org/wiki/index.php/Plain_text_protocol#LISTVAL
+
+        """
+        numvalues = self._cmd('LISTVAL')
+        lines = []
+        if numvalues:
+            lines = self._readlines(numvalues)
+        return lines
+
+    def putnotif(self, message, options={}):
+        """Send a PUTNOTIF command.
+
+        Options must be passed as a Python dictionary. Example:
+          options={'severity': 'failure', 'host': 'example.com'}
+
+        Full documentation:
+            http://collectd.org/wiki/index.php/Plain_text_protocol#PUTNOTIF
+
+        """
+        args = []
+        if options:
+            options_args = map(lambda x: "%s=%s" % (x, options[x]), options)
+            args.extend(options_args)
+        args.append('message="%s"' % message)
+        return self._cmd('PUTNOTIF %s' % ' '.join(args))
+
+    def putval(self, identifier, values, options={}):
+        """Send a PUTVAL command.
+
+        Options must be passed as a Python dictionary. Example:
+          options={'interval': 10}
+
+        Full documentation:
+            http://collectd.org/wiki/index.php/Plain_text_protocol#PUTVAL
+
+        """
+        args = []
+        args.append('"%s"' % identifier)
+        if options:
+            options_args = map(lambda x: "%s=%s" % (x, options[x]), options)
+            args.extend(options_args)
+        values = map(str, values)
+        args.append(':'.join(values))
+        return self._cmd('PUTVAL %s' % ' '.join(args))
+
+    def _cmd(self, c):
+        if self.noisy:
+            print "[send] %s" % c
+        self._sock.send(c + "\n")
+        status_message = self._readline()
+        if self.noisy:
+            print "[recive] %s" % status_message
+        if not status_message:
+            return None
+        code, message = status_message.split(' ', 1)
+        if int(code):
+            return int(code)
+        return False
+
+    def _readline(self):
+        """Read single line from socket"""
+        data = ''
+        buf = []
+        recv = self._sock.recv
+        while data != "\n":
+            data = recv(1)
+            if not data:
+                break
+            if data != "\n":
+                buf.append(data)
+        return ''.join(buf)
+
+    def _readlines(self, sizehint=0):
+        """Read multiple lines from socket"""
+        total = 0
+        list = []
+        while True:
+            line = self._readline()
+            if not line:
+                break
+            list.append(line)
+            total = len(list)
+            if sizehint and total >= sizehint:
+                break
+        return list
+
+    def __del__(self):
+        self._sock.close()
+
+
+if __name__ == '__main__':
+    """Collect values from socket and dump to STDOUT"""
+
+    c = Collectd('/var/run/collectd-unixsock', noisy=True)
+    list = c.listval()
+    for val in list:
+        stamp, identifier = val.split()
+        print "\n%s" % identifier
+        print "\tUpdate time: %s" % stamp
+
+        values = c.getval(identifier)
+        print "\tValue list: %s" % ', '.join(values)
+
+        # don't fetch thresholds by default because collectd will crash
+        # if there is no treshold for the given identifier
+        #thresholds = c.getthreshold(identifier)
+        #print "\tThresholds: %s" % ', '.join(thresholds)
index 1322d0d..e56017e 100644 (file)
@@ -139,21 +139,23 @@ GraphWidth 400
 <Type df_complex>
   Module GenericStacked
   DataSources value
-  RRDTitle "disk usage on {plugin_instance}"
+  RRDTitle "Disk/Volume usage on {plugin_instance}"
   RRDVerticalLabel "Byte"
-  RRDFormat "%6.2lf%s"
-  DSName "snap_used    used for snapshots"
-  DSName "snap_reserved snapshot reserve "
-  DSName "used         in use            "
-  DSName "free         free              "
-  DSName "sis_saved    sis_saved         "
-  Order free snap_used snap_reserved sis_saved used
+  RRDFormat "%5.1lf%s"
+  DSName "sis_saved         SIS saved         "
+  DSName "free              Free              "
+  DSName "used              Used              "
+  DSName "snap_normal_used  Snap used (normal)"
+  DSName "snap_reserved     Snap reserved     "
+  DSName "snap_reserve_used Snap used (resv)  "
+  Order sis_saved free used snap_normal_used snap_reserved snap_reserve_used
+  Color sis_saved 00e0e0
+  Color free  00ff00
   Color snap_reverse   ff8000
   Color used  ff0000
-  Color snap_used   000080
-  Color snap_reserved   ff8000
-  Color free  00ff00
-  Color sis_saved 00e0e0
+  Color snap_normal_used  c10640
+  Color snap_reserved     f15aef
+  Color snap_reserve_used 820c81
 </Type>
 <Type disk_latency>
   Module GenericIO
index fd8d404..07381db 100644 (file)
        InstancePrefix "input"
        Values ".1.3.6.1.4.1.1909.10.6.1.1.3"
     </Data>
+
+    #
+    # Mikrotik RouterBoards
+    #
+    # Wireless statistics: station mode
+    <Data "mikrotik_wl_sta_bitrate_tx">
+        tYPE "Bitrate"
+        Table true
+        InstancePrefix "st-tx-"
+        Instance ".1.3.6.1.4.1.14988.1.1.1.1.1.5"
+        Values ".1.3.6.1.4.1.14988.1.1.1.1.1.2"
+    </Data>
+
+    <Data "mikrotik_wl_sta_bitrate_rx">
+        Type "bitrate"
+        Table true
+        InstancePrefix "st-rx-"
+        Instance ".1.3.6.1.4.1.14988.1.1.1.1.1.5"
+        Values ".1.3.6.1.4.1.14988.1.1.1.1.1.3"
+    </Data>
+
+    <Data "mikrotik_wl_sta_signal">
+        Type "signal_power"
+        Table true
+        InstancePrefix "st-"
+        Instance ".1.3.6.1.4.1.14988.1.1.1.1.1.5"
+        Values ".1.3.6.1.4.1.14988.1.1.1.1.1.4"
+    </Data>
+
+    # Wireless statistics: AP mode / registration table
+    <Data "mikrotik_wl_reg_signal">
+        Type "signal_power"
+        Table true
+        InstancePrefix "ap-"
+        Instance ".1.3.6.1.4.1.14988.1.1.1.2.1.1"
+        Values ".1.3.6.1.4.1.14988.1.1.1.2.1.3"
+    </Data>
+
+    <Data "mikrotik_wl_reg_octets">
+        Type "if_octets"
+        Table true
+        InstancePrefix "ap-"
+        Instance ".1.3.6.1.4.1.14988.1.1.1.2.1.1"
+        Values ".1.3.6.1.4.1.14988.1.1.1.2.1.5" ".1.3.6.1.4.1.14988.1.1.1.2.1.4"
+    </Data>
+
+    <Data "mikrotik_wl_reg_packets">
+        Type "if_packets"
+        Table true
+        InstancePrefix "ap-"
+        Instance ".1.3.6.1.4.1.14988.1.1.1.2.1.1"
+        Values ".1.3.6.1.4.1.14988.1.1.1.2.1.7" ".1.3.6.1.4.1.14988.1.1.1.2.1.6"
+    </Data>
+
+    <Data "mikrotik_wl_reg_bitrate_tx">
+        Type "bitrate"
+        Table true
+        InstancePrefix "ap-tx-"
+        Instance ".1.3.6.1.4.1.14988.1.1.1.2.1.1"
+        Values ".1.3.6.1.4.1.14988.1.1.1.2.1.8"
+    </Data>
+
+    <Data "mikrotik_wl_reg_bitrate_rx">
+        Type "bitrate"
+        Table true
+        InstancePrefix "ap-rx-"
+        Instance ".1.3.6.1.4.1.14988.1.1.1.2.1.1"
+        Values ".1.3.6.1.4.1.14988.1.1.1.2.1.9"
+    </Data>
 </Plugin>
index b240618..4b9fa0e 100644 (file)
@@ -173,6 +173,14 @@ collectd_LDADD += "-dlopen" conntrack.la
 collectd_DEPENDENCIES += conntrack.la
 endif
 
+if BUILD_PLUGIN_CONTEXTSWITCH
+pkglib_LTLIBRARIES += contextswitch.la
+contextswitch_la_SOURCES = contextswitch.c
+contextswitch_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" contextswitch.la
+collectd_DEPENDENCIES += contextswitch.la
+endif
+
 if BUILD_PLUGIN_CPU
 pkglib_LTLIBRARIES += cpu.la
 cpu_la_SOURCES = cpu.c
@@ -470,6 +478,14 @@ collectd_LDADD += "-dlopen" match_empty_counter.la
 collectd_DEPENDENCIES += match_empty_counter.la
 endif
 
+if BUILD_PLUGIN_MATCH_HASHED
+pkglib_LTLIBRARIES += match_hashed.la
+match_hashed_la_SOURCES = match_hashed.c
+match_hashed_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" match_hashed.la
+collectd_DEPENDENCIES += match_hashed.la
+endif
+
 if BUILD_PLUGIN_MATCH_REGEX
 pkglib_LTLIBRARIES += match_regex.la
 match_regex_la_SOURCES = match_regex.c
@@ -804,6 +820,16 @@ collectd_LDADD += "-dlopen" protocols.la
 collectd_DEPENDENCIES += protocols.la
 endif
 
+if BUILD_PLUGIN_ROUTEROS
+pkglib_LTLIBRARIES += routeros.la
+routeros_la_SOURCES = routeros.c
+routeros_la_CPPFLAGS = $(BUILD_WITH_LIBROUTEROS_CPPFLAGS)
+routeros_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBROUTEROS_LDFLAGS)
+routeros_la_LIBADD = -lrouteros
+collectd_LDADD += "-dlopen" routeros.la
+collectd_DEPENDENCIES += routeros.la
+endif
+
 if BUILD_PLUGIN_RRDCACHED
 pkglib_LTLIBRARIES += rrdcached.la
 rrdcached_la_SOURCES = rrdcached.c utils_rrdcreate.c utils_rrdcreate.h
@@ -1141,7 +1167,7 @@ EXTRA_DIST +=   collectd.conf.pod \
 
 .pod.1:
        pod2man --release=$(VERSION) --center=$(PACKAGE) $< \
-               >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true
+               >.pod2man.tmp.$$$$ 2>/dev/null && mv -f .pod2man.tmp.$$$$ $@ || true
        @if grep '\<POD ERRORS\>' $@ >/dev/null 2>&1; \
        then \
                echo "$@ has some POD errors!"; false; \
@@ -1149,7 +1175,7 @@ EXTRA_DIST +=   collectd.conf.pod \
 
 .pod.5:
        pod2man --section=5 --release=$(VERSION) --center=$(PACKAGE) $< \
-               >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true
+               >.pod2man.tmp.$$$$ 2>/dev/null && mv -f .pod2man.tmp.$$$$ $@ || true
        @if grep '\<POD ERRORS\>' $@ >/dev/null 2>&1; \
        then \
                echo "$@ has some POD errors!"; false; \
index a333bf2..df1b560 100644 (file)
@@ -212,13 +212,9 @@ static int config_set_boolean (int *ret_boolean, /* {{{ */
        else /* if (ci->values[0].type != OCONFIG_TYPE_STRING) */
        {
                char *string = ci->values[0].value.string;
-               if ((strcasecmp ("true", string) == 0)
-                               || (strcasecmp ("yes", string) == 0)
-                               || (strcasecmp ("on", string) == 0))
+               if (IS_TRUE (string))
                        *ret_boolean = 1;
-               else if ((strcasecmp ("false", string) == 0)
-                               || (strcasecmp ("no", string) == 0)
-                               || (strcasecmp ("off", string) == 0))
+               else if (IS_FALSE (string))
                        *ret_boolean = 0;
                else
                {
index 1e7eca1..6782fce 100644 (file)
@@ -562,12 +562,12 @@ static int ascent_init (void) /* {{{ */
   curl_easy_setopt (curl, CURLOPT_URL, url);
   curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
 
-  if ((verify_peer == NULL) || (strcmp (verify_peer, "true") == 0))
+  if ((verify_peer == NULL) || IS_TRUE (verify_peer))
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
   else
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0);
 
-  if ((verify_host == NULL) || (strcmp (verify_host, "true") == 0))
+  if ((verify_host == NULL) || IS_TRUE (verify_host))
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
   else
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0);
index 136c75e..ac679c5 100644 (file)
@@ -641,12 +641,13 @@ L<python(1)>,
 
 =head1 AUTHOR
 
-The C<python plugin> has been written by Sebastian Harl
-E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
+The C<python plugin> has been written by
+Sven Trenkel E<lt>collectdE<nbsp>atE<nbsp>semidefinite.deE<gt>.
 
-This manpage has been written by Florian Forster
-E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt> and Sebastian Harl
-E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
+This manpage has been written by Sven Trenkel
+E<lt>collectdE<nbsp>atE<nbsp>semidefinite.deE<gt>.
+It is based on the L<collectd-perl(5)> manual page by
+Florian Forster E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt> and
+Sebastian Harl E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
 
 =cut
-
index f34113d..51e0481 100644 (file)
@@ -184,7 +184,7 @@ This value is not applied to counter-values.
 I<Value> is added to gauge-values returned by the SNMP-agent after they have
 been multiplied by any B<Scale> value. If, for example, a thermometer returns
 degrees Kelvin you could specify a shift of B<273.15> here to store values in
-degrees Celsius. The default value is is course B<0.0>.
+degrees Celsius. The default value is of course B<0.0>.
 
 This value is not applied to counter-values.
 
index 576abef..bc69a3b 100644 (file)
@@ -102,9 +102,7 @@ static int init_hostname (void)
        }
 
        str = global_option_get ("FQDNLookup");
-       if ((strcasecmp ("false", str) == 0)
-                       || (strcasecmp ("no", str) == 0)
-                       || (strcasecmp ("off", str) == 0))
+       if (IS_FALSE (str))
                return (0);
 
        memset (&ai_hints, '\0', sizeof (ai_hints));
index e78f9dc..ad13353 100644 (file)
@@ -57,6 +57,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_BATTERY_TRUE@LoadPlugin battery
 #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
+#@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 #@BUILD_PLUGIN_CPUFREQ_TRUE@LoadPlugin cpufreq
 @LOAD_PLUGIN_CSV@LoadPlugin csv
@@ -81,6 +82,7 @@ FQDNLookup   true
 #@BUILD_PLUGIN_JAVA_TRUE@LoadPlugin java
 #@BUILD_PLUGIN_LIBVIRT_TRUE@LoadPlugin libvirt
 @BUILD_PLUGIN_LOAD_TRUE@@BUILD_PLUGIN_LOAD_TRUE@LoadPlugin load
+#@BUILD_PLUGIN_MADWIFI_TRUE@LoadPlugin madwifi
 #@BUILD_PLUGIN_MBMON_TRUE@LoadPlugin mbmon
 #@BUILD_PLUGIN_MEMCACHEC_TRUE@LoadPlugin memcachec
 #@BUILD_PLUGIN_MEMCACHED_TRUE@LoadPlugin memcached
@@ -175,7 +177,26 @@ FQDNLookup   true
 #  </View>
 #</Plugin>
 
-#<Plugin couchdb>
+#<Plugin csv>
+#      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/csv"
+#      StoreRates false
+#</Plugin>
+
+#<Plugin curl>
+#  <Page "stock_quotes">
+#    URL "http://finance.google.com/finance?q=NYSE%3AAMD"
+#    User "foo"
+#    Password "bar"
+#    <Match>
+#      Regex "<span +class=\"pr\"[^>]*> *([0-9]*\\.[0-9]+) *</span>"
+#      DSType "GaugeAverage"
+#      Type "stock_value"
+#      Instance "AMD"
+#    </Match>
+#  </Page>
+#</Plugin>
+
+#<Plugin curl_json>
 ## See: http://wiki.apache.org/couchdb/Runtime_Statistics
 #  <URL "http://localhost:5984/_stats">
 #    Instance "httpd"
@@ -206,25 +227,6 @@ FQDNLookup   true
 #  </URL>
 #</Plugin>
 
-#<Plugin csv>
-#      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/csv"
-#      StoreRates false
-#</Plugin>
-
-#<Plugin curl>
-#  <Page "stock_quotes">
-#    URL "http://finance.google.com/finance?q=NYSE%3AAMD"
-#    User "foo"
-#    Password "bar"
-#    <Match>
-#      Regex "<span +class=\"pr\"[^>]*> *([0-9]*\\.[0-9]+) *</span>"
-#      DSType "GaugeAverage"
-#      Type "stock_value"
-#      Instance "AMD"
-#    </Match>
-#  </Page>
-#</Plugin>
-
 #<Plugin dbi>
 #      <Query "num_of_customers">
 #              Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl"
@@ -263,6 +265,7 @@ FQDNLookup   true
 #<Plugin dns>
 #      Interface "eth0"
 #      IgnoreSource "192.168.0.1"
+#      SelectNumericQueryTypes true
 #</Plugin>
 
 #<Plugin email>
@@ -313,12 +316,6 @@ FQDNLookup   true
 #  TranslateDevicename false
 #</Plugin>
 
-#<Plugin http>
-#      URL "http://example.com/collectd-import"
-#      User "www-user"
-#      Password "secret"
-#</Plugin>
-
 #<Plugin interface>
 #      Interface "eth0"
 #      IgnoreSelected false
@@ -364,6 +361,17 @@ FQDNLookup   true
 #      HostnameFormat name
 #</Plugin>
 
+#<Plugin madwifi>
+#      Interface "wlan0"
+#      IgnoreSelected false
+#      Source "SysFS"
+#      WatchSet "None"
+#      WatchAdd "node_octets"
+#      WatchAdd "node_rssi"
+#      WatchAdd "is_rx_acl"
+#      WatchAdd "is_scan_active"
+#</Plugin>
+
 #<Plugin mbmon>
 #      Host "127.0.0.1"
 #      Port "411"
@@ -421,6 +429,7 @@ FQDNLookup   true
 #      TimeToLive "128"
 #      Forward false
 #      CacheFlush 1800
+#      ReportStats false
 @LOAD_PLUGIN_NETWORK@</Plugin>
 
 #<Plugin nginx>
@@ -730,6 +739,7 @@ FQDNLookup   true
 ##############################################################################
 
 # Load required matches:
+#@BUILD_PLUGIN_MATCH_EMPTY_COUNTER_TRUE@LoadPlugin match_empty_counter
 #@BUILD_PLUGIN_MATCH_REGEX_TRUE@LoadPlugin match_regex
 #@BUILD_PLUGIN_MATCH_VALUE_TRUE@LoadPlugin match_value
 #@BUILD_PLUGIN_MATCH_TIMEDIFF_TRUE@LoadPlugin match_timediff
index f7531cd..71b9989 100644 (file)
@@ -870,19 +870,44 @@ Select partitions based on the mountpoint.
 
 Select partitions based on the filesystem type.
 
-=item B<IgnoreSelected> I<true>|I<false>
+=item B<IgnoreSelected> B<true>|B<false>
 
 Invert the selection: If set to true, all partitions B<except> the ones that
 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>
+=item B<ReportByDevice> B<true>|B<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).
 
+=item B<ReportReserved> B<true>|B<false>
+
+When enabled, the blocks reserved for root are reported separately. When
+disabled (the default for backwards compatibility reasons) the reserved space
+will be included in the "free" space.
+
+When disabled, the "df" type will be used to store "free" and "used" space. The
+mount point or disk name (see option B<ReportByDevice>) is used as type
+instance in this case (again: backwards compatibility).
+
+When enabled, the type "df_complex" is used and three files are created. The
+mount point or disk name is used as plugin instance and the type instance is
+set to "free", "reserved" and "used" as appropriate.
+
+Enabling this option is recommended.
+
+=item B<ReportInodes> B<true>|B<false>
+
+Enables or disables reporting of free, reserved and used inodes. Defaults to
+inode collection being disabled.
+
+Enable this option if inodes are a scarce resource for you, usually because
+many small files are stored on the disk. This is a usual scenario for mail
+transfer agents and web caches.
+
 =back
 
 =head2 Plugin C<disk>
@@ -2316,6 +2341,14 @@ normally doesn't do much, this value should not be too small. The default is
 1800 seconds, but setting this to 86400 seconds (one day) will not do much harm
 either.
 
+=item B<ReportStats> B<true>|B<false>
+
+The network plugin cannot only receive and send statistics, it can also create
+statistics about itself. Collected data included the number of received and
+sent octets and packets, the length of the receive queue and the number of
+values handled. When set to B<true>, the I<Network plugin> will make these
+statistics available. Defaults to B<false>.
+
 =back
 
 =head2 Plugin C<nginx>
@@ -2617,6 +2650,12 @@ Available options:
 
 Specifies the location of the status file.
 
+=item B<Compression> B<true>|B<false>
+
+Sets whether or not statistics about the compression used by OpenVPN should be
+collected. This information is only available in I<single> mode. Enabled by
+default.
+
 =back
 
 =head2 Plugin C<oracle>
@@ -3202,7 +3241,7 @@ C<I<prefix>/var/run/collectd-powerdns>.
 Select more detailed statistics of processes matching this name. The statistics
 collected for these selected processes are size of the resident segment size
 (RSS), user- and system-time used, number of processes and number of threads,
-and minor and major pagefaults.
+io data (where available) and minor and major pagefaults.
 
 =item B<ProcessMatch> I<name> I<regex>
 
@@ -3248,6 +3287,66 @@ matching values will be ignored.
 
 =back
 
+=head2 Plugin C<routeros>
+
+The C<routeros> plugin connects to a device running I<RouterOS>, the
+Linux-based operating system for routers by I<MikroTik>. The plugin uses
+I<librouteros> to connect and reads information about the interfaces and
+wireless connections of the device. The configuration supports querying
+multiple routers:
+
+  <Plugin "routeros">
+    <Router>
+      Host "router0.example.com"
+      User "collectd"
+      Password "secr3t"
+      CollectInterface true
+    </Router>
+    <Router>
+      Host "router1.example.com"
+      User "collectd"
+      Password "5ecret"
+      CollectInterface true
+      CollectRegistrationTable true
+    </Router>
+  </Plugin>
+
+As you can see above, the configuration of the I<routeros> plugin consists of
+one or more B<E<lt>RouterE<gt>> blocks. Within each block, the following
+options are understood:
+
+=over 4
+
+=item B<Host> I<Host>
+
+Hostname or IP-address of the router to connect to.
+
+=item B<Port> I<Port>
+
+Port name or port number used when connecting. If left unspecified, the default
+will be chosen by I<librouteros>, currently "8728". This option expects a
+string argument, even when a numeric port number is given.
+
+=item B<User> I<User>
+
+Use the user name I<User> to authenticate. Defaults to "admin".
+
+=item B<Password> I<Password>
+
+Set the password used to authenticate.
+
+=item B<CollectInterface> B<true>|B<false>
+
+When set to B<true>, interface statistics will be collected for all interfaces
+present on the device. Defaults to B<false>.
+
+=item B<CollectRegistrationTable> B<true>|B<false>
+
+When set to B<true>, information about wireless LAN connections will be
+collected. Defaults to B<false>.
+
+=back
+
 =head2 Plugin C<rrdcached>
 
 The C<rrdcached> plugin uses the RRDtool accelerator daemon, L<rrdcached(1)>,
@@ -4607,6 +4706,77 @@ 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.
 
+=item B<hashed>
+
+Calculates a hash value of the host name and matches values according to that
+hash value. This makes it possible to divide all hosts into groups and match
+only values that are in a specific group. The intended use is in load
+balancing, where you want to handle only part of all data and leave the rest
+for other servers.
+
+The hashing function used tries to distribute the hosts evenly. First, it
+calculates a 32E<nbsp>bit hash value using the characters of the hostname:
+
+  hash_value = 0;
+  for (i = 0; host[i] != 0; i++)
+    hash_value = (hash_value * 251) + host[i];
+
+The constant 251 is a prime number which is supposed to make this hash value
+more random. The code then checks the group for this host according to the
+I<Total> and I<Match> arguments:
+
+  if ((hash_value % Total) == Match)
+    matches;
+  else
+    does not match;
+
+Please note that when you set I<Total> to two (i.E<nbsp>e. you have only two
+groups), then the least significant bit of the hash value will be the XOR of
+all least significant bits in the host name. One consequence is that when you
+have two hosts, "server0.example.com" and "server1.example.com", where the host
+name differs in one digit only and the digits differ by one, those hosts will
+never end up in the same group.
+
+Available options:
+
+=over 4
+
+=item B<Match> I<Match> I<Total>
+
+Divide the data into I<Total> groups and match all hosts in group I<Match> as
+described above. The groups are numbered from zero, i.E<nbsp>e. I<Match> must
+be smaller than I<Total>. I<Total> must be at least one, although only values
+greater than one really do make any sense.
+
+You can repeat this option to match multiple groups, for example:
+
+  Match 3 7
+  Match 5 7
+
+The above config will divide the data into seven groups and match groups three
+and five. One use would be to keep every value on two hosts so that if one
+fails the missing data can later be reconstructed from the second host.
+
+=back
+
+Example:
+
+ # Operate on the pre-cache chain, so that ignored values are not even in the
+ # global cache.
+ <Chain "PreCache">
+   <Rule>
+     <Match "hashed">
+       # Divide all received hosts in seven groups and accept all hosts in
+       # group three.
+       Match 3 7
+     </Match>
+     # If matched: Return and continue.
+     Target "return"
+   </Rule>
+   # If not matched: Return and stop.
+   Target "stop"
+ </Chain>
+
 =back
 
 =head2 Available targets
index 79ad9f6..b2997d6 100644 (file)
@@ -943,7 +943,7 @@ int cf_util_get_string (const oconfig_item_t *ci, char **ret_string) /* {{{ */
 
        if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
        {
-               ERROR ("cf_util_get_string: The %s plugin requires "
+               ERROR ("cf_util_get_string: The %s option requires "
                                "exactly one string argument.", ci->key);
                return (-1);
        }
@@ -959,6 +959,23 @@ int cf_util_get_string (const oconfig_item_t *ci, char **ret_string) /* {{{ */
        return (0);
 } /* }}} int cf_util_get_string */
 
+int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
+{
+       if ((ci == NULL) || (ret_bool == NULL))
+               return (EINVAL);
+
+       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+       {
+               ERROR ("cf_util_get_boolean: The %s option requires "
+                               "exactly one string argument.", ci->key);
+               return (-1);
+       }
+
+       *ret_bool = ci->values[0].value.boolean ? true : false;
+
+       return (0);
+} /* }}} int cf_util_get_boolean */
+
 /* Assures that the config option is a string. The string is then converted to
  * a port number using `service_name_to_port_number' and returned. Returns the
  * port number in the range [1-65535] or less than zero upon failure. */
@@ -966,7 +983,7 @@ int cf_util_get_port_number (const oconfig_item_t *ci) /* {{{ */
 {
        if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
        {
-               ERROR ("cf_util_get_port_number: The %s plugin requires "
+               ERROR ("cf_util_get_port_number: The %s option requires "
                                "exactly one string argument.", ci->key);
                return (-1);
        }
index 74d074e..a73def2 100644 (file)
@@ -91,6 +91,10 @@ const char *global_option_get (const char *option);
  * success. */
 int cf_util_get_string (const oconfig_item_t *ci, char **ret_string);
 
+/* Assures the config option is a boolean and assignes it to `ret_bool'.
+ * Otherwise, `ret_bool' is not changed and non-zero is returned. */
+int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool);
+
 /* Assures that the config option is a string. The string is then converted to
  * a port number using `service_name_to_port_number' and returned. Returns the
  * port number in the range [1-65535] or less than zero upon failure. */
diff --git a/src/contextswitch.c b/src/contextswitch.c
new file mode 100644 (file)
index 0000000..7787203
--- /dev/null
@@ -0,0 +1,98 @@
+/**
+ * collectd - src/contextswitch.c
+ * Copyright (C) 2009  Patrik Weiskircher
+ *
+ * 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:
+ *   Patrik Weiskircher <weiskircher at inqnet.at>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if !KERNEL_LINUX
+# error "No applicable input method."
+#endif
+
+static void cs_submit (unsigned long context_switches)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = (derive_t) context_switches;
+
+       vl.values = values;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "contextswitch", sizeof (vl.plugin));
+       sstrncpy (vl.type, "contextswitch", sizeof (vl.type));
+
+       plugin_dispatch_values (&vl);
+}
+
+static int cs_read (void)
+{
+       FILE *fh;
+       char buffer[64];
+       int numfields;
+       char *fields[3];
+       unsigned long result = 0;
+       int status = -2;
+
+       fh = fopen ("/proc/stat", "r");
+       if (fh == NULL) {
+               ERROR ("contextswitch plugin: unable to open /proc/stat: %s",
+                               sstrerror (errno, buffer, sizeof (buffer)));
+               return (-1);
+       }
+
+       while (fgets(buffer, sizeof(buffer), fh) != NULL)
+       {
+               char *endptr;
+
+               numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE (fields));
+               if (numfields != 2)
+                       continue;
+
+               if (strcmp("ctxt", fields[0]) != 0)
+                       continue;
+
+               errno = 0;
+               endptr = NULL;
+               result = strtoul(fields[1], &endptr, 10);
+               if ((endptr == fields[1]) || (errno != 0)) {
+                       ERROR ("contextswitch plugin: Cannot parse ctxt value: %s",
+                                       fields[1]);
+                       status = -1;
+                       break;
+               }
+
+               cs_submit(result);
+               status = 0;
+               break;
+       }
+       fclose(fh);
+
+       if (status == -2)
+               ERROR ("contextswitch plugin: Unable to find context switch value.");
+
+       return status;
+}
+
+void module_register (void)
+{
+       plugin_register_read ("contextswitch", cs_read);
+} /* void module_register */
index c4bd1a6..b92b0e2 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -123,6 +123,9 @@ static int numcpu;
 
 #elif defined(HAVE_SYSCTLBYNAME)
 static int numcpu;
+#  ifdef HAVE_SYSCTL_KERN_CP_TIMES
+static int maxcpu;
+#  endif /* HAVE_SYSCTL_KERN_CP_TIMES */
 /* #endif HAVE_SYSCTLBYNAME */
 
 #elif defined(HAVE_LIBSTATGRAB)
@@ -193,12 +196,22 @@ static int init (void)
        if (sysctlbyname ("hw.ncpu", &numcpu, &numcpu_size, NULL, 0) < 0)
        {
                char errbuf[1024];
-               WARNING ("cpu plugin: sysctlbyname: %s",
+               WARNING ("cpu plugin: sysctlbyname(hw.ncpu): %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
 
-#ifndef HAVE_SYSCTL_KERN_CP_TIMES
+#ifdef HAVE_SYSCTL_KERN_CP_TIMES
+       numcpu_size = sizeof (maxcpu);
+
+       if (sysctlbyname("kern.smp.maxcpus", &maxcpu, &numcpu_size, NULL, 0) < 0)
+       {
+               char errbuf[1024];
+               WARNING ("cpu plugin: sysctlbyname(kern.smp.maxcpus): %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+#else
        if (numcpu != 1)
                NOTICE ("cpu: Only one processor supported when using `sysctlbyname' (found %i)", numcpu);
 #endif
@@ -467,7 +480,7 @@ static int cpu_read (void)
        }
 /* #endif CAN_USE_SYSCTL */
 #elif defined(HAVE_SYSCTLBYNAME) && defined(HAVE_SYSCTL_KERN_CP_TIMES)
-       long cpuinfo[numcpu][CPUSTATES];
+       long cpuinfo[maxcpu][CPUSTATES];
        size_t cpuinfo_size;
        int i;
 
index 78037a9..0b34687 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -246,16 +246,10 @@ static int csv_config (const char *key, const char *value)
        }
        else if (strcasecmp ("StoreRates", key) == 0)
        {
-               if ((strcasecmp ("True", value) == 0)
-                               || (strcasecmp ("Yes", value) == 0)
-                               || (strcasecmp ("On", value) == 0))
-               {
+               if (IS_TRUE (value))
                        store_rates = 1;
-               }
                else
-               {
                        store_rates = 0;
-               }
        }
        else
        {
index 194c257..62775fd 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -50,7 +50,9 @@ static const char *config_keys[] =
        "MountPoint",
        "FSType",
        "IgnoreSelected",
-        "ReportByDevice"
+       "ReportByDevice",
+       "ReportReserved",
+       "ReportInodes"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
@@ -59,6 +61,8 @@ static ignorelist_t *il_mountpoint = NULL;
 static ignorelist_t *il_fstype = NULL;
 
 static _Bool by_device = false;
+static _Bool report_reserved = false;
+static _Bool report_inodes = false;
 
 static int df_init (void)
 {
@@ -117,11 +121,31 @@ static int df_config (const char *key, const char *value)
 
                return (0);
        }
+       else if (strcasecmp (key, "ReportReserved") == 0)
+       {
+               if (IS_TRUE (value))
+                       report_reserved = true;
+               else
+                       report_reserved = false;
+
+               return (0);
+       }
+       else if (strcasecmp (key, "ReportInodes") == 0)
+       {
+               if (IS_TRUE (value))
+                       report_inodes = true;
+               else
+                       report_inodes = false;
+
+               return (0);
+       }
+
 
        return (-1);
 }
 
-static void df_submit (char *df_name,
+static void df_submit_two (char *df_name,
+               const char *type,
                gauge_t df_used,
                gauge_t df_free)
 {
@@ -136,11 +160,36 @@ static void df_submit (char *df_name,
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "df", sizeof (vl.plugin));
        sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
-       sstrncpy (vl.type, "df", sizeof (vl.host));
+       sstrncpy (vl.type, type, sizeof (vl.type));
        sstrncpy (vl.type_instance, df_name, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
-} /* void df_submit */
+} /* void df_submit_two */
+
+__attribute__ ((nonnull(2)))
+static void df_submit_one (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, "df", 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 df_submit_one */
 
 static int df_read (void)
 {
@@ -153,17 +202,15 @@ static int df_read (void)
        cu_mount_t *mnt_list;
        cu_mount_t *mnt_ptr;
 
-       unsigned long long blocksize;
-       gauge_t df_free;
-       gauge_t df_used;
-       char disk_name[256];
-
        mnt_list = NULL;
        if (cu_mount_getlist (&mnt_list) == NULL)
                return (-1);
 
        for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next)
        {
+               unsigned long long blocksize;
+               char disk_name[256];
+
                if (ignorelist_match (il_device,
                                        (mnt_ptr->spec_device != NULL)
                                        ? mnt_ptr->spec_device
@@ -186,10 +233,6 @@ static int df_read (void)
                if (!statbuf.f_blocks)
                        continue;
 
-               blocksize = BLOCKSIZE(statbuf);
-               df_free = statbuf.f_bfree * blocksize;
-               df_used = (statbuf.f_blocks - statbuf.f_bfree) * blocksize;
-
                if (by_device) 
                {
                        /* eg, /dev/hda1  -- strip off the "/dev/" */
@@ -223,7 +266,66 @@ static int df_read (void)
                        }
                }
 
-               df_submit (disk_name, df_used, df_free);
+               blocksize = BLOCKSIZE(statbuf);
+
+               if (report_reserved)
+               {
+                       uint64_t blk_free;
+                       uint64_t blk_reserved;
+                       uint64_t blk_used;
+
+                       /* Sanity-check for the values in the struct */
+                       if (statbuf.f_bfree < statbuf.f_bavail)
+                               statbuf.f_bfree = statbuf.f_bavail;
+                       if (statbuf.f_blocks < statbuf.f_bfree)
+                               statbuf.f_blocks = statbuf.f_bfree;
+
+                       blk_free = (uint64_t) statbuf.f_bavail;
+                       blk_reserved = (uint64_t) (statbuf.f_bfree - statbuf.f_bavail);
+                       blk_used = (uint64_t) (statbuf.f_blocks - statbuf.f_bfree);
+                       
+                       df_submit_one (disk_name, "df_complex", "free",
+                                       (gauge_t) (blk_free * blocksize));
+                       df_submit_one (disk_name, "df_complex", "reserved",
+                                       (gauge_t) (blk_reserved * blocksize));
+                       df_submit_one (disk_name, "df_complex", "used",
+                                       (gauge_t) (blk_used * blocksize));
+               }
+               else /* compatibility code */
+               {
+                       gauge_t df_free;
+                       gauge_t df_used;
+
+                       df_free = statbuf.f_bfree * blocksize;
+                       df_used = (statbuf.f_blocks - statbuf.f_bfree) * blocksize;
+
+                       df_submit_two (disk_name, "df", df_used, df_free);
+               }
+
+               /* inode handling */
+               if (report_inodes)
+               {
+                       uint64_t inode_free;
+                       uint64_t inode_reserved;
+                       uint64_t inode_used;
+
+                       /* Sanity-check for the values in the struct */
+                       if (statbuf.f_ffree < statbuf.f_favail)
+                               statbuf.f_ffree = statbuf.f_favail;
+                       if (statbuf.f_files < statbuf.f_ffree)
+                               statbuf.f_files = statbuf.f_ffree;
+
+                       inode_free = (uint64_t) statbuf.f_favail;
+                       inode_reserved = (uint64_t) (statbuf.f_ffree - statbuf.f_favail);
+                       inode_used = (uint64_t) (statbuf.f_files - statbuf.f_ffree);
+                       
+                       df_submit_one (disk_name, "df_inodes", "free",
+                                       (gauge_t) inode_free);
+                       df_submit_one (disk_name, "df_inodes", "reserved",
+                                       (gauge_t) inode_reserved);
+                       df_submit_one (disk_name, "df_inodes", "used",
+                                       (gauge_t) inode_used);
+               }
        }
 
        cu_mount_freelist (mnt_list);
index 489770c..94257fe 100644 (file)
@@ -132,9 +132,7 @@ static int disk_config (const char *key, const char *value)
   else if (strcasecmp ("IgnoreSelected", key) == 0)
   {
     int invert = 1;
-    if ((strcasecmp ("True", value) == 0)
-       || (strcasecmp ("Yes", value) == 0)
-       || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       invert = 0;
     ignorelist_set_invert (ignorelist, invert);
   }
@@ -150,7 +148,7 @@ static int disk_init (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H
        kern_return_t status;
-       
+
        if (io_master_port != MACH_PORT_NULL)
        {
                mach_port_deallocate (mach_task_self (),
index 3357ea0..2ffc42a 100644 (file)
@@ -542,9 +542,9 @@ static int staging_entry_update (const char *host, const char *name, /* {{{ */
   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;
+    se->vl.values[ds_index].derive += value.derive;
   else if (ds_type == DS_TYPE_ABSOLUTE)
-    se->vl.values[ds_index].ABSOLUTE = value.absolute;
+    se->vl.values[ds_index].absolute = value.absolute;
 
   se->flags |= (0x01 << ds_index);
 
index 2405d8c..4e08375 100644 (file)
@@ -233,9 +233,7 @@ static int hddtemp_config (const char *key, const char *value)
        }
        else if (strcasecmp (key, "TranslateDevicename") == 0)
        {
-               if ((strcasecmp ("true", value) == 0)
-                               || (strcasecmp ("yes", value) == 0)
-                               || (strcasecmp ("on", value) == 0))
+               if (IS_TRUE (value))
                        translate_devicename = 1;
                else
                        translate_devicename = 0;
index fad37db..8557cb1 100644 (file)
@@ -99,9 +99,7 @@ static int interface_config (const char *key, const char *value)
        else if (strcasecmp (key, "IgnoreSelected") == 0)
        {
                int invert = 1;
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        invert = 0;
                ignorelist_set_invert (ignorelist, invert);
        }
@@ -224,7 +222,7 @@ static int interface_read (void)
        char buffer[1024];
        unsigned long long incoming, outgoing;
        char *device;
-       
+
        char *dummy;
        char *fields[16];
        int numfields;
@@ -250,7 +248,7 @@ static int interface_read (void)
 
                if (device[0] == '\0')
                        continue;
-               
+
                numfields = strsplit (dummy, fields, 16);
 
                if (numfields < 11)
index 441ad8f..95b3dbf 100644 (file)
@@ -632,31 +632,23 @@ static int c_ipmi_config (const char *key, const char *value)
   else if (strcasecmp ("IgnoreSelected", key) == 0)
   {
     int invert = 1;
-    if ((strcasecmp ("True", value) == 0)
-        || (strcasecmp ("Yes", value) == 0)
-        || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       invert = 0;
     ignorelist_set_invert (ignorelist, invert);
   }
   else if (strcasecmp ("NotifySensorAdd", key) == 0)
   {
-    if ((strcasecmp ("True", value) == 0)
-        || (strcasecmp ("Yes", value) == 0)
-        || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       c_ipmi_nofiy_add = 1;
   }
   else if (strcasecmp ("NotifySensorRemove", key) == 0)
   {
-    if ((strcasecmp ("True", value) == 0)
-        || (strcasecmp ("Yes", value) == 0)
-        || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       c_ipmi_nofiy_remove = 1;
   }
   else if (strcasecmp ("NotifySensorNotPresent", key) == 0)
   {
-    if ((strcasecmp ("True", value) == 0)
-        || (strcasecmp ("Yes", value) == 0)
-        || (strcasecmp ("On", value) == 0))
+    if (IS_TRUE (value))
       c_ipmi_nofiy_notpresent = 1;
   }
   else
index 1aef344..401cc6f 100644 (file)
--- a/src/irq.c
+++ b/src/irq.c
@@ -85,9 +85,7 @@ static int irq_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))
                        irq_list_action = 1;
                else
                        irq_list_action = 0;
index 6f9e5f1..bcbf0e6 100644 (file)
@@ -187,9 +187,7 @@ lv_config (const char *key, const char *value)
     }
 
     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_domains, 0);
             ignorelist_set_invert (il_block_devices, 0);
index cf5221b..575b4ca 100644 (file)
@@ -81,7 +81,7 @@ static int load_read (void)
 
        char *fields[8];
        int numfields;
-       
+
        if ((loadavg = fopen ("/proc/loadavg", "r")) == NULL)
        {
                char errbuf[1024];
index 03af7a3..7b96ac5 100644 (file)
@@ -74,9 +74,7 @@ static int logfile_config (const char *key, const char *value)
                log_file = strdup (value);
        }
        else if (0 == strcasecmp (key, "Timestamp")) {
-               if ((strcasecmp (value, "false") == 0)
-                               || (strcasecmp (value, "no") == 0)
-                               || (strcasecmp (value, "off") == 0))
+               if (IS_FALSE (value))
                        print_timestamp = 0;
                else
                        print_timestamp = 1;
diff --git a/src/match_hashed.c b/src/match_hashed.c
new file mode 100644 (file)
index 0000000..062a7a7
--- /dev/null
@@ -0,0 +1,184 @@
+/**
+ * collectd - src/match_hashed.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 mh_hash_match_s
+{
+  uint32_t match;
+  uint32_t total;
+};
+typedef struct mh_hash_match_s mh_hash_match_t;
+
+struct mh_match_s;
+typedef struct mh_match_s mh_match_t;
+struct mh_match_s
+{
+  mh_hash_match_t *matches;
+  size_t           matches_num;
+};
+
+/*
+ * internal helper functions
+ */
+static int mh_config_match (const oconfig_item_t *ci, /* {{{ */
+    mh_match_t *m)
+{
+  mh_hash_match_t *tmp;
+
+  if ((ci->values_num != 2)
+      || (ci->values[0].type != OCONFIG_TYPE_NUMBER)
+      || (ci->values[1].type != OCONFIG_TYPE_NUMBER))
+  {
+    ERROR ("hashed match: The `Match' option requires "
+        "exactly two numeric arguments.");
+    return (-1);
+  }
+
+  if ((ci->values[0].value.number < 0)
+      || (ci->values[1].value.number < 0))
+  {
+    ERROR ("hashed match: The arguments of the `Match' "
+        "option must be positive.");
+    return (-1);
+  }
+
+  tmp = realloc (m->matches, sizeof (*tmp) * (m->matches_num + 1));
+  if (tmp == NULL)
+  {
+    ERROR ("hashed match: realloc failed.");
+    return (-1);
+  }
+  m->matches = tmp;
+  tmp = m->matches + m->matches_num;
+
+  tmp->match = (uint32_t) (ci->values[0].value.number + .5);
+  tmp->total = (uint32_t) (ci->values[1].value.number + .5);
+
+  if (tmp->match >= tmp->total)
+  {
+    ERROR ("hashed match: The first argument of the `Match' option "
+        "must be smaller than the second argument.");
+    return (-1);
+  }
+  assert (tmp->total != 0);
+
+  m->matches_num++;
+  return (0);
+} /* }}} int mh_config_match */
+
+static int mh_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+{
+  mh_match_t *m;
+  int i;
+
+  m = (mh_match_t *) malloc (sizeof (*m));
+  if (m == NULL)
+  {
+    ERROR ("mh_create: malloc failed.");
+    return (-ENOMEM);
+  }
+  memset (m, 0, sizeof (*m));
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Match", child->key) == 0)
+      mh_config_match (child, m);
+    else
+      ERROR ("hashed match: No such config option: %s", child->key);
+  }
+
+  if (m->matches_num == 0)
+  {
+    sfree (m->matches);
+    sfree (m);
+    ERROR ("hashed match: No matches were configured. Not creating match.");
+    return (-1);
+  }
+
+  *user_data = m;
+  return (0);
+} /* }}} int mh_create */
+
+static int mh_destroy (void **user_data) /* {{{ */
+{
+  mh_match_t *mh;
+
+  if ((user_data == NULL) || (*user_data == NULL))
+    return (0);
+
+  mh = *user_data;
+  sfree (mh->matches);
+  sfree (mh);
+
+  return (0);
+} /* }}} int mh_destroy */
+
+static int mh_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
+    const value_list_t *vl,
+    notification_meta_t __attribute__((unused)) **meta, void **user_data)
+{
+  mh_match_t *m;
+  uint32_t hash_val;
+  const char *host_ptr;
+  size_t i;
+
+  if ((user_data == NULL) || (*user_data == NULL))
+    return (-1);
+
+  m = *user_data;
+
+  hash_val = 0;
+
+  for (host_ptr = vl->host; *host_ptr != 0; host_ptr++)
+  {
+    /* 251 is the largest prime smaller than 256. */
+    hash_val = (hash_val * 251) + ((uint32_t) *host_ptr);
+  }
+  DEBUG ("hashed match: host = %s; hash_val = %"PRIu32";", vl->host, hash_val);
+
+  for (i = 0; i < m->matches_num; i++)
+    if ((hash_val % m->matches[i].total) == m->matches[i].match)
+      return (FC_MATCH_MATCHES);
+
+  return (FC_MATCH_NO_MATCH);
+} /* }}} int mh_match */
+
+void module_register (void)
+{
+  match_proc_t mproc;
+
+  memset (&mproc, 0, sizeof (mproc));
+  mproc.create  = mh_create;
+  mproc.destroy = mh_destroy;
+  mproc.match   = mh_match;
+  fc_register_match ("hashed", mproc);
+} /* module_register */
+
+/* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
index 799a80c..e0e0cbb 100644 (file)
@@ -250,7 +250,7 @@ static int memory_read (void)
 #elif KERNEL_LINUX
        FILE *fh;
        char buffer[1024];
-       
+
        char *fields[8];
        int numfields;
 
index b11e9fa..ad19c6d 100644 (file)
@@ -253,11 +253,6 @@ struct host_config_s {
 
        struct host_config_s *next;
 };
-#define HOST_INIT { NULL, NA_SERVER_TRANSPORT_HTTPS, NULL, 0, NULL, NULL, 0, \
-       NULL, NULL, NULL, NULL, NULL, NULL, \
-       NULL}
-
-static host_config_t *global_host_config;
 
 /*
  * Free functions
@@ -930,14 +925,14 @@ static int cna_setup_wafl (cfg_wafl_t *cw) /* {{{ */
                ERROR ("netapp plugin: na_elem_new failed.");
                return (-1);
        }
-       na_child_add_string(e, "foo", "name_cache_hit");
-       na_child_add_string(e, "foo", "name_cache_miss");
-       na_child_add_string(e, "foo", "find_dir_hit");
-       na_child_add_string(e, "foo", "find_dir_miss");
-       na_child_add_string(e, "foo", "buf_hash_hit");
-       na_child_add_string(e, "foo", "buf_hash_miss");
-       na_child_add_string(e, "foo", "inode_cache_hit");
-       na_child_add_string(e, "foo", "inode_cache_miss");
+       na_child_add_string(e, "counter", "name_cache_hit");
+       na_child_add_string(e, "counter", "name_cache_miss");
+       na_child_add_string(e, "counter", "find_dir_hit");
+       na_child_add_string(e, "counter", "find_dir_miss");
+       na_child_add_string(e, "counter", "buf_hash_hit");
+       na_child_add_string(e, "counter", "buf_hash_miss");
+       na_child_add_string(e, "counter", "inode_cache_hit");
+       na_child_add_string(e, "counter", "inode_cache_miss");
 
        na_child_add(cw->query, e);
 
@@ -1130,8 +1125,8 @@ static int cna_setup_disk (cfg_disk_t *cd) /* {{{ */
                ERROR ("netapp plugin: na_elem_new failed.");
                return (-1);
        }
-       na_child_add_string(e, "foo", "disk_busy");
-       na_child_add_string(e, "foo", "base_for_disk_busy");
+       na_child_add_string(e, "counter", "disk_busy");
+       na_child_add_string(e, "counter", "base_for_disk_busy");
        na_child_add(cd->query, e);
 
        return (0);
@@ -1296,13 +1291,12 @@ static int cna_setup_volume_perf (cfg_volume_perf_t *cd) /* {{{ */
                ERROR ("netapp plugin: na_elem_new failed.");
                return (-1);
        }
-       /* "foo" means: This string has to be here but the content doesn't matter. */
-       na_child_add_string(e, "foo", "read_ops");
-       na_child_add_string(e, "foo", "write_ops");
-       na_child_add_string(e, "foo", "read_data");
-       na_child_add_string(e, "foo", "write_data");
-       na_child_add_string(e, "foo", "read_latency");
-       na_child_add_string(e, "foo", "write_latency");
+       na_child_add_string(e, "counter", "read_ops");
+       na_child_add_string(e, "counter", "write_ops");
+       na_child_add_string(e, "counter", "read_data");
+       na_child_add_string(e, "counter", "write_data");
+       na_child_add_string(e, "counter", "read_latency");
+       na_child_add_string(e, "counter", "write_latency");
        na_child_add(cd->query, e);
 
        return (0);
@@ -2356,8 +2350,7 @@ static int cna_config_system (host_config_t *host, /* {{{ */
 } /* }}} int cna_config_system */
 
 /* Corresponds to a <Host /> block. */
-static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
-               const host_config_t *default_host)
+static host_config_t *cna_config_host (const oconfig_item_t *ci) /* {{{ */
 {
        oconfig_item_t *item;
        host_config_t *host;
@@ -2370,7 +2363,18 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
        }
 
        host = malloc(sizeof(*host));
-       memcpy (host, default_host, sizeof (*host));
+       memset (host, 0, sizeof (*host));
+       host->name = NULL;
+       host->protocol = NA_SERVER_TRANSPORT_HTTPS;
+       host->host = NULL;
+       host->username = NULL;
+       host->password = NULL;
+       host->srv = NULL;
+       host->cfg_wafl = NULL;
+       host->cfg_disk = NULL;
+       host->cfg_volume_perf = NULL;
+       host->cfg_volume_usage = NULL;
+       host->cfg_system = NULL;
 
        status = cf_util_get_string (ci, &host->name);
        if (status != 0)
@@ -2457,15 +2461,36 @@ static host_config_t *cna_config_host (const oconfig_item_t *ci, /* {{{ */
  *
  * Pretty standard stuff here.
  */
-static int cna_init(void) { /* {{{ */
-       char err[256];
-       host_config_t *host;
-       
-       if (!global_host_config) {
-               WARNING("netapp plugin: Plugin loaded but no hosts defined.");
-               return 1;
+static int cna_init_host (host_config_t *host) /* {{{ */
+{
+       if (host == NULL)
+               return (EINVAL);
+
+       if (host->srv != NULL)
+               return (0);
+
+       /* Request version 1.1 of the ONTAP API */
+       host->srv = na_server_open(host->host,
+                       /* major version = */ 1, /* minor version = */ 1); 
+       if (host->srv == NULL) {
+               ERROR ("netapp plugin: na_server_open (%s) failed.", host->host);
+               return (-1);
        }
 
+       na_server_set_transport_type(host->srv, host->protocol,
+                       /* transportarg = */ NULL);
+       na_server_set_port(host->srv, host->port);
+       na_server_style(host->srv, NA_STYLE_LOGIN_PASSWORD);
+       na_server_adminuser(host->srv, host->username, host->password);
+       na_server_set_timeout(host->srv, 5 /* seconds */);
+
+       return 0;
+} /* }}} int cna_init_host */
+
+static int cna_init (void) /* {{{ */
+{
+       char err[256];
+
        memset (err, 0, sizeof (err));
        if (!na_startup(err, sizeof(err))) {
                err[sizeof (err) - 1] = 0;
@@ -2473,84 +2498,76 @@ static int cna_init(void) { /* {{{ */
                return 1;
        }
 
-       for (host = global_host_config; host; host = host->next) {
-               /* Request version 1.1 of the ONTAP API */
-               host->srv = na_server_open(host->host,
-                               /* major version = */ 1, /* minor version = */ 1); 
-               if (host->srv == NULL) {
-                       ERROR ("netapp plugin: na_server_open (%s) failed.", host->host);
-                       continue;
-               }
+       return (0);
+} /* }}} cna_init */
 
-               if (host->interval < interval_g)
-                       host->interval = interval_g;
+static int cna_read (user_data_t *ud) { /* {{{ */
+       host_config_t *host;
+       int status;
+
+       if ((ud == NULL) || (ud->data == NULL))
+               return (-1);
+
+       host = ud->data;
+
+       status = cna_init_host (host);
+       if (status != 0)
+               return (status);
+       
+       cna_query_wafl (host);
+       cna_query_disk (host);
+       cna_query_volume_perf (host);
+       cna_query_volume_usage (host);
+       cna_query_system (host);
 
-               na_server_set_transport_type(host->srv, host->protocol,
-                               /* transportarg = */ NULL);
-               na_server_set_port(host->srv, host->port);
-               na_server_style(host->srv, NA_STYLE_LOGIN_PASSWORD);
-               na_server_adminuser(host->srv, host->username, host->password);
-               na_server_set_timeout(host->srv, 5 /* seconds */);
-       }
        return 0;
-} /* }}} int cna_init */
+} /* }}} int cna_read */
 
 static int cna_config (oconfig_item_t *ci) { /* {{{ */
        int i;
        oconfig_item_t *item;
-       host_config_t default_host = HOST_INIT;
-       
+
        for (i = 0; i < ci->children_num; ++i) {
                item = ci->children + i;
 
-               if (!strcasecmp(item->key, "Host")) {
+               if (strcasecmp(item->key, "Host") == 0)
+               {
                        host_config_t *host;
-                       host_config_t *tmp;
+                       char cb_name[256];
+                       struct timespec interval;
+                       user_data_t ud;
 
-                       host = cna_config_host(item, &default_host);
+                       host = cna_config_host (item);
                        if (host == NULL)
                                continue;
 
-                       for (tmp = global_host_config; tmp != NULL; tmp = tmp->next)
-                       {
-                               if (strcasecmp (host->name, tmp->name) == 0)
-                                       WARNING ("netapp plugin: Duplicate definition of host `%s'. "
-                                                       "This is probably a bad idea.",
-                                                       host->name);
+                       ssnprintf (cb_name, sizeof (cb_name), "netapp-%s", host->name);
 
-                               if (tmp->next == NULL)
-                                       break;
-                       }
+                       memset (&interval, 0, sizeof (interval));
+                       interval.tv_sec = host->interval;
 
-                       host->next = NULL;
-                       if (tmp == NULL)
-                               global_host_config = host;
-                       else
-                               tmp->next = host;
-               } else {
+                       memset (&ud, 0, sizeof (ud));
+                       ud.data = host;
+                       ud.free_func = (void (*) (void *)) free_host_config;
+
+                       plugin_register_complex_read (cb_name,
+                                       /* callback  = */ cna_read, 
+                                       /* interval  = */ (host->interval > 0) ? &interval : NULL,
+                                       /* user data = */ &ud);
+                       continue;
+               }
+               else /* if (item->key != "Host") */
+               {
                        WARNING("netapp plugin: Ignoring unknown config option \"%s\".", item->key);
                }
        }
        return 0;
 } /* }}} int cna_config */
 
-static int cna_read (void) { /* {{{ */
-       host_config_t *host;
-       
-       for (host = global_host_config; host; host = host->next) {
-               cna_query_wafl (host);
-               cna_query_disk (host);
-               cna_query_volume_perf (host);
-               cna_query_volume_usage (host);
-               cna_query_system (host);
-       }
-       return 0;
-} /* }}} int cna_read */
-
 static int cna_shutdown (void) /* {{{ */
 {
-       free_host_config (global_host_config);
-       global_host_config = NULL;
+       /* Clean up system resources and stuff. */
+       na_shutdown ();
 
        return (0);
 } /* }}} int cna_shutdown */
@@ -2558,7 +2575,6 @@ static int cna_shutdown (void) /* {{{ */
 void module_register(void) {
        plugin_register_complex_config("netapp", cna_config);
        plugin_register_init("netapp", cna_init);
-       plugin_register_read("netapp", cna_read);
        plugin_register_shutdown("netapp", cna_shutdown);
 }
 
index b15768e..49c4e99 100644 (file)
@@ -526,9 +526,7 @@ static int ir_config (const char *key, const char *value)
     }
     else
     {
-      if ((strcasecmp (fields[0], "yes") == 0)
-         || (strcasecmp (fields[0], "true") == 0)
-         || (strcasecmp (fields[0], "on") == 0))
+      if (IS_TRUE (fields[0]))
        ir_ignorelist_invert = 0;
       else
        ir_ignorelist_invert = 1;
index 109289e..1b45375 100644 (file)
@@ -255,6 +255,7 @@ typedef struct receive_list_entry_s receive_list_entry_t;
 static int network_config_ttl = 0;
 static size_t network_config_packet_size = 1024;
 static int network_config_forward = 0;
+static int network_config_stats = 0;
 
 static sockent_t *sending_sockets = NULL;
 
@@ -262,6 +263,7 @@ static receive_list_entry_t *receive_list_head = NULL;
 static receive_list_entry_t *receive_list_tail = NULL;
 static pthread_mutex_t       receive_list_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t        receive_list_cond = PTHREAD_COND_INITIALIZER;
+static uint64_t              receive_list_length = 0;
 
 static sockent_t     *listen_sockets = NULL;
 static struct pollfd *listen_sockets_pollfd = NULL;
@@ -282,6 +284,22 @@ 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;
 
+/* XXX: These counters are incremented from one place only. The spot in which
+ * the values are incremented is either only reachable by one thread (the
+ * dispatch thread, for example) or locked by some lock (send_buffer_lock for
+ * example). Only if neither is true, the stats_lock is acquired. The counters
+ * are always read without holding a lock in the hope that writing 8 bytes to
+ * memory is an atomic operation. */
+static uint64_t stats_octets_rx  = 0;
+static uint64_t stats_octets_tx  = 0;
+static uint64_t stats_packets_rx = 0;
+static uint64_t stats_packets_tx = 0;
+static uint64_t stats_values_dispatched = 0;
+static uint64_t stats_values_not_dispatched = 0;
+static uint64_t stats_values_sent = 0;
+static uint64_t stats_values_not_sent = 0;
+static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER;
+
 /*
  * Private functions
  */
@@ -340,12 +358,13 @@ static int network_dispatch_values (value_list_t *vl) /* {{{ */
   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);
+    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
+    stats_values_not_dispatched++;
     return (0);
   }
 
@@ -368,6 +387,7 @@ static int network_dispatch_values (value_list_t *vl) /* {{{ */
   }
 
   plugin_dispatch_values (vl);
+  stats_values_dispatched++;
 
   meta_data_destroy (vl->meta);
   vl->meta = NULL;
@@ -1968,6 +1988,7 @@ static void *dispatch_thread (void __attribute__((unused)) *arg) /* {{{ */
     ent = receive_list_head;
     if (ent != NULL)
       receive_list_head = ent->next;
+    receive_list_length--;
     pthread_mutex_unlock (&receive_list_lock);
 
     /* Check whether we are supposed to exit. We do NOT check `listen_loop'
@@ -2019,11 +2040,13 @@ static int network_receive (void) /* {{{ */
 
        receive_list_entry_t *private_list_head;
        receive_list_entry_t *private_list_tail;
+       uint64_t              private_list_length;
 
         assert (listen_sockets_num > 0);
 
        private_list_head = NULL;
        private_list_tail = NULL;
+       private_list_length = 0;
 
        while (listen_loop == 0)
        {
@@ -2060,6 +2083,9 @@ static int network_receive (void) /* {{{ */
                                return (-1);
                        }
 
+                       stats_octets_rx += ((uint64_t) buffer_len);
+                       stats_packets_rx++;
+
                        /* TODO: Possible performance enhancement: Do not free
                         * these entries in the dispatch thread but put them in
                         * another list, so we don't have to allocate more and
@@ -2074,6 +2100,7 @@ static int network_receive (void) /* {{{ */
                        ent->data = malloc (network_config_packet_size);
                        if (ent->data == NULL)
                        {
+                               sfree (ent);
                                ERROR ("network plugin: malloc failed.");
                                return (-1);
                        }
@@ -2088,22 +2115,28 @@ static int network_receive (void) /* {{{ */
                        else
                                private_list_tail->next = ent;
                        private_list_tail = ent;
+                       private_list_length++;
 
                        /* Do not block here. Blocking here has led to
                         * insufficient performance in the past. */
                        if (pthread_mutex_trylock (&receive_list_lock) == 0)
                        {
+                               assert (((receive_list_head == NULL) && (receive_list_length == 0))
+                                               || ((receive_list_head != NULL) && (receive_list_length != 0)));
+
                                if (receive_list_head == NULL)
                                        receive_list_head = private_list_head;
                                else
                                        receive_list_tail->next = private_list_head;
                                receive_list_tail = private_list_tail;
-
-                               private_list_head = NULL;
-                               private_list_tail = NULL;
+                               receive_list_length += private_list_length;
 
                                pthread_cond_signal (&receive_list_cond);
                                pthread_mutex_unlock (&receive_list_lock);
+
+                               private_list_head = NULL;
+                               private_list_tail = NULL;
+                               private_list_length = 0;
                        }
                } /* for (listen_sockets_pollfd) */
        } /* while (listen_loop == 0) */
@@ -2118,9 +2151,11 @@ static int network_receive (void) /* {{{ */
                else
                        receive_list_tail->next = private_list_head;
                receive_list_tail = private_list_tail;
+               receive_list_length += private_list_length;
 
                private_list_head = NULL;
                private_list_tail = NULL;
+               private_list_length = 0;
 
                pthread_cond_signal (&receive_list_cond);
                pthread_mutex_unlock (&receive_list_lock);
@@ -2429,6 +2464,10 @@ static void flush_buffer (void)
                        send_buffer_fill);
 
        network_send_buffer (send_buffer, (size_t) send_buffer_fill);
+
+       stats_octets_tx += ((uint64_t) send_buffer_fill);
+       stats_packets_tx++;
+
        network_init_buffer ();
 }
 
@@ -2446,6 +2485,11 @@ static int network_write (const data_set_t *ds, const value_list_t *vl,
          DEBUG ("network plugin: network_write: "
              "NOT sending %s.", name);
 #endif
+         /* Counter is not protected by another lock and may be reached by
+          * multiple threads */
+         pthread_mutex_lock (&stats_lock);
+         stats_values_not_sent++;
+         pthread_mutex_unlock (&stats_lock);
          return (0);
        }
 
@@ -2463,6 +2507,8 @@ static int network_write (const data_set_t *ds, const value_list_t *vl,
                /* status == bytes added to the buffer */
                send_buffer_fill += status;
                send_buffer_ptr  += status;
+
+               stats_values_sent++;
        }
        else
        {
@@ -2477,6 +2523,8 @@ static int network_write (const data_set_t *ds, const value_list_t *vl,
                {
                        send_buffer_fill += status;
                        send_buffer_ptr  += status;
+
+                       stats_values_sent++;
                }
        }
 
@@ -2518,13 +2566,9 @@ static int network_config_set_boolean (const oconfig_item_t *ci, /* {{{ */
   {
     char *str = ci->values[0].value.string;
 
-    if ((strcasecmp ("true", str) == 0)
-        || (strcasecmp ("yes", str) == 0)
-        || (strcasecmp ("on", str) == 0))
+    if (IS_TRUE (str))
       *retval = 1;
-    else if ((strcasecmp ("false", str) == 0)
-        || (strcasecmp ("no", str) == 0)
-        || (strcasecmp ("off", str) == 0))
+    else if (IS_FALSE (str))
       *retval = 0;
     else
     {
@@ -2801,6 +2845,8 @@ static int network_config (oconfig_item_t *ci) /* {{{ */
       network_config_set_buffer_size (child);
     else if (strcasecmp ("Forward", child->key) == 0)
       network_config_set_boolean (child, &network_config_forward);
+    else if (strcasecmp ("ReportStats", child->key) == 0)
+      network_config_set_boolean (child, &network_config_stats);
     else if (strcasecmp ("CacheFlush", child->key) == 0)
       /* no op for backwards compatibility only */;
     else
@@ -2927,6 +2973,83 @@ static int network_shutdown (void)
        return (0);
 } /* int network_shutdown */
 
+static int network_stats_read (void) /* {{{ */
+{
+       uint64_t copy_octets_rx;
+       uint64_t copy_octets_tx;
+       uint64_t copy_packets_rx;
+       uint64_t copy_packets_tx;
+       uint64_t copy_values_dispatched;
+       uint64_t copy_values_not_dispatched;
+       uint64_t copy_values_sent;
+       uint64_t copy_values_not_sent;
+       uint64_t copy_receive_list_length;
+       value_list_t vl = VALUE_LIST_INIT;
+       value_t values[2];
+
+       copy_octets_rx = stats_octets_rx;
+       copy_octets_tx = stats_octets_tx;
+       copy_packets_rx = stats_packets_rx;
+       copy_packets_tx = stats_packets_tx;
+       copy_values_dispatched = stats_values_dispatched;
+       copy_values_not_dispatched = stats_values_not_dispatched;
+       copy_values_sent = stats_values_sent;
+       copy_values_not_sent = stats_values_not_sent;
+       copy_receive_list_length = receive_list_length;
+
+       /* Initialize `vl' */
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = 0;
+       vl.interval = interval_g;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "network", sizeof (vl.plugin));
+
+       /* Octets received / sent */
+       vl.values[0].counter = (counter_t) copy_octets_rx;
+       vl.values[1].counter = (counter_t) copy_octets_tx;
+       sstrncpy (vl.type, "if_octets", sizeof (vl.type));
+       plugin_dispatch_values (&vl);
+
+       /* Packets received / send */
+       vl.values[0].counter = (counter_t) copy_packets_rx;
+       vl.values[1].counter = (counter_t) copy_packets_tx;
+       sstrncpy (vl.type, "if_packets", sizeof (vl.type));
+       plugin_dispatch_values (&vl);
+
+       /* Values (not) dispatched and (not) send */
+       sstrncpy (vl.type, "total_values", sizeof (vl.type));
+       vl.values_len = 1;
+
+       vl.values[0].derive = (derive_t) copy_values_dispatched;
+       sstrncpy (vl.type_instance, "dispatch-accepted",
+                       sizeof (vl.type_instance));
+       plugin_dispatch_values (&vl);
+
+       vl.values[0].derive = (derive_t) copy_values_not_dispatched;
+       sstrncpy (vl.type_instance, "dispatch-rejected",
+                       sizeof (vl.type_instance));
+       plugin_dispatch_values (&vl);
+
+       vl.values[0].derive = (derive_t) copy_values_sent;
+       sstrncpy (vl.type_instance, "send-accepted",
+                       sizeof (vl.type_instance));
+       plugin_dispatch_values (&vl);
+
+       vl.values[0].derive = (derive_t) copy_values_not_sent;
+       sstrncpy (vl.type_instance, "send-rejected",
+                       sizeof (vl.type_instance));
+       plugin_dispatch_values (&vl);
+
+       /* Receive queue length */
+       vl.values[0].gauge = (gauge_t) copy_receive_list_length;
+       sstrncpy (vl.type, "queue_length", sizeof (vl.type));
+       vl.type_instance[0] = 0;
+       plugin_dispatch_values (&vl);
+
+       return (0);
+} /* }}} int network_stats_read */
+
 static int network_init (void)
 {
        static _Bool have_init = false;
@@ -2943,6 +3066,9 @@ static int network_init (void)
        gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
 #endif
 
+       if (network_config_stats != 0)
+               plugin_register_read ("network", network_stats_read);
+
        plugin_register_shutdown ("network", network_shutdown);
 
        send_buffer = malloc (network_config_packet_size);
index 53137a7..6976842 100644 (file)
@@ -143,7 +143,7 @@ static int init (void)
 
   curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
 
-  if ((verify_peer == NULL) || (strcmp (verify_peer, "true") == 0))
+  if ((verify_peer == NULL) || IS_TRUE (verify_peer))
   {
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 1);
   }
@@ -152,7 +152,7 @@ static int init (void)
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0);
   }
 
-  if ((verify_host == NULL) || (strcmp (verify_host, "true") == 0))
+  if ((verify_host == NULL) || IS_TRUE (verify_host))
   {
     curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 2);
   }
index 9d716ad..ecc87c7 100644 (file)
@@ -278,9 +278,7 @@ static int ntpd_config (const char *key, const char *value)
        }
        else if (strcasecmp (key, "ReverseLookups") == 0)
        {
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        do_reverse_lookups = 1;
                else
                        do_reverse_lookups = 0;
index 261457a..cae0d63 100644 (file)
@@ -89,9 +89,7 @@ static int cow_load_config (const char *key, const char *value)
   else if (strcasecmp (key, "IgnoreSelected") == 0)
   {
     ignorelist_set_invert (sensor_list, 1);
-    if ((strcasecmp (value, "True") == 0)
-        || (strcasecmp (value, "Yes") == 0)
-        || (strcasecmp (value, "On") == 0))
+    if (IS_TRUE (value))
       ignorelist_set_invert (sensor_list, 0);
   }
   else if (strcasecmp (key, "Device") == 0)
index 9f386f0..1ac5083 100644 (file)
@@ -2,6 +2,7 @@
  * collectd - src/openvpn.c
  * Copyright (C) 2008  Doug MacEachern
  * Copyright (C) 2009  Florian octo Forster
+ * Copyright (C) 2009  Marco Chiappero
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * Authors:
  *   Doug MacEachern <dougm at hyperic.com>
  *   Florian octo Forster <octo at verplant.org>
+ *   Marco Chiappero <marco at absence.it>
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 
-#define DEFAULT_STATUS_FILE "/etc/openvpn/openvpn-status.log"
-#define CLIENT_LIST_PREFIX  "CLIENT_LIST,"
+#define V1STRING "Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since\n"
+#define V2STRING "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t)\n"
+#define V3STRING "HEADER CLIENT_LIST Common Name Real Address Virtual Address Bytes Received Bytes Sent Connected Since Connected Since (time_t)\n"
+#define VSSTRING "OpenVPN STATISTICS\n"
 
-static char *status_file = NULL;
 
-/* For compression stats we need to convert these counters to a rate. */
-static counter_t pre_compress_old    = 0;
-static counter_t post_compress_old   = 0;
-static counter_t pre_decompress_old  = 0;
-static counter_t post_decompress_old = 0;
-static int compression_counter_valid = 0;
+struct vpn_status_s
+{
+       char *file;
+       enum
+       {
+               MULTI1 = 1, /* status-version 1 */
+               MULTI2,     /* status-version 2 */
+               MULTI3,     /* status-version 3 */
+               SINGLE = 10 /* currently no versions for single mode, maybe in the future */
+       } version;
+       char *name;
+};
+typedef struct vpn_status_s vpn_status_t;
+
+static vpn_status_t **vpn_list = NULL;
+static int vpn_num = 0;
+
+static int store_compression = 1;
+static int new_naming_schema = 0;
 
 static const char *config_keys[] =
 {
-       "StatusFile"
+       "StatusFile",
+       "Compression",
+       "ImprovedNamingSchema"
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-/* copy-n-pasted from common.c - changed delim to "," */
+
+/* Helper function
+ * copy-n-pasted from common.c - changed delim to ","  */
 static int openvpn_strsplit (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, ",", &saveptr)) != NULL)
-        {
-                ptr = NULL;
-                i++;
-
-                if (i >= size)
-                        break;
-        }
-
-        return (i);
+       size_t i;
+       char *ptr;
+       char *saveptr;
+
+       i = 0;
+       ptr = string;
+       saveptr = NULL;
+       while ((fields[i] = strtok_r (ptr, ",", &saveptr)) != NULL)
+       {
+               ptr = NULL;
+               i++;
+
+               if (i >= size)
+                       break;
+       }
+
+       return (i);
 } /* int openvpn_strsplit */
 
-static void openvpn_submit (char *name, counter_t rx, counter_t tx)
+/* dispatches stats about traffic (TCP or UDP) generated by the tunnel per single endpoint */
+static void iostats_submit (char *pinst, char *tinst, counter_t rx, counter_t tx)
 {
        value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
@@ -73,163 +94,533 @@ static void openvpn_submit (char *name, counter_t rx, counter_t tx)
        values[0].counter = rx;
        values[1].counter = tx;
 
+       /* NOTE ON THE NEW NAMING SCHEMA:
+        *       using plugin_instance to identify each vpn config (and
+        *       status) file; using type_instance to identify the endpoint
+        *       host when in multimode, traffic or overhead when in single.
+        */
+
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "openvpn", sizeof (vl.plugin));
-       sstrncpy (vl.plugin_instance, name, sizeof (vl.plugin_instance));
+       if (pinst != NULL)
+               sstrncpy (vl.plugin_instance, pinst,
+                               sizeof (vl.plugin_instance));
        sstrncpy (vl.type, "if_octets", sizeof (vl.type));
+       if (tinst != NULL)
+               sstrncpy (vl.type_instance, tinst, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
-} /* void openvpn_submit */
+} /* void traffic_submit */
 
-static void compression_submit (char *type_instance, gauge_t ratio)
+/* dispatches stats about data compression shown when in single mode */
+static void compression_submit (char *pinst, char *tinst,
+               counter_t uncompressed, counter_t compressed)
 {
-       value_t values[1];
+       value_t values[2];
        value_list_t vl = VALUE_LIST_INIT;
 
-       values[0].gauge = ratio;
+       values[0].counter = uncompressed;
+       values[1].counter = compressed;
 
        vl.values = values;
        vl.values_len = STATIC_ARRAY_SIZE (values);
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "openvpn", sizeof (vl.plugin));
-       sstrncpy (vl.type, "compression_ratio", sizeof (vl.type));
-       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type));
+       if (pinst != NULL)
+               sstrncpy (vl.plugin_instance, pinst,
+                               sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "compression", sizeof (vl.type));
+       if (tinst != NULL)
+               sstrncpy (vl.type_instance, tinst, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
 } /* void compression_submit */
 
-static int openvpn_read (void)
+static int single_read (char *name, FILE *fh)
 {
-       FILE *fh;
        char buffer[1024];
-       char *fields[10];
+       char *fields[4];
        const int max_fields = STATIC_ARRAY_SIZE (fields);
-       int   fields_num;
+       int  fields_num, read = 0;
+
+       counter_t link_rx, link_tx;
+       counter_t tun_rx, tun_tx;
+       counter_t pre_compress, post_compress;
+       counter_t pre_decompress, post_decompress;
+       counter_t overhead_rx, overhead_tx;
+
+       link_rx = 0;
+       link_tx = 0;
+       tun_rx = 0;
+       tun_tx = 0;
+       pre_compress = 0;
+       post_compress = 0;
+       pre_decompress = 0;
+       post_decompress = 0;
+       overhead_rx = 0;
+       overhead_tx = 0;
 
-       counter_t pre_compress_new    = 0;
-       counter_t post_compress_new   = 0;
-       counter_t pre_decompress_new  = 0;
-       counter_t post_decompress_new = 0;
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               fields_num = openvpn_strsplit (buffer, fields, max_fields);
 
-       /* Clear the least significant four bits, just to make sure all four
-        * counters above are considered to be invalid. */
-       compression_counter_valid &= ~0x0f;
+               /* status file is generated by openvpn/sig.c:print_status()
+                * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/sig.c
+                *
+                * The line we're expecting has 2 fields. We ignore all lines
+                *  with more or less fields.
+                */
+               if (fields_num != 2)
+               {
+                       continue;
+               }
 
-       fh = fopen ((status_file != NULL)
-                       ? status_file
-                       : DEFAULT_STATUS_FILE, "r");
-       if (fh == NULL)
-               return (-1);
+               if (strcmp (fields[0], "TUN/TAP read bytes") == 0)
+               {
+                       /* read from the system and sent over the tunnel */
+                       tun_tx = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "TUN/TAP write bytes") == 0)
+               {
+                       /* read from the tunnel and written in the system */
+                       tun_rx = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "TCP/UDP read bytes") == 0)
+               {
+                       link_rx = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "TCP/UDP write bytes") == 0)
+               {
+                       link_tx = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "pre-compress bytes") == 0)
+               {
+                       pre_compress = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "post-compress bytes") == 0)
+               {
+                       post_compress = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "pre-decompress bytes") == 0)
+               {
+                       pre_decompress = atoll (fields[1]);
+               }
+               else if (strcmp (fields[0], "post-decompress bytes") == 0)
+               {
+                       post_decompress = atoll (fields[1]);
+               }
+       }
+
+       iostats_submit (name, "traffic", link_rx, link_tx);
+
+       /* we need to force this order to avoid negative values with these unsigned */
+       overhead_rx = (((link_rx - pre_decompress) + post_decompress) - tun_rx);
+       overhead_tx = (((link_tx - post_compress) + pre_compress) - tun_tx);
 
-        /* status file is generated by openvpn/multi.c:multi_print_status()
-        * this plugin requires server.conf: "status-version 2"
-         * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
-         */
+       iostats_submit (name, "overhead", overhead_rx, overhead_tx);
+
+       if (store_compression)
+       {
+               compression_submit (name, "data_in", post_decompress, pre_decompress);
+               compression_submit (name, "data_out", pre_compress, post_compress);
+       }
+
+       read = 1;
+
+       return (read);
+} /* int single_read */
+
+/* for reading status version 1 */
+static int multi1_read (char *name, FILE *fh)
+{
+       char buffer[1024];
+       char *fields[10];
+       int  fields_num, read = 0, found_header = 0;
+
+       /* read the file until the "ROUTING TABLE" line is found (no more info after) */
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
-               fields_num = openvpn_strsplit (buffer, fields, max_fields);
+               if (strcmp (buffer, "ROUTING TABLE\n") == 0)
+                       break;
 
-               /* Expect at least ``key,value''. */
-               if (fields_num < 2)
+               if (strcmp (buffer, V1STRING) == 0)
+               {
+                       found_header = 1;
                        continue;
+               }
 
-               if (strcmp (fields[0], "CLIENT_LIST") == 0)
-               {
-                       char *name;
-                       counter_t rx;
-                       counter_t tx;
+               /* skip the first lines until the client list section is found */
+               if (found_header == 0)
+                       /* we can't start reading data until this string is found */
+                       continue;
 
-                       /* The line we're expecting has 8 fields. We ignore all lines
-                        * with more or less fields. */
-                       if (fields_num != 8)
-                               continue;
+               fields_num = openvpn_strsplit (buffer,
+                               fields, STATIC_ARRAY_SIZE (fields));
+               if (fields_num < 4)
+                       continue;
 
-                       name =      fields[1];  /* "Common Name" */
-                       rx = atoll (fields[4]); /* "Bytes Received */
-                       tx = atoll (fields[5]); /* "Bytes Sent" */
-                       openvpn_submit (name, rx, tx);
+               if (new_naming_schema)
+               {
+                       iostats_submit (fields[0],          /* "Common Name" */
+                                       NULL,               /* unused when in multimode */
+                                       atoll (fields[2]),  /* "Bytes Received" */
+                                       atoll (fields[3])); /* "Bytes Sent" */
                }
-               else if (strcmp (fields[0], "pre-compress") == 0)
+               else
                {
-                       pre_compress_new = atoll (fields[1]);
-                       compression_counter_valid |= 0x01;
+                       iostats_submit (name,               /* vpn instance */
+                                       fields[0],          /* "Common Name" */
+                                       atoll (fields[2]),  /* "Bytes Received" */
+                                       atoll (fields[3])); /* "Bytes Sent" */
                }
-               else if (strcmp (fields[0], "post-compress") == 0)
+
+               read = 1;
+       }
+
+       return (read);
+} /* int multi1_read */
+
+/* for reading status version 2 */
+static int multi2_read (char *name, FILE *fh)
+{
+       char buffer[1024];
+       char *fields[10];
+       const int max_fields = STATIC_ARRAY_SIZE (fields);
+       int  fields_num, read = 0;
+
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               fields_num = openvpn_strsplit (buffer, fields, max_fields);
+
+               /* status file is generated by openvpn/multi.c:multi_print_status()
+                * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
+                *
+                * The line we're expecting has 8 fields. We ignore all lines
+                *  with more or less fields.
+                */
+               if (fields_num != 8)
+                       continue;
+
+               if (strcmp (fields[0], "CLIENT_LIST") != 0)
+                       continue;
+
+               if (new_naming_schema)
                {
-                       post_compress_new = atoll (fields[1]);
-                       compression_counter_valid |= 0x02;
+                       /* plugin inst = file name, type inst = fields[1] */
+                       iostats_submit (name,               /* vpn instance */
+                                       fields[1],          /* "Common Name" */
+                                       atoll (fields[4]),  /* "Bytes Received" */
+                                       atoll (fields[5])); /* "Bytes Sent" */
                }
-               else if (strcmp (fields[0], "pre-decompress") == 0)
+               else
+               {
+                       /* plugin inst = fields[1], type inst = "" */
+                       iostats_submit (fields[1],          /* "Common Name" */
+                                       NULL,               /* unused when in multimode */
+                                       atoll (fields[4]),  /* "Bytes Received" */
+                                       atoll (fields[5])); /* "Bytes Sent" */
+               }
+
+               read = 1;
+       }
+
+       return (read);
+} /* int multi2_read */
+
+/* for reading status version 3 */
+static int multi3_read (char *name, FILE *fh)
+{
+       char buffer[1024];
+       char *fields[15];
+       const int max_fields = STATIC_ARRAY_SIZE (fields);
+       int  fields_num, read = 0;
+
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               fields_num = strsplit (buffer, fields, max_fields);
+
+               /* status file is generated by openvpn/multi.c:multi_print_status()
+                * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
+                *
+                * The line we're expecting has 12 fields. We ignore all lines
+                *  with more or less fields.
+                */
+               if (fields_num != 12)
                {
-                       pre_decompress_new = atoll (fields[1]);
-                       compression_counter_valid |= 0x04;
+                       continue;
                }
-               else if (strcmp (fields[0], "post-decompress") == 0)
+               else
                {
-                       post_decompress_new = atoll (fields[1]);
-                       compression_counter_valid |= 0x08;
+                       if (strcmp (fields[0], "CLIENT_LIST") != 0)
+                               continue;
+
+                       if (new_naming_schema)
+                       {
+                               iostats_submit (name,               /* vpn instance */
+                                               fields[1],          /* "Common Name" */
+                                               atoll (fields[4]),  /* "Bytes Received" */
+                                               atoll (fields[5])); /* "Bytes Sent" */
+                       }
+                       else
+                       {
+                               iostats_submit (fields[1],          /* "Common Name" */
+                                               NULL,               /* unused when in multimode */
+                                               atoll (fields[4]),  /* "Bytes Received" */
+                                               atoll (fields[5])); /* "Bytes Sent" */
+                       }
+
+                       read = 1;
                }
        }
-       fclose (fh);
 
-       /* Check that all four counters are valid, {pre,post}_*_{old,new}. */
-       if ((compression_counter_valid & 0x33) == 0x33)
+       return (read);
+} /* int multi3_read */
+
+/* read callback */
+static int openvpn_read (void)
+{
+       FILE *fh;
+       int  i, read;
+
+       read = 0;
+
+       /* call the right read function for every status entry in the list */
+       for (i = 0; i < vpn_num; i++)
        {
-               counter_t pre_diff;
-               counter_t post_diff;
+               fh = fopen (vpn_list[i]->file, "r");
+               if (fh == NULL)
+               {
+                       char errbuf[1024];
+                       WARNING ("openvpn plugin: fopen(%s) failed: %s", vpn_list[i]->file,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+
+                       continue;
+               }
 
-               pre_diff = counter_diff (pre_compress_old, pre_compress_new);
-               post_diff = counter_diff (post_compress_old, post_compress_new);
+               switch (vpn_list[i]->version)
+               {
+                       case SINGLE:
+                               read = single_read(vpn_list[i]->name, fh);
+                               break;
 
-               /* If we compress, we're sending. */
-               compression_submit ("tx",
-                               ((gauge_t) post_diff) / ((gauge_t) pre_diff));
+                       case MULTI1:
+                               read = multi1_read(vpn_list[i]->name, fh);
+                               break;
+
+                       case MULTI2:
+                               read = multi2_read(vpn_list[i]->name, fh);
+                               break;
+
+                       case MULTI3:
+                               read = multi3_read(vpn_list[i]->name, fh);
+                               break;
+               }
+
+               fclose (fh);
        }
 
-       /* Now check the other found counters. */
-       if ((compression_counter_valid & 0xcc) == 0xcc)
+       return (read ? 0 : -1);
+} /* int openvpn_read */
+
+static int version_detect (const char *filename)
+{
+       FILE *fh;
+       char buffer[1024];
+       int version = 0;
+
+       /* Sanity checking. We're called from the config handling routine, so
+        * better play it save. */
+       if ((filename == NULL) || (*filename == 0))
+               return (0);
+
+       fh = fopen (filename, "r");
+       if (fh == NULL)
        {
-               counter_t pre_diff;
-               counter_t post_diff;
+               char errbuf[1024];
+               WARNING ("openvpn plugin: Unable to read \"%s\": %s", filename,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (0);
+       }
 
-               pre_diff = counter_diff (pre_decompress_old, pre_decompress_new);
-               post_diff = counter_diff (post_decompress_old, post_decompress_new);
+       /* now search for the specific multimode data format */
+       while ((fgets (buffer, sizeof (buffer), fh)) != NULL)
+       {
+               /* we look at the first line searching for SINGLE mode configuration */
+               if (strcmp (buffer, VSSTRING) == 0)
+               {
+                       DEBUG ("openvpn plugin: found status file version SINGLE");
+                       version = SINGLE;
+                       break;
+               }
+               /* searching for multi version 1 */
+               else if (strcmp (buffer, V1STRING) == 0)
+               {
+                       DEBUG ("openvpn plugin: found status file version MULTI1");
+                       version = MULTI1;
+                       break;
+               }
+               /* searching for multi version 2 */
+               else if (strcmp (buffer, V2STRING) == 0)
+               {
+                       DEBUG ("openvpn plugin: found status file version MULTI2");
+                       version = MULTI2;
+                       break;
+               }
+               /* searching for multi version 3 */
+               else if (strcmp (buffer, V3STRING) == 0)
+               {
+                       DEBUG ("openvpn plugin: found status file version MULTI3");
+                       version = MULTI3;
+                       break;
+               }
+       }
 
-               /* If we decompress, we're receiving. */
-               compression_submit ("rx",
-                               ((gauge_t) pre_diff) / ((gauge_t) post_diff));
+       if (version == 0)
+       {
+               /* This is only reached during configuration, so complaining to
+                * the user is in order. */
+               NOTICE ("openvpn plugin: %s: Unknown file format, please "
+                               "report this as bug. Make sure to include "
+                               "your status file, so the plugin can "
+                               "be adapted.", filename);
        }
 
-       /* Now copy all the new counters to the old counters and move the flags
-        * up. */
-       pre_compress_old = pre_compress_new;
-       post_compress_old = post_compress_new;
-       pre_decompress_old = pre_decompress_new;
-       post_decompress_old = post_decompress_new;
-       compression_counter_valid = (compression_counter_valid & 0x0f) << 4;
+       fclose (fh);
 
-       return (0);
-} /* int openvpn_read */
+       return version;
+} /* int version_detect */
 
 static int openvpn_config (const char *key, const char *value)
 {
        if (strcasecmp ("StatusFile", key) == 0)
        {
-               sfree (status_file);
+               char    *status_file, *status_name, *filename;
+               int     status_version, i;
+               vpn_status_t *temp;
+
+               /* try to detect the status file format */
+               status_version = version_detect (value);
+
+               if (status_version == 0)
+               {
+                       WARNING ("openvpn plugin: unable to detect status version, \
+                               discarding status file \"%s\".", value);
+                       return (1);
+               }
+
                status_file = sstrdup (value);
-       }
+               if (status_file == NULL)
+               {
+                       char errbuf[1024];
+                       WARNING ("openvpn plugin: sstrdup failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                       return (1);
+               }
+
+               /* it determines the file name as string starting at location filename + 1 */
+               filename = strrchr (status_file, (int) '/');
+               if (filename == NULL)
+               {
+                       /* status_file is already the file name only */
+                       status_name = status_file;
+               }
+               else
+               {
+                       /* doesn't waste memory, uses status_file starting at filename + 1 */
+                       status_name = filename + 1;
+               }
+
+               /* scan the list looking for a clone */
+               for (i = 0; i < vpn_num; i++)
+               {
+                       if (strcasecmp (vpn_list[i]->name, status_name) == 0)
+                       {
+                               WARNING ("openvpn plugin: status filename \"%s\" "
+                                               "already used, please choose a "
+                                               "different one.", status_name);
+                               sfree (status_file);
+                               return (1);
+                       }
+               }
+
+               /* create a new vpn element since file, version and name are ok */
+               temp = (vpn_status_t *) malloc (sizeof (vpn_status_t));
+               temp->file = status_file;
+               temp->version = status_version;
+               temp->name = status_name;
+
+               vpn_list = (vpn_status_t **) realloc (vpn_list, (vpn_num + 1) * sizeof (vpn_status_t *));
+               if (vpn_list == NULL)
+               {
+                       char errbuf[1024];
+                       ERROR ("openvpn plugin: malloc failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+
+                       sfree (temp->file);
+                       sfree (temp);
+                       return (1);
+               }
+
+               vpn_list[vpn_num] = temp;
+               vpn_num++;
+
+               DEBUG ("openvpn plugin: status file \"%s\" added", temp->file);
+
+       } /* if (strcasecmp ("StatusFile", key) == 0) */
+       else if (strcasecmp ("Compression", key) == 0)
+       {
+               if (IS_TRUE (value))
+                       store_compression = 1;
+               else
+               {
+                       store_compression = 0;
+                       DEBUG ("openvpn plugin: no 'compression statistcs' collected");
+               }
+       } /* if (strcasecmp ("Compression", key) == 0) */
+       else if (strcasecmp ("ImprovedNamingSchema", key) == 0)
+       {
+               if (IS_TRUE (value))
+               {
+                       DEBUG ("openvpn plugin: using the new naming schema");
+                       new_naming_schema = 1;
+               }
+               else
+               {
+                       new_naming_schema = 0;
+               }
+       } /* if (strcasecmp ("ImprovedNamingSchema", key) == 0) */
        else
        {
                return (-1);
        }
-        return (0);
+
+       return (0);
 } /* int openvpn_config */
 
+/* shutdown callback */
+static int openvpn_shutdown (void)
+{
+       int i;
+
+       for (i = 0; i < vpn_num; i++)
+       {
+               sfree (vpn_list[i]->file);
+               sfree (vpn_list[i]);
+       }
+
+       sfree (vpn_list);
+
+       return (0);
+} /* int openvpn_shutdown */
+
 void module_register (void)
 {
        plugin_register_config ("openvpn", openvpn_config,
-                               config_keys, config_keys_num);
+                       config_keys, config_keys_num);
        plugin_register_read ("openvpn", openvpn_read);
+       plugin_register_shutdown ("openvpn", openvpn_shutdown);
 } /* void module_register */
+
+/* vim: set sw=2 ts=2 : */
index 8169f4a..5e5fde0 100644 (file)
@@ -1488,6 +1488,49 @@ TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
        return r->entry;
 }
 
+/* How many rules in this chain? */
+#if 0
+static unsigned int
+TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       iptc_fn = TC_NUM_RULES;
+       CHECK(*handle);
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return (unsigned int)-1;
+       }
+       
+       return c->num_rules;
+}
+#endif
+
+#if 0
+static const STRUCT_ENTRY *
+TC_GET_RULE(const char *chain, unsigned int n, TC_HANDLE_T *handle)
+{
+       struct chain_head *c;
+       struct rule_head *r;
+       
+       iptc_fn = TC_GET_RULE;
+
+       CHECK(*handle);
+
+       c = iptcc_find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       r = iptcc_get_rule_num(c, n);
+       if (!r)
+               return NULL;
+       return r->entry;
+}
+#endif
+
 /* Returns a pointer to the target name of this position. */
 static const char *standard_target_map(int verdict)
 {
index 2579856..24b2698 100644 (file)
@@ -1461,7 +1461,12 @@ void plugin_log (int level, const char *format, ...)
        llentry_t *le;
 
        if (list_log == NULL)
+       {
+               va_start (ap, format);
+               vfprintf (stderr, format, ap);
+               va_end (ap);
                return;
+       }
 
 #if !COLLECT_DEBUG
        if (level >= LOG_DEBUG)
index fd06b21..35f99a9 100644 (file)
@@ -136,6 +136,12 @@ typedef struct procstat_entry_s
        unsigned long cpu_user_counter;
        unsigned long cpu_system_counter;
 
+       /* io data */
+       long io_rchar;
+       long io_wchar;
+       long io_syscr;
+       long io_syscw;
+
        struct procstat_entry_s *next;
 } procstat_entry_t;
 
@@ -159,6 +165,12 @@ typedef struct procstat
        unsigned long cpu_user_counter;
        unsigned long cpu_system_counter;
 
+       /* io data */
+       long io_rchar;
+       long io_wchar;
+       long io_syscr;
+       long io_syscw;
+
        struct procstat   *next;
        struct procstat_entry_s *instances;
 } procstat_t;
@@ -231,7 +243,7 @@ static void ps_list_register (const char *name, const char *regexp)
                return;
        }
 #endif
-       
+
        for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
        {
                if (strcmp (ptr->name, name) == 0)
@@ -307,13 +319,13 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                if ((pse == NULL) || (pse->id != entry->id))
                {
                        procstat_entry_t *new;
-                       
+
                        new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
                        if (new == NULL)
                                return;
                        memset (new, 0, sizeof (procstat_entry_t));
                        new->id = entry->id;
-                       
+
                        if (pse == NULL)
                                ps->instances = new;
                        else
@@ -328,6 +340,10 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                pse->vmem_size  = entry->vmem_size;
                pse->vmem_rss   = entry->vmem_rss;
                pse->stack_size = entry->stack_size;
+               pse->io_rchar   = entry->io_rchar;
+               pse->io_wchar   = entry->io_wchar;
+               pse->io_syscr   = entry->io_syscr;
+               pse->io_syscw   = entry->io_syscw;
 
                ps->num_proc   += pse->num_proc;
                ps->num_lwp    += pse->num_lwp;
@@ -335,6 +351,11 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                ps->vmem_rss   += pse->vmem_rss;
                ps->stack_size += pse->stack_size;
 
+               ps->io_rchar   += ((pse->io_rchar == -1)?0:pse->io_rchar);
+               ps->io_wchar   += ((pse->io_wchar == -1)?0:pse->io_wchar);
+               ps->io_syscr   += ((pse->io_syscr == -1)?0:pse->io_syscr);
+               ps->io_syscw   += ((pse->io_syscw == -1)?0:pse->io_syscw);
+
                if ((entry->vmem_minflt_counter == 0)
                                && (entry->vmem_majflt_counter == 0))
                {
@@ -356,7 +377,7 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                                pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
                        }
                        pse->vmem_minflt_counter = entry->vmem_minflt_counter;
-                       
+
                        if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
                        {
                                pse->vmem_majflt = entry->vmem_majflt_counter
@@ -393,7 +414,7 @@ static void ps_list_add (const char *name, const char *cmdline, procstat_entry_t
                                pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
                        }
                        pse->cpu_user_counter = entry->cpu_user_counter;
-                       
+
                        if (entry->cpu_system_counter < pse->cpu_system_counter)
                        {
                                pse->cpu_system = entry->cpu_system_counter
@@ -425,6 +446,10 @@ static void ps_list_reset (void)
                ps->vmem_size   = 0;
                ps->vmem_rss    = 0;
                ps->stack_size  = 0;
+               ps->io_rchar = -1;
+               ps->io_wchar = -1;
+               ps->io_syscr = -1;
+               ps->io_syscw = -1;
 
                pse_prev = NULL;
                pse = ps->instances;
@@ -607,12 +632,33 @@ static void ps_submit_proc_list (procstat_t *ps)
        vl.values_len = 2;
        plugin_dispatch_values (&vl);
 
+       if ( (ps->io_rchar != -1) && (ps->io_wchar != -1) )
+       {
+               sstrncpy (vl.type, "ps_disk_octets", sizeof (vl.type));
+               vl.values[0].counter = ps->io_rchar;
+               vl.values[1].counter = ps->io_wchar;
+               vl.values_len = 2;
+               plugin_dispatch_values (&vl);
+       }
+
+       if ( (ps->io_syscr != -1) && (ps->io_syscw != -1) )
+       {
+               sstrncpy (vl.type, "ps_disk_ops", sizeof (vl.type));
+               vl.values[0].counter = ps->io_syscr;
+               vl.values[1].counter = ps->io_syscw;
+               vl.values_len = 2;
+               plugin_dispatch_values (&vl);
+       }
+
        DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
                        "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
-                       "cpu_user_counter = %lu; cpu_system_counter = %lu;",
+                       "cpu_user_counter = %lu; cpu_system_counter = %lu; "
+                       "io_rchar = %ld; io_wchar = %ld; "
+                       "io_syscr = %ld; io_syscw = %ld;",
                        ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
                        ps->vmem_minflt_counter, ps->vmem_majflt_counter,
-                       ps->cpu_user_counter, ps->cpu_system_counter);
+                       ps->cpu_user_counter, ps->cpu_system_counter,
+                       ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
 } /* void ps_submit_proc_list */
 
 /* ------- additional functions for KERNEL_LINUX/HAVE_THREAD_INFO ------- */
@@ -644,6 +690,52 @@ static int ps_read_tasks (int pid)
        return ((count >= 1) ? count : 1);
 } /* int *ps_read_tasks */
 
+static procstat_t *ps_read_io (int pid, procstat_t *ps)
+{
+       FILE *fh;
+       char buffer[1024];
+       char filename[64];
+
+       char *fields[8];
+       int numfields;
+
+       ssnprintf (filename, sizeof (filename), "/proc/%i/io", pid);
+       if ((fh = fopen (filename, "r")) == NULL)
+               return (NULL);
+
+       while (fgets (buffer, 1024, fh) != NULL)
+       {
+               long *val = NULL;
+
+               if (strncasecmp (buffer, "rchar:", 6) == 0)
+                       val = &(ps->io_rchar);
+               else if (strncasecmp (buffer, "wchar:", 6) == 0)
+                       val = &(ps->io_wchar);
+               else if (strncasecmp (buffer, "syscr:", 6) == 0)
+                       val = &(ps->io_syscr);
+               else if (strncasecmp (buffer, "syscw:", 6) == 0)
+                       val = &(ps->io_syscw);
+               else
+                       continue;
+
+               numfields = strsplit (buffer, fields, 8);
+
+               if (numfields < 2)
+                       continue;
+
+               *val = atol (fields[1]);
+       }
+
+       if (fclose (fh))
+       {
+               char errbuf[1024];
+               WARNING ("processes: fclose: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
+
+       return (ps);
+} /* procstat_t *ps_read_io */
+
 int ps_read_process (int pid, procstat_t *ps, char *state)
 {
        char  filename[64];
@@ -704,7 +796,10 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        else
        {
                if ( (ps->num_lwp = ps_read_tasks (pid)) == -1 )
-                       return (-1);
+               {
+                       /* returns -1 => kernel 2.4 */
+                       ps->num_lwp = 1;
+               }
                ps->num_proc = 1;
        }
 
@@ -743,6 +838,17 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        ps->vmem_rss = (unsigned long) vmem_rss;
        ps->stack_size = (unsigned long) stack_size;
 
+       if ( (ps_read_io (pid, ps)) == NULL)
+       {
+               /* no io data */
+               ps->io_rchar = -1;
+               ps->io_wchar = -1;
+               ps->io_syscr = -1;
+               ps->io_syscw = -1;
+
+               DEBUG("ps_read_process: not get io data for pid %i",pid);
+       }
+
        /* success */
        return (0);
 } /* int ps_read_process (...) */
@@ -836,6 +942,70 @@ static char *ps_get_cmdline (pid_t pid, char *name, char *buf, size_t buf_len)
        }
        return buf;
 } /* char *ps_get_cmdline (...) */
+
+static unsigned long read_fork_rate ()
+{
+       FILE *proc_stat;
+       char buf[1024];
+       unsigned long result = 0;
+       int numfields;
+       char *fields[3];
+
+       proc_stat = fopen("/proc/stat", "r");
+       if (proc_stat == NULL) {
+               char errbuf[1024];
+               ERROR ("processes plugin: fopen (/proc/stat) failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return ULONG_MAX;
+       }
+
+       while (fgets (buf, sizeof(buf), proc_stat) != NULL)
+       {
+               char *endptr;
+
+               numfields = strsplit(buf, fields, STATIC_ARRAY_SIZE (fields));
+               if (numfields != 2)
+                       continue;
+
+               if (strcmp ("processes", fields[0]) != 0)
+                       continue;
+
+               errno = 0;
+               endptr = NULL;
+               result = strtoul(fields[1], &endptr, 10);
+               if ((endptr == fields[1]) || (errno != 0)) {
+                       ERROR ("processes plugin: Cannot parse fork rate: %s",
+                                       fields[1]);
+                       result = ULONG_MAX;
+                       break;
+               }
+
+               break;
+       }
+
+       fclose(proc_stat);
+
+       return result;
+}
+
+static void ps_submit_fork_rate (unsigned long value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].derive = (derive_t) value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "processes", sizeof (vl.plugin));
+       sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
+       sstrncpy (vl.type, "fork_rate", sizeof (vl.type));
+       sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+}
+
 #endif /* KERNEL_LINUX */
 
 #if HAVE_THREAD_INFO
@@ -1158,6 +1328,8 @@ static int ps_read (void)
        procstat_entry_t pse;
        char       state;
 
+       unsigned long fork_rate;
+
        procstat_t *ps_ptr;
 
        running = sleeping = zombies = stopped = paging = blocked = 0;
@@ -1205,6 +1377,11 @@ static int ps_read (void)
                pse.cpu_system = 0;
                pse.cpu_system_counter = ps.cpu_system_counter;
 
+               pse.io_rchar = ps.io_rchar;
+               pse.io_wchar = ps.io_wchar;
+               pse.io_syscr = ps.io_syscr;
+               pse.io_syscw = ps.io_syscw;
+
                switch (state)
                {
                        case 'R': running++;  break;
@@ -1231,6 +1408,10 @@ static int ps_read (void)
 
        for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
                ps_submit_proc_list (ps_ptr);
+
+       fork_rate = read_fork_rate();
+       if (fork_rate != ULONG_MAX)
+               ps_submit_fork_rate(fork_rate);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKVM_GETPROCS && HAVE_STRUCT_KINFO_PROC_FREEBSD
@@ -1329,6 +1510,12 @@ static int ps_read (void)
                        * 1000
                        + procs[i].ki_rusage.ru_stime.tv_usec;
 
+               /* no io data */
+               pse.io_rchar = -1;
+               pse.io_wchar = -1;
+               pse.io_syscr = -1;
+               pse.io_syscw = -1;
+
                switch (procs[i].ki_stat)
                {
                        case SSTOP:     stopped++;      break;
index 75e9a1c..73fe154 100644 (file)
@@ -224,9 +224,7 @@ static int protocols_config (const char *key, const char *value)
   else if (strcasecmp (key, "IgnoreSelected") == 0)
   {
     int invert = 1;
-    if ((strcasecmp (value, "True") == 0)
-        || (strcasecmp (value, "Yes") == 0)
-        || (strcasecmp (value, "On") == 0))
+    if (IS_TRUE (value))
       invert = 0;
     ignorelist_set_invert (values_list, invert);
   }
diff --git a/src/routeros.c b/src/routeros.c
new file mode 100644 (file)
index 0000000..02c96d7
--- /dev/null
@@ -0,0 +1,347 @@
+/**
+ * collectd - src/routeros.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 <routeros_api.h>
+
+struct cr_data_s
+{
+  ros_connection_t *connection;
+
+  char *node;
+  char *service;
+  char *username;
+  char *password;
+
+  _Bool collect_interface;
+  _Bool collect_regtable;
+};
+typedef struct cr_data_s cr_data_t;
+
+static void cr_submit_io (const char *type, const char *type_instance, /* {{{ */
+    counter_t rx, counter_t tx)
+{
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = rx;
+       values[1].counter = tx;
+
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "routeros", sizeof (vl.plugin));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* }}} void cr_submit_io */
+
+static void submit_interface (const ros_interface_t *i) /* {{{ */
+{
+  if (i == NULL)
+    return;
+
+  if (!i->running)
+  {
+    submit_interface (i->next);
+    return;
+  }
+
+  cr_submit_io ("if_packets", i->name,
+      (counter_t) i->rx_packets, (counter_t) i->tx_packets);
+  cr_submit_io ("if_octets", i->name,
+      (counter_t) i->rx_bytes, (counter_t) i->tx_bytes);
+  cr_submit_io ("if_errors", i->name,
+      (counter_t) i->rx_errors, (counter_t) i->tx_errors);
+  cr_submit_io ("if_dropped", i->name,
+      (counter_t) i->rx_drops, (counter_t) i->tx_drops);
+
+  submit_interface (i->next);
+} /* }}} void submit_interface */
+
+static int handle_interface (__attribute__((unused)) ros_connection_t *c, /* {{{ */
+    const ros_interface_t *i, __attribute__((unused)) void *user_data)
+{
+  submit_interface (i);
+  return (0);
+} /* }}} int handle_interface */
+
+static void cr_submit_gauge (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 = STATIC_ARRAY_SIZE (values);
+       sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+       sstrncpy (vl.plugin, "routeros", sizeof (vl.plugin));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (&vl);
+} /* }}} void cr_submit_gauge */
+
+static void submit_regtable (const ros_registration_table_t *r) /* {{{ */
+{
+  char type_instance[DATA_MAX_NAME_LEN];
+
+  if (r == NULL)
+    return;
+
+  /*** RX ***/
+  ssnprintf (type_instance, sizeof (type_instance), "%s-%s-rx",
+      r->interface, r->radio_name);
+  cr_submit_gauge ("bitrate", type_instance,
+      (gauge_t) (1000000.0 * r->rx_rate));
+  cr_submit_gauge ("signal_power", type_instance,
+      (gauge_t) r->rx_signal_strength);
+  cr_submit_gauge ("signal_quality", type_instance,
+      (gauge_t) r->rx_ccq);
+
+  /*** TX ***/
+  ssnprintf (type_instance, sizeof (type_instance), "%s-%s-tx",
+      r->interface, r->radio_name);
+  cr_submit_gauge ("bitrate", type_instance,
+      (gauge_t) (1000000.0 * r->tx_rate));
+  cr_submit_gauge ("signal_power", type_instance,
+      (gauge_t) r->tx_signal_strength);
+  cr_submit_gauge ("signal_quality", type_instance,
+      (gauge_t) r->tx_ccq);
+
+  /*** RX / TX ***/
+  ssnprintf (type_instance, sizeof (type_instance), "%s-%s",
+      r->interface, r->radio_name);
+  cr_submit_io ("if_octets", type_instance,
+      (counter_t) r->rx_bytes, (counter_t) r->tx_bytes);
+  cr_submit_gauge ("snr", type_instance, (gauge_t) r->signal_to_noise);
+
+  submit_regtable (r->next);
+} /* }}} void submit_regtable */
+
+static int handle_regtable (__attribute__((unused)) ros_connection_t *c, /* {{{ */
+    const ros_registration_table_t *r,
+    __attribute__((unused)) void *user_data)
+{
+  submit_regtable (r);
+  return (0);
+} /* }}} int handle_regtable */
+
+static int cr_read (user_data_t *user_data) /* {{{ */
+{
+  int status;
+  cr_data_t *rd;
+
+  if (user_data == NULL)
+    return (EINVAL);
+
+  rd = user_data->data;
+  if (rd == NULL)
+    return (EINVAL);
+
+  if (rd->connection == NULL)
+  {
+    rd->connection = ros_connect (rd->node, rd->service,
+       rd->username, rd->password);
+    if (rd->connection == NULL)
+    {
+      char errbuf[128];
+      ERROR ("routeros plugin: ros_connect failed: %s",
+         sstrerror (errno, errbuf, sizeof (errbuf)));
+      return (-1);
+    }
+  }
+  assert (rd->connection != NULL);
+
+  if (rd->collect_interface)
+  {
+    status = ros_interface (rd->connection, handle_interface,
+       /* user data = */ NULL);
+    if (status != 0)
+    {
+      char errbuf[128];
+      ERROR ("routeros plugin: ros_interface failed: %s",
+         sstrerror (status, errbuf, sizeof (errbuf)));
+      ros_disconnect (rd->connection);
+      rd->connection = NULL;
+      return (-1);
+    }
+  }
+
+  if (rd->collect_regtable)
+  {
+    status = ros_registration_table (rd->connection, handle_regtable,
+       /* user data = */ NULL);
+    if (status != 0)
+    {
+      char errbuf[128];
+      ERROR ("routeros plugin: ros_registration_table failed: %s",
+         sstrerror (status, errbuf, sizeof (errbuf)));
+      ros_disconnect (rd->connection);
+      rd->connection = NULL;
+      return (-1);
+    }
+  }
+
+  return (0);
+} /* }}} int cr_read */
+
+static void cr_free_data (cr_data_t *ptr) /* {{{ */
+{
+  if (ptr == NULL)
+    return;
+
+  ros_disconnect (ptr->connection);
+  ptr->connection = NULL;
+
+  sfree (ptr->node);
+  sfree (ptr->service);
+  sfree (ptr->username);
+  sfree (ptr->password);
+
+  sfree (ptr);
+} /* }}} void cr_free_data */
+
+static int cr_config_router (oconfig_item_t *ci) /* {{{ */
+{
+  cr_data_t *router_data;
+  char read_name[128];
+  user_data_t user_data;
+  int status;
+  int i;
+
+  router_data = malloc (sizeof (*router_data));
+  if (router_data == NULL)
+    return (-1);
+  memset (router_data, 0, sizeof (router_data));
+  router_data->connection = NULL;
+  router_data->node = NULL;
+  router_data->service = NULL;
+  router_data->username = NULL;
+  router_data->password = NULL;
+  router_data->collect_interface = false;
+  router_data->collect_regtable = false;
+
+  status = 0;
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Host", child->key) == 0)
+      status = cf_util_get_string (child, &router_data->node);
+    else if (strcasecmp ("Port", child->key) == 0)
+      status = cf_util_get_string (child, &router_data->service);
+    else if (strcasecmp ("User", child->key) == 0)
+      status = cf_util_get_string (child, &router_data->username);
+    else if (strcasecmp ("Password", child->key) == 0)
+      status = cf_util_get_string (child, &router_data->password);
+    else if (strcasecmp ("CollectInterface", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_interface);
+    else if (strcasecmp ("CollectRegistrationTable", child->key) == 0)
+      cf_util_get_boolean (child, &router_data->collect_regtable);
+    else
+    {
+      WARNING ("routeros plugin: Unknown config option `%s'.", child->key);
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  if (status == 0)
+  {
+    if (router_data->node == NULL)
+    {
+      ERROR ("routeros plugin: No `Host' option within a `Router' block. "
+         "Where should I connect to?");
+      status = -1;
+    }
+
+    if (router_data->password == NULL)
+    {
+      ERROR ("routeros plugin: No `Password' option within a `Router' block. "
+         "How should I authenticate?");
+      status = -1;
+    }
+
+    if (!router_data->collect_interface
+       && !router_data->collect_regtable)
+    {
+      ERROR ("routeros plugin: No `Collect*' option within a `Router' block. "
+         "What statistics should I collect?");
+      status = -1;
+    }
+  }
+
+  if ((status == 0) && (router_data->username == NULL))
+  {
+    router_data->username = sstrdup ("admin");
+    if (router_data->username == NULL)
+    {
+      ERROR ("routeros plugin: sstrdup failed.");
+      status = -1;
+    }
+  }
+
+  ssnprintf (read_name, sizeof (read_name), "routeros/%s", router_data->node);
+  user_data.data = router_data;
+  user_data.free_func = (void *) cr_free_data;
+  if (status == 0)
+    status = plugin_register_complex_read (read_name, cr_read,
+       /* interval = */ NULL, &user_data);
+
+  if (status != 0)
+    cr_free_data (router_data);
+
+  return (status);
+} /* }}} int cr_config_router */
+
+static int cr_config (oconfig_item_t *ci)
+{
+  int i;
+
+  for (i = 0; i < ci->children_num; i++)
+  {
+    oconfig_item_t *child = ci->children + i;
+
+    if (strcasecmp ("Router", child->key) == 0)
+      cr_config_router (child);
+    else
+    {
+      WARNING ("routeros plugin: Unknown config option `%s'.", child->key);
+    }
+  }
+
+  return (0);
+} /* }}} int cr_config */
+
+void module_register (void)
+{
+  plugin_register_complex_config ("routeros", cr_config);
+} /* void module_register */
+
+/* vim: set sw=2 noet fdm=marker : */
index 728ada4..aab8c11 100644 (file)
@@ -24,6 +24,7 @@
 #include "common.h"
 #include "utils_rrdcreate.h"
 
+#undef HAVE_CONFIG_H
 #include <rrd_client.h>
 
 /*
@@ -192,18 +193,14 @@ static int rc_config (const char *key, const char *value)
   }
   else if (strcasecmp ("CreateFiles", key) == 0)
   {
-    if ((strcasecmp ("false", value) == 0)
-        || (strcasecmp ("no", value) == 0)
-        || (strcasecmp ("off", value) == 0))
+    if (IS_FALSE (value))
       config_create_files = 0;
     else
       config_create_files = 1;
   }
   else if (strcasecmp ("CollectStatistics", key) == 0)
   {
-    if ((strcasecmp ("false", value) == 0)
-        || (strcasecmp ("no", value) == 0)
-        || (strcasecmp ("off", value) == 0))
+    if (IS_FALSE (value))
       config_collect_stats = 0;
     else
       config_collect_stats = 1;
index fa461a2..8391346 100644 (file)
@@ -237,9 +237,7 @@ static int sensors_config (const char *key, const char *value)
        else if (strcasecmp (key, "IgnoreSelected") == 0)
        {
                ignorelist_set_invert (sensor_list, 1);
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        ignorelist_set_invert (sensor_list, 0);
        }
        else
index 7568cf6..00df377 100644 (file)
@@ -724,7 +724,8 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
     double scale, double shift)
 {
   value_t ret;
-  uint64_t temp = 0;
+  uint64_t tmp_unsigned = 0;
+  int64_t tmp_signed = 0;
   int defined = 1;
 
   if ((vl->type == ASN_INTEGER)
@@ -735,15 +736,17 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
 #endif
       || (vl->type == ASN_GAUGE))
   {
-    temp = (uint32_t) *vl->val.integer;
-    DEBUG ("snmp plugin: Parsed int32 value is %"PRIu64".", temp);
+    tmp_unsigned = (uint32_t) *vl->val.integer;
+    tmp_signed = (int32_t) *vl->val.integer;
+    DEBUG ("snmp plugin: Parsed int32 value is %"PRIi64".", tmp_signed);
   }
   else if (vl->type == ASN_COUNTER64)
   {
-    temp = (uint32_t) vl->val.counter64->high;
-    temp = temp << 32;
-    temp += (uint32_t) vl->val.counter64->low;
-    DEBUG ("snmp plugin: Parsed int64 value is %"PRIu64".", temp);
+    tmp_unsigned = (uint32_t) vl->val.counter64->high;
+    tmp_unsigned = tmp_unsigned << 32;
+    tmp_unsigned += (uint32_t) vl->val.counter64->low;
+    tmp_signed = (int64_t) tmp_unsigned;
+    DEBUG ("snmp plugin: Parsed int64 value is %"PRIu64".", tmp_unsigned);
   }
   else if (vl->type == ASN_OCTET_STR)
   {
@@ -805,17 +808,19 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
     }
   } /* if (vl->type == ASN_OCTET_STR) */
   else if (type == DS_TYPE_COUNTER)
-    ret.counter = temp;
+  {
+    ret.counter = tmp_unsigned;
+  }
   else if (type == DS_TYPE_GAUGE)
   {
     ret.gauge = NAN;
     if (defined != 0)
-      ret.gauge = (scale * temp) + shift;
+      ret.gauge = (scale * tmp_signed) + shift;
   }
   else if (type == DS_TYPE_DERIVE)
-    ret.derive = (derive_t) temp;
+    ret.derive = (derive_t) tmp_signed;
   else if (type == DS_TYPE_ABSOLUTE)
-    ret.absolute = (absolute_t) temp;
+    ret.absolute = (absolute_t) tmp_unsigned;
   else
   {
     ERROR ("snmp plugin: csnmp_value_list_to_value: Unknown data source "
@@ -889,6 +894,72 @@ static int csnmp_check_res_left_subtree (const host_definition_t *host,
   return (0);
 } /* int csnmp_check_res_left_subtree */
 
+static int csnmp_strvbcopy_hexstring (char *dst, /* {{{ */
+    const struct variable_list *vb, size_t dst_size)
+{
+  char *buffer_ptr;
+  size_t buffer_free;
+  size_t i;
+
+  buffer_ptr = dst;
+  buffer_free = dst_size;
+
+  for (i = 0; i < vb->val_len; i++)
+  {
+    int status;
+
+    status = snprintf (buffer_ptr, buffer_free,
+       (i == 0) ? "%02x" : ":%02x", (unsigned int) vb->val.bitstring[i]);
+
+    if (status >= buffer_free)
+    {
+      buffer_ptr += (buffer_free - 1);
+      *buffer_ptr = 0;
+      return (dst_size + (buffer_free - status));
+    }
+    else /* if (status < buffer_free) */
+    {
+      buffer_ptr += status;
+      buffer_free -= status;
+    }
+  }
+
+  return ((int) (dst_size - buffer_free));
+} /* }}} int csnmp_strvbcopy_hexstring */
+
+static int csnmp_strvbcopy (char *dst, /* {{{ */
+    const struct variable_list *vb, size_t dst_size)
+{
+  char *src;
+  size_t num_chars;
+  size_t i;
+
+  if (vb->type == ASN_OCTET_STR)
+    src = (char *) vb->val.string;
+  else if (vb->type == ASN_BIT_STR)
+    src = (char *) vb->val.bitstring;
+  else
+  {
+    dst[0] = 0;
+    return (EINVAL);
+  }
+
+  num_chars = dst_size - 1;
+  if (num_chars > vb->val_len)
+    num_chars = vb->val_len;
+
+  for (i = 0; i < num_chars; i++)
+  {
+    /* Check for control characters. */
+    if ((src[i] >= 0) && (src[i] < 32))
+      return (csnmp_strvbcopy_hexstring (dst, vb, dst_size));
+    dst[i] = src[i];
+  }
+  dst[num_chars] = 0;
+
+  return ((int) vb->val_len);
+} /* }}} int csnmp_strvbcopy */
+
 static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     csnmp_list_instances_t **tail,
     const struct snmp_pdu *res)
@@ -917,17 +988,8 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   if ((vb->type == ASN_OCTET_STR) || (vb->type == ASN_BIT_STR))
   {
     char *ptr;
-    size_t instance_len;
-
-    memset (il->instance, 0, sizeof (il->instance));
-    instance_len = sizeof (il->instance) - 1;
-    if (instance_len > vb->val_len)
-      instance_len = vb->val_len;
 
-    sstrncpy (il->instance, (char *) ((vb->type == ASN_OCTET_STR)
-         ? vb->val.string
-         : vb->val.bitstring),
-       instance_len + 1);
+    csnmp_strvbcopy (il->instance, vb, sizeof (il->instance));
 
     for (ptr = il->instance; *ptr != '\0'; ptr++)
     {
index 22eda1f..6ccae56 100644 (file)
@@ -173,7 +173,7 @@ static int swap_read (void)
 #if KERNEL_LINUX
        FILE *fh;
        char buffer[1024];
-       
+
        char *fields[8];
        int numfields;
 
index f164b53..d68cd09 100644 (file)
@@ -454,9 +454,7 @@ static int conn_config (const char *key, const char *value)
 {
   if (strcasecmp (key, "ListeningPorts") == 0)
   {
-    if ((strcasecmp (value, "Yes") == 0)
-       || (strcasecmp (value, "True") == 0)
-       || (strcasecmp (value, "On") == 0))
+    if (IS_TRUE (value))
       port_collect_listening = 1;
     else
       port_collect_listening = 0;
index fe54aee..2b70805 100644 (file)
@@ -198,17 +198,13 @@ static int thermal_config (const char *key, const char *value)
        else if (strcasecmp (key, "IgnoreSelected") == 0)
        {
                ignorelist_set_invert (device_list, 1);
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        ignorelist_set_invert (device_list, 0);
        }
        else if (strcasecmp (key, "ForceUseProcfs") == 0)
        {
                force_procfs = 0;
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
+               if (IS_TRUE (value))
                        force_procfs = 1;
        }
        else
index e67ccce..a5872eb 100644 (file)
@@ -12,13 +12,15 @@ ath_nodes           value:GAUGE:0:65535
 ath_stat               value:COUNTER:0:4294967295
 bitrate                        value:GAUGE:0:4294967295
 bytes                  value:GAUGE:0:U
-cache_ratio             value:GAUGE:0:100
+cache_ratio            value:GAUGE:0:100
 cache_result           value:COUNTER:0:4294967295
 cache_size             value:GAUGE:0:4294967295
 charge                 value:GAUGE:0:U
+compression            uncompressed:COUNTER:0:U, compressed:COUNTER:0:U
 compression_ratio      value:GAUGE:0:2
 connections            value:COUNTER:0:U
 conntrack              entropy:GAUGE:0:4294967295
+contextswitch          contextswitches:DERIVE:0:U
 counter                        value:COUNTER:U:U
 cpufreq                        value:GAUGE:0:U
 cpu                    value:COUNTER:0:4294967295
@@ -26,12 +28,13 @@ current                     value:GAUGE:U:U
 delay                  seconds:GAUGE:-1000000:1000000
 derive                 value:DERIVE:0:U
 df                     used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623
-df_complex              value:GAUGE:0:U
-disk_latency            read:GAUGE:0:U, write:GAUGE:0:U
+df_complex             value:GAUGE:0:U
+df_inodes              value:GAUGE:0:U
+disk_latency           read:GAUGE:0:U, write:GAUGE:0:U
 disk_merged            read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
 disk_octets            read:COUNTER:0:17179869183, write:COUNTER:0:17179869183
 disk_ops               read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
-disk_ops_complex        value:COUNTER:0:4294967296
+disk_ops_complex       value:COUNTER:0:4294967296
 disk_time              read:COUNTER:0:1000000, write:COUNTER:0:1000000
 dns_answer             value:COUNTER:0:65535
 dns_notify             value:COUNTER:0:65535
@@ -54,11 +57,12 @@ 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
+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
+fork_rate              value:DERIVE:0:U
 gauge                  value:GAUGE:U:U
 http_request_methods   count:COUNTER:0:134217728
 http_requests          count:COUNTER:0:134217728
@@ -120,13 +124,15 @@ protocol_counter  value:COUNTER:0:U
 ps_count               processes:GAUGE:0:1000000, threads:GAUGE:0:1000000
 ps_cputime             user:COUNTER:0:16000000, syst:COUNTER:0:16000000
 ps_pagefaults          minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807
+ps_disk_octets         read:DERIVE:0:U, write:DERIVE:0:U
+ps_disk_ops            read:DERIVE:0:U, write:DERIVE:0:U
 ps_rss                 value:GAUGE:0:9223372036854775807
 ps_stacksize           value:GAUGE:0:9223372036854775807
 ps_state               value:GAUGE:0:65535
 ps_vm                  value:GAUGE:0:9223372036854775807
 queue_length           value:GAUGE:0:U
-records                 count:GAUGE:0:U
 response_time          value:GAUGE:0:U
+records                        count:GAUGE:0:U
 route_etx              value:GAUGE:0:U
 route_metric           value:GAUGE:0:U
 routes                 value:GAUGE:0:U
@@ -134,6 +140,7 @@ 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
+snr                    value:GAUGE:0:U
 spam_check             value:GAUGE:0:U
 spam_score             value:GAUGE:U:U
 swap_io                        value:DERIVE:0:1099511627776
@@ -146,6 +153,7 @@ 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
+total_values           value:DERIVE:0:U
 uptime                 value:GAUGE:0:4294967295
 users                  users:GAUGE:0:65535
 virt_cpu_total         ns:COUNTER:0:256000000000
index be36abc..0b89748 100644 (file)
@@ -158,32 +158,45 @@ static int us_open_socket (void)
 
 static void *us_handle_client (void *arg)
 {
-       int fd;
+       int fdin;
+       int fdout;
        FILE *fhin, *fhout;
 
-       fd = *((int *) arg);
+       fdin = *((int *) arg);
        free (arg);
        arg = NULL;
 
-       DEBUG ("unixsock plugin: us_handle_client: Reading from fd #%i", fd);
+       DEBUG ("unixsock plugin: us_handle_client: Reading from fd #%i", fdin);
 
-       fhin  = fdopen (fd, "r");
+       fdout = dup (fdin);
+       if (fdout < 0)
+       {
+               char errbuf[1024];
+               ERROR ("unixsock plugin: dup failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               close (fdin);
+               pthread_exit ((void *) 1);
+       }
+
+       fhin  = fdopen (fdin, "r");
        if (fhin == NULL)
        {
                char errbuf[1024];
                ERROR ("unixsock plugin: fdopen failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
-               close (fd);
+               close (fdin);
+               close (fdout);
                pthread_exit ((void *) 1);
        }
 
-       fhout = fdopen (fd, "w");
+       fhout = fdopen (fdout, "w");
        if (fhout == NULL)
        {
                char errbuf[1024];
                ERROR ("unixsock plugin: fdopen failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
-               fclose (fhin); /* this closes fd as well */
+               fclose (fhin); /* this closes fdin as well */
+               close (fdout);
                pthread_exit ((void *) 1);
        }
 
@@ -231,11 +244,12 @@ static void *us_handle_client (void *arg)
 
                fields_num = strsplit (buffer_copy, fields,
                                sizeof (fields) / sizeof (fields[0]));
-
                if (fields_num < 1)
                {
-                       close (fd);
-                       break;
+                       fprintf (fhout, "-1 Internal error\n");
+                       fclose (fhin);
+                       fclose (fhout);
+                       pthread_exit ((void *) 1);
                }
 
                if (strcasecmp (fields[0], "getval") == 0)
index 6775d20..d32f1db 100644 (file)
@@ -77,9 +77,7 @@ static int vmem_config (const char *key, const char *value)
 {
   if (strcasecmp ("Verbose", key) == 0)
   {
-    if ((strcasecmp ("true", value) == 0)
-       || (strcasecmp ("yes", value) == 0)
-       || (strcasecmp ("on", value) == 0))
+    if (IS_TRUE (value))
       verbose_output = 1;
     else
       verbose_output = 0;
index f2c61f5..e0114d0 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-DEFAULT_VERSION="4.8.0.git"
+DEFAULT_VERSION="4.8.1.git"
 
 VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"