From: Florian Forster Date: Tue, 8 Dec 2009 12:28:10 +0000 (+0100) Subject: Merge branch 'st/python' X-Git-Tag: collectd-4.9.0~31 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=f02efdb47ff2037ff0365fdd5c3c1b6a418c3431;hp=563b2bdd2b89244fab328b964f5a44ecfde607bd;p=collectd.git Merge branch 'st/python' --- diff --git a/.gitignore b/.gitignore index 46c8c048..e8f9af66 100644 --- a/.gitignore +++ b/.gitignore @@ -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 41c9bb0a..5abbfa33 100644 --- a/AUTHORS +++ b/AUTHORS @@ -85,6 +85,7 @@ Lyonel Vincent Marco Chiappero - uptime plugin. - ip6tables support in the iptables plugin. + - openvpn plugin (support for more status file formats) Michael Stapelberg - OpenBSD port of the tcpconns plugin. @@ -110,6 +111,11 @@ Oleg King Ondrej Zajicek - madwifi plugin. +Patrik Weiskircher + - Contextswitch plugin. + - Forkrate counter in the processes plugin. + - INode count in the DF plugin. + Paul Sadauskas - tokyotyrant plugin. - `ReportByDevice' option of the df plugin. diff --git a/ChangeLog b/ChangeLog index 83980834..ecc3869d 100644 --- 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 @@ -47,6 +58,17 @@ 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 70118ad1..e95bae62 100644 --- 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. + * libgcrypt (optional) Used by the `network' plugin for encryption and authentication. + * 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. + (and others) * libmemcached (optional) Used by the `memcachec' plugin to connect to a memcache daemon. + * 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. + * librouteros (optional) + Used by the `routeros' plugin to connect to a device running `RouterOS'. + + * 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. + * libtokyotyrant (optional) + Used by the tokyotyrant plugin. + + * libupsclient/nut (optional) For the `nut' plugin which queries nut's `upsd'. @@ -603,7 +618,7 @@ Prerequisites * libyajl (optional) Parse JSON data. This is needed for the `curl_json' plugin. - + Configuring / Compiling / Installing ------------------------------------ diff --git a/bindings/java/org/collectd/java/GenericJMXConfConnection.java b/bindings/java/org/collectd/java/GenericJMXConfConnection.java index 7214fd76..ffa9ded4 100644 --- a/bindings/java/org/collectd/java/GenericJMXConfConnection.java +++ b/bindings/java/org/collectd/java/GenericJMXConfConnection.java @@ -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 () diff --git a/bindings/java/org/collectd/java/GenericJMXConfMBean.java b/bindings/java/org/collectd/java/GenericJMXConfMBean.java index 27e9e329..1587bd5f 100644 --- a/bindings/java/org/collectd/java/GenericJMXConfMBean.java +++ b/bindings/java/org/collectd/java/GenericJMXConfMBean.java @@ -170,7 +170,7 @@ class GenericJMXConfMBean return (this._name); } /* }}} */ - public void query (MBeanServerConnection conn, PluginData pd) /* {{{ */ + public int query (MBeanServerConnection conn, PluginData pd) /* {{{ */ { Set names; Iterator 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 */ } diff --git a/configure.in b/configure.in index 67f837ea..ea1e40e2 100644 --- a/configure.in +++ b/configure.in @@ -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 index 445b1838..00000000 --- a/contrib/collectd-network.py +++ /dev/null @@ -1,318 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- -# vim: fileencoding=utf-8 -# -# Copyright © 2009 Adrian Perez -# -# 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(" 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 index 2d7430d2..00000000 --- a/contrib/collectd-unixsock.py +++ /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 -# -# SocketFile "/var/run/collectd-unixsock" -# SocketPerms "0775" -# -# -# Copyright (C) 2008 Clay Loveless -# -# 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 index 00000000..445b1838 --- /dev/null +++ b/contrib/collectd_network.py @@ -0,0 +1,318 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# vim: fileencoding=utf-8 +# +# Copyright © 2009 Adrian Perez +# +# 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(" 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 index 00000000..ebe549c8 --- /dev/null +++ b/contrib/collectd_unixsock.py @@ -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 +# +# SocketFile "/var/run/collectd-unixsock" +# SocketPerms "0775" +# +# +# Copyright (C) 2008 Clay Loveless +# +# 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) diff --git a/contrib/collection3/etc/collection.conf b/contrib/collection3/etc/collection.conf index 1322d0d9..e56017e1 100644 --- a/contrib/collection3/etc/collection.conf +++ b/contrib/collection3/etc/collection.conf @@ -139,21 +139,23 @@ GraphWidth 400 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 Module GenericIO diff --git a/contrib/snmp-data.conf b/contrib/snmp-data.conf index fd8d4044..07381db0 100644 --- a/contrib/snmp-data.conf +++ b/contrib/snmp-data.conf @@ -452,4 +452,73 @@ InstancePrefix "input" Values ".1.3.6.1.4.1.1909.10.6.1.1.3" + + # + # Mikrotik RouterBoards + # + # Wireless statistics: station mode + + 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" + + + + 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" + + + + 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" + + + # Wireless statistics: AP mode / registration table + + 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" + + + + 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" + + + + 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" + + + + 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" + + + + 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" + diff --git a/src/Makefile.am b/src/Makefile.am index b240618d..4b9fa0ee 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 '\' $@ >/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 '\' $@ >/dev/null 2>&1; \ then \ echo "$@ has some POD errors!"; false; \ diff --git a/src/apache.c b/src/apache.c index a333bf2b..df1b560f 100644 --- a/src/apache.c +++ b/src/apache.c @@ -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 { diff --git a/src/ascent.c b/src/ascent.c index 1e7eca14..6782fce1 100644 --- a/src/ascent.c +++ b/src/ascent.c @@ -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); diff --git a/src/collectd-python.pod b/src/collectd-python.pod index 136c75e6..ac679c5d 100644 --- a/src/collectd-python.pod +++ b/src/collectd-python.pod @@ -641,12 +641,13 @@ L, =head1 AUTHOR -The C has been written by Sebastian Harl -EshEatEtokkee.orgE. +The C has been written by +Sven Trenkel EcollectdEatEsemidefinite.deE. -This manpage has been written by Florian Forster -EoctoEatEverplant.orgE and Sebastian Harl -EshEatEtokkee.orgE. +This manpage has been written by Sven Trenkel +EcollectdEatEsemidefinite.deE. +It is based on the L manual page by +Florian Forster EoctoEatEverplant.orgE and +Sebastian Harl EshEatEtokkee.orgE. =cut - diff --git a/src/collectd-snmp.pod b/src/collectd-snmp.pod index f34113d9..51e04813 100644 --- a/src/collectd-snmp.pod +++ b/src/collectd-snmp.pod @@ -184,7 +184,7 @@ This value is not applied to counter-values. I is added to gauge-values returned by the SNMP-agent after they have been multiplied by any B 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. diff --git a/src/collectd.c b/src/collectd.c index 576abef4..bc69a3b7 100644 --- a/src/collectd.c +++ b/src/collectd.c @@ -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)); diff --git a/src/collectd.conf.in b/src/collectd.conf.in index e78f9dce..ad13353f 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -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 # # -# +# +# DataDir "@prefix@/var/lib/@PACKAGE_NAME@/csv" +# StoreRates false +# + +# +# +# URL "http://finance.google.com/finance?q=NYSE%3AAMD" +# User "foo" +# Password "bar" +# +# Regex "]*> *([0-9]*\\.[0-9]+) *" +# DSType "GaugeAverage" +# Type "stock_value" +# Instance "AMD" +# +# +# + +# ## See: http://wiki.apache.org/couchdb/Runtime_Statistics # # Instance "httpd" @@ -206,25 +227,6 @@ FQDNLookup true # # -# -# DataDir "@prefix@/var/lib/@PACKAGE_NAME@/csv" -# StoreRates false -# - -# -# -# URL "http://finance.google.com/finance?q=NYSE%3AAMD" -# User "foo" -# Password "bar" -# -# Regex "]*> *([0-9]*\\.[0-9]+) *" -# DSType "GaugeAverage" -# Type "stock_value" -# Instance "AMD" -# -# -# - # # # Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl" @@ -263,6 +265,7 @@ FQDNLookup true # # Interface "eth0" # IgnoreSource "192.168.0.1" +# SelectNumericQueryTypes true # # @@ -313,12 +316,6 @@ FQDNLookup true # TranslateDevicename false # -# -# URL "http://example.com/collectd-import" -# User "www-user" -# Password "secret" -# - # # Interface "eth0" # IgnoreSelected false @@ -364,6 +361,17 @@ FQDNLookup true # HostnameFormat name # +# +# Interface "wlan0" +# IgnoreSelected false +# Source "SysFS" +# WatchSet "None" +# WatchAdd "node_octets" +# WatchAdd "node_rssi" +# WatchAdd "is_rx_acl" +# WatchAdd "is_scan_active" +# + # # Host "127.0.0.1" # Port "411" @@ -421,6 +429,7 @@ FQDNLookup true # TimeToLive "128" # Forward false # CacheFlush 1800 +# ReportStats false @LOAD_PLUGIN_NETWORK@ # @@ -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 diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index f7531cd8..71b99898 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -870,19 +870,44 @@ Select partitions based on the mountpoint. Select partitions based on the filesystem type. -=item B I|I +=item B B|B Invert the selection: If set to true, all partitions B 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 partitions are selected. -=item B I|I +=item B B|B Report using the device name rather than the mountpoint. i.e. with this I, (the default), it will report a disk as "root", but with it I, it will be "sda1" (or whichever). +=item B B|B + +When enabled, the blocks reserved for root are reported separately. When +disabled (the default for backwards compatibility reasons) the reserved space +will be included in the "free" space. + +When disabled, the "df" type will be used to store "free" and "used" space. The +mount point or disk name (see option B) is used as type +instance in this case (again: backwards compatibility). + +When enabled, the type "df_complex" is used and three files are created. The +mount point or disk name is used as plugin instance and the type instance is +set to "free", "reserved" and "used" as appropriate. + +Enabling this option is recommended. + +=item B B|B + +Enables or disables reporting of free, reserved and used inodes. Defaults to +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 @@ -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 B|B + +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, the I will make these +statistics available. Defaults to B. + =back =head2 Plugin C @@ -2617,6 +2650,12 @@ Available options: Specifies the location of the status file. +=item B B|B + +Sets whether or not statistics about the compression used by OpenVPN should be +collected. This information is only available in I mode. Enabled by +default. + =back =head2 Plugin C @@ -3202,7 +3241,7 @@ C/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 I I @@ -3248,6 +3287,66 @@ matching values will be ignored. =back +=head2 Plugin C + +The C plugin connects to a device running I, the +Linux-based operating system for routers by I. The plugin uses +I to connect and reads information about the interfaces and +wireless connections of the device. The configuration supports querying +multiple routers: + + + + Host "router0.example.com" + User "collectd" + Password "secr3t" + CollectInterface true + + + Host "router1.example.com" + User "collectd" + Password "5ecret" + CollectInterface true + CollectRegistrationTable true + + + +As you can see above, the configuration of the I plugin consists of +one or more BRouterE> blocks. Within each block, the following +options are understood: + +=over 4 + +=item B I + +Hostname or IP-address of the router to connect to. + +=item B I + +Port name or port number used when connecting. If left unspecified, the default +will be chosen by I, currently "8728". This option expects a +string argument, even when a numeric port number is given. + +=item B I + +Use the user name I to authenticate. Defaults to "admin". + +=item B I + +Set the password used to authenticate. + +=item B B|B + +When set to B, interface statistics will be collected for all interfaces +present on the device. Defaults to B. + +=item B B|B + +When set to B, information about wireless LAN connections will be +collected. Defaults to B. + +=back + =head2 Plugin C The C plugin uses the RRDtool accelerator daemon, L, @@ -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 + +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 32Ebit 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 and I arguments: + + if ((hash_value % Total) == Match) + matches; + else + does not match; + +Please note that when you set I to two (i.Ee. 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 I I + +Divide the data into I groups and match all hosts in group I as +described above. The groups are numbered from zero, i.Ee. I must +be smaller than I. I 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. + + + + # Divide all received hosts in seven groups and accept all hosts in + # group three. + Match 3 7 + + # If matched: Return and continue. + Target "return" + + # If not matched: Return and stop. + Target "stop" + + =back =head2 Available targets diff --git a/src/configfile.c b/src/configfile.c index 79ad9f6d..b2997d64 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -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); } diff --git a/src/configfile.h b/src/configfile.h index 74d074ed..a73def21 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -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 index 00000000..7787203d --- /dev/null +++ b/src/contextswitch.c @@ -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 + **/ + +#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 */ diff --git a/src/cpu.c b/src/cpu.c index c4bd1a6d..b92b0e2f 100644 --- 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; diff --git a/src/csv.c b/src/csv.c index 78037a94..0b34687d 100644 --- 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 { diff --git a/src/df.c b/src/df.c index 194c2573..62775fd2 100644 --- 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); diff --git a/src/disk.c b/src/disk.c index 489770c7..94257fef 100644 --- a/src/disk.c +++ b/src/disk.c @@ -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 (), diff --git a/src/gmond.c b/src/gmond.c index 3357ea0b..2ffc42a5 100644 --- a/src/gmond.c +++ b/src/gmond.c @@ -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); diff --git a/src/hddtemp.c b/src/hddtemp.c index 2405d8c3..4e083753 100644 --- a/src/hddtemp.c +++ b/src/hddtemp.c @@ -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; diff --git a/src/interface.c b/src/interface.c index fad37dbe..8557cb10 100644 --- a/src/interface.c +++ b/src/interface.c @@ -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) diff --git a/src/ipmi.c b/src/ipmi.c index 441ad8fb..95b3dbf5 100644 --- a/src/ipmi.c +++ b/src/ipmi.c @@ -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 diff --git a/src/irq.c b/src/irq.c index 1aef344f..401cc6f9 100644 --- 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; diff --git a/src/libvirt.c b/src/libvirt.c index 6f9e5f12..bcbf0e6a 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -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); diff --git a/src/load.c b/src/load.c index cf5221b2..575b4ca4 100644 --- a/src/load.c +++ b/src/load.c @@ -81,7 +81,7 @@ static int load_read (void) char *fields[8]; int numfields; - + if ((loadavg = fopen ("/proc/loadavg", "r")) == NULL) { char errbuf[1024]; diff --git a/src/logfile.c b/src/logfile.c index 03af7a3f..7b96ac57 100644 --- a/src/logfile.c +++ b/src/logfile.c @@ -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 index 00000000..062a7a72 --- /dev/null +++ b/src/match_hashed.c @@ -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 + **/ + +#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 : */ diff --git a/src/memory.c b/src/memory.c index 799a80c3..e0e0cbb1 100644 --- a/src/memory.c +++ b/src/memory.c @@ -250,7 +250,7 @@ static int memory_read (void) #elif KERNEL_LINUX FILE *fh; char buffer[1024]; - + char *fields[8]; int numfields; diff --git a/src/netapp.c b/src/netapp.c index b11e9fa2..ad19c6da 100644 --- a/src/netapp.c +++ b/src/netapp.c @@ -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 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); } diff --git a/src/netlink.c b/src/netlink.c index b15768e7..49c4e990 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -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; diff --git a/src/network.c b/src/network.c index 109289e3..1b453753 100644 --- a/src/network.c +++ b/src/network.c @@ -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); diff --git a/src/nginx.c b/src/nginx.c index 53137a71..69768427 100644 --- a/src/nginx.c +++ b/src/nginx.c @@ -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); } diff --git a/src/ntpd.c b/src/ntpd.c index 9d716adc..ecc87c78 100644 --- a/src/ntpd.c +++ b/src/ntpd.c @@ -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; diff --git a/src/onewire.c b/src/onewire.c index 261457a1..cae0d63d 100644 --- a/src/onewire.c +++ b/src/onewire.c @@ -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) diff --git a/src/openvpn.c b/src/openvpn.c index 9f386f0d..1ac50830 100644 --- a/src/openvpn.c +++ b/src/openvpn.c @@ -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 @@ -19,53 +20,73 @@ * Authors: * Doug MacEachern * Florian octo Forster + * Marco Chiappero **/ #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 : */ diff --git a/src/owniptc/libiptc.c b/src/owniptc/libiptc.c index 8169f4a6..5e5fde08 100644 --- a/src/owniptc/libiptc.c +++ b/src/owniptc/libiptc.c @@ -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) { diff --git a/src/plugin.c b/src/plugin.c index 25798564..24b26986 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -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) diff --git a/src/processes.c b/src/processes.c index fd06b21b..35f99a9b 100644 --- a/src/processes.c +++ b/src/processes.c @@ -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; diff --git a/src/protocols.c b/src/protocols.c index 75e9a1c4..73fe1543 100644 --- a/src/protocols.c +++ b/src/protocols.c @@ -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 index 00000000..02c96d75 --- /dev/null +++ b/src/routeros.c @@ -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 + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#include + +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 : */ diff --git a/src/rrdcached.c b/src/rrdcached.c index 728ada45..aab8c11e 100644 --- a/src/rrdcached.c +++ b/src/rrdcached.c @@ -24,6 +24,7 @@ #include "common.h" #include "utils_rrdcreate.h" +#undef HAVE_CONFIG_H #include /* @@ -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; diff --git a/src/sensors.c b/src/sensors.c index fa461a2f..8391346b 100644 --- a/src/sensors.c +++ b/src/sensors.c @@ -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 diff --git a/src/snmp.c b/src/snmp.c index 7568cf67..00df3779 100644 --- a/src/snmp.c +++ b/src/snmp.c @@ -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++) { diff --git a/src/swap.c b/src/swap.c index 22eda1f5..6ccae563 100644 --- a/src/swap.c +++ b/src/swap.c @@ -173,7 +173,7 @@ static int swap_read (void) #if KERNEL_LINUX FILE *fh; char buffer[1024]; - + char *fields[8]; int numfields; diff --git a/src/tcpconns.c b/src/tcpconns.c index f164b53b..d68cd096 100644 --- a/src/tcpconns.c +++ b/src/tcpconns.c @@ -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; diff --git a/src/thermal.c b/src/thermal.c index fe54aee4..2b708052 100644 --- a/src/thermal.c +++ b/src/thermal.c @@ -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 diff --git a/src/types.db b/src/types.db index e67ccceb..a5872ebf 100644 --- a/src/types.db +++ b/src/types.db @@ -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 diff --git a/src/unixsock.c b/src/unixsock.c index be36abc8..0b897482 100644 --- a/src/unixsock.c +++ b/src/unixsock.c @@ -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) diff --git a/src/vmem.c b/src/vmem.c index 6775d20d..d32f1db8 100644 --- a/src/vmem.c +++ b/src/vmem.c @@ -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; diff --git a/version-gen.sh b/version-gen.sh index f2c61f54..e0114d04 100755 --- a/version-gen.sh +++ b/version-gen.sh @@ -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-//'`"