Luboš Staněk <lubek@users.sourceforge.net>
Niki W. Waibel <niki>
Sebastian Harl <tokkee>
+Rodolphe Quiedeville <rquiedeville@bearstech.com>
Amit Gupta <amit.gupta221 at gmail.com>
- Multiple servers in the apache plugin.
+ - curl_xml plugin.
Anthony Dewhurst <dewhurst at gmail.com>
- zfs_arc plugin.
+2010-01-14, Version 4.9.1
+ * Documentation: Some manpage fixes.
+ * Default config: Added sample configuration for missing plugins.
+ * apache plugin: Fix a segmentation fault in the config handling of
+ VerifyPeer / VerifyHost. Thanks to "plazmus" for his or her patch.
+ * processes plugin: Fix handling of derive data sources.
+ * rrdtool plugin: Fix a bug with random write timeouts. Due to an
+ incorrect initialization some files may be suspended basically
+ indefinitely. After flushing the files they were written regularly
+ again.
+ * routeros plugin: Use the node name for the "host" field.
+ * Monitorus.pm: Put the plugin into the "Collectd::Plugins" namespace.
+ * Perl bindings: Fix a warning that was printed when building
+ debugging output.
+
2009-12-21, Version 4.9.0
* contextswitch plugin: The new ContextSwitch plugin gathers the
number of context switches done by the CPU. Thanks to Patrik
* scale target: This target to scale (multiply) values by an arbitrary
value has been added.
+2010-04-07, Version 4.8.4
+ * Build system, various plugins: Fixes for AIX compatibility have been
+ added. Thanks to Manuel Sanmartin for his patches.
+ * Build system: Checking for "nanosleep" on old Solaris machines has
+ been fixed. Thanks to Vincent McIntyre and Sebastian Harl for
+ figuring out a way to make this work.
+ * collectd: Serialization of NANs in JSON format has been fixed.
+ Thanks to Chris Buben for pointing out the resulting syntax error.
+ * collectd: Checks whether a "sleep" returned early have been added;
+ the cases are now handled correctly. Thanks to Michael Stapelberg
+ for the patch.
+ * collectd: Continue reading files in a directory when parsing one
+ file fails.
+ * apache plugin: Collection of the number of active connections has
+ been fixed for Apache 2.*.
+ * exec plugin: Error messages have been improved. The "running" flag
+ is now cleared correctly when forking a child fails.
+ * iptables plugin: Fix a violation of aliasing rules. This resolves a
+ warning / error with new GCC versions. Thanks to Jan Engelhardt for
+ the work-around.
+ * java plugin: The Java API files are now packaged into a .jar file.
+ Thanks to Amit Gupta for his patch.
+ * network plugin: A memory leak when receiving encrypted network
+ packets has been fixed.
+ * oracle plugin: Fix checking for lost connections and reconnect in
+ this case. Thanks to Sven Trenkel for pointing out the problem.
+ * unixsock plugin: A memory leak in the "LISTVAL" command has been
+ fixed. Thanks to Peter Warasin for pointing it out.
+ * write_http plugin: Use the "any" authentication schema. This used to
+ be "digest". Thanks to Paul Sadauskas for the patch.
+
+2010-01-14, Version 4.8.3
+ * Documentation: Some manpage fixes.
+ * rrdtool plugin: Fix a bug with random write timeouts. Due to an
+ incorrect initialization some files may be suspended basically
+ indefinitely. After flushing the files they were written regularly
+ again.
+
2009-12-18, Version 4.8.2
* Build system, java plugin: Don't use “find -L” to search for Java
headers, because it's a GNU extension.
Retrieves JSON data via cURL and parses it according to user
configuration.
+ - curl_xml
+ Retrieves XML data via cURL and parses it according to user
+ configuration.
+
- dbi
Executes SQL statements on various databases and interprets the returned
data.
Memory utilization: Memory occupied by running processes, page cache,
buffer cache and free.
+ - modbus
+ Reads values from Modbus/TCP enabled devices. Supports reading values
+ from multiple "slaves" so gateway devices can be used.
+
- multimeter
Information provided by serial multimeters, such as the `Metex
M-4650CR'.
- zfs_arc
Statistics for ZFS' “Adaptive Replacement Cache” (ARC).
- * Output can be written or send to various destinations by the following
+ * Output can be written or sent to various destinations by the following
plugins:
- csv
network plugins, makes sure your resources are used efficiently. Also,
since collectd is programmed multithreaded it benefits from hyperthreading
and multicore processors and makes sure that the daemon isn't idle if only
- one plugins waits for an IO-operation to complete.
+ one plugin waits for an IO-operation to complete.
* Once set up, hardly any maintenance is necessary. Setup is kept as easy
as possible and the default values should be okay for most users.
Used by the `memcachec' plugin to connect to a memcache daemon.
<http://tangent.org/552/libmemcached.html>
+ * libmodbus (optional)
+ Used by the `modbus' plugin to communicate with Modbus/TCP devices.
+ <https://launchpad.net/libmodbus>
+
* libmysqlclient (optional)
Unsurprisingly used by the `mysql' plugin.
<http://dev.mysql.com/>
- * libnatapp (optional)
+ * libnetapp (optional)
Required for the “netapp” plugin.
This library is part of the “Manage ONTAP SDK” published by NetApp.
src/battery.c: commend not working code.
Wishlist:
-* Update the RPM specfile to
- - build `collectd-apache'
- - be free of syntax errors.
* Port nfs module to solaris
* Port tape module to Linux
* Port the apple_sensors plugin to Linux/PPC.
java-build-stamp: org/collectd/api/*.java
$(JAVAC) -d "." "$(srcdir)/org/collectd/api"/*.java
$(JAVAC) -d "." "$(srcdir)/org/collectd/java"/*.java
+ mkdir -p .libs
+ $(JAR) cf .libs/collectd-api.jar "org/collectd/api"/*.class
+ $(JAR) cf .libs/generic-jmx.jar "org/collectd/java"/*.class
touch "$@"
all-local: java-build-stamp
install-exec-local: java-build-stamp
- mkdir -p "$(DESTDIR)$(pkgdatadir)/java/org/collectd/api"
- $(INSTALL) -m 644 "org/collectd/api"/*.class \
- "$(DESTDIR)$(pkgdatadir)/java/org/collectd/api/"
- mkdir -p "$(DESTDIR)$(pkgdatadir)/java/org/collectd/java"
- $(INSTALL) -m 644 "org/collectd/java"/*.class \
- "$(DESTDIR)$(pkgdatadir)/java/org/collectd/java/"
+ mkdir -p "$(DESTDIR)$(pkgdatadir)/java"
+ $(INSTALL) -m 644 .libs/collectd-api.jar \
+ "$(DESTDIR)$(pkgdatadir)/java"
+ $(INSTALL) -m 644 .libs/generic-jmx.jar \
+ "$(DESTDIR)$(pkgdatadir)/java"
clean-local:
rm -f "org/collectd/api"/*.class
rm -f "org/collectd/java"/*.class
+ rm -f .libs
rm -f "java-build-stamp"
my %cf_callbacks : shared = ();
my %types = (
+ TYPE_CONFIG, "config",
TYPE_INIT, "init",
TYPE_READ, "read",
TYPE_WRITE, "write",
# Checks for library functions.
#
AC_PROG_GCC_TRADITIONAL
-AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf)
+AC_CHECK_FUNCS(gettimeofday select strdup strtol getaddrinfo getnameinfo strchr memcpy strstr strcmp strncmp strncpy strlen strncasecmp strcasecmp openlog closelog sysconf if_indextoname)
AC_FUNC_STRERROR_R
AM_CONDITIONAL(BUILD_WITH_LIBSOCKET, test "x$socket_needs_socket" = "xyes")
nanosleep_needs_rt="no"
-AC_CHECK_FUNCS(nanosleep, [], AC_CHECK_LIB(rt, nanosleep, [nanosleep_needs_rt="yes"], AC_MSG_ERROR(cannot find nanosleep)))
+nanosleep_needs_posix4="no"
+AC_CHECK_FUNCS(nanosleep,
+ [],
+ AC_CHECK_LIB(rt, nanosleep,
+ [nanosleep_needs_rt="yes"],
+ AC_CHECK_LIB(posix4, nanosleep,
+ [nanosleep_needs_posix4="yes"],
+ AC_MSG_ERROR(cannot find nanosleep))))
AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes")
+AM_CONDITIONAL(BUILD_WITH_LIBPOSIX4, test "x$nanosleep_needs_posix4" = "xyes")
AC_CHECK_FUNCS(sysctl, [have_sysctl="yes"], [have_sysctl="no"])
AC_CHECK_FUNCS(sysctlbyname, [have_sysctlbyname="yes"], [have_sysctlbyname="no"])
[Define if the function getmntent exists. It's the version from libgen.])
fi
+# Check for htonll
+AC_MSG_CHECKING([if have htonll defined])
+
+ have_htonll="no"
+ AC_RUN_IFELSE([
+ AC_LANG_PROGRAM([
+#include <sys/types.h>
+#include <netinet/in.h>
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+ ], [
+ return htonll(0);
+ ])
+ ], [
+ have_htonll="yes"
+ AC_DEFINE(HAVE_HTONLL, 1, [Define if the function htonll exists.])
+ ])
+
+AC_MSG_RESULT([$have_htonll])
+
# Check for structures
AC_CHECK_MEMBERS([struct if_data.ifi_ibytes, struct if_data.ifi_opackets, struct if_data.ifi_ierrors],
[AC_DEFINE(HAVE_STRUCT_IF_DATA, 1, [Define if struct if_data exists and is usable.])],
#include <linux/netdevice.h>
])
+AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [],
+ [],
+ [
+ #include <netinet/in.h>
+ #include <net/if.h>
+ ])
+
AC_CHECK_MEMBERS([struct kinfo_proc.ki_pid, struct kinfo_proc.ki_rssize, struct kinfo_proc.ki_rusage],
[
AC_DEFINE(HAVE_STRUCT_KINFO_PROC_FREEBSD, 1,
with_java_cflags=""
with_java_libs=""
JAVAC="$JAVAC"
+JAR="$JAR"
AC_ARG_WITH(java, [AS_HELP_STRING([--with-java@<:@=PREFIX@:>@], [Path to Java home.])],
[
if test "x$withval" = "xno"
AC_MSG_RESULT([not found])
fi
fi
+ if test "x$JAR" = "x"
+ then
+ AC_MSG_CHECKING([for jar])
+ TMPDIR=`find "$with_java_home" -name jar -type f | head -n 1`
+ if test "x$TMPDIR" != "x"
+ then
+ JAR="$TMPDIR"
+ AC_MSG_RESULT([$JAR])
+ else
+ AC_MSG_RESULT([not found])
+ fi
+ fi
else if test "x$with_java_home" != "x"
then
AC_MSG_WARN([JAVA_HOME: No such directory: $with_java_home])
then
with_java="no (javac not found)"
fi
+if test "x$JAR" = "x"
+then
+ with_jar_path="$PATH"
+ if test "x$with_java_home" != "x"
+ then
+ with_jar_path="$with_java_home:$with_jar_path"
+ if test -d "$with_java_home/bin"
+ then
+ with_jar_path="$with_java_home/bin:$with_jar_path"
+ fi
+ fi
+
+ AC_PATH_PROG(JAR, jar, [], "$with_jar_path")
+fi
+if test "x$JAR" = "x"
+then
+ with_java="no (jar not found)"
+fi
SAVE_CPPFLAGS="$CPPFLAGS"
SAVE_CFLAGS="$CFLAGS"
AM_CONDITIONAL(BUILD_WITH_LIBMEMCACHED, test "x$with_libmemcached" = "xyes")
# }}}
+# --with-libmodbus {{{
+with_libmodbus_config=""
+with_libmodbus_cflags=""
+with_libmodbus_libs=""
+AC_ARG_WITH(libmodbus, [AS_HELP_STRING([--with-libmodbus@<:@=PREFIX@:>@], [Path to the modbus library.])],
+[
+ if test "x$withval" = "xno"
+ then
+ with_libmodbus="no"
+ else if test "x$withval" = "xyes"
+ then
+ with_libmodbus="use_pkgconfig"
+ else if test -d "$with_libmodbus/lib"
+ then
+ AC_MSG_NOTICE([Not checking for libmodbus: Manually configured])
+ with_libmodbus_cflags="-I$withval/include"
+ with_libmodbus_libs="-L$withval/lib -lmodbus"
+ with_libmodbus="yes"
+ fi; fi; fi
+],
+[with_libmodbus="use_pkgconfig"])
+
+# configure using pkg-config
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+ if test "x$PKG_CONFIG" = "x"
+ then
+ with_libmodbus="no (Don't have pkg-config)"
+ fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+ AC_MSG_NOTICE([Checking for modbus using $PKG_CONFIG])
+ $PKG_CONFIG --exists 'modbus' 2>/dev/null
+ if test $? -ne 0
+ then
+ with_libmodbus="no (pkg-config doesn't know library)"
+ fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+ with_libmodbus_cflags="`$PKG_CONFIG --cflags 'modbus'`"
+ if test $? -ne 0
+ then
+ with_libmodbus="no ($PKG_CONFIG failed)"
+ fi
+ with_libmodbus_libs="`$PKG_CONFIG --libs 'modbus'`"
+ if test $? -ne 0
+ then
+ with_libmodbus="no ($PKG_CONFIG failed)"
+ fi
+fi
+if test "x$with_libmodbus" = "xuse_pkgconfig"
+then
+ with_libmodbus="yes"
+fi
+
+# with_libmodbus_cflags and with_libmodbus_libs are set up now, let's do
+# the actual checks.
+if test "x$with_libmodbus" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
+
+ AC_CHECK_HEADERS(modbus/modbus.h, [], [with_libmodbus="no (modbus/modbus.h not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libmodbus" = "xyes"
+then
+ SAVE_CPPFLAGS="$CPPFLAGS"
+ SAVE_LDFLAGS="$LDFLAGS"
+
+ CPPFLAGS="$CPPFLAGS $with_libmodbus_cflags"
+ LDFLAGS="$LDFLAGS $with_libmodbus_libs"
+
+ AC_CHECK_LIB(modbus, modbus_init_tcp,
+ [with_libmodbus="yes"],
+ [with_libmodbus="no (symbol modbus_init_tcp not found)"])
+
+ CPPFLAGS="$SAVE_CPPFLAGS"
+ LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libmodbus" = "xyes"
+then
+ BUILD_WITH_LIBMODBUS_CFLAGS="$with_libmodbus_cflags"
+ BUILD_WITH_LIBMODBUS_LIBS="$with_libmodbus_libs"
+ AC_SUBST(BUILD_WITH_LIBMODBUS_CFLAGS)
+ AC_SUBST(BUILD_WITH_LIBMODBUS_LIBS)
+fi
+# }}}
+
# --with-libmysql {{{
with_mysql_config="mysql_config"
with_mysql_cflags=""
enable_plugin="yes"
force="yes"
else
- enable_plugin="no"
+ enable_plugin="no (disabled on command line)"
fi; fi
],
[
plugin_cpu="no"
plugin_cpufreq="no"
plugin_curl_json="no"
+plugin_curl_xml="no"
plugin_df="no"
plugin_disk="no"
plugin_entropy="no"
plugin_curl_json="yes"
fi
+if test "x$with_libcurl" = "xyes" && test "x$with_libxml2" = "xyes"
+then
+ plugin_curl_xml="yes"
+fi
+
if test "x$have_processor_info" = "xyes"
then
plugin_cpu="yes"
AC_PLUGIN([csv], [yes], [CSV output plugin])
AC_PLUGIN([curl], [$with_libcurl], [CURL generic web statistics])
AC_PLUGIN([curl_json], [$plugin_curl_json], [CouchDB statistics])
+AC_PLUGIN([curl_xml], [$plugin_curl_xml], [CURL generic xml statistics])
AC_PLUGIN([dbi], [$with_libdbi], [General database statistics])
AC_PLUGIN([df], [$plugin_df], [Filesystem usage statistics])
AC_PLUGIN([disk], [$plugin_disk], [Disk usage statistics])
AC_PLUGIN([memcachec], [$with_libmemcached], [memcachec statistics])
AC_PLUGIN([memcached], [yes], [memcached statistics])
AC_PLUGIN([memory], [$plugin_memory], [Memory usage])
+AC_PLUGIN([modbus], [$with_libmodbus], [Modbus plugin])
AC_PLUGIN([multimeter], [$plugin_multimeter], [Read multimeter values])
AC_PLUGIN([mysql], [$with_libmysql], [MySQL statistics])
AC_PLUGIN([netapp], [$with_libnetapp], [NetApp plugin])
libkstat . . . . . . $with_kstat
libkvm . . . . . . . $with_libkvm
libmemcached . . . . $with_libmemcached
+ libmodbus . . . . . . $with_libmodbus
libmysql . . . . . . $with_libmysql
libnetapp . . . . . . $with_libnetapp
libnetlink . . . . . $with_libnetlink
csv . . . . . . . . . $enable_csv
curl . . . . . . . . $enable_curl
curl_json . . . . . . $enable_curl_json
+ curl_xml . . . . . . $enable_curl_xml
dbi . . . . . . . . . $enable_dbi
df . . . . . . . . . $enable_df
disk . . . . . . . . $enable_disk
memcachec . . . . . . $enable_memcachec
memcached . . . . . . $enable_memcached
memory . . . . . . . $enable_memory
+ modbus . . . . . . . $enable_modbus
multimeter . . . . . $enable_multimeter
mysql . . . . . . . . $enable_mysql
netapp . . . . . . . $enable_netapp
our $Config = "/etc/collection.conf";
our @DataDirs = ();
+our @DontShowTypes = ();
our $LibDir;
our $ValidTimespan =
$value =~ s#/*$##;
$LibDir = $value;
}
+ elsif ($key eq 'dontshowtype')
+ {
+ push (@DontShowTypes, $value);
+ }
else
{
print STDERR "Unknown key: $key\n";
my $name = "$_";
$name =~ s/\.rrd$//i;
my ($type, $instance) = split (m/-/, $name, 2);
+ if (grep { $_ eq $type } @DontShowTypes) { next; }
$types{$type} = [] if (!$types{$type});
push (@{$types{$type}}, $instance) if (defined ($instance));
}
'GPRINT:avg:LAST:%5.1lf%s Last',
'GPRINT:avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
],
+ apache_connections => ['DEF:min={file}:count:MIN',
+ 'DEF:avg={file}:count:AVERAGE',
+ 'DEF:max={file}:count:MAX',
+ "AREA:max#$HalfBlue",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullBlue:Connections",
+ 'GPRINT:min:MIN:%6.2lf Min,',
+ 'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+ 'GPRINT:max:MAX:%6.2lf Max,',
+ 'GPRINT:avg:LAST:%6.2lf Last'
+ ],
+ apache_idle_workers => ['DEF:min={file}:count:MIN',
+ 'DEF:avg={file}:count:AVERAGE',
+ 'DEF:max={file}:count:MAX',
+ "AREA:max#$HalfBlue",
+ "AREA:min#$Canvas",
+ "LINE1:avg#$FullBlue:Idle Workers",
+ 'GPRINT:min:MIN:%6.2lf Min,',
+ 'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+ 'GPRINT:max:MAX:%6.2lf Max,',
+ 'GPRINT:avg:LAST:%6.2lf Last'
+ ],
apache_requests => ['DEF:min={file}:count:MIN',
'DEF:avg={file}:count:AVERAGE',
'DEF:max={file}:count:MAX',
#!/usr/bin/perl
+#
+# collectd - contrib/exec-munin.px
+# Copyright (C) 2007,2008 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 octo Forster <octo at verplant.org>
+#
+
use strict;
use warnings;
=back
+=back
+
=cut
sub handle_config_addtype
if ($perfdata)
{
- push (@serviceperfdata, split (' ', $perfdata));
+ push (@serviceperfdata, split (' ', $perfdata));
}
$state = 1;
if ($perfdata)
{
- push (@serviceperfdata, split (' ', $perfdata));
- $state = 2;
+ push (@serviceperfdata, split (' ', $perfdata));
+ $state = 2;
}
}
else # ($state == 2)
for (@serviceperfdata)
{
handle_performance_data ($host, 'nagios', $pinst, $script->{'type'},
- $time, $_);
+ $time, $_);
}
}
} # execute_script
+
+%define with_java %(test -z "$JAVA_HOME" ; echo $?)
+
Summary: Statistics collection daemon for filling RRD files.
Name: collectd
-Version: 4.3.1
-Release: 0.centos5
+Version: 4.9.0
+Release: 1%{?dist}
Source: http://collectd.org/files/%{name}-%{version}.tar.gz
License: GPL
Group: System Environment/Daemons
BuildRoot: %{_tmppath}/%{name}-%{version}-root
-BuildPrereq: lm_sensors-devel, mysql-devel, rrdtool-devel, curl-devel, libpcap-devel, net-snmp-devel, libstatgrab-devel, mysql-devel, libxml2-devel, libiptcdata-devel
+BuildPrereq: lm_sensors-devel, rrdtool-devel, libpcap-devel, net-snmp-devel, libstatgrab-devel, libxml2-devel, libiptcdata-devel
+# libcurl deps
+BuildPrereq: curl-devel,libidn-devel,openssl-devel
Requires: rrdtool, perl-Regexp-Common, libstatgrab
Packager: RightScale <support@rightscale.com>
Vendor: collectd development team <collectd@verplant.org>
system. Also, the statistics are very fine grained since the files are updated
every 10 seconds.
+
%package apache
Summary: apache-plugin for collectd.
Group: System Environment/Daemons
%description snmp
This plugin for collectd allows querying of network equipment using SNMP.
+%if %with_java
+%package java
+Summary: java-module for collectd.
+Group: System Environment/Daemons
+Requires: collectd = %{version}, jdk >= 1.6
+BuildPrereq: jdk >= 1.6
+%description java
+This plugin for collectd allows plugins to be written in Java and executed
+in an embedded JVM.
+%endif
+
%prep
rm -rf $RPM_BUILD_ROOT
%setup
%build
-./configure CFLAGS=-"DLT_LAZY_OR_NOW='RTLD_LAZY|RTLD_GLOBAL'" --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir} --enable-apache --enable-email --enable-mysql --enable-dns
+./configure CFLAGS=-"DLT_LAZY_OR_NOW='RTLD_LAZY|RTLD_GLOBAL'" --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir} \
+ %{!?with_java:"--with-java=$JAVA_HOME --enable-java"} \
+ --disable-battery
make
%install
mkdir -p $RPM_BUILD_ROOT/var/lib/collectd
### Clean up docs
find contrib/ -type f -exec %{__chmod} a-x {} \;
+
###Modify Config for Redhat Based Distros
-cp contrib/redhat/collectd.conf $RPM_BUILD_ROOT/etc/collectd.conf
sed -i 's:#BaseDir "/usr/var/lib/collectd":BaseDir "/var/lib/collectd":' $RPM_BUILD_ROOT/etc/collectd.conf
sed -i 's:#PIDFile "/usr/var/run/collectd.pid":PIDFile "/var/run/collectd.pid":' $RPM_BUILD_ROOT/etc/collectd.conf
-sed -i 's:#PluginDir "/usr/lib/collectd":PluginDir "/usr/lib/collectd":' $RPM_BUILD_ROOT/etc/collectd.conf
+sed -i 's:#PluginDir "/usr/lib/collectd":PluginDir "%{_libdir}/collectd":' $RPM_BUILD_ROOT/etc/collectd.conf
sed -i 's:#TypesDB "/usr/share/collectd/types.db":TypesDB "/usr/share/collectd/types.db":' $RPM_BUILD_ROOT/etc/collectd.conf
-sed -i 's:#Interval 10:Interval 10:' $RPM_BUILD_ROOT/etc/collectd.conf
+sed -i 's:#Interval 10:Interval 30:' $RPM_BUILD_ROOT/etc/collectd.conf
sed -i 's:#ReadThreads 5:ReadThreads 5:' $RPM_BUILD_ROOT/etc/collectd.conf
+###Include broken out config directory
+echo -e '\nInclude "/etc/collectd.d"' >> $RPM_BUILD_ROOT/etc/collectd.conf
+
##Move config contribs
cp contrib/redhat/apache.conf $RPM_BUILD_ROOT/etc/collectd.d/apache.conf
cp contrib/redhat/email.conf $RPM_BUILD_ROOT/etc/collectd.d/email.conf
%files
%defattr(-,root,root)
%doc AUTHORS COPYING ChangeLog INSTALL NEWS README contrib/
-%attr(0644,root,root) %config(noreplace) /etc/collectd.conf
+%config %attr(0644,root,root) /etc/collectd.conf
%attr(0755,root,root) /etc/rc.d/init.d/collectd
%attr(0755,root,root) /var/www/cgi-bin/collection.cgi
%attr(0755,root,root) %{_sbindir}/collectd
%attr(0644,root,root) %{_mandir}/man5/*
%dir /etc/collectd.d
-%attr(0644,root,root) %{_libdir}/%{name}/apcups.so*
-%attr(0644,root,root) %{_libdir}/%{name}/apcups.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/apple_sensors.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/battery.so*
-%attr(0644,root,root) %{_libdir}/%{name}/battery.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/conntrack.so*
-%attr(0644,root,root) %{_libdir}/%{name}/conntrack.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/cpufreq.so*
-%attr(0644,root,root) %{_libdir}/%{name}/cpufreq.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/cpu.so*
-%attr(0644,root,root) %{_libdir}/%{name}/cpu.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/csv.so*
-%attr(0644,root,root) %{_libdir}/%{name}/csv.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/df.so*
-%attr(0644,root,root) %{_libdir}/%{name}/df.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/disk.so*
-%attr(0644,root,root) %{_libdir}/%{name}/disk.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/dns.so*
-%attr(0644,root,root) %{_libdir}/%{name}/dns.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/entropy.so*
-%attr(0644,root,root) %{_libdir}/%{name}/entropy.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/exec.so*
-%attr(0644,root,root) %{_libdir}/%{name}/exec.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/hddtemp.so*
-%attr(0644,root,root) %{_libdir}/%{name}/hddtemp.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/interface.so*
-%attr(0644,root,root) %{_libdir}/%{name}/interface.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/iptables.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/iptables.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/irq.so*
-%attr(0644,root,root) %{_libdir}/%{name}/irq.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/load.so*
-%attr(0644,root,root) %{_libdir}/%{name}/load.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/logfile.so*
-%attr(0644,root,root) %{_libdir}/%{name}/logfile.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/mbmon.so
-%attr(0644,root,root) %{_libdir}/%{name}/mbmon.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/memcached.so*
-%attr(0644,root,root) %{_libdir}/%{name}/memcached.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/memory.so*
-%attr(0644,root,root) %{_libdir}/%{name}/memory.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/multimeter.so*
-%attr(0644,root,root) %{_libdir}/%{name}/multimeter.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/network.so*
-%attr(0644,root,root) %{_libdir}/%{name}/network.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/nfs.so*
-%attr(0644,root,root) %{_libdir}/%{name}/nfs.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/ntpd.so*
-%attr(0644,root,root) %{_libdir}/%{name}/ntpd.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/nut.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/nut.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/perl.so*
-%attr(0644,root,root) %{_libdir}/%{name}/perl.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/ping.so*
-%attr(0644,root,root) %{_libdir}/%{name}/ping.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/processes.so*
-%attr(0644,root,root) %{_libdir}/%{name}/processes.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/rrdtool.so*
-%attr(0644,root,root) %{_libdir}/%{name}/rrdtool.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/serial.so*
-%attr(0644,root,root) %{_libdir}/%{name}/serial.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/swap.so*
-%attr(0644,root,root) %{_libdir}/%{name}/swap.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/syslog.so*
-%attr(0644,root,root) %{_libdir}/%{name}/syslog.la
-
-#%attr(0644,root,root) %{_libdir}/%{name}/tape.so*
-#%attr(0644,root,root) %{_libdir}/%{name}/tape.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/tcpconns.so*
-%attr(0644,root,root) %{_libdir}/%{name}/tcpconns.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/unixsock.so*
-%attr(0644,root,root) %{_libdir}/%{name}/unixsock.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/users.so*
-%attr(0644,root,root) %{_libdir}/%{name}/users.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/uuid.so*
-%attr(0644,root,root) %{_libdir}/%{name}/uuid.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/vserver.so*
-%attr(0644,root,root) %{_libdir}/%{name}/vserver.la
-
-%attr(0644,root,root) %{_libdir}/%{name}/wireless.so*
-%attr(0644,root,root) %{_libdir}/%{name}/wireless.la
+# client
+%attr(0644,root,root) /usr/include/collectd/client.h
+%attr(0644,root,root) /usr/include/collectd/lcc_features.h
+
+%attr(0644,root,root) %{_libdir}/libcollectdclient.*
+%attr(0644,root,root) %{_libdir}/pkgconfig/libcollectdclient.pc
+
+# macro to grab binaries for a plugin, given a name
+%define plugin_macro() \
+%attr(0644,root,root) %{_libdir}/%{name}/%1.a \
+%attr(0644,root,root) %{_libdir}/%{name}/%1.so* \
+%attr(0644,root,root) %{_libdir}/%{name}/%1.la
+
+%plugin_macro apcups
+%plugin_macro ascent
+%plugin_macro bind
+%plugin_macro conntrack
+%plugin_macro contextswitch
+%plugin_macro cpufreq
+%plugin_macro cpu
+%plugin_macro csv
+%plugin_macro curl
+%plugin_macro df
+%plugin_macro disk
+%plugin_macro dns
+%plugin_macro entropy
+%plugin_macro email
+%plugin_macro exec
+%plugin_macro filecount
+%plugin_macro fscache
+%plugin_macro hddtemp
+%plugin_macro interface
+%plugin_macro iptables
+%plugin_macro irq
+%plugin_macro load
+%plugin_macro logfile
+%plugin_macro madwifi
+
+%plugin_macro match_empty_counter
+%plugin_macro match_hashed
+%plugin_macro match_regex
+%plugin_macro match_timediff
+%plugin_macro match_value
+
+%plugin_macro mbmon
+%plugin_macro memcached
+%plugin_macro memory
+%plugin_macro multimeter
+%plugin_macro network
+%plugin_macro nfs
+%plugin_macro ntpd
+%plugin_macro openvpn
+%plugin_macro olsrd
+%plugin_macro perl
+%plugin_macro powerdns
+%plugin_macro processes
+%plugin_macro protocols
+%plugin_macro python
+%plugin_macro rrdtool
+%plugin_macro serial
+%plugin_macro sensors
+%plugin_macro swap
+%plugin_macro syslog
+%plugin_macro table
+%plugin_macro tail
+
+%plugin_macro target_notification
+%plugin_macro target_replace
+%plugin_macro target_scale
+%plugin_macro target_set
+
+%plugin_macro tcpconns
+%plugin_macro teamspeak2
+%plugin_macro ted
+%plugin_macro thermal
+%plugin_macro unixsock
+%plugin_macro uptime
+%plugin_macro users
+%plugin_macro uuid
+%plugin_macro vmem
+%plugin_macro vserver
+%plugin_macro wireless
+%plugin_macro write_http
%attr(0644,root,root) %{_datadir}/%{name}/types.db
%exclude %{_libdir}/perl5/5.8.8/%{_arch}-linux-thread-multi/perllocal.pod
-%attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/Collectd.pm
-%attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/Collectd/Unixsock.pm
%attr(0644,root,root) %{_libdir}/perl5/site_perl/5.8.8/%{_arch}-linux-thread-multi/auto/Collectd/.packlist
+%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd.pm
+%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Unixsock.pm
+%attr(0644,root,root) /usr/lib/perl5/site_perl/5.8.8/Collectd/Plugins/OpenVZ.pm
%attr(0644,root,root) /usr/share/man/man3/Collectd::Unixsock.3pm.gz
+%exclude /usr/share/collectd/postgresql_default.conf
+
%dir /var/lib/collectd
+%if %with_java
+%files java
+%attr(0644,root,root) /usr/share/%{name}/java/org/collectd/api/*.class
+%attr(0644,root,root) /usr/share/%{name}/java/org/collectd/java/*.class
+%plugin_macro java
+%endif
+
%files apache
-%attr(0644,root,root) %{_libdir}/%{name}/apache.so*
-%attr(0644,root,root) %{_libdir}/%{name}/apache.la
-%attr(0644,root,root) /etc/collectd.d/apache.conf
+%config %attr(0644,root,root) /etc/collectd.d/apache.conf
+%plugin_macro apache
%files email
%attr(0644,root,root) %{_libdir}/%{name}/email.so*
%attr(0644,root,root) %{_libdir}/%{name}/email.la
-%attr(0644,root,root) /etc/collectd.d/email.conf
+%config %attr(0644,root,root) /etc/collectd.d/email.conf
%files mysql
-%attr(0644,root,root) %{_libdir}/%{name}/mysql.so*
-%attr(0644,root,root) %{_libdir}/%{name}/mysql.la
-%attr(0644,root,root) /etc/collectd.d/mysql.conf
+%config %attr(0644,root,root) /etc/collectd.d/mysql.conf
+%plugin_macro mysql
%files nginx
-%attr(0644,root,root) %{_libdir}/%{name}/nginx.so*
-%attr(0644,root,root) %{_libdir}/%{name}/nginx.la
-%attr(0644,root,root) /etc/collectd.d/nginx.conf
+%config %attr(0644,root,root) /etc/collectd.d/nginx.conf
+%plugin_macro nginx
%files sensors
%attr(0644,root,root) %{_libdir}/%{name}/sensors.so*
%attr(0644,root,root) %{_libdir}/%{name}/sensors.la
-%attr(0644,root,root) /etc/collectd.d/sensors.conf
+%config %attr(0644,root,root) /etc/collectd.d/sensors.conf
%files snmp
-%attr(0644,root,root) %{_libdir}/%{name}/snmp.so*
-%attr(0644,root,root) %{_libdir}/%{name}/snmp.la
%attr(0644,root,root) /etc/collectd.d/snmp.conf
+%plugin_macro snmp
%changelog
+* Tue Jan 04 2010 Rackspace <stu.hood@rackspace.com> 4.9.0
+- New upstream version
+- Changes to support 4.9.0
+- Added support for Java/GenericJMX plugin
+
* Mon Mar 17 2008 RightScale <support@rightscale.com> 4.3.1
- New upstream version
- Changes to support 4.3.1
#!/usr/bin/perl
#
# collectd - snmp-probe-host.px
-# Copyright (C) 2008 Florian octo Forster
+# Copyright (C) 2008,2009 Florian octo Forster
+# Copyright (C) 2009 noris network AG
#
# 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
use Getopt::Long ('GetOptions');
use Socket6;
+our %ExcludeOptions =
+(
+ 'IF-MIB64' => qr/^\.?1\.3\.6\.1\.2\.1\.31/,
+ 'IF-MIB32' => qr/^\.?1\.3\.6\.1\.2\.1\.2/
+);
+
sub get_config
{
my %conf;
{
my $sess = shift;
my $conf = shift;
+ my $excludes = @_ ? shift : [];
my @oids;
my $cmd = 'GET';
my $vl;
$oid_orig = $tmp;
}
+ for (@$excludes)
+ {
+ if ($oid_orig =~ $_)
+ {
+ return;
+ }
+ }
+
$vb = SNMP::Varbind->new ([$oid_orig]);
if ($cmd eq 'GET')
my $host = shift;
my $community = shift;
my $data = shift;
+ my $excludes = @_ ? shift : [];
my $version = 2;
my @valid_data = ();
my $begin;
for (keys %$data)
{
my $name = $_;
- if (probe_one ($sess, $data->{$name}))
+ if (probe_one ($sess, $data->{$name}, $excludes))
{
push (@valid_data, $name);
}
-C | --config Path to config file holding the SNMP data blocks.
-c | --community SNMP community to use. Default: `public'.
-h | --help Print this information and exit.
+ -x | --exclude Exclude a specific MIB. Call with "help" for more
+ information.
USAGE
exit (1);
}
+sub exit_usage_exclude
+{
+ print "Available exclude MIBs:\n\n";
+ for (sort (keys %ExcludeOptions))
+ {
+ print " $_\n";
+ }
+ print "\n";
+ exit (1);
+}
+
=head1 NAME
snmp-probe-host.px - Find out what information an SNMP device provides.
my $community = 'public';
my $conf;
my $working_data;
+my @excludes = ();
=head1 OPTIONS
SNMP community to use. Should be pretty straight forward.
+=item B<--exclude> I<MIB>
+
+This option can be used to exclude specific data from being enabled in the
+generated config. Currently the following MIBs are understood:
+
+=over 4
+
+=item B<IF-MIB>
+
+Exclude interface information, such as I<ifOctets> and I<ifPackets>.
+
+=back
+
=back
=cut
GetOptions ('H|host|hostname=s' => \$host,
'C|conf|config=s' => \$file,
'c|community=s' => \$community,
+ 'x|exclude=s' => \@excludes,
'h|help' => \&exit_usage) or die;
if (!$host)
exit (1);
}
+if (@excludes)
+{
+ my $tmp = join (',', @excludes);
+ my @tmp = split (/\s*,\s*/, $tmp);
+
+ @excludes = ();
+ for (@tmp)
+ {
+ my $mib = uc ($_);
+ if ($mib eq 'HELP')
+ {
+ exit_usage_exclude ();
+ }
+ elsif (!exists ($ExcludeOptions{$mib}))
+ {
+ print STDERR "No such MIB: $mib\n";
+ exit_usage_exclude ();
+ }
+ push (@excludes, $ExcludeOptions{$mib});
+ }
+}
+
$conf = get_config ($file) or die ("Cannot read config");
if (!defined ($conf->{'plugin'})
exit (1);
}
-probe_all ($host, $community, $conf->{'plugin'}{'snmp'}{'data'});
+probe_all ($host, $community, $conf->{'plugin'}{'snmp'}{'data'}, \@excludes);
exit (0);
+++ /dev/null
-collectd on Debian
-==================
-
-General notes:
---------------
-
-- This package is split up into several packages to prevent you from having to
- install dependencies (or recommended packages) that you don't actually need.
- Any plugin that has dependencies other than libc gets its own package.
-
-Configuring collectd:
----------------------
-
-- collectd uses a similar configuration layout as openvpn does. That is to
- say that one daemon process is started for each configuration file found in
- /etc/collectd/.
-
-- See collectd.conf(5) for details about configuring collectd.
-
-Building your own plugins:
---------------------------
-
-- If you want to contribute plugins to the official distribution you should
- read http://collectd.org/dev-info.shtml.
-
-- If you want to build plugins for your personal use only simply install the
- collectd-dev package and use /usr/share/doc/collectd-dev/examples/myplugin.c
- as a starting point (Note: This is already a working example, though it does
- not collect any useful data).
-
- The resulting file can be compiled as follows:
-
- gcc -shared -o myplugin.so myplugin.c
-
- Copy myplugin.so to /usr/lib/collectd and add the following line to your
- collectd config file:
-
- LoadPlugin myplugin
-
- Restart collectd and you're done.
-
+++ /dev/null
-collectd (3.11.5-0octo1) unstable; urgency=low
-
- * New upstream release.
-
- -- Florian Forster <octo@leeloo.home.verplant.org> Tue, 29 May 2007 22:50:59 +0200
-
-collectd (3.11.4-0octo1) unstable; urgency=low
-
- * New upstream release.
-
- -- Florian Forster <octo@leeloo.home.verplant.org> Tue, 10 Apr 2007 17:18:11 +0200
-
-collectd (3.11.3-0octo1) unstable; urgency=low
-
- * New upstream release.
-
- -- Florian octo Forster <octo@alice.lan.home.verplant.org> Fri, 30 Mar 2007 22:08:40 +0200
-
-collectd (3.11.0-0octo1) unstable; urgency=low
-
- * New upstream release.
- * The package collectd-mbmon has been added.
-
- -- Florian octo Forster <octo@alice.lan.home.verplant.org> Fri, 22 Dec 2006 15:10:37 +0100
-
-collectd (3.10.4-0octo1) unstable; urgency=low
-
- * New upstream release.
-
- -- Florian Forster <octo@leeloo.home.verplant.org> Thu, 21 Dec 2006 20:36:37 +0100
-
-collectd (3.10.3-0octo1) unstable; urgency=low
-
- * New upstream release.
-
- -- Florian Forster <octo@leeloo.home.verplant.org> Sat, 4 Nov 2006 13:46:49 +0100
-
-collectd (3.10.1-4) unstable; urgency=low
-
- * Changed collectd-dbg's section and priority to "devel" and "extra"
- respectively.
- * Set init start sequence code to 95 to be sure to start after any daemons
- that data is collected from.
-
- -- Sebastian Harl <sh@tokkee.org> Thu, 5 Oct 2006 10:25:07 +0000
-
-collectd (3.10.1-3) unstable; urgency=low
-
- * Added --oknodo to start-stop-daemon in the init script (Closes: #379703).
-
- -- Sebastian Harl <sh@tokkee.org> Tue, 25 Jul 2006 18:34:55 +0200
-
-collectd (3.10.1-2) unstable; urgency=low
-
- * Added collectd-dbg package.
-
- -- Sebastian Harl <sh@tokkee.org> Sun, 23 Jul 2006 23:39:42 +0200
-
-collectd (3.10.1-1) unstable; urgency=low
-
- * New upstream release.
- * Dynamically link against external liboping.
- - New binary package collectd-ping.
- - Added collectd-ping to suggested packages.
- * Moved config file from /usr/share/doc/collectd/examples/ to
- /etc/collectd/.
-
- -- Sebastian Harl <sh@tokkee.org> Sat, 22 Jul 2006 21:43:37 +0200
-
-collectd (3.10.0-1) unstable; urgency=low
-
- * New upstream release.
- New plugins:
- - APC UPS's charge, load, input/output/battery voltage, etc.: apcups
- - NTP daemon's local clock drift, offset to peers, etc.: ntpd
- * Upstream no longer provides a debian/ directory. Thus no repackaging is
- required any longer.
- * Not using getifaddrs() is now the default in upstream. getifaddrs.dpatch
- no longer needed.
- * Added collectd-hddtemp as a suggestion to the collectd package.
-
- -- Sebastian Harl <sh@tokkee.org> Sun, 9 Jul 2006 21:52:13 +0200
-
-collectd (3.9.4+debian-1) unstable; urgency=low
-
- * Initial release (Closes: #373008).
- * Removed upstream's debian/ directory from .orig.tar.gz.
- * getifaddrs.dpatch: Patching src/traffic.c to read data from /proc instead
- of using getifaddrs(). getifaddrs() does not seem to work correctly on
- AMD64.
-
- -- Sebastian Harl <sh@tokkee.org> Fri, 7 Jul 2006 15:49:42 +0200
-
+++ /dev/null
-# Config file for collectd(1).
-#
-# Some plugins need additional configuration and are disabled by default.
-# Please read collectd.conf(5) for details.
-
-Mode Local
-
-# If in "Client" mode you have to specify which server to send datagrams to.
-#Mode Client
-#Server 123.123.123.123 12345
-
-#Mode Server
-
-#Mode Log
-
-#DataDir /var/lib/collectd
-#PIDFILE /var/run/collectd.pid
-#PluginDir /usr/lib/collectd
-
-#LoadPlugin apache
-#LoadPlugin apcups
-#LoadPlugin apple_sensors
-LoadPlugin battery
-LoadPlugin cpu
-#LoadPlugin cpufreq
-LoadPlugin df
-LoadPlugin disk
-#LoadPlugin hddtemp
-LoadPlugin load
-#LoadPlugin mbmon
-LoadPlugin memory
-#LoadPlugin mysql
-#LoadPlugin nfs
-#LoadPlugin ntpd
-#LoadPlugin ping
-LoadPlugin processes
-#LoadPlugin sensors
-#LoadPlugin serial
-LoadPlugin swap
-#LoadPlugin tape
-LoadPlugin traffic
-LoadPlugin users
-#LoadPlugin vserver
-#LoadPlugin wireless
-
-#<Plugin apache>
-# URL http://localhost/status?auto
-# User www-user
-# Password secret
-# CACert /etc/ssl/ca.crt
-#</Plugin>
-
-#<Plugin apcups>
-# Host localhost
-# Port 3551
-#</Plugin>
-
-#<Plugin hddtemp>
-# Host 127.0.0.1
-# Port 7634
-#</Plugin>
-
-#<Plugin mbmon>
-# Host 127.0.0.1
-# Port 411
-#</Plugin>
-
-#<Plugin ntpd>
-# Host localhost
-# Port 123
-#</Plugin>
-
-#<Plugin mysql>
-# Host localhost
-# User db_user
-# Password secret
-# Database db_name
-#</Plugin>
-
-#<Plugin ping>
-# Host host.foo.bar
-# Host host.baz.qux
-#</Plugin>
-
-#<Plugin traffic>
-# Interface eth0
-# IgnoreSelected false
-#</Plugin>
-
+++ /dev/null
-#!/bin/bash
-#
-# collectd Initscript for collectd
-# http://collectd.org/
-# Authors: Florian Forster <octo@verplant.org>
-# Sebastian Harl <sh@tokkee.org>
-#
-
-set -e
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-DESC="Statistics collection daemon"
-NAME=collectd
-DAEMON=/usr/sbin/$NAME
-SCRIPTNAME=/etc/init.d/$NAME
-ARGS=""
-
-CONFIGDIR=/etc/collectd
-# for backward compatibility
-FALLBACKCONF=/etc/collectd.conf
-
-# Gracefully exit if the package has been removed.
-test -x $DAEMON || exit 0
-
-if [ -r /etc/default/$NAME ]
-then
- . /etc/default/$NAME
-fi
-
-#
-# Function that starts the daemon/service.
-#
-d_start() {
- i=0
-
- if [ ! -d "$CONFIGDIR" ]
- then
- if [ -e "$FALLBACKCONF" ]
- then
- $DAEMON -C "$FALLBACKCONF" 2> /dev/null
- else
- echo ""
- echo "This package is not configured yet. Please refer"
- echo "to /usr/share/doc/collectd/README.Debian for"
- echo "details."
- echo ""
- exit 0
- fi
- else
- for FILE in `ls $CONFIGDIR/*.conf 2>/dev/null`
- do
- NAME=`basename "$FILE" .conf`
-
- if [ $i == 0 ]
- then
- echo -n " ("
- else
- echo -n ", "
- fi
-
- $DAEMON -C "$FILE" 2> /dev/null
- if [ $? == 0 ]
- then
- echo -n "$NAME"
- else
- echo -n "$NAME failed"
- fi
-
- i=$(($i+1))
- done
-
- if [ $i == 0 ]
- then
- echo -n "[no config found]"
- exit 1
- else
- echo -n ")"
- fi
- fi
-}
-
-#
-# Function that stops the daemon/service.
-#
-d_stop() {
- start-stop-daemon --stop --quiet --oknodo --exec $DAEMON
-}
-
-case "$1" in
- start)
- echo -n "Starting $DESC: $NAME"
- d_start
- echo "."
- ;;
- stop)
- echo -n "Stopping $DESC: $NAME"
- d_stop
- echo "."
- ;;
- restart|force-reload)
- echo -n "Restarting $DESC: $NAME"
- d_stop
- sleep 1
- d_start
- echo "."
- ;;
- *)
- echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
- exit 1
- ;;
-esac
-
-exit 0
-
-# vim: syntax=sh noexpandtab sw=8 ts=8 :
+++ /dev/null
-#!/bin/bash
-# postrm script for collectd
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * <postrm> `remove'
-# * <postrm> `purge'
-# * <old-postrm> `upgrade' <new-version>
-# * <new-postrm> `failed-upgrade' <old-version>
-# * <new-postrm> `abort-install'
-# * <new-postrm> `abort-install' <old-version>
-# * <new-postrm> `abort-upgrade' <old-version>
-# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
- purge)
- rm -rf /var/lib/collectd
- rm -rf /etc/collectd
- ;;
-
- remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
- ;;
-
- *)
- echo "postrm called with unknown argument \`$1'" >&2
- exit 1
- ;;
-
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
-
+++ /dev/null
-Source: collectd
-Section: utils
-Priority: optional
-Maintainer: Sebastian Harl <sh@tokkee.org>
-Build-Depends: debhelper (>= 5), autotools-dev, libcurl3-dev,
- libmysqlclient15-dev | libmysqlclient14-dev, librrd2-dev | librrd0-dev,
- libsensors-dev, liboping-dev (> 0.3.3)
-Standards-Version: 3.7.2
-
-Package: collectd
-Architecture: any
-Depends: ${shlibs:Depends}
-Suggests: collectd-apache, collectd-mysql, collectd-sensors, collectd-dev,
- collectd-hddtemp, collectd-mbmon, collectd-ping, librrds-perl
-Description: statistics collection daemon
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the main program file and the following plugins:
- * APC UPS's charge, load, input/output/battery voltage, etc.: apcups
- * Apple computer's sensors information: apple_sensors (server mode only)
- * battery status: battery
- * CPU utilization: cpu
- * CPU frequency: cpufreq
- * disk space usage: df
- * disk and partition throughput: disk
- * system load averages: load
- * memory usage: memory
- * NFS utilization: nfs
- * NTP daemon's local clock drift, offset to peers, etc.: ntpd
- * number of processes: processes
- * serial port traffic: serial
- * swap usage: swap
- * tape drive usage: tape (server mode only)
- * network traffic: traffic
- * number of users logged into the system: users
- * system ressources used by Linux-VServers: vserver
- * wireless network stats: wireless
-
-Package: collectd-dbg
-Section: devel
-Architecture: any
-Priority: extra
-Depends: collectd (= ${Source-Version}), collectd-apache (= ${Source-Version}),
- collectd-hddtemp (= ${Source-Version}), collectd-mysql (= ${Source-Version}),
- collectd-ping (= ${Source-Version}), collectd-sensors (= ${Source-Version})
-Description: statistics collection daemon (debugging symbols)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the debugging symbols.
-
-Package: collectd-apache
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Description: statistics collection daemon (Apache plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the Apache plugin which collects Apache statistics
- provided by Apache's mod_status.
-
-Package: collectd-hddtemp
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Recommends: hddtemp
-Description: statistics collection daemon (hddtemp plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the hddtemp plugin which collects harddisk temperatures.
-
-Package: collectd-mbmon
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Recommends: mbmon (>= 2.01)
-Description: statistics collection daemon (mbmon plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the mbmon plugin which collects motherboard status
- information: temperatures, voltages and cooling fan speeds.
-
-Package: collectd-mysql
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Description: statistics collection daemon (MySQL plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the MySQL plugin which collects MySQL statistics
- provided by MySQL's "show status" command.
-
-Package: collectd-ping
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Description: statistics collection daemon (ping plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the ping plugin which collects network latency
- statistics.
-
-Package: collectd-sensors
-Architecture: any
-Depends: collectd (= ${Source-Version}), ${shlibs:Depends}
-Description: statistics collection daemon (sensors plugin)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the sensors plugin which collects lm_sensors
- information (e.g. CPU temperature, fan speeds).
-
-Package: collectd-dev
-Architecture: all
-Depends: collectd (= ${Source-Version})
-Description: statistics collection daemon (development files)
- collectd is a small daemon written in C for performance. It reads various
- system statistics and updates RRD files, creating them if necessary. Since
- the daemon doesn't need to startup every time it wants to update the files
- it's very fast and easy on the system. Also, the statistics are very fine
- grained since the files are updated every 10 seconds.
- .
- This package contains the development files needed to create your own
- plugins.
+++ /dev/null
-This package was debianized by Sebastian Harl <sh@tokkee.org> on
-Wed, 10 May 2006 09:20:39 +0200.
-
-It was downloaded from <http://collectd.org/files/>.
-
-Upstream Authors:
- Core Developer:
- Florian Forster <octo@verplant.org>
-
- For individual credits, see AUTHORS.
-
-Copyright Holders (in alphabetical order):
- Andre M. Hedrick <andre@suse.com>
- Anthony Gialluca <tonyabg@charter.net>
- David Bacher <drbacher@gmail.com>
- Florian Forster <octo@verplant.org>
- Free Software Foundation, Inc.
- Jason Pepas <cell@ices.utexas.edu>
- Kern Sibbald
- Lyonel Vincent <lyonel@ezix.org>
- Niki W. Waibel <niki.waibel@newlogic.com>
- Peter Holik <peter@holik.at>
- Roy Keene, Keene Enterprises <libconfig@rkeene.org>
- Scott Garrett <sgarrett@technomancer.com>
- Sebastian Harl <sh@tokkee.org>
- Vincent Stehlé <vincent.stehle@free.fr>
-
-License:
-
- Files src/apache.c
- src/apple_sensors.c
- src/mysql.c
- src/ntpd.c
- src/battery.c
- src/wireless.c
- Copyright (C) 2006 Florian Forster <octo@verplant.org>
-
- File src/apcups.c
- Copyright (C) 2006 Anthony Gialluca <tonyabg@charter.net>
- Copyright (C) 2000-2004 Kern Sibbald
- Copyright (C) 1996-99 Andre M. Hedrick <andre@suse.com>
-
- File src/cpufreq.c
- Copyright (C) 2005, 2006 Peter Holik <peter@holik.at>
-
- File src/hddtemp.c
- Copyright (C) 2005, 2006 Vincent Stehlé <vincent.stehle@free.fr>
-
- File src/mbmon.c
- Copyright (C) 2006 Flavio Stanchina <flavio@stanchina.net>
-
- File src/nfs.c
- Copyright (C) 2005, 2006 Jason Pepas <cell@ices.utexas.edu>
-
- File src/processes.c
- Copyright (C) 2005 Lyonel Vincent <lyonel@ezix.org>
- Copyright (C) 2006 Florian Forster <octo@verplant.org> (Mach code)
-
- File src/serial.c
- Copyright (C) 2005, 2006 David Bacher <drbacher@gmail.com>
-
- File src/tape.c
- Copyright (C) 2005, 2006 Scott Garrett <sgarrett@technomancer.com>
-
- File src/users.c
- Copyright (C) 2005, 2006 Sebastian Harl <sh@tokkee.org>
-
- Files src/utils_debug.c
- src/utils_debug.h
- src/utils_mount.c
- src/utils_mount.h
- Copyright (C) 2005, 2006 Niki W. Waibel <niki.waibel@gmx.net>
-
- File src/vserver.c
- Copyright (C) 2006 Sebastian Harl <sh@tokkee.org>
-
- All other files (except libltdl/* and src/libconfig/*):
- Copyright (C) 2005, 2006 Florian Forster <octo@verplant.org>
-
- This package is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This package 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 package; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
- USA.
-
-On Debian systems, the complete text of the GNU General Public License
-can be found in `/usr/share/common-licenses/GPL'.
-
- File libltdl/ltdl.c
- Copyright (C) 1998-2000, 2004, 2005 Free Software Foundation, Inc.
- Originally by Thomas Tanner <tanner@ffii.org>
-
- File libltdl/ltdl.h
- Copyright (C) 1998-2000 Free Software Foundation, Inc.
- Originally by Thomas Tanner <tanner@ffii.org>
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
- USA.
-
-On Debian systems, the complete text of the GNU Lesser General Public License
-can be found in `/usr/share/common-licenses/LGPL'.
-
- Files src/libconfig/*
- Copyright (C) Roy Keene <libconfig@rkeene.org>
-
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation
- files (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge,
- publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-The Debian packaging is (C) 2006, Sebastian Harl <sh@tokkee.org> and
-is licensed under the GPL, see above.
-
+++ /dev/null
-/*
- * /usr/share/doc/collectd/examples/sample_plugin.c
- *
- * A sample plugin for collectd.
- *
- * Written by Sebastian Harl <sh@tokkee.org>
- *
- * This is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#include <collectd/common.h> /* rrd_update_file */
-#include <collectd/plugin.h> /* plugin_* */
-
-#include <stdio.h>
-#include <stdlib.h>
-
-/* Optional config file support */
-/* #include <collectd/configfile.h> */
-
-/* Optional debugging support
- * (only available if collectd was compiled with debugging support) */
-/* #include <collectd/utils_debug.h> */
-
-#define MODULE_NAME "myplugin"
-
-/* Name of the rrd file under DataDir (/var/lib/collectd by default)
- *
- * The name may contain slashes to create subdirectories. */
-static char *my_rrd = "myplugin.rrd";
-
-/* DS definitions for the rrd file
- *
- * See the rrdcreate(1) manpage for details. The heartbeat is configurable in
- * collectd. It defaults to 25. */
-static char *ds_def[] =
-{
- "DS:my_ds:GAUGE:25:0:U",
- NULL
-};
-
-/* DS count */
-static int ds_num = 1;
-
-/* Time at which the read function is called */
-extern time_t curtime;
-
-/* Initialize the plugin
- *
- * This function is called to set up a plugin before using it. */
-static void my_init(void)
-{
- /* we have nothing to do here :-) */
- return;
-}
-
-/* Get the data
- *
- * This function implements the magic used to get the desired values that
- * should be stored in the rrd file. It uses plugin_submit to transfer the
- * data to whatever place is configured in the config file. If there are more
- * than one instances you should pass a uniq identifier as seconds argument to
- * the plugin_submit function. */
-#define BUFSIZE 256
-static void my_read(void)
-{
- long int data = 0;
- char buf[BUFSIZE] = "";
-
- /* magic ;-) */
- data = random();
-
- if (snprintf(buf, BUFSIZE, "%u:%li",
- (unsigned int)curtime, data) >= BUFSIZE)
- return;
-
- plugin_submit(MODULE_NAME, NULL, buf);
- return;
-}
-#undef BUFSIZE
-
-/* Save the data
- *
- * This function saves the data to the appropriate location by calling
- * rrd_update_file. It is used to "calculate" the filename and DS definition
- * appropriate for the given instance. */
-static void my_write(host, inst, val)
- char *host;
- char *inst;
- char *val;
-{
- rrd_update_file(host, my_rrd, val, ds_def, ds_num);
- return;
-}
-
-/* Register the plugin
- *
- * This function registers the plugin with collectd. It has to be named
- * "module_register". */
-void module_register(void)
-{
- plugin_register(MODULE_NAME, my_init, my_read, my_write);
- return;
-}
-
+++ /dev/null
-#!/usr/bin/make -f
-# debian/rules for collectd
-#
-# Written by Sebastian Harl <sh@tokkee.org>.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-# These are used for cross-compiling and for saving the configure script
-# from having to guess our platform (since we know it already)
-DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
-DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
-
-CFLAGS = -Wall -g
-
-ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
- CFLAGS += -O0
-else
- CFLAGS += -O2
-endif
-
-config.status: configure
- dh_testdir
- CFLAGS="$(CFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) \
- --build=$(DEB_BUILD_GNU_TYPE) --prefix=/usr \
- --mandir=\$${prefix}/share/man \
- --localstatedir=/var --sysconfdir=/etc
-
-build: build-stamp
-
-build-stamp: config.status
- dh_testdir
-
- $(MAKE)
-
- touch build-stamp
-
-clean:
- dh_testdir
- dh_testroot
- rm -f build-stamp
-
- -$(MAKE) distclean
-
- dh_clean
-
-install-indep:
- dh_testdir
- dh_testroot
- dh_clean -k
- dh_installdirs -i
-
- include_dir=$(CURDIR)/debian/collectd-dev/usr/include/collectd/ \
- && mkdir -p $$include_dir \
- && cp src/*.h $$include_dir
-
-install-arch: build
- dh_testdir
- dh_testroot
- dh_clean -k
- dh_installdirs -a
-
- $(MAKE) install DESTDIR=$(CURDIR)/debian/collectd
-
- rm -f $(CURDIR)/debian/collectd/usr/lib/collectd/*.la
-
- rm -f $(CURDIR)/debian/collectd/etc/collectd.conf
- install -D -m 0644 $(CURDIR)/debian/collectd.conf \
- $(CURDIR)/debian/collectd/etc/collectd/collectd.conf
-
- for PLUGIN in apache hddtemp mbmon mysql ping sensors; do \
- plugin_dir=$(CURDIR)/debian/collectd-$$PLUGIN/usr/lib/collectd/; \
- mkdir -p $$plugin_dir; \
- mv $(CURDIR)/debian/collectd/usr/lib/collectd/$$PLUGIN.so \
- $$plugin_dir; \
- done
-
-binary-indep: install-indep
- dh_testdir
- dh_testroot
- dh_installchangelogs -i ChangeLog
- dh_installdocs -A -i debian/README.Debian AUTHORS README TODO
- dh_installexamples -i debian/examples/myplugin.c
- dh_compress -i
- dh_fixperms -i
- dh_installdeb -i
- dh_gencontrol -i
- dh_md5sums -i
- dh_builddeb -i
-
-binary-arch: build install-arch
- dh_testdir
- dh_testroot
- dh_installchangelogs -a ChangeLog
- dh_installdocs -A -a debian/README.Debian AUTHORS README TODO
- dh_installexamples -a contrib/collectd2html.pl contrib/collection.cgi
- dh_installinit -a -- defaults 95
- dh_installman -a src/collectd.1 src/collectd.conf.5
- dh_link -a
- dh_strip -a --dbg-package=collectd-dbg
- dh_compress -a
- dh_fixperms -a
- dh_installdeb -a
- dh_shlibdeps -a
- dh_gencontrol -a
- dh_md5sums -a
- dh_builddeb -a
-
-binary: binary-arch binary-indep
-.PHONY: build clean binary-indep binary-arch binary install-indep install-arch
-
if BUILD_WITH_LIBRT
collectd_LDADD += -lrt
endif
+if BUILD_WITH_LIBPOSIX4
+collectd_LDADD += -lposix4
+endif
if BUILD_WITH_LIBSOCKET
collectd_LDADD += -lsocket
endif
collectd_DEPENDENCIES += curl_json.la
endif
+if BUILD_PLUGIN_CURL_XML
+pkglib_LTLIBRARIES += curl_xml.la
+curl_xml_la_SOURCES = curl_xml.c
+curl_xml_la_LDFLAGS = -module -avoid-version
+curl_xml_la_CFLAGS = $(AM_CFLAGS) \
+ $(BUILD_WITH_LIBCURL_CFLAGS) $(BUILD_WITH_LIBXML2_CFLAGS)
+curl_xml_la_LIBADD = $(BUILD_WITH_LIBCURL_LIBS) $(BUILD_WITH_LIBXML2_LIBS)
+collectd_LDADD += "-dlopen" curl_xml.la
+collectd_DEPENDENCIES += curl_xml.la
+endif
+
if BUILD_PLUGIN_DBI
pkglib_LTLIBRARIES += dbi.la
dbi_la_SOURCES = dbi.c \
endif
endif
+if BUILD_PLUGIN_MODBUS
+pkglib_LTLIBRARIES += modbus.la
+modbus_la_SOURCES = modbus.c
+modbus_la_LDFLAGS = -module -avoid-version
+modbus_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMODBUS_CFLAGS)
+modbus_la_LIBADD = $(BUILD_WITH_LIBMODBUS_LIBS)
+collectd_LDADD += "-dlopen" modbus.la
+collectd_DEPENDENCIES += modbus.la
+endif
+
if BUILD_PLUGIN_MULTIMETER
pkglib_LTLIBRARIES += multimeter.la
multimeter_la_SOURCES = multimeter.c
(st->host != NULL) ? st->host : hostname_g,
(st->name != NULL) ? st->name : "default"),
- status = plugin_register_complex_read (callback_name,
+ status = plugin_register_complex_read (/* group = */ NULL,
+ /* name = */ callback_name,
/* callback = */ apache_read_host,
/* interval = */ NULL,
/* user_data = */ &ud);
{
if (strcmp (fields[0], "Scoreboard:") == 0)
submit_scoreboard (fields[1], st);
- else if (strcmp (fields[0], "BusyServers:") == 0)
+ else if ((strcmp (fields[0], "BusyServers:") == 0) /* Apache 1.* */
+ || (strcmp (fields[0], "BusyWorkers:") == 0) /* Apache 2.* */)
submit_gauge ("apache_connections", NULL, atol (fields[1]), st);
+ else if ((strcmp (fields[0], "IdleServers:") == 0) /* Apache 1.x */
+ || (strcmp (fields[0], "IdleWorkers:") == 0) /* Apache 2.x */)
+ submit_gauge ("apache_idle_workers", NULL, atol (fields[1]), st);
}
}
# ...
<Plugin perl>
IncludeDir "/path/to/perl/plugins"
- BaseName "Collectd::Plugin"
+ BaseName "Collectd::Plugins"
EnableDebugger ""
LoadPlugin "FooBar"
=item B<BaseName> I<Name>
Prepends I<Name>B<::> to all plugin names loaded after this option. This is
-provided for convenience to keep plugin names short.
+provided for convenience to keep plugin names short. All Perl-based plugins
+provided with the I<collectd> distributions reside in the C<Collectd::Plugins>
+namespace.
=item E<lt>B<Plugin> I<Name>E<gt> block
=item Config-Item
-A config-item is one structure which keeps the informations provided in the
+A config-item is one structure which keeps the information provided in the
configuration file. The array of children keeps one entry for each
configuration option. Each such entry is another config-item structure, which
may nest further if nested blocks are used.
=item
-Please feel free to send in new plugins to collectd's mailinglist at
+Please feel free to send in new plugins to collectd's mailing list at
E<lt>collectdE<nbsp>atE<nbsp>verplant.orgE<gt> for review and, possibly,
inclusion in the main distribution. In the latter case, we will take care of
keeping the plugin up to date and adapting it to new versions of collectd.
Loads the Python plugin I<Plugin>. Unlike most other LoadPlugin lines, this one
should be a block containing the line "Globals true". This will cause collectd
to export the name of all objects in the python interpreter for all plugins to
-see. If you don't do this or your platform does not support it, the embeded
-interpreter will start anywa but you won't be able to load certain python
+see. If you don't do this or your platform does not support it, the embedded
+interpreter will start anyway but you won't be able to load certain python
modules, e.g. "time".
=item B<Encoding> I<Name>
=head2 Config
-The Config class is an object which keeps the informations provided in the
+The Config class is an object which keeps the information provided in the
configuration file. The sequence of children keeps one entry for each
configuration option. Each such entry is another Config instance, which
may nest further if nested blocks are used.
=item
This plugin is not compatible with python3. Trying to compile it with python3
-will fail because of the ways string, unicode and bytearray bahavior was
+will fail because of the ways string, unicode and bytearray behavior was
changed.
=item
#endif
" -h Display help (this message)\n"
"\nBuiltin defaults:\n"
- " Config-File "CONFIGFILE"\n"
- " PID-File "PIDFILE"\n"
- " Data-Directory "PKGLOCALSTATEDIR"\n"
+ " Config file "CONFIGFILE"\n"
+ " PID file "PIDFILE"\n"
+ " Plugin directory "PLUGINDIR"\n"
+ " Data directory "PKGLOCALSTATEDIR"\n"
"\n"PACKAGE" "VERSION", http://collectd.org/\n"
"by Florian octo Forster <octo@verplant.org>\n"
"for contributions see `AUTHORS'\n");
@LOAD_PLUGIN_CSV@LoadPlugin csv
#@BUILD_PLUGIN_CURL_TRUE@LoadPlugin curl
#@BUILD_PLUGIN_CURL_JSON_TRUE@LoadPlugin curl_json
+#@BUILD_PLUGIN_CURL_XML_TRUE@LoadPlugin curl_xml
#@BUILD_PLUGIN_DBI_TRUE@LoadPlugin dbi
#@BUILD_PLUGIN_DF_TRUE@LoadPlugin df
#@BUILD_PLUGIN_DISK_TRUE@LoadPlugin disk
# </URL>
#</Plugin>
+#<Plugin "curl_xml">
+# <URL "http://localhost/stats.xml">
+# Host "my_host"
+# Instance "some_instance"
+# User "collectd"
+# Password "thaiNg0I"
+# VerifyPeer true
+# VerifyHost true
+# CACert "/path/to/ca.crt"
+#
+# <XPath "table[@id=\"magic_level\"]/tr">
+# Type "magic_level"
+# #InstancePrefix "prefix-"
+# InstanceFrom "td[1]"
+# ValuesFrom "td[2]/span[@class=\"level\"]"
+# </XPath>
+# </URL>
+#</Plugin>
+
#<Plugin dbi>
# <Query "num_of_customers">
# Statement "SELECT 'customers' AS c_key, COUNT(*) AS c_value FROM customers_tbl"
#<Plugin "java">
# JVMArg "-verbose:jni"
-# JVMArg "-Djava.class.path=@prefix@/share/collectd/java"
+# JVMArg "-Djava.class.path=@prefix@/share/collectd/java/collectd-api.jar"
#
# LoadPlugin "org.collectd.java.Foobar"
# <Plugin "org.collectd.java.Foobar">
#<Plugin perl>
# IncludeDir "/my/include/path"
-# BaseName "Collectd::Plugin"
+# BaseName "Collectd::Plugins"
# EnableDebugger ""
# LoadPlugin Monitorus
# LoadPlugin OpenVZ
# Password "dozaiTh4"
# CollectInterface true
# CollectRegistrationTable true
+# CollectCPULoad true
+# CollectMemory true
+# CollectDF true
+# CollectDisk true
# </Router>
#</Plugin>
# </Match>
# <Match>
# Regex "\\<R=local_user\\>"
+# ExcludeRegex "\\<R=local_user\\>.*mail_spool defer"
# DSType "CounterInc"
# Type "counter"
# Instance "local_user"
=item B<Hostname> I<Name>
Sets the hostname that identifies a host. If you omit this setting, the
-hostname will be determinded using the L<gethostname(2)> system call.
+hostname will be determined using the L<gethostname(2)> system call.
=item B<FQDNLookup> B<true|false>
and the match infrastructure (the same code used by the tail plugin) to use
regular expressions with the received data.
-The following example will read the current value of AMD stock from google's
+The following example will read the current value of AMD stock from Google's
finance page and dispatch the value to collectd.
<Plugin curl>
In the B<Plugin> block, there may be one or more B<URL> blocks, each defining
a URL to be fetched via HTTP (using libcurl) and one or more B<Key> blocks.
-The B<Key> string argument must be in a path format, of which is used to collect
-a value from a JSON map object. If a B<Key> path element is that of a I<*> wildcard,
-the values for all keys will be collectd.
+The B<Key> string argument must be in a path format, which is used to collect a
+value from a JSON map object. If a path element of B<Key> is the
+I<*>E<nbsp>wildcard, the values for all keys will be collectd.
The following options are valid within B<URL> blocks:
=back
+=head2 Plugin C<curl_xml>
+
+The B<curl_xml plugin> uses B<libcurl> (L<http://curl.haxx.se/>) and B<libxml2>
+(L<http://xmlsoft.org/>) to retrieve XML data via cURL.
+
+ <Plugin "curl_xml">
+ <URL "http://localhost/stats.xml">
+ Host "my_host"
+ Instance "some_instance"
+ User "collectd"
+ Password "thaiNg0I"
+ VerifyPeer true
+ VerifyHost true
+ CACert "/path/to/ca.crt"
+
+ <XPath "table[@id=\"magic_level\"]/tr">
+ Type "magic_level"
+ #InstancePrefix "prefix-"
+ InstanceFrom "td[1]"
+ ValuesFrom "td[2]/span[@class=\"level\"]"
+ </XPath>
+ </URL>
+ </Plugin>
+
+In the B<Plugin> block, there may be one or more B<URL> blocks, each defining a
+URL to be fetched via HTTP (using libcurl). Within each B<URL> block there are
+options which specify the connection parameters, for example authentication
+information, and one or more B<XPath> blocks.
+
+Each B<XPath> block specifies how to get one type of information. The
+string argument must be a valid XPath expression which returns a list
+of "base elements". One value is dispatched for each "base element". The
+I<type instance> and values are looked up using further I<XPath> expressions
+that should be relative to the base element.
+
+Within the B<URL> block the following options are accepted:
+
+=over 4
+
+=item B<Host> I<Name>
+
+Use I<Name> as the host name when submitting values. Defaults to the global
+host name setting.
+
+=item B<Instance> I<Instance>
+
+Use I<Instance> as the plugin instance when submitting values. Defaults to an
+empty string (no plugin instance).
+
+=item B<User> I<User>
+=item B<Password> I<Password>
+=item B<VerifyPeer> B<true>|B<false>
+=item B<VerifyHost> B<true>|B<false>
+=item B<CACert> I<CA Cert File>
+
+These options behave exactly equivalent to the appropriate options of the
+I<cURL> and I<cURL-JSON> plugins. Please see there for a detailed description.
+
+=item E<lt>B<XPath> I<XPath-expression>E<gt>
+
+Within each B<URL> block, there must be one or more B<XPath> blocks. Each
+B<XPath> block specifies how to get one type of information. The string
+argument must be a valid XPath expression which returns a list of "base
+elements". One value is dispatched for each "base element".
+
+Within the B<XPath> block the following options are accepted:
+
+=over 4
+
+=item B<Type> I<Type>
+
+Specifies the I<Type> used for submitting patches. This determines the number
+of values that are required / expected and whether the strings are parsed as
+signed or unsigned integer or as double values. See L<types.db(5)> for details.
+This option is required.
+
+=item B<InstancePrefix> I<InstancePrefix>
+
+Prefix the I<type instance> with I<InstancePrefix>. The values are simply
+concatenated together without any separator.
+This option is optional.
+
+=item B<InstanceFrom> I<InstanceFrom>
+
+Specifies a XPath expression to use for determining the I<type instance>. The
+XPath expression must return exactly one element. The element's value is then
+used as I<type instance>, possibly prefixed with I<InstancePrefix> (see above).
+
+This value is required. As a special exception, if the "base XPath expression"
+(the argument to the B<XPath> block) returns exactly one argument, then this
+option may be omitted.
+
+=item B<ValuesFrom> I<ValuesFrom> [I<ValuesFrom> ...]
+
+Specifies one or more XPath expression to use for reading the values. The
+number of XPath expressions must match the number of data sources in the
+I<type> specified with B<Type> (see above). Each XPath expression must return
+exactly one element. The element's value is then parsed as a number and used as
+value for the appropriate value in the value list dispatched to the daemon.
+
+=back
+
+=back
+
=head2 Plugin C<dbi>
This plugin uses the B<dbi> library (L<http://libdbi.sourceforge.net/>) to
Query blocks define I<SQL> statements and how the returned data should be
interpreted. They are identified by the name that is given in the opening line
of the block. Thus the name needs to be unique. Other than that, the name is
-not used in collectd.
+not used in collectd.
In each B<Query> block, there is one or more B<Result> blocks. B<Result> blocks
define which column holds which value or instance information. You can use
different syntax. The plugin will use only those queries, where the specified
minimum and maximum versions fit the version of the database in use.
-The database version is determined by C<dbi_conn_get_engine_version>, see the
+The database version is determined by C<dbi_conn_get_engine_version>, see the
L<libdbi documentation|http://libdbi.sourceforge.net/docs/programmers-guide/reference-conn.html#DBI-CONN-GET-ENGINE-VERSION>
for details. Basically, each part of the version is assumed to be in the range
from B<00> to B<99> and all dots are removed. So version "4.1.2" becomes
=item B<Interface> I<Interface>
-The dns plugin uses B<libpcap> to capture dns traffic and analyses it. This
+The dns plugin uses B<libpcap> to capture dns traffic and analyzes it. This
option sets the interface that should be used. If this option is not set, or
set to "any", the plugin will try to get packets from B<all> interfaces. This
may not work on certain platforms, such as MacE<nbsp>OSE<nbsp>X.
Prefix all lines printed by the current time. Defaults to B<true>.
+=item B<PrintSeverity> B<true>|B<false>
+
+When enabled, all lines are prefixed by the severity of the log message, for
+example "warning". Defaults to B<false>.
+
=back
B<Note>: There is no need to notify the daemon after moving or removing the
=back
+=head2 Plugin C<modbus>
+
+The B<modbus plugin> connects to a Modbus "slave" via Modbus/TCP and reads
+register values. It supports reading single registers (unsigned 16E<nbsp>bit
+values), large integer values (unsigned 32E<nbsp>bit values) and floating point
+values (two registers interpreted as IEEE floats in big endian notation).
+
+Synopsis:
+
+ <Data "voltage-input-1">
+ RegisterBase 0
+ RegisterType float
+ Type voltage
+ Instance "input-1"
+ </Data>
+
+ <Data "voltage-input-2">
+ RegisterBase 2
+ RegisterType float
+ Type voltage
+ Instance "input-2"
+ </Data>
+
+ <Host "modbus.example.com">
+ Address "192.168.0.42"
+ Port "502"
+ Interval 60
+
+ <Slave 1>
+ Instance "power-supply"
+ Collect "voltage-input-1"
+ Collect "voltage-input-2"
+ </Slave>
+ </Host>
+
+=over 4
+
+=item E<lt>B<Data> I<Name>E<gt> blocks
+
+Data blocks define a mapping between register numbers and the "types" used by
+I<collectd>.
+
+Within E<lt>DataE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<RegisterBase> I<Number>
+
+Configures the base register to read from the device. If the option
+B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
+register will be read (the register number is increased by one).
+
+=item B<RegisterType> B<Uint16>|B<Uint32>|B<Float>
+
+Specifies what kind of data is returned by the device. If the type is B<Uint32>
+or B<Float>, two 16E<nbsp>bit registers will be read and the data is combined
+into one value. Defaults to B<Uint16>.
+
+=item B<Type> I<Type>
+
+Specifies the "type" (data set) to use when dispatching the value to
+I<collectd>. Currently, only data sets with exactly one data source are
+supported.
+
+=item B<Instance> I<Instance>
+
+Sets the type instance to use when dispatching the value to I<collectd>. If
+unset, an empty string (no type instance) is used.
+
+=back
+
+=item E<lt>B<Host> I<Name>E<gt> blocks
+
+Host blocks are used to specify to which hosts to connect and what data to read
+from their "slaves". The string argument I<Name> is used as hostname when
+dispatching the values to I<collectd>.
+
+Within E<lt>HostE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<Address> I<Hostname>
+
+Specifies the node name (the actual network address) used to connect to the
+host. This may be an IP address or a hostname. Please note that the used
+I<libmodbus> library only supports IPv4 at the moment.
+
+=item B<Port> I<Service>
+
+Specifies the port used to connect to the host. The port can either be given as
+a number or as a service name. Please note that the I<Service> argument must be
+a string, even if ports are given in their numerical form. Defaults to "502".
+
+=item B<Interval> I<Interval>
+
+Sets the interval (in seconds) in which the values will be collected from this
+host. By default the global B<Interval> setting will be used.
+
+=item E<lt>B<Slave> I<ID>E<gt>
+
+Over each TCP connection, multiple Modbus devices may be reached. The slave ID
+is used to specify which device should be addressed. For each device you want
+to query, one B<Slave> block must be given.
+
+Within E<lt>SlaveE<nbsp>/E<gt> blocks, the following options are allowed:
+
+=over 4
+
+=item B<Instance> I<Instance>
+
+Specify the plugin instance to use when dispatching the values to I<collectd>.
+By default "slave_I<ID>" is used.
+
+=item B<Collect> I<DataName>
+
+Specifies which data to retrieve from the device. I<DataName> must be the same
+string as the I<Name> argument passed to a B<Data> block. You can specify this
+option multiple times to collect more than one value from a slave. At least one
+B<Collect> option is mandatory.
+
+=back
+
+=back
+
+=back
+
=head2 Plugin C<mysql>
The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to
=head2 Plugin C<netapp>
-The netapp plugin can collect various performance and capacity informations
+The netapp plugin can collect various performance and capacity information
from a NetApp filer using the NetApp API.
Please note that NetApp has a wide line of products and a lot of different
=item B<IgnoreSelected>
-The behaviour is the same as with all other similar plugins: If nothing is
+The behavior is the same as with all other similar plugins: If nothing is
selected at all, everything is collected. If some things are selected using the
options described above, only these statistics are collected. If you set
B<IgnoreSelected> to B<true>, this behavior is inverted, i.E<nbsp>e. the
The Network plugin sends data to a remote instance of collectd, receives data
from a remote instance, or both at the same time. Data which has been received
-from the network is usually not transmitted again, but this can be actived, see
+from the network is usually not transmitted again, but this can be activated, see
the B<Forward> option below.
The default IPv6 multicast group is C<ff18::efc0:4a42>. The default IPv4
This feature is only available if the I<network> plugin was linked with
I<libgcrypt>.
+=item B<Interface> I<Interface name>
+
+Set the outgoing interface for IP packets. This applies at least
+to IPv6 packets and if possible to IPv4. If this option is not applicable,
+undefined or a non-existent interface name is specified, the default
+behaviour is to let the kernel choose the appropriate interface. Be warned
+that the manual selection of an interface for unicast traffic is only
+necessary in rare cases.
+
=back
=item B<E<lt>Listen> I<Host> [I<Port>]B<E<gt>>
using L<stat(2)>. If the file has been changed, the contents is re-read. While
the file is being read, it is locked using L<fcntl(2)>.
+=item B<Interface> I<Interface name>
+
+Set the incoming interface for IP packets explicitly. This applies at least
+to IPv6 packets and if possible to IPv4. If this option is not applicable,
+undefined or a non-existent interface name is specified, the default
+behaviour is, to let the kernel choose the appropriate interface. Thus incoming
+traffic gets only accepted, if it arrives on the given interface.
+
=back
=item B<TimeToLive> I<1-255>
=item B<ReverseLookups> B<true>|B<false>
-Sets wether or not to perform reverse lookups on peers. Since the name or
+Sets whether or not to perform reverse lookups on peers. Since the name or
IP-address may be used in a filename it is recommended to disable reverse
lookups. The default is to do reverse lookups to preserve backwards
compatibility, though.
Specifies the location of the status file.
-=item B<Compression> B<true>|B<false>
+=item B<ImprovedNamingSchema> B<true>|B<false>
+
+When enabled, the filename of the status file will be used as plugin instance
+and the client's "common name" will be used as type instance. This is required
+when reading multiple status files. Enabling this option is recommended, but to
+maintain backwards compatibility this option is disabled by default.
+
+=item B<CollectCompression> B<true>|B<false>
Sets whether or not statistics about the compression used by OpenVPN should be
collected. This information is only available in I<single> mode. Enabled by
default.
+=item B<CollectIndividualUsers> B<true>|B<false>
+
+Sets whether or not traffic information is collected for each connected client
+individually. If set to false, currently no traffic data is collected at all
+because aggregating this data in a save manner is tricky. Defaults to B<true>.
+
+=item B<CollectUserCount> B<true>|B<false>
+
+When enabled, the number of currently connected clients or users is collected.
+This is expecially interesting when B<CollectIndividualUsers> is disabled, but
+can be configured independently from that option. Defaults to B<false>.
+
=back
=head2 Plugin C<oracle>
</Database>
<Database bar>
+ Interval 300
Service "service_name"
Query backend # predefined
Query rt36_tickets
=item I<interval>
-The interval collectd is using (as specified by the B<Interval> option).
+The interval with which this database is queried (as specified by the database
+specific or global B<Interval> options).
=back
=over 4
+=item B<Interval> I<seconds>
+
+Specify the interval with which the database should be queried. The default is
+to use the global B<Interval> setting.
+
=item B<Host> I<hostname>
Specify the hostname or IP of the PostgreSQL server to connect to. If the
User "collectd"
Password "secr3t"
CollectInterface true
+ CollectCPULoad true
+ CollectMemory true
</Router>
<Router>
Host "router1.example.com"
Password "5ecret"
CollectInterface true
CollectRegistrationTable true
+ CollectDF true
+ CollectDisk true
</Router>
</Plugin>
When set to B<true>, information about wireless LAN connections will be
collected. Defaults to B<false>.
+=item B<CollectCPULoad> B<true>|B<false>
+
+When set to B<true>, information about the CPU usage will be collected. The
+number is a dimensionless value where zero indicates no CPU usage at all.
+Defaults to B<false>.
+
+=item B<CollectMemory> B<true>|B<false>
+
+When enabled, the amount of used and free memory will be collected. How used
+memory is calculated is unknown, for example whether or not caches are counted
+as used space.
+Defaults to B<false>.
+
+=item B<CollectDF> B<true>|B<false>
+
+When enabled, the amount of used and free disk space will be collected.
+Defaults to B<false>.
+
+=item B<CollectDisk> B<true>|B<false>
+
+When enabled, the number of sectors written and bad blocks will be collected.
+Defaults to B<false>.
+
=back
=head2 Plugin C<rrdcached>
</Match>
<Match>
Regex "\\<R=local_user\\>"
+ ExcludeRegex "\\<R=local_user\\>.*mail_spool defer"
DSType "CounterInc"
Type "counter"
Instance "local_user"
Regex "SPAM \\(Score: (-?[0-9]+\\.[0-9]+)\\)"
+=item B<ExcludeRegex> I<regex>
+
+Sets an optional regular expression to use for excluding lines from the match.
+An example which excludes all connections from localhost from the match:
+
+ ExcludeRegex "127\\.0\\.0\\.1"
+
=item B<DSType> I<Type>
Sets how the values are cumulated. I<Type> is one of:
=head2 Plugin C<tokyotyrant>
-The C<tokyotyrant plugin> connects to a TokyoTyrant server and collects a
+The C<tokyotyrant plugin> connects to a TokyoTyrant server and collects a
couple metrics: number of records, and database size on disk.
=over 4
Defaults to B<Command>.
+=item B<StoreRates> B<true|false>
+
+If set to B<true>, convert counter values to rates. If set to B<false> (the
+default) counter values are stored as is, i.E<nbsp>e. as an increasing integer
+number.
+
=back
=head1 THRESHOLD CONFIGURATION
=back
-=head2 Built-in targets
+=head2 Built-in targets
The following targets are built into the core daemon and therefore need no
plugins to be loaded:
the identifier of a value. If multiple regular expressions are given, B<all>
regexen must match for a value to match.
+=item B<Invert> B<false>|B<true>
+
+When set to B<true>, the result of the match is inverted, i.e. all value lists
+where all regular expressions apply are not matched, all other value lists are
+matched. Defaults to B<false>.
+
=back
Example:
# ifndef isnan
# define isnan(f) ((f) != (f))
# endif /* !defined(isnan) */
+# ifndef isfinite
+# define isfinite(f) (((f) - (f)) == 0.0)
+# endif
+# ifndef isinf
+# define isinf(f) (!isfinite(f) && !isnan(f))
+# endif
#endif /* NAN_ZERO_ZERO */
/* Try really, really hard to determine endianess. Under NexentaStor 1.0.2 this
}
#endif /* HAVE_LIBKSTAT */
+#ifndef HAVE_HTONLL
unsigned long long ntohll (unsigned long long n)
{
#if BYTE_ORDER == BIG_ENDIAN
return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
#endif
} /* unsigned long long htonll */
+#endif /* HAVE_HTONLL */
#if FP_LAYOUT_NEED_NOTHING
/* Well, we need nothing.. */
return (service_number);
return (-1);
} /* int service_name_to_port_number */
+
+int strtoderive (const char *string, derive_t *ret_value) /* {{{ */
+{
+ derive_t tmp;
+ char *endptr;
+
+ if ((string == NULL) || (ret_value == NULL))
+ return (EINVAL);
+
+ errno = 0;
+ endptr = NULL;
+ tmp = (derive_t) strtoll (string, &endptr, /* base = */ 0);
+ if ((endptr == string) || (errno != 0))
+ return (-1);
+
+ *ret_value = tmp;
+ return (0);
+} /* }}} int strtoderive */
long long get_kstat_value (kstat_t *ksp, char *name);
#endif
+#ifndef HAVE_HTONLL
unsigned long long ntohll (unsigned long long n);
unsigned long long htonll (unsigned long long n);
+#endif
#if FP_LAYOUT_NEED_NOTHING
# define ntohd(d) (d)
* (in the range [1-65535]). Returns less than zero on error. */
int service_name_to_port_number (const char *service_name);
+int strtoderive (const char *string, derive_t *ret_value);
+
#endif /* COMMON_H */
ERROR ("configfile: malloc failed.");
return (NULL);
}
- memset (root, '\0', sizeof (oconfig_item_t));
+ memset (root, 0, sizeof (oconfig_item_t));
while ((de = readdir (dh)) != NULL)
{
char name[1024];
char **tmp;
- if ((de->d_name[0] == '.') || (de->d_name[0] == '\0'))
+ if ((de->d_name[0] == '.') || (de->d_name[0] == 0))
continue;
status = ssnprintf (name, sizeof (name), "%s/%s",
char *name = filenames[i];
temp = cf_read_generic (name, depth);
- if (temp == NULL) {
- int j;
- for (j = i; j < filenames_num; ++j)
- free (filenames[j]);
- free (filenames);
- oconfig_free (root);
- return (NULL);
+ if (temp == NULL)
+ {
+ /* An error should already have been reported. */
+ sfree (name);
+ continue;
}
cf_ci_append_children (root, temp);
return (0);
} /* }}} int cf_util_get_string */
+/* Assures the config option is a string and copies it to the provided buffer.
+ * Assures null-termination. */
+int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer, /* {{{ */
+ size_t buffer_size)
+{
+ if ((ci == NULL) || (buffer == NULL) || (buffer_size < 1))
+ return (EINVAL);
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ ERROR ("cf_util_get_string_buffer: The %s option requires "
+ "exactly one string argument.", ci->key);
+ return (-1);
+ }
+
+ strncpy (buffer, ci->values[0].value.string, buffer_size);
+ buffer[buffer_size - 1] = 0;
+
+ return (0);
+} /* }}} int cf_util_get_string_buffer */
+
+/* Assures the config option is a number and returns it as an int. */
+int cf_util_get_int (const oconfig_item_t *ci, int *ret_value) /* {{{ */
+{
+ if ((ci == NULL) || (ret_value == NULL))
+ return (EINVAL);
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+ {
+ ERROR ("cf_util_get_int: The %s option requires "
+ "exactly one numeric argument.", ci->key);
+ return (-1);
+ }
+
+ *ret_value = (int) ci->values[0].value.number;
+
+ return (0);
+} /* }}} int cf_util_get_int */
+
int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool) /* {{{ */
{
if ((ci == NULL) || (ret_bool == NULL))
if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
{
ERROR ("cf_util_get_boolean: The %s option requires "
- "exactly one string argument.", ci->key);
+ "exactly one boolean argument.", ci->key);
return (-1);
}
* success. */
int cf_util_get_string (const oconfig_item_t *ci, char **ret_string);
+/* Assures the config option is a string and copies it to the provided buffer.
+ * Assures null-termination. */
+int cf_util_get_string_buffer (const oconfig_item_t *ci, char *buffer,
+ size_t buffer_size);
+
+/* Assures the config option is a number and returns it as an int. */
+int cf_util_get_int (const oconfig_item_t *ci, int *ret_value);
+
/* Assures the config option is a boolean and assignes it to `ret_bool'.
* Otherwise, `ret_bool' is not changed and non-zero is returned. */
int cf_util_get_boolean (const oconfig_item_t *ci, _Bool *ret_bool);
# error "No applicable input method."
#endif
-static void cs_submit (unsigned long context_switches)
+static void cs_submit (derive_t context_switches)
{
value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
char buffer[64];
int numfields;
char *fields[3];
- unsigned long result = 0;
+ derive_t result = 0;
int status = -2;
fh = fopen ("/proc/stat", "r");
errno = 0;
endptr = NULL;
- result = strtoul(fields[1], &endptr, 10);
+ result = (derive_t) strtoll (fields[1], &endptr, /* base = */ 10);
if ((endptr == fields[1]) || (errno != 0)) {
ERROR ("contextswitch plugin: Cannot parse ctxt value: %s",
fields[1]);
fprintf (use_stdio == 1 ? stdout : stderr,
"PUTVAL %s interval=%i %s\n",
- filename, interval_g, values);
+ filename, vl->interval, values);
return (0);
}
struct web_match_s /* {{{ */
{
char *regex;
+ char *exclude_regex;
int dstype;
char *type;
char *instance;
if (strcasecmp ("Regex", child->key) == 0)
status = cc_config_add_string ("Regex", &match->regex, child);
+ else if (strcasecmp ("ExcludeRegex", child->key) == 0)
+ status = cc_config_add_string ("ExcludeRegex", &match->exclude_regex, child);
else if (strcasecmp ("DSType", child->key) == 0)
status = cc_config_add_match_dstype (&match->dstype, child);
else if (strcasecmp ("Type", child->key) == 0)
if (status != 0)
return (status);
- match->match = match_create_simple (match->regex, match->dstype);
+ match->match = match_create_simple (match->regex, match->exclude_regex,
+ match->dstype);
if (match->match == NULL)
{
ERROR ("curl plugin: tail_match_add_match_simple failed.");
ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
db->instance, db->url);
- plugin_register_complex_read (cb_name, cj_read,
+ plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
/* interval = */ NULL, &ud);
}
else
--- /dev/null
+/**
+ * collectd - src/curl_xml.c
+ * Copyright (C) 2009,2010 Amit Gupta
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Amit Gupta <amit.gupta221 at gmail.com>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_llist.h"
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+#include <curl/curl.h>
+
+#define CX_DEFAULT_HOST "localhost"
+
+/*
+ * Private data structures
+ */
+struct cx_values_s /* {{{ */
+{
+ char path[DATA_MAX_NAME_LEN];
+ size_t path_len;
+};
+typedef struct cx_values_s cx_values_t;
+/* }}} */
+
+struct cx_xpath_s /* {{{ */
+{
+ char *path;
+ char *type;
+ cx_values_t *values;
+ int values_len;
+ char *instance_prefix;
+ char *instance;
+ int is_table;
+ unsigned long magic;
+};
+typedef struct cx_xpath_s cx_xpath_t;
+/* }}} */
+
+struct cx_s /* {{{ */
+{
+ char *instance;
+ char *host;
+
+ char *url;
+ char *user;
+ char *pass;
+ char *credentials;
+ _Bool verify_peer;
+ _Bool verify_host;
+ char *cacert;
+
+ CURL *curl;
+ char curl_errbuf[CURL_ERROR_SIZE];
+ char *buffer;
+ size_t buffer_size;
+ size_t buffer_fill;
+
+ llist_t *list; /* list of xpath blocks */
+};
+typedef struct cx_s cx_t; /* }}} */
+
+/*
+ * Private functions
+ */
+static size_t cx_curl_callback (void *buf, /* {{{ */
+ size_t size, size_t nmemb, void *user_data)
+{
+ size_t len = size * nmemb;
+ cx_t *db;
+
+ db = user_data;
+ if (db == NULL)
+ {
+ ERROR ("curl_xml plugin: cx_curl_callback: "
+ "user_data pointer is NULL.");
+ return (0);
+ }
+
+ if (len <= 0)
+ return (len);
+
+ if ((db->buffer_fill + len) >= db->buffer_size)
+ {
+ char *temp;
+
+ temp = (char *) realloc (db->buffer,
+ db->buffer_fill + len + 1);
+ if (temp == NULL)
+ {
+ ERROR ("curl_xml plugin: realloc failed.");
+ return (0);
+ }
+ db->buffer = temp;
+ db->buffer_size = db->buffer_fill + len + 1;
+ }
+
+ memcpy (db->buffer + db->buffer_fill, (char *) buf, len);
+ db->buffer_fill += len;
+ db->buffer[db->buffer_fill] = 0;
+
+ return (len);
+} /* }}} size_t cx_curl_callback */
+
+static void cx_xpath_free (cx_xpath_t *xpath) /* {{{ */
+{
+ if (xpath == NULL)
+ return;
+
+ sfree (xpath->path);
+ sfree (xpath->type);
+ sfree (xpath->instance_prefix);
+ sfree (xpath->instance);
+ sfree (xpath->values);
+ sfree (xpath);
+} /* }}} void cx_xpath_free */
+
+static void cx_list_free (llist_t *list) /* {{{ */
+{
+ llentry_t *le;
+
+ le = llist_head (list);
+ while (le != NULL)
+ {
+ llentry_t *le_next;
+
+ le_next = le->next;
+
+ sfree (le->key);
+ cx_xpath_free (le->value);
+
+ le = le_next;
+ }
+
+ llist_destroy (list);
+ list = NULL;
+} /* }}} void cx_list_free */
+
+static void cx_free (void *arg) /* {{{ */
+{
+ cx_t *db;
+
+ DEBUG ("curl_xml plugin: cx_free (arg = %p);", arg);
+
+ db = (cx_t *) arg;
+
+ if (db == NULL)
+ return;
+
+ if (db->curl != NULL)
+ curl_easy_cleanup (db->curl);
+ db->curl = NULL;
+
+ if (db->list != NULL)
+ cx_list_free (db->list);
+
+ sfree (db->buffer);
+ sfree (db->instance);
+ sfree (db->host);
+
+ sfree (db->url);
+ sfree (db->user);
+ sfree (db->pass);
+ sfree (db->credentials);
+ sfree (db->cacert);
+
+ sfree (db);
+} /* }}} void cx_free */
+
+static int cx_check_type (const data_set_t *ds, cx_xpath_t *xpath) /* {{{ */
+{
+ if (!ds)
+ {
+ WARNING ("curl_xml plugin: DataSet `%s' not defined.", xpath->type);
+ return (-1);
+ }
+
+ if (ds->ds_num != xpath->values_len)
+ {
+ WARNING ("curl_xml plugin: DataSet `%s' requires %i values, but config talks about %i",
+ xpath->type, ds->ds_num, xpath->values_len);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} cx_check_type */
+
+static xmlXPathObjectPtr cx_evaluate_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ xmlChar *expr)
+{
+ xmlXPathObjectPtr xpath_obj;
+
+ /* XXX: When to free this? */
+ xpath_obj = xmlXPathEvalExpression(BAD_CAST expr, xpath_ctx);
+ if (xpath_obj == NULL)
+ {
+ WARNING ("curl_xml plugin: "
+ "Error unable to evaluate xpath expression \"%s\". Skipping...", expr);
+ return NULL;
+ }
+
+ return xpath_obj;
+} /* }}} cx_evaluate_xpath */
+
+static int cx_if_not_text_node (xmlNodePtr node) /* {{{ */
+{
+ if (node->type == XML_TEXT_NODE || node->type == XML_ATTRIBUTE_NODE)
+ return (0);
+
+ WARNING ("curl_xml plugin: "
+ "Node \"%s\" doesn't seem to be a text node. Skipping...", node->name);
+ return -1;
+} /* }}} cx_if_not_text_node */
+
+static int cx_handle_single_value_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath,
+ const data_set_t *ds, value_list_t *vl, int index)
+{
+ xmlXPathObjectPtr values_node_obj;
+ xmlNodeSetPtr values_node;
+ int tmp_size;
+ char *node_value;
+
+ values_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->values[index].path);
+ if (values_node_obj == NULL)
+ return (-1); /* Error already logged. */
+
+ values_node = values_node_obj->nodesetval;
+ tmp_size = (values_node) ? values_node->nodeNr : 0;
+
+ if (tmp_size == 0)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" doesn't match any of the nodes. "
+ "Skipping...", xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ if (tmp_size > 1)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return "
+ "only one node. Skipping...", xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ /* ignoring the element if other than textnode/attribute*/
+ if (cx_if_not_text_node(values_node->nodeTab[0]))
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return "
+ "only text/attribute node which is not the case. Skipping...",
+ xpath->values[index].path);
+ xmlXPathFreeObject (values_node_obj);
+ return (-1);
+ }
+
+ node_value = (char *) xmlNodeGetContent(values_node->nodeTab[0]);
+ switch (ds->ds[index].type)
+ {
+ case DS_TYPE_COUNTER:
+ vl->values[index].counter = (counter_t) strtoull (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_DERIVE:
+ vl->values[index].derive = (derive_t) strtoll (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_ABSOLUTE:
+ vl->values[index].absolute = (absolute_t) strtoull (node_value,
+ /* endptr = */ NULL, /* base = */ 0);
+ break;
+ case DS_TYPE_GAUGE:
+ vl->values[index].gauge = (gauge_t) strtod (node_value,
+ /* endptr = */ NULL);
+ }
+
+ /* free up object */
+ xmlXPathFreeObject (values_node_obj);
+
+ /* We have reached here which means that
+ * we have got something to work */
+ return (0);
+} /* }}} int cx_handle_single_value_xpath */
+
+static int cx_handle_all_value_xpaths (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath,
+ const data_set_t *ds, value_list_t *vl)
+{
+ value_t values[xpath->values_len];
+ int status;
+ int i;
+
+ assert (xpath->values_len > 0);
+ assert (xpath->values_len == vl->values_len);
+ assert (xpath->values_len == ds->ds_num);
+ vl->values = values;
+
+ for (i = 0; i < xpath->values_len; i++)
+ {
+ status = cx_handle_single_value_xpath (xpath_ctx, xpath, ds, vl, i);
+ if (status != 0)
+ return (-1); /* An error has been printed. */
+ } /* for (i = 0; i < xpath->values_len; i++) */
+
+ plugin_dispatch_values (vl);
+ vl->values = NULL;
+
+ return (0);
+} /* }}} int cx_handle_all_value_xpaths */
+
+static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */
+ cx_xpath_t *xpath, value_list_t *vl,
+ _Bool is_table)
+{
+ xmlXPathObjectPtr instance_node_obj = NULL;
+ xmlNodeSetPtr instance_node = NULL;
+
+ memset (vl->type_instance, 0, sizeof (vl->type_instance));
+
+ /* If the base xpath returns more than one block, the result is assumed to be
+ * a table. The `Instnce' option is not optional in this case. Check for the
+ * condition and inform the user. */
+ if (is_table && (vl->type_instance == NULL))
+ {
+ WARNING ("curl_xml plugin: "
+ "Base-XPath %s is a table (more than one result was returned), "
+ "but no instance-XPath has been defined.",
+ xpath->path);
+ return (-1);
+ }
+
+ /* instance has to be an xpath expression */
+ if (xpath->instance != NULL)
+ {
+ int tmp_size;
+
+ instance_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST xpath->instance);
+ if (instance_node_obj == NULL)
+ return (-1); /* error is logged already */
+
+ instance_node = instance_node_obj->nodesetval;
+ tmp_size = (instance_node) ? instance_node->nodeNr : 0;
+
+ if ( (tmp_size == 0) && (is_table) )
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression for 'InstanceFrom' \"%s\" doesn't match "
+ "any of the nodes. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+
+ if (tmp_size > 1)
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression for 'InstanceFrom' \"%s\" is expected "
+ "to return only one text node. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+
+ /* ignoring the element if other than textnode/attribute */
+ if (cx_if_not_text_node(instance_node->nodeTab[0]))
+ {
+ WARNING ("curl_xml plugin: "
+ "relative xpath expression \"%s\" is expected to return only text node "
+ "which is not the case. Skipping the node.", xpath->instance);
+ xmlXPathFreeObject (instance_node_obj);
+ return (-1);
+ }
+ } /* if (xpath->instance != NULL) */
+
+ if (xpath->instance_prefix != NULL)
+ {
+ if (instance_node != NULL)
+ ssnprintf (vl->type_instance, sizeof (vl->type_instance),"%s%s",
+ xpath->instance_prefix, (char *) xmlNodeGetContent(instance_node->nodeTab[0]));
+ else
+ sstrncpy (vl->type_instance, xpath->instance_prefix,
+ sizeof (vl->type_instance));
+ }
+ else
+ {
+ /* If instance_prefix and instance_node are NULL, then
+ * don't set the type_instance */
+ if (instance_node != NULL)
+ sstrncpy (vl->type_instance, (char *) xmlNodeGetContent(instance_node->nodeTab[0]),
+ sizeof (vl->type_instance));
+ }
+
+ /* Free `instance_node_obj' this late, because `instance_node' points to
+ * somewhere inside this structure. */
+ xmlXPathFreeObject (instance_node_obj);
+
+ return (0);
+} /* }}} int cx_handle_instance_xpath */
+
+static int cx_handle_base_xpath (char *plugin_instance, /* {{{ */
+ xmlXPathContextPtr xpath_ctx, const data_set_t *ds,
+ char *base_xpath, cx_xpath_t *xpath)
+{
+ int total_nodes;
+ int i;
+
+ xmlXPathObjectPtr base_node_obj = NULL;
+ xmlNodeSetPtr base_nodes = NULL;
+
+ value_list_t vl = VALUE_LIST_INIT;
+
+ base_node_obj = cx_evaluate_xpath (xpath_ctx, BAD_CAST base_xpath);
+ if (base_node_obj == NULL)
+ return -1; /* error is logged already */
+
+ base_nodes = base_node_obj->nodesetval;
+ total_nodes = (base_nodes) ? base_nodes->nodeNr : 0;
+
+ if (total_nodes == 0)
+ {
+ ERROR ("curl_xml plugin: "
+ "xpath expression \"%s\" doesn't match any of the nodes. "
+ "Skipping the xpath block...", base_xpath);
+ xmlXPathFreeObject (base_node_obj);
+ return -1;
+ }
+
+ /* If base_xpath returned multiple results, then */
+ /* Instance in the xpath block is required */
+ if (total_nodes > 1 && xpath->instance == NULL)
+ {
+ ERROR ("curl_xml plugin: "
+ "InstanceFrom is must in xpath block since the base xpath expression \"%s\" "
+ "returned multiple results. Skipping the xpath block...", base_xpath);
+ return -1;
+ }
+
+ /* set the values for the value_list */
+ vl.values_len = ds->ds_num;
+ sstrncpy (vl.type, xpath->type, sizeof (vl.type));
+ sstrncpy (vl.plugin, "curl_xml", sizeof (vl.plugin));
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ if (plugin_instance != NULL)
+ sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+
+ for (i = 0; i < total_nodes; i++)
+ {
+ int status;
+
+ xpath_ctx->node = base_nodes->nodeTab[i];
+
+ status = cx_handle_instance_xpath (xpath_ctx, xpath, &vl,
+ /* is_table = */ (total_nodes > 1));
+ if (status != 0)
+ continue; /* An error has already been reported. */
+
+ status = cx_handle_all_value_xpaths (xpath_ctx, xpath, ds, &vl);
+ if (status != 0)
+ continue; /* An error has been logged. */
+ } /* for (i = 0; i < total_nodes; i++) */
+
+ /* free up the allocated memory */
+ xmlXPathFreeObject (base_node_obj);
+
+ return (0);
+} /* }}} cx_handle_base_xpath */
+
+static int cx_handle_parsed_xml(xmlDocPtr doc, /* {{{ */
+ xmlXPathContextPtr xpath_ctx, cx_t *db)
+{
+ llentry_t *le;
+ const data_set_t *ds;
+ cx_xpath_t *xpath;
+ int status=-1;
+
+
+ le = llist_head (db->list);
+ while (le != NULL)
+ {
+ /* get the ds */
+ xpath = (cx_xpath_t *) le->value;
+ ds = plugin_get_ds (xpath->type);
+
+ if ( (cx_check_type(ds, xpath) == 0) &&
+ (cx_handle_base_xpath(db->instance, xpath_ctx, ds, le->key, xpath) == 0) )
+ status = 0; /* we got atleast one success */
+
+ le = le->next;
+ } /* while (le != NULL) */
+
+ return status;
+} /* }}} cx_handle_parsed_xml */
+
+static int cx_parse_stats_xml(xmlChar* xml, cx_t *db) /* {{{ */
+{
+ int status;
+ xmlDocPtr doc;
+ xmlXPathContextPtr xpath_ctx;
+
+ /* Load the XML */
+ doc = xmlParseDoc(xml);
+ if (doc == NULL)
+ {
+ ERROR ("curl_xml plugin: Failed to parse the xml document - %s", xml);
+ return (-1);
+ }
+
+ xpath_ctx = xmlXPathNewContext(doc);
+ if(xpath_ctx == NULL)
+ {
+ ERROR ("curl_xml plugin: Failed to create the xml context");
+ xmlFreeDoc(doc);
+ return (-1);
+ }
+
+ status = cx_handle_parsed_xml (doc, xpath_ctx, db);
+ /* Cleanup */
+ xmlXPathFreeContext(xpath_ctx);
+ xmlFreeDoc(doc);
+ return status;
+} /* }}} cx_parse_stats_xml */
+
+static int cx_curl_perform (cx_t *db, CURL *curl) /* {{{ */
+{
+ int status;
+ long rc;
+ char *ptr;
+ char *url;
+
+ db->buffer_fill = 0;
+ status = curl_easy_perform (curl);
+
+ curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &rc);
+
+ if (rc != 200)
+ {
+ ERROR ("curl_xml plugin: curl_easy_perform failed with response code %ld (%s)",
+ rc, url);
+ return (-1);
+ }
+
+ if (status != 0)
+ {
+ ERROR ("curl_xml plugin: curl_easy_perform failed with status %i: %s (%s)",
+ status, db->curl_errbuf, url);
+ return (-1);
+ }
+
+ ptr = db->buffer;
+
+ status = cx_parse_stats_xml(BAD_CAST ptr, db);
+ db->buffer_fill = 0;
+
+ return status;
+} /* }}} int cx_curl_perform */
+
+static int cx_read (user_data_t *ud) /* {{{ */
+{
+ cx_t *db;
+
+ if ((ud == NULL) || (ud->data == NULL))
+ {
+ ERROR ("curl_xml plugin: cx_read: Invalid user data.");
+ return (-1);
+ }
+
+ db = (cx_t *) ud->data;
+
+ return cx_curl_perform (db, db->curl);
+} /* }}} int cx_read */
+
+/* Configuration handling functions {{{ */
+
+static int cx_config_add_values (const char *name, cx_xpath_t *xpath, /* {{{ */
+ oconfig_item_t *ci)
+{
+ int i;
+
+ if (ci->values_num < 1)
+ {
+ WARNING ("curl_xml plugin: `ValuesFrom' needs at least one argument.");
+ return (-1);
+ }
+
+ for (i = 0; i < ci->values_num; i++)
+ if (ci->values[i].type != OCONFIG_TYPE_STRING)
+ {
+ WARNING ("curl_xml plugin: `ValuesFrom' needs only string argument.");
+ return (-1);
+ }
+
+ sfree (xpath->values);
+
+ xpath->values_len = 0;
+ xpath->values = (cx_values_t *) malloc (sizeof (cx_values_t) * ci->values_num);
+ if (xpath->values == NULL)
+ return (-1);
+ xpath->values_len = ci->values_num;
+
+ /* populate cx_values_t structure */
+ for (i = 0; i < ci->values_num; i++)
+ {
+ xpath->values[i].path_len = sizeof (ci->values[i].value.string);
+ sstrncpy (xpath->values[i].path, ci->values[i].value.string, sizeof (xpath->values[i].path));
+ }
+
+ return (0);
+} /* }}} cx_config_add_values */
+
+static int cx_config_add_xpath (cx_t *db, /* {{{ */
+ oconfig_item_t *ci)
+{
+ cx_xpath_t *xpath;
+ int status;
+ int i;
+
+ xpath = (cx_xpath_t *) malloc (sizeof (*xpath));
+ if (xpath == NULL)
+ {
+ ERROR ("curl_xml plugin: malloc failed.");
+ return (-1);
+ }
+ memset (xpath, 0, sizeof (*xpath));
+
+ status = cf_util_get_string (ci, &xpath->path);
+ if (status != 0)
+ {
+ sfree (xpath);
+ return (status);
+ }
+
+ /* error out if xpath->path is an empty string */
+ if (*xpath->path == 0)
+ {
+ ERROR ("curl_xml plugin: invalid xpath. "
+ "xpath value can't be an empty string");
+ return (-1);
+ }
+
+ status = 0;
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Type", child->key) == 0)
+ status = cf_util_get_string (child, &xpath->type);
+ else if (strcasecmp ("InstancePrefix", child->key) == 0)
+ status = cf_util_get_string (child, &xpath->instance_prefix);
+ else if (strcasecmp ("InstanceFrom", child->key) == 0)
+ status = cf_util_get_string (child, &xpath->instance);
+ else if (strcasecmp ("ValuesFrom", child->key) == 0)
+ status = cx_config_add_values ("ValuesFrom", xpath, child);
+ else
+ {
+ WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ if (status == 0 && xpath->type == NULL)
+ {
+ WARNING ("curl_xml plugin: `Type' missing in `xpath' block.");
+ status = -1;
+ }
+
+ if (status == 0)
+ {
+ char *name;
+ llentry_t *le;
+
+ if (db->list == NULL)
+ {
+ db->list = llist_create();
+ if (db->list == NULL)
+ {
+ ERROR ("curl_xml plugin: list creation failed.");
+ return (-1);
+ }
+ }
+
+ name = strdup(xpath->path);
+ if (name == NULL)
+ {
+ ERROR ("curl_xml plugin: strdup failed.");
+ return (-1);
+ }
+
+ le = llentry_create (name, xpath);
+ if (le == NULL)
+ {
+ ERROR ("curl_xml plugin: llentry_create failed.");
+ return (-1);
+ }
+
+ llist_append (db->list, le);
+ }
+
+ return (status);
+} /* }}} int cx_config_add_xpath */
+
+/* Initialize db->curl */
+static int cx_init_curl (cx_t *db) /* {{{ */
+{
+ db->curl = curl_easy_init ();
+ if (db->curl == NULL)
+ {
+ ERROR ("curl_xml plugin: curl_easy_init failed.");
+ return (-1);
+ }
+
+ curl_easy_setopt (db->curl, CURLOPT_WRITEFUNCTION, cx_curl_callback);
+ curl_easy_setopt (db->curl, CURLOPT_WRITEDATA, db);
+ curl_easy_setopt (db->curl, CURLOPT_USERAGENT,
+ PACKAGE_NAME"/"PACKAGE_VERSION);
+ curl_easy_setopt (db->curl, CURLOPT_ERRORBUFFER, db->curl_errbuf);
+ curl_easy_setopt (db->curl, CURLOPT_URL, db->url);
+
+ if (db->user != NULL)
+ {
+ size_t credentials_size;
+
+ credentials_size = strlen (db->user) + 2;
+ if (db->pass != NULL)
+ credentials_size += strlen (db->pass);
+
+ db->credentials = (char *) malloc (credentials_size);
+ if (db->credentials == NULL)
+ {
+ ERROR ("curl_xml plugin: malloc failed.");
+ return (-1);
+ }
+
+ ssnprintf (db->credentials, credentials_size, "%s:%s",
+ db->user, (db->pass == NULL) ? "" : db->pass);
+ curl_easy_setopt (db->curl, CURLOPT_USERPWD, db->credentials);
+ }
+
+ curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYPEER, db->verify_peer ? 1L : 0L);
+ curl_easy_setopt (db->curl, CURLOPT_SSL_VERIFYHOST,
+ db->verify_host ? 2L : 0L);
+ if (db->cacert != NULL)
+ curl_easy_setopt (db->curl, CURLOPT_CAINFO, db->cacert);
+
+ return (0);
+} /* }}} int cx_init_curl */
+
+static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
+{
+ cx_t *db;
+ int status = 0;
+ int i;
+
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("curl_xml plugin: The `URL' block "
+ "needs exactly one string argument.");
+ return (-1);
+ }
+
+ db = (cx_t *) malloc (sizeof (*db));
+ if (db == NULL)
+ {
+ ERROR ("curl_xml plugin: malloc failed.");
+ return (-1);
+ }
+ memset (db, 0, sizeof (*db));
+
+ if (strcasecmp ("URL", ci->key) == 0)
+ {
+ status = cf_util_get_string (ci, &db->url);
+ if (status != 0)
+ {
+ sfree (db);
+ return (status);
+ }
+ }
+ else
+ {
+ ERROR ("curl_xml plugin: cx_config: "
+ "Invalid key: %s", ci->key);
+ return (-1);
+ }
+
+ /* Fill the `cx_t' structure.. */
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Instance", child->key) == 0)
+ status = cf_util_get_string (child, &db->instance);
+ else if (strcasecmp ("Host", child->key) == 0)
+ status = cf_util_get_string (child, &db->host);
+ else if (strcasecmp ("User", child->key) == 0)
+ status = cf_util_get_string (child, &db->user);
+ else if (strcasecmp ("Password", child->key) == 0)
+ status = cf_util_get_string (child, &db->pass);
+ else if (strcasecmp ("VerifyPeer", child->key) == 0)
+ status = cf_util_get_boolean (child, &db->verify_peer);
+ else if (strcasecmp ("VerifyHost", child->key) == 0)
+ status = cf_util_get_boolean (child, &db->verify_host);
+ else if (strcasecmp ("CACert", child->key) == 0)
+ status = cf_util_get_string (child, &db->cacert);
+ else if (strcasecmp ("xpath", child->key) == 0)
+ status = cx_config_add_xpath (db, child);
+ else
+ {
+ WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if (status == 0)
+ {
+ if (db->list == NULL)
+ {
+ WARNING ("curl_xml plugin: No (valid) `Key' block "
+ "within `URL' block `%s'.", db->url);
+ status = -1;
+ }
+ if (status == 0)
+ status = cx_init_curl (db);
+ }
+
+ /* If all went well, register this database for reading */
+ if (status == 0)
+ {
+ user_data_t ud;
+ char cb_name[DATA_MAX_NAME_LEN];
+
+ if (db->instance == NULL)
+ db->instance = strdup("default");
+
+ DEBUG ("curl_xml plugin: Registering new read callback: %s",
+ db->instance);
+
+ memset (&ud, 0, sizeof (ud));
+ ud.data = (void *) db;
+ ud.free_func = cx_free;
+
+ ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s",
+ db->instance, db->url);
+
+ plugin_register_complex_read (/* group = */ NULL, cb_name, cx_read,
+ /* interval = */ NULL, &ud);
+ }
+ else
+ {
+ cx_free (db);
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int cx_config_add_url */
+
+/* }}} End of configuration handling functions */
+
+static int cx_config (oconfig_item_t *ci) /* {{{ */
+{
+ int success;
+ int errors;
+ int status;
+ int i;
+
+ success = 0;
+ errors = 0;
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("URL", child->key) == 0)
+ {
+ status = cx_config_add_url (child);
+ if (status == 0)
+ success++;
+ else
+ errors++;
+ }
+ else
+ {
+ WARNING ("curl_xml plugin: Option `%s' not allowed here.", child->key);
+ errors++;
+ }
+ }
+
+ if ((success == 0) && (errors > 0))
+ {
+ ERROR ("curl_xml plugin: All statements failed.");
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int cx_config */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("curl_xml", cx_config);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
cdbi_driver_option_t *driver_options;
size_t driver_options_num;
+ udb_query_preparation_area_t **q_prep_areas;
udb_query_t **queries;
size_t queries_num;
}
sfree (db->driver_options);
+ if (db->q_prep_areas)
+ for (i = 0; i < db->queries_num; ++i)
+ udb_query_delete_preparation_area (db->q_prep_areas[i]);
+ free (db->q_prep_areas);
+
sfree (db);
} /* }}} void cdbi_database_free */
break;
} /* while (status == 0) */
+ while ((status == 0) && (db->queries_num > 0))
+ {
+ db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+ db->queries_num, sizeof (*db->q_prep_areas));
+
+ if (db->q_prep_areas == NULL)
+ {
+ WARNING ("dbi plugin: malloc failed");
+ status = -1;
+ break;
+ }
+
+ for (i = 0; i < db->queries_num; ++i)
+ {
+ db->q_prep_areas[i]
+ = udb_query_allocate_preparation_area (db->queries[i]);
+
+ if (db->q_prep_areas[i] == NULL)
+ {
+ WARNING ("dbi plugin: udb_query_allocate_preparation_area failed");
+ status = -1;
+ break;
+ }
+ }
+
+ break;
+ }
+
/* If all went well, add this database to the global list of databases. */
if (status == 0)
{
} /* }}} int cdbi_init */
static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
- udb_query_t *q)
+ udb_query_t *q, udb_query_preparation_area_t *prep_area)
{
const char *statement;
dbi_result res;
sstrncpy (column_names[i], column_name, DATA_MAX_NAME_LEN);
} /* }}} for (i = 0; i < column_num; i++) */
- udb_query_prepare_result (q, hostname_g, /* plugin = */ "dbi", db->name,
- column_names, column_num);
+ udb_query_prepare_result (q, prep_area, hostname_g,
+ /* plugin = */ "dbi", db->name,
+ column_names, column_num, /* interval = */ -1);
/* 0 = error; 1 = success; */
status = dbi_result_first_row (res); /* {{{ */
"return any rows?",
db->name, udb_query_get_name (q),
cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
BAIL_OUT (-1);
} /* }}} */
* to dispatch the row to the daemon. */
if (status == 0) /* {{{ */
{
- status = udb_query_handle_result (q, column_values);
+ status = udb_query_handle_result (q, prep_area, column_values);
if (status != 0)
{
ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
} /* }}} while (42) */
/* Tell the db query interface that we're done with this query. */
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
/* Clean up and return `status = 0' (success) */
BAIL_OUT (0);
&& (udb_query_check_version (db->queries[i], db_version) == 0))
continue;
- status = cdbi_read_database_query (db, db->queries[i]);
+ status = cdbi_read_database_query (db,
+ db->queries[i], db->q_prep_areas[i]);
if (status == 0)
success++;
}
/**
* collectd - src/exec.c
- * Copyright (C) 2007-2009 Florian octo Forster
+ * Copyright (C) 2007-2010 Florian octo Forster
* Copyright (C) 2007-2009 Sebastian Harl
* Copyright (C) 2008 Peter Holik
*
setenv ("COLLECTD_HOSTNAME", buffer, /* overwrite = */ 1);
} /* }}} void set_environment */
+__attribute__((noreturn))
static void exec_child (program_list_t *pl) /* {{{ */
{
int status;
status = getpwnam_r (pl->user, &sp, nambuf, sizeof (nambuf), &sp_ptr);
if (status != 0)
{
- ERROR ("exec plugin: getpwnam_r failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ ERROR ("exec plugin: Failed to get user information for user ``%s'': %s",
+ pl->user, sstrerror (errno, errbuf, sizeof (errbuf)));
exit (-1);
}
if (sp_ptr == NULL)
status = getgrnam_r (pl->group, &gr, nambuf, sizeof (nambuf), &gr_ptr);
if (0 != status)
{
- ERROR ("exec plugin: getgrnam_r failed: %s",
+ ERROR ("exec plugin: Failed to get group information "
+ "for group ``%s'': %s", pl->group,
sstrerror (errno, errbuf, sizeof (errbuf)));
exit (-1);
}
status = execvp (pl->exec, pl->argv);
- ERROR ("exec plugin: exec failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ ERROR ("exec plugin: Failed to execute ``%s'': %s",
+ pl->exec, sstrerror (errno, errbuf, sizeof (errbuf)));
exit (-1);
} /* void exec_child }}} */
status = fork_child (pl, NULL, &fd, &fd_err);
if (status < 0)
+ {
+ /* Reset the "running" flag */
+ pthread_mutex_lock (&pl_lock);
+ pl->flags &= ~PL_RUNNING;
+ pthread_mutex_unlock (&pl_lock);
pthread_exit ((void *) 1);
+ }
pl->pid = status;
assert (pl->pid != 0);
return (0);
} /* int exec_read }}} */
-static int exec_notification (const notification_t *n,
+static int exec_notification (const notification_t *n, /* {{{ */
user_data_t __attribute__((unused)) *user_data)
{
program_list_t *pl;
/**
* collectd - src/filter_chain.h
- * Copyright (C) 2008,2009 Florian octo Forster
+ * Copyright (C) 2008-2010 Florian octo Forster
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
if ((plugin_list == NULL) || (plugin_list[0] == NULL))
{
status = plugin_write (/* plugin = */ NULL, ds, vl);
- if (status != 0)
+ if (status == ENOENT)
+ {
+ INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
+ "all write plugins failed with status %i (ENOENT). "
+ "Most likely this means you didn't load any write plugins.",
+ status);
+ }
+ else if (status != 0)
{
INFO ("Filter subsystem: Built-in target `write': Dispatching value to "
"all write plugins failed with status %i.", status);
ud.data = (void *) cbi;
ud.free_func = cjni_callback_info_destroy;
- plugin_register_complex_read (cbi->name, cjni_read,
+ plugin_register_complex_read (/* group = */ NULL, cbi->name, cjni_read,
/* interval = */ NULL, &ud);
(*jvm_env)->DeleteLocalRef (jvm_env, o_read);
static char *log_file = NULL;
static int print_timestamp = 1;
+static int print_severity = 0;
static const char *config_keys[] =
{
"LogLevel",
"File",
- "Timestamp"
+ "Timestamp",
+ "PrintSeverity"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
print_timestamp = 0;
else
print_timestamp = 1;
+ } else if (0 == strcasecmp(key, "PrintSeverity")) {
+ if (IS_FALSE (value))
+ print_severity = 0;
+ else
+ print_severity = 1;
}
else {
return -1;
return 0;
} /* int logfile_config (const char *, const char *) */
-static void logfile_print (const char *msg, time_t timestamp_time)
+static void logfile_print (const char *msg, int severity, time_t timestamp_time)
{
FILE *fh;
int do_close = 0;
struct tm timestamp_tm;
char timestamp_str[64];
+ char level_str[16] = "";
+
+ if (print_severity)
+ {
+ switch (severity)
+ {
+ case LOG_ERR:
+ snprintf(level_str, sizeof (level_str), "[error] ");
+ break;
+ case LOG_WARNING:
+ snprintf(level_str, sizeof (level_str), "[warning] ");
+ break;
+ case LOG_NOTICE:
+ snprintf(level_str, sizeof (level_str), "[notice] ");
+ break;
+ case LOG_INFO:
+ snprintf(level_str, sizeof (level_str), "[info] ");
+ break;
+ case LOG_DEBUG:
+ snprintf(level_str, sizeof (level_str), "[debug] ");
+ break;
+ default:
+ break;
+ }
+ }
if (print_timestamp)
{
else
{
if (print_timestamp)
- fprintf (fh, "[%s] %s\n", timestamp_str, msg);
+ fprintf (fh, "[%s] %s%s\n", timestamp_str, level_str, msg);
else
- fprintf (fh, "%s\n", msg);
+ fprintf (fh, "%s%s\n", level_str, msg);
if (do_close != 0)
fclose (fh);
if (severity > log_level)
return;
- logfile_print (msg, time (NULL));
+ logfile_print (msg, severity, time (NULL));
} /* void logfile_log (int, const char *) */
static int logfile_notification (const notification_t *n,
buf[sizeof (buf) - 1] = '\0';
- logfile_print (buf,
+ logfile_print (buf, LOG_INFO,
(n->time > 0) ? n->time : time (NULL));
return (0);
mr_regex_t *plugin_instance;
mr_regex_t *type;
mr_regex_t *type_instance;
+ _Bool invert;
};
/*
return (-ENOMEM);
}
memset (m, 0, sizeof (*m));
+
+ m->invert = 0;
status = 0;
for (i = 0; i < ci->children_num; i++)
status = mr_config_add_regex (&m->type, child);
else if (strcasecmp ("TypeInstance", child->key) == 0)
status = mr_config_add_regex (&m->type_instance, child);
+ else if (strcasecmp ("Invert", child->key) == 0)
+ status = cf_util_get_boolean(child, &m->invert);
else
{
log_err ("The `%s' configuration option is not understood and "
void **user_data)
{
mr_match_t *m;
+ int match_value = FC_MATCH_MATCHES;
+ int nomatch_value = FC_MATCH_NO_MATCH;
if ((user_data == NULL) || (*user_data == NULL))
return (-1);
m = *user_data;
+ if (m->invert)
+ {
+ match_value = FC_MATCH_NO_MATCH;
+ nomatch_value = FC_MATCH_MATCHES;
+ }
+
if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
if (mr_match_regexen (m->plugin_instance,
vl->plugin_instance) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
if (mr_match_regexen (m->type_instance,
vl->type_instance) == FC_MATCH_NO_MATCH)
- return (FC_MATCH_NO_MATCH);
+ return (nomatch_value);
- return (FC_MATCH_MATCHES);
+ return (match_value);
} /* }}} int mr_match */
void module_register (void)
struct web_match_s /* {{{ */
{
char *regex;
+ char *exclude_regex;
int dstype;
char *type;
char *instance;
if (strcasecmp ("Regex", child->key) == 0)
status = cmc_config_add_string ("Regex", &match->regex, child);
+ else if (strcasecmp ("ExcludeRegex", child->key) == 0)
+ status = cmc_config_add_string ("ExcludeRegex", &match->exclude_regex, child);
else if (strcasecmp ("DSType", child->key) == 0)
status = cmc_config_add_match_dstype (&match->dstype, child);
else if (strcasecmp ("Type", child->key) == 0)
if (status != 0)
return (status);
- match->match = match_create_simple (match->regex, match->dstype);
+ match->match = match_create_simple (match->regex, match->exclude_regex,
+ match->dstype);
if (match->match == NULL)
{
ERROR ("memcachec plugin: tail_match_add_match_simple failed.");
# include <netinet/in.h>
# include <netinet/tcp.h>
+/* Hack to work around the missing define in AIX */
+#ifndef MSG_DONTWAIT
+# define MSG_DONTWAIT MSG_NONBLOCK
+#endif
+
#define MEMCACHED_DEF_HOST "127.0.0.1"
#define MEMCACHED_DEF_PORT "11211"
--- /dev/null
+/**
+ * collectd - src/modbus.c
+ * Copyright (C) 2010 noris network AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Florian Forster <octo at noris.net>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <netdb.h>
+
+#include <modbus/modbus.h>
+
+#ifndef MODBUS_TCP_DEFAULT_PORT
+# ifdef MODBUS_TCP_PORT
+# define MODBUS_TCP_DEFAULT_PORT MODBUS_TCP_PORT
+# else
+# define MODBUS_TCP_DEFAULT_PORT 502
+# endif
+#endif
+
+/*
+ * <Data "data_name">
+ * RegisterBase 1234
+ * RegisterType float
+ * Type gauge
+ * Instance "..."
+ * </Data>
+ *
+ * <Host "name">
+ * Address "addr"
+ * Port "1234"
+ * Interval 60
+ *
+ * <Slave 1>
+ * Instance "foobar" # optional
+ * Collect "data_name"
+ * </Slave>
+ * </Host>
+ */
+
+/*
+ * Data structures
+ */
+enum mb_register_type_e /* {{{ */
+{
+ REG_TYPE_UINT16,
+ REG_TYPE_UINT32,
+ REG_TYPE_FLOAT
+}; /* }}} */
+typedef enum mb_register_type_e mb_register_type_t;
+
+struct mb_data_s;
+typedef struct mb_data_s mb_data_t;
+struct mb_data_s /* {{{ */
+{
+ char *name;
+ int register_base;
+ mb_register_type_t register_type;
+ char type[DATA_MAX_NAME_LEN];
+ char instance[DATA_MAX_NAME_LEN];
+
+ mb_data_t *next;
+}; /* }}} */
+
+struct mb_slave_s /* {{{ */
+{
+ int id;
+ char instance[DATA_MAX_NAME_LEN];
+ mb_data_t *collect;
+}; /* }}} */
+typedef struct mb_slave_s mb_slave_t;
+
+struct mb_host_s /* {{{ */
+{
+ char host[DATA_MAX_NAME_LEN];
+ char node[NI_MAXHOST];
+ /* char service[NI_MAXSERV]; */
+ int port;
+ int interval;
+
+ mb_slave_t *slaves;
+ size_t slaves_num;
+
+ modbus_param_t connection;
+ _Bool is_connected;
+ _Bool have_reconnected;
+}; /* }}} */
+typedef struct mb_host_s mb_host_t;
+
+struct mb_data_group_s;
+typedef struct mb_data_group_s mb_data_group_t;
+struct mb_data_group_s /* {{{ */
+{
+ mb_data_t *registers;
+ size_t registers_num;
+
+ mb_data_group_t *next;
+}; /* }}} */
+
+/*
+ * Global variables
+ */
+static mb_data_t *data_definitions = NULL;
+
+/*
+ * Functions
+ */
+static mb_data_t *data_get_by_name (mb_data_t *src, /* {{{ */
+ const char *name)
+{
+ mb_data_t *ptr;
+
+ if (name == NULL)
+ return (NULL);
+
+ for (ptr = src; ptr != NULL; ptr = ptr->next)
+ if (strcasecmp (ptr->name, name) == 0)
+ return (ptr);
+
+ return (NULL);
+} /* }}} mb_data_t *data_get_by_name */
+
+static int data_append (mb_data_t **dst, mb_data_t *src) /* {{{ */
+{
+ mb_data_t *ptr;
+
+ if ((dst == NULL) || (src == NULL))
+ return (EINVAL);
+
+ ptr = *dst;
+
+ if (ptr == NULL)
+ {
+ *dst = src;
+ return (0);
+ }
+
+ while (ptr->next != NULL)
+ ptr = ptr->next;
+
+ ptr->next = src;
+
+ return (0);
+} /* }}} int data_append */
+
+/* Copy a single mb_data_t and append it to another list. */
+static int data_copy (mb_data_t **dst, const mb_data_t *src) /* {{{ */
+{
+ mb_data_t *tmp;
+ int status;
+
+ if ((dst == NULL) || (src == NULL))
+ return (EINVAL);
+
+ tmp = malloc (sizeof (*tmp));
+ if (tmp == NULL)
+ return (ENOMEM);
+ memcpy (tmp, src, sizeof (*tmp));
+ tmp->name = NULL;
+ tmp->next = NULL;
+
+ tmp->name = strdup (src->name);
+ if (tmp->name == NULL)
+ {
+ sfree (tmp);
+ return (ENOMEM);
+ }
+
+ status = data_append (dst, tmp);
+ if (status != 0)
+ {
+ sfree (tmp->name);
+ sfree (tmp);
+ return (status);
+ }
+
+ return (0);
+} /* }}} int data_copy */
+
+/* Lookup a single mb_data_t instance, copy it and append the copy to another
+ * list. */
+static int data_copy_by_name (mb_data_t **dst, mb_data_t *src, /* {{{ */
+ const char *name)
+{
+ mb_data_t *ptr;
+
+ if ((dst == NULL) || (src == NULL) || (name == NULL))
+ return (EINVAL);
+
+ ptr = data_get_by_name (src, name);
+ if (ptr == NULL)
+ return (ENOENT);
+
+ return (data_copy (dst, ptr));
+} /* }}} int data_copy_by_name */
+
+/* Read functions */
+
+static int mb_submit (mb_host_t *host, mb_slave_t *slave, /* {{{ */
+ mb_data_t *data, value_t value)
+{
+ value_list_t vl = VALUE_LIST_INIT;
+
+ if ((host == NULL) || (slave == NULL) || (data == NULL))
+ return (EINVAL);
+
+ if (host->interval <= 0)
+ host->interval = interval_g;
+
+ if (slave->instance[0] == 0)
+ ssnprintf (slave->instance, sizeof (slave->instance), "slave_%i",
+ slave->id);
+
+ vl.values = &value;
+ vl.values_len = 1;
+ vl.interval = host->interval;
+ sstrncpy (vl.host, host->host, sizeof (vl.host));
+ sstrncpy (vl.plugin, "modbus", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, slave->instance, sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, data->type, sizeof (vl.type));
+ sstrncpy (vl.type_instance, data->instance, sizeof (vl.type_instance));
+
+ return (plugin_dispatch_values (&vl));
+} /* }}} int mb_submit */
+
+static float mb_register_to_float (uint16_t hi, uint16_t lo) /* {{{ */
+{
+ union
+ {
+ uint8_t b[4];
+ float f;
+ } conv;
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* little endian */
+ conv.b[0] = lo & 0x00ff;
+ conv.b[1] = (lo >> 8) & 0x00ff;
+ conv.b[2] = hi & 0x00ff;
+ conv.b[3] = (hi >> 8) & 0x00ff;
+#else
+ conv.b[3] = lo & 0x00ff;
+ conv.b[2] = (lo >> 8) & 0x00ff;
+ conv.b[1] = hi & 0x00ff;
+ conv.b[0] = (hi >> 8) & 0x00ff;
+#endif
+
+ return (conv.f);
+} /* }}} float mb_register_to_float */
+
+static int mb_init_connection (mb_host_t *host) /* {{{ */
+{
+ int status;
+
+ if (host == NULL)
+ return (EINVAL);
+
+ if (host->is_connected)
+ return (0);
+
+ /* Only reconnect once per interval. */
+ if (host->have_reconnected)
+ return (-1);
+
+ modbus_set_debug (&host->connection, 1);
+
+#if 0
+ /* We'll do the error handling ourselves. */
+ modbus_set_error_handling (&host->connection, NOP_ON_ERROR);
+#endif
+
+ if ((host->port < 1) || (host->port > 65535))
+ host->port = MODBUS_TCP_DEFAULT_PORT;
+
+ DEBUG ("Modbus plugin: Trying to connect to \"%s\", port %i.",
+ host->node, host->port);
+
+ modbus_init_tcp (&host->connection,
+ /* host = */ host->node);
+#if 0
+ /* port = */ host->port);
+#endif
+
+ status = modbus_connect (&host->connection);
+ if (status != 0)
+ {
+ ERROR ("Modbus plugin: modbus_connect (%s, %i) failed with status %i.",
+ host->node, host->port, status);
+ return (status);
+ }
+
+ host->is_connected = 1;
+ host->have_reconnected = 1;
+ return (0);
+} /* }}} int mb_init_connection */
+
+#define CAST_TO_VALUE_T(ds,vt,raw) do { \
+ if ((ds)->ds[0].type == DS_TYPE_COUNTER) \
+ (vt).counter = (counter_t) (raw); \
+ else if ((ds)->ds[0].type == DS_TYPE_GAUGE) \
+ (vt).gauge = (gauge_t) (raw); \
+ else if ((ds)->ds[0].type == DS_TYPE_DERIVE) \
+ (vt).derive = (derive_t) (raw); \
+ else /* if (ds->ds[0].type == DS_TYPE_ABSOLUTE) */ \
+ (vt).absolute = (absolute_t) (raw); \
+} while (0)
+
+static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
+ mb_data_t *data)
+{
+ int values[2];
+ int values_num;
+ const data_set_t *ds;
+ int status;
+ int i;
+
+ if ((host == NULL) || (slave == NULL) || (data == NULL))
+ return (EINVAL);
+
+ ds = plugin_get_ds (data->type);
+ if (ds == NULL)
+ {
+ ERROR ("Modbus plugin: Type \"%s\" is not defined.", data->type);
+ return (-1);
+ }
+
+ if (ds->ds_num != 1)
+ {
+ ERROR ("Modbus plugin: The type \"%s\" has %i data sources. "
+ "I can only handle data sets with only one data source.",
+ data->type, ds->ds_num);
+ return (-1);
+ }
+
+ if ((ds->ds[0].type != DS_TYPE_GAUGE)
+ && (data->register_type != REG_TYPE_UINT32))
+ {
+ NOTICE ("Modbus plugin: The data source of type \"%s\" is %s, not gauge. "
+ "This will most likely result in problems, because the register type "
+ "is not UINT32.", data->type, DS_TYPE_TO_STRING (ds->ds[0].type));
+ }
+
+ memset (values, 0, sizeof (values));
+ if ((data->register_type == REG_TYPE_UINT32)
+ || (data->register_type == REG_TYPE_FLOAT))
+ values_num = 2;
+ else
+ values_num = 1;
+
+ for (i = 0; i < 2; i++)
+ {
+ status = read_holding_registers (&host->connection,
+ /* slave = */ slave->id, /* start_addr = */ data->register_base,
+ /* num_registers = */ values_num, /* buffer = */ values);
+ if (status > 0)
+ break;
+
+ if (host->is_connected)
+ modbus_close (&host->connection);
+ host->is_connected = 0;
+
+ /* If we already tried reconnecting this round, give up. */
+ if (host->have_reconnected)
+ {
+ ERROR ("Modbus plugin: read_holding_registers (%s) failed. "
+ "Reconnecting has already been tried. Giving up.", host->host);
+ return (-1);
+ }
+
+ /* Maybe the device closed the connection during the waiting interval.
+ * Try re-establishing the connection. */
+ status = mb_init_connection (host);
+ if (status != 0)
+ {
+ ERROR ("Modbus plugin: read_holding_registers (%s) failed. "
+ "While trying to reconnect, connecting to \"%s\" failed. "
+ "Giving up.",
+ host->host, host->node);
+ return (-1);
+ }
+
+ DEBUG ("Modbus plugin: Re-established connection to %s", host->host);
+
+ /* try again */
+ continue;
+ } /* for (i = 0, 1) */
+
+ DEBUG ("Modbus plugin: mb_read_data: Success! "
+ "read_holding_registers returned with status %i.", status);
+
+ if (data->register_type == REG_TYPE_FLOAT)
+ {
+ float float_value;
+ value_t vt;
+
+ float_value = mb_register_to_float (values[0], values[1]);
+ DEBUG ("Modbus plugin: mb_read_data: "
+ "Returned float value is %g", (double) float_value);
+
+ CAST_TO_VALUE_T (ds, vt, float_value);
+ mb_submit (host, slave, data, vt);
+ }
+ else if (data->register_type == REG_TYPE_UINT32)
+ {
+ uint32_t v32;
+ value_t vt;
+
+ v32 = (values[0] << 16) | values[1];
+ DEBUG ("Modbus plugin: mb_read_data: "
+ "Returned uint32 value is %"PRIu32, v32);
+
+ CAST_TO_VALUE_T (ds, vt, v32);
+ mb_submit (host, slave, data, vt);
+ }
+ else /* if (data->register_type == REG_TYPE_UINT16) */
+ {
+ value_t vt;
+
+ DEBUG ("Modbus plugin: mb_read_data: "
+ "Returned uint16 value is %"PRIu16, values[0]);
+
+ CAST_TO_VALUE_T (ds, vt, values[0]);
+ mb_submit (host, slave, data, vt);
+ }
+
+ return (0);
+} /* }}} int mb_read_data */
+
+static int mb_read_slave (mb_host_t *host, mb_slave_t *slave) /* {{{ */
+{
+ mb_data_t *data;
+ int success;
+ int status;
+
+ if ((host == NULL) || (slave == NULL))
+ return (EINVAL);
+
+ success = 0;
+ for (data = slave->collect; data != NULL; data = data->next)
+ {
+ status = mb_read_data (host, slave, data);
+ if (status == 0)
+ success++;
+ }
+
+ if (success == 0)
+ return (-1);
+ else
+ return (0);
+} /* }}} int mb_read_slave */
+
+static int mb_read (user_data_t *user_data) /* {{{ */
+{
+ mb_host_t *host;
+ size_t i;
+ int success;
+ int status;
+
+ if ((user_data == NULL) || (user_data->data == NULL))
+ return (EINVAL);
+
+ host = user_data->data;
+
+ /* Clear the reconnect flag. */
+ host->have_reconnected = 0;
+
+ success = 0;
+ for (i = 0; i < host->slaves_num; i++)
+ {
+ status = mb_read_slave (host, host->slaves + i);
+ if (status == 0)
+ success++;
+ }
+
+ if (success == 0)
+ return (-1);
+ else
+ return (0);
+} /* }}} int mb_read */
+
+/* Free functions */
+
+static void data_free_one (mb_data_t *data) /* {{{ */
+{
+ if (data == NULL)
+ return;
+
+ sfree (data->name);
+ sfree (data);
+} /* }}} void data_free_one */
+
+static void data_free_all (mb_data_t *data) /* {{{ */
+{
+ mb_data_t *next;
+
+ if (data == NULL)
+ return;
+
+ next = data->next;
+ data_free_one (data);
+
+ data_free_all (next);
+} /* }}} void data_free_all */
+
+static void slaves_free_all (mb_slave_t *slaves, size_t slaves_num) /* {{{ */
+{
+ size_t i;
+
+ if (slaves == NULL)
+ return;
+
+ for (i = 0; i < slaves_num; i++)
+ data_free_all (slaves[i].collect);
+ sfree (slaves);
+} /* }}} void slaves_free_all */
+
+static void host_free (void *void_host) /* {{{ */
+{
+ mb_host_t *host = void_host;
+
+ if (host == NULL)
+ return;
+
+ slaves_free_all (host->slaves, host->slaves_num);
+ sfree (host);
+} /* }}} void host_free */
+
+/* Config functions */
+
+static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */
+{
+ mb_data_t data;
+ int status;
+ int i;
+
+ memset (&data, 0, sizeof (data));
+ data.name = NULL;
+ data.register_type = REG_TYPE_UINT16;
+ data.next = NULL;
+
+ status = cf_util_get_string (ci, &data.name);
+ if (status != 0)
+ return (status);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ status = 0;
+
+ if (strcasecmp ("Type", child->key) == 0)
+ status = cf_util_get_string_buffer (child,
+ data.type, sizeof (data.type));
+ else if (strcasecmp ("Instance", child->key) == 0)
+ status = cf_util_get_string_buffer (child,
+ data.instance, sizeof (data.instance));
+ else if (strcasecmp ("RegisterBase", child->key) == 0)
+ status = cf_util_get_int (child, &data.register_base);
+ else if (strcasecmp ("RegisterType", child->key) == 0)
+ {
+ char tmp[16];
+ status = cf_util_get_string_buffer (child, tmp, sizeof (tmp));
+ if (status != 0)
+ /* do nothing */;
+ else if (strcasecmp ("Uint16", tmp) == 0)
+ data.register_type = REG_TYPE_UINT16;
+ else if (strcasecmp ("Uint32", tmp) == 0)
+ data.register_type = REG_TYPE_UINT32;
+ else if (strcasecmp ("Float", tmp) == 0)
+ data.register_type = REG_TYPE_FLOAT;
+ else
+ {
+ ERROR ("Modbus plugin: The register type \"%s\" is unknown.", tmp);
+ status = -1;
+ }
+ }
+ else
+ {
+ ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ assert (data.name != NULL);
+ if (data.type[0] == 0)
+ {
+ ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
+ data.name);
+ status = -1;
+ }
+
+ if (status == 0)
+ data_copy (&data_definitions, &data);
+
+ sfree (data.name);
+
+ return (status);
+} /* }}} int mb_config_add_data */
+
+static int mb_config_set_host_address (mb_host_t *host, /* {{{ */
+ const char *address)
+{
+ struct addrinfo *ai_list;
+ struct addrinfo *ai_ptr;
+ struct addrinfo ai_hints;
+ int status;
+
+ if ((host == NULL) || (address == NULL))
+ return (EINVAL);
+
+ memset (&ai_hints, 0, sizeof (ai_hints));
+#if AI_ADDRCONFIG
+ ai_hints.ai_flags |= AI_ADDRCONFIG;
+#endif
+ /* XXX: libmodbus can only handle IPv4 addresses. */
+ ai_hints.ai_family = AF_INET;
+ ai_hints.ai_addr = NULL;
+ ai_hints.ai_canonname = NULL;
+ ai_hints.ai_next = NULL;
+
+ ai_list = NULL;
+ status = getaddrinfo (address, /* service = */ NULL,
+ &ai_hints, &ai_list);
+ if (status != 0)
+ {
+ char errbuf[1024];
+ ERROR ("Modbus plugin: getaddrinfo failed: %s",
+ (status == EAI_SYSTEM)
+ ? sstrerror (errno, errbuf, sizeof (errbuf))
+ : gai_strerror (status));
+ return (status);
+ }
+
+ for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+ {
+ status = getnameinfo (ai_ptr->ai_addr, ai_ptr->ai_addrlen,
+ host->node, sizeof (host->node),
+ /* service = */ NULL, /* length = */ 0,
+ /* flags = */ NI_NUMERICHOST);
+ if (status == 0)
+ break;
+ } /* for (ai_ptr) */
+
+ freeaddrinfo (ai_list);
+
+ if (status != 0)
+ ERROR ("Modbus plugin: Unable to translate node name: \"%s\"", address);
+ else /* if (status == 0) */
+ {
+ DEBUG ("Modbus plugin: mb_config_set_host_address: %s -> %s",
+ address, host->node);
+ }
+
+ return (status);
+} /* }}} int mb_config_set_host_address */
+
+static int mb_config_add_slave (mb_host_t *host, oconfig_item_t *ci) /* {{{ */
+{
+ mb_slave_t *slave;
+ int status;
+ int i;
+
+ if ((host == NULL) || (ci == NULL))
+ return (EINVAL);
+
+ slave = realloc (host->slaves, sizeof (*slave) * (host->slaves_num + 1));
+ if (slave == NULL)
+ return (ENOMEM);
+ host->slaves = slave;
+ slave = host->slaves + host->slaves_num;
+ memset (slave, 0, sizeof (*slave));
+ slave->collect = NULL;
+
+ status = cf_util_get_int (ci, &slave->id);
+ if (status != 0)
+ return (status);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ status = 0;
+
+ if (strcasecmp ("Instance", child->key) == 0)
+ status = cf_util_get_string_buffer (child,
+ slave->instance, sizeof (slave->instance));
+ else if (strcasecmp ("Collect", child->key) == 0)
+ {
+ char buffer[1024];
+ status = cf_util_get_string_buffer (child, buffer, sizeof (buffer));
+ if (status == 0)
+ data_copy_by_name (&slave->collect, data_definitions, buffer);
+ status = 0; /* continue after failure. */
+ }
+ else
+ {
+ ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ }
+
+ if ((status == 0) && (slave->collect == NULL))
+ status = EINVAL;
+
+ if (slave->id < 0)
+ status = EINVAL;
+
+ if (status == 0)
+ host->slaves_num++;
+ else /* if (status != 0) */
+ data_free_all (slave->collect);
+
+ return (status);
+} /* }}} int mb_config_add_slave */
+
+static int mb_config_add_host (oconfig_item_t *ci) /* {{{ */
+{
+ mb_host_t *host;
+ int status;
+ int i;
+
+ host = malloc (sizeof (*host));
+ if (host == NULL)
+ return (ENOMEM);
+ memset (host, 0, sizeof (*host));
+ host->slaves = NULL;
+
+ status = cf_util_get_string_buffer (ci, host->host, sizeof (host->host));
+ if (status != 0)
+ return (status);
+ if (host->host[0] == 0)
+ return (EINVAL);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+ status = 0;
+
+ if (strcasecmp ("Address", child->key) == 0)
+ {
+ char buffer[NI_MAXHOST];
+ status = cf_util_get_string_buffer (child, buffer, sizeof (buffer));
+ if (status == 0)
+ status = mb_config_set_host_address (host, buffer);
+ }
+ else if (strcasecmp ("Port", child->key) == 0)
+ {
+ host->port = cf_util_get_port_number (child);
+ if (host->port <= 0)
+ status = -1;
+ }
+ else if (strcasecmp ("Interval", child->key) == 0)
+ status = cf_util_get_int (child, &host->interval);
+ else if (strcasecmp ("Slave", child->key) == 0)
+ /* Don't set status: Gracefully continue if a slave fails. */
+ mb_config_add_slave (host, child);
+ else
+ {
+ ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+ status = -1;
+ }
+
+ if (status != 0)
+ break;
+ } /* for (i = 0; i < ci->children_num; i++) */
+
+ assert (host->host[0] != 0);
+ if (host->host[0] == 0)
+ {
+ ERROR ("Modbus plugin: Data block \"%s\": No type has been specified.",
+ host->host);
+ status = -1;
+ }
+
+ if (status == 0)
+ {
+ user_data_t ud;
+ char name[1024];
+ struct timespec interval;
+
+ ud.data = host;
+ ud.free_func = host_free;
+
+ ssnprintf (name, sizeof (name), "modbus-%s", host->host);
+
+ interval.tv_nsec = 0;
+ if (host->interval > 0)
+ interval.tv_sec = host->interval;
+ else
+ interval.tv_sec = 0;
+
+ plugin_register_complex_read (/* group = */ NULL, name,
+ mb_read, (interval.tv_sec > 0) ? &interval : NULL, &ud);
+ }
+ else
+ {
+ host_free (host);
+ }
+
+ return (status);
+} /* }}} int mb_config_add_host */
+
+static int mb_config (oconfig_item_t *ci) /* {{{ */
+{
+ int i;
+
+ if (ci == NULL)
+ return (EINVAL);
+
+ for (i = 0; i < ci->children_num; i++)
+ {
+ oconfig_item_t *child = ci->children + i;
+
+ if (strcasecmp ("Data", child->key) == 0)
+ mb_config_add_data (child);
+ else if (strcasecmp ("Host", child->key) == 0)
+ mb_config_add_host (child);
+ else
+ ERROR ("Modbus plugin: Unknown configuration option: %s", child->key);
+ }
+
+ return (0);
+} /* }}} int mb_config */
+
+/* ========= */
+
+static int mb_shutdown (void) /* {{{ */
+{
+ data_free_all (data_definitions);
+ data_definitions = NULL;
+
+ return (0);
+} /* }}} int mb_shutdown */
+
+void module_register (void)
+{
+ plugin_register_complex_config ("modbus", mb_config);
+ plugin_register_shutdown ("modbus", mb_shutdown);
+} /* void module_register */
+
+/* vim: set sw=2 sts=2 et fdm=marker : */
else
sstrncpy (cb_name, "mysql", sizeof (cb_name));
- plugin_register_complex_read (cb_name, mysql_read,
+ plugin_register_complex_read (/* group = */ NULL, cb_name,
+ mysql_read,
/* interval = */ NULL, &ud);
}
else
}
else
{
- INFO ("mysql plugin: Sucessfully connected to database %s "
+ INFO ("mysql plugin: Successfully connected to database %s "
"at server %s (server version: %s, protocol version: %d)",
(db->database != NULL) ? db->database : "<none>",
mysql_get_host_info (db->con),
{
ERROR ("netapp plugin: cna_handle_volume_snap_usage: "
"na_elem_child (\"snapshots\") failed "
- "for host %s.", hostname);
+ "for host %s.", host->name);
na_elem_free(data);
return;
}
{
ERROR ("netapp plugin: cna_handle_volume_usage_data: "
"na_elem_child (\"volumes\") failed "
- "for host %s.", hostname);
+ "for host %s.", host->name);
return (-1);
}
ud.data = host;
ud.free_func = (void (*) (void *)) free_host_config;
- plugin_register_complex_read (cb_name,
+ plugin_register_complex_read (/* group = */ NULL, cb_name,
/* callback = */ cna_read,
/* interval = */ (host->interval > 0) ? &interval : NULL,
/* user data = */ &ud);
#if HAVE_POLL_H
# include <poll.h>
#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
#if HAVE_LIBGCRYPT
# include <gcrypt.h>
char *node;
char *service;
+ int interface;
union
{
return (!received);
} /* }}} _Bool check_send_okay */
-static int network_dispatch_values (value_list_t *vl) /* {{{ */
+static int network_dispatch_values (value_list_t *vl, /* {{{ */
+ const char *username)
{
int status;
return (status);
}
+ if (username != NULL)
+ {
+ status = meta_data_add_string (vl->meta, "network:username", username);
+ if (status != 0)
+ {
+ ERROR ("network plugin: meta_data_add_string failed.");
+ meta_data_destroy (vl->meta);
+ vl->meta = NULL;
+ return (status);
+ }
+ }
+
plugin_dispatch_values (vl);
stats_values_dispatched++;
break;
default:
- sfree (pkg_types);
- sfree (pkg_values);
NOTICE ("network plugin: parse_part_values: "
"Don't know how to handle data source type %"PRIu8,
pkg_types[i]);
+ sfree (pkg_types);
+ sfree (pkg_values);
return (-1);
} /* switch (pkg_types[i]) */
}
#define PP_SIGNED 0x01
#define PP_ENCRYPTED 0x02
static int parse_packet (sockent_t *se,
- void *buffer, size_t buffer_size, int flags);
+ void *buffer, size_t buffer_size, int flags,
+ const char *username);
#define BUFFER_READ(p,s) do { \
memcpy ((p), buffer + buffer_offset, (s)); \
{
ERROR ("network plugin: gcry_md_setkey failed: %s", gcry_strerror (err));
gcry_md_close (hd);
+ sfree (secret);
+ sfree (pss.username);
return (-1);
}
gcry_md_close (hd);
hd = NULL;
- sfree (secret);
- sfree (pss.username);
-
if (memcmp (pss.hash, hash, sizeof (pss.hash)) != 0)
{
WARNING ("network plugin: Verifying HMAC-SHA-256 signature failed: "
else
{
parse_packet (se, buffer + buffer_offset, buffer_len - buffer_offset,
- flags | PP_SIGNED);
+ flags | PP_SIGNED, pss.username);
}
+ sfree (secret);
+ sfree (pss.username);
+
*ret_buffer = buffer + buffer_len;
*ret_buffer_len = 0;
warning_has_been_printed = 1;
}
- parse_packet (se, buffer + part_len, buffer_size - part_len, flags);
+ parse_packet (se, buffer + part_len, buffer_size - part_len, flags,
+ /* username = */ NULL);
*ret_buffer = buffer + buffer_size;
*ret_buffer_size = 0;
cypher = network_get_aes256_cypher (se, pea.iv, sizeof (pea.iv),
pea.username);
if (cypher == NULL)
+ {
+ sfree (pea.username);
return (-1);
+ }
payload_len = part_size - (PART_ENCRYPTION_AES256_SIZE + username_len);
assert (payload_len > 0);
/* in = */ NULL, /* in len = */ 0);
if (err != 0)
{
+ sfree (pea.username);
ERROR ("network plugin: gcry_cipher_decrypt returned: %s",
gcry_strerror (err));
return (-1);
buffer + buffer_offset, payload_len);
if (memcmp (hash, pea.hash, sizeof (hash)) != 0)
{
+ sfree (pea.username);
ERROR ("network plugin: Decryption failed: Checksum mismatch.");
return (-1);
}
parse_packet (se, buffer + buffer_offset, payload_len,
- flags | PP_ENCRYPTED);
+ flags | PP_ENCRYPTED, pea.username);
+
+ /* XXX: Free pea.username?!? */
/* Update return values */
*ret_buffer = buffer + part_size;
*ret_buffer_len = buffer_len - part_size;
+ sfree (pea.username);
+
return (0);
} /* }}} int parse_part_encr_aes256 */
/* #endif HAVE_LIBGCRYPT */
#undef BUFFER_READ
static int parse_packet (sockent_t *se, /* {{{ */
- void *buffer, size_t buffer_size, int flags)
+ void *buffer, size_t buffer_size, int flags,
+ const char *username)
{
int status;
if (status != 0)
break;
- network_dispatch_values (&vl);
+ network_dispatch_values (&vl, username);
sfree (vl.values);
}
if (setsockopt (se->data.client.fd, IPPROTO_IP, optname,
&network_config_ttl,
- sizeof (network_config_ttl)) == -1)
+ sizeof (network_config_ttl)) != 0)
{
char errbuf[1024];
ERROR ("setsockopt: %s",
if (setsockopt (se->data.client.fd, IPPROTO_IPV6, optname,
&network_config_ttl,
- sizeof (network_config_ttl)) == -1)
+ sizeof (network_config_ttl)) != 0)
{
char errbuf[1024];
ERROR ("setsockopt: %s",
return (0);
} /* int network_set_ttl */
-static int network_bind_socket (int fd, const struct addrinfo *ai)
+static int network_set_interface (const sockent_t *se, const struct addrinfo *ai) /* {{{ */
+{
+ DEBUG ("network plugin: network_set_interface: interface index = %i;",
+ se->interface);
+
+ assert (se->type == SOCKENT_TYPE_CLIENT);
+
+ if (ai->ai_family == AF_INET)
+ {
+ struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
+
+ if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
+ {
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ /* If possible, use the "ip_mreqn" structure which has
+ * an "interface index" member. Using the interface
+ * index is preferred here, because of its similarity
+ * to the way IPv6 handles this. Unfortunately, it
+ * appears not to be portable. */
+ struct ip_mreqn mreq;
+
+ memset (&mreq, 0, sizeof (mreq));
+ mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+ mreq.imr_address.s_addr = ntohl (INADDR_ANY);
+ mreq.imr_ifindex = se->interface;
+#else
+ struct ip_mreq mreq;
+
+ memset (&mreq, 0, sizeof (mreq));
+ mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
+ mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
+#endif
+
+ if (setsockopt (se->data.client.fd, IPPROTO_IP, IP_MULTICAST_IF,
+ &mreq, sizeof (mreq)) != 0)
+ {
+ char errbuf[1024];
+ ERROR ("setsockopt: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+
+ return (0);
+ }
+ }
+ else if (ai->ai_family == AF_INET6)
+ {
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
+
+ if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
+ {
+ if (setsockopt (se->data.client.fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &se->interface,
+ sizeof (se->interface)) != 0)
+ {
+ char errbuf[1024];
+ ERROR ("setsockopt: %s",
+ sstrerror (errno, errbuf,
+ sizeof (errbuf)));
+ return (-1);
+ }
+
+ return (0);
+ }
+ }
+
+ /* else: Not a multicast interface. */
+#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
+ if (se->interface != 0)
+ {
+ char interface_name[IFNAMSIZ];
+
+ if (if_indextoname (se->interface, interface_name) == NULL)
+ return (-1);
+
+ DEBUG ("network plugin: Binding socket to interface %s", interface_name);
+
+ if (setsockopt (se->data.client.fd, SOL_SOCKET, SO_BINDTODEVICE,
+ interface_name,
+ sizeof(interface_name)) == -1 )
+ {
+ char errbuf[1024];
+ ERROR ("setsockopt: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ }
+/* #endif HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
+
+#else
+ WARNING ("network plugin: Cannot set the interface on a unicast "
+ "socket because "
+# if !defined(SO_BINDTODEVICE)
+ "the the \"SO_BINDTODEVICE\" socket option "
+# else
+ "the \"if_indextoname\" function "
+# endif
+ "is not available on your system.");
+#endif
+
+ return (0);
+} /* }}} network_set_interface */
+
+static int network_bind_socket (int fd, const struct addrinfo *ai, const int interface_idx)
{
int loop = 0;
int yes = 1;
struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
{
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ struct ip_mreqn mreq;
+#else
struct ip_mreq mreq;
+#endif
DEBUG ("fd = %i; IPv4 multicast address found", fd);
mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
- mreq.imr_interface.s_addr = htonl (INADDR_ANY);
+#if HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
+ /* Set the interface using the interface index if
+ * possible (available). Unfortunately, the struct
+ * ip_mreqn is not portable. */
+ mreq.imr_address.s_addr = ntohl (INADDR_ANY);
+ mreq.imr_ifindex = interface_idx;
+#else
+ mreq.imr_interface.s_addr = ntohl (INADDR_ANY);
+#endif
if (setsockopt (fd, IPPROTO_IP, IP_MULTICAST_LOOP,
&loop, sizeof (loop)) == -1)
sizeof (errbuf)));
return (-1);
}
+
+ return (0);
}
}
else if (ai->ai_family == AF_INET6)
* single interface; programs running on
* multihomed hosts may need to join the same
* group on more than one interface.*/
- mreq.ipv6mr_interface = 0;
+ mreq.ipv6mr_interface = interface_idx;
if (setsockopt (fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
&loop, sizeof (loop)) == -1)
sizeof (errbuf)));
return (-1);
}
+
+ return (0);
}
}
+#if defined(HAVE_IF_INDEXTONAME) && HAVE_IF_INDEXTONAME && defined(SO_BINDTODEVICE)
+ /* if a specific interface was set, bind the socket to it. But to avoid
+ * possible problems with multicast routing, only do that for non-multicast
+ * addresses */
+ if (interface_idx != 0)
+ {
+ char interface_name[IFNAMSIZ];
+
+ if (if_indextoname (interface_idx, interface_name) == NULL)
+ return (-1);
+
+ DEBUG ("fd = %i; Binding socket to interface %s", fd, interface_name);
+
+ if (setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE,
+ interface_name,
+ sizeof(interface_name)) == -1 )
+ {
+ char errbuf[1024];
+ ERROR ("setsockopt: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ }
+#endif /* HAVE_IF_INDEXTONAME && SO_BINDTODEVICE */
+
return (0);
} /* int network_bind_socket */
se->type = SOCKENT_TYPE_CLIENT;
se->node = NULL;
se->service = NULL;
+ se->interface = 0;
se->next = NULL;
if (type == SOCKENT_TYPE_SERVER)
continue;
}
- status = network_bind_socket (*tmp, ai_ptr);
+ status = network_bind_socket (*tmp, ai_ptr, se->interface);
if (status != 0)
{
close (*tmp);
se->data.client.addrlen = ai_ptr->ai_addrlen;
network_set_ttl (se, ai_ptr);
+ network_set_interface (se, ai_ptr);
/* We don't open more than one write-socket per
* node/service pair.. */
continue;
}
- parse_packet (se, ent->data, ent->data_len, /* flags = */ 0);
+ parse_packet (se, ent->data, ent->data_len, /* flags = */ 0,
+ /* username = */ NULL);
sfree (ent->data);
sfree (ent);
} /* while (42) */
return (0);
} /* }}} int network_config_set_ttl */
+static int network_config_set_interface (const oconfig_item_t *ci, /* {{{ */
+ int *interface)
+{
+ if ((ci->values_num != 1)
+ || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ {
+ WARNING ("network plugin: The `Interface' config option needs exactly "
+ "one string argument.");
+ return (-1);
+ }
+
+ if (interface == NULL)
+ return (-1);
+
+ *interface = if_nametoindex (ci->values[0].value.string);
+
+ return (0);
+} /* }}} int network_config_set_interface */
+
static int network_config_set_buffer_size (const oconfig_item_t *ci) /* {{{ */
{
int tmp;
&se->data.server.security_level);
else
#endif /* HAVE_LIBGCRYPT */
+ if (strcasecmp ("Interface", child->key) == 0)
+ network_config_set_interface (child,
+ &se->interface);
+ else
{
WARNING ("network plugin: Option `%s' is not allowed here.",
child->key);
&se->data.client.security_level);
else
#endif /* HAVE_LIBGCRYPT */
+ if (strcasecmp ("Interface", child->key) == 0)
+ network_config_set_interface (child,
+ &se->interface);
+ else
{
WARNING ("network plugin: Option `%s' is not allowed here.",
child->key);
if (ow_interval > 0)
cb_interval.tv_sec = (time_t) ow_interval;
- plugin_register_complex_read ("onewire", cow_read,
+ plugin_register_complex_read (/* group = */ NULL, "onewire", cow_read,
&cb_interval, /* user data = */ NULL);
plugin_register_shutdown ("onewire", cow_shutdown);
* Copyright (C) 2008 Doug MacEachern
* Copyright (C) 2009 Florian octo Forster
* Copyright (C) 2009 Marco Chiappero
+ * Copyright (C) 2009 Fabian Schuh
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Doug MacEachern <dougm at hyperic.com>
* Florian octo Forster <octo at verplant.org>
* Marco Chiappero <marco at absence.it>
+ * Fabian Schuh <mail at xeroc.org>
**/
#include "collectd.h"
static vpn_status_t **vpn_list = NULL;
static int vpn_num = 0;
-static int store_compression = 1;
-static int new_naming_schema = 0;
+static _Bool new_naming_schema = 0;
+static _Bool collect_compression = 1;
+static _Bool collect_user_count = 0;
+static _Bool collect_individual_users = 1;
static const char *config_keys[] =
{
"StatusFile",
- "Compression",
- "ImprovedNamingSchema"
+ "Compression", /* old, deprecated name */
+ "ImprovedNamingSchema",
+ "CollectCompression",
+ "CollectUserCount",
+ "CollectIndividualUsers"
};
static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
return (i);
} /* int openvpn_strsplit */
+/* dispatches number of users */
+static void numusers_submit (char *pinst, char *tinst, gauge_t value)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].gauge = value;
+
+ vl.values = values;
+ vl.values_len = STATIC_ARRAY_SIZE (values);
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "openvpn", sizeof (vl.plugin));
+ sstrncpy (vl.type, "users", sizeof (vl.type));
+ if (pinst != NULL)
+ sstrncpy (vl.plugin_instance, pinst, sizeof (vl.plugin_instance));
+ if (tinst != NULL)
+ sstrncpy (vl.type_instance, tinst, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+} /* void numusers_submit */
+
/* dispatches stats about traffic (TCP or UDP) generated by the tunnel per single endpoint */
static void iostats_submit (char *pinst, char *tinst, counter_t rx, counter_t tx)
{
iostats_submit (name, "overhead", overhead_rx, overhead_tx);
- if (store_compression)
+ if (collect_compression)
{
compression_submit (name, "data_in", post_decompress, pre_decompress);
compression_submit (name, "data_out", pre_compress, post_compress);
char buffer[1024];
char *fields[10];
int fields_num, read = 0, found_header = 0;
+ long long sum_users = 0;
/* read the file until the "ROUTING TABLE" line is found (no more info after) */
while (fgets (buffer, sizeof (buffer), fh) != NULL)
if (fields_num < 4)
continue;
- if (new_naming_schema)
+ if (collect_user_count)
+ /* If so, sum all users, ignore the individuals*/
{
- iostats_submit (fields[0], /* "Common Name" */
- NULL, /* unused when in multimode */
- atoll (fields[2]), /* "Bytes Received" */
- atoll (fields[3])); /* "Bytes Sent" */
+ sum_users += 1;
}
- else
+ if (collect_individual_users)
{
- iostats_submit (name, /* vpn instance */
- fields[0], /* "Common Name" */
- atoll (fields[2]), /* "Bytes Received" */
- atoll (fields[3])); /* "Bytes Sent" */
+ if (new_naming_schema)
+ {
+ iostats_submit (name, /* vpn instance */
+ fields[0], /* "Common Name" */
+ atoll (fields[2]), /* "Bytes Received" */
+ atoll (fields[3])); /* "Bytes Sent" */
+ }
+ else
+ {
+ iostats_submit (fields[0], /* "Common Name" */
+ NULL, /* unused when in multimode */
+ atoll (fields[2]), /* "Bytes Received" */
+ atoll (fields[3])); /* "Bytes Sent" */
+ }
}
read = 1;
}
+ if (collect_user_count)
+ {
+ numusers_submit(name, name, sum_users);
+ read = 1;
+ }
+
return (read);
} /* int multi1_read */
char *fields[10];
const int max_fields = STATIC_ARRAY_SIZE (fields);
int fields_num, read = 0;
+ long long sum_users = 0;
while (fgets (buffer, sizeof (buffer), fh) != NULL)
{
if (strcmp (fields[0], "CLIENT_LIST") != 0)
continue;
- if (new_naming_schema)
+ if (collect_user_count)
+ /* If so, sum all users, ignore the individuals*/
{
- /* plugin inst = file name, type inst = fields[1] */
- iostats_submit (name, /* vpn instance */
- fields[1], /* "Common Name" */
- atoll (fields[4]), /* "Bytes Received" */
- atoll (fields[5])); /* "Bytes Sent" */
+ sum_users += 1;
}
- else
+ if (collect_individual_users)
{
- /* plugin inst = fields[1], type inst = "" */
- iostats_submit (fields[1], /* "Common Name" */
- NULL, /* unused when in multimode */
- atoll (fields[4]), /* "Bytes Received" */
- atoll (fields[5])); /* "Bytes Sent" */
+ if (new_naming_schema)
+ {
+ /* plugin inst = file name, type inst = fields[1] */
+ iostats_submit (name, /* vpn instance */
+ fields[1], /* "Common Name" */
+ atoll (fields[4]), /* "Bytes Received" */
+ atoll (fields[5])); /* "Bytes Sent" */
+ }
+ else
+ {
+ /* plugin inst = fields[1], type inst = "" */
+ iostats_submit (fields[1], /* "Common Name" */
+ NULL, /* unused when in multimode */
+ atoll (fields[4]), /* "Bytes Received" */
+ atoll (fields[5])); /* "Bytes Sent" */
+ }
}
read = 1;
}
+ if (collect_user_count)
+ {
+ numusers_submit(name, name, sum_users);
+ read = 1;
+ }
+
return (read);
} /* int multi2_read */
char *fields[15];
const int max_fields = STATIC_ARRAY_SIZE (fields);
int fields_num, read = 0;
+ long long sum_users = 0;
while (fgets (buffer, sizeof (buffer), fh) != NULL)
{
if (strcmp (fields[0], "CLIENT_LIST") != 0)
continue;
- if (new_naming_schema)
+ if (collect_user_count)
+ /* If so, sum all users, ignore the individuals*/
{
- iostats_submit (name, /* vpn instance */
- fields[1], /* "Common Name" */
- atoll (fields[4]), /* "Bytes Received" */
- atoll (fields[5])); /* "Bytes Sent" */
+ sum_users += 1;
}
- else
+
+ if (collect_individual_users)
{
- iostats_submit (fields[1], /* "Common Name" */
- NULL, /* unused when in multimode */
- atoll (fields[4]), /* "Bytes Received" */
- atoll (fields[5])); /* "Bytes Sent" */
+ if (new_naming_schema)
+ {
+ iostats_submit (name, /* vpn instance */
+ fields[1], /* "Common Name" */
+ atoll (fields[4]), /* "Bytes Received" */
+ atoll (fields[5])); /* "Bytes Sent" */
+ }
+ else
+ {
+ iostats_submit (fields[1], /* "Common Name" */
+ NULL, /* unused when in multimode */
+ atoll (fields[4]), /* "Bytes Received" */
+ atoll (fields[5])); /* "Bytes Sent" */
+ }
}
read = 1;
}
}
+ if (collect_user_count)
+ {
+ numusers_submit(name, name, sum_users);
+ read = 1;
+ }
+
return (read);
} /* int multi3_read */
{
char errbuf[1024];
WARNING ("openvpn plugin: fopen(%s) failed: %s", vpn_list[i]->file,
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
continue;
}
if (status_version == 0)
{
WARNING ("openvpn plugin: unable to detect status version, \
- discarding status file \"%s\".", value);
+ discarding status file \"%s\".", value);
return (1);
}
{
char errbuf[1024];
WARNING ("openvpn plugin: sstrdup failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
return (1);
}
{
char errbuf[1024];
ERROR ("openvpn plugin: malloc failed: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
+ sstrerror (errno, errbuf, sizeof (errbuf)));
sfree (temp->file);
sfree (temp);
DEBUG ("openvpn plugin: status file \"%s\" added", temp->file);
} /* if (strcasecmp ("StatusFile", key) == 0) */
- else if (strcasecmp ("Compression", key) == 0)
+ else if ((strcasecmp ("CollectCompression", key) == 0)
+ || (strcasecmp ("Compression", key) == 0)) /* old, deprecated name */
{
- if (IS_TRUE (value))
- store_compression = 1;
+ if (IS_FALSE (value))
+ collect_compression = 0;
else
- {
- store_compression = 0;
- DEBUG ("openvpn plugin: no 'compression statistcs' collected");
- }
- } /* if (strcasecmp ("Compression", key) == 0) */
+ collect_compression = 1;
+ } /* if (strcasecmp ("CollectCompression", key) == 0) */
else if (strcasecmp ("ImprovedNamingSchema", key) == 0)
{
if (IS_TRUE (value))
new_naming_schema = 0;
}
} /* if (strcasecmp ("ImprovedNamingSchema", key) == 0) */
+ else if (strcasecmp("CollectUserCount", key) == 0)
+ {
+ if (IS_TRUE(value))
+ collect_user_count = 1;
+ else
+ collect_user_count = 0;
+ } /* if (strcasecmp("CollectUserCount", key) == 0) */
+ else if (strcasecmp("CollectIndividualUsers", key) == 0)
+ {
+ if (IS_FALSE (value))
+ collect_individual_users = 0;
+ else
+ collect_individual_users = 1;
+ } /* if (strcasecmp("CollectIndividualUsers", key) == 0) */
else
{
return (-1);
return (0);
} /* int openvpn_shutdown */
+static int openvpn_init (void)
+{
+ if (!collect_individual_users
+ && !collect_compression
+ && !collect_user_count)
+ {
+ WARNING ("OpenVPN plugin: Neither `CollectIndividualUsers', "
+ "`CollectCompression', nor `CollectUserCount' is true. There's no "
+ "data left to collect.");
+ return (-1);
+ }
+
+ plugin_register_read ("openvpn", openvpn_read);
+ plugin_register_shutdown ("openvpn", openvpn_shutdown);
+
+ return (0);
+} /* int openvpn_init */
+
void module_register (void)
{
plugin_register_config ("openvpn", openvpn_config,
config_keys, config_keys_num);
- plugin_register_read ("openvpn", openvpn_read);
- plugin_register_shutdown ("openvpn", openvpn_shutdown);
+ plugin_register_init ("openvpn", openvpn_init);
} /* void module_register */
/* vim: set sw=2 ts=2 : */
char *username;
char *password;
+ udb_query_preparation_area_t **q_prep_areas;
udb_query_t **queries;
size_t queries_num;
static void o_database_free (o_database_t *db) /* {{{ */
{
+ size_t i;
+
if (db == NULL)
return;
sfree (db->password);
sfree (db->queries);
+ if (db->q_prep_areas != NULL)
+ for (i = 0; i < db->queries_num; ++i)
+ udb_query_delete_preparation_area (db->q_prep_areas[i]);
+ free (db->q_prep_areas);
+
sfree (db);
} /* }}} void o_database_free */
break;
} /* while (status == 0) */
+ while ((status == 0) && (db->queries_num > 0))
+ {
+ db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+ db->queries_num, sizeof (*db->q_prep_areas));
+
+ if (db->q_prep_areas == NULL)
+ {
+ WARNING ("oracle plugin: malloc failed");
+ status = -1;
+ break;
+ }
+
+ for (i = 0; i < db->queries_num; ++i)
+ {
+ db->q_prep_areas[i]
+ = udb_query_allocate_preparation_area (db->queries[i]);
+
+ if (db->q_prep_areas[i] == NULL)
+ {
+ WARNING ("oracle plugin: udb_query_allocate_preparation_area failed");
+ status = -1;
+ break;
+ }
+ }
+
+ break;
+ }
+
/* If all went well, add this query to the list of queries within the
* database structure. */
if (status == 0)
} /* }}} int o_init */
static int o_read_database_query (o_database_t *db, /* {{{ */
- udb_query_t *q)
+ udb_query_t *q, udb_query_preparation_area_t *prep_area)
{
char **column_names;
char **column_values;
} /* for (j = 1; j <= param_counter; j++) */
/* }}} End of the ``define'' stuff. */
- status = udb_query_prepare_result (q, hostname_g, /* plugin = */ "oracle",
- db->name, column_names, column_num);
+ status = udb_query_prepare_result (q, prep_area, hostname_g,
+ /* plugin = */ "oracle", db->name, column_names, column_num,
+ /* interval = */ -1);
if (status != 0)
{
ERROR ("oracle plugin: o_read_database_query (%s, %s): "
break;
}
- status = udb_query_handle_result (q, column_values);
+ status = udb_query_handle_result (q, prep_area, column_values);
if (status != 0)
{
WARNING ("oracle plugin: o_read_database_query (%s, %s): "
o_report_error ("o_read_database", "OCIAttrGet", oci_error);
return (-1);
}
- assert (server_handle != NULL);
- connection_status = 0;
- status = OCIAttrGet ((void *) server_handle, OCI_HTYPE_SERVER,
- (void *) &connection_status, /* size pointer = */ NULL,
- OCI_ATTR_SERVER_STATUS, oci_error);
- if (status != OCI_SUCCESS)
+ if (server_handle == NULL)
{
- o_report_error ("o_read_database", "OCIAttrGet", oci_error);
- return (-1);
+ connection_status = OCI_SERVER_NOT_CONNECTED;
+ }
+ else /* if (server_handle != NULL) */
+ {
+ connection_status = 0;
+ status = OCIAttrGet ((void *) server_handle, OCI_HTYPE_SERVER,
+ (void *) &connection_status, /* size pointer = */ NULL,
+ OCI_ATTR_SERVER_STATUS, oci_error);
+ if (status != OCI_SUCCESS)
+ {
+ o_report_error ("o_read_database", "OCIAttrGet", oci_error);
+ return (-1);
+ }
}
if (connection_status != OCI_SERVER_NORMAL)
db->connect_id, db->oci_service_context);
for (i = 0; i < db->queries_num; i++)
- o_read_database_query (db, db->queries[i]);
+ o_read_database_query (db, db->queries[i], db->q_prep_areas[i]);
return (0);
} /* }}} int o_read_database */
t = GET_TARGET(e);
printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
- int pos = *(int *)t->data;
+ const unsigned char *data = t->data;
+ int pos = *(const int *)data;
if (pos < 0)
printf("verdict=%s\n",
pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
t = ip6t_get_target(e);
printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
- int pos = *(int *)t->data;
+ const unsigned char *data = t->data;
+ int pos = *(const int *)data;
if (pos < 0)
printf("verdict=%s\n",
pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
* to be called from specific places within the parser */
static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
{
+ const unsigned char *data;
+
if (h->chain_iterator_cur) {
/* policy rule is last rule */
struct rule_head *pr = (struct rule_head *)
h->chain_iterator_cur->rules.prev;
/* save verdict */
- h->chain_iterator_cur->verdict =
- *(int *)GET_TARGET(pr->entry)->data;
+ data = GET_TARGET(pr->entry)->data;
+ h->chain_iterator_cur->verdict = *(const int *)data;
/* save counter and counter_map information */
h->chain_iterator_cur->counter_map.maptype =
{
STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
struct rule_head *r = container_of(e, struct rule_head, entry[0]);
+ const unsigned char *data;
iptc_fn = TC_GET_TARGET;
return r->jump->name;
break;
case IPTCC_R_STANDARD:
- spos = *(int *)GET_TARGET(e)->data;
+ data = GET_TARGET(e)->data;
+ spos = *(const int *)data;
DEBUGP("r=%p, spos=%d'\n", r, spos);
return standard_target_map(spos);
break;
count = 0;
for (hl = hostlist_head; hl != NULL; hl = hl->next)
{
- int status;
- status = ping_host_add (pingobj, hl->host);
- if (status != 0)
- WARNING ("ping plugin: ping_host_add (%s) failed.", hl->host);
+ int tmp_status;
+ tmp_status = ping_host_add (pingobj, hl->host);
+ if (tmp_status != 0)
+ WARNING ("ping plugin: ping_host_add (%s) failed: %s",
+ hl->host, ping_get_error (pingobj));
else
count++;
}
if (gettimeofday (&tv_begin, NULL) < 0)
{
- ERROR ("ping plugin: gettimeofday failed");
+ char errbuf[1024];
+ ERROR ("ping plugin: gettimeofday failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
ping_thread_error = 1;
break;
}
if (gettimeofday (&tv_end, NULL) < 0)
{
- ERROR ("ping plugin: gettimeofday failed");
+ char errbuf[1024];
+ ERROR ("ping plugin: gettimeofday failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
ping_thread_error = 1;
break;
}
#define rf_callback rf_super.cf_callback
#define rf_udata rf_super.cf_udata
callback_func_t rf_super;
+ char rf_group[DATA_MAX_NAME_LEN];
char rf_name[DATA_MAX_NAME_LEN];
int rf_type;
struct timespec rf_interval;
return (0);
}
+static _Bool timeout_reached(struct timespec timeout)
+{
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return (now.tv_sec >= timeout.tv_sec && now.tv_usec >= (timeout.tv_nsec / 1000));
+}
+
static void *plugin_read_thread (void __attribute__((unused)) *args)
{
while (read_loop != 0)
struct timeval now;
int status;
int rf_type;
+ int rc;
/* Get the read function that needs to be read next. */
rf = c_heap_get_root (read_heap);
/* sleep until this entry is due,
* using pthread_cond_timedwait */
pthread_mutex_lock (&read_lock);
- pthread_cond_timedwait (&read_cond, &read_lock,
+ /* In pthread_cond_timedwait, spurious wakeups are possible
+ * (and really happen, at least on NetBSD with > 1 CPU), thus
+ * we need to re-evaluate the condition every time
+ * pthread_cond_timedwait returns. */
+ rc = 0;
+ while (!timeout_reached(rf->rf_next_read) && rc == 0) {
+ rc = pthread_cond_timedwait (&read_cond, &read_lock,
&rf->rf_next_read);
+ }
+
/* Must hold `real_lock' when accessing `rf->rf_type'. */
rf_type = rf->rf_type;
pthread_mutex_unlock (&read_lock);
rf->rf_callback = (void *) callback;
rf->rf_udata.data = NULL;
rf->rf_udata.free_func = NULL;
+ rf->rf_group[0] = '\0';
sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
rf->rf_type = RF_SIMPLE;
rf->rf_interval.tv_sec = 0;
return (plugin_insert_read (rf));
} /* int plugin_register_read */
-int plugin_register_complex_read (const char *name,
+int plugin_register_complex_read (const char *group, const char *name,
plugin_read_cb callback,
const struct timespec *interval,
user_data_t *user_data)
memset (rf, 0, sizeof (read_func_t));
rf->rf_callback = (void *) callback;
+ if (group != NULL)
+ sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
+ else
+ rf->rf_group[0] = '\0';
sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
rf->rf_type = RF_COMPLEX;
if (interval != NULL)
return (0);
} /* }}} int plugin_unregister_read */
+static int compare_read_func_group (llentry_t *e, void *ud) /* {{{ */
+{
+ read_func_t *rf = e->value;
+ char *group = ud;
+
+ return strcmp (rf->rf_group, (const char *)group);
+} /* }}} int compare_read_func_group */
+
+int plugin_unregister_read_group (const char *group) /* {{{ */
+{
+ llentry_t *le;
+ read_func_t *rf;
+
+ int found = 0;
+
+ if (group == NULL)
+ return (-ENOENT);
+
+ pthread_mutex_lock (&read_lock);
+
+ if (read_list == NULL)
+ {
+ pthread_mutex_unlock (&read_lock);
+ return (-ENOENT);
+ }
+
+ while (42)
+ {
+ le = llist_search_custom (read_list,
+ compare_read_func_group, (void *)group);
+
+ if (le == NULL)
+ break;
+
+ ++found;
+
+ llist_remove (read_list, le);
+
+ rf = le->value;
+ assert (rf != NULL);
+ rf->rf_type = RF_REMOVE;
+
+ llentry_destroy (le);
+
+ DEBUG ("plugin_unregister_read_group: "
+ "Marked `%s' (group `%s') for removal.",
+ rf->rf_name, group);
+ }
+
+ pthread_mutex_unlock (&read_lock);
+
+ if (found == 0)
+ {
+ WARNING ("plugin_unregister_read_group: No such "
+ "group of read function: %s", group);
+ return (-ENOENT);
+ }
+
+ return (0);
+} /* }}} int plugin_unregister_read_group */
+
int plugin_unregister_write (const char *name)
{
return (plugin_unregister (list_write, name));
va_list ap;
llentry_t *le;
- if (list_log == NULL)
- {
- va_start (ap, format);
- vfprintf (stderr, format, ap);
- va_end (ap);
- return;
- }
-
#if !COLLECT_DEBUG
if (level >= LOG_DEBUG)
return;
msg[sizeof (msg) - 1] = '\0';
va_end (ap);
+ if (list_log == NULL)
+ {
+ fprintf (stderr, "%s\n", msg);
+ return;
+ }
+
le = llist_head (list_log);
while (le != NULL)
{
plugin_init_cb callback);
int plugin_register_read (const char *name,
int (*callback) (void));
-int plugin_register_complex_read (const char *name,
+int plugin_register_complex_read (const char *group, const char *name,
plugin_read_cb callback,
const struct timespec *interval,
user_data_t *user_data);
int plugin_unregister_complex_config (const char *name);
int plugin_unregister_init (const char *name);
int plugin_unregister_read (const char *name);
+int plugin_unregister_read_group (const char *group);
int plugin_unregister_write (const char *name);
int plugin_unregister_flush (const char *name);
int plugin_unregister_shutdown (const char *name);
* collectd - src/postgresql.c
* Copyright (C) 2008, 2009 Sebastian Harl
* Copyright (C) 2009 Florian Forster
+ * All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*
* Authors:
* Sebastian Harl <sh at tokkee.org>
int max_params_num;
/* user configuration */
+ udb_query_preparation_area_t **q_prep_areas;
udb_query_t **queries;
size_t queries_num;
+ int interval;
+
char *host;
char *port;
char *database;
static udb_query_t **queries = NULL;
static size_t queries_num = 0;
-static c_psql_database_t *databases = NULL;
-static int databases_num = 0;
-
static c_psql_database_t *c_psql_database_new (const char *name)
{
c_psql_database_t *db;
- ++databases_num;
- if (NULL == (databases = (c_psql_database_t *)realloc (databases,
- databases_num * sizeof (*databases)))) {
+ db = (c_psql_database_t *)malloc (sizeof (*db));
+ if (NULL == db) {
log_err ("Out of memory.");
- exit (5);
+ return NULL;
}
- db = databases + (databases_num - 1);
-
db->conn = NULL;
C_COMPLAIN_INIT (&db->conn_complaint);
db->max_params_num = 0;
+ db->q_prep_areas = NULL;
db->queries = NULL;
db->queries_num = 0;
+ db->interval = 0;
+
db->database = sstrdup (name);
db->host = NULL;
db->port = NULL;
return db;
} /* c_psql_database_new */
-static void c_psql_database_delete (c_psql_database_t *db)
+static void c_psql_database_delete (void *data)
{
+ size_t i;
+
+ c_psql_database_t *db = data;
+
PQfinish (db->conn);
db->conn = NULL;
+ if (db->q_prep_areas)
+ for (i = 0; i < db->queries_num; ++i)
+ udb_query_delete_preparation_area (db->q_prep_areas[i]);
+ free (db->q_prep_areas);
+
sfree (db->queries);
db->queries_num = 0;
return;
} /* c_psql_database_delete */
+static int c_psql_connect (c_psql_database_t *db)
+{
+ char conninfo[4096];
+ char *buf = conninfo;
+ int buf_len = sizeof (conninfo);
+ int status;
+
+ if (! db)
+ return -1;
+
+ status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
+ if (0 < status) {
+ buf += status;
+ buf_len -= status;
+ }
+
+ C_PSQL_PAR_APPEND (buf, buf_len, "host", db->host);
+ C_PSQL_PAR_APPEND (buf, buf_len, "port", db->port);
+ C_PSQL_PAR_APPEND (buf, buf_len, "user", db->user);
+ C_PSQL_PAR_APPEND (buf, buf_len, "password", db->password);
+ C_PSQL_PAR_APPEND (buf, buf_len, "sslmode", db->sslmode);
+ C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
+ C_PSQL_PAR_APPEND (buf, buf_len, "service", db->service);
+
+ db->conn = PQconnectdb (conninfo);
+ db->proto_version = PQprotocolVersion (db->conn);
+ return 0;
+} /* c_psql_connect */
+
static int c_psql_check_connection (c_psql_database_t *db)
{
+ _Bool init = 0;
+
+ if (! db->conn) {
+ init = 1;
+
+ /* trigger c_release() */
+ if (0 == db->conn_complaint.interval)
+ db->conn_complaint.interval = 1;
+
+ c_psql_connect (db);
+ }
+
/* "ping" */
PQclear (PQexec (db->conn, "SELECT 42;"));
}
db->proto_version = PQprotocolVersion (db->conn);
- if (3 > db->proto_version)
- log_warn ("Protocol version %d does not support parameters.",
- db->proto_version);
}
db->server_version = PQserverVersion (db->conn);
- c_release (LOG_INFO, &db->conn_complaint,
- "Successfully reconnected to database %s", PQdb (db->conn));
+ if (c_would_release (&db->conn_complaint)) {
+ char *server_host;
+ int server_version;
+
+ server_host = PQhost (db->conn);
+ server_version = PQserverVersion (db->conn);
+
+ c_do_release (LOG_INFO, &db->conn_complaint,
+ "Successfully %sconnected to database %s (user %s) "
+ "at server %s%s%s (server version: %d.%d.%d, "
+ "protocol version: %d, pid: %d)", init ? "" : "re",
+ PQdb (db->conn), PQuser (db->conn),
+ C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
+ C_PSQL_SERVER_VERSION3 (server_version),
+ db->proto_version, PQbackendPID (db->conn));
+
+ if (3 > db->proto_version)
+ log_warn ("Protocol version %d does not support parameters.",
+ db->proto_version);
+ }
return 0;
} /* c_psql_check_connection */
params[i] = db->user;
break;
case C_PSQL_PARAM_INTERVAL:
- ssnprintf (interval, sizeof (interval), "%i", interval_g);
+ ssnprintf (interval, sizeof (interval), "%i",
+ db->interval > 0 ? db->interval : interval_g);
params[i] = interval;
break;
default:
NULL, NULL, /* return text data */ 0);
} /* c_psql_exec_query_params */
-static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q)
+static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
+ udb_query_preparation_area_t *prep_area)
{
PGresult *res;
* `BAIL_OUT'. */
column_names[col] = PQfname (res, col);
if (NULL == column_names[col]) {
- log_err ("Failed to resolv name of column %i.", col);
+ log_err ("Failed to resolve name of column %i.", col);
BAIL_OUT (-1);
}
}
else
host = db->host;
- status = udb_query_prepare_result (q, host, "postgresql",
- db->database, column_names, (size_t) column_num);
+ status = udb_query_prepare_result (q, prep_area, host, "postgresql",
+ db->database, column_names, (size_t) column_num, db->interval);
if (0 != status) {
log_err ("udb_query_prepare_result failed with status %i.",
status);
if (col < column_num)
continue;
- status = udb_query_handle_result (q, column_values);
+ status = udb_query_handle_result (q, prep_area, column_values);
if (status != 0) {
log_err ("udb_query_handle_result failed with status %i.",
status);
}
} /* for (row = 0; row < rows_num; ++row) */
+ udb_query_finish_result (q, prep_area);
+
BAIL_OUT (0);
#undef BAIL_OUT
} /* c_psql_exec_query */
-static int c_psql_read (void)
+static int c_psql_read (user_data_t *ud)
{
+ c_psql_database_t *db;
+
int success = 0;
int i;
- for (i = 0; i < databases_num; ++i) {
- c_psql_database_t *db = databases + i;
+ if ((ud == NULL) || (ud->data == NULL)) {
+ log_err ("c_psql_read: Invalid user data.");
+ return -1;
+ }
- int j;
+ db = ud->data;
- assert (NULL != db->database);
+ assert (NULL != db->database);
- if (0 != c_psql_check_connection (db))
- continue;
+ if (0 != c_psql_check_connection (db))
+ return -1;
- for (j = 0; j < db->queries_num; ++j)
- {
- udb_query_t *q;
+ for (i = 0; i < db->queries_num; ++i)
+ {
+ udb_query_preparation_area_t *prep_area;
+ udb_query_t *q;
- q = db->queries[j];
+ prep_area = db->q_prep_areas[i];
+ q = db->queries[i];
- if ((0 != db->server_version)
+ if ((0 != db->server_version)
&& (udb_query_check_version (q, db->server_version) <= 0))
- continue;
-
- c_psql_exec_query (db, q);
- }
+ continue;
- ++success;
+ if (0 == c_psql_exec_query (db, q, prep_area))
+ success = 1;
}
if (! success)
static int c_psql_shutdown (void)
{
- int i;
-
- if ((NULL == databases) || (0 == databases_num))
- return 0;
-
- plugin_unregister_read ("postgresql");
- plugin_unregister_shutdown ("postgresql");
-
- for (i = 0; i < databases_num; ++i)
- c_psql_database_delete (databases + i);
-
- sfree (databases);
- databases_num = 0;
+ plugin_unregister_read_group ("postgresql");
udb_query_free (queries, queries_num);
queries = NULL;
return 0;
} /* c_psql_shutdown */
-static int c_psql_init (void)
-{
- int i;
-
- if ((NULL == databases) || (0 == databases_num))
- return 0;
-
- for (i = 0; i < databases_num; ++i) {
- c_psql_database_t *db = databases + i;
-
- char conninfo[4096];
- char *buf = conninfo;
- int buf_len = sizeof (conninfo);
- int status;
-
- char *server_host;
- int server_version;
-
- /* this will happen during reinitialization */
- if (NULL != db->conn) {
- c_psql_check_connection (db);
- continue;
- }
-
- status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
- if (0 < status) {
- buf += status;
- buf_len -= status;
- }
-
- C_PSQL_PAR_APPEND (buf, buf_len, "host", db->host);
- C_PSQL_PAR_APPEND (buf, buf_len, "port", db->port);
- C_PSQL_PAR_APPEND (buf, buf_len, "user", db->user);
- C_PSQL_PAR_APPEND (buf, buf_len, "password", db->password);
- C_PSQL_PAR_APPEND (buf, buf_len, "sslmode", db->sslmode);
- C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
- C_PSQL_PAR_APPEND (buf, buf_len, "service", db->service);
-
- db->conn = PQconnectdb (conninfo);
- if (0 != c_psql_check_connection (db))
- continue;
-
- db->proto_version = PQprotocolVersion (db->conn);
-
- server_host = PQhost (db->conn);
- server_version = PQserverVersion (db->conn);
- log_info ("Sucessfully connected to database %s (user %s) "
- "at server %s%s%s (server version: %d.%d.%d, "
- "protocol version: %d, pid: %d)",
- PQdb (db->conn), PQuser (db->conn),
- C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
- C_PSQL_SERVER_VERSION3 (server_version),
- db->proto_version, PQbackendPID (db->conn));
-
- if (3 > db->proto_version)
- log_warn ("Protocol version %d does not support parameters.",
- db->proto_version);
- }
-
- plugin_register_read ("postgresql", c_psql_read);
- plugin_register_shutdown ("postgresql", c_psql_shutdown);
- return 0;
-} /* c_psql_init */
-
static int config_set_s (char *name, char **var, const oconfig_item_t *ci)
{
if ((0 != ci->children_num) || (1 != ci->values_num)
return 0;
} /* config_set_s */
+static int config_set_i (char *name, int *var,
+ const oconfig_item_t *ci, int min)
+{
+ int value;
+
+ if ((0 != ci->children_num) || (1 != ci->values_num)
+ || (OCONFIG_TYPE_NUMBER != ci->values[0].type)) {
+ log_err ("%s expects a single number argument.", name);
+ return 1;
+ }
+
+ value = (int)ci->values[0].value.number;
+
+ if (value < min) {
+ log_err ("%s expects a number greater or equal to %i.", name, min);
+ return 1;
+ }
+
+ *var = value;
+ return 0;
+} /* config_set_s */
+
static int config_query_param_add (udb_query_t *q, oconfig_item_t *ci)
{
c_psql_user_data_t *data;
{
c_psql_database_t *db;
+ char cb_name[DATA_MAX_NAME_LEN];
+ struct timespec cb_interval;
+ user_data_t ud;
+
int i;
if ((1 != ci->values_num)
return 1;
}
+ memset (&ud, 0, sizeof (ud));
+
db = c_psql_database_new (ci->values[0].value.string);
+ if (db == NULL)
+ return -1;
for (i = 0; i < ci->children_num; ++i) {
oconfig_item_t *c = ci->children + i;
else if (0 == strcasecmp (c->key, "Query"))
udb_query_pick_from_list (c, queries, queries_num,
&db->queries, &db->queries_num);
+ else if (0 == strcasecmp (c->key, "Interval"))
+ config_set_i ("Interval", &db->interval, c, /* min = */ 1);
else
log_warn ("Ignoring unknown config key \"%s\".", c->key);
}
/* If no `Query' options were given, add the default queries.. */
- if (db->queries_num == 0)
- {
+ if (db->queries_num == 0) {
for (i = 0; i < def_queries_num; i++)
udb_query_pick_from_list_by_name (def_queries[i],
queries, queries_num,
&db->queries, &db->queries_num);
}
+ if (db->queries_num > 0) {
+ db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+ db->queries_num, sizeof (*db->q_prep_areas));
+
+ if (db->q_prep_areas == NULL) {
+ log_err ("Out of memory.");
+ c_psql_database_delete (db);
+ return -1;
+ }
+ }
+
for (i = 0; (size_t)i < db->queries_num; ++i) {
c_psql_user_data_t *data;
data = udb_query_get_user_data (db->queries[i]);
if ((data != NULL) && (data->params_num > db->max_params_num))
db->max_params_num = data->params_num;
+
+ db->q_prep_areas[i]
+ = udb_query_allocate_preparation_area (db->queries[i]);
+
+ if (db->q_prep_areas[i] == NULL) {
+ log_err ("Out of memory.");
+ c_psql_database_delete (db);
+ return -1;
+ }
}
+
+ ud.data = db;
+ ud.free_func = c_psql_database_delete;
+
+ ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->database);
+
+ memset (&cb_interval, 0, sizeof (cb_interval));
+ if (db->interval > 0)
+ cb_interval.tv_sec = (time_t)db->interval;
+
+ plugin_register_complex_read ("postgresql", cb_name, c_psql_read,
+ /* interval = */ &cb_interval, &ud);
return 0;
} /* c_psql_config_database */
void module_register (void)
{
plugin_register_complex_config ("postgresql", c_psql_config);
- plugin_register_init ("postgresql", c_psql_init);
+ plugin_register_shutdown ("postgresql", c_psql_shutdown);
} /* module_register */
/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
* Copyright (C) 2009 Sebastian Harl
* Copyright (C) 2009 Andrés J. Díaz
* Copyright (C) 2009 Manuel Sanmartin
+ * Copyright (C) 2010 Clément Stenac
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Sebastian Harl <sh at tokkee.org>
* Andrés J. Díaz <ajdiaz at connectical.com>
* Manuel Sanmartin
+ * Clément Stenac <clement.stenac at diwi.org>
**/
#include "collectd.h"
# define ARG_MAX 4096
#endif
-#define BUFSIZE 256
-
static const char *config_keys[] =
{
"Process",
unsigned long num_lwp;
unsigned long vmem_size;
unsigned long vmem_rss;
+ unsigned long vmem_data;
+ unsigned long vmem_code;
unsigned long stack_size;
unsigned long vmem_minflt;
unsigned long cpu_system_counter;
/* io data */
- long io_rchar;
- long io_wchar;
- long io_syscr;
- long io_syscw;
+ derive_t io_rchar;
+ derive_t io_wchar;
+ derive_t io_syscr;
+ derive_t io_syscw;
struct procstat_entry_s *next;
} procstat_entry_t;
unsigned long num_lwp;
unsigned long vmem_size;
unsigned long vmem_rss;
+ unsigned long vmem_data;
+ unsigned long vmem_code;
unsigned long stack_size;
unsigned long vmem_minflt_counter;
unsigned long cpu_system_counter;
/* io data */
- long io_rchar;
- long io_wchar;
- long io_syscr;
- long io_syscw;
+ derive_t io_rchar;
+ derive_t io_wchar;
+ derive_t io_syscr;
+ derive_t io_syscw;
struct procstat *next;
struct procstat_entry_s *instances;
ERROR ("processes plugin: ps_list_register: "
"Regular expression \"%s\" found in config "
"file, but support for regular expressions "
- "has been dispabled at compile time.",
+ "has been disabled at compile time.",
regexp);
sfree (new);
return;
pse->num_lwp = entry->num_lwp;
pse->vmem_size = entry->vmem_size;
pse->vmem_rss = entry->vmem_rss;
+ pse->vmem_data = entry->vmem_data;
+ pse->vmem_code = entry->vmem_code;
pse->stack_size = entry->stack_size;
pse->io_rchar = entry->io_rchar;
pse->io_wchar = entry->io_wchar;
ps->num_lwp += pse->num_lwp;
ps->vmem_size += pse->vmem_size;
ps->vmem_rss += pse->vmem_rss;
+ ps->vmem_data += pse->vmem_data;
+ ps->vmem_code += pse->vmem_code;
ps->stack_size += pse->stack_size;
ps->io_rchar += ((pse->io_rchar == -1)?0:pse->io_rchar);
ps->num_lwp = 0;
ps->vmem_size = 0;
ps->vmem_rss = 0;
+ ps->vmem_data = 0;
+ ps->vmem_code = 0;
ps->stack_size = 0;
ps->io_rchar = -1;
ps->io_wchar = -1;
vl.values_len = 1;
plugin_dispatch_values (&vl);
+ sstrncpy (vl.type, "ps_data", sizeof (vl.type));
+ vl.values[0].gauge = ps->vmem_data;
+ vl.values_len = 1;
+ plugin_dispatch_values (&vl);
+
+ sstrncpy (vl.type, "ps_code", sizeof (vl.type));
+ vl.values[0].gauge = ps->vmem_code;
+ vl.values_len = 1;
+ plugin_dispatch_values (&vl);
+
sstrncpy (vl.type, "ps_stacksize", sizeof (vl.type));
vl.values[0].gauge = ps->stack_size;
vl.values_len = 1;
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[0].derive = ps->io_rchar;
+ vl.values[1].derive = 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[0].derive = ps->io_syscr;
+ vl.values[1].derive = ps->io_syscw;
vl.values_len = 2;
plugin_dispatch_values (&vl);
}
- DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
+ DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; "
+ "vmem_size = %lu; vmem_rss = %lu; vmem_data = %lu; "
+ "vmem_code = %lu; "
"vmem_minflt_counter = %lu; vmem_majflt_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,
+ "io_rchar = %"PRIi64"; io_wchar = %"PRIi64"; "
+ "io_syscr = %"PRIi64"; io_syscw = %"PRIi64";",
+ ps->name, ps->num_proc, ps->num_lwp,
+ ps->vmem_size, ps->vmem_rss,
+ ps->vmem_data, ps->vmem_code,
ps->vmem_minflt_counter, ps->vmem_majflt_counter,
ps->cpu_user_counter, ps->cpu_system_counter,
ps->io_rchar, ps->io_wchar, ps->io_syscr, ps->io_syscw);
return ((count >= 1) ? count : 1);
} /* int *ps_read_tasks */
+/* Read advanced virtual memory data from /proc/pid/status */
+static procstat_t *ps_read_vmem (int pid, procstat_t *ps)
+{
+ FILE *fh;
+ char buffer[1024];
+ char filename[64];
+ unsigned long long lib = 0;
+ unsigned long long exe = 0;
+ unsigned long long data = 0;
+ char *fields[8];
+ int numfields;
+
+ ssnprintf (filename, sizeof (filename), "/proc/%i/status", pid);
+ if ((fh = fopen (filename, "r")) == NULL)
+ return (NULL);
+
+ while (fgets (buffer, sizeof(buffer), fh) != NULL)
+ {
+ long long tmp;
+ char *endptr;
+
+ if (strncmp (buffer, "Vm", 2) != 0)
+ continue;
+
+ numfields = strsplit (buffer, fields,
+ STATIC_ARRAY_SIZE (fields));
+
+ if (numfields < 2)
+ continue;
+
+ errno = 0;
+ endptr = NULL;
+ tmp = strtoll (fields[1], &endptr, /* base = */ 10);
+ if ((errno == 0) && (endptr != fields[1]))
+ {
+ if (strncmp (buffer, "VmData", 6) == 0)
+ {
+ data = tmp;
+ }
+ else if (strncmp (buffer, "VmLib", 5) == 0)
+ {
+ lib = tmp;
+ }
+ else if (strncmp(buffer, "VmExe", 5) == 0)
+ {
+ exe = tmp;
+ }
+ }
+ } /* while (fgets) */
+
+ if (fclose (fh))
+ {
+ char errbuf[1024];
+ WARNING ("processes: fclose: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ }
+
+ ps->vmem_data = data * 1024;
+ ps->vmem_code = (exe + lib) * 1024;
+
+ return (ps);
+} /* procstat_t *ps_read_vmem */
+
static procstat_t *ps_read_io (int pid, procstat_t *ps)
{
FILE *fh;
if ((fh = fopen (filename, "r")) == NULL)
return (NULL);
- while (fgets (buffer, 1024, fh) != NULL)
+ while (fgets (buffer, sizeof (buffer), fh) != NULL)
{
- long *val = NULL;
+ derive_t *val = NULL;
+ long long tmp;
+ char *endptr;
if (strncasecmp (buffer, "rchar:", 6) == 0)
val = &(ps->io_rchar);
else
continue;
- numfields = strsplit (buffer, fields, 8);
+ numfields = strsplit (buffer, fields,
+ STATIC_ARRAY_SIZE (fields));
if (numfields < 2)
continue;
- *val = atol (fields[1]);
- }
+ errno = 0;
+ endptr = NULL;
+ tmp = strtoll (fields[1], &endptr, /* base = */ 10);
+ if ((errno != 0) || (endptr == fields[1]))
+ *val = -1;
+ else
+ *val = (derive_t) tmp;
+ } /* while (fgets) */
if (fclose (fh))
{
return (-1);
buffer[i] = 0;
- fields_len = strsplit (buffer, fields, 64);
+ fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
if (fields_len < 24)
{
DEBUG ("processes plugin: ps_read_process (pid = %i):"
cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
vmem_rss = vmem_rss * pagesize_g;
+ if ( (ps_read_vmem(pid, ps)) == NULL)
+ {
+ /* No VMem data */
+ ps->vmem_data = -1;
+ ps->vmem_code = -1;
+ DEBUG("ps_read_process: did not get vmem data for pid %i",pid);
+ }
+
ps->cpu_user_counter = (unsigned long) cpu_user_counter;
ps->cpu_system_counter = (unsigned long) cpu_system_counter;
ps->vmem_size = (unsigned long) vmem_size;
errno = 0;
endptr = NULL;
- result = strtoul(fields[1], &endptr, 10);
+ result = strtoul(fields[1], &endptr, /* base = */ 10);
if ((endptr == fields[1]) || (errno != 0)) {
ERROR ("processes plugin: Cannot parse fork rate: %s",
fields[1]);
}
pse.num_proc++;
+ pse.vmem_size = task_basic_info.virtual_size;
pse.vmem_rss = task_basic_info.resident_size;
+ /* Does not seem to be easily exposed */
+ pse.vmem_data = 0;
+ pse.vmem_code = 0;
pse.vmem_minflt_counter = task_events_info.cow_faults;
pse.vmem_majflt_counter = task_events_info.faults;
pse.num_lwp = ps.num_lwp;
pse.vmem_size = ps.vmem_size;
pse.vmem_rss = ps.vmem_rss;
+ pse.vmem_data = ps.vmem_data;
+ pse.vmem_code = ps.vmem_code;
pse.stack_size = ps.stack_size;
pse.vmem_minflt = 0;
pse.vmem_size = procs[i].ki_size;
pse.vmem_rss = procs[i].ki_rssize * getpagesize();
+ pse.vmem_data = procs[i].ki_dsize * getpagesize();
+ pse.vmem_code = procs[i].ki_tsize * getpagesize();
pse.stack_size = procs[i].ki_ssize * getpagesize();
pse.vmem_minflt = 0;
pse.vmem_minflt_counter = procs[i].ki_rusage.ru_minflt;
pse.vmem_size = procentry[i].pi_tsize + procentry[i].pi_dvm * pagesize;
pse.vmem_rss = (procentry[i].pi_drss + procentry[i].pi_trss) * pagesize;
+ /* Not supported */
+ pse.vmem_data = 0;
+ pse.vmem_code = 0;
pse.stack_size = 0;
ps_list_add (cmdline, cargs, &pse);
user_data->data = c;
ts.tv_sec = interval;
ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
- plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
+ plugin_register_complex_read(/* group = */ NULL, buf,
+ cpy_read_callback, &ts, user_data);
return cpy_string_to_unicode_or_bytes(buf);
}
_Bool collect_interface;
_Bool collect_regtable;
+ _Bool collect_cpu_load;
+ _Bool collect_memory;
+ _Bool collect_df;
+ _Bool collect_disk;
};
typedef struct cr_data_s cr_data_t;
plugin_dispatch_values (&vl);
} /* }}} void cr_submit_gauge */
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0)
+static void cr_submit_counter (cr_data_t *rd, const char *type, /* {{{ */
+ const char *type_instance, counter_t value)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].counter = value;
+
+ vl.values = values;
+ vl.values_len = STATIC_ARRAY_SIZE (values);
+ sstrncpy (vl.host, rd->node, sizeof (vl.host)); /* FIXME */
+ sstrncpy (vl.plugin, "routeros", sizeof (vl.plugin));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+ sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+ plugin_dispatch_values (&vl);
+} /* }}} void cr_submit_gauge */
+#endif
+
static void submit_regtable (cr_data_t *rd, /* {{{ */
const ros_registration_table_t *r)
{
return (0);
} /* }}} int handle_regtable */
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) /* FIXME */
+static int handle_system_resource (__attribute__((unused)) ros_connection_t *c, /* {{{ */
+ const ros_system_resource_t *r,
+ __attribute__((unused)) void *user_data)
+{
+ cr_data_t *rd;
+
+ if ((r == NULL) || (user_data == NULL))
+ return (EINVAL);
+ rd = user_data;
+
+ if (rd->collect_cpu_load)
+ cr_submit_gauge (rd, "gauge", "cpu_load", (gauge_t) r->cpu_load);
+
+ if (rd->collect_memory)
+ {
+ cr_submit_gauge (rd, "memory", "used",
+ (gauge_t) (r->total_memory - r->free_memory));
+ cr_submit_gauge (rd, "memory", "free", (gauge_t) r->free_memory);
+ }
+
+ if (rd->collect_df)
+ {
+ cr_submit_gauge (rd, "df_complex", "used",
+ (gauge_t) (r->total_memory - r->free_memory));
+ cr_submit_gauge (rd, "df_complex", "free", (gauge_t) r->free_memory);
+ }
+
+ if (rd->collect_disk)
+ {
+ cr_submit_counter (rd, "counter", "secors_written", (counter_t) r->write_sect_total);
+ cr_submit_gauge (rd, "gauge", "bad_blocks", (gauge_t) r->bad_blocks);
+ }
+
+ return (0);
+} /* }}} int handle_system_resource */
+#endif
+
static int cr_read (user_data_t *user_data) /* {{{ */
{
int status;
}
}
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) /* FIXME */
+ if (rd->collect_cpu_load
+ || rd->collect_memory
+ || rd->collect_df
+ || rd->collect_disk)
+ {
+ status = ros_system_resource (rd->connection, handle_system_resource,
+ /* user data = */ rd);
+ if (status != 0)
+ {
+ char errbuf[128];
+ ERROR ("routeros plugin: ros_system_resource failed: %s",
+ sstrerror (status, errbuf, sizeof (errbuf)));
+ ros_disconnect (rd->connection);
+ rd->connection = NULL;
+ return (-1);
+ }
+ }
+#endif
+
return (0);
} /* }}} int cr_read */
router_data->service = NULL;
router_data->username = NULL;
router_data->password = NULL;
- router_data->collect_interface = false;
- router_data->collect_regtable = false;
status = 0;
for (i = 0; i < ci->children_num; i++)
cf_util_get_boolean (child, &router_data->collect_interface);
else if (strcasecmp ("CollectRegistrationTable", child->key) == 0)
cf_util_get_boolean (child, &router_data->collect_regtable);
+#if ROS_VERSION >= ROS_VERSION_ENCODE(1, 1, 0) /* FIXME */
+ else if (strcasecmp ("CollectCPULoad", child->key) == 0)
+ cf_util_get_boolean (child, &router_data->collect_cpu_load);
+ else if (strcasecmp ("CollectMemory", child->key) == 0)
+ cf_util_get_boolean (child, &router_data->collect_memory);
+ else if (strcasecmp ("CollectDF", child->key) == 0)
+ cf_util_get_boolean (child, &router_data->collect_df);
+ else if (strcasecmp ("CollectDisk", child->key) == 0)
+ cf_util_get_boolean (child, &router_data->collect_disk);
+#endif
else
{
WARNING ("routeros plugin: Unknown config option `%s'.", child->key);
user_data.data = router_data;
user_data.free_func = (void *) cr_free_data;
if (status == 0)
- status = plugin_register_complex_read (read_name, cr_read,
- /* interval = */ NULL, &user_data);
+ status = plugin_register_complex_read (/* group = */ NULL, read_name,
+ cr_read, /* interval = */ NULL, &user_data);
if (status != 0)
cr_free_data (router_data);
sfree (queue_entry);
} /* while (42) */
- pthread_mutex_lock (&cache_lock);
- c_avl_destroy (cache);
- cache = NULL;
- pthread_mutex_unlock (&cache_lock);
-
pthread_exit ((void *) 0);
return ((void *) 0);
} /* void *rrd_queue_thread */
return (0);
} /* int rrd_cache_insert */
+static int rrd_cache_destroy (void) /* {{{ */
+{
+ void *key = NULL;
+ void *value = NULL;
+
+ int non_empty = 0;
+
+ pthread_mutex_lock (&cache_lock);
+
+ if (cache == NULL)
+ {
+ pthread_mutex_unlock (&cache_lock);
+ return (0);
+ }
+
+ while (c_avl_pick (cache, &key, &value) == 0)
+ {
+ rrd_cache_t *rc;
+ int i;
+
+ sfree (key);
+ key = NULL;
+
+ rc = value;
+ value = NULL;
+
+ if (rc->values_num > 0)
+ non_empty++;
+
+ for (i = 0; i < rc->values_num; i++)
+ sfree (rc->values[i]);
+ sfree (rc->values);
+ sfree (rc);
+ }
+
+ c_avl_destroy (cache);
+ cache = NULL;
+
+ if (non_empty > 0)
+ {
+ INFO ("rrdtool plugin: %i cache %s had values when destroying the cache.",
+ non_empty, (non_empty == 1) ? "entry" : "entries");
+ }
+ else
+ {
+ DEBUG ("rrdtool plugin: No values have been lost "
+ "when destroying the cache.");
+ }
+
+ pthread_mutex_unlock (&cache_lock);
+ return (0);
+} /* }}} int rrd_cache_destroy */
+
static int rrd_compare_numeric (const void *a_ptr, const void *b_ptr)
{
int a = *((int *) a_ptr);
char values[512];
int status;
+ if (do_shutdown)
+ return (0);
+
if (0 != strcmp (ds->type, vl->type)) {
ERROR ("rrdtool plugin: DS type does not match value list type");
return -1;
DEBUG ("rrdtool plugin: queue_thread exited.");
}
- /* TODO: Maybe it'd be a good idea to free the cache here.. */
+ rrd_cache_destroy ();
return (0);
} /* int rrd_shutdown */
if (hd->interval != 0)
cb_interval.tv_sec = (time_t) hd->interval;
- status = plugin_register_complex_read (cb_name, csnmp_read_host,
- /* interval = */ &cb_interval, /* user_data = */ &cb_data);
+ status = plugin_register_complex_read (/* group = */ NULL, cb_name,
+ csnmp_read_host, /* interval = */ &cb_interval,
+ /* user_data = */ &cb_data);
if (status != 0)
{
ERROR ("snmp plugin: Registering complex read function failed.");
char temp[DATA_MAX_NAME_LEN];
if (instance_list_ptr == NULL)
- ssnprintf (temp, sizeof (temp), "%u", (uint32_t) subid);
+ ssnprintf (temp, sizeof (temp), "%"PRIu32, (uint32_t) subid);
else
sstrncpy (temp, instance_list_ptr->instance, sizeof (temp));
#elif HAVE_PERFSTAT
static int pagesize;
static perfstat_memory_total_t pmemory;
-/*# endif HAVE_PERFSTAT */
+/*# endif HAVE_PERFSTAT */
#else
# error "No applicable input method."
char *fields[8];
int numfields;
+ _Bool old_kernel=0;
+
derive_t swap_used = 0;
derive_t swap_cached = 0;
derive_t swap_free = 0;
if ((fh = fopen ("/proc/vmstat", "r")) == NULL)
{
- char errbuf[1024];
- WARNING ("swap: fopen: %s",
- sstrerror (errno, errbuf, sizeof (errbuf)));
- return (-1);
+ // /proc/vmstat does not exist in kernels <2.6
+ if ((fh = fopen ("/proc/stat", "r")) == NULL )
+ {
+ char errbuf[1024];
+ WARNING ("swap: fopen: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (-1);
+ }
+ else
+ old_kernel = 1;
}
while (fgets (buffer, 1024, fh) != NULL)
{
- derive_t *val = NULL;
-
- if (strncasecmp (buffer, "pswpin", 6) == 0)
- val = &swap_in;
- else if (strncasecmp (buffer, "pswpout", 7) == 0)
- val = &swap_out;
- else
- continue;
-
- numfields = strsplit (buffer, fields, 8);
-
- if (numfields < 2)
- continue;
-
- *val = (derive_t) atoll (fields[1]);
- }
+ numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+
+ if (!old_kernel)
+ {
+ if (numfields != 2)
+ continue;
+
+ if (strcasecmp ("pswpin", fields[0]) != 0)
+ strtoderive (fields[1], &swap_in);
+ else if (strcasecmp ("pswpout", fields[0]) == 0)
+ strtoderive (fields[1], &swap_out);
+ }
+ else /* if (old_kernel) */
+ {
+ if (numfields != 3)
+ continue;
+
+ if (strcasecmp ("page", fields[0]) == 0)
+ {
+ strtoderive (fields[1], &swap_in);
+ strtoderive (fields[2], &swap_out);
+ }
+ }
+ } /* while (fgets) */
if (fclose (fh))
{
swap_submit ("cached", swap_cached, DS_TYPE_GAUGE);
swap_submit ("in", swap_in, DS_TYPE_DERIVE);
swap_submit ("out", swap_out, DS_TYPE_DERIVE);
-
/* #endif KERNEL_LINUX */
#elif HAVE_LIBKSTAT
* However, Solaris does not allow to allocated/reserved more than the
* available swap (physical memory + disk swap), so the pedant may
* prefer: allocated + unallocated = reserved, available
- *
+ *
* We map the above to: used + resv = n/a, free
*
* Does your brain hurt yet? - Christophe Kalt
* Instance "exim"
* <Match>
* Regex "S=([1-9][0-9]*)"
+ * ExcludeRegex "U=root.*S="
* DSType "CounterAdd"
* Type "ipt_bytes"
* Instance "total"
struct ctail_config_match_s
{
char *regex;
+ char *excluderegex;
int flags;
char *type;
char *type_instance;
if (strcasecmp ("Regex", option->key) == 0)
status = ctail_config_add_string ("Regex", &cm.regex, option);
+ else if (strcasecmp ("ExcludeRegex", option->key) == 0)
+ status = ctail_config_add_string ("ExcludeRegex", &cm.excluderegex,
+ option);
else if (strcasecmp ("DSType", option->key) == 0)
status = ctail_config_add_match_dstype (&cm, option);
else if (strcasecmp ("Type", option->key) == 0)
if (status == 0)
{
- status = tail_match_add_match_simple (tm, cm.regex, cm.flags,
- "tail", plugin_instance, cm.type, cm.type_instance);
+ status = tail_match_add_match_simple (tm, cm.regex, cm.excluderegex,
+ cm.flags, "tail", plugin_instance, cm.type, cm.type_instance);
if (status != 0)
{
}
sfree (cm.regex);
+ sfree (cm.excluderegex);
sfree (cm.type);
sfree (cm.type_instance);
static int ts_destroy (void **user_data) /* {{{ */
{
+ ts_data_t **data;
+
if (user_data == NULL)
return (-EINVAL);
- free (*user_data);
- *user_data = NULL;
+ data = (ts_data_t **) user_data;
+
+ free (*data);
+ *data = NULL;
return (0);
} /* }}} int ts_destroy */
absolute count:ABSOLUTE:0:U
apache_bytes count:COUNTER:0:134217728
apache_connections count:GAUGE:0:65535
+apache_idle_workers count:GAUGE:0:65535
apache_requests count:COUNTER:0:134217728
apache_scoreboard count:GAUGE:0:65535
arc_counts demand_data:COUNTER:0:U, demand_metadata:COUNTER:0:U, prefetch_data:COUNTER:0:U, prefetch_metadata:COUNTER:0:U
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_code value:GAUGE:0:9223372036854775807
+ps_data value:GAUGE:0:9223372036854775807
ps_stacksize value:GAUGE:0:9223372036854775807
ps_state value:GAUGE:0:65535
ps_vm value:GAUGE:0:9223372036854775807
data-source specification. Each data-source is defined by a quadruple made up
of the data-source name, type, minimal and maximal values, delimited by colons
(":"): I<ds-name>:I<ds-type>:I<min>:I<max>. I<ds-type> may be either
-B<COUNTER> or B<GAUGE>. I<min> and I<max> define the range of valid values for
+B<ABSOLUTE>, B<COUNTER>, B<DERIVE>, or B<GAUGE>. I<min> and I<max> define the
+range of valid values for
data stored for this data-source. If B<U> is specified for either the min or
max value, it will be set to unknown, meaning that no range checks will
happen. See L<rrdcreate(1)> for more details.
#include "utils_cache.h"
#include "utils_parse_option.h"
+#define free_everything_and_return(status) do { \
+ size_t j; \
+ for (j = 0; j < number; j++) { \
+ sfree(names[j]); \
+ names[j] = NULL; \
+ } \
+ sfree(names); \
+ sfree(times); \
+ return (status); \
+ } while (0)
+
#define print_to_socket(fh, ...) \
if (fprintf (fh, __VA_ARGS__) < 0) { \
char errbuf[1024]; \
WARNING ("handle_listval: failed to write to socket #%i: %s", \
fileno (fh), sstrerror (errno, errbuf, sizeof (errbuf))); \
- return -1; \
+ free_everything_and_return (-1); \
}
int handle_listval (FILE *fh, char *buffer)
if (status != 0)
{
print_to_socket (fh, "-1 Cannot parse command.\n");
- return (-1);
+ free_everything_and_return (-1);
}
assert (command != NULL);
if (strcasecmp ("LISTVAL", command) != 0)
{
print_to_socket (fh, "-1 Unexpected command: `%s'.\n", command);
- return (-1);
+ free_everything_and_return (-1);
}
if (*buffer != 0)
{
print_to_socket (fh, "-1 Garbage after end of command: %s\n", buffer);
- return (-1);
+ free_everything_and_return (-1);
}
status = uc_get_names (&names, ×, &number);
{
DEBUG ("command listval: uc_get_names failed with status %i", status);
print_to_socket (fh, "-1 uc_get_names failed.\n");
- return (-1);
+ free_everything_and_return (-1);
}
print_to_socket (fh, "%i Value%s found\n",
(int) number, (number == 1) ? "" : "s");
for (i = 0; i < number; i++)
- {
print_to_socket (fh, "%u %s\n", (unsigned int) times[i], names[i]);
- sfree(names[i]);
- }
- sfree(names);
- sfree(times);
-
- return (0);
+ free_everything_and_return (0);
} /* int handle_listval */
/* vim: set sw=2 sts=2 ts=8 : */
char **values;
size_t values_num;
- /* Preparation area */
- const data_set_t *ds;
- size_t *instances_pos;
- size_t *values_pos;
- char **instances_buffer;
- char **values_buffer;
-
/* Legacy data */
int legacy_mode;
size_t legacy_position;
unsigned int min_version;
unsigned int max_version;
- /* Preparation area */
+ udb_result_t *results;
+}; /* }}} */
+
+struct udb_result_preparation_area_s /* {{{ */
+{
+ const data_set_t *ds;
+ size_t *instances_pos;
+ size_t *values_pos;
+ char **instances_buffer;
+ char **values_buffer;
+
+ struct udb_result_preparation_area_s *next;
+}; /* }}} */
+typedef struct udb_result_preparation_area_s udb_result_preparation_area_t;
+
+struct udb_query_preparation_area_s /* {{{ */
+{
size_t column_num;
char *host;
char *plugin;
char *db_name;
- udb_result_t *results;
+ int interval;
+
+ udb_result_preparation_area_t *result_prep_areas;
}; /* }}} */
/*
/*
* Legacy result private functions
*/
-static void udb_legacy_result_finish_result (udb_result_t *r) /* {{{ */
+static void udb_legacy_result_finish_result (const udb_result_t const *r, /* {{{ */
+ udb_result_preparation_area_t *prep_area)
{
- if (r == NULL)
+ if ((r == NULL) || (prep_area))
return;
assert (r->legacy_mode == 1);
- r->ds = NULL;
+ prep_area->ds = NULL;
} /* }}} void udb_legacy_result_finish_result */
static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
- udb_query_t *q, char **column_values)
+ udb_query_preparation_area_t *q_area,
+ udb_result_preparation_area_t *r_area,
+ const udb_query_t const *q, char **column_values)
{
value_list_t vl = VALUE_LIST_INIT;
value_t value;
char *value_str;
assert (r->legacy_mode == 1);
- assert (r->ds != NULL);
- assert (r->ds->ds_num == 1);
+ assert (r_area->ds != NULL);
+ assert (r_area->ds->ds_num == 1);
vl.values = &value;
vl.values_len = 1;
value_str = column_values[r->legacy_position];
- if (0 != parse_value (value_str, &vl.values[0], r->ds->ds[0].type))
+ if (0 != parse_value (value_str, &vl.values[0], r_area->ds->ds[0].type))
{
ERROR ("db query utils: udb_legacy_result_handle_result: "
"Parsing `%s' as %s failed.", value_str,
- DS_TYPE_TO_STRING (r->ds->ds[0].type));
+ DS_TYPE_TO_STRING (r_area->ds->ds[0].type));
errno = EINVAL;
return (-1);
}
- sstrncpy (vl.host, q->host, sizeof (vl.host));
- sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
+ if (q_area->interval > 0)
+ vl.interval = q_area->interval;
+
+ sstrncpy (vl.host, q_area->host, sizeof (vl.host));
+ sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
sstrncpy (vl.type, r->type, sizeof (vl.type));
if (r->instance_prefix != NULL)
return (0);
} /* }}} int udb_legacy_result_handle_result */
-static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
+static int udb_legacy_result_prepare_result (const udb_result_t const *r, /* {{{ */
+ udb_result_preparation_area_t *prep_area,
char **column_names, size_t column_num)
{
if (r == NULL)
assert (r->legacy_mode == 1);
/* Make sure previous preparations are cleaned up. */
- udb_legacy_result_finish_result (r);
+ udb_legacy_result_finish_result (r, prep_area);
if (r->legacy_position >= column_num)
{
}
/* Read `ds' and check number of values {{{ */
- r->ds = plugin_get_ds (r->type);
- if (r->ds == NULL)
+ prep_area->ds = plugin_get_ds (r->type);
+ if (prep_area->ds == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
"known by the daemon. See types.db(5) for details.",
return (-1);
}
- if (r->ds->ds_num != 1)
+ if (prep_area->ds->ds_num != 1)
{
ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
"requires exactly %i values, but the legacy configuration "
"requires exactly one!",
r->type,
- r->ds->ds_num);
+ prep_area->ds->ds_num);
return (-1);
}
/* }}} */
/*
* Result private functions
*/
-static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
+static int udb_result_submit (udb_result_t *r, /* {{{ */
+ udb_result_preparation_area_t *r_area,
+ const udb_query_t const *q, udb_query_preparation_area_t *q_area)
{
value_list_t vl = VALUE_LIST_INIT;
size_t i;
assert (r != NULL);
assert (r->legacy_mode == 0);
- assert (r->ds != NULL);
- assert (((size_t) r->ds->ds_num) == r->values_num);
+ assert (r_area->ds != NULL);
+ assert (((size_t) r_area->ds->ds_num) == r->values_num);
- vl.values = (value_t *) calloc (r->ds->ds_num, sizeof (value_t));
+ vl.values = (value_t *) calloc (r_area->ds->ds_num, sizeof (value_t));
if (vl.values == NULL)
{
ERROR ("db query utils: malloc failed.");
return (-1);
}
- vl.values_len = r->ds->ds_num;
+ vl.values_len = r_area->ds->ds_num;
for (i = 0; i < r->values_num; i++)
{
- char *value_str = r->values_buffer[i];
+ char *value_str = r_area->values_buffer[i];
- if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i].type))
+ if (0 != parse_value (value_str, &vl.values[i], r_area->ds->ds[i].type))
{
ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
- value_str, DS_TYPE_TO_STRING (r->ds->ds[i].type));
+ value_str, DS_TYPE_TO_STRING (r_area->ds->ds[i].type));
errno = EINVAL;
return (-1);
}
}
- sstrncpy (vl.host, q->host, sizeof (vl.host));
- sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
+ if (q_area->interval > 0)
+ vl.interval = q_area->interval;
+
+ sstrncpy (vl.host, q_area->host, sizeof (vl.host));
+ sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
sstrncpy (vl.type, r->type, sizeof (vl.type));
/* Set vl.type_instance {{{ */
if (r->instance_prefix == NULL)
{
strjoin (vl.type_instance, sizeof (vl.type_instance),
- r->instances_buffer, r->instances_num, "-");
+ r_area->instances_buffer, r->instances_num, "-");
}
else
{
char tmp[DATA_MAX_NAME_LEN];
- strjoin (tmp, sizeof (tmp), r->instances_buffer, r->instances_num, "-");
+ strjoin (tmp, sizeof (tmp), r_area->instances_buffer,
+ r->instances_num, "-");
tmp[sizeof (tmp) - 1] = 0;
snprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
return (0);
} /* }}} void udb_result_submit */
-static void udb_result_finish_result (udb_result_t *r) /* {{{ */
+static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */
+ udb_result_preparation_area_t *prep_area)
{
- if (r == NULL)
+ if ((r == NULL) || (prep_area == NULL))
return;
if (r->legacy_mode == 1)
{
- udb_legacy_result_finish_result (r);
+ udb_legacy_result_finish_result (r, prep_area);
return;
}
assert (r->legacy_mode == 0);
- r->ds = NULL;
- sfree (r->instances_pos);
- sfree (r->values_pos);
- sfree (r->instances_buffer);
- sfree (r->values_buffer);
+ prep_area->ds = NULL;
+ sfree (prep_area->instances_pos);
+ sfree (prep_area->values_pos);
+ sfree (prep_area->instances_buffer);
+ sfree (prep_area->values_buffer);
} /* }}} void udb_result_finish_result */
static int udb_result_handle_result (udb_result_t *r, /* {{{ */
- udb_query_t *q, char **column_values)
+ udb_query_preparation_area_t *q_area,
+ udb_result_preparation_area_t *r_area,
+ const udb_query_t const *q, char **column_values)
{
size_t i;
+ assert (r && q_area && r_area);
+
if (r->legacy_mode == 1)
- return (udb_legacy_result_handle_result (r, q, column_values));
+ return (udb_legacy_result_handle_result (r, q_area, r_area,
+ q, column_values));
assert (r->legacy_mode == 0);
for (i = 0; i < r->instances_num; i++)
- r->instances_buffer[i] = column_values[r->instances_pos[i]];
+ r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]];
for (i = 0; i < r->values_num; i++)
- r->values_buffer[i] = column_values[r->values_pos[i]];
+ r_area->values_buffer[i] = column_values[r_area->values_pos[i]];
- return udb_result_submit (r, q);
+ return udb_result_submit (r, r_area, q, q_area);
} /* }}} int udb_result_handle_result */
-static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
+static int udb_result_prepare_result (const udb_result_t const *r, /* {{{ */
+ udb_result_preparation_area_t *prep_area,
char **column_names, size_t column_num)
{
size_t i;
- if (r == NULL)
+ if ((r == NULL) || (prep_area == NULL))
return (-EINVAL);
if (r->legacy_mode == 1)
- return (udb_legacy_result_prepare_result (r, column_names, column_num));
+ return (udb_legacy_result_prepare_result (r, prep_area,
+ column_names, column_num));
assert (r->legacy_mode == 0);
#define BAIL_OUT(status) \
- r->ds = NULL; \
- sfree (r->instances_pos); \
- sfree (r->values_pos); \
- sfree (r->instances_buffer); \
- sfree (r->values_buffer); \
+ prep_area->ds = NULL; \
+ sfree (prep_area->instances_pos); \
+ sfree (prep_area->values_pos); \
+ sfree (prep_area->instances_buffer); \
+ sfree (prep_area->values_buffer); \
return (status)
/* Make sure previous preparations are cleaned up. */
- udb_result_finish_result (r);
- r->instances_pos = NULL;
- r->values_pos = NULL;
+ udb_result_finish_result (r, prep_area);
+ prep_area->instances_pos = NULL;
+ prep_area->values_pos = NULL;
/* Read `ds' and check number of values {{{ */
- r->ds = plugin_get_ds (r->type);
- if (r->ds == NULL)
+ prep_area->ds = plugin_get_ds (r->type);
+ if (prep_area->ds == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
"known by the daemon. See types.db(5) for details.",
BAIL_OUT (-1);
}
- if (((size_t) r->ds->ds_num) != r->values_num)
+ if (((size_t) prep_area->ds->ds_num) != r->values_num)
{
ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
"requires exactly %i value%s, but the configuration specifies %zu.",
r->type,
- r->ds->ds_num, (r->ds->ds_num == 1) ? "" : "s",
+ prep_area->ds->ds_num, (prep_area->ds->ds_num == 1) ? "" : "s",
r->values_num);
BAIL_OUT (-1);
}
* r->values_buffer {{{ */
if (r->instances_num > 0)
{
- r->instances_pos = (size_t *) calloc (r->instances_num, sizeof (size_t));
- if (r->instances_pos == NULL)
+ prep_area->instances_pos
+ = (size_t *) calloc (r->instances_num, sizeof (size_t));
+ if (prep_area->instances_pos == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
BAIL_OUT (-ENOMEM);
}
- r->instances_buffer = (char **) calloc (r->instances_num, sizeof (char *));
- if (r->instances_buffer == NULL)
+ prep_area->instances_buffer
+ = (char **) calloc (r->instances_num, sizeof (char *));
+ if (prep_area->instances_buffer == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
BAIL_OUT (-ENOMEM);
}
} /* if (r->instances_num > 0) */
- r->values_pos = (size_t *) calloc (r->values_num, sizeof (size_t));
- if (r->values_pos == NULL)
+ prep_area->values_pos
+ = (size_t *) calloc (r->values_num, sizeof (size_t));
+ if (prep_area->values_pos == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
BAIL_OUT (-ENOMEM);
}
- r->values_buffer = (char **) calloc (r->values_num, sizeof (char *));
- if (r->values_buffer == NULL)
+ prep_area->values_buffer
+ = (char **) calloc (r->values_num, sizeof (char *));
+ if (prep_area->values_buffer == NULL)
{
ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
BAIL_OUT (-ENOMEM);
{
if (strcasecmp (r->instances[i], column_names[j]) == 0)
{
- r->instances_pos[i] = j;
+ prep_area->instances_pos[i] = j;
break;
}
}
{
if (strcasecmp (r->values[i], column_names[j]) == 0)
{
- r->values_pos[i] = j;
+ prep_area->values_pos[i] = j;
break;
}
}
return (1);
} /* }}} int udb_query_check_version */
-void udb_query_finish_result (udb_query_t *q) /* {{{ */
+void udb_query_finish_result (const udb_query_t const *q, /* {{{ */
+ udb_query_preparation_area_t *prep_area)
{
+ udb_result_preparation_area_t *r_area;
udb_result_t *r;
- if (q == NULL)
+ if ((q == NULL) || (prep_area == NULL))
return;
- q->column_num = 0;
- sfree (q->host);
- sfree (q->plugin);
- sfree (q->db_name);
+ prep_area->column_num = 0;
+ sfree (prep_area->host);
+ sfree (prep_area->plugin);
+ sfree (prep_area->db_name);
- for (r = q->results; r != NULL; r = r->next)
- udb_result_finish_result (r);
+ prep_area->interval = -1;
+
+ for (r = q->results, r_area = prep_area->result_prep_areas;
+ r != NULL; r = r->next, r_area = r_area->next)
+ {
+ /* this may happen during error conditions of the caller */
+ if (r_area == NULL)
+ break;
+ udb_result_finish_result (r, r_area);
+ }
} /* }}} void udb_query_finish_result */
-int udb_query_handle_result (udb_query_t *q, char **column_values) /* {{{ */
+int udb_query_handle_result (const udb_query_t const *q, /* {{{ */
+ udb_query_preparation_area_t *prep_area, char **column_values)
{
+ udb_result_preparation_area_t *r_area;
udb_result_t *r;
int success;
int status;
- if (q == NULL)
+ if ((q == NULL) || (prep_area == NULL))
return (-EINVAL);
- if ((q->column_num < 1) || (q->host == NULL) || (q->plugin == NULL)
- || (q->db_name == NULL))
+ if ((prep_area->column_num < 1) || (prep_area->host == NULL)
+ || (prep_area->plugin == NULL) || (prep_area->db_name == NULL))
{
ERROR ("db query utils: Query `%s': Query is not prepared; "
"can't handle result.", q->name);
{
size_t i;
- for (i = 0; i < q->column_num; i++)
+ for (i = 0; i < prep_area->column_num; i++)
{
DEBUG ("db query utils: udb_query_handle_result (%s, %s): "
"column[%zu] = %s;",
- q->db_name, q->name, i, column_values[i]);
+ prep_area->db_name, q->name, i, column_values[i]);
}
} while (0);
#endif /* }}} */
success = 0;
- for (r = q->results; r != NULL; r = r->next)
+ for (r = q->results, r_area = prep_area->result_prep_areas;
+ r != NULL; r = r->next, r_area = r_area->next)
{
- status = udb_result_handle_result (r, q, column_values);
+ status = udb_result_handle_result (r, prep_area, r_area,
+ q, column_values);
if (status == 0)
success++;
}
if (success == 0)
{
ERROR ("db query utils: udb_query_handle_result (%s, %s): "
- "All results failed.", q->db_name, q->name);
+ "All results failed.", prep_area->db_name, q->name);
return (-1);
}
return (0);
} /* }}} int udb_query_handle_result */
-int udb_query_prepare_result (udb_query_t *q, /* {{{ */
+int udb_query_prepare_result (const udb_query_t const *q, /* {{{ */
+ udb_query_preparation_area_t *prep_area,
const char *host, const char *plugin, const char *db_name,
- char **column_names, size_t column_num)
+ char **column_names, size_t column_num, int interval)
{
+ udb_result_preparation_area_t *r_area;
udb_result_t *r;
int status;
- if (q == NULL)
+ if ((q == NULL) || (prep_area == NULL))
return (-EINVAL);
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
+
+ prep_area->column_num = column_num;
+ prep_area->host = strdup (host);
+ prep_area->plugin = strdup (plugin);
+ prep_area->db_name = strdup (db_name);
- q->column_num = column_num;
- q->host = strdup (host);
- q->plugin = strdup (plugin);
- q->db_name = strdup (db_name);
+ prep_area->interval = interval;
- if ((q->host == NULL) || (q->plugin == NULL) || (q->db_name == NULL))
+ if ((prep_area->host == NULL) || (prep_area->plugin == NULL)
+ || (prep_area->db_name == NULL))
{
ERROR ("db query utils: Query `%s': Prepare failed: Out of memory.", q->name);
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
return (-ENOMEM);
}
} while (0);
#endif
- for (r = q->results; r != NULL; r = r->next)
+ for (r = q->results, r_area = prep_area->result_prep_areas;
+ r != NULL; r = r->next, r_area = r_area->next)
{
- status = udb_result_prepare_result (r, column_names, column_num);
+ if (! r_area)
+ {
+ ERROR ("db query utils: Query `%s': Invalid number of result "
+ "preparation areas.", q->name);
+ udb_query_finish_result (q, prep_area);
+ return (-EINVAL);
+ }
+
+ status = udb_result_prepare_result (r, r_area, column_names, column_num);
if (status != 0)
{
- udb_query_finish_result (q);
+ udb_query_finish_result (q, prep_area);
return (status);
}
}
return (0);
} /* }}} int udb_query_prepare_result */
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area (udb_query_t *q) /* {{{ */
+{
+ udb_query_preparation_area_t *q_area;
+ udb_result_preparation_area_t **next_r_area;
+ udb_result_t *r;
+
+ q_area = (udb_query_preparation_area_t *)malloc (sizeof (*q_area));
+ if (q_area == NULL)
+ return NULL;
+
+ memset (q_area, 0, sizeof (*q_area));
+
+ next_r_area = &q_area->result_prep_areas;
+ for (r = q->results; r != NULL; r = r->next)
+ {
+ udb_result_preparation_area_t *r_area;
+
+ r_area = (udb_result_preparation_area_t *)malloc (sizeof (*r_area));
+ if (r_area == NULL)
+ {
+ for (r_area = q_area->result_prep_areas;
+ r_area != NULL; r_area = r_area->next)
+ {
+ free (r_area);
+ }
+ free (q_area);
+ return NULL;
+ }
+
+ memset (r_area, 0, sizeof (*r_area));
+
+ *next_r_area = r_area;
+ next_r_area = &r_area->next;
+ }
+
+ return (q_area);
+} /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */
+
+void
+udb_query_delete_preparation_area (udb_query_preparation_area_t *q_area) /* {{{ */
+{
+ udb_result_preparation_area_t *r_area;
+
+ if (q_area == NULL)
+ return;
+
+ r_area = q_area->result_prep_areas;
+ while (r_area != NULL)
+ {
+ udb_result_preparation_area_t *area = r_area;
+
+ r_area = r_area->next;
+
+ sfree (area->instances_pos);
+ sfree (area->values_pos);
+ sfree (area->instances_buffer);
+ sfree (area->values_buffer);
+ free (area);
+ }
+
+ sfree (q_area->host);
+ sfree (q_area->plugin);
+ sfree (q_area->db_name);
+
+ free (q_area);
+} /* }}} void udb_query_delete_preparation_area */
+
/* vim: set sw=2 sts=2 et fdm=marker : */
struct udb_query_s;
typedef struct udb_query_s udb_query_t;
+struct udb_query_preparation_area_s;
+typedef struct udb_query_preparation_area_s udb_query_preparation_area_t;
+
typedef int (*udb_query_create_callback_t) (udb_query_t *q,
oconfig_item_t *ci);
*/
int udb_query_check_version (udb_query_t *q, unsigned int version);
-int udb_query_prepare_result (udb_query_t *q,
+int udb_query_prepare_result (const udb_query_t const *q,
+ udb_query_preparation_area_t *prep_area,
const char *host, const char *plugin, const char *db_name,
- char **column_names, size_t column_num);
-int udb_query_handle_result (udb_query_t *q, char **column_values);
-void udb_query_finish_result (udb_query_t *q);
+ char **column_names, size_t column_num, int interval);
+int udb_query_handle_result (const udb_query_t const *q,
+ udb_query_preparation_area_t *prep_area, char **column_values);
+void udb_query_finish_result (const udb_query_t const *q,
+ udb_query_preparation_area_t *prep_area);
+
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area (udb_query_t *q);
+void
+udb_query_delete_preparation_area (udb_query_preparation_area_t *q_area);
#endif /* UTILS_DB_QUERY_H */
/* vim: set sw=2 sts=2 et : */
#include "plugin.h"
#include "common.h"
+#include "utils_cache.h"
#include "utils_format_json.h"
static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
} /* }}} int buffer_add_string */
static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
- const data_set_t *ds, const value_list_t *vl)
+ const data_set_t *ds, const value_list_t *vl, int store_rates)
{
size_t offset = 0;
int i;
+ gauge_t *rates = NULL;
memset (buffer, 0, buffer_size);
status = ssnprintf (buffer + offset, buffer_size - offset, \
__VA_ARGS__); \
if (status < 1) \
+ { \
+ sfree(rates); \
return (-1); \
+ } \
else if (((size_t) status) >= (buffer_size - offset)) \
+ { \
+ sfree(rates); \
return (-ENOMEM); \
+ } \
else \
- offset += ((size_t) status); \
+ offset += ((size_t) status); \
} while (0)
BUFFER_ADD ("[");
BUFFER_ADD (",");
if (ds->ds[i].type == DS_TYPE_GAUGE)
- BUFFER_ADD ("%g", vl->values[i].gauge);
+ {
+ if(isfinite (vl->values[i].gauge))
+ BUFFER_ADD ("%g", vl->values[i].gauge);
+ else
+ BUFFER_ADD ("null");
+ }
+ else if (store_rates)
+ {
+ if (rates == NULL)
+ rates = uc_get_rate (ds, vl);
+ if (rates == NULL)
+ {
+ WARNING ("utils_format_json: uc_get_rate failed.");
+ sfree(rates);
+ return (-1);
+ }
+
+ if(isfinite (rates[i]))
+ BUFFER_ADD ("%g", rates[i]);
+ else
+ BUFFER_ADD ("null");
+ }
else if (ds->ds[i].type == DS_TYPE_COUNTER)
BUFFER_ADD ("%llu", vl->values[i].counter);
else if (ds->ds[i].type == DS_TYPE_DERIVE)
{
ERROR ("format_json: Unknown data source type: %i",
ds->ds[i].type);
+ sfree (rates);
return (-1);
}
} /* for ds->ds_num */
#undef BUFFER_ADD
DEBUG ("format_json: values_to_json: buffer = %s;", buffer);
-
+ sfree(rates);
return (0);
} /* }}} int values_to_json */
-static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
+static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl)
+{
+ size_t offset = 0;
+ int i;
+
+ memset (buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) do { \
+ int status; \
+ status = ssnprintf (buffer + offset, buffer_size - offset, \
+ __VA_ARGS__); \
+ if (status < 1) \
+ return (-1); \
+ else if (((size_t) status) >= (buffer_size - offset)) \
+ return (-ENOMEM); \
+ else \
+ offset += ((size_t) status); \
+} while (0)
+
+ BUFFER_ADD ("[");
+ for (i = 0; i < ds->ds_num; i++)
+ {
+ if (i > 0)
+ BUFFER_ADD (",");
+
+ BUFFER_ADD ("\"%s\"", DS_TYPE_TO_STRING (ds->ds[i].type));
+ } /* for ds->ds_num */
+ BUFFER_ADD ("]");
+
+#undef BUFFER_ADD
+
+ DEBUG ("format_json: dstypes_to_json: buffer = %s;", buffer);
+
+ return (0);
+} /* }}} int dstypes_to_json */
+
+static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */
const data_set_t *ds, const value_list_t *vl)
{
+ size_t offset = 0;
+ int i;
+
+ memset (buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) do { \
+ int status; \
+ status = ssnprintf (buffer + offset, buffer_size - offset, \
+ __VA_ARGS__); \
+ if (status < 1) \
+ return (-1); \
+ else if (((size_t) status) >= (buffer_size - offset)) \
+ return (-ENOMEM); \
+ else \
+ offset += ((size_t) status); \
+} while (0)
+
+ BUFFER_ADD ("[");
+ for (i = 0; i < ds->ds_num; i++)
+ {
+ if (i > 0)
+ BUFFER_ADD (",");
+
+ BUFFER_ADD ("\"%s\"", ds->ds[i].name);
+ } /* for ds->ds_num */
+ BUFFER_ADD ("]");
+
+#undef BUFFER_ADD
+
+ DEBUG ("format_json: dsnames_to_json: buffer = %s;", buffer);
+
+ return (0);
+} /* }}} int dsnames_to_json */
+
+static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
+ const data_set_t *ds, const value_list_t *vl, int store_rates)
+{
char temp[512];
size_t offset = 0;
int status;
* a square bracket in `format_json_finalize'. */
BUFFER_ADD (",{");
- status = values_to_json (temp, sizeof (temp), ds, vl);
+ status = values_to_json (temp, sizeof (temp), ds, vl, store_rates);
if (status != 0)
return (status);
BUFFER_ADD ("\"values\":%s", temp);
+ status = dstypes_to_json (temp, sizeof (temp), ds, vl);
+ if (status != 0)
+ return (status);
+ BUFFER_ADD (",\"dstypes\":%s", temp);
+
+ status = dsnames_to_json (temp, sizeof (temp), ds, vl);
+ if (status != 0)
+ return (status);
+ BUFFER_ADD (",\"dsnames\":%s", temp);
+
BUFFER_ADD (",\"time\":%lu", (unsigned long) vl->time);
BUFFER_ADD (",\"interval\":%i", vl->interval);
static int format_json_value_list_nocheck (char *buffer, /* {{{ */
size_t *ret_buffer_fill, size_t *ret_buffer_free,
const data_set_t *ds, const value_list_t *vl,
- size_t temp_size)
+ int store_rates, size_t temp_size)
{
char temp[temp_size];
int status;
- status = value_list_to_json (temp, sizeof (temp), ds, vl);
+ status = value_list_to_json (temp, sizeof (temp), ds, vl, store_rates);
if (status != 0)
return (status);
temp_size = strlen (temp);
int format_json_value_list (char *buffer, /* {{{ */
size_t *ret_buffer_fill, size_t *ret_buffer_free,
- const data_set_t *ds, const value_list_t *vl)
+ const data_set_t *ds, const value_list_t *vl, int store_rates)
{
if ((buffer == NULL)
|| (ret_buffer_fill == NULL) || (ret_buffer_free == NULL)
return (format_json_value_list_nocheck (buffer,
ret_buffer_fill, ret_buffer_free, ds, vl,
- (*ret_buffer_free) - 2));
+ store_rates, (*ret_buffer_free) - 2));
} /* }}} int format_json_value_list */
/* vim: set sw=2 sts=2 et fdm=marker : */
size_t *ret_buffer_fill, size_t *ret_buffer_free);
int format_json_value_list (char *buffer,
size_t *ret_buffer_fill, size_t *ret_buffer_free,
- const data_set_t *ds, const value_list_t *vl);
+ const data_set_t *ds, const value_list_t *vl, int store_rates);
int format_json_finalize (char *buffer,
size_t *ret_buffer_fill, size_t *ret_buffer_free);
return (l ? l->size : 0);
}
+static int llist_strcmp (llentry_t *e, void *ud)
+{
+ if ((e == NULL) || (ud == NULL))
+ return (-1);
+ return (strcmp (e->key, (const char *)ud));
+}
+
llentry_t *llist_search (llist_t *l, const char *key)
{
+ return (llist_search_custom (l, llist_strcmp, (void *)key));
+}
+
+llentry_t *llist_search_custom (llist_t *l,
+ int (*compare) (llentry_t *, void *), void *user_data)
+{
llentry_t *e;
if (l == NULL)
return (NULL);
- for (e = l->head; e != NULL; e = e->next)
- if (strcmp (key, e->key) == 0)
+ e = l->head;
+ while (e != NULL) {
+ llentry_t *next = e->next;
+
+ if (compare (e, user_data) == 0)
break;
+ e = next;
+ }
+
return (e);
}
int llist_size (llist_t *l);
llentry_t *llist_search (llist_t *l, const char *key);
+llentry_t *llist_search_custom (llist_t *l,
+ int (*compare) (llentry_t *, void *), void *user_data);
llentry_t *llist_head (llist_t *l);
llentry_t *llist_tail (llist_t *l);
#include <regex.h>
#define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
+#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
struct cu_match_s
{
regex_t regex;
+ regex_t excluderegex;
int flags;
int (*callback) (const char *str, char * const *matches, size_t matches_num,
/*
* Public functions
*/
-cu_match_t *match_create_callback (const char *regex,
+cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
int (*callback) (const char *str,
char * const *matches, size_t matches_num, void *user_data),
void *user_data)
cu_match_t *obj;
int status;
- DEBUG ("utils_match: match_create_callback: regex = %s", regex);
+ DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
+ regex, excluderegex);
obj = (cu_match_t *) malloc (sizeof (cu_match_t));
if (obj == NULL)
return (NULL);
}
+ if (excluderegex && strcmp(excluderegex, "") != 0) {
+ status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
+ if (status != 0)
+ {
+ ERROR ("Compiling the excluding regular expression \"%s\" failed.",
+ excluderegex);
+ sfree (obj);
+ return (NULL);
+ }
+ obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
+ }
+
obj->callback = callback;
obj->user_data = user_data;
return (obj);
} /* cu_match_t *match_create_callback */
-cu_match_t *match_create_simple (const char *regex, int match_ds_type)
+cu_match_t *match_create_simple (const char *regex,
+ const char *excluderegex, int match_ds_type)
{
cu_match_value_t *user_data;
cu_match_t *obj;
memset (user_data, '\0', sizeof (cu_match_value_t));
user_data->ds_type = match_ds_type;
- obj = match_create_callback (regex, default_callback, user_data);
+ obj = match_create_callback (regex, excluderegex,
+ default_callback, user_data);
if (obj == NULL)
{
sfree (user_data);
if ((obj == NULL) || (str == NULL))
return (-1);
+ if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
+ status = regexec (&obj->excluderegex, str,
+ STATIC_ARRAY_SIZE (re_match), re_match,
+ /* eflags = */ 0);
+ /* Regex did match, so exclude this line */
+ if (status == 0) {
+ DEBUG("ExludeRegex matched, don't count that line\n");
+ return (0);
+ }
+ }
+
status = regexec (&obj->regex, str,
STATIC_ARRAY_SIZE (re_match), re_match,
/* eflags = */ 0);
* then only the submatch (the part in the parenthesis) will be passed to the
* callback. If there is no submatch, then the entire string is passed to the
* callback.
+ * The optional `excluderegex' allows to exclude the line from the match, if
+ * the excluderegex matches.
*/
-cu_match_t *match_create_callback (const char *regex,
+cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
int (*callback) (const char *str,
char * const *matches, size_t matches_num, void *user_data),
void *user_data);
* The function will not search for anything in the string and increase
* value.counter by one.
*/
-cu_match_t *match_create_simple (const char *regex, int ds_type);
+cu_match_t *match_create_simple (const char *regex,
+ const char *excluderegex, int ds_type);
/*
* NAME
} /* int tail_match_add_match */
int tail_match_add_match_simple (cu_tail_match_t *obj,
- const char *regex, int ds_type,
+ const char *regex, const char *excluderegex, int ds_type,
const char *plugin, const char *plugin_instance,
const char *type, const char *type_instance)
{
cu_tail_match_simple_t *user_data;
int status;
- match = match_create_simple (regex, ds_type);
+ match = match_create_simple (regex, excluderegex, ds_type);
if (match == NULL)
return (-1);
* The values gathered are dispatched by the tail_match module in this case. The
* passed `plugin', `plugin_instance', `type', and `type_instance' are
* directly used when submitting these values.
+ * With excluderegex it is possible to exlude lines from the match.
*
* RETURN VALUE
* Zero upon success, non-zero otherwise.
*/
int tail_match_add_match_simple (cu_tail_match_t *obj,
- const char *regex, int ds_type,
+ const char *regex, const char *excluderegex, int ds_type,
const char *plugin, const char *plugin_instance,
const char *type, const char *type_instance);
int verify_peer;
int verify_host;
char *cacert;
+ int store_rates;
#define WH_FORMAT_COMMAND 0
#define WH_FORMAT_JSON 1
ssnprintf (cb->credentials, credentials_size, "%s:%s",
cb->user, (cb->pass == NULL) ? "" : cb->pass);
curl_easy_setopt (cb->curl, CURLOPT_USERPWD, cb->credentials);
- curl_easy_setopt (cb->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+ curl_easy_setopt (cb->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
}
curl_easy_setopt (cb->curl, CURLOPT_SSL_VERIFYPEER, cb->verify_peer);
static int wh_value_list_to_string (char *buffer, /* {{{ */
size_t buffer_size,
- const data_set_t *ds, const value_list_t *vl)
+ const data_set_t *ds, const value_list_t *vl,
+ wh_callback_t *cb)
{
size_t offset = 0;
int status;
int i;
+ gauge_t *rates = NULL;
assert (0 == strcmp (ds->type, vl->type));
status = ssnprintf (buffer + offset, buffer_size - offset, \
__VA_ARGS__); \
if (status < 1) \
+ { \
+ sfree (rates); \
return (-1); \
+ } \
else if (((size_t) status) >= (buffer_size - offset)) \
+ { \
+ sfree (rates); \
return (-1); \
+ } \
else \
offset += ((size_t) status); \
} while (0)
BUFFER_ADD ("%lu", (unsigned long) vl->time);
for (i = 0; i < ds->ds_num; i++)
-{
- if (ds->ds[i].type == DS_TYPE_GAUGE)
- BUFFER_ADD (":%f", vl->values[i].gauge);
- else if (ds->ds[i].type == DS_TYPE_COUNTER)
- BUFFER_ADD (":%llu", vl->values[i].counter);
- else if (ds->ds[i].type == DS_TYPE_DERIVE)
- BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
- else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
- BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
- else
{
- ERROR ("write_http plugin: Unknown data source type: %i",
- ds->ds[i].type);
- return (-1);
- }
-} /* for ds->ds_num */
+ if (ds->ds[i].type == DS_TYPE_GAUGE)
+ BUFFER_ADD (":%f", vl->values[i].gauge);
+ else if (cb->store_rates)
+ {
+ if (rates == NULL)
+ rates = uc_get_rate (ds, vl);
+ if (rates == NULL)
+ {
+ WARNING ("write_http plugin: "
+ "uc_get_rate failed.");
+ return (-1);
+ }
+ BUFFER_ADD (":%g", rates[i]);
+ }
+ else if (ds->ds[i].type == DS_TYPE_COUNTER)
+ BUFFER_ADD (":%llu", vl->values[i].counter);
+ else if (ds->ds[i].type == DS_TYPE_DERIVE)
+ BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
+ else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+ BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
+ else
+ {
+ ERROR ("write_http plugin: Unknown data source type: %i",
+ ds->ds[i].type);
+ sfree (rates);
+ return (-1);
+ }
+ } /* for ds->ds_num */
#undef BUFFER_ADD
-return (0);
+ sfree (rates);
+ return (0);
} /* }}} int wh_value_list_to_string */
static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{{ */
/* Convert the values to an ASCII representation and put that into
* `values'. */
- status = wh_value_list_to_string (values, sizeof (values), ds, vl);
+ status = wh_value_list_to_string (values, sizeof (values), ds, vl, cb);
if (status != 0) {
ERROR ("write_http plugin: error with "
"wh_value_list_to_string");
status = format_json_value_list (cb->send_buffer,
&cb->send_buffer_fill,
&cb->send_buffer_free,
- ds, vl);
+ ds, vl, cb->store_rates);
if (status == (-ENOMEM))
{
status = wh_flush_nolock (/* timeout = */ -1, cb);
status = format_json_value_list (cb->send_buffer,
&cb->send_buffer_fill,
&cb->send_buffer_free,
- ds, vl);
+ ds, vl, cb->store_rates);
}
if (status != 0)
{
config_set_string (&cb->cacert, child);
else if (strcasecmp ("Format", child->key) == 0)
config_set_format (cb, child);
+ else if (strcasecmp ("StoreRates", child->key) == 0)
+ config_set_boolean (&cb->store_rates, child);
else
{
ERROR ("write_http plugin: Invalid configuration "
#!/bin/sh
-DEFAULT_VERSION="4.8.2.git"
+DEFAULT_VERSION="4.8.4.git"
VERSION="`git describe 2> /dev/null | sed -e 's/^collectd-//'`"