Merge branch 'collectd-3.11'
authorFlorian Forster <octo@huhu.verplant.org>
Wed, 30 May 2007 06:06:48 +0000 (08:06 +0200)
committerFlorian Forster <octo@huhu.verplant.org>
Wed, 30 May 2007 06:06:48 +0000 (08:06 +0200)
Conflicts:

ChangeLog
configure.in

121 files changed:
AUTHORS
ChangeLog
Makefile.am
README
collectd.spec [deleted file]
configure.in
contrib/PerlLib/Collectd/Unixsock.pm [new file with mode: 0644]
contrib/README
contrib/collection.cgi
contrib/extractDS.px [new file with mode: 0755]
contrib/fedora/collectd.spec [new file with mode: 0644]
contrib/fedora/init.d-collectd [new file with mode: 0644]
contrib/init.d-rh7 [deleted file]
contrib/iptables/accounting.sh [new file with mode: 0755]
contrib/migrate-3-4.px [new file with mode: 0755]
contrib/sles10.1/collectd.spec [new file with mode: 0644]
contrib/sles10.1/init.d-collectd [new file with mode: 0755]
src/Makefile.am
src/apache.c
src/apcups.c
src/apple_sensors.c
src/battery.c
src/collectd-email.pod [new file with mode: 0644]
src/collectd-exec.pod [new file with mode: 0644]
src/collectd-nagios.c [new file with mode: 0644]
src/collectd-nagios.pod [new file with mode: 0644]
src/collectd-perl.pod [new file with mode: 0644]
src/collectd-unixsock.pod [new file with mode: 0644]
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.h
src/collectd.pod
src/common.c
src/common.h
src/configfile.c
src/configfile.h
src/cpu.c
src/cpufreq.c
src/csv.c [new file with mode: 0644]
src/df.c
src/disk.c
src/dns.c
src/email.c
src/entropy.c [new file with mode: 0644]
src/exec.c [new file with mode: 0644]
src/hddtemp.c
src/interface.c [new file with mode: 0644]
src/iptables.c [new file with mode: 0644]
src/irq.c [new file with mode: 0644]
src/libconfig/AUTHORS [deleted file]
src/libconfig/LICENSE [deleted file]
src/libconfig/Makefile.am [deleted file]
src/libconfig/README [deleted file]
src/libconfig/TODO [deleted file]
src/libconfig/compat.h [deleted file]
src/libconfig/conf_apache.c [deleted file]
src/libconfig/conf_apache.h [deleted file]
src/libconfig/conf_colon.c [deleted file]
src/libconfig/conf_colon.h [deleted file]
src/libconfig/conf_equal.c [deleted file]
src/libconfig/conf_equal.h [deleted file]
src/libconfig/conf_section.c [deleted file]
src/libconfig/conf_section.h [deleted file]
src/libconfig/conf_space.c [deleted file]
src/libconfig/conf_space.h [deleted file]
src/libconfig/conf_xml.c [deleted file]
src/libconfig/conf_xml.h [deleted file]
src/libconfig/libconfig.c [deleted file]
src/libconfig/libconfig.h [deleted file]
src/libconfig/libconfig_private.h [deleted file]
src/libconfig/test-lc.c [deleted file]
src/libconfig/win32.h [deleted file]
src/liboconfig/AUTHORS [new file with mode: 0644]
src/liboconfig/COPYING [new file with mode: 0644]
src/liboconfig/ChangeLog [new file with mode: 0644]
src/liboconfig/Makefile.am [new file with mode: 0644]
src/liboconfig/aux_types.h [new file with mode: 0644]
src/liboconfig/oconfig.c [new file with mode: 0644]
src/liboconfig/oconfig.h [new file with mode: 0644]
src/liboconfig/parser.y [new file with mode: 0644]
src/liboconfig/scanner.l [new file with mode: 0644]
src/liboping/Makefile.am
src/load.c
src/logfile.c [new file with mode: 0644]
src/mbmon.c
src/memory.c
src/multimeter.c
src/mysql.c
src/network.c
src/network.h
src/nfs.c
src/ntpd.c
src/nut.c [new file with mode: 0644]
src/perl.c [new file with mode: 0644]
src/ping.c
src/plugin.c
src/plugin.h
src/processes.c
src/rrdtool.c [new file with mode: 0644]
src/sensors.c
src/serial.c
src/swap.c
src/syslog.c [new file with mode: 0644]
src/tape.c
src/traffic.c [deleted file]
src/types.db [new file with mode: 0644]
src/types_list.c [new file with mode: 0644]
src/types_list.h [new file with mode: 0644]
src/unixsock.c [new file with mode: 0644]
src/users.c
src/utils_avltree.c [new file with mode: 0644]
src/utils_avltree.h [new file with mode: 0644]
src/utils_debug.c [deleted file]
src/utils_debug.h [deleted file]
src/utils_ignorelist.c
src/utils_llist.c [new file with mode: 0644]
src/utils_llist.h [new file with mode: 0644]
src/utils_mount.c
src/vserver.c
src/wireless.c

diff --git a/AUTHORS b/AUTHORS
index b123877..0c1b322 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,18 +4,24 @@ This package was written by:
 apcups plugin by:
   Anthony Gialluca <tonyabg at charter.net>
 
-cpufreq and multimeter module by:
+cpufreq, multimeter and irq module by:
   Peter Holik <peter at holik.at>
 
 hddtemp module by:
   Vincent Stehlé <vincent.stehle at free.fr>
 
+iptables module by:
+  Sjoerd van der Berg <harekiet at gmail.com>
+
 mbmon plugin by:
   Flavio Stanchina <flavio at stanchina.net>
 
 nfs module by:
   Jason Pepas <cell at ices.utexas.edu>
 
+perl module by:
+  Sebastian Harl <sh at tokkee.org>
+
 processes module by:
   Lyonel Vincent <lyonel at ezix.org>
 
index d6c87c6..afcb453 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,48 @@
+2007-04-02, Version 4.0.0
+       * collectd: The plugin-infrastructure has been changed to allow for
+         more types of plugins, namely `write' and `log' plugins.
+       * collectd: The read-function has been changed to read many plugins in
+         parallel, using threads. Thus, plugins generally need to use
+         thread-safe functions from now on.
+       * collectd: The '-t' command line options allows to perform syntax tests
+         of the configuration file and exit immediately.
+       * csv plugin: The new `csv' plugin handles output to `comma separated
+         values'-files.
+       * rrdtool plugin: The new `rrdtool' plugin handles output to
+         RRD-files. Data can be cached to combine multiple updates into one
+         write to increase IO-performance.
+       * network plugin: The new `network' plugin handles IO via the network.
+         It implements a different, much more extensible protocol which can
+         combine many values in one packet, decreasing the number of UDP-
+         packets being sent. It can read from and send to the network and
+         with the appropriate configuration even forward packets to other
+         networks.
+       * unixsock plugin: The new `unixsock' plugin provides an interface to
+         communicate with the daemon while it is running. Right now the
+         commands `GETVAL' and `PUTVAL' are implemented, but more are to
+         come.
+       * perl plugin: The new `perl' plugin allows you to write extensions
+         for collectd in the scripting-language Perl.
+       * logfile plugin: The new `logfile' plugin writes logmessages to files
+         or STDOUT or STDERR.
+       * syslog plugin: The new `syslog' plugin sends logmessages to the
+         system's syslog daemon.
+       * entropy plugin: The new `entropy' plugin collects the amount of
+         entropy currently being available to the system.
+       * exec plugin: The new `exec' plugin forks child processes and reads
+         back values provided by the forked processes.
+       * iptables plugin: The new `iptables' plugin reads counters from
+         iptables rules. Thanks to Sjoerd van der Berg for contributing this
+         plugin.
+       * irq plugin: The new `irq' plugin collects the IRQ-counters. Thanks
+         to Peter Holik for contributing this plugin.
+       * nut plugin: The new `nut' plugin connects the upsd of the `network
+         ups tools' and reads information about the connected UPS.
+       * apache plugin: Support for lighttpd's `BusyServers' (aka.
+         connections) field was added by Florent Monbillard.
+       * collectd-nagios: The new `collectd-nagios' binary queries values
+         from collectd, parses them and exits according to Nagios-standards.
+
 2007-05-29, Version 3.11.5
        * configure: Added `AC_SYS_LARGEFILE' for LFS.
        * ntpd plugin: Fix a potential buffer overflow.
index 5015b66..34c36cc 100644 (file)
@@ -2,7 +2,7 @@ SUBDIRS = libltdl src
 
 INCLUDES = $(LTDLINCL)
 
-EXTRA_DIST = collectd.spec contrib
+EXTRA_DIST = contrib
 
 dist-hook:
        find $(distdir) -type d -name '.svn' | xargs rm -rf
diff --git a/README b/README
index 4a09fee..555af86 100644 (file)
--- a/README
+++ b/README
@@ -14,99 +14,182 @@ Features
 
   * collectd is able to collect the following data:
 
-    - Apache server utilization
-      (Number of bytes transfered, number of requests handled and detailed
-      scoreboard statistics)
+    - apache
+      Apache server utilization: Number of bytes transfered, number of
+      requests handled and detailed scoreboard statistics
 
-    - APC UPS Daemon
-      (UPS charge, load, input/output/battery voltage, etc)
+    - apcups
+      APC UPS Daemon: UPS charge, load, input/output/battery voltage, etc.
 
-    - Apple Sensors
-      (Temperature, fanspeed and voltage sensors of apple computers)
+    - apple_sensors
+      Sensors in Macs running Mac OS X / Darwin: Temperature, fanspeed and
+      voltage sensors.
 
-    - Battery
-      (Charge, current and charge of ACPI and PMU based batteries)
+    - battery
+      Batterycharge, -current and volatage of ACPI and PMU based laptop
+      batteries.
 
-    - CPU utilization
-      (Time spent in system, user, nice and idle)
+    - cpu
+      CPU utilization: Time spent in the system, user, nice, idle, and related
+      states.
 
-    - CPU frequency
-      (For laptops with speed step or a similar technology)
+    - cpufreq
+      CPU frequency (For laptops with speed step or a similar technology)
 
-    - Mountpoint usage
-      (Basically the values `df(1)' delivers)
+    - df
+      Mountpoint usage (Basically the values `df(1)' delivers)
 
-    - Disk utilization
-      (Sectors read/written, number of read/write actions, time spent doing IO)
+    - disk
+      Disk utilization: Sectors read/written, number of read/write actions,
+      average time an IO-operation took to complete.
 
-    - DNS traffic
-      (query types, response codes, opcodes and traffic)
+    - dns
+      DNS traffic: Query types, response codes, opcodes and traffic/octets
+      transfered.
 
-    - Email statistics
-      (count, traffic, spam scores and checks)
+    - email
+      Email statistics: Count, traffic, spam scores and checks.
+      See collectd-email(5).
 
-    - Harddisk temperatures
-      (Uhm, yeah, temperature of harddisks that is ;)
+    - entropy
+      Amount of entropy available to the system.
 
-    - System load
-      (Load average over the last 1, 5 and 15 minutes)
+    - exec
+      Values gathered by a custom program or script.
+      See collectd-exec(5).
 
-    - mbmon - motherboard monitoring
-      (temperature, fanspeed and voltage information)
+    - hddtemp
+      Harddisk temperatures using hddtempd.
 
-    - Memory utilization
-      (Memory occupied by running processes, page cache, buffer cache and free)
+    - interface
+      Interface traffic: Number of octets, packets and errors for each
+      interface.
 
-    - Multimeter
-      (Information provided by serial multimeters, such as the Â«Metex M-4650CR»)
+    - iptables
+      Iptables' counters: Number of bytes that were matched by a certain
+      iptables rule.
 
-    - MySQL server statistics
-      (Commands issued, handlers triggered, thread usage, query cache
-      utilization and traffic sent/received)
+    - irq
+      IRQ counters: Frequency in which certain interrupts occur.
 
-    - NFS Procedures
-      (Which NFS command were called how often. Only NFSv2 and NFSv3 right now)
+    - load
+      System load average over the last 1, 5 and 15 minutes.
 
-    - NTP Daemon
-      (Local clock drift, offset to peers, etc)
+    - mbmon
+      Motherboard sensors: temperature, fanspeed and voltage information,
+      using mbmon(1).
 
-    - Ping latency
-      (Time to reach the default gateway or another given host)
+    - memory
+      Memory utilization: Memory occupied by running processes, page cache,
+      buffer cache and free.
 
-    - Process counts
-      (Number of running, sleeping, zombie, ... processes)
+    - multimeter
+      Information provided by serial multimeters, such as the `Metex
+      M-4650CR'.
 
-    - Sensors
-      (lm_sensors voltages, temperatures and fan rotation speeds)
+    - mysql
+      MySQL server statistics: Commands issued, handlers triggered, thread
+      usage, query cache utilization and traffic/octets sent and received.
 
-    - Serial
-      (RX and TX of serial interfaces)
+    - network
+      Receive values that were collected by other hosts. Large setups will
+      want to collect the data on one dedicated machine, and this is the
+      plugin of choice for that.
 
-    - Swap
-      (Pages swapped out onto harddisk or whatever is called `swap' by the OS..)
+    - nfs
+      NFS Procedures: Which NFS command were called how often. Only NFSv2 and
+      NFSv3 right now.
 
-    - Tape
-      (Read and write bytes and operations on tape devices)
+    - ntp
+      NTP daemon statistics: Local clock drift, offset to peers, etc.
 
-    - Traffic
-      (In/Outbound traffic on the interfaces)
+    - nut
+      Network UPS tools: UPS current, voltage, power, charge, utilisation,
+      temperature, etc. See upsd(8).
 
-    - Users
-      (Currently logged in users)
+    - perl
+      The perl plugin implements a Perl-interpreter into collectd. You can
+      write your own plugins in Perl and return arbitary values using this
+      API. See collectd-perl(5).
 
-    - VServer
-      (System ressources used by vservers)
+    - ping
+      Network latency: Time to reach the default gateway or another given
+      host.
 
-    - Wireless
-      (Link quality of wireless cards)
+    - processes
+      Process counts: Number of running, sleeping, zombie, ... processes.
 
-  * Performance: Running as a daemon collectd doesn't spend much time in
-    startup. Since collectd links against libping, librrd and libsensors it
-    doesn't need to start any other processes.
+    - sensors
+      System sensors, accessed using lm_sensors: Voltages, temperatures and
+      fan rotation speeds.
 
-  * Hardly any maintenance neccessary and setup is trivial.
+    - serial
+      RX and TX of serial interfaces. Linux only; needs root privileges.
 
-  * Extremely easy and failsafe network operation possible.
+    - swap
+      Pages swapped out onto harddisk or whatever is called `swap' by the OS..
+
+    - tape
+      Bytes and operations read and written on tape devices. Solaris only.
+
+    - users
+      Users currently logged in.
+
+    - vserver
+      System ressources used by Linux VServers.
+      See <http://linux-vserver.org/>.
+
+    - wireless
+      Link quality of wireless cards. Linux only.
+
+  * Output can be written or send to various destinations by the following
+    plugins:
+
+    - csv
+      Write to comma seperated values (CSV) files. This needs lots of
+      diskspace but is extremely portable and can be analysed with almost
+      every program that can analyse anything. Even Microsoft's Excel..
+
+    - network
+      Send the data to a remote host to save the data somehow. This is useful
+      for large setups where the data should be saved by a dedicated machine.
+
+    - perl
+      Of course the values are propagated to plugins written in Perl, too, so
+      you can easily do weird stuff with the plugins we didn't dare think of
+      ;) See collectd-perl(5).
+
+    - rrdtool
+      Output to round-robin-database (RRD) files using librrd. See rrdtool(1).
+      This is likely the most popular destination for such values. Since
+      updates to RRD-files are somewhat expensive this plugin can cache
+      updates to the files and write a bunch of updates at once, which lessens
+      system load a lot.
+
+    - unixsock
+      One can query the values from the unixsock plugin whenever they're
+      needed. Please read collectd-unixsock(5) for a description on how that's
+      done.
+
+  * Logging is, as everything in collectd, provided by plugins. The following
+    plugins keep up informed about what's going on:
+
+    - logfile
+      Writes logmessages to a file or STDOUT/STDERR.
+
+    - syslog
+      Logs to the standard UNIX logging mechanismn, syslog.
+
+  * Performance: Since collectd is running as a daemon it doesn't spend much
+    time starting up again and again. With the exception of the exec plugin no
+    processes are forked. Caching in output plugins, such as the rrdtool and
+    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.
+    
+  * Once set up, hardly any maintenence is neccessary. Setup is kept as easy
+    as possible and the default values should be okay for most users.
 
 
 Operation
@@ -116,23 +199,25 @@ Operation
     Run `collectd -h' for a list of builtin defaults. See `collectd.conf(5)'
     for a list of options and a syntax description.
 
-  * When running collectd writes system statistics in RRD-files. Per default
-    they reside in `/var/lib/collectd'.
+  * When the `csv' or `rrdtool' plugins are loaded they'll write the values to
+    files. The usual place for these files is beneath `/var/lib/collectd'.
 
-  * When using the `ping' plugin collectd needs to run as user root, since only
-    root can craft ICMP packages needed to ping other hosts. collectd should
-    NOT be installed setuid root since it can be used to overwrite valuable
-    files..
+  * When using some of the plugins, collectd needs to run as user root, since only
+    root can do certain things, such as craft ICMP packages needed to ping
+    other hosts. collectd should NOT be installed setuid root since it can be
+    used to overwrite valuable files!
 
   * Sample scripts to generate graphs reside in `contrib/' in the source
     package or somewhere near `/usr/share/doc/collectd' in most distributions.
     Please be aware that those script are meant as a starting point for your
     own experiments.. Some of them require the `RRDs' Perl module.
-    (`librrds-perl' on Debian)
+    (`librrds-perl' on Debian) If you have written a more sophisticated
+    solution please share it with us.
 
   * The RRAs of the automatically created RRD files depend on the `step'
-    and `heartbeat' settings given on compile time. For a list of the
-    default RRAs take a look in the collectd(1) manpage.
+    and `heartbeat' settings given. If change these settings you may need to
+    re-create the files, losing all data. Please be aware of that when changing
+    the values and read the rrdtool(1) manpage thoroughly.
 
 
 Prerequisites
@@ -142,37 +227,55 @@ Prerequisites
 
   * Usual suspects: C compiler, linker, preprocessor, make, ...
 
-  * rrdtool (optional; headers and library; rrdtool 1.0 and 1.2 both work fine)
+  * A POSIX-threads (pthread) implementation.
+    Since gathering some statistics is slow (network connections, slow devices,
+    etc) the collectd is parellelized. The POSIX threads interface is being
+    used and should be found in various implementations for hopefully all
+    platforms.
+
+  * libcurl (optional)
+    If you want to use the `apache' plugin
+
+  * libiptc (optional)
+    For querying iptables counters.
+
+  * libmysqlclient (optional)
+
+  * liboping (optional, if not found a version shipped with this distribution
+    can be used)
+    Used by the `ping' plugin to send and receive ICMP packets.
+
+  * libpcap (optional)
+    Used to capture packets by the `dns' plugin.
+
+  * librrd (optional; headers and library; rrdtool 1.0 and 1.2 both work fine)
     If built without `librrd' the resulting binary will be `client only', i.e.
     will send it's values via multicast and not create any RRD files itself.
     Alternatively you can chose to write CSV-files (Comma Seperated Values)
     instead.
 
-  * libmysqlclient (optional)
-
-  * lm-sensors (optional)
+  * libsensors (optional)
+    To read from `lm_sensors'.
 
   * libstatgrab may be used to collect statistics on systems other than Linux
     and/or Solaris. Note that CPU- and disk-statistics, while being provided
     by this library, are not supported in collectd right now..
     <http://www.i-scream.org/libstatgrab/> 
 
-  * libcurl (optional)
-    If you want to use the `apache' plugin
+  * libupsclient/nut (optional)
+    For the `nut' plugin which queries nut's `upsd'.
 
   * librt, libsocket, libkstat, libdevinfo
     Various standard Solaris libraries which provide system functions.
 
-  * libpthread (optional)
-    For parallelization, especially for plugins that communicate with the
-    outside, e. g. with a socket.
-
   * CoreFoundation.framework and IOKit.framework
-    For copiling on darwin in general and the `apple_sensors' plugin in
+    For compiling on Darwin in general and the `apple_sensors' plugin in
     particular.
 
 Author
 ------
 
-  Florian octo Forster <octo at verplant.org>
+  Florian octo Forster <octo at verplant.org>,
+  Sebastian tokkee Harl <sh at tokkee.org>,
+  and many contributors (see `AUTHORS').
 
diff --git a/collectd.spec b/collectd.spec
deleted file mode 100644 (file)
index 6116ffd..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-Summary:       Statistics collection daemon for filling RRD files.
-Name:           collectd
-Version:       3.11.2
-Release:       0
-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
-Requires:      rrdtool
-Packager:      Florian octo Forster <octo@verplant.org>
-Vendor:                Florian octo Forster <octo@verplant.org>
-
-%description
-collectd is a small daemon written in C for performance.  It reads various
-system  statistics  and updates  RRD files,  creating  them if neccessary.
-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.
-
-%package apache
-Summary:       apache-plugin for collectd.
-Group:         System Environment/Daemons
-Requires:      collectd = %{version}, libcurl3
-%description apache
-This plugin collectd data provided by Apache's `mod_status'.
-
-%package mysql
-Summary:       mysql-module for collectd.
-Group:         System Environment/Daemons
-Requires:      collectd = %{version}, mysql
-%description mysql
-MySQL  querying  plugin.  This plugins  provides data of  issued commands,
-called handlers and database traffic.
-
-%package sensors
-Summary:       libsensors-module for collectd.
-Group:         System Environment/Daemons
-Requires:      collectd = %{version}, lm_sensors
-%description sensors
-This  plugin  for  collectd  provides  querying  of sensors  supported  by
-lm_sensors.
-
-%prep
-rm -rf $RPM_BUILD_ROOT
-%setup
-
-%build
-./configure --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir}
-make
-
-%install
-make install DESTDIR=$RPM_BUILD_ROOT
-mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
-cp src/collectd.conf $RPM_BUILD_ROOT/etc/collectd.conf
-cp contrib/init.d-rh7 $RPM_BUILD_ROOT/etc/rc.d/init.d/collectd
-mkdir -p $RPM_BUILD_ROOT/var/lib/collectd
-rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/*.a
-rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/*.la
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%files
-%defattr(-,root,root)
-%doc AUTHORS COPYING ChangeLog INSTALL NEWS README
-%doc contrib
-%config /etc/collectd.conf
-%attr(0755,root,root) /etc/rc.d/init.d/collectd
-%attr(0755,root,root) %{_sbindir}/collectd
-%attr(0444,root,root) %{_mandir}/man1/*
-%attr(0444,root,root) %{_mandir}/man5/*
-%attr(0444,root,root) %{_libdir}/%{name}/apcups.so*
-%attr(0444,root,root) %{_libdir}/%{name}/apple_sensors.so*
-%attr(0444,root,root) %{_libdir}/%{name}/battery.so*
-%attr(0444,root,root) %{_libdir}/%{name}/cpu.so*
-%attr(0444,root,root) %{_libdir}/%{name}/cpufreq.so*
-%attr(0444,root,root) %{_libdir}/%{name}/df.so*
-%attr(0444,root,root) %{_libdir}/%{name}/disk.so*
-%attr(0444,root,root) %{_libdir}/%{name}/dns.so
-%attr(0444,root,root) %{_libdir}/%{name}/email.so
-%attr(0444,root,root) %{_libdir}/%{name}/hddtemp.so*
-%attr(0444,root,root) %{_libdir}/%{name}/load.so*
-%attr(0444,root,root) %{_libdir}/%{name}/mbmon.so
-%attr(0444,root,root) %{_libdir}/%{name}/memory.so*
-%attr(0444,root,root) %{_libdir}/%{name}/multimeter.so*
-%attr(0444,root,root) %{_libdir}/%{name}/nfs.so*
-%attr(0444,root,root) %{_libdir}/%{name}/ntpd.so*
-%attr(0444,root,root) %{_libdir}/%{name}/ping.so*
-%attr(0444,root,root) %{_libdir}/%{name}/processes.so*
-%attr(0444,root,root) %{_libdir}/%{name}/serial.so*
-%attr(0444,root,root) %{_libdir}/%{name}/swap.so*
-%attr(0444,root,root) %{_libdir}/%{name}/tape.so*
-%attr(0444,root,root) %{_libdir}/%{name}/traffic.so*
-%attr(0444,root,root) %{_libdir}/%{name}/users.so*
-%attr(0444,root,root) %{_libdir}/%{name}/vserver.so*
-%attr(0444,root,root) %{_libdir}/%{name}/wireless.so*
-
-%dir /var/lib/collectd
-
-%files apache
-%attr(0444,root,root) %{_libdir}/%{name}/apache.so*
-
-%files mysql
-%attr(0444,root,root) %{_libdir}/%{name}/mysql.so*
-
-%files sensors
-%attr(0444,root,root) %{_libdir}/%{name}/sensors.so*
-
-%changelog
-* Sun Jul 09 2006 Florian octo Forster <octo@verplant.org> 3.10.0-1
-- New upstream version
-
-* Tue Jun 25 2006 Florian octo Forster <octo@verplant.org> 3.9.4-1
-- New upstream version
-
-* Tue Jun 01 2006 Florian octo Forster <octo@verplant.org> 3.9.3-1
-- New upstream version
-
-* Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.9.2-1
-- New upstream version
-
-* Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.8.5-1
-- New upstream version
-
-* Fri Apr 21 2006 Florian octo Forster <octo@verplant.org> 3.9.1-1
-- New upstream version
-
-* Fri Apr 14 2006 Florian octo Forster <octo@verplant.org> 3.9.0-1
-- New upstream version
-- Added the `apache' package.
-
-* Thu Mar 14 2006 Florian octo Forster <octo@verplant.org> 3.8.2-1
-- New upstream version
-
-* Thu Mar 13 2006 Florian octo Forster <octo@verplant.org> 3.8.1-1
-- New upstream version
-
-* Thu Mar 09 2006 Florian octo Forster <octo@verplant.org> 3.8.0-1
-- New upstream version
-
-* Sat Feb 18 2006 Florian octo Forster <octo@verplant.org> 3.7.2-1
-- Include `tape.so' so the build doesn't terminate because of missing files..
-- New upstream version
-
-* Sat Feb 04 2006 Florian octo Forster <octo@verplant.org> 3.7.1-1
-- New upstream version
-
-* Mon Jan 30 2006 Florian octo Forster <octo@verplant.org> 3.7.0-1
-- New upstream version
-- Removed the extra `hddtemp' package
-
-* Tue Jan 24 2006 Florian octo Forster <octo@verplant.org> 3.6.2-1
-- New upstream version
-
-* Fri Jan 20 2006 Florian octo Forster <octo@verplant.org> 3.6.1-1
-- New upstream version
-
-* Fri Jan 20 2006 Florian octo Forster <octo@verplant.org> 3.6.0-1
-- New upstream version
-- Added config file, `collectd.conf(5)', `df.so'
-- Added package `collectd-mysql', dependency on `mysqlclient10 | mysql'
-
-* Wed Dec 07 2005 Florian octo Forster <octo@verplant.org> 3.5.0-1
-- New upstream version
-
-* Sat Nov 26 2005 Florian octo Forster <octo@verplant.org> 3.4.0-1
-- New upstream version
-
-* Sat Nov 05 2005 Florian octo Forster <octo@verplant.org> 3.3.0-1
-- New upstream version
-
-* Tue Oct 26 2005 Florian octo Forster <octo@verplant.org> 3.2.0-1
-- New upstream version
-- Added statement to remove the `*.la' files. This fixes a problem when
-  `Unpackaged files terminate build' is in effect.
-- Added `processes.so*' to the main package
-
-* Fri Oct 14 2005 Florian octo Forster <octo@verplant.org> 3.1.0-1
-- New upstream version
-- Added package `collectd-hddtemp'
-
-* Fri Sep 30 2005 Florian octo Forster <octo@verplant.org> 3.0.0-1
-- New upstream version
-- Split the package into `collectd' and `collectd-sensors'
-
-* Fri Sep 16 2005 Florian octo Forster <octo@verplant.org> 2.1.0-1
-- New upstream version
-
-* Mon Sep 10 2005 Florian octo Forster <octo@verplant.org> 2.0.0-1
-- New upstream version
-
-* Mon Aug 29 2005 Florian octo Forster <octo@verplant.org> 1.8.0-1
-- New upstream version
-
-* Sun Aug 25 2005 Florian octo Forster <octo@verplant.org> 1.7.0-1
-- New upstream version
-
-* Sun Aug 21 2005 Florian octo Forster <octo@verplant.org> 1.6.0-1
-- New upstream version
-
-* Sun Jul 17 2005 Florian octo Forster <octo@verplant.org> 1.5.1-1
-- New upstream version
-
-* Sun Jul 17 2005 Florian octo Forster <octo@verplant.org> 1.5-1
-- New upstream version
-
-* Mon Jul 11 2005 Florian octo Forster <octo@verplant.org> 1.4.2-1
-- New upstream version
-
-* Sat Jul 09 2005 Florian octo Forster <octo@verplant.org> 1.4-1
-- Built on RedHat 7.3
index c0bc516..e440749 100644 (file)
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-AC_INIT(collectd, 3.11.5)
+AC_INIT(collectd, 4.0.0-rc10)
 AC_CONFIG_SRCDIR(src/collectd.c)
 AC_CONFIG_HEADERS(src/config.h)
 AM_INIT_AUTOMAKE(dist-bzip2)
@@ -26,7 +26,8 @@ AC_SUBST(LTDLINCL)
 AC_SUBST(LIBLTDL)
 AC_LIBTOOL_DLOPEN
 AC_PROG_LIBTOOL
-#AC_PROG_RANLIB
+AC_PROG_LEX
+AC_PROG_YACC
 AC_CONFIG_SUBDIRS(libltdl)
 
 #
@@ -46,11 +47,12 @@ AC_CHECK_HEADERS(assert.h)
 AC_CHECK_HEADERS(sys/types.h)
 AC_CHECK_HEADERS(sys/socket.h)
 AC_CHECK_HEADERS(sys/select.h)
-AC_CHECK_HEADERS(sys/poll.h)
+AC_CHECK_HEADERS(poll.h)
 AC_CHECK_HEADERS(netdb.h)
 AC_CHECK_HEADERS(arpa/inet.h)
 AC_CHECK_HEADERS(sys/resource.h)
 AC_CHECK_HEADERS(sys/param.h)
+AC_CHECK_HEADERS(kstat.h)
 
 # For ping library
 AC_CHECK_HEADERS(netinet/in_systm.h, [], [],
@@ -151,6 +153,23 @@ AC_CHECK_HEADERS(netinet/icmp6.h, [], [],
 # include <netinet/ip6.h>
 #endif
 ])
+AC_CHECK_HEADERS(netinet/tcp.h, [], [],
+[#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_NETINET_IN_SYSTM_H
+# include <netinet/in_systm.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_IP_H
+# include <netinet/ip.h>
+#endif
+])
 AC_CHECK_HEADERS(netinet/udp.h, [], [],
 [#if HAVE_STDINT_H
 # include <stdint.h>
@@ -191,6 +210,7 @@ AC_CHECK_HEADERS(mach/kern_return.h)
 
 # For hddtemp module
 AC_CHECK_HEADERS(linux/major.h)
+AC_CHECK_HEADERS(libgen.h)
 
 # For the apple_sensors module
 AC_CHECK_HEADERS(CoreFoundation/CoreFoundation.h)
@@ -226,7 +246,7 @@ AC_CHECK_HEADERS(sys/swap.h)
 AC_CHECK_HEADERS(utmp.h)
 AC_CHECK_HEADERS(utmpx.h)
 
-# For traffic plugin
+# For interface plugin
 AC_CHECK_HEADERS(ifaddrs.h)
 AC_CHECK_HEADERS(net/if.h, [], [],
 [
@@ -263,7 +283,7 @@ AC_CHECK_HEADERS(linux/netdevice.h, [], [],
 AC_CHECK_HEADERS(curl/curl.h)
 
 # For quota module
-AC_CHECK_HEADERS(grp.h pwd.h sys/ucred.h)
+AC_CHECK_HEADERS(pwd.h sys/ucred.h)
 AC_CHECK_HEADERS(ctype.h)
 AC_CHECK_HEADERS(limits.h)
 AC_CHECK_HEADERS(sys/quota.h)
@@ -331,7 +351,13 @@ AC_CHECK_HEADERS(netinet/if_ether.h, [], [],
 #endif
 ])
 
-dnl Checking for libraries
+# For the multimeter plugin
+AC_CHECK_HEADERS(termios.h)
+AC_CHECK_HEADERS(sys/ioctl.h)
+
+#
+# Checking for libraries
+#
 AC_CHECK_LIB(m, ext)
 
 #
@@ -383,9 +409,104 @@ AC_CHECK_FUNCS(getutent getutxent)
 AC_CHECK_FUNCS(quotactl)
 AC_CHECK_FUNCS(getgrgid getpwuid)
 
-# For traffic module
+# For interface module
 AC_CHECK_FUNCS(getifaddrs)
 
+# Check for NAN
+nan_type="none"
+if test "x$nan_type" = "xnone"; then
+  AC_CACHE_CHECK([whether NAN is defined by default],
+    [have_nan_default],
+    AC_COMPILE_IFELSE(
+      AC_LANG_PROGRAM(
+      [[
+#include <stdlib.h>
+#include <math.h>
+static float foo = NAN;
+      ]],
+      [[
+       if (isnan (foo))
+        return 0;
+       else
+       return 1;
+      ]]),
+      [have_nan_default="yes"],
+      [have_nan_default="no"]
+    )
+  )
+  if test "x$have_nan_default" = "xyes"
+  then
+   nan_type="default"
+   AC_DEFINE(NAN_STATIC_DEFAULT, 1,
+     [Define if NAN is defined by default and can initialize static variables.])
+  fi
+fi
+if test "x$nan_type" = "xnone"; then
+  AC_CACHE_CHECK([whether NAN is defined by __USE_ISOC99],
+    [have_nan_isoc],
+    AC_COMPILE_IFELSE(
+      AC_LANG_PROGRAM(
+      [[
+#include <stdlib.h>
+#define __USE_ISOC99 1
+#include <math.h>
+static float foo = NAN;
+      ]],
+      [[
+       if (isnan (foo))
+        return 0;
+       else
+       return 1;
+      ]]),
+      [have_nan_isoc="yes"],
+      [have_nan_isoc="no"]
+    )
+  )
+  if test "x$have_nan_isoc" = "xyes"
+  then
+   nan_type="isoc99"
+   AC_DEFINE(NAN_STATIC_ISOC, 1,
+     [Define if NAN is defined by __USE_ISOC99 and can initialize static variables.])
+  fi
+fi
+if test "x$nan_type" = "xnone"; then
+  AC_CACHE_CHECK([whether NAN can be defined by 0/0],
+    [have_nan_zero],
+    AC_RUN_IFELSE(
+      AC_LANG_PROGRAM(
+      [[
+#include <stdlib.h>
+#include <math.h>
+#ifdef NAN
+# undef NAN
+#endif
+#define NAN (0.0 / 0.0)
+#ifndef isnan
+# define isnan(f) ((f) != (f))
+#endif
+static float foo = NAN;
+      ]],
+      [[
+       if (isnan (foo))
+        return 0;
+       else
+       return 1;
+      ]]),
+      [have_nan_zero="yes"],
+      [have_nan_zero="no"]
+    )
+  )
+  if test "x$have_nan_zero" = "xyes"
+  then
+   nan_type="zero"
+   AC_DEFINE(NAN_ZERO_ZERO, 1,
+     [Define if NAN can be defined as (0.0 / 0.0)])
+  fi
+fi
+if test "x$nan_type" = "xnone"; then
+  AC_MSG_ERROR([Didn't find out how to statically initialize variables to NAN. Sorry.])
+fi
+
 # For mount interface
 #AC_CHECK_FUNCS(getfsent getvfsent)
 
@@ -531,6 +652,15 @@ AC_CHECK_MEMBERS([struct udphdr.dest, struct udphdr.source], [], [],
 #endif
 ])
 
+AC_CHECK_MEMBERS([kstat_io_t.nwritten, kstat_io_t.writes, kstat_io_t.nwrites, kstat_io_t.wtime],
+       [],
+       [],
+       [
+#if HAVE_KSTAT_H
+# include <kstat.h>
+#endif
+       ])
+
 AC_MSG_CHECKING([for kernel type ($host_os)])
 case $host_os in
        *linux*)
@@ -857,6 +987,53 @@ AC_DEFINE_UNQUOTED(COLLECT_LIBMYSQL, [$collect_libmysql],
        [Wether or not to use mysql library])
 AM_CONDITIONAL(BUILD_WITH_LIBMYSQL, test "x$with_libmysql" = "xyes")
 
+with_own_liboconfig="no"
+liboconfig_LDFLAGS="$LDFLAGS"
+liboconfig_CPPFLAGS="$CPPFLAGS"
+AC_ARG_WITH(liboconfig, [AS_HELP_STRING([--with-liboconfig@<:@=PREFIX@:>@], [Path to liboconfig.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               if test -d "$withval/lib"
+               then
+                       liboconfig_LDFLAGS="$LDFLAGS -L$withval/lib"
+               fi
+               if test -d "$withval/include"
+               then
+                       liboconfig_CPPFLAGS="$CPPFLAGS -I$withval/include"
+               fi
+       fi
+       if test "x$withval" = "xno"
+       then
+               AC_MSG_ERROR("liboconfig is required")
+       fi
+],
+[
+       with_liboconfig="yes"
+])
+
+save_LDFLAGS="$LDFLAGS"
+save_CPPFLAGS="$CPPFLAGS"
+LDFLAGS="$liboconfig_LDFLAGS"
+CPPFLAGS="$liboconfig_CPPFLAGS"
+AC_CHECK_LIB(oconfig, oconfig_parse_fh,
+[
+       with_liboconfig="yes"
+       with_own_liboconfig="no"
+],
+[
+       with_liboconfig="yes"
+       with_own_liboconfig="yes"
+       LDFLAGS="$save_LDFLAGS"
+       CPPFLAGS="$save_CPPFLAGS"
+])
+
+AM_CONDITIONAL(BUILD_WITH_OWN_LIBOCONFIG, test "x$with_own_liboconfig" = "xyes")
+if test "x$with_own_liboconfig" = "xyes"
+then
+       with_liboconfig="yes (shipped version)"
+fi
+
 #with_liboping="yes"
 with_own_liboping="no"
 liboping_LDFLAGS="$LDFLAGS"
@@ -946,41 +1123,129 @@ AC_DEFINE_UNQUOTED(COLLECT_LIBPCAP, [$collect_libpcap],
        [Wether or not to use the pcap library])
 AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes")
 
-# Define `step' and `hearbeat' values..
-declare -i collectd_step=10
-declare -i collectd_heartbeat=25
-AC_ARG_WITH(step, [AS_HELP_STRING([--with-step=SECONDS], [Interval in which plugins are queried.])],
+perl_interpreter="perl"
+AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])],
 [
-       if test "x$withval" != "xno" -a "x$withval" != "xyes"
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
        then
-               declare -i tmp_collectd_step="$withval"
-               if test $tmp_collectd_step -gt 0
-               then
-                       collectd_step=$tmp_collectd_step
-                       let "collectd_heartbeat=$collectd_step*2"
-               fi
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               perl_interpreter="$withval/bin/perl"
+               with_libperl="yes"
        fi
-], [])
-AC_ARG_WITH(heartbeat, [AS_HELP_STRING([--with-heartbeat=SECONDS], [Heartbeat of the DS in generated RRD files.])],
+],
 [
-       if test "x$withval" != "xno" -a "x$withval" != "xyes"
+       with_libperl="yes"
+])
+if test "x$with_libperl" = "xyes"
+then
+  SAVE_CFLAGS=$CFLAGS
+  SAVE_LDFLAGS=$LDFLAGS
+  PERL_CFLAGS=`$perl_interpreter -MExtUtils::Embed -e ccopts`
+  PERL_LDFLAGS=`$perl_interpreter -MExtUtils::Embed -e ldopts`
+  CFLAGS="$CFLAGS $PERL_CFLAGS"
+  LDFLAGS="$LDFLAGS $PERL_LDFLAGS"
+
+  AC_CACHE_CHECK([for libperl],
+    [have_libperl],
+    AC_LINK_IFELSE(
+      AC_LANG_PROGRAM(
+      [[
+#include <EXTERN.h>
+#include <perl.h>
+#include <XSUB.h>
+      ]],
+      [[
+       PerlInterpreter *perl = NULL;
+       Perl_load_module (perl, PERL_LOADMOD_NOIMPORT,
+                        newSVpv ("Collectd::Plugin::FooBar", 24),
+                        Nullsv);
+      ]]),
+      [have_libperl="yes"],
+      [have_libperl="no"]
+    )
+  )
+
+  if test "x$have_libperl" = "xyes"
+  then
+         AC_DEFINE(HAVE_LIBPERL, 1, [Define if libperl is present and usable.])
+         AC_SUBST(PERL_CFLAGS)
+         AC_SUBST(PERL_LDFLAGS)
+  else
+         with_libperl="no"
+  fi
+
+  CFLAGS=$SAVE_CFLAGS
+  LDFLAGS=$SAVE_LDFLAGS
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBPERL, test "x$with_libperl" = "xyes")
+
+AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
        then
-               declare -i tmp_collectd_heartbeat="$withval"
-               if test $tmp_collectd_heartbeat -gt 0
-               then
-                       collectd_heartbeat=$tmp_collectd_heartbeat
-               fi
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libiptc="yes"
+       fi
+],
+[
+       if test "x$ac_system" = "xLinux"
+       then
+               with_libiptc="yes"
+       else
+               with_libiptc="no (Linux only)"
        fi
-], [])
+])
+if test "x$with_libiptc" = "xyes"
+then
+       AC_CHECK_LIB(iptc, iptc_init,
+       [
+               AC_DEFINE(HAVE_LIBIPTC, 1, [Define to 1 if you have the iptc library (-liptc).])
+       ], [with_libiptc="no (libiptc not found)"])
+fi
+if test "x$with_libiptc" = "xyes"
+then
+       AC_CHECK_HEADERS(libiptc/libiptc.h,
+       [
+               AC_DEFINE(HAVE_LIBIPTC_LIBIPTC_H, 1, [Define to 1 if you have the <libiptc/libiptc.h> header file.])
+       ], [with_libiptc="no (libiptc/libiptc.h not found)"])
+fi
+if test "x$with_libiptc" = "xyes"
+then
+       collect_libiptc=1
+else
+       collect_libiptc=0
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBIPTC, test "x$with_libiptc" = "xyes")
 
-if test $collectd_step -ne 10
+AC_ARG_WITH(libupsclient, [AS_HELP_STRING([--with-libupsclient@<:@=PREFIX@:>@], [Path to libupsclient.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libupsclient="yes"
+       fi
+],
+[
+       with_libupsclient="yes"
+])
+if test "x$with_libupsclient" = "xyes"
 then
-       AC_DEFINE_UNQUOTED(COLLECTD_STEP, "$collectd_step", [Interval in which plugins are queried.])
+       AC_CHECK_LIB(upsclient, upscli_connect,
+       [
+               AC_DEFINE(HAVE_LIBUPSCLIENT, 1, [Define to 1 if you have the upsclient library (-lupsclient).])
+       ], [with_libupsclient="no (libupsclient not found)"])
 fi
-if test $collectd_heartbeat -ne 25
+if test "x$with_libupsclient" = "xyes"
 then
-       AC_DEFINE_UNQUOTED(COLLECTD_HEARTBEAT, "$collectd_heartbeat", [Interval in which plugins are queried.])
+       AC_CHECK_HEADERS(upsclient.h,
+       [
+               AC_DEFINE(HAVE_UPSCLIENT_H, 1, [Define to 1 if you have the <upsclient.h> header file.])
+       ], [with_libupsclient="no (upsclient.h not found)"])
 fi
+AM_CONDITIONAL(BUILD_WITH_LIBUPSCLIENT, test "x$with_libupsclient" = "xyes")
 
 # Check for enabled/disabled features
 #
@@ -1060,30 +1325,47 @@ AC_COLLECTD([battery],   [disable], [module], [battery statistics])
 AC_COLLECTD([cpu],       [disable], [module], [cpu usage statistics])
 AC_COLLECTD([cpufreq],   [disable], [module], [system cpu frequency statistics])
 AC_COLLECTD([disk],      [disable], [module], [disk/partition statistics])
+AC_COLLECTD([csv],       [disable], [module], [csv output plugin])
 AC_COLLECTD([df],        [disable], [module], [df statistics])
 AC_COLLECTD([dns],       [disable], [module], [dns statistics])
 AC_COLLECTD([email],     [disable], [module], [email statistics])
-AC_COLLECTD([quota],     [enable],  [module], [quota statistics (experimental)])
+AC_COLLECTD([entropy],   [disable], [module], [entropy statistics])
+AC_COLLECTD([exec],      [disable], [module], [exec of external programs])
 AC_COLLECTD([hddtemp],   [disable], [module], [hdd temperature statistics])
+AC_COLLECTD([interface], [disable], [module], [interface statistics])
+AC_COLLECTD([iptables],  [disable], [module], [IPtables statistics])
+AC_COLLECTD([irq],       [disable], [module], [irq statistics])
 AC_COLLECTD([load],      [disable], [module], [system load statistics])
 AC_COLLECTD([mbmon],     [disable], [module], [motherboard monitor statistics])
 AC_COLLECTD([memory],    [disable], [module], [memory statistics])
 AC_COLLECTD([multimeter],[disable], [module], [multimeter statistics])
 AC_COLLECTD([mysql],     [disable], [module], [mysql statistics])
+AC_COLLECTD([network],   [disable], [module], [network functionality])
 AC_COLLECTD([nfs],       [disable], [module], [nfs statistics])
-AC_COLLECTD([ntpd],      [disable], [module], [nfs statistics])
+AC_COLLECTD([ntpd],      [disable], [module], [ntpd statistics])
+AC_COLLECTD([nut],       [disable], [module], [network UPS tools statistics])
+AC_COLLECTD([perl],      [disable], [module], [embedded perl interpreter])
 AC_COLLECTD([ping],      [disable], [module], [ping statistics])
 AC_COLLECTD([processes], [disable], [module], [processes statistics])
 AC_COLLECTD([sensors],   [disable], [module], [lm_sensors statistics])
 AC_COLLECTD([serial],    [disable], [module], [serial statistics])
+AC_COLLECTD([logfile],   [disable], [module], [logfile log facility])
 AC_COLLECTD([swap],      [disable], [module], [swap statistics])
+AC_COLLECTD([syslog],    [disable], [module], [syslog log facility])
 AC_COLLECTD([tape],      [disable], [module], [tape statistics])
-AC_COLLECTD([traffic],   [disable], [module], [system traffic statistics])
+AC_COLLECTD([unixsock],  [disable], [module], [UNIX socket plugin])
 AC_COLLECTD([users],     [disable], [module], [user count statistics])
 AC_COLLECTD([vserver],   [disable], [module], [vserver statistics])
 AC_COLLECTD([wireless],  [disable], [module], [wireless link statistics])
 
-AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/libconfig/Makefile src/liboping/Makefile)
+AC_OUTPUT(Makefile src/Makefile src/collectd.conf src/liboconfig/Makefile src/liboping/Makefile)
+
+if test "x$with_libperl" = "xyes"
+then
+       with_libperl="yes (version `perl -MConfig -e 'print $Config{version};'`)"
+else
+       enable_perl="no (needs libperl)"
+fi
 
 cat <<EOF;
 
@@ -1091,20 +1373,22 @@ Configuration:
   Libraries:
     libcurl . . . . . . $with_libcurl
     libiokit  . . . . . $with_libiokit
-    liboping  . . . . . $with_liboping
-    librrd  . . . . . . $with_rrdtool
-    lm_sensors  . . . . $with_lm_sensors
-    libstatgrab . . . . $with_libstatgrab
+    libiptc . . . . . . $with_libiptc
     libkstat  . . . . . $with_kstat
     libmysql  . . . . . $with_libmysql
+    liboconfig  . . . . $with_liboconfig
+    liboping  . . . . . $with_liboping
     libpcap . . . . . . $with_libpcap
+    libperl . . . . . . $with_libperl
     libpthread  . . . . $with_libpthread
+    librrd  . . . . . . $with_rrdtool
+    libsensors  . . . . $with_lm_sensors
+    libstatgrab . . . . $with_libstatgrab
+    libupsclient  . . . $with_libupsclient
 
   Features:
-    debug . . . . . . . $enable_debug
     daemon mode . . . . $enable_daemon
-    step  . . . . . . . $collectd_step seconds
-    heartbeat . . . . . $collectd_heartbeat seconds
+    debug . . . . . . . $enable_debug
 
   Modules:
     apache  . . . . . . $enable_apache
@@ -1113,25 +1397,36 @@ Configuration:
     battery . . . . . . $enable_battery
     cpu . . . . . . . . $enable_cpu
     cpufreq . . . . . . $enable_cpufreq
+    csv . . . . . . . . $enable_csv
     df  . . . . . . . . $enable_df
     disk  . . . . . . . $enable_disk
     dns . . . . . . . . $enable_dns
     email . . . . . . . $enable_email
+    entropy . . . . . . $enable_entropy
+    exec  . . . . . . . $enable_exec
     hddtemp . . . . . . $enable_hddtemp
+    interface . . . . . $enable_interface
+    iptables  . . . . . $enable_iptables
+    irq . . . . . . . . $enable_irq
     load  . . . . . . . $enable_load
+    logfile . . . . . . $enable_logfile
     mbmon . . . . . . . $enable_mbmon
     memory  . . . . . . $enable_memory
     multimeter  . . . . $enable_multimeter
     mysql . . . . . . . $enable_mysql
+    network . . . . . . $enable_network
     nfs . . . . . . . . $enable_nfs
     ntpd  . . . . . . . $enable_ntpd
+    nut . . . . . . . . $enable_nut
+    perl  . . . . . . . $enable_perl
     ping  . . . . . . . $enable_ping
     processes . . . . . $enable_processes
     sensors . . . . . . $enable_sensors
     serial  . . . . . . $enable_serial
     swap  . . . . . . . $enable_swap
+    syslog  . . . . . . $enable_syslog
     tape  . . . . . . . $enable_tape
-    traffic . . . . . . $enable_traffic
+    unixsock  . . . . . $enable_unixsock
     users . . . . . . . $enable_users
     vserver . . . . . . $enable_vserver
     wireless  . . . . . $enable_wireless
diff --git a/contrib/PerlLib/Collectd/Unixsock.pm b/contrib/PerlLib/Collectd/Unixsock.pm
new file mode 100644 (file)
index 0000000..92e8772
--- /dev/null
@@ -0,0 +1,249 @@
+package Collectd::Unixsock;
+
+=head1 NAME
+
+Collectd::Unixsock - Abstraction layer for accessing the functionality by collectd's unixsock plugin.
+
+=head1 SYNOPSIS
+
+  use Collectd::Unixsock ();
+
+  my $sock = Collectd::Unixsock->new ($path);
+
+  my $value = $sock->getval (%identifier);
+  $sock->putval (%identifier,
+                 time => time (),
+                values => [123, 234, 345]);
+
+  $sock->destroy ();
+
+=head1 DESCRIPTION
+
+collectd's unixsock plugin allows external programs to access the values it has
+collected or received and to submit own values. This Perl-module is simply a
+little abstraction layer over this interface to make it even easier for
+programmers to interact with the daemon.
+
+=cut
+
+use strict;
+use warnings;
+
+use Carp (qw(cluck confess));
+use IO::Socket::UNIX;
+use Regexp::Common (qw(number));
+
+return (1);
+
+sub _create_socket
+{
+       my $path = shift;
+       my $sock = IO::Socket::UNIX->new (Type => SOCK_STREAM, Peer => $path);
+       if (!$sock)
+       {
+               cluck ("Cannot open UNIX-socket $path: $!");
+               return;
+       }
+       return ($sock);
+} # _create_socket
+
+=head1 VALUE IDENTIFIER
+
+The values in the collectd are identified using an five-tupel (host, plugin,
+plugin-instance, type, type-instance) where only plugin-instance and
+type-instance may be NULL (or undefined). Many functions expect an
+I<%identifier> hash that has at least the members B<host>, B<plugin>, and
+B<type>, possibly completed by B<plugin_instance> and B<type_instance>.
+
+Usually you can pass this hash as follows:
+
+  $obj->method (host => $host, plugin => $plugin, type => $type, %other_args);
+
+=cut
+
+sub _create_identifier
+{
+       my $args = shift;
+       my $host;
+       my $plugin;
+       my $type;
+
+       if (!$args->{'host'} || !$args->{'plugin'} || !$args->{'type'})
+       {
+               cluck ("Need `host', `plugin' and `type'");
+               return;
+       }
+
+       $host = $args->{'host'};
+       $plugin = $args->{'plugin'};
+       $plugin .= '-' . $args->{'plugin_instance'} if (defined ($args->{'plugin_instance'}));
+       $type = $args->{'type'};
+       $type .= '-' . $args->{'type_instance'} if (defined ($args->{'type_instance'}));
+
+       return ("$host/$plugin/$type");
+} # _create_identifier
+
+=head1 PUBLIC METHODS
+
+=over 4
+
+=item I<$obj> = Collectd::Unixsock->B<new> ([I<$path>]);
+
+Creates a new connection to the daemon. The optional I<$path> argument gives
+the path to the UNIX socket of the C<unixsock plugin> and defaults to
+F</var/run/collectd-unixsock>. Returns the newly created object on success and
+false on error.
+
+=cut
+
+sub new
+{
+       my $pkg = shift;
+       my $path = @_ ? shift : '/var/run/collectd-unixsock';
+       my $sock = _create_socket ($path) or return;
+       my $obj = bless (
+               {
+                       path => $path,
+                       sock => $sock,
+                       error => 'No error'
+               }, $pkg);
+       return ($obj);
+} # new
+
+=item I<$res> = I<$obj>-E<gt>B<getval> (I<%identifier>);
+
+Requests a value-list from the daemon. On success a hash-ref is returned with
+the name of each data-source as the key and the according value as, well, the
+value. On error false is returned.
+
+=cut
+
+sub getval
+{
+       my $obj = shift;
+       my %args = @_;
+
+       my $status;
+       my $fh = $obj->{'sock'} or confess;
+       my $msg;
+       my $identifier;
+
+       my $ret = {};
+
+       $identifier = _create_identifier (\%args) or return;
+
+       $msg = "GETVAL $identifier\n";
+       #print "-> $msg";
+       send ($fh, $msg, 0) or confess ("send: $!");
+
+       $msg = undef;
+       recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
+       #print "<- $msg";
+
+       ($status, $msg) = split (' ', $msg, 2);
+       if ($status <= 0)
+       {
+               $obj->{'error'} = $msg;
+               return;
+       }
+
+       for (split (' ', $msg))
+       {
+               my $entry = $_;
+               if ($entry =~ m/^(\w+)=NaN$/)
+               {
+                       $ret->{$1} = undef;
+               }
+               elsif ($entry =~ m/^(\w+)=($RE{num}{real})$/)
+               {
+                       $ret->{$1} = 0.0 + $2;
+               }
+       }
+
+       return ($ret);
+} # getval
+
+=item I<$obj>-E<gt>B<putval> (I<%identifier>, B<time> => I<$time>, B<values> => [...]);
+
+Submits a value-list to the daemon. If the B<time> argument is omitted
+C<time()> is used. The requierd argument B<values> is a reference to an array
+of values that is to be submitted. The number of values must match the number
+of values expected for the given B<type> (see L<VALUE IDENTIFIER>), though this
+is checked by the daemon, not the Perl module. Also, gauge data-sources
+(e.E<nbsp>g. system-load) may be C<undef>. Returns true upon success and false
+otherwise.
+
+=cut
+
+sub putval
+{
+       my $obj = shift;
+       my %args = @_;
+
+       my $status;
+       my $fh = $obj->{'sock'} or confess;
+       my $msg;
+       my $identifier;
+       my $values;
+
+       $identifier = _create_identifier (\%args) or return;
+       if (!$args{'values'})
+       {
+               cluck ("Need argument `values'");
+               return;
+       }
+
+       if (!ref ($args{'values'}))
+       {
+               $values = $args{'values'};
+       }
+       else
+       {
+               my $time = $args{'time'} ? $args{'time'} : time ();
+               $values = join (':', $time, map { defined ($_) ? $_ : 'U' } (@{$args{'values'}}));
+       }
+
+       $msg = "PUTVAL $identifier $values\n";
+       #print "-> $msg";
+       send ($fh, $msg, 0) or confess ("send: $!");
+       $msg = undef;
+       recv ($fh, $msg, 1024, 0) or confess ("recv: $!");
+       #print "<- $msg";
+
+       ($status, $msg) = split (' ', $msg, 2);
+       return (1) if ($status == 0);
+
+       $obj->{'error'} = $msg;
+       return;
+} # putval
+
+=item I<$obj>-E<gt>destroy ();
+
+Closes the socket before the object is destroyed. This function is also
+automatically called then the object goes out of scope.
+
+=back
+
+=cut
+
+sub destroy
+{
+       my $obj = shift;
+       if ($obj->{'sock'})
+       {
+               close ($obj->{'sock'});
+               delete ($obj->{'sock'});
+       }
+}
+
+sub DESTROY
+{
+       my $obj = shift;
+       $obj->destroy ();
+}
+
+=head1 AUTHOR
+
+Florian octo Forster E<lt>octo@verplant.orgE<gt>
+
+=cut
index 7f21040..ab3c978 100644 (file)
@@ -2,11 +2,16 @@ The files in this directory may be used to perform common tasks that aren't
 exactly `collectd's job. They may or may not require in-depth knowlege of RRD
 files and/or `collectd's inner workings. Use at your own risk.
 
+PerlLib/
+--------
+  Perl modules to be used in conjunction with collectd. See the perldoc
+documentation of the .pm-files to find out what they're good for.
+
 add_rra.sh
 ----------
   Before version 3.9.0 collectd used to create a different set of RRAs. The
-most detailed of these old RRAs hat a one minute resolution. This script can be
-used to add three more RRAs: minimum, maximum and average with a ten second
+most detailed of these old RRAs hat a one minute resolution. This script can
+be used to add three more RRAs: minimum, maximum and average with a ten second
 resolution and 2200 rows (~6 hours). This will make houly statistics much more
 interesting. Please note that no sanity- checking whatsoever is performed. You
 can seriously fuck up your RRD files if you don't know what you're doing.
@@ -17,15 +22,28 @@ collectd2html.pl
 `/var/lib/collectd/' and generate an HTML file and a directory containing
 several PNG files which are graphs of the RRD files found.
 
-collectd.conf
--------------
-  A sample config file. Used by the Debian package.
-
 collection.cgi
 --------------
   Sample CGI script that creates graphs on the fly. The Perl module `RRDs' is
 needed (Debian package `librrds-perl').
 
-init.d-rh7
-----------
-  Sample init script. Used by the RPM specfile.
+extractDS.px
+------------
+  Creates a new RRD-file with only one data-source (DS) of the source-RRD-
+file. That is very handy when you realise that you have bundled up DSes in one
+RRD-file that should have been in multiple RRD-files instead. Is is used by
+`migrate-3-4.px' to split up the cpu-, nfs-, swap-files and possibly others.
+
+fedora/
+-------
+  Init-script and Spec-file that can be used when creating RPM-packages for
+Fedora.
+
+migrate-3-4.px
+--------------
+  Migration-script to ease the switch from version 3 to version 4. Many
+RRD-files are expected in a different place, some have been changed (DSes have
+been renamed) and others have bee split up into multiple files. This script
+prints a bash-script to STDOUT which should do most of the work for you. You
+may still need to do some things by hand, read `README.migration' for more
+details.
index 5c8bd4d..155653d 100755 (executable)
 use strict;
 use warnings;
 
-no warnings ('qw');
+use Carp (qw(cluck confess));
+use CGI (':cgi');
+use CGI::Carp ('fatalsToBrowser');
+use HTML::Entities ('encode_entities');
+use URI::Escape ('uri_escape');
+use RRDs ();
+use Data::Dumper ();
 
-use CGI;
-use RRDs;
-use Fcntl (qw(:flock));
-use Carp (qw(carp cluck confess croak));
+our $Config = "/etc/collection.conf";
+our @DataDirs = ();
+our $LibDir;
 
-our $Config = read_config ();
+our @RRDDefaultArgs = ('-w', '800');
 
-our $AbsDir;
-our $RelDir;
-our $Type;
-our $Inst;
-our $TimeSpan;
-
-for (qw(Red Green Blue Yellow Cyan Magenta))
-{
-       $Config->{'Colors'}{"Half$_"} = color_calculate_transparent ($Config->{'Colors'}{'Alpha'},
-               $Config->{'Colors'}{'Canvas'}, $Config->{'Colors'}{"Full$_"});
-}
-
-$Config->{'Colors'}{'HalfBlueGreen'} = color_calculate_transparent ($Config->{'Colors'}{'Alpha'},
-       $Config->{'Colors'}{'Canvas'}, $Config->{'Colors'}{'FullGreen'}, $Config->{'Colors'}{'FullBlue'});
-$Config->{'Colors'}{'HalfRedBlue'} = color_calculate_transparent ($Config->{'Colors'}{'Alpha'},
-       $Config->{'Colors'}{'Canvas'}, $Config->{'Colors'}{'FullBlue'}, $Config->{'Colors'}{'FullRed'});
+our $Args = {};
 
 our $GraphDefs;
-{
-       my $Alpha  = $Config->{'Colors'}{'Alpha'};
-       my $Canvas = $Config->{'Colors'}{'Canvas'};
-
-       my $FullRed    = $Config->{'Colors'}{'FullRed'};
-       my $FullGreen  = $Config->{'Colors'}{'FullGreen'};
-       my $FullBlue   = $Config->{'Colors'}{'FullBlue'};
-       my $FullYellow = $Config->{'Colors'}{'FullYellow'};
-       my $FullCyan   = $Config->{'Colors'}{'FullCyan'};
-       my $FullMagenta= $Config->{'Colors'}{'FullMagenta'};
-
-       my $HalfRed    = $Config->{'Colors'}{'HalfRed'};
-       my $HalfGreen  = $Config->{'Colors'}{'HalfGreen'};
-       my $HalfBlue   = $Config->{'Colors'}{'HalfBlue'};
-       my $HalfYellow = $Config->{'Colors'}{'HalfYellow'};
-       my $HalfCyan   = $Config->{'Colors'}{'HalfCyan'};
-       my $HalfMagenta= $Config->{'Colors'}{'HalfMagenta'};
-
-       my $HalfBlueGreen = $Config->{'Colors'}{'HalfBlueGreen'};
-       my $HalfRedBlue   = $Config->{'Colors'}{'HalfRedBlue'};
-       
-       $GraphDefs =
-       {
-               apache_bytes => ['DEF:min_raw={file}:count:MIN',
-                       'DEF:avg_raw={file}:count:AVERAGE',
-                       'DEF:max_raw={file}:count:MAX',
-                       'CDEF:min=min_raw,8,*',
-                       'CDEF:avg=avg_raw,8,*',
-                       'CDEF:max=max_raw,8,*',
-                       'CDEF:mytime=avg_raw,TIME,TIME,IF',
-                       'CDEF:sample_len_raw=mytime,PREV(mytime),-',
-                       'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
-                       'CDEF:avg_sample=avg_raw,UN,0,avg_raw,IF,sample_len,*',
-                       'CDEF:avg_sum=PREV,UN,0,PREV,IF,avg_sample,+',
-                       "AREA:avg#$HalfBlue",
-                       "LINE1:avg#$FullBlue:Bit/s",
-                       'GPRINT:min:MIN:%5.1lf%s Min,',
-                       'GPRINT:avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:max:MAX:%5.1lf%s Max,',
-                       'GPRINT:avg:LAST:%5.1lf%s Last',
-                       'GPRINT:avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
-               ],
-               apache_requests => ['DEF:min={file}:count:MIN',
-                       'DEF:avg={file}:count:AVERAGE',
-                       'DEF:max={file}:count:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Requests/s",
-                       '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_scoreboard => ['DEF:min={file}:count:MIN',
-                       'DEF:avg={file}:count:AVERAGE',
-                       'DEF:max={file}:count:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Processes",
-                       'GPRINT:min:MIN:%6.2lf Min,',
-                       'GPRINT:avg:AVERAGE:%6.2lf Avg,',
-                       'GPRINT:max:MAX:%6.2lf Max,',
-                       'GPRINT:avg:LAST:%6.2lf Last'
-               ],
-               charge => [
-                       'DEF:avg={file}:charge:AVERAGE',
-                       'DEF:min={file}:charge:MIN',
-                       'DEF:max={file}:charge:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Charge",
-                       'GPRINT:min:MIN:%5.1lf%sAh Min,',
-                       'GPRINT:avg:AVERAGE:%5.1lf%sAh Avg,',
-                       'GPRINT:max:MAX:%5.1lf%sAh Max,',
-                       'GPRINT:avg:LAST:%5.1lf%sAh Last\l'
-               ],
-               charge_percent => [
-                       'DEF:avg={file}:percent:AVERAGE',
-                       'DEF:min={file}:percent:MIN',
-                       'DEF:max={file}:percent:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Charge",
-                       'GPRINT:min:MIN:%5.1lf%s%% Min,',
-                       'GPRINT:avg:AVERAGE:%5.1lf%s%% Avg,',
-                       'GPRINT:max:MAX:%5.1lf%s%% Max,',
-                       'GPRINT:avg:LAST:%5.1lf%s%% Last\l'
-               ],
-               cpu => ['DEF:user_avg={file}:user:AVERAGE',
-                       'DEF:user_min={file}:user:MIN',
-                       'DEF:user_max={file}:user:MAX',
-                       'DEF:nice_avg={file}:nice:AVERAGE',
-                       'DEF:nice_min={file}:nice:MIN',
-                       'DEF:nice_max={file}:nice:MAX',
-                       'DEF:syst_avg={file}:syst:AVERAGE',
-                       'DEF:syst_min={file}:syst:MIN',
-                       'DEF:syst_max={file}:syst:MAX',
-                       'DEF:idle_avg={file}:idle:AVERAGE',
-                       'DEF:idle_min={file}:idle:MIN',
-                       'DEF:idle_max={file}:idle:MAX',
-                       'DEF:wait_avg={file}:wait:AVERAGE',
-                       'DEF:wait_min={file}:wait:MIN',
-                       'DEF:wait_max={file}:wait:MAX',
-                       'CDEF:user_avg_notnull=user_avg,UN,0,user_avg,IF',
-                       'CDEF:nice_avg_notnull=nice_avg,UN,0,nice_avg,IF',
-                       'CDEF:syst_avg_notnull=syst_avg,UN,0,syst_avg,IF',
-                       'CDEF:idle_avg_notnull=idle_avg,UN,0,idle_avg,IF',
-                       'CDEF:wait_avg_notnull=wait_avg,UN,0,wait_avg,IF',
-                       'CDEF:totl_avg_notnull=user_avg_notnull,nice_avg_notnull,+,syst_avg_notnull,+,idle_avg_notnull,+,wait_avg_notnull,+',
-                       'CDEF:user_avg_pct=user_avg_notnull,100,*,totl_avg_notnull,/',
-                       'CDEF:nice_avg_pct=nice_avg_notnull,100,*,totl_avg_notnull,/',
-                       'CDEF:syst_avg_pct=syst_avg_notnull,100,*,totl_avg_notnull,/',
-                       'CDEF:wait_avg_pct=wait_avg_notnull,100,*,totl_avg_notnull,/',
-                       'CDEF:nice_acc=syst_avg_pct,wait_avg_pct,user_avg_pct,nice_avg_pct,+,+,+',
-                       'CDEF:user_acc=syst_avg_pct,wait_avg_pct,user_avg_pct,+,+',
-                       'CDEF:wait_acc=syst_avg_pct,wait_avg_pct,+',
-                       'CDEF:syst_acc=syst_avg_pct',
-#                      'CDEF:nice_acc=syst_avg_notnull,wait_avg_notnull,user_avg_notnull,nice_avg_notnull,+,+,+',
-#                      'CDEF:user_acc=syst_avg_notnull,wait_avg_notnull,user_avg_notnull,+,+',
-#                      'CDEF:wait_acc=syst_avg_notnull,wait_avg_notnull,+',
-#                      'CDEF:syst_acc=syst_avg_notnull',
-                       "AREA:nice_acc#$HalfGreen",
-                       "AREA:user_acc#$HalfBlue",
-                       "AREA:wait_acc#$HalfYellow",
-                       "AREA:syst_acc#$HalfRed",
-                       "LINE1:nice_acc#$FullGreen:Nice   ",
-                       'GPRINT:nice_min:MIN:%5.1lf%% Min,',
-                       'GPRINT:nice_avg:AVERAGE:%5.1lf%% Avg,',
-                       'GPRINT:nice_max:MAX:%5.1lf%% Max,',
-                       'GPRINT:nice_avg:LAST:%5.1lf%% Last\l',
-                       "LINE1:user_acc#$FullBlue:User   ",
-                       'GPRINT:user_min:MIN:%5.1lf%% Min,',
-                       'GPRINT:user_avg:AVERAGE:%5.1lf%% Avg,',
-                       'GPRINT:user_max:MAX:%5.1lf%% Max,',
-                       'GPRINT:user_avg:LAST:%5.1lf%% Last\l',
-                       "LINE1:wait_acc#$FullYellow:Wait-IO",
-                       'GPRINT:wait_min:MIN:%5.1lf%% Min,',
-                       'GPRINT:wait_avg:AVERAGE:%5.1lf%% Avg,',
-                       'GPRINT:wait_max:MAX:%5.1lf%% Max,',
-                       'GPRINT:wait_avg:LAST:%5.1lf%% Last\l',
-                       "LINE1:syst_acc#$FullRed:System ",
-                       'GPRINT:syst_min:MIN:%5.1lf%% Min,',
-                       'GPRINT:syst_avg:AVERAGE:%5.1lf%% Avg,',
-                       'GPRINT:syst_max:MAX:%5.1lf%% Max,',
-                       'GPRINT:syst_avg:LAST:%5.1lf%% Last\l'
-               ],
-               current => [
-                       'DEF:avg={file}:current:AVERAGE',
-                       'DEF:min={file}:current:MIN',
-                       'DEF:max={file}:current:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Current",
-                       'GPRINT:min:MIN:%5.1lf%sA Min,',
-                       'GPRINT:avg:AVERAGE:%5.1lf%sA Avg,',
-                       'GPRINT:max:MAX:%5.1lf%sA Max,',
-                       'GPRINT:avg:LAST:%5.1lf%sA Last\l'
-               ],
-               df => [
-                       'DEF:free_avg={file}:free:AVERAGE',
-                       'DEF:free_min={file}:free:MIN',
-                       'DEF:free_max={file}:free:MAX',
-                       'DEF:used_avg={file}:used:AVERAGE',
-                       'DEF:used_min={file}:used:MIN',
-                       'DEF:used_max={file}:used:MAX',
-                       'CDEF:total=free_avg,used_avg,+',
-                       'CDEF:free_pct=100,free_avg,*,total,/',
-                       'CDEF:used_pct=100,used_avg,*,total,/',
-                       'CDEF:free_acc=free_pct,used_pct,+',
-                       'CDEF:used_acc=used_pct',
-                       "AREA:free_acc#$HalfGreen",
-                       "AREA:used_acc#$HalfRed",
-                       "LINE1:free_acc#$FullGreen:Free",
-                       'GPRINT:free_min:MIN:%5.1lf%sB Min,',
-                       'GPRINT:free_avg:AVERAGE:%5.1lf%sB Avg,',
-                       'GPRINT:free_max:MAX:%5.1lf%sB Max,',
-                       'GPRINT:free_avg:LAST:%5.1lf%sB Last\l',
-                       "LINE1:used_acc#$FullRed:Used",
-                       'GPRINT:used_min:MIN:%5.1lf%sB Min,',
-                       'GPRINT:used_avg:AVERAGE:%5.1lf%sB Avg,',
-                       'GPRINT:used_max:MAX:%5.1lf%sB Max,',
-                       'GPRINT:used_avg:LAST:%5.1lf%sB Last\l'
-               ],
-               disk => [
-                       'DEF:rtime_avg={file}:rtime:AVERAGE',
-                       'DEF:rtime_min={file}:rtime:MIN',
-                       'DEF:rtime_max={file}:rtime:MAX',
-                       'DEF:wtime_avg={file}:wtime:AVERAGE',
-                       'DEF:wtime_min={file}:wtime:MIN',
-                       'DEF:wtime_max={file}:wtime:MAX',
-                       'CDEF:rtime_avg_ms=rtime_avg,1000,/',
-                       'CDEF:rtime_min_ms=rtime_min,1000,/',
-                       'CDEF:rtime_max_ms=rtime_max,1000,/',
-                       'CDEF:wtime_avg_ms=wtime_avg,1000,/',
-                       'CDEF:wtime_min_ms=wtime_min,1000,/',
-                       'CDEF:wtime_max_ms=wtime_max,1000,/',
-                       'CDEF:total_avg_ms=rtime_avg_ms,wtime_avg_ms,+',
-                       'CDEF:total_min_ms=rtime_min_ms,wtime_min_ms,+',
-                       'CDEF:total_max_ms=rtime_max_ms,wtime_max_ms,+',
-                       "AREA:total_max_ms#$HalfRed",
-                       "AREA:total_min_ms#$Canvas",
-                       "LINE1:wtime_avg_ms#$FullGreen:Write",
-                       'GPRINT:wtime_min_ms:MIN:%5.1lf%s Min,',
-                       'GPRINT:wtime_avg_ms:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:wtime_max_ms:MAX:%5.1lf%s Max,',
-                       'GPRINT:wtime_avg_ms:LAST:%5.1lf%s Last\n',
-                       "LINE1:rtime_avg_ms#$FullBlue:Read ",
-                       'GPRINT:rtime_min_ms:MIN:%5.1lf%s Min,',
-                       'GPRINT:rtime_avg_ms:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:rtime_max_ms:MAX:%5.1lf%s Max,',
-                       'GPRINT:rtime_avg_ms:LAST:%5.1lf%s Last\n',
-                       "LINE1:total_avg_ms#$FullRed:Total",
-                       'GPRINT:total_min_ms:MIN:%5.1lf%s Min,',
-                       'GPRINT:total_avg_ms:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:total_max_ms:MAX:%5.1lf%s Max,',
-                       'GPRINT:total_avg_ms:LAST:%5.1lf%s Last'
-               ],
-               dns_traffic => ['DEF:rsp_min_raw={file}:responses:MIN',
-                       'DEF:rsp_avg_raw={file}:responses:AVERAGE',
-                       'DEF:rsp_max_raw={file}:responses:MAX',
-                       'DEF:qry_min_raw={file}:queries:MIN',
-                       'DEF:qry_avg_raw={file}:queries:AVERAGE',
-                       'DEF:qry_max_raw={file}:queries:MAX',
-                       'CDEF:rsp_min=rsp_min_raw,8,*',
-                       'CDEF:rsp_avg=rsp_avg_raw,8,*',
-                       'CDEF:rsp_max=rsp_max_raw,8,*',
-                       'CDEF:qry_min=qry_min_raw,8,*',
-                       'CDEF:qry_avg=qry_avg_raw,8,*',
-                       'CDEF:qry_max=qry_max_raw,8,*',
-                       'CDEF:overlap=rsp_avg,qry_avg,GT,qry_avg,rsp_avg,IF',
-                       'CDEF:mytime=rsp_avg_raw,TIME,TIME,IF',
-                       'CDEF:sample_len_raw=mytime,PREV(mytime),-',
-                       'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
-                       'CDEF:rsp_avg_sample=rsp_avg_raw,UN,0,rsp_avg_raw,IF,sample_len,*',
-                       'CDEF:rsp_avg_sum=PREV,UN,0,PREV,IF,rsp_avg_sample,+',
-                       'CDEF:qry_avg_sample=qry_avg_raw,UN,0,qry_avg_raw,IF,sample_len,*',
-                       'CDEF:qry_avg_sum=PREV,UN,0,PREV,IF,qry_avg_sample,+',
-                       "AREA:rsp_avg#$HalfGreen",
-                       "AREA:qry_avg#$HalfBlue",
-                       "AREA:overlap#$HalfBlueGreen",
-                       "LINE1:rsp_avg#$FullGreen:Responses",
-                       'GPRINT:rsp_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:rsp_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:rsp_avg:LAST:%5.1lf%s Last',
-                       'GPRINT:rsp_avg_sum:LAST:(ca. %5.1lf%sB Total)\l',
-                       "LINE1:qry_avg#$FullBlue:Queries  ",
-                       #'GPRINT:qry_min:MIN:%5.1lf %s Min,',
-                       'GPRINT:qry_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:qry_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:qry_avg:LAST:%5.1lf%s Last',
-                       'GPRINT:qry_avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
-               ],
-               email => [
-                       'DEF:avg={file}:count:AVERAGE',
-                       'DEF:min={file}:count:MIN',
-                       'DEF:max={file}:count:MAX',
-                       "AREA:max#$HalfMagenta",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullMagenta:Count ",
-                       'GPRINT:min:MIN:%4.1lf Min,',
-                       'GPRINT:avg:AVERAGE:%4.1lf Avg,',
-                       'GPRINT:max:MAX:%4.1lf Max,',
-                       'GPRINT:avg:LAST:%4.1lf Last\l'
-               ],
-               email_size => [
-                       'DEF:avg={file}:size:AVERAGE',
-                       'DEF:min={file}:size:MIN',
-                       'DEF:max={file}:size:MAX',
-                       "AREA:max#$HalfMagenta",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullMagenta:Count ",
-                       'GPRINT:min:MIN:%4.1lf Min,',
-                       'GPRINT:avg:AVERAGE:%4.1lf Avg,',
-                       'GPRINT:max:MAX:%4.1lf Max,',
-                       'GPRINT:avg:LAST:%4.1lf Last\l'
-               ],
-               spam_score => [
-                       'DEF:avg={file}:score:AVERAGE',
-                       'DEF:min={file}:score:MIN',
-                       'DEF:max={file}:score:MAX',
-                       "AREA:max#$HalfMagenta",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullMagenta:Count ",
-                       'GPRINT:min:MIN:%4.1lf Min,',
-                       'GPRINT:avg:AVERAGE:%4.1lf Avg,',
-                       'GPRINT:max:MAX:%4.1lf Max,',
-                       'GPRINT:avg:LAST:%4.1lf Last\l'
-               ],
-               spam_check => [
-                       'DEF:avg={file}:hits:AVERAGE',
-                       'DEF:min={file}:hits:MIN',
-                       'DEF:max={file}:hits:MAX',
-                       "AREA:max#$HalfMagenta",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullMagenta:Count ",
-                       'GPRINT:min:MIN:%4.1lf Min,',
-                       'GPRINT:avg:AVERAGE:%4.1lf Avg,',
-                       'GPRINT:max:MAX:%4.1lf Max,',
-                       'GPRINT:avg:LAST:%4.1lf Last\l'
-               ],
-               fanspeed => [
-                       'DEF:temp_avg={file}:value:AVERAGE',
-                       'DEF:temp_min={file}:value:MIN',
-                       'DEF:temp_max={file}:value:MAX',
-                       "AREA:temp_max#$HalfMagenta",
-                       "AREA:temp_min#$Canvas",
-                       "LINE1:temp_avg#$FullMagenta:RPM",
-                       'GPRINT:temp_min:MIN:%4.1lf Min,',
-                       'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
-                       'GPRINT:temp_max:MAX:%4.1lf Max,',
-                       'GPRINT:temp_avg:LAST:%4.1lf Last\l'
-               ],
-               frequency_offset => [ # NTPd
-                       'DEF:ppm_avg={file}:ppm:AVERAGE',
-                       'DEF:ppm_min={file}:ppm:MIN',
-                       'DEF:ppm_max={file}:ppm:MAX',
-                       "AREA:ppm_max#$HalfBlue",
-                       "AREA:ppm_min#$Canvas",
-                       "LINE1:ppm_avg#$FullBlue:{inst}",
-                       'GPRINT:ppm_min:MIN:%5.2lf Min,',
-                       'GPRINT:ppm_avg:AVERAGE:%5.2lf Avg,',
-                       'GPRINT:ppm_max:MAX:%5.2lf Max,',
-                       'GPRINT:ppm_avg:LAST:%5.2lf Last'
-               ],
-               hddtemp => [
-                       'DEF:temp_avg={file}:value:AVERAGE',
-                       'DEF:temp_min={file}:value:MIN',
-                       'DEF:temp_max={file}:value:MAX',
-                       "AREA:temp_max#$HalfRed",
-                       "AREA:temp_min#$Canvas",
-                       "LINE1:temp_avg#$FullRed:Temperature",
-                       'GPRINT:temp_min:MIN:%4.1lf Min,',
-                       'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
-                       'GPRINT:temp_max:MAX:%4.1lf Max,',
-                       'GPRINT:temp_avg:LAST:%4.1lf Last\l'
-               ],
-               if_packets => ['DEF:tx_min={file}:tx:MIN',
-                       'DEF:tx_avg={file}:tx:AVERAGE',
-                       'DEF:tx_max={file}:tx:MAX',
-                       'DEF:rx_min={file}:rx:MIN',
-                       'DEF:rx_avg={file}:rx:AVERAGE',
-                       'DEF:rx_max={file}:rx:MAX',
-                       'CDEF:overlap=tx_avg,rx_avg,GT,rx_avg,tx_avg,IF',
-                       'CDEF:mytime=tx_avg,TIME,TIME,IF',
-                       'CDEF:sample_len_raw=mytime,PREV(mytime),-',
-                       'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
-                       'CDEF:tx_avg_sample=tx_avg,UN,0,tx_avg,IF,sample_len,*',
-                       'CDEF:tx_avg_sum=PREV,UN,0,PREV,IF,tx_avg_sample,+',
-                       'CDEF:rx_avg_sample=rx_avg,UN,0,rx_avg,IF,sample_len,*',
-                       'CDEF:rx_avg_sum=PREV,UN,0,PREV,IF,rx_avg_sample,+',
-                       "AREA:tx_avg#$HalfGreen",
-                       "AREA:rx_avg#$HalfBlue",
-                       "AREA:overlap#$HalfBlueGreen",
-                       "LINE1:tx_avg#$FullGreen:TX",
-                       'GPRINT:tx_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:tx_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:tx_avg:LAST:%5.1lf%s Last',
-                       'GPRINT:tx_avg_sum:LAST:(ca. %4.0lf%s Total)\l',
-                       "LINE1:rx_avg#$FullBlue:RX",
-                       #'GPRINT:rx_min:MIN:%5.1lf %s Min,',
-                       'GPRINT:rx_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:rx_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:rx_avg:LAST:%5.1lf%s Last',
-                       'GPRINT:rx_avg_sum:LAST:(ca. %4.0lf%s Total)\l'
-               ],
-               load => ['DEF:s_avg={file}:shortterm:AVERAGE',
-                       'DEF:s_min={file}:shortterm:MIN',
-                       'DEF:s_max={file}:shortterm:MAX',
-                       'DEF:m_avg={file}:midterm:AVERAGE',
-                       'DEF:m_min={file}:midterm:MIN',
-                       'DEF:m_max={file}:midterm:MAX',
-                       'DEF:l_avg={file}:longterm:AVERAGE',
-                       'DEF:l_min={file}:longterm:MIN',
-                       'DEF:l_max={file}:longterm:MAX',
-                       "AREA:s_max#$HalfGreen",
-                       "AREA:s_min#$Canvas",
-                       "LINE1:s_avg#$FullGreen: 1m average",
-                       'GPRINT:s_min:MIN:%4.2lf Min,',
-                       'GPRINT:s_avg:AVERAGE:%4.2lf Avg,',
-                       'GPRINT:s_max:MAX:%4.2lf Max,',
-                       'GPRINT:s_avg:LAST:%4.2lf Last\n',
-                       "LINE1:m_avg#$FullBlue: 5m average",
-                       'GPRINT:m_min:MIN:%4.2lf Min,',
-                       'GPRINT:m_avg:AVERAGE:%4.2lf Avg,',
-                       'GPRINT:m_max:MAX:%4.2lf Max,',
-                       'GPRINT:m_avg:LAST:%4.2lf Last\n',
-                       "LINE1:l_avg#$FullRed:15m average",
-                       'GPRINT:l_min:MIN:%4.2lf Min,',
-                       'GPRINT:l_avg:AVERAGE:%4.2lf Avg,',
-                       'GPRINT:l_max:MAX:%4.2lf Max,',
-                       'GPRINT:l_avg:LAST:%4.2lf Last'
-               ],
-               load_percent => [
-                       'DEF:avg={file}:percent:AVERAGE',
-                       'DEF:min={file}:percent:MIN',
-                       'DEF:max={file}:percent:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Load",
-                       'GPRINT:min:MIN:%5.1lf%s%% Min,',
-                       'GPRINT:avg:AVERAGE:%5.1lf%s%% Avg,',
-                       'GPRINT:max:MAX:%5.1lf%s%% Max,',
-                       'GPRINT:avg:LAST:%5.1lf%s%% Last\l'
-               ],
-               mails => ['DEF:rawgood={file}:good:AVERAGE',
-                       'DEF:rawspam={file}:spam:AVERAGE',
-                       'CDEF:good=rawgood,UN,0,rawgood,IF',
-                       'CDEF:spam=rawspam,UN,0,rawspam,IF',
-                       'CDEF:negspam=spam,-1,*',
-                       "AREA:good#$HalfGreen",
-                       "LINE1:good#$FullGreen:Good mails",
-                       'GPRINT:good:AVERAGE:%4.1lf Avg,',
-                       'GPRINT:good:MAX:%4.1lf Max,',
-                       'GPRINT:good:LAST:%4.1lf Last\n',
-                       "AREA:negspam#$HalfRed",
-                       "LINE1:negspam#$FullRed:Spam mails",
-                       'GPRINT:spam:AVERAGE:%4.1lf Avg,',
-                       'GPRINT:spam:MAX:%4.1lf Max,',
-                       'GPRINT:spam:LAST:%4.1lf Last',
-                       'HRULE:0#000000'],
-               memory => [
-                       'DEF:used_avg={file}:used:AVERAGE',
-                       'DEF:free_avg={file}:free:AVERAGE',
-                       'DEF:buffers_avg={file}:buffers:AVERAGE',
-                       'DEF:cached_avg={file}:cached:AVERAGE',
-                       'DEF:used_min={file}:used:MIN',
-                       'DEF:free_min={file}:free:MIN',
-                       'DEF:buffers_min={file}:buffers:MIN',
-                       'DEF:cached_min={file}:cached:MIN',
-                       'DEF:used_max={file}:used:MAX',
-                       'DEF:free_max={file}:free:MAX',
-                       'DEF:buffers_max={file}:buffers:MAX',
-                       'DEF:cached_max={file}:cached:MAX',
-                       'CDEF:cached_avg_nn=cached_avg,UN,0,cached_avg,IF',
-                       'CDEF:buffers_avg_nn=buffers_avg,UN,0,buffers_avg,IF',
-                       'CDEF:free_cached_buffers_used=free_avg,cached_avg_nn,+,buffers_avg_nn,+,used_avg,+',
-                       'CDEF:cached_buffers_used=cached_avg,buffers_avg_nn,+,used_avg,+',
-                       'CDEF:buffers_used=buffers_avg,used_avg,+',
-                       "AREA:free_cached_buffers_used#$HalfGreen",
-                       "AREA:cached_buffers_used#$HalfBlue",
-                       "AREA:buffers_used#$HalfYellow",
-                       "AREA:used_avg#$HalfRed",
-                       "LINE1:free_cached_buffers_used#$FullGreen:Free        ",
-                       'GPRINT:free_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:free_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:free_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:free_avg:LAST:%5.1lf%s Last\n',
-                       "LINE1:cached_buffers_used#$FullBlue:Page cache  ",
-                       'GPRINT:cached_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:cached_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:cached_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:cached_avg:LAST:%5.1lf%s Last\n',
-                       "LINE1:buffers_used#$FullYellow:Buffer cache",
-                       'GPRINT:buffers_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:buffers_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:buffers_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:buffers_avg:LAST:%5.1lf%s Last\n',
-                       "LINE1:used_avg#$FullRed:Used        ",
-                       'GPRINT:used_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:used_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:used_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:used_avg:LAST:%5.1lf%s Last'
-               ],
-               mysql_commands => [
-                       "DEF:val_avg={file}:value:AVERAGE",
-                       "DEF:val_min={file}:value:MIN",
-                       "DEF:val_max={file}:value:MAX",
-                       "AREA:val_max#$HalfBlue",
-                       "AREA:val_min#$Canvas",
-                       "LINE1:val_avg#$FullBlue:{inst}",
-                       'GPRINT:val_min:MIN:%5.2lf Min,',
-                       'GPRINT:val_avg:AVERAGE:%5.2lf Avg,',
-                       'GPRINT:val_max:MAX:%5.2lf Max,',
-                       'GPRINT:val_avg:LAST:%5.2lf Last'
-               ],
-               mysql_handler => [
-                       "DEF:val_avg={file}:value:AVERAGE",
-                       "DEF:val_min={file}:value:MIN",
-                       "DEF:val_max={file}:value:MAX",
-                       "AREA:val_max#$HalfBlue",
-                       "AREA:val_min#$Canvas",
-                       "LINE1:val_avg#$FullBlue:{inst}",
-                       'GPRINT:val_min:MIN:%5.2lf Min,',
-                       'GPRINT:val_avg:AVERAGE:%5.2lf Avg,',
-                       'GPRINT:val_max:MAX:%5.2lf Max,',
-                       'GPRINT:val_avg:LAST:%5.2lf Last'
-               ],
-               mysql_qcache => [
-                       "DEF:hits_min={file}:hits:MIN",
-                       "DEF:hits_avg={file}:hits:AVERAGE",
-                       "DEF:hits_max={file}:hits:MAX",
-                       "DEF:inserts_min={file}:inserts:MIN",
-                       "DEF:inserts_avg={file}:inserts:AVERAGE",
-                       "DEF:inserts_max={file}:inserts:MAX",
-                       "DEF:not_cached_min={file}:not_cached:MIN",
-                       "DEF:not_cached_avg={file}:not_cached:AVERAGE",
-                       "DEF:not_cached_max={file}:not_cached:MAX",
-                       "DEF:lowmem_prunes_min={file}:lowmem_prunes:MIN",
-                       "DEF:lowmem_prunes_avg={file}:lowmem_prunes:AVERAGE",
-                       "DEF:lowmem_prunes_max={file}:lowmem_prunes:MAX",
-                       "DEF:queries_min={file}:queries_in_cache:MIN",
-                       "DEF:queries_avg={file}:queries_in_cache:AVERAGE",
-                       "DEF:queries_max={file}:queries_in_cache:MAX",
-                       "CDEF:unknown=queries_avg,UNKN,+",
-                       "CDEF:not_cached_agg=hits_avg,inserts_avg,+,not_cached_avg,+",
-                       "CDEF:inserts_agg=hits_avg,inserts_avg,+",
-                       "CDEF:hits_agg=hits_avg",
-                       "AREA:not_cached_agg#$HalfYellow",
-                       "AREA:inserts_agg#$HalfBlue",
-                       "AREA:hits_agg#$HalfGreen",
-                       "LINE1:not_cached_agg#$FullYellow:Not Cached      ",
-                       'GPRINT:not_cached_min:MIN:%5.2lf Min,',
-                       'GPRINT:not_cached_avg:AVERAGE:%5.2lf Avg,',
-                       'GPRINT:not_cached_max:MAX:%5.2lf Max,',
-                       'GPRINT:not_cached_avg:LAST:%5.2lf Last\l',
-                       "LINE1:inserts_agg#$FullBlue:Inserts         ",
-                       'GPRINT:inserts_min:MIN:%5.2lf Min,',
-                       'GPRINT:inserts_avg:AVERAGE:%5.2lf Avg,',
-                       'GPRINT:inserts_max:MAX:%5.2lf Max,',
-                       'GPRINT:inserts_avg:LAST:%5.2lf Last\l',
-                       "LINE1:hits_agg#$FullGreen:Hits            ",
-                       'GPRINT:hits_min:MIN:%5.2lf Min,',
-                       'GPRINT:hits_avg:AVERAGE:%5.2lf Avg,',
-                       'GPRINT:hits_max:MAX:%5.2lf Max,',
-                       'GPRINT:hits_avg:LAST:%5.2lf Last\l',
-                       "LINE1:lowmem_prunes_avg#$FullRed:Lowmem Prunes   ",
-                       'GPRINT:lowmem_prunes_min:MIN:%5.2lf Min,',
-                       'GPRINT:lowmem_prunes_avg:AVERAGE:%5.2lf Avg,',
-                       'GPRINT:lowmem_prunes_max:MAX:%5.2lf Max,',
-                       'GPRINT:lowmem_prunes_avg:LAST:%5.2lf Last\l',
-                       "LINE1:unknown#$Canvas:Queries in cache",
-                       'GPRINT:queries_min:MIN:%5.0lf Min,',
-                       'GPRINT:queries_avg:AVERAGE:%5.0lf Avg,',
-                       'GPRINT:queries_max:MAX:%5.0lf Max,',
-                       'GPRINT:queries_avg:LAST:%5.0lf Last\l'
-               ],
-               mysql_threads => [
-                       "DEF:running_min={file}:running:MIN",
-                       "DEF:running_avg={file}:running:AVERAGE",
-                       "DEF:running_max={file}:running:MAX",
-                       "DEF:connected_min={file}:connected:MIN",
-                       "DEF:connected_avg={file}:connected:AVERAGE",
-                       "DEF:connected_max={file}:connected:MAX",
-                       "DEF:cached_min={file}:cached:MIN",
-                       "DEF:cached_avg={file}:cached:AVERAGE",
-                       "DEF:cached_max={file}:cached:MAX",
-                       "DEF:created_min={file}:created:MIN",
-                       "DEF:created_avg={file}:created:AVERAGE",
-                       "DEF:created_max={file}:created:MAX",
-                       "CDEF:unknown=created_avg,UNKN,+",
-                       "CDEF:cached_agg=connected_avg,cached_avg,+",
-                       "AREA:cached_agg#$HalfGreen",
-                       "AREA:connected_avg#$HalfBlue",
-                       "AREA:running_avg#$HalfRed",
-                       "LINE1:cached_agg#$FullGreen:Cached   ",
-                       'GPRINT:cached_min:MIN:%5.1lf Min,',
-                       'GPRINT:cached_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:cached_max:MAX:%5.1lf Max,',
-                       'GPRINT:cached_avg:LAST:%5.1lf Last\l',
-                       "LINE1:connected_avg#$FullBlue:Connected",
-                       'GPRINT:connected_min:MIN:%5.1lf Min,',
-                       'GPRINT:connected_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:connected_max:MAX:%5.1lf Max,',
-                       'GPRINT:connected_avg:LAST:%5.1lf Last\l',
-                       "LINE1:running_avg#$FullRed:Running  ",
-                       'GPRINT:running_min:MIN:%5.1lf Min,',
-                       'GPRINT:running_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:running_max:MAX:%5.1lf Max,',
-                       'GPRINT:running_avg:LAST:%5.1lf Last\l',
-                       "LINE1:unknown#$Canvas:Created  ",
-                       'GPRINT:created_min:MIN:%5.0lf Min,',
-                       'GPRINT:created_avg:AVERAGE:%5.0lf Avg,',
-                       'GPRINT:created_max:MAX:%5.0lf Max,',
-                       'GPRINT:created_avg:LAST:%5.0lf Last\l'
-               ],
-               nfs3_procedures => [
-                       "DEF:null_avg={file}:null:AVERAGE",
-                       "DEF:getattr_avg={file}:getattr:AVERAGE",
-                       "DEF:setattr_avg={file}:setattr:AVERAGE",
-                       "DEF:lookup_avg={file}:lookup:AVERAGE",
-                       "DEF:access_avg={file}:access:AVERAGE",
-                       "DEF:readlink_avg={file}:readlink:AVERAGE",
-                       "DEF:read_avg={file}:read:AVERAGE",
-                       "DEF:write_avg={file}:write:AVERAGE",
-                       "DEF:create_avg={file}:create:AVERAGE",
-                       "DEF:mkdir_avg={file}:mkdir:AVERAGE",
-                       "DEF:symlink_avg={file}:symlink:AVERAGE",
-                       "DEF:mknod_avg={file}:mknod:AVERAGE",
-                       "DEF:remove_avg={file}:remove:AVERAGE",
-                       "DEF:rmdir_avg={file}:rmdir:AVERAGE",
-                       "DEF:rename_avg={file}:rename:AVERAGE",
-                       "DEF:link_avg={file}:link:AVERAGE",
-                       "DEF:readdir_avg={file}:readdir:AVERAGE",
-                       "DEF:readdirplus_avg={file}:readdirplus:AVERAGE",
-                       "DEF:fsstat_avg={file}:fsstat:AVERAGE",
-                       "DEF:fsinfo_avg={file}:fsinfo:AVERAGE",
-                       "DEF:pathconf_avg={file}:pathconf:AVERAGE",
-                       "DEF:commit_avg={file}:commit:AVERAGE",
-                       "DEF:null_max={file}:null:MAX",
-                       "DEF:getattr_max={file}:getattr:MAX",
-                       "DEF:setattr_max={file}:setattr:MAX",
-                       "DEF:lookup_max={file}:lookup:MAX",
-                       "DEF:access_max={file}:access:MAX",
-                       "DEF:readlink_max={file}:readlink:MAX",
-                       "DEF:read_max={file}:read:MAX",
-                       "DEF:write_max={file}:write:MAX",
-                       "DEF:create_max={file}:create:MAX",
-                       "DEF:mkdir_max={file}:mkdir:MAX",
-                       "DEF:symlink_max={file}:symlink:MAX",
-                       "DEF:mknod_max={file}:mknod:MAX",
-                       "DEF:remove_max={file}:remove:MAX",
-                       "DEF:rmdir_max={file}:rmdir:MAX",
-                       "DEF:rename_max={file}:rename:MAX",
-                       "DEF:link_max={file}:link:MAX",
-                       "DEF:readdir_max={file}:readdir:MAX",
-                       "DEF:readdirplus_max={file}:readdirplus:MAX",
-                       "DEF:fsstat_max={file}:fsstat:MAX",
-                       "DEF:fsinfo_max={file}:fsinfo:MAX",
-                       "DEF:pathconf_max={file}:pathconf:MAX",
-                       "DEF:commit_max={file}:commit:MAX",
-                       "CDEF:other_avg=null_avg,readlink_avg,create_avg,mkdir_avg,symlink_avg,mknod_avg,remove_avg,rmdir_avg,rename_avg,link_avg,readdir_avg,readdirplus_avg,fsstat_avg,fsinfo_avg,pathconf_avg,+,+,+,+,+,+,+,+,+,+,+,+,+,+",
-                       "CDEF:other_max=null_max,readlink_max,create_max,mkdir_max,symlink_max,mknod_max,remove_max,rmdir_max,rename_max,link_max,readdir_max,readdirplus_max,fsstat_max,fsinfo_max,pathconf_max,+,+,+,+,+,+,+,+,+,+,+,+,+,+",
-                       "CDEF:stack_read=read_avg",
-                       "CDEF:stack_getattr=stack_read,getattr_avg,+",
-                       "CDEF:stack_access=stack_getattr,access_avg,+",
-                       "CDEF:stack_lookup=stack_access,lookup_avg,+",
-                       "CDEF:stack_write=stack_lookup,write_avg,+",
-                       "CDEF:stack_commit=stack_write,commit_avg,+",
-                       "CDEF:stack_setattr=stack_commit,setattr_avg,+",
-                       "CDEF:stack_other=stack_setattr,other_avg,+",
-                       "AREA:stack_other#$HalfRed",
-                       "AREA:stack_setattr#$HalfGreen",
-                       "AREA:stack_commit#$HalfYellow",
-                       "AREA:stack_write#$HalfGreen",
-                       "AREA:stack_lookup#$HalfBlue",
-                       "AREA:stack_access#$HalfMagenta",
-                       "AREA:stack_getattr#$HalfCyan",
-                       "AREA:stack_read#$HalfBlue",
-                       "LINE1:stack_other#$FullRed:Other  ",
-                       'GPRINT:other_max:MAX:%5.1lf Max,',
-                       'GPRINT:other_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:other_avg:LAST:%5.1lf Last\l',
-                       "LINE1:stack_setattr#$FullGreen:setattr",
-                       'GPRINT:setattr_max:MAX:%5.1lf Max,',
-                       'GPRINT:setattr_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:setattr_avg:LAST:%5.1lf Last\l',
-                       "LINE1:stack_commit#$FullYellow:commit ",
-                       'GPRINT:commit_max:MAX:%5.1lf Max,',
-                       'GPRINT:commit_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:commit_avg:LAST:%5.1lf Last\l',
-                       "LINE1:stack_write#$FullGreen:write  ",
-                       'GPRINT:write_max:MAX:%5.1lf Max,',
-                       'GPRINT:write_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:write_avg:LAST:%5.1lf Last\l',
-                       "LINE1:stack_lookup#$FullBlue:lookup ",
-                       'GPRINT:lookup_max:MAX:%5.1lf Max,',
-                       'GPRINT:lookup_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:lookup_avg:LAST:%5.1lf Last\l',
-                       "LINE1:stack_access#$FullMagenta:access ",
-                       'GPRINT:access_max:MAX:%5.1lf Max,',
-                       'GPRINT:access_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:access_avg:LAST:%5.1lf Last\l',
-                       "LINE1:stack_getattr#$FullCyan:getattr",
-                       'GPRINT:getattr_max:MAX:%5.1lf Max,',
-                       'GPRINT:getattr_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:getattr_avg:LAST:%5.1lf Last\l',
-                       "LINE1:stack_read#$FullBlue:read   ",
-                       'GPRINT:read_max:MAX:%5.1lf Max,',
-                       'GPRINT:read_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:read_avg:LAST:%5.1lf Last\l'
-               ],
-               opcode => [
-                       'DEF:avg={file}:value:AVERAGE',
-                       'DEF:min={file}:value:MIN',
-                       'DEF:max={file}:value:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Queries/s",
-                       'GPRINT:min:MIN:%9.3lf Min,',
-                       'GPRINT:avg:AVERAGE:%9.3lf Average,',
-                       'GPRINT:max:MAX:%9.3lf Max,',
-                       'GPRINT:avg:LAST:%9.3lf Last\l'
-               ],
-               partition => [
-                       "DEF:rbyte_avg={file}:rbytes:AVERAGE",
-                       "DEF:rbyte_min={file}:rbytes:MIN",
-                       "DEF:rbyte_max={file}:rbytes:MAX",
-                       "DEF:wbyte_avg={file}:wbytes:AVERAGE",
-                       "DEF:wbyte_min={file}:wbytes:MIN",
-                       "DEF:wbyte_max={file}:wbytes:MAX",
-                       'CDEF:overlap=wbyte_avg,rbyte_avg,GT,rbyte_avg,wbyte_avg,IF',
-                       "AREA:wbyte_avg#$HalfGreen",
-                       "AREA:rbyte_avg#$HalfBlue",
-                       "AREA:overlap#$HalfBlueGreen",
-                       "LINE1:wbyte_avg#$FullGreen:Write",
-                       'GPRINT:wbyte_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:wbyte_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:wbyte_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:wbyte_avg:LAST:%5.1lf%s Last\l',
-                       "LINE1:rbyte_avg#$FullBlue:Read ",
-                       'GPRINT:rbyte_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:rbyte_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:rbyte_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:rbyte_avg:LAST:%5.1lf%s Last\l'
-               ],
-               ping => ['DEF:ping_avg={file}:ping:AVERAGE',
-                       'DEF:ping_min={file}:ping:MIN',
-                       'DEF:ping_max={file}:ping:MAX',
-                       "AREA:ping_max#$HalfBlue",
-                       "AREA:ping_min#$Canvas",
-                       "LINE1:ping_avg#$FullBlue:Ping",
-                       'GPRINT:ping_min:MIN:%4.1lf ms Min,',
-                       'GPRINT:ping_avg:AVERAGE:%4.1lf ms Avg,',
-                       'GPRINT:ping_max:MAX:%4.1lf ms Max,',
-                       'GPRINT:ping_avg:LAST:%4.1lf ms Last'],
-               processes => [
-                       "DEF:running_avg={file}:running:AVERAGE",
-                       "DEF:running_min={file}:running:MIN",
-                       "DEF:running_max={file}:running:MAX",
-                       "DEF:sleeping_avg={file}:sleeping:AVERAGE",
-                       "DEF:sleeping_min={file}:sleeping:MIN",
-                       "DEF:sleeping_max={file}:sleeping:MAX",
-                       "DEF:zombies_avg={file}:zombies:AVERAGE",
-                       "DEF:zombies_min={file}:zombies:MIN",
-                       "DEF:zombies_max={file}:zombies:MAX",
-                       "DEF:stopped_avg={file}:stopped:AVERAGE",
-                       "DEF:stopped_min={file}:stopped:MIN",
-                       "DEF:stopped_max={file}:stopped:MAX",
-                       "DEF:paging_avg={file}:paging:AVERAGE",
-                       "DEF:paging_min={file}:paging:MIN",
-                       "DEF:paging_max={file}:paging:MAX",
-                       "DEF:blocked_avg={file}:blocked:AVERAGE",
-                       "DEF:blocked_min={file}:blocked:MIN",
-                       "DEF:blocked_max={file}:blocked:MAX",
-                       'CDEF:paging_acc=sleeping_avg,running_avg,stopped_avg,zombies_avg,blocked_avg,paging_avg,+,+,+,+,+',
-                       'CDEF:blocked_acc=sleeping_avg,running_avg,stopped_avg,zombies_avg,blocked_avg,+,+,+,+',
-                       'CDEF:zombies_acc=sleeping_avg,running_avg,stopped_avg,zombies_avg,+,+,+',
-                       'CDEF:stopped_acc=sleeping_avg,running_avg,stopped_avg,+,+',
-                       'CDEF:running_acc=sleeping_avg,running_avg,+',
-                       'CDEF:sleeping_acc=sleeping_avg',
-                       "AREA:paging_acc#$HalfYellow",
-                       "AREA:blocked_acc#$HalfCyan",
-                       "AREA:zombies_acc#$HalfRed",
-                       "AREA:stopped_acc#$HalfMagenta",
-                       "AREA:running_acc#$HalfGreen",
-                       "AREA:sleeping_acc#$HalfBlue",
-                       "LINE1:paging_acc#$FullYellow:Paging  ",
-                       'GPRINT:paging_min:MIN:%5.1lf Min,',
-                       'GPRINT:paging_avg:AVERAGE:%5.1lf Average,',
-                       'GPRINT:paging_max:MAX:%5.1lf Max,',
-                       'GPRINT:paging_avg:LAST:%5.1lf Last\l',
-                       "LINE1:blocked_acc#$FullCyan:Blocked ",
-                       'GPRINT:blocked_min:MIN:%5.1lf Min,',
-                       'GPRINT:blocked_avg:AVERAGE:%5.1lf Average,',
-                       'GPRINT:blocked_max:MAX:%5.1lf Max,',
-                       'GPRINT:blocked_avg:LAST:%5.1lf Last\l',
-                       "LINE1:zombies_acc#$FullRed:Zombies ",
-                       'GPRINT:zombies_min:MIN:%5.1lf Min,',
-                       'GPRINT:zombies_avg:AVERAGE:%5.1lf Average,',
-                       'GPRINT:zombies_max:MAX:%5.1lf Max,',
-                       'GPRINT:zombies_avg:LAST:%5.1lf Last\l',
-                       "LINE1:stopped_acc#$FullMagenta:Stopped ",
-                       'GPRINT:stopped_min:MIN:%5.1lf Min,',
-                       'GPRINT:stopped_avg:AVERAGE:%5.1lf Average,',
-                       'GPRINT:stopped_max:MAX:%5.1lf Max,',
-                       'GPRINT:stopped_avg:LAST:%5.1lf Last\l',
-                       "LINE1:running_acc#$FullGreen:Running ",
-                       'GPRINT:running_min:MIN:%5.1lf Min,',
-                       'GPRINT:running_avg:AVERAGE:%5.1lf Average,',
-                       'GPRINT:running_max:MAX:%5.1lf Max,',
-                       'GPRINT:running_avg:LAST:%5.1lf Last\l',
-                       "LINE1:sleeping_acc#$FullBlue:Sleeping",
-                       'GPRINT:sleeping_min:MIN:%5.1lf Min,',
-                       'GPRINT:sleeping_avg:AVERAGE:%5.1lf Average,',
-                       'GPRINT:sleeping_max:MAX:%5.1lf Max,',
-                       'GPRINT:sleeping_avg:LAST:%5.1lf Last\l'
-               ],
-               ps_rss => [
-                       'DEF:avg={file}:byte:AVERAGE',
-                       'DEF:min={file}:byte:MIN',
-                       'DEF:max={file}:byte:MAX',
-                       "AREA:avg#$HalfBlue",
-                       "LINE1:avg#$FullBlue:RSS",
-                       'GPRINT:min:MIN:%5.1lf%s Min,',
-                       'GPRINT:avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:max:MAX:%5.1lf%s Max,',
-                       'GPRINT:avg:LAST:%5.1lf%s Last\l'
-               ],
-               ps_cputime => [
-                       'DEF:user_avg_raw={file}:user:AVERAGE',
-                       'DEF:user_min_raw={file}:user:MIN',
-                       'DEF:user_max_raw={file}:user:MAX',
-                       'DEF:syst_avg_raw={file}:syst:AVERAGE',
-                       'DEF:syst_min_raw={file}:syst:MIN',
-                       'DEF:syst_max_raw={file}:syst:MAX',
-                       'CDEF:user_avg=user_avg_raw,1000000,/',
-                       'CDEF:user_min=user_min_raw,1000000,/',
-                       'CDEF:user_max=user_max_raw,1000000,/',
-                       'CDEF:syst_avg=syst_avg_raw,1000000,/',
-                       'CDEF:syst_min=syst_min_raw,1000000,/',
-                       'CDEF:syst_max=syst_max_raw,1000000,/',
-                       'CDEF:user_syst=syst_avg,UN,0,syst_avg,IF,user_avg,+',
-                       "AREA:user_syst#$HalfBlue",
-                       "AREA:syst_avg#$HalfRed",
-                       "LINE1:user_syst#$FullBlue:User  ",
-                       'GPRINT:user_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:user_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:user_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:user_avg:LAST:%5.1lf%s Last\l',
-                       "LINE1:syst_avg#$FullRed:System",
-                       'GPRINT:syst_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:syst_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:syst_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:syst_avg:LAST:%5.1lf%s Last\l'
-               ],
-               ps_count => [
-                       'DEF:procs_avg={file}:processes:AVERAGE',
-                       'DEF:procs_min={file}:processes:MIN',
-                       'DEF:procs_max={file}:processes:MAX',
-                       'DEF:thrds_avg={file}:threads:AVERAGE',
-                       'DEF:thrds_min={file}:threads:MIN',
-                       'DEF:thrds_max={file}:threads:MAX',
-                       "AREA:thrds_avg#$HalfBlue",
-                       "AREA:procs_avg#$HalfRed",
-                       "LINE1:thrds_avg#$FullBlue:Threads  ",
-                       'GPRINT:thrds_min:MIN:%5.1lf Min,',
-                       'GPRINT:thrds_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:thrds_max:MAX:%5.1lf Max,',
-                       'GPRINT:thrds_avg:LAST:%5.1lf Last\l',
-                       "LINE1:procs_avg#$FullRed:Processes",
-                       'GPRINT:procs_min:MIN:%5.1lf Min,',
-                       'GPRINT:procs_avg:AVERAGE:%5.1lf Avg,',
-                       'GPRINT:procs_max:MAX:%5.1lf Max,',
-                       'GPRINT:procs_avg:LAST:%5.1lf Last\l'
-               ],
-               ps_pagefaults => [
-                       'DEF:minor_avg={file}:minflt:AVERAGE',
-                       'DEF:minor_min={file}:minflt:MIN',
-                       'DEF:minor_max={file}:minflt:MAX',
-                       'DEF:major_avg={file}:majflt:AVERAGE',
-                       'DEF:major_min={file}:majflt:MIN',
-                       'DEF:major_max={file}:majflt:MAX',
-                       'CDEF:minor_major=major_avg,UN,0,major_avg,IF,minor_avg,+',
-                       "AREA:minor_major#$HalfBlue",
-                       "AREA:major_avg#$HalfRed",
-                       "LINE1:minor_major#$FullBlue:Minor",
-                       'GPRINT:minor_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:minor_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:minor_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:minor_avg:LAST:%5.1lf%s Last\l',
-                       "LINE1:major_avg#$FullRed:Major",
-                       'GPRINT:major_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:major_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:major_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:major_avg:LAST:%5.1lf%s Last\l'
-               ],
-               qtype => [
-                       'DEF:avg={file}:value:AVERAGE',
-                       'DEF:min={file}:value:MIN',
-                       'DEF:max={file}:value:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Queries/s",
-                       'GPRINT:min:MIN:%9.3lf Min,',
-                       'GPRINT:avg:AVERAGE:%9.3lf Average,',
-                       'GPRINT:max:MAX:%9.3lf Max,',
-                       'GPRINT:avg:LAST:%9.3lf Last\l'
-               ],
-               rcode => [
-                       'DEF:avg={file}:value:AVERAGE',
-                       'DEF:min={file}:value:MIN',
-                       'DEF:max={file}:value:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Queries/s",
-                       'GPRINT:min:MIN:%9.3lf Min,',
-                       'GPRINT:avg:AVERAGE:%9.3lf Average,',
-                       'GPRINT:max:MAX:%9.3lf Max,',
-                       'GPRINT:avg:LAST:%9.3lf Last\l'
-               ],
-               swap => [
-                       'DEF:used_avg={file}:used:AVERAGE',
-                       'DEF:used_min={file}:used:MIN',
-                       'DEF:used_max={file}:used:MAX',
-                       'DEF:free_avg={file}:free:AVERAGE',
-                       'DEF:free_min={file}:free:MIN',
-                       'DEF:free_max={file}:free:MAX',
-                       'DEF:cach_avg={file}:cached:AVERAGE',
-                       'DEF:cach_min={file}:cached:MIN',
-                       'DEF:cach_max={file}:cached:MAX',
-                       'DEF:resv_avg={file}:resv:AVERAGE',
-                       'DEF:resv_min={file}:resv:MIN',
-                       'DEF:resv_max={file}:resv:MAX',
-                       'CDEF:cach_avg_notnull=cach_avg,UN,0,cach_avg,IF',
-                       'CDEF:resv_avg_notnull=resv_avg,UN,0,resv_avg,IF',
-                       'CDEF:used_acc=used_avg',
-                       'CDEF:resv_acc=used_acc,resv_avg_notnull,+',
-                       'CDEF:cach_acc=resv_acc,cach_avg_notnull,+',
-                       'CDEF:free_acc=cach_acc,free_avg,+',
-                       "AREA:free_acc#$HalfGreen",
-                       "AREA:cach_acc#$HalfBlue",
-                       "AREA:resv_acc#$HalfYellow",
-                       "AREA:used_acc#$HalfRed",
-                       "LINE1:free_acc#$FullGreen:Free    ",
-                       'GPRINT:free_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:free_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:free_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:free_avg:LAST:%5.1lf%s Last\n',
-                       "LINE1:cach_acc#$FullBlue:Cached  ",
-                       'GPRINT:cach_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:cach_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:cach_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:cach_avg:LAST:%5.1lf%s Last\l',
-                       "LINE1:resv_acc#$FullYellow:Reserved",
-                       'GPRINT:resv_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:resv_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:resv_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:resv_avg:LAST:%5.1lf%s Last\n',
-                       "LINE1:used_acc#$FullRed:Used    ",
-                       'GPRINT:used_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:used_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:used_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:used_avg:LAST:%5.1lf%s Last\l'
-               ],
-               temperature => [
-                       'DEF:temp_avg={file}:value:AVERAGE',
-                       'DEF:temp_min={file}:value:MIN',
-                       'DEF:temp_max={file}:value:MAX',
-                       "AREA:temp_max#$HalfRed",
-                       "AREA:temp_min#$Canvas",
-                       "LINE1:temp_avg#$FullRed:Temperature",
-                       'GPRINT:temp_min:MIN:%4.1lf Min,',
-                       'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
-                       'GPRINT:temp_max:MAX:%4.1lf Max,',
-                       'GPRINT:temp_avg:LAST:%4.1lf Last\l'
-               ],
-               timeleft => [
-                       'DEF:avg={file}:timeleft:AVERAGE',
-                       'DEF:min={file}:timeleft:MIN',
-                       'DEF:max={file}:timeleft:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Time left [min]",
-                       'GPRINT:min:MIN:%5.1lf%s Min,',
-                       'GPRINT:avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:max:MAX:%5.1lf%s Max,',
-                       'GPRINT:avg:LAST:%5.1lf%s Last\l'
-               ],
-               time_offset => [ # NTPd
-                       'DEF:s_avg={file}:seconds:AVERAGE',
-                       'DEF:s_min={file}:seconds:MIN',
-                       'DEF:s_max={file}:seconds:MAX',
-                       "AREA:s_max#$HalfBlue",
-                       "AREA:s_min#$Canvas",
-                       "LINE1:s_avg#$FullBlue:{inst}",
-                       'GPRINT:s_min:MIN:%7.3lf%s Min,',
-                       'GPRINT:s_avg:AVERAGE:%7.3lf%s Avg,',
-                       'GPRINT:s_max:MAX:%7.3lf%s Max,',
-                       'GPRINT:s_avg:LAST:%7.3lf%s Last'
-               ],
-               traffic => ['DEF:out_min_raw={file}:outgoing:MIN',
-                       'DEF:out_avg_raw={file}:outgoing:AVERAGE',
-                       'DEF:out_max_raw={file}:outgoing:MAX',
-                       'DEF:inc_min_raw={file}:incoming:MIN',
-                       'DEF:inc_avg_raw={file}:incoming:AVERAGE',
-                       'DEF:inc_max_raw={file}:incoming:MAX',
-                       'CDEF:out_min=out_min_raw,8,*',
-                       'CDEF:out_avg=out_avg_raw,8,*',
-                       'CDEF:out_max=out_max_raw,8,*',
-                       'CDEF:inc_min=inc_min_raw,8,*',
-                       'CDEF:inc_avg=inc_avg_raw,8,*',
-                       'CDEF:inc_max=inc_max_raw,8,*',
-                       'CDEF:overlap=out_avg,inc_avg,GT,inc_avg,out_avg,IF',
-                       'CDEF:mytime=out_avg_raw,TIME,TIME,IF',
-                       'CDEF:sample_len_raw=mytime,PREV(mytime),-',
-                       'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
-                       'CDEF:out_avg_sample=out_avg_raw,UN,0,out_avg_raw,IF,sample_len,*',
-                       'CDEF:out_avg_sum=PREV,UN,0,PREV,IF,out_avg_sample,+',
-                       'CDEF:inc_avg_sample=inc_avg_raw,UN,0,inc_avg_raw,IF,sample_len,*',
-                       'CDEF:inc_avg_sum=PREV,UN,0,PREV,IF,inc_avg_sample,+',
-                       "AREA:out_avg#$HalfGreen",
-                       "AREA:inc_avg#$HalfBlue",
-                       "AREA:overlap#$HalfBlueGreen",
-                       "LINE1:out_avg#$FullGreen:Outgoing",
-                       'GPRINT:out_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:out_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:out_avg:LAST:%5.1lf%s Last',
-                       'GPRINT:out_avg_sum:LAST:(ca. %5.1lf%sB Total)\l',
-                       "LINE1:inc_avg#$FullBlue:Incoming",
-                       #'GPRINT:inc_min:MIN:%5.1lf %s Min,',
-                       'GPRINT:inc_avg:AVERAGE:%5.1lf%s Avg,',
-                       'GPRINT:inc_max:MAX:%5.1lf%s Max,',
-                       'GPRINT:inc_avg:LAST:%5.1lf%s Last',
-                       'GPRINT:inc_avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
-               ],
-                cpufreq => [
-                        'DEF:cpufreq_avg={file}:value:AVERAGE',
-                        'DEF:cpufreq_min={file}:value:MIN',
-                        'DEF:cpufreq_max={file}:value:MAX',
-                        "AREA:cpufreq_max#$HalfBlue",
-                        "AREA:cpufreq_min#$Canvas",
-                        "LINE1:cpufreq_avg#$FullBlue:Frequency",
-                        'GPRINT:cpufreq_min:MIN:%5.1lf%s Min,',
-                        'GPRINT:cpufreq_avg:AVERAGE:%5.1lf%s Avg,',
-                        'GPRINT:cpufreq_max:MAX:%5.1lf%s Max,',
-                        'GPRINT:cpufreq_avg:LAST:%5.1lf%s Last\l'
-                ],
-               multimeter => [
-                           'DEF:multimeter_avg={file}:value:AVERAGE',
-                           'DEF:multimeter_min={file}:value:MIN',
-                           'DEF:multimeter_max={file}:value:MAX',
-                           "AREA:multimeter_max#$HalfBlue",
-                           "AREA:multimeter_min#$Canvas",
-                           "LINE1:multimeter_avg#$FullBlue:Multimeter",
-                           'GPRINT:multimeter_min:MIN:%4.1lf Min,',
-                           'GPRINT:multimeter_avg:AVERAGE:%4.1lf Average,',
-                           'GPRINT:multimeter_max:MAX:%4.1lf Max,',
-                           'GPRINT:multimeter_avg:LAST:%4.1lf Last\l'
-               ],
-               users => [
-                           'DEF:users_avg={file}:users:AVERAGE',
-                           'DEF:users_min={file}:users:MIN',
-                           'DEF:users_max={file}:users:MAX',
-                           "AREA:users_max#$HalfBlue",
-                           "AREA:users_min#$Canvas",
-                           "LINE1:users_avg#$FullBlue:Users",
-                           'GPRINT:users_min:MIN:%4.1lf Min,',
-                           'GPRINT:users_avg:AVERAGE:%4.1lf Average,',
-                           'GPRINT:users_max:MAX:%4.1lf Max,',
-                           'GPRINT:users_avg:LAST:%4.1lf Last\l'
-               ],
-               voltage => [
-                       'DEF:avg={file}:voltage:AVERAGE',
-                       'DEF:min={file}:voltage:MIN',
-                       'DEF:max={file}:voltage:MAX',
-                       "AREA:max#$HalfBlue",
-                       "AREA:min#$Canvas",
-                       "LINE1:avg#$FullBlue:Voltage",
-                       'GPRINT:min:MIN:%5.1lf%sV Min,',
-                       'GPRINT:avg:AVERAGE:%5.1lf%sV Avg,',
-                       'GPRINT:max:MAX:%5.1lf%sV Max,',
-                       'GPRINT:avg:LAST:%5.1lf%sV Last\l'
-               ],
-               vs_threads => [
-                       "DEF:total_avg={file}:total:AVERAGE",
-                       "DEF:total_min={file}:total:MIN",
-                       "DEF:total_max={file}:total:MAX",
-                       "DEF:running_avg={file}:running:AVERAGE",
-                       "DEF:running_min={file}:running:MIN",
-                       "DEF:running_max={file}:running:MAX",
-                       "DEF:uninterruptible_avg={file}:uninterruptible:AVERAGE",
-                       "DEF:uninterruptible_min={file}:uninterruptible:MIN",
-                       "DEF:uninterruptible_max={file}:uninterruptible:MAX",
-                       "DEF:onhold_avg={file}:onhold:AVERAGE",
-                       "DEF:onhold_min={file}:onhold:MIN",
-                       "DEF:onhold_max={file}:onhold:MAX",
-                       "LINE1:total_avg#$FullYellow:Total   ",
-                       'GPRINT:total_min:MIN:%5.1lf Min,',
-                       'GPRINT:total_avg:AVERAGE:%5.1lf Avg.,',
-                       'GPRINT:total_max:MAX:%5.1lf Max,',
-                       'GPRINT:total_avg:LAST:%5.1lf Last\l',
-                       "LINE1:running_avg#$FullRed:Running ",
-                       'GPRINT:running_min:MIN:%5.1lf Min,',
-                       'GPRINT:running_avg:AVERAGE:%5.1lf Avg.,',          
-                       'GPRINT:running_max:MAX:%5.1lf Max,',
-                       'GPRINT:running_avg:LAST:%5.1lf Last\l',
-                       "LINE1:uninterruptible_avg#$FullGreen:Unintr  ",
-                       'GPRINT:uninterruptible_min:MIN:%5.1lf Min,',
-                       'GPRINT:uninterruptible_avg:AVERAGE:%5.1lf Avg.,',
-                       'GPRINT:uninterruptible_max:MAX:%5.1lf Max,',
-                       'GPRINT:uninterruptible_avg:LAST:%5.1lf Last\l',
-                       "LINE1:onhold_avg#$FullBlue:Onhold  ",
-                       'GPRINT:onhold_min:MIN:%5.1lf Min,',
-                       'GPRINT:onhold_avg:AVERAGE:%5.1lf Avg.,',
-                       'GPRINT:onhold_max:MAX:%5.1lf Max,',
-                       'GPRINT:onhold_avg:LAST:%5.1lf Last\l'
-               ],
-               vs_memory => [
-                       'DEF:vm_avg={file}:vm:AVERAGE',
-                       'DEF:vm_min={file}:vm:MIN',
-                       'DEF:vm_max={file}:vm:MAX',
-                       'DEF:vml_avg={file}:vml:AVERAGE',
-                       'DEF:vml_min={file}:vml:MIN',
-                       'DEF:vml_max={file}:vml:MAX',
-                       'DEF:rss_avg={file}:rss:AVERAGE',
-                       'DEF:rss_min={file}:rss:MIN',
-                       'DEF:rss_max={file}:rss:MAX',
-                       'DEF:anon_avg={file}:anon:AVERAGE',
-                       'DEF:anon_min={file}:anon:MIN',
-                       'DEF:anon_max={file}:anon:MAX',
-                       "LINE1:vm_avg#$FullYellow:VM     ",
-                       'GPRINT:vm_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:vm_avg:AVERAGE:%5.1lf%s Avg.,',
-                       'GPRINT:vm_max:MAX:%5.1lf%s Avg.,',
-                       'GPRINT:vm_avg:LAST:%5.1lf%s Last\l',
-                       "LINE1:vml_avg#$FullRed:Locked ",
-                       'GPRINT:vml_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:vml_avg:AVERAGE:%5.1lf%s Avg.,',
-                       'GPRINT:vml_max:MAX:%5.1lf%s Avg.,',
-                       'GPRINT:vml_avg:LAST:%5.1lf%s Last\l',
-                       "LINE1:rss_avg#$FullGreen:RSS    ",
-                       'GPRINT:rss_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:rss_avg:AVERAGE:%5.1lf%s Avg.,',
-                       'GPRINT:rss_max:MAX:%5.1lf%s Avg.,',
-                       'GPRINT:rss_avg:LAST:%5.1lf%s Last\l',
-                       "LINE1:anon_avg#$FullBlue:Anon.  ",
-                       'GPRINT:anon_min:MIN:%5.1lf%s Min,',
-                       'GPRINT:anon_avg:AVERAGE:%5.1lf%s Avg.,',
-                       'GPRINT:anon_max:MAX:%5.1lf%s Avg.,',
-                       'GPRINT:anon_avg:LAST:%5.1lf%s Last\l',
-               ],
-               vs_processes => [
-                       'DEF:proc_avg={file}:total:AVERAGE',
-                       'DEF:proc_min={file}:total:MIN',
-                       'DEF:proc_max={file}:total:MAX',
-                       "AREA:proc_max#$HalfBlue",
-                       "AREA:proc_min#$Canvas",
-                       "LINE1:proc_avg#$FullBlue:Processes",
-                       'GPRINT:proc_min:MIN:%4.1lf Min,',
-                       'GPRINT:proc_avg:AVERAGE:%4.1lf Avg.,',
-                       'GPRINT:proc_max:MAX:%4.1lf Max,',
-                       'GPRINT:proc_avg:LAST:%4.1lf Last\l'
-               ],
-       };
-       $GraphDefs->{'disk'} = $GraphDefs->{'partition'};
-       $GraphDefs->{'if_errors'} = $GraphDefs->{'if_packets'};
-       $GraphDefs->{'meminfo'} = $GraphDefs->{'memory'};
-       $GraphDefs->{'sensors'} = $GraphDefs->{'temperature'};
-
-       $GraphDefs->{'delay'}           = $GraphDefs->{'time_offset'};
-       $GraphDefs->{'time_dispersion'} = $GraphDefs->{'time_offset'};
-}
-
-our $GraphArgs =
-{
-       apache_bytes => ['-t', 'apache traffic', '-v', 'Bit/s'],
-       apache_requests => ['-t', 'apache requests', '-v', 'Requests/s'],
-       apache_scoreboard => ['-t', 'apache scoreboard {inst}', '-v', 'Processes'],
-       charge => ['-t', '{host} charge', '-v', 'Ampere hours'],
-       charge_percent => ['-t', '{host} charge', '-v', 'Percent'],
-       cpu => ['-t', '{host} cpu{inst} usage', '-v', 'Percent', '-l', '0'],
-       cpufreq => ['-t', '{host} cpu{inst} usage', '-v', 'Mhz'],
-       current => ['-t', '{host} current', '-v', 'Ampere'],
-       #disk => ['-t', '{host} disk {inst} IO wait', '-v', 'Seconds'],
-       delay => ['-t', 'NTPd peer delay ({inst})', '-v', 'Seconds'],
-       df => ['-t', '{host}:{inst} usage', '-v', 'Percent', '-l', '0'],
-       disk => ['-t', '{host} disk {inst} usage', '-v', 'Byte/s'],
-       dns_traffic => ['-t', '{host} DNS traffic', '-v', 'Bit/s'],
-       email => ['-t', '{host} E-Mail {inst} count', '-v', 'E-Mails'],
-       email_size => ['-t', '{host} E-Mail {inst} size', '-v', 'Bytes'],
-       spam_score => ['-t', '{host} spam score', '-v', 'score'],
-       spam_check => ['-t', '{host} spam checks {inst}', '-v', 'hits'],
-       fanspeed => ['-t', '{host} fanspeed {inst}', '-v', 'RPM'],
-       frequency_offset => ['-t', 'NTPd frequency offset ({inst})', '-v', 'Parts per million'],
-       hddtemp => ['-t', '{host} hdd temperature {inst}', '-v', '°Celsius'],
-       if_errors => ['-t', '{host} {inst} errors', '-v', 'Errors/s'],
-       if_packets => ['-t', '{host} {inst} packets', '-v', 'Packets/s'],
-       load => ['-t', '{host} load average', '-v', 'System load', '-X', '0'],
-       load_percent => ['-t', '{host} load', '-v', 'Percent'],
-       mails   => ['-t', '{host} mail count', '-v', 'Amount', '-X', '0'],
-       memory => ['-t', '{host} memory usage', '-v', 'Bytes', '-b', '1024', '-l', '0'],
-       mysql_commands => ['-t', 'mysql command {inst}', '-v', 'Issues/s' ],
-       mysql_handler => ['-t', 'mysql handler {inst}', '-v', 'Issues/s' ],
-       mysql_qcache => ['-t', 'mysql query cache', '-v', 'Queries/s' ],
-       mysql_threads => ['-t', 'mysql threads', '-v', 'Threads' ],
-       nfs3_procedures => ['-t', '{host} NFSv3 {inst} procedures', '-v', 'Procedures/s' ],
-       opcode => ['-t', 'OpCode {inst}', '-v', 'Queries/s'],
-       partition => ['-t', '{host} partition {inst} usage', '-v', 'Byte/s'],
-       ping => ['-t', '{host} ping to {inst}', '-v', 'ms'],
-       processes => ['-t', '{host} processes', '-v', 'Processes'],
-       ps_rss => ['-t', '{host} process {inst} RSS', '-v', 'Bytes', '-b', '1024'],
-       ps_cputime => ['-t', '{host} process {inst} CPU usage', '-v', 'Seconds'],
-       ps_count => ['-t', '{host} process {inst} count', '-v', 'Threads/Processes'],
-       ps_pagefaults => ['-t', '{host} process {inst} pagefaults', '-v', 'Pagefaults/s'],
-       qtype => ['-t', 'QType {inst}', '-v', 'Queries/s'],
-       rcode => ['-t', 'RCode {inst}', '-v', 'Queries/s'],
-       sensors => ['-t', '{host} sensor {inst}', '-v', '°Celsius'],
-       swap => ['-t', '{host} swap usage', '-v', 'Bytes', '-b', '1024', '-l', '0'],
-       temperature => ['-t', '{host} temperature {inst}', '-v', '°Celsius'],
-       timeleft => ['-t', '{host} UPS time left', '-v', 'Time [min]'],
-       time_offset => ['-t', 'NTPd time offset ({inst})', '-v', 'Seconds'],
-       time_dispersion => ['-t', 'NTPd time dispersion ({inst})', '-v', 'Seconds'],
-       traffic => ['-t', '{host} {inst} traffic', '-v', 'Bit/s'],
-       users => ['-t', '{host} users', '-v', 'Users'],
-       multimeter => ['-t', '{host} multimeter', '-v', 'Value'],
-       voltage => ['-t', '{host} voltage {inst}', '-v', 'Volts'],
-       vs_threads => ['-t', '{host} threads', '-v', 'Threads'],
-       vs_memory => ['-t', '{host} memory usage', '-v', 'Bytes'],
-       vs_processes => ['-t', '{host} processes', '-v', 'Processes'],
-};
-
-our $GraphMulti =
-{
-       apache_scoreboard => \&output_graph_apache_scoreboard,
-       cpu     => \&output_graph_cpu,
-       cpufreq => 1,
-       disk    => 1,
-       email   => \&output_graph_email_count,
-       email_size => \&output_graph_email_size,
-       spam_score => 1,
-       spam_check => \&output_graph_spam_check,
-       load    => 0,
-       mails   => 0,
-       memory  => 0,
-       mysql_commands => \&output_graph_mysql_commands,
-       mysql_handler => \&output_graph_mysql_handler,
-       partition => 1,
-       ping    => \&output_graph_ping,
-       qtype => \&output_graph_named_qtype,
-       rcode => \&output_graph_named_rcode,
-       sensors => 1,
-       traffic => 1,
-       users => 1,
-       multimeter => 1
-};
-
-our @Info;
-if (defined ($ENV{'GATEWAY_INTERFACE'}))
-{
-       @Info = ($ENV{'PATH_INFO'} || '') =~ m#([\w\-\.]+)#g;
-}
-else
-{
-       @Info = @ARGV;
-}
+load_graph_definitions ();
 
-parse_pathinfo (@Info);
-
-if ($TimeSpan)
-{
-       output_graph ();
-}
-else
+for (qw(action host plugin plugin_instance type type_instance timespan))
 {
-       output_page ();
+       $Args->{$_} = param ($_);
 }
 
-exit (0);
+exit (main ());
 
-sub output_graph_cpu
+sub read_config
 {
-       my @inst = @_;
-       my @ret = ();
-
-       die if (@inst < 2);
-
-       for (@inst)
-       {
-               push (@ret,
-                       "DEF:user_avg_$_=$AbsDir/cpu-$_.rrd:user:AVERAGE",
-                       "DEF:user_min_$_=$AbsDir/cpu-$_.rrd:user:MIN",
-                       "DEF:user_max_$_=$AbsDir/cpu-$_.rrd:user:MAX",
-                       "DEF:nice_avg_$_=$AbsDir/cpu-$_.rrd:nice:AVERAGE",
-                       "DEF:nice_min_$_=$AbsDir/cpu-$_.rrd:nice:MIN",
-                       "DEF:nice_max_$_=$AbsDir/cpu-$_.rrd:nice:MAX",
-                       "DEF:syst_avg_$_=$AbsDir/cpu-$_.rrd:syst:AVERAGE",
-                       "DEF:syst_min_$_=$AbsDir/cpu-$_.rrd:syst:MIN",
-                       "DEF:syst_max_$_=$AbsDir/cpu-$_.rrd:syst:MAX",
-                       "DEF:wait_avg_$_=$AbsDir/cpu-$_.rrd:wait:AVERAGE",
-                       "DEF:wait_min_$_=$AbsDir/cpu-$_.rrd:wait:MIN",
-                       "DEF:wait_max_$_=$AbsDir/cpu-$_.rrd:wait:MAX");
-       }
-
-       for (qw(user nice syst wait))
+       my $fh;
+       open ($fh, "< $Config") or confess ("open ($Config): $!");
+       while (my $line = <$fh>)
        {
-               my $def = $_;
-               my $cdef;
+               chomp ($line);
+               next if (!$line);
+               next if ($line =~ m/^\s*#/);
+               next if ($line =~ m/^\s*$/);
 
-               my $default_value = ($def eq 'user' or $def eq 'syst') ? 'UNKN' : '0';
+               my $key;
+               my $value;
 
-               for (qw(avg min max))
+               if ($line =~ m/^([A-Za-z]+):\s*"((?:[^"\\]+|\\.)*)"$/)
                {
-                       my $cf = $_;
-
-                       for (@inst)
-                       {
-                               push (@ret, "CDEF:${def}_${cf}_notnull_${_}=${def}_${cf}_${_},UN,0,${def}_${cf}_${_},IF");
-                               push (@ret, "CDEF:${def}_${cf}_defined_${_}=${def}_${cf}_${_},UN,0,1,IF");
-                       }
-
-                       $cdef = "CDEF:${def}_${cf}_num=" . join (',', map { "${def}_${cf}_defined_${_}" } (@inst));
-                       $cdef .= ',+' x (scalar (@inst) - 1);
-                       push (@ret, $cdef);
-
-                       $cdef = "CDEF:${def}_${cf}=${def}_${cf}_num," . join (',', map { "${def}_${cf}_notnull_${_}" } (@inst));
-                       $cdef .= ',+' x (scalar (@inst) - 1);
-                       $cdef .= ",${def}_${cf}_num,${def}_${cf}_num,1,IF,/,$default_value,IF";
-                       push (@ret, $cdef);
-                       push (@ret, "CDEF:${def}_${cf}_notnull=${def}_${cf},UN,0,${def}_${cf},IF");
+                       $key = lc ($1); $value = $2;
+                       $value =~ s/\\(.)/$1/g;
                }
-       }
-
-       push (@ret,
-               "CDEF:nice_acc=syst_avg_notnull,wait_avg_notnull,user_avg_notnull,nice_avg_notnull,+,+,+",
-               "CDEF:user_acc=syst_avg_notnull,wait_avg_notnull,user_avg_notnull,+,+",
-               "CDEF:wait_acc=syst_avg_notnull,wait_avg_notnull,+",
-               "CDEF:syst_acc=syst_avg_notnull");
-
-       push (@ret, grep { $_ !~ m/^C?DEF/ } (@{$GraphDefs->{'cpu'}}));
-
-       return (@ret);
-}
-
-sub output_graph_apache_scoreboard
-{
-       my @inst = @_;
-       my @ret = ();
-
-       die if (@inst < 2);
-
-       my @colors = get_n_colors (scalar (@inst));
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
-       {
-               my $inst = $inst[$i];
-               push (@ret,
-                       "DEF:avg_$i=$AbsDir/apache_scoreboard-$inst.rrd:count:AVERAGE",
-                       "DEF:min_$i=$AbsDir/apache_scoreboard-$inst.rrd:count:MIN",
-                       "DEF:max_$i=$AbsDir/apache_scoreboard-$inst.rrd:count:MAX");
-       }
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
-       {
-               my $inst = $inst[$i];
-               my $color = $colors[$i];
-
-               if (length ($inst) > 15)
+               elsif ($line =~ m/([A-Za-z]+):\s*([0-9]+)$/)
                {
-                       $inst = substr ($inst, 0, 12) . '...';
+                       $key = lc ($1); $value = 0 + $2;
                }
                else
                {
-                       $inst = sprintf ('%-15s', $inst);
+                       print STDERR "Cannot parse line: $line\n";
+                       next;
                }
 
-               push (@ret,
-                       "LINE1:avg_$i#$color:$inst",
-                       "GPRINT:min_$i:MIN:%6.2lf Min,",
-                       "GPRINT:avg_$i:AVERAGE:%6.2lf Avg,",
-                       "GPRINT:max_$i:MAX:%6.2lf Max,",
-                       "GPRINT:avg_$i:LAST:%6.2lf Last\\l");
-       }
-
-       return (@ret);
-}
-
-sub output_graph_ping
-{
-       my @inst = @_;
-       my @ret = ();
-
-       die if (@inst < 2);
-
-       my @colors = get_n_colors (scalar (@inst));
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
-       {
-               my $inst = $inst[$i];
-               push (@ret,
-                       "DEF:avg_$i=$AbsDir/ping-$inst.rrd:ping:AVERAGE",
-                       "DEF:min_$i=$AbsDir/ping-$inst.rrd:ping:MIN",
-                       "DEF:max_$i=$AbsDir/ping-$inst.rrd:ping:MAX");
-       }
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
-       {
-               my $inst = $inst[$i];
-               my $color = $colors[$i];
-
-               if (length ($inst) > 15)
+               if ($key eq 'datadir')
                {
-                       $inst = substr ($inst, 0, 12) . '...';
+                       $value =~ s#/*$##;
+                       push (@DataDirs, $value);
                }
-               else
+               elsif ($key eq 'libdir')
                {
-                       $inst = sprintf ('%-15s', $inst);
-               }
-
-               push (@ret,
-                       "LINE1:avg_$i#$color:$inst",
-                       "GPRINT:min_$i:MIN:%4.1lf ms Min,",
-                       "GPRINT:avg_$i:AVERAGE:%4.1lf ms Avg,",
-                       "GPRINT:max_$i:MAX:%4.1lf ms Max,",
-                       "GPRINT:avg_$i:LAST:%4.1lf ms Last\\l");
-       }
-
-       return (@ret);
-}
-
-sub output_graph_email_count
-{
-       my @inst = @_;
-       my @ret  = ();
-
-       my $c = scalar @inst;
-
-       my @colors = get_n_colors ($c);
-
-       for (my $i = 0; $i < $c; ++$i) {
-               push @ret,
-                       "DEF:min_$i=$AbsDir/email-$inst[$i].rrd:count:MIN",
-                       "DEF:avg_$i=$AbsDir/email-$inst[$i].rrd:count:AVERAGE",
-                       "DEF:max_$i=$AbsDir/email-$inst[$i].rrd:count:MAX";
-       }
-
-       for (my $i = 0; $i < $c; ++$i) {
-               my $s = "";
-
-               if (length $inst[$i] > 10) {
-                       $s = substr ($inst[$i], 0, 7) . '...';
-               }
-               else {
-                       $s = sprintf '%-10s', $inst[$i];
-               }
-
-               push @ret,
-                       "LINE1:avg_$i#$colors[$i]:$s",
-                       "GPRINT:min_$i:MIN:%4.1lf Min,",
-                       "GPRINT:avg_$i:AVERAGE:%4.1lf Avg,",
-                       "GPRINT:max_$i:MAX:%4.1lf Max,",
-                       "GPRINT:avg_$i:LAST:%4.1lf Last\\l";
-       }
-       return @ret;
-}
-
-sub output_graph_email_size
-{
-       my @inst = @_;
-       my @ret  = ();
-
-       my $c = scalar @inst;
-
-       my @colors = get_n_colors ($c);
-
-       for (my $i = 0; $i < $c; ++$i) {
-               push @ret,
-                       "DEF:min_$i=$AbsDir/email_size-$inst[$i].rrd:size:MIN",
-                       "DEF:avg_$i=$AbsDir/email_size-$inst[$i].rrd:size:AVERAGE",
-                       "DEF:max_$i=$AbsDir/email_size-$inst[$i].rrd:size:MAX";
-       }
-
-       for (my $i = 0; $i < $c; ++$i) {
-               my $s = "";
-
-               if (length $inst[$i] > 10) {
-                       $s = substr ($inst[$i], 0, 7) . '...';
-               }
-               else {
-                       $s = sprintf '%-10s', $inst[$i];
-               }
-
-               push @ret,
-                       "LINE1:avg_$i#$colors[$i]:$s",
-                       "GPRINT:min_$i:MIN:%4.1lf Min,",
-                       "GPRINT:avg_$i:AVERAGE:%4.1lf Avg,",
-                       "GPRINT:max_$i:MAX:%4.1lf Max,",
-                       "GPRINT:avg_$i:LAST:%4.1lf Last\\l";
-       }
-       return @ret;
-}
-
-sub output_graph_spam_check
-{
-       my @inst = sort @_;
-       my @ret  = ();
-
-       my $c = scalar @inst;
-
-       my @colors = get_n_colors ($c);
-
-       for (my $i = 0; $i < $c; ++$i) {
-               push @ret,
-                       "DEF:min_$i=$AbsDir/spam_check-$inst[$i].rrd:hits:MIN",
-                       "DEF:avg_$i=$AbsDir/spam_check-$inst[$i].rrd:hits:AVERAGE",
-                       "DEF:max_$i=$AbsDir/spam_check-$inst[$i].rrd:hits:MAX";
-       }
-
-       for (my $i = 0; $i < $c; ++$i) {
-               my $s = "";
-
-               if (length $inst[$i] > 25) {
-                       $s = substr ($inst[$i], 0, 22) . '...';
-               }
-               else {
-                       $s = sprintf '%-25s', $inst[$i];
-               }
-
-               push @ret,
-                       "LINE1:avg_$i#$colors[$i]:$s",
-                       "GPRINT:min_$i:MIN:%4.1lf Min,",
-                       "GPRINT:avg_$i:AVERAGE:%4.1lf Avg,",
-                       "GPRINT:max_$i:MAX:%4.1lf Max,",
-                       "GPRINT:avg_$i:LAST:%4.1lf Last\\l";
-       }
-       return @ret;
-}
-
-sub output_graph_mysql_commands
-{
-       my @inst = @_;
-       my @ret = ();
-
-       die if (@inst < 2);
-
-       my @colors = get_n_colors (scalar (@inst));
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
-       {
-               my $inst = $inst[$i];
-               push (@ret,
-                       "DEF:avg_$i=$AbsDir/mysql_commands-$inst.rrd:value:AVERAGE",
-                       "DEF:min_$i=$AbsDir/mysql_commands-$inst.rrd:value:MIN",
-                       "DEF:max_$i=$AbsDir/mysql_commands-$inst.rrd:value:MAX");
-       }
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
-       {
-               my $inst = $inst[$i];
-               my $color = $colors[$i];
-
-               if (length ($inst) > 18)
-               {
-                       $inst = substr ($inst, 0, 15) . '...';
-               }
-               else
-               {
-                       $inst = sprintf ('%-18s', $inst);
-               }
-
-               push (@ret,
-                       "LINE1:avg_$i#$color:$inst",
-                       "GPRINT:min_$i:MIN:%6.1lf Min,",
-                       "GPRINT:avg_$i:AVERAGE:%6.1lf Avg,",
-                       "GPRINT:max_$i:MAX:%6.1lf Max,",
-                       "GPRINT:avg_$i:LAST:%6.1lf Last\\l");
-       }
-
-       return (@ret);
-}
-
-sub output_graph_mysql_handler
-{
-       my @inst = @_;
-       my @ret = ();
-
-       die if (@inst < 2);
-
-       my @colors = get_n_colors (scalar (@inst));
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
-       {
-               my $inst = $inst[$i];
-               push (@ret,
-                       "DEF:avg_$i=$AbsDir/mysql_handler-$inst.rrd:value:AVERAGE",
-                       "DEF:min_$i=$AbsDir/mysql_handler-$inst.rrd:value:MIN",
-                       "DEF:max_$i=$AbsDir/mysql_handler-$inst.rrd:value:MAX");
-       }
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
-       {
-               my $inst = $inst[$i];
-               my $color = $colors[$i];
-
-               if (length ($inst) > 18)
-               {
-                       $inst = substr ($inst, 0, 15) . '...';
+                       $value =~ s#/*$##;
+                       $LibDir = $value;
                }
                else
                {
-                       $inst = sprintf ('%-18s', $inst);
+                       print STDERR "Unknown key: $key\n";
                }
-
-               push (@ret,
-                       "LINE1:avg_$i#$color:$inst",
-                       "GPRINT:min_$i:MIN:%6.1lf Min,",
-                       "GPRINT:avg_$i:AVERAGE:%6.1lf Avg,",
-                       "GPRINT:max_$i:MAX:%6.1lf Max,",
-                       "GPRINT:avg_$i:LAST:%6.1lf Last\\l");
        }
+       close ($fh);
+} # read_config
+
+#sub validate_args
+#{
+#      if (!$Args->{'host'} || ($Args->{'host'} =~ m#/#)
+#              || !$Args->{'plugin'} || ($Args->{'plugin'} =~ m#/#)
+#              || (defined ($Args->{'plugin_instance'}) && ($Args->{'plugin_instance'} =~ m#/#))
+#              || !$Args->{'type'} || ($Args->{'type'} =~ m#/#)
+#              || (defined ($Args->{'type_instance'}) && ($Args->{'type_instance'} =~ m#/#)))
+#      {
+#              delete ($Args->{'host'});
+#              delete ($Args->{'plugin'});
+#              delete ($Args->{'plugin_instance'});
+#              delete ($Args->{'type'});
+#              delete ($Args->{'type_instance'});
+#      }
+#      elsif ($Args->{'type_instance'} eq '*')
+#      {
+#              my $subdir = $Args->{'host'} . '/'
+#              . $Args->{'plugin'} . (defined ($Args->{'plugin_instance'}) ? ('-' . $Args->{'plugin_instance'}) : '');
+#              my %type_instances = ();
+#              my $regex_str = '^' . quotemeta ($Args->{'type'}) . '-(.*)\.rrd';
+#              my $regex = qr/$regex_str/;
+#              my $mtime = 0;
+#
+#              for (my $i = 0; $i < @DataDirs; $i++)
+#              {
+#                      my $dh;
+#                      my @files = ();
+#                      opendir ($dh, $DataDirs[$i] . '/' . $subdir) or next;
+#                      for (readdir ($dh))
+#                      {
+#                              my $file = $_;
+#                              if ($file =~ $regex)
+#                              {
+#                                      my $tmp_mtime;
+#                                      my $type_instance = $1;
+#                                      my $filename = $DataDirs[$i] . '/' . $subdir . '/' . $Args->{'type'} . '-' . $type_instance . '.rrd';
+#
+#                                      $type_instances{$type_instance} = [] if (!exists ($type_instances{$type_instance}));
+#                                      $tmp_mtime = (stat ($filename))[9];
+#                                      next if (!$tmp_mtime);
+#
+#                                      push (@{$type_instances{$type_instance}}, $filename);
+#                                      $mtime = $tmp_mtime if ($mtime < $tmp_mtime);
+#                              }
+#                      }
+#                      closedir ($dh);
+#              }
+#
+#              if (!keys %type_instances)
+#              {
+#                      print STDOUT header (-Status => '404 File not found', -Content_Type => 'text/plain');
+#                      print STDOUT <<MSG;
+#Sorry, the requested file could not be found anywhere.
+#DataDirs = ${\join ('; ', @DataDirs)}
+#subdir   = $subdir
+#MSG
+#                      exit (0);
+#              }
+#
+#              $Args->{'files'} = \%type_instances;
+#              $Args->{'mtime'} = $mtime;
+#      }
+#      else
+#      {
+#              my $filename = $Args->{'host'} . '/'
+#              . $Args->{'plugin'} . (defined ($Args->{'plugin_instance'}) ? ('-' . $Args->{'plugin_instance'}) : '') . '/'
+#              . $Args->{'type'} . (defined ($Args->{'type_instance'}) ? ('-' . $Args->{'type_instance'}) : '') . '.rrd';
+#              my @files = ();
+#              my $mtime = 0;
+#
+#              for (my $i = 0; $i < @DataDirs; $i++)
+#              {
+#                      my $tmp_file;
+#                      my $tmp_mtime;
+#                      $tmp_file = $DataDirs[$i] . '/' . $filename;
+#                      next if (!-e $tmp_file);
+#                      $tmp_mtime = (stat ($tmp_file))[9];
+#                      next if (!$tmp_mtime);
+#
+#                      push (@files, $tmp_file);
+#                      $mtime = $tmp_mtime if ($mtime < $tmp_mtime);
+#              }
+#
+#              if (!@files)
+#              {
+#                      print STDOUT header (-Status => '404 File not found', -Content_Type => 'text/plain');
+#                      print STDOUT <<MSG;
+#Sorry, the requested file could not be found anywhere.
+#DataDirs = ${\join ('; ', @DataDirs)}
+#filename = $filename
+#MSG
+#                      exit (0);
+#              }
+#              $Args->{'files'} = \@files;
+#              $Args->{'mtime'} = $mtime;
+#      }
+#
+#      if ($Args->{'timespan'})
+#      {
+#              if ($Args->{'timespan'} =~ m/^([0-9]+)$/)
+#              {
+#                      $Args->{'timespan'} = (-1) * $1;
+#              }
+#              elsif ($Args->{'timespan'} =~ m/^(hour|day|week|month|year)$/)
+#              {
+#                      my %map =
+#                      (
+#                              hour => -3600,
+#                              day => -86400,
+#                              week => 7 * -86400,
+#                              month => 31 * -86400,
+#                              year => 366 * -86400
+#                      );
+#                      $Args->{'timespan'} = $map{$1};
+#              }
+#              else
+#              {
+#                      $Args->{'timespan'} = -86400;
+#              }
+#      }
+#} # validate_args
 
-       return (@ret);
-}
-
-sub output_graph_named_qtype
+sub validate_args
 {
-       my @inst = @_;
-       my @ret = ();
-
-       die if (@inst < 2);
-
-       my @colors = get_n_colors (scalar (@inst));
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
+       if ($Args->{'action'} && ($Args->{'action'} =~ m/^(overview|show_host|show_plugin|show_type|show_graph)$/))
        {
-               my $inst = $inst[$i];
-               push (@ret,
-                       "DEF:avg_$i=$AbsDir/qtype-$inst.rrd:value:AVERAGE",
-                       "DEF:min_$i=$AbsDir/qtype-$inst.rrd:value:MIN",
-                       "DEF:max_$i=$AbsDir/qtype-$inst.rrd:value:MAX");
+               $Args->{'action'} = $1;
        }
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
-       {
-               my $inst = $inst[$i];
-               my $color = $colors[$i];
-               my $type = ($i == 0) ? 'AREA' : 'STACK';
-
-               if (length ($inst) > 5)
-               {
-                       $inst = substr ($inst, 0, 5);
-               }
-               else
-               {
-                       $inst = sprintf ('%-5s', $inst);
-               }
-
-               push (@ret,
-                       "$type:avg_$i#$color:$inst",
-                       "GPRINT:min_$i:MIN:%9.3lf Min,",
-                       "GPRINT:avg_$i:AVERAGE:%9.3lf Avg,",
-                       "GPRINT:max_$i:MAX:%9.3lf Max,",
-                       "GPRINT:avg_$i:LAST:%9.3lf Last\\l");
-       }
-
-       return (@ret);
-}
-
-sub output_graph_named_rcode
-{
-       my @inst = @_;
-       my @ret = ();
-
-       die if (@inst < 2);
-
-       my @colors = get_n_colors (scalar (@inst));
-
-       for (my $i = 0; $i < scalar (@inst); $i++)
+       else
        {
-               my $inst = $inst[$i];
-               push (@ret,
-                       "DEF:avg_$i=$AbsDir/rcode-$inst.rrd:value:AVERAGE",
-                       "DEF:min_$i=$AbsDir/rcode-$inst.rrd:value:MIN",
-                       "DEF:max_$i=$AbsDir/rcode-$inst.rrd:value:MAX");
+               $Args->{'action'} = 'overview';
        }
 
-       for (my $i = 0; $i < scalar (@inst); $i++)
+       if ($Args->{'host'} && ($Args->{'host'} =~ m#/#))
        {
-               my $inst = $inst[$i];
-               my $color = $colors[$i];
-               my $type = ($i == 0) ? 'AREA' : 'STACK';
-
-               if (length ($inst) > 8)
-               {
-                       $inst = substr ($inst, 0, 6) . '..';
-               }
-               else
-               {
-                       $inst = sprintf ('%-8s', $inst);
-               }
-
-               push (@ret,
-                       "$type:avg_$i#$color:$inst",
-                       "GPRINT:min_$i:MIN:%9.3lf Min,",
-                       "GPRINT:avg_$i:AVERAGE:%9.3lf Avg,",
-                       "GPRINT:max_$i:MAX:%9.3lf Max,",
-                       "GPRINT:avg_$i:LAST:%9.3lf Last\\l");
+               delete ($Args->{'host'});
        }
 
-       return (@ret);
-}
-sub output_graph
-{
-       die unless (defined ($GraphDefs->{$Type}));
-
-       my $host;
-       my @cmd = ();
-       my $file = $AbsDir . '/';
-       my $files = get_all_files ($AbsDir);
-
-       #
-       # get hostname
-       #
-       if ($RelDir =~ m#([^/]+)$#)
+       if ($Args->{'plugin'} && ($Args->{'plugin'} =~ m#/#))
        {
-               $host = $1;
-       }
-       else
-       {
-               $host = $Config->{'HostName'};
+               delete ($Args->{'plugin'});
        }
 
-       #
-       # get timespan
-       #
-       if ($TimeSpan =~ m/(\d+)/)
+       if ($Args->{'type'} && ($Args->{'type'} =~ m#/#))
        {
-               $TimeSpan = -1 * int ($1);
-       }
-       else
-       {
-               my %t = (hour => -3600, day => -86400, week => -604800, month => -2678400, year => -31622400);
-               die unless (defined ($t{$TimeSpan}));
-               $TimeSpan = $t{$TimeSpan};
+               delete ($Args->{'type'});
        }
 
-       if (scalar (@{$files->{$Type}}) == 1)
+       if (!$Args->{'plugin'} || ($Args->{'plugin_instance'}
+               && ($Args->{'plugin_instance'} =~ m#/#)))
        {
-               $Inst = $files->{$Type}[0];
+               delete ($Args->{'plugin_instance'});
        }
 
-       #push (@cmd, '-', '-a', 'PNG', '-s', $TimeSpan, '-w', 800, '-h', 150);
-       push (@cmd, '-', '-a', 'PNG', '-s', $TimeSpan);
-       push (@cmd, @{$GraphArgs->{$Type}}) if (defined ($GraphArgs->{$Type}));
-
-       for (qw(Back ShadeA ShadeB Font Canvas Grid MGrid Frame Arrow))
+       if (!$Args->{'type'} || ($Args->{'type_instance'}
+               && ($Args->{'type_instance'} =~ m#/#)))
        {
-               push (@cmd, '-c', uc ($_) . '#' . $Config->{'Colors'}{$_});
+               delete ($Args->{'type_instance'});
        }
 
-       if ((length ($Inst) == 0) and (ref ($GraphMulti->{$Type}) eq 'CODE'))
+       if (defined ($Args->{'timespan'})
+         && ($Args->{'timespan'} =~ m/^(hour|day|week|month|year)$/))
        {
-               push (@cmd, $GraphMulti->{$Type}->(@{$files->{$Type}}));
+         $Args->{'timespan'} = $1;
        }
        else
        {
-               if (length ("$Inst"))
-               {
-                       $file .= "$Type-$Inst.rrd";
-               }
-               else
-               {
-                       $file .= "$Type.rrd";
-               }
-
-               die ("File not found: $file") unless (-e $file);
-
-               push (@cmd, @{$GraphDefs->{$Type}});
-       }
-
-       for (@cmd)
-       {
-               $_ =~ s/{file}/$file/g;
-               $_ =~ s/{host}/$host/g;
-               $_ =~ s/{inst}/$Inst/g;
-               $_ =~ s/{type}/$Type/g;
-       }
-
-       $| = 1;
-
-       print STDOUT <<HEADER if (defined ($ENV{'GATEWAY_INTERFACE'}));
-Content-Type: image/png
-Cache-Control: no-cache
-
-HEADER
-
-       if (1)
-       {
-               my $fh;
-               open ($fh, ">/tmp/collection.log") or die ("open: $!");
-               flock ($fh, LOCK_EX) or die ("flock: $!");
-
-               print $fh join ("\n\t", @cmd) . "\n";
-
-               close ($fh);
+         $Args->{'timespan'} = 'day';
        }
+} # validate_args
 
-       RRDs::graph (@cmd);
-
-       die ('RRDs::error: ' . RRDs::error ()) if (RRDs::error ());
-}
-
-sub output_page
+sub _find_hosts
 {
-       my $files = get_all_files ($AbsDir);
-       my $dirs  = get_all_dirs  ($AbsDir);
-
-       print STDOUT <<HEADER if (defined ($ENV{'GATEWAY_INTERFACE'}));
-Content-Type: text/html
-Cache-Control: no-cache
-
-<html>
-       <head>
-               <title>Collection: $RelDir</title>
-               <style type="text/css">
-                       img { border: none; display: block; }
-               </style>
-       </head>
-
-       <body>
-HEADER
-
-       my $MySelf = defined ($ENV{'GATEWAY_INTERFACE'}) ? $ENV{'SCRIPT_NAME'} : $0;
-
-       if ((length ($Type) != 0) and (length ($Inst) == 0) and (ref ($GraphMulti->{$Type}) eq 'CODE') and (scalar (@{$files->{$Type}}) > 1))
-       {
-               print qq(\t\t<div><a href="$MySelf$RelDir">Go up</a></div>\n);
-
-               print "\t\t<ul>\n";
-               for (@{$files->{$Type}})
-               {
-                       print qq(\t\t\t<li><a href="$MySelf$RelDir/$Type/$_">$_</a></li>\n);
-               }
-               print <<HTML;
-               </ul>
-
-               <h3>Hourly</h3>
-               <div><img src="$MySelf$RelDir/$Type/hour" /></div>
-               <h3>Daily</h3>
-               <div><img src="$MySelf$RelDir/$Type/day" /></div>
-               <h3>Weekly</h3>
-               <div><img src="$MySelf$RelDir/$Type/week" /></div>
-               <h3>Monthly</h3>
-               <div><img src="$MySelf$RelDir/$Type/month" /></div>
-               <h3>Yearly</h3>
-               <div><img src="$MySelf$RelDir/$Type/year" /></div>
-HTML
-       }
-       elsif (length ($Type) != 0)
-       {
-               my $ext = length ($Inst) ? "$Type/$Inst" : $Type;
-
-               if ((ref ($GraphMulti->{$Type}) eq 'CODE') and (scalar (@{$files->{$Type}}) > 1))
-               {
-                       print qq(<div><a href="$MySelf$RelDir/$Type">Go up</a></div>\n);
-               }
-               else
-               {
-                       print qq(<div><a href="$MySelf$RelDir">Go up</a></div>\n);
-               }
+  my %hosts = ();
 
-               print <<HTML;
-               <h3>Hourly</h3>
-               <div><img src="$MySelf$RelDir/$ext/hour" /></div>
-               <h3>Daily</h3>
-               <div><img src="$MySelf$RelDir/$ext/day" /></div>
-               <h3>Weekly</h3>
-               <div><img src="$MySelf$RelDir/$ext/week" /></div>
-               <h3>Monthly</h3>
-               <div><img src="$MySelf$RelDir/$ext/month" /></div>
-               <h3>Yearly</h3>
-               <div><img src="$MySelf$RelDir/$ext/year" /></div>
-HTML
-       }
-       else
-       {
-               if ($RelDir)
-               {
-                       my ($up) = $RelDir =~ m#(.*)/[^/]+$#;
-                       print qq(\t\t<div><a href="$MySelf$up">Go up</a></div>\n);
-               }
+  for (my $i = 0; $i < @DataDirs; $i++)
+  {
+    my @tmp;
+    my $dh;
 
-               if (@$dirs)
-               {
-                       print "<ul>\n";
-                       for (@$dirs)
-                       {
-                               print qq(<li>$AbsDir/<a href="$MySelf$RelDir/$_">$_</a></li>\n);
-                       }
-                       print "</ul>\n";
-               }
+    opendir ($dh, $DataDirs[$i]) or next;
+    @tmp = grep { ($_ !~ m/^\./) && (-d $DataDirs[$i] . '/' . $_) } (readdir ($dh));
+    closedir ($dh);
 
-               for (sort (keys %$files))
-               {
-                       my $type = $_;
-
-                       if (ref ($GraphMulti->{$type}) eq 'CODE')
-                       {
-                               print qq(\t\t<a href="$MySelf$RelDir/$type" />),
-                               qq(<img src="$MySelf$RelDir/$type/day" /></a>\n);
-                               next;
-                       }
-
-                       for (@{$files->{$type}})
-                       {
-                               my $inst = "$_";
-
-                               if (length ($inst))
-                               {
-                                       print qq(\t\t<a href="$MySelf$RelDir/$type/$inst" />),
-                                       qq(<img src="$MySelf$RelDir/$type/$inst/day" /></a>\n);
-                               }
-                               else
-                               {
-                                       print qq(\t\t<a href="$MySelf$RelDir/$type" />),
-                                       qq(<img src="$MySelf$RelDir/$type/day" /></a>\n);
-                               }
-                       }
-               }
-       }
+    $hosts{$_} = 1 for (@tmp);
+  } # for (@DataDirs)
 
-       print STDOUT <<FOOTER if (defined ($ENV{'GATEWAY_INTERFACE'}));
-       </body>
-</html>
-FOOTER
-}
+  return (keys %hosts);
+} # _find_hosts
 
-sub output_xml
+sub _find_plugins
 {
-       my $files = get_all_files ();
-
-       print STDOUT <<HEADER if (defined ($ENV{'GATEWAY_INTERFACE'}));
-Content-Type: text/xml
-Cache-Control: no-cache
-
-HEADER
-       print STDOUT pl2xml ($files);
-}
-
-sub read_config
+  my $host = shift;
+  my %plugins = ();
+
+  for (my $i = 0; $i < @DataDirs; $i++)
+  {
+    my $dir = $DataDirs[$i] . "/$host";
+    my @tmp;
+    my $dh;
+
+    opendir ($dh, $dir) or next;
+    @tmp = grep { ($_ !~ m/^\./) && (-d "$dir/$_") } (readdir ($dh));
+    closedir ($dh);
+
+    for (@tmp)
+    {
+      my ($plugin, $instance) = split (m/-/, $_, 2);
+      $plugins{$plugin} = [] if (!$plugins{$plugin});
+      push (@{$plugins{$plugin}}, $instance) if (defined ($instance));
+    }
+  } # for (@DataDirs)
+
+  return (%plugins);
+} # _find_hosts
+
+sub _find_types
 {
-       my $file = @_ ? shift : '/etc/collection.conf';
-       my $conf;
-       my $fh;
-
-#      if (open ($fh, "< $file"))
-#      {
-#              my $xml;
-#              local $/ = undef;
-#              $xml = <$fh>;
-#
-#              eval
-#              {
-#                      $conf = xml2pl ($xml);
-#              };
-#              close ($fh);
-#      }
-
-       if (!$conf)
-       {
-               return ({
-                               Colors =>
-                               {
-                                       Back    => 'FFFFFF',
-                                       ShadeA  => 'FFFFFF',
-                                       ShadeB  => 'FFFFFF',
-                                       Font    => '000000',
-                                       Canvas  => 'F5F5F5',
-                                       Grid    => 'D0D0D0',
-                                       MGrid   => 'A0A0A0',
-                                       Frame   => '646464',
-                                       Arrow   => 'FF0000',
-
-                                       FullRed         => 'FF0000',
-                                       FullBlue        => '0000FF',
-                                       FullGreen       => '00E000',
-                                       FullYellow      => 'F0A000',
-                                       FullCyan        => '00A0FF',
-                                       FullMagenta     => 'A000FF',
-                                       Alpha           => 0.25,
-                                       HalfRed         => 'F8B8B8',
-                                       HalfBlue        => 'B8B8F8',
-                                       HalfGreen       => 'B8F0B8',
-                                       HalfYellow      => 'F4F4B8'
-                               },
-                               Directory => '/var/lib/collectd',
-                               HostName  => (defined ($ENV{'SERVER_NAME'}) ? $ENV{'SERVER_NAME'} : 'localhost')
-                       });
-       }
-       else
-       {
-               return ($conf);
-       }
-}
-
-sub parse_pathinfo
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+  my %types = ();
+
+  for (my $i = 0; $i < @DataDirs; $i++)
+  {
+    my $dir = $DataDirs[$i] . "/$host/$plugin" . (defined ($plugin_instance) ? "-$plugin_instance" : '');
+    my @tmp;
+    my $dh;
+
+    opendir ($dh, $dir) or next;
+    @tmp = grep { ($_ !~ m/^\./) && ($_ =~ m/\.rrd$/i) && (-f "$dir/$_") } (readdir ($dh));
+    closedir ($dh);
+
+    for (@tmp)
+    {
+      my $name = "$_";
+      $name =~ s/\.rrd$//i;
+      my ($type, $instance) = split (m/-/, $name, 2);
+      $types{$type} = [] if (!$types{$type});
+      push (@{$types{$type}}, $instance) if (defined ($instance));
+    }
+  } # for (@DataDirs)
+
+  return (%types);
+} # _find_types
+
+# sub _find_files_plugin
+# {
+#      my $host = shift;
+#      my $plugin = shift;
+#      my $plugin_instance = shift;
+#      my $type = shift;
+#      my $type_instance = shift;
+# 
+#      my @plugins = ();
+#      my %files = ();
+# 
+#      if (!$plugin || ($plugin eq '*'))
+#      {
+#      }
+#      else
+#      {
+#              @plugins = ($plugin);
+#      }
+# } # _find_files_plugin
+# 
+# sub _find_files_host
+# {
+#      my $host = shift;
+#      my $plugin = shift;
+#      my $plugin_instance = shift;
+#      my $type = shift;
+#      my $type_instance = shift;
+# 
+#      my @hosts = ();
+#      my %files = ();
+# 
+#      if (!$host || ($host eq '*'))
+#      {
+#              my %hosts;
+#              for (my $i = 0; $i < @DataDirs; $i++)
+#              {
+#                      my @tmp;
+#                      my $dh;
+#                      
+#                      opendir ($dh, $DataDirs[$i]) or next;
+#                      @tmp = grep { ($_ !~ m/^\./) && (-d $_) } (readdir ($dh));
+#                      closedir ($dh);
+# 
+#                      $hosts{$_} = 1 for (@tmp);
+#              } # for (@DataDirs)
+#              @hosts = keys %hosts;
+#      }
+#      else
+#      {
+#              @hosts = ($host);
+#      }
+# 
+#      for (my $i = 0; $i < @hosts; $i++)
+#      {
+#              my @files = _find_files_plugin ($hosts[$i], $plugin,
+#                      $plugin_instance, $type, $type_instance);
+#              $files{$_} = 1 for (@files);
+#      }
+# 
+#      return (wantarray () ? keys %files : [keys %files]);
+# } # _find_files_host
+# 
+# sub _find_files
+# {
+#   my $host = shift;
+#   my $plugin = shift;
+#   my $plugin_instance = shift;
+#   my $type = shift;
+#   my $type_instance = shift;
+# 
+#   if ($host eq '*')
+#   {
+#     my @hosts = _find_all_hosts ();
+# 
+#   for (my $i = 0; $i < @DataDirs; $i++)
+#   {
+# 
+#   } # for (i)
+# } # _find_files
+
+sub list_hosts
 {
-       my @info = @_;
-
-       $AbsDir = $Config->{'Directory'};
-       $RelDir = '';
-
-       while (@info and -d $AbsDir . '/' . $info[0])
-       {
-               my $new = shift (@info);
-               next if ($new =~ m/^\./);
-
-               $AbsDir .= '/' . $new;
-               $RelDir .= '/' . $new;
-       }
-
-       $Type = '';
-       $Inst = '';
-       $TimeSpan = '';
-
-       confess ("parse_pathinfo: too many elements in pathinfo") if (scalar (@info) > 3);
-       return unless (@info);
-
-       $Type = shift (@info);
-       return unless (@info);
-
-       if ($info[-1] =~ m/^(hour|day|week|month|year)$/i)
-       {
-               $TimeSpan = pop (@info);
-       }
+  my @hosts = _find_hosts ();
+  @hosts = sort (@hosts);
 
-       $Inst = shift (@info) if (@info);
+  print "<ul>\n";
+  for (my $i = 0; $i < @hosts; $i++)
+  {
+    my $host_html = encode_entities ($hosts[$i]);
+    my $host_url = uri_escape ($hosts[$i]);
 
-       confess ("unrecognized elements in pathinfo") if (@info);
-}
+    print qq(  <li><a href="${\script_name ()}?action=show_host;host=$host_url">$host_html</a></li>\n);
+  }
+  print "</ul>\n";
+} # list_hosts
 
-sub get_all_files
+sub action_show_host
 {
-       my $dir = @_ ? shift : $Config->{'Directory'};
-       my $hash = {};
-       my $dh;
-
-       if (opendir ($dh, $dir))
-       {
-               while (my $thing = readdir ($dh))
-               {
-                       next if ($thing =~ m/^\./);
-
-                       my $type;
-                       my $inst;
-
-                       if ($thing =~ m/^(\w+)-([\w\-\.]+)\.rrd$/)
-                       {
-                               $type = $1;
-                               $inst = $2;
-                       }
-                       elsif ($thing =~ m/^(\w+)\.rrd$/)
-                       {
-                               $type = $1;
-                               $inst = '';
-                       }
-                       else
-                       {
-                               next;
-                       }
-
-                       # Only load RRD files we can actually display..
-                       next unless (defined ($GraphDefs->{$type}));
-
-                       $hash->{$type} = [] unless (defined ($hash->{$type}));
-                       push (@{$hash->{$type}}, $inst);
-               }
-
-               closedir ($dh);
-       }
-
-       return ($hash);
-}
-
-sub get_all_dirs
+  my $host = shift;
+  my $host_url = uri_escape ($host);
+  my %plugins = _find_plugins ($host);
+
+  print qq(    <div><a href="${\script_name ()}?action=overview">Back to list of hosts</a></div>\n);
+
+  print "<ul>\n";
+  for (sort (keys %plugins))
+  {
+    my $plugin = $_;
+    my $plugin_html = encode_entities ($plugin);
+    my $plugin_url = uri_escape ($plugin);
+
+    for (@{$plugins{$plugin}})
+    {
+      my $instance = $_;
+      my $instance_html = encode_entities ($instance);
+      my $instance_url = uri_escape ($instance);
+
+      print qq#  <li><a href="${\script_name ()}?action=show_plugin;host=$host_url;plugin=$plugin_url;plugin_instance=$instance_url">$plugin_html ($instance_html)</a></li>\n#;
+    }
+
+    if (!@{$plugins{$plugin}})
+    {
+      print qq#  <li><a href="${\script_name ()}?action=show_plugin;host=$host_url;plugin=$plugin_url">$plugin_html</a></li>\n#;
+    }
+  } # for (%plugins)
+  print "</ul>\n";
+} # action_show_host
+
+sub action_show_plugin
 {
-       my $dir = @_ ? shift : $Config->{'Directory'};
-       my @ret = ();
-       my $dh;
-
-       if (opendir ($dh, $dir))
-       {
-               while (my $thing = readdir ($dh))
-               {
-                       next if ($thing =~ m/^\./);
-
-                       next if (!-d "$dir/$thing");
-
-                       push (@ret, $thing);
-               }
-
-               closedir ($dh);
-       }
-
-       return (@ret) if (wantarray ());
-       return (\@ret);
-}
-
-sub color_hex2rgb
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+
+  my $host_url = uri_escape ($host);
+  my $plugin_url = uri_escape ($plugin);
+  my $plugin_instance_url = defined ($plugin_instance) ? uri_escape ($plugin_instance) : undef;
+
+  my %types = _find_types ($host, $plugin, $plugin_instance);
+
+  my $url_prefix = script_name () . "?host=$host_url;plugin=$plugin_url";
+  $url_prefix .= ";plugin_instance=$plugin_instance_url" if (defined ($plugin_instance));
+
+  print qq(    <div><a href="${\script_name ()}?action=show_host;host=$host_url">Back to list of plugins</a></div>\n);
+
+  for (sort (keys %types))
+  {
+    my $type = $_;
+    my $type_html = encode_entities ($type);
+    my $type_url = uri_escape ($type);
+
+    if (!defined ($GraphDefs->{$type}))
+    {
+      print qq#  <div><em>Unknown type &quot;$type_html&quot;</em></div>\n#;
+      next;
+    }
+
+    for (@{$types{$type}})
+    {
+      my $instance = $_;
+      my $instance_html = encode_entities ($instance);
+      my $instance_url = uri_escape ($instance);
+
+      print qq#  <div><a href="$url_prefix;type=$type_url;type_instance=$instance_url;action=show_type"><img src="$url_prefix;type=$type_url;type_instance=$instance_url;action=show_graph" /></a></div>\n#;
+    }
+
+    if (!@{$types{$type}})
+    {
+      print qq#  <div><a href="$url_prefix;type=$type_url;action=show_type"><img src="$url_prefix;type=$type_url;action=show_graph" /></a></div>\n#;
+    }
+  }
+} # action_show_plugin
+
+sub action_show_type
 {
-       my $color = shift;
-
-       my ($red, $green, $blue) = map { ord (pack ("H2", $_)) } ($color =~ m/([A-Fa-f0-9]{2})/g);
-       #print STDERR "$color -> rgb($red,$green,$blue)\n";
-
-       return ($red, $green, $blue);
-}
-
-sub color_rgb2hex
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+  my $type = shift;
+  my $type_instance = shift;
+
+  my $host_url = uri_escape ($host);
+  my $plugin_url = uri_escape ($plugin);
+  my $plugin_html = encode_entities ($plugin);
+  my $plugin_instance_url = defined ($plugin_instance) ? uri_escape ($plugin_instance) : undef;
+  my $type_url = uri_escape ($type);
+  my $type_instance_url = defined ($type_instance) ? uri_escape ($type_instance) : undef;
+
+  my $url_prefix = script_name () . "?action=show_plugin;host=$host_url;plugin=$plugin_url";
+  $url_prefix .= ";plugin_instance=$plugin_instance_url" if (defined ($plugin_instance));
+
+  print qq(    <div><a href="$url_prefix">Back to plugin &quot;$plugin_html&quot;</a></div>\n);
+
+  $url_prefix = script_name () . "?action=show_graph;host=$host_url;plugin=$plugin_url";
+  $url_prefix .= ";plugin_instance=$plugin_instance_url" if (defined ($plugin_instance));
+  $url_prefix .= ";type=$type_url";
+  $url_prefix .= ";type_instance=$type_instance_url" if (defined ($type_instance));
+
+  for (qw(hour day week month year))
+  {
+    my $timespan = $_;
+
+    print qq#  <div><img src="$url_prefix;timespan=$timespan" /></div>\n#;
+  }
+} # action_show_type
+
+sub action_show_graph
 {
-       croak unless (scalar (@_) == 3);
-       
-       my ($red, $green, $blue) = @_;
-
-       my $ret = sprintf ("%02X%02X%02X", $red, $green, $blue);
-       #print STDERR "rgb($red,$green,$blue) -> $ret\n";
+  my $host = shift;
+  my $plugin = shift;
+  my $plugin_instance = shift;
+  my $type = shift;
+  my $type_instance = shift;
+  my @rrd_args;
+  my $title;
+  
+  my %times = (hour => -3600, day => -86400, week => 7 * -86400, month => 31 * -86400, year => 366 * -86400);
+  my $start_time = $times{$Args->{'timespan'}} || -86400;
+
+  #print STDERR Data::Dumper->Dump ([$Args], ['Args']);
+
+  return if (!defined ($GraphDefs->{$type}));
+  @rrd_args = @{$GraphDefs->{$type}};
+
+  $title = "$host/$plugin" . (defined ($plugin_instance) ? "-$plugin_instance" : '')
+  . "/$type" . (defined ($type_instance) ? "-$type_instance" : '');
+
+  for (my $i = 0; $i < @DataDirs; $i++)
+  {
+    my $file = $DataDirs[$i] . "/$title.rrd";
+    next if (!-f $file);
+
+    s/{file}/$file/ for (@rrd_args);
+
+    RRDs::graph ('-', '-a', 'PNG', '-s', $start_time, '-t', $title, @RRDDefaultArgs, @rrd_args);
+    if (my $err = RRDs::error ())
+    {
+      die ("RRDs::graph: $err");
+    }
+  }
+} # action_show_graph
+
+sub print_header
+{
+  print <<HEAD;
+Content-Type: application/xhtml+xml; charset=utf-8
+Cache-Control: no-cache
 
-       return ($ret);
-}
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
+  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+  <head>
+    <title>collection.cgi, Version 2</title>
+    <style type="text/css">
+      img
+      {
+       border: none;
+      }
+    </style>
+  </head>
+
+  <body>
+HEAD
+} # print_header
+
+sub print_footer
+{
+  print <<FOOT;
+  </body>
+</html>
+FOOT
+} # print_footer
 
-sub color_calculate_transparent
+sub main
 {
-       my $alpha = shift;
-       my $canvas = [color_hex2rgb (shift)];
-       my @colors = map { [color_hex2rgb ($_)] } (@_);
+       read_config ();
+       validate_args ();
 
-       if (($alpha < 0.0) or ($alpha > 1.0))
+       if (defined ($Args->{'host'})
+         && defined ($Args->{'plugin'})
+         && defined ($Args->{'type'})
+         && ($Args->{'action'} eq 'show_graph'))
        {
-               $alpha = 1.0;
+         $| = 1;
+         print STDOUT header (-Content_Type => 'image/png');
+         action_show_graph ($Args->{'host'},
+           $Args->{'plugin'}, $Args->{'plugin_instance'},
+           $Args->{'type'}, $Args->{'type_instance'});
+         return (0);
        }
 
-       if ($alpha == 0.0)
+       print_header ();
+
+       if (!$Args->{'host'})
        {
-               return (color_rgb2hex (@$canvas));
+         list_hosts ();
        }
-       if ($alpha == 1.0)
+       elsif (!$Args->{'plugin'})
        {
-               return (color_rgb2hex (@{$colors[-1]}));
+         action_show_host ($Args->{'host'});
        }
-
-       my $ret = _color_calculate_transparent ($alpha, $canvas, @colors);
-
-       return (color_rgb2hex (@$ret));
-}
-
-sub _color_calculate_transparent
-{
-       my $alpha = shift;
-       my $canvas = shift;
-       my $color = shift;
-       my @colors = @_ ? shift : ();
-       my $ret = [0, 0, 0];
-
-       for (my $i = 0; $i < 3; $i++)
+       elsif (!$Args->{'type'})
        {
-               $ret->[$i] = ($alpha * $color->[$i]) + ((1 - $alpha) * $canvas->[$i]);
+         action_show_plugin ($Args->{'host'},
+           $Args->{'plugin'}, $Args->{'plugin_instance'});
        }
-
-       return (_color_calculate_transparent ($alpha, $ret, @colors)) if (@colors);
-       return ($ret);
-}
-
-sub get_n_colors
-{
-       my $num = shift;
-       my @ret = ();
-
-       for (my $i = 0; $i < $num; $i++)
+       else
        {
-               my $pos = 6 * $i / $num;
-               my $n = int ($pos);
-               my $p = $pos - $n;
-               my $q = 1 - $p;
-
-               my $red   = 0;
-               my $green = 0;
-               my $blue  = 0;
-
-               if ($n == 0)
-               {
-                       $red  = 255;
-                       $blue = 255 * $p;
-               }
-               elsif ($n == 1)
-               {
-                       $red  = 255 * $q;
-                       $blue = 255;
-               }
-               elsif ($n == 2)
-               {
-                       $green = 255 * $p;
-                       $blue  = 255;
-               }
-               elsif ($n == 3)
-               {
-                       $green = 255;
-                       $blue  = 255 * $q;
-               }
-               elsif ($n == 4)
-               {
-                       $red   = 255 * $p;
-                       $green = 255;
-               }
-               elsif ($n == 5)
-               {
-                       $red   = 255;
-                       $green = 255 * $q;
-               }
-               else { die; }
-
-               push (@ret, sprintf ("%02x%02x%02x", $red, $green, $blue));
+         action_show_type ($Args->{'host'},
+           $Args->{'plugin'}, $Args->{'plugin_instance'},
+           $Args->{'type'}, $Args->{'type_instance'});
        }
 
-       return (@ret);
+       print_footer ();
+
+       return (0);
 }
+
+sub load_graph_definitions
+{
+  my $Canvas = 'FFFFFF';
+
+  my $FullRed    = 'FF0000';
+  my $FullGreen  = '00E000';
+  my $FullBlue   = '0000FF';
+  my $FullYellow = 'F0A000';
+  my $FullCyan   = '00A0FF';
+  my $FullMagenta= 'A000FF';
+
+  my $HalfRed    = 'F7B7B7';
+  my $HalfGreen  = 'B7EFB7';
+  my $HalfBlue   = 'B7B7F7';
+  my $HalfYellow = 'F3DFB7';
+  my $HalfCyan   = 'B7DFF7';
+  my $HalfMagenta= 'DFB7F7';
+
+  my $HalfBlueGreen = '89B3C9';
+
+  $GraphDefs =
+  {
+    apache_bytes => ['DEF:min_raw={file}:count:MIN',
+    'DEF:avg_raw={file}:count:AVERAGE',
+    'DEF:max_raw={file}:count:MAX',
+    'CDEF:min=min_raw,8,*',
+    'CDEF:avg=avg_raw,8,*',
+    'CDEF:max=max_raw,8,*',
+    'CDEF:mytime=avg_raw,TIME,TIME,IF',
+    'CDEF:sample_len_raw=mytime,PREV(mytime),-',
+    'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
+    'CDEF:avg_sample=avg_raw,UN,0,avg_raw,IF,sample_len,*',
+    'CDEF:avg_sum=PREV,UN,0,PREV,IF,avg_sample,+',
+    "AREA:avg#$HalfBlue",
+    "LINE1:avg#$FullBlue:Bit/s",
+    'GPRINT:min:MIN:%5.1lf%s Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:max:MAX:%5.1lf%s Max,',
+    'GPRINT:avg:LAST:%5.1lf%s Last',
+    'GPRINT:avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
+    ],
+    apache_requests => ['DEF:min={file}:count:MIN',
+    'DEF:avg={file}:count:AVERAGE',
+    'DEF:max={file}:count:MAX',
+    'CDEF:moving_average=PREV,UN,avg,PREV,IF,0.8,*,avg,0.2,*,+',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    'LINE1:moving_average#000000',
+    "LINE1:avg#$FullBlue:Requests/s",
+    '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_scoreboard => ['DEF:min={file}:count:MIN',
+    'DEF:avg={file}:count:AVERAGE',
+    'DEF:max={file}:count:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Processes",
+    'GPRINT:min:MIN:%6.2lf Min,',
+    'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:max:MAX:%6.2lf Max,',
+    'GPRINT:avg:LAST:%6.2lf Last'
+    ],
+    charge => [
+    'DEF:avg={file}:charge:AVERAGE',
+    'DEF:min={file}:charge:MIN',
+    'DEF:max={file}:charge:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Charge",
+    'GPRINT:min:MIN:%5.1lf%sAh Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%sAh Avg,',
+    'GPRINT:max:MAX:%5.1lf%sAh Max,',
+    'GPRINT:avg:LAST:%5.1lf%sAh Last\l'
+    ],
+    charge_percent => [
+    'DEF:avg={file}:percent:AVERAGE',
+    'DEF:min={file}:percent:MIN',
+    'DEF:max={file}:percent:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Charge",
+    'GPRINT:min:MIN:%5.1lf%s%% Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%s%% Avg,',
+    'GPRINT:max:MAX:%5.1lf%s%% Max,',
+    'GPRINT:avg:LAST:%5.1lf%s%% Last\l'
+    ],
+    cpu => ['-v', 'CPU load',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Percent",
+    'GPRINT:min:MIN:%6.2lf%% Min,',
+    'GPRINT:avg:AVERAGE:%6.2lf%% Avg,',
+    'GPRINT:max:MAX:%6.2lf%% Max,',
+    'GPRINT:avg:LAST:%6.2lf%% Last\l'
+    ],
+    current => [
+    'DEF:avg={file}:current:AVERAGE',
+    'DEF:min={file}:current:MIN',
+    'DEF:max={file}:current:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Current",
+    'GPRINT:min:MIN:%5.1lf%sA Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%sA Avg,',
+    'GPRINT:max:MAX:%5.1lf%sA Max,',
+    'GPRINT:avg:LAST:%5.1lf%sA Last\l'
+    ],
+    df => ['-v', 'Percent',
+    'DEF:free_avg={file}:free:AVERAGE',
+    'DEF:free_min={file}:free:MIN',
+    'DEF:free_max={file}:free:MAX',
+    'DEF:used_avg={file}:used:AVERAGE',
+    'DEF:used_min={file}:used:MIN',
+    'DEF:used_max={file}:used:MAX',
+    'CDEF:total=free_avg,used_avg,+',
+    'CDEF:free_pct=100,free_avg,*,total,/',
+    'CDEF:used_pct=100,used_avg,*,total,/',
+    'CDEF:free_acc=free_pct,used_pct,+',
+    'CDEF:used_acc=used_pct',
+    "AREA:free_acc#$HalfGreen",
+    "AREA:used_acc#$HalfRed",
+    "LINE1:free_acc#$FullGreen:Free",
+    'GPRINT:free_min:MIN:%5.1lf%sB Min,',
+    'GPRINT:free_avg:AVERAGE:%5.1lf%sB Avg,',
+    'GPRINT:free_max:MAX:%5.1lf%sB Max,',
+    'GPRINT:free_avg:LAST:%5.1lf%sB Last\l',
+    "LINE1:used_acc#$FullRed:Used",
+    'GPRINT:used_min:MIN:%5.1lf%sB Min,',
+    'GPRINT:used_avg:AVERAGE:%5.1lf%sB Avg,',
+    'GPRINT:used_max:MAX:%5.1lf%sB Max,',
+    'GPRINT:used_avg:LAST:%5.1lf%sB Last\l'
+    ],
+    disk => [
+    'DEF:rtime_avg={file}:rtime:AVERAGE',
+    'DEF:rtime_min={file}:rtime:MIN',
+    'DEF:rtime_max={file}:rtime:MAX',
+    'DEF:wtime_avg={file}:wtime:AVERAGE',
+    'DEF:wtime_min={file}:wtime:MIN',
+    'DEF:wtime_max={file}:wtime:MAX',
+    'CDEF:rtime_avg_ms=rtime_avg,1000,/',
+    'CDEF:rtime_min_ms=rtime_min,1000,/',
+    'CDEF:rtime_max_ms=rtime_max,1000,/',
+    'CDEF:wtime_avg_ms=wtime_avg,1000,/',
+    'CDEF:wtime_min_ms=wtime_min,1000,/',
+    'CDEF:wtime_max_ms=wtime_max,1000,/',
+    'CDEF:total_avg_ms=rtime_avg_ms,wtime_avg_ms,+',
+    'CDEF:total_min_ms=rtime_min_ms,wtime_min_ms,+',
+    'CDEF:total_max_ms=rtime_max_ms,wtime_max_ms,+',
+    "AREA:total_max_ms#$HalfRed",
+    "AREA:total_min_ms#$Canvas",
+    "LINE1:wtime_avg_ms#$FullGreen:Write",
+    'GPRINT:wtime_min_ms:MIN:%5.1lf%s Min,',
+    'GPRINT:wtime_avg_ms:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:wtime_max_ms:MAX:%5.1lf%s Max,',
+    'GPRINT:wtime_avg_ms:LAST:%5.1lf%s Last\n',
+    "LINE1:rtime_avg_ms#$FullBlue:Read ",
+    'GPRINT:rtime_min_ms:MIN:%5.1lf%s Min,',
+    'GPRINT:rtime_avg_ms:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:rtime_max_ms:MAX:%5.1lf%s Max,',
+    'GPRINT:rtime_avg_ms:LAST:%5.1lf%s Last\n',
+    "LINE1:total_avg_ms#$FullRed:Total",
+    'GPRINT:total_min_ms:MIN:%5.1lf%s Min,',
+    'GPRINT:total_avg_ms:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:total_max_ms:MAX:%5.1lf%s Max,',
+    'GPRINT:total_avg_ms:LAST:%5.1lf%s Last'
+    ],
+    disk_octets => ['-v', 'Bytes/s',
+    'DEF:out_min={file}:write:MIN',
+    'DEF:out_avg={file}:write:AVERAGE',
+    'DEF:out_max={file}:write:MAX',
+    'DEF:inc_min={file}:read:MIN',
+    'DEF:inc_avg={file}:read:AVERAGE',
+    'DEF:inc_max={file}:read:MAX',
+    'CDEF:overlap=out_avg,inc_avg,GT,inc_avg,out_avg,IF',
+    'CDEF:mytime=out_avg,TIME,TIME,IF',
+    'CDEF:sample_len_raw=mytime,PREV(mytime),-',
+    'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
+    'CDEF:out_avg_sample=out_avg,UN,0,out_avg,IF,sample_len,*',
+    'CDEF:out_avg_sum=PREV,UN,0,PREV,IF,out_avg_sample,+',
+    'CDEF:inc_avg_sample=inc_avg,UN,0,inc_avg,IF,sample_len,*',
+    'CDEF:inc_avg_sum=PREV,UN,0,PREV,IF,inc_avg_sample,+',
+    "AREA:out_avg#$HalfGreen",
+    "AREA:inc_avg#$HalfBlue",
+    "AREA:overlap#$HalfBlueGreen",
+    "LINE1:out_avg#$FullGreen:Written",
+    'GPRINT:out_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:out_max:MAX:%5.1lf%s Max,',
+    'GPRINT:out_avg:LAST:%5.1lf%s Last',
+    'GPRINT:out_avg_sum:LAST:(ca. %5.1lf%sB Total)\l',
+    "LINE1:inc_avg#$FullBlue:Read   ",
+    'GPRINT:inc_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:inc_max:MAX:%5.1lf%s Max,',
+    'GPRINT:inc_avg:LAST:%5.1lf%s Last',
+    'GPRINT:inc_avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
+    ],
+    disk_merged => ['-v', 'Merged Ops/s',
+    'DEF:out_min={file}:write:MIN',
+    'DEF:out_avg={file}:write:AVERAGE',
+    'DEF:out_max={file}:write:MAX',
+    'DEF:inc_min={file}:read:MIN',
+    'DEF:inc_avg={file}:read:AVERAGE',
+    'DEF:inc_max={file}:read:MAX',
+    'CDEF:overlap=out_avg,inc_avg,GT,inc_avg,out_avg,IF',
+    "AREA:out_avg#$HalfGreen",
+    "AREA:inc_avg#$HalfBlue",
+    "AREA:overlap#$HalfBlueGreen",
+    "LINE1:out_avg#$FullGreen:Written",
+    'GPRINT:out_avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:out_max:MAX:%6.2lf Max,',
+    'GPRINT:out_avg:LAST:%6.2lf Last\l',
+    "LINE1:inc_avg#$FullBlue:Read   ",
+    'GPRINT:inc_avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:inc_max:MAX:%6.2lf Max,',
+    'GPRINT:inc_avg:LAST:%6.2lf Last\l'
+    ],
+    disk_ops => ['-v', 'Ops/s',
+    'DEF:out_min={file}:write:MIN',
+    'DEF:out_avg={file}:write:AVERAGE',
+    'DEF:out_max={file}:write:MAX',
+    'DEF:inc_min={file}:read:MIN',
+    'DEF:inc_avg={file}:read:AVERAGE',
+    'DEF:inc_max={file}:read:MAX',
+    'CDEF:overlap=out_avg,inc_avg,GT,inc_avg,out_avg,IF',
+    "AREA:out_avg#$HalfGreen",
+    "AREA:inc_avg#$HalfBlue",
+    "AREA:overlap#$HalfBlueGreen",
+    "LINE1:out_avg#$FullGreen:Written",
+    'GPRINT:out_avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:out_max:MAX:%6.2lf Max,',
+    'GPRINT:out_avg:LAST:%6.2lf Last\l',
+    "LINE1:inc_avg#$FullBlue:Read   ",
+    'GPRINT:inc_avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:inc_max:MAX:%6.2lf Max,',
+    'GPRINT:inc_avg:LAST:%6.2lf Last\l'
+    ],
+    disk_time => ['-v', 'Seconds/s',
+    'DEF:out_min_raw={file}:write:MIN',
+    'DEF:out_avg_raw={file}:write:AVERAGE',
+    'DEF:out_max_raw={file}:write:MAX',
+    'DEF:inc_min_raw={file}:read:MIN',
+    'DEF:inc_avg_raw={file}:read:AVERAGE',
+    'DEF:inc_max_raw={file}:read:MAX',
+    'CDEF:out_min=out_min_raw,1000,/',
+    'CDEF:out_avg=out_avg_raw,1000,/',
+    'CDEF:out_max=out_max_raw,1000,/',
+    'CDEF:inc_min=inc_min_raw,1000,/',
+    'CDEF:inc_avg=inc_avg_raw,1000,/',
+    'CDEF:inc_max=inc_max_raw,1000,/',
+    'CDEF:overlap=out_avg,inc_avg,GT,inc_avg,out_avg,IF',
+    "AREA:out_avg#$HalfGreen",
+    "AREA:inc_avg#$HalfBlue",
+    "AREA:overlap#$HalfBlueGreen",
+    "LINE1:out_avg#$FullGreen:Written",
+    'GPRINT:out_avg:AVERAGE:%5.1lf%ss Avg,',
+    'GPRINT:out_max:MAX:%5.1lf%ss Max,',
+    'GPRINT:out_avg:LAST:%5.1lf%ss Last\l',
+    "LINE1:inc_avg#$FullBlue:Read   ",
+    'GPRINT:inc_avg:AVERAGE:%5.1lf%ss Avg,',
+    'GPRINT:inc_max:MAX:%5.1lf%ss Max,',
+    'GPRINT:inc_avg:LAST:%5.1lf%ss Last\l'
+    ],
+    dns_traffic => ['DEF:rsp_min_raw={file}:responses:MIN',
+    'DEF:rsp_avg_raw={file}:responses:AVERAGE',
+    'DEF:rsp_max_raw={file}:responses:MAX',
+    'DEF:qry_min_raw={file}:queries:MIN',
+    'DEF:qry_avg_raw={file}:queries:AVERAGE',
+    'DEF:qry_max_raw={file}:queries:MAX',
+    'CDEF:rsp_min=rsp_min_raw,8,*',
+    'CDEF:rsp_avg=rsp_avg_raw,8,*',
+    'CDEF:rsp_max=rsp_max_raw,8,*',
+    'CDEF:qry_min=qry_min_raw,8,*',
+    'CDEF:qry_avg=qry_avg_raw,8,*',
+    'CDEF:qry_max=qry_max_raw,8,*',
+    'CDEF:overlap=rsp_avg,qry_avg,GT,qry_avg,rsp_avg,IF',
+    'CDEF:mytime=rsp_avg_raw,TIME,TIME,IF',
+    'CDEF:sample_len_raw=mytime,PREV(mytime),-',
+    'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
+    'CDEF:rsp_avg_sample=rsp_avg_raw,UN,0,rsp_avg_raw,IF,sample_len,*',
+    'CDEF:rsp_avg_sum=PREV,UN,0,PREV,IF,rsp_avg_sample,+',
+    'CDEF:qry_avg_sample=qry_avg_raw,UN,0,qry_avg_raw,IF,sample_len,*',
+    'CDEF:qry_avg_sum=PREV,UN,0,PREV,IF,qry_avg_sample,+',
+    "AREA:rsp_avg#$HalfGreen",
+    "AREA:qry_avg#$HalfBlue",
+    "AREA:overlap#$HalfBlueGreen",
+    "LINE1:rsp_avg#$FullGreen:Responses",
+    'GPRINT:rsp_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:rsp_max:MAX:%5.1lf%s Max,',
+    'GPRINT:rsp_avg:LAST:%5.1lf%s Last',
+    'GPRINT:rsp_avg_sum:LAST:(ca. %5.1lf%sB Total)\l',
+    "LINE1:qry_avg#$FullBlue:Queries  ",
+    #'GPRINT:qry_min:MIN:%5.1lf %s Min,',
+    'GPRINT:qry_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:qry_max:MAX:%5.1lf%s Max,',
+    'GPRINT:qry_avg:LAST:%5.1lf%s Last',
+    'GPRINT:qry_avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
+    ],
+    email => [
+    'DEF:avg={file}:count:AVERAGE',
+    'DEF:min={file}:count:MIN',
+    'DEF:max={file}:count:MAX',
+    "AREA:max#$HalfMagenta",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullMagenta:Count ",
+    'GPRINT:min:MIN:%4.1lf Min,',
+    'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+    'GPRINT:max:MAX:%4.1lf Max,',
+    'GPRINT:avg:LAST:%4.1lf Last\l'
+    ],
+    email_size => [
+    'DEF:avg={file}:size:AVERAGE',
+    'DEF:min={file}:size:MIN',
+    'DEF:max={file}:size:MAX',
+    "AREA:max#$HalfMagenta",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullMagenta:Count ",
+    'GPRINT:min:MIN:%4.1lf Min,',
+    'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+    'GPRINT:max:MAX:%4.1lf Max,',
+    'GPRINT:avg:LAST:%4.1lf Last\l'
+    ],
+    spam_score => ['-v', 'Score',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Score ",
+    'GPRINT:min:MIN:%4.1lf Min,',
+    'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+    'GPRINT:max:MAX:%4.1lf Max,',
+    'GPRINT:avg:LAST:%4.1lf Last\l'
+    ],
+    spam_check => [
+    'DEF:avg={file}:hits:AVERAGE',
+    'DEF:min={file}:hits:MIN',
+    'DEF:max={file}:hits:MAX',
+    "AREA:max#$HalfMagenta",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullMagenta:Count ",
+    'GPRINT:min:MIN:%4.1lf Min,',
+    'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+    'GPRINT:max:MAX:%4.1lf Max,',
+    'GPRINT:avg:LAST:%4.1lf Last\l'
+    ],
+    entropy => ['DEF:avg={file}:entropy:AVERAGE',
+    'DEF:min={file}:entropy:MIN',
+    'DEF:max={file}:entropy:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Bits",
+    'GPRINT:min:MIN:%4.0lfbit Min,',
+    'GPRINT:avg:AVERAGE:%4.0lfbit Avg,',
+    'GPRINT:max:MAX:%4.0lfbit Max,',
+    'GPRINT:avg:LAST:%4.0lfbit Last\l'
+    ],
+    fanspeed => ['-v', 'RPM',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfMagenta",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullMagenta:RPM",
+    'GPRINT:min:MIN:%4.1lf Min,',
+    'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+    'GPRINT:max:MAX:%4.1lf Max,',
+    'GPRINT:avg:LAST:%4.1lf Last\l'
+    ],
+    frequency => ['-v', 'Hertz',
+    'DEF:avg={file}:frequency:AVERAGE',
+    'DEF:min={file}:frequency:MIN',
+    'DEF:max={file}:frequency:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Frequency [Hz]",
+    'GPRINT:min:MIN:%4.1lf Min,',
+    'GPRINT:avg:AVERAGE:%4.1lf Avg,',
+    'GPRINT:max:MAX:%4.1lf Max,',
+    'GPRINT:avg:LAST:%4.1lf Last\l'
+    ],
+    frequency_offset => [ # NTPd
+    'DEF:ppm_avg={file}:ppm:AVERAGE',
+    'DEF:ppm_min={file}:ppm:MIN',
+    'DEF:ppm_max={file}:ppm:MAX',
+    "AREA:ppm_max#$HalfBlue",
+    "AREA:ppm_min#$Canvas",
+    "LINE1:ppm_avg#$FullBlue:{inst}",
+    'GPRINT:ppm_min:MIN:%5.2lf Min,',
+    'GPRINT:ppm_avg:AVERAGE:%5.2lf Avg,',
+    'GPRINT:ppm_max:MAX:%5.2lf Max,',
+    'GPRINT:ppm_avg:LAST:%5.2lf Last'
+    ],
+    hddtemp => [
+    'DEF:temp_avg={file}:value:AVERAGE',
+    'DEF:temp_min={file}:value:MIN',
+    'DEF:temp_max={file}:value:MAX',
+    "AREA:temp_max#$HalfRed",
+    "AREA:temp_min#$Canvas",
+    "LINE1:temp_avg#$FullRed:Temperature",
+    'GPRINT:temp_min:MIN:%4.1lf Min,',
+    'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
+    'GPRINT:temp_max:MAX:%4.1lf Max,',
+    'GPRINT:temp_avg:LAST:%4.1lf Last\l'
+    ],
+    if_packets => ['-v', 'Packets/s',
+    'DEF:tx_min={file}:tx:MIN',
+    'DEF:tx_avg={file}:tx:AVERAGE',
+    'DEF:tx_max={file}:tx:MAX',
+    'DEF:rx_min={file}:rx:MIN',
+    'DEF:rx_avg={file}:rx:AVERAGE',
+    'DEF:rx_max={file}:rx:MAX',
+    'CDEF:overlap=tx_avg,rx_avg,GT,rx_avg,tx_avg,IF',
+    'CDEF:mytime=tx_avg,TIME,TIME,IF',
+    'CDEF:sample_len_raw=mytime,PREV(mytime),-',
+    'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
+    'CDEF:tx_avg_sample=tx_avg,UN,0,tx_avg,IF,sample_len,*',
+    'CDEF:tx_avg_sum=PREV,UN,0,PREV,IF,tx_avg_sample,+',
+    'CDEF:rx_avg_sample=rx_avg,UN,0,rx_avg,IF,sample_len,*',
+    'CDEF:rx_avg_sum=PREV,UN,0,PREV,IF,rx_avg_sample,+',
+    "AREA:tx_avg#$HalfGreen",
+    "AREA:rx_avg#$HalfBlue",
+    "AREA:overlap#$HalfBlueGreen",
+    "LINE1:tx_avg#$FullGreen:TX",
+    'GPRINT:tx_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:tx_max:MAX:%5.1lf%s Max,',
+    'GPRINT:tx_avg:LAST:%5.1lf%s Last',
+    'GPRINT:tx_avg_sum:LAST:(ca. %4.0lf%s Total)\l',
+    "LINE1:rx_avg#$FullBlue:RX",
+    #'GPRINT:rx_min:MIN:%5.1lf %s Min,',
+    'GPRINT:rx_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:rx_max:MAX:%5.1lf%s Max,',
+    'GPRINT:rx_avg:LAST:%5.1lf%s Last',
+    'GPRINT:rx_avg_sum:LAST:(ca. %4.0lf%s Total)\l'
+    ],
+    irq => ['-v', 'Issues/s',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Issues/s",
+    'GPRINT:min:MIN:%6.2lf Min,',
+    'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:max:MAX:%6.2lf Max,',
+    'GPRINT:avg:LAST:%6.2lf Last\l'
+    ],
+    load => ['DEF:s_avg={file}:shortterm:AVERAGE',
+    'DEF:s_min={file}:shortterm:MIN',
+    'DEF:s_max={file}:shortterm:MAX',
+    'DEF:m_avg={file}:midterm:AVERAGE',
+    'DEF:m_min={file}:midterm:MIN',
+    'DEF:m_max={file}:midterm:MAX',
+    'DEF:l_avg={file}:longterm:AVERAGE',
+    'DEF:l_min={file}:longterm:MIN',
+    'DEF:l_max={file}:longterm:MAX',
+    "AREA:s_max#$HalfGreen",
+    "AREA:s_min#$Canvas",
+    "LINE1:s_avg#$FullGreen: 1m average",
+    'GPRINT:s_min:MIN:%4.2lf Min,',
+    'GPRINT:s_avg:AVERAGE:%4.2lf Avg,',
+    'GPRINT:s_max:MAX:%4.2lf Max,',
+    'GPRINT:s_avg:LAST:%4.2lf Last\n',
+    "LINE1:m_avg#$FullBlue: 5m average",
+    'GPRINT:m_min:MIN:%4.2lf Min,',
+    'GPRINT:m_avg:AVERAGE:%4.2lf Avg,',
+    'GPRINT:m_max:MAX:%4.2lf Max,',
+    'GPRINT:m_avg:LAST:%4.2lf Last\n',
+    "LINE1:l_avg#$FullRed:15m average",
+    'GPRINT:l_min:MIN:%4.2lf Min,',
+    'GPRINT:l_avg:AVERAGE:%4.2lf Avg,',
+    'GPRINT:l_max:MAX:%4.2lf Max,',
+    'GPRINT:l_avg:LAST:%4.2lf Last'
+    ],
+    load_percent => [
+    'DEF:avg={file}:percent:AVERAGE',
+    'DEF:min={file}:percent:MIN',
+    'DEF:max={file}:percent:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Load",
+    'GPRINT:min:MIN:%5.1lf%s%% Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%s%% Avg,',
+    'GPRINT:max:MAX:%5.1lf%s%% Max,',
+    'GPRINT:avg:LAST:%5.1lf%s%% Last\l'
+    ],
+    mails => ['DEF:rawgood={file}:good:AVERAGE',
+    'DEF:rawspam={file}:spam:AVERAGE',
+    'CDEF:good=rawgood,UN,0,rawgood,IF',
+    'CDEF:spam=rawspam,UN,0,rawspam,IF',
+    'CDEF:negspam=spam,-1,*',
+    "AREA:good#$HalfGreen",
+    "LINE1:good#$FullGreen:Good mails",
+    'GPRINT:good:AVERAGE:%4.1lf Avg,',
+    'GPRINT:good:MAX:%4.1lf Max,',
+    'GPRINT:good:LAST:%4.1lf Last\n',
+    "AREA:negspam#$HalfRed",
+    "LINE1:negspam#$FullRed:Spam mails",
+    'GPRINT:spam:AVERAGE:%4.1lf Avg,',
+    'GPRINT:spam:MAX:%4.1lf Max,',
+    'GPRINT:spam:LAST:%4.1lf Last',
+    'HRULE:0#000000'
+    ],
+    memory => ['-b', '1024', '-v', 'Bytes',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Memory",
+    'GPRINT:min:MIN:%5.1lf%sbyte Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%sbyte Avg,',
+    'GPRINT:max:MAX:%5.1lf%sbyte Max,',
+    'GPRINT:avg:LAST:%5.1lf%sbyte Last\l'
+    ],
+    old_memory => [
+    'DEF:used_avg={file}:used:AVERAGE',
+    'DEF:free_avg={file}:free:AVERAGE',
+    'DEF:buffers_avg={file}:buffers:AVERAGE',
+    'DEF:cached_avg={file}:cached:AVERAGE',
+    'DEF:used_min={file}:used:MIN',
+    'DEF:free_min={file}:free:MIN',
+    'DEF:buffers_min={file}:buffers:MIN',
+    'DEF:cached_min={file}:cached:MIN',
+    'DEF:used_max={file}:used:MAX',
+    'DEF:free_max={file}:free:MAX',
+    'DEF:buffers_max={file}:buffers:MAX',
+    'DEF:cached_max={file}:cached:MAX',
+    'CDEF:cached_avg_nn=cached_avg,UN,0,cached_avg,IF',
+    'CDEF:buffers_avg_nn=buffers_avg,UN,0,buffers_avg,IF',
+    'CDEF:free_cached_buffers_used=free_avg,cached_avg_nn,+,buffers_avg_nn,+,used_avg,+',
+    'CDEF:cached_buffers_used=cached_avg,buffers_avg_nn,+,used_avg,+',
+    'CDEF:buffers_used=buffers_avg,used_avg,+',
+    "AREA:free_cached_buffers_used#$HalfGreen",
+    "AREA:cached_buffers_used#$HalfBlue",
+    "AREA:buffers_used#$HalfYellow",
+    "AREA:used_avg#$HalfRed",
+    "LINE1:free_cached_buffers_used#$FullGreen:Free        ",
+    'GPRINT:free_min:MIN:%5.1lf%s Min,',
+    'GPRINT:free_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:free_max:MAX:%5.1lf%s Max,',
+    'GPRINT:free_avg:LAST:%5.1lf%s Last\n',
+    "LINE1:cached_buffers_used#$FullBlue:Page cache  ",
+    'GPRINT:cached_min:MIN:%5.1lf%s Min,',
+    'GPRINT:cached_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:cached_max:MAX:%5.1lf%s Max,',
+    'GPRINT:cached_avg:LAST:%5.1lf%s Last\n',
+    "LINE1:buffers_used#$FullYellow:Buffer cache",
+    'GPRINT:buffers_min:MIN:%5.1lf%s Min,',
+    'GPRINT:buffers_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:buffers_max:MAX:%5.1lf%s Max,',
+    'GPRINT:buffers_avg:LAST:%5.1lf%s Last\n',
+    "LINE1:used_avg#$FullRed:Used        ",
+    'GPRINT:used_min:MIN:%5.1lf%s Min,',
+    'GPRINT:used_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:used_max:MAX:%5.1lf%s Max,',
+    'GPRINT:used_avg:LAST:%5.1lf%s Last'
+    ],
+    mysql_commands => ['-v', 'Issues/s',
+    "DEF:val_avg={file}:value:AVERAGE",
+    "DEF:val_min={file}:value:MIN",
+    "DEF:val_max={file}:value:MAX",
+    "AREA:val_max#$HalfBlue",
+    "AREA:val_min#$Canvas",
+    "LINE1:val_avg#$FullBlue:{inst}",
+    'GPRINT:val_min:MIN:%5.2lf Min,',
+    'GPRINT:val_avg:AVERAGE:%5.2lf Avg,',
+    'GPRINT:val_max:MAX:%5.2lf Max,',
+    'GPRINT:val_avg:LAST:%5.2lf Last'
+    ],
+    mysql_handler => ['-v', 'Issues/s',
+    "DEF:val_avg={file}:value:AVERAGE",
+    "DEF:val_min={file}:value:MIN",
+    "DEF:val_max={file}:value:MAX",
+    "AREA:val_max#$HalfBlue",
+    "AREA:val_min#$Canvas",
+    "LINE1:val_avg#$FullBlue:{inst}",
+    'GPRINT:val_min:MIN:%5.2lf Min,',
+    'GPRINT:val_avg:AVERAGE:%5.2lf Avg,',
+    'GPRINT:val_max:MAX:%5.2lf Max,',
+    'GPRINT:val_avg:LAST:%5.2lf Last'
+    ],
+    mysql_octets => ['-v', 'Bytes/s',
+    'DEF:out_min={file}:tx:MIN',
+    'DEF:out_avg={file}:tx:AVERAGE',
+    'DEF:out_max={file}:tx:MAX',
+    'DEF:inc_min={file}:rx:MIN',
+    'DEF:inc_avg={file}:rx:AVERAGE',
+    'DEF:inc_max={file}:rx:MAX',
+    'CDEF:overlap=out_avg,inc_avg,GT,inc_avg,out_avg,IF',
+    'CDEF:mytime=out_avg,TIME,TIME,IF',
+    'CDEF:sample_len_raw=mytime,PREV(mytime),-',
+    'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
+    'CDEF:out_avg_sample=out_avg,UN,0,out_avg,IF,sample_len,*',
+    'CDEF:out_avg_sum=PREV,UN,0,PREV,IF,out_avg_sample,+',
+    'CDEF:inc_avg_sample=inc_avg,UN,0,inc_avg,IF,sample_len,*',
+    'CDEF:inc_avg_sum=PREV,UN,0,PREV,IF,inc_avg_sample,+',
+    "AREA:out_avg#$HalfGreen",
+    "AREA:inc_avg#$HalfBlue",
+    "AREA:overlap#$HalfBlueGreen",
+    "LINE1:out_avg#$FullGreen:Written",
+    'GPRINT:out_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:out_max:MAX:%5.1lf%s Max,',
+    'GPRINT:out_avg:LAST:%5.1lf%s Last',
+    'GPRINT:out_avg_sum:LAST:(ca. %5.1lf%sB Total)\l',
+    "LINE1:inc_avg#$FullBlue:Read   ",
+    'GPRINT:inc_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:inc_max:MAX:%5.1lf%s Max,',
+    'GPRINT:inc_avg:LAST:%5.1lf%s Last',
+    'GPRINT:inc_avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
+    ],
+    mysql_qcache => ['-v', 'Queries/s',
+    "DEF:hits_min={file}:hits:MIN",
+    "DEF:hits_avg={file}:hits:AVERAGE",
+    "DEF:hits_max={file}:hits:MAX",
+    "DEF:inserts_min={file}:inserts:MIN",
+    "DEF:inserts_avg={file}:inserts:AVERAGE",
+    "DEF:inserts_max={file}:inserts:MAX",
+    "DEF:not_cached_min={file}:not_cached:MIN",
+    "DEF:not_cached_avg={file}:not_cached:AVERAGE",
+    "DEF:not_cached_max={file}:not_cached:MAX",
+    "DEF:lowmem_prunes_min={file}:lowmem_prunes:MIN",
+    "DEF:lowmem_prunes_avg={file}:lowmem_prunes:AVERAGE",
+    "DEF:lowmem_prunes_max={file}:lowmem_prunes:MAX",
+    "DEF:queries_min={file}:queries_in_cache:MIN",
+    "DEF:queries_avg={file}:queries_in_cache:AVERAGE",
+    "DEF:queries_max={file}:queries_in_cache:MAX",
+    "CDEF:unknown=queries_avg,UNKN,+",
+    "CDEF:not_cached_agg=hits_avg,inserts_avg,+,not_cached_avg,+",
+    "CDEF:inserts_agg=hits_avg,inserts_avg,+",
+    "CDEF:hits_agg=hits_avg",
+    "AREA:not_cached_agg#$HalfYellow",
+    "AREA:inserts_agg#$HalfBlue",
+    "AREA:hits_agg#$HalfGreen",
+    "LINE1:not_cached_agg#$FullYellow:Not Cached      ",
+    'GPRINT:not_cached_min:MIN:%5.2lf Min,',
+    'GPRINT:not_cached_avg:AVERAGE:%5.2lf Avg,',
+    'GPRINT:not_cached_max:MAX:%5.2lf Max,',
+    'GPRINT:not_cached_avg:LAST:%5.2lf Last\l',
+    "LINE1:inserts_agg#$FullBlue:Inserts         ",
+    'GPRINT:inserts_min:MIN:%5.2lf Min,',
+    'GPRINT:inserts_avg:AVERAGE:%5.2lf Avg,',
+    'GPRINT:inserts_max:MAX:%5.2lf Max,',
+    'GPRINT:inserts_avg:LAST:%5.2lf Last\l',
+    "LINE1:hits_agg#$FullGreen:Hits            ",
+    'GPRINT:hits_min:MIN:%5.2lf Min,',
+    'GPRINT:hits_avg:AVERAGE:%5.2lf Avg,',
+    'GPRINT:hits_max:MAX:%5.2lf Max,',
+    'GPRINT:hits_avg:LAST:%5.2lf Last\l',
+    "LINE1:lowmem_prunes_avg#$FullRed:Lowmem Prunes   ",
+    'GPRINT:lowmem_prunes_min:MIN:%5.2lf Min,',
+    'GPRINT:lowmem_prunes_avg:AVERAGE:%5.2lf Avg,',
+    'GPRINT:lowmem_prunes_max:MAX:%5.2lf Max,',
+    'GPRINT:lowmem_prunes_avg:LAST:%5.2lf Last\l',
+    "LINE1:unknown#$Canvas:Queries in cache",
+    'GPRINT:queries_min:MIN:%5.0lf Min,',
+    'GPRINT:queries_avg:AVERAGE:%5.0lf Avg,',
+    'GPRINT:queries_max:MAX:%5.0lf Max,',
+    'GPRINT:queries_avg:LAST:%5.0lf Last\l'
+    ],
+    mysql_threads => ['-v', 'Threads',
+    "DEF:running_min={file}:running:MIN",
+    "DEF:running_avg={file}:running:AVERAGE",
+    "DEF:running_max={file}:running:MAX",
+    "DEF:connected_min={file}:connected:MIN",
+    "DEF:connected_avg={file}:connected:AVERAGE",
+    "DEF:connected_max={file}:connected:MAX",
+    "DEF:cached_min={file}:cached:MIN",
+    "DEF:cached_avg={file}:cached:AVERAGE",
+    "DEF:cached_max={file}:cached:MAX",
+    "DEF:created_min={file}:created:MIN",
+    "DEF:created_avg={file}:created:AVERAGE",
+    "DEF:created_max={file}:created:MAX",
+    "CDEF:unknown=created_avg,UNKN,+",
+    "CDEF:cached_agg=connected_avg,cached_avg,+",
+    "AREA:cached_agg#$HalfGreen",
+    "AREA:connected_avg#$HalfBlue",
+    "AREA:running_avg#$HalfRed",
+    "LINE1:cached_agg#$FullGreen:Cached   ",
+    'GPRINT:cached_min:MIN:%5.1lf Min,',
+    'GPRINT:cached_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:cached_max:MAX:%5.1lf Max,',
+    'GPRINT:cached_avg:LAST:%5.1lf Last\l',
+    "LINE1:connected_avg#$FullBlue:Connected",
+    'GPRINT:connected_min:MIN:%5.1lf Min,',
+    'GPRINT:connected_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:connected_max:MAX:%5.1lf Max,',
+    'GPRINT:connected_avg:LAST:%5.1lf Last\l',
+    "LINE1:running_avg#$FullRed:Running  ",
+    'GPRINT:running_min:MIN:%5.1lf Min,',
+    'GPRINT:running_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:running_max:MAX:%5.1lf Max,',
+    'GPRINT:running_avg:LAST:%5.1lf Last\l',
+    "LINE1:unknown#$Canvas:Created  ",
+    'GPRINT:created_min:MIN:%5.0lf Min,',
+    'GPRINT:created_avg:AVERAGE:%5.0lf Avg,',
+    'GPRINT:created_max:MAX:%5.0lf Max,',
+    'GPRINT:created_avg:LAST:%5.0lf Last\l'
+    ],
+    nfs_procedure => ['-v', 'Issues/s',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Issues/s",
+    'GPRINT:min:MIN:%6.2lf Min,',
+    'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:max:MAX:%6.2lf Max,',
+    'GPRINT:avg:LAST:%6.2lf Last\l'
+    ],
+    nfs3_procedures => [
+    "DEF:null_avg={file}:null:AVERAGE",
+    "DEF:getattr_avg={file}:getattr:AVERAGE",
+    "DEF:setattr_avg={file}:setattr:AVERAGE",
+    "DEF:lookup_avg={file}:lookup:AVERAGE",
+    "DEF:access_avg={file}:access:AVERAGE",
+    "DEF:readlink_avg={file}:readlink:AVERAGE",
+    "DEF:read_avg={file}:read:AVERAGE",
+    "DEF:write_avg={file}:write:AVERAGE",
+    "DEF:create_avg={file}:create:AVERAGE",
+    "DEF:mkdir_avg={file}:mkdir:AVERAGE",
+    "DEF:symlink_avg={file}:symlink:AVERAGE",
+    "DEF:mknod_avg={file}:mknod:AVERAGE",
+    "DEF:remove_avg={file}:remove:AVERAGE",
+    "DEF:rmdir_avg={file}:rmdir:AVERAGE",
+    "DEF:rename_avg={file}:rename:AVERAGE",
+    "DEF:link_avg={file}:link:AVERAGE",
+    "DEF:readdir_avg={file}:readdir:AVERAGE",
+    "DEF:readdirplus_avg={file}:readdirplus:AVERAGE",
+    "DEF:fsstat_avg={file}:fsstat:AVERAGE",
+    "DEF:fsinfo_avg={file}:fsinfo:AVERAGE",
+    "DEF:pathconf_avg={file}:pathconf:AVERAGE",
+    "DEF:commit_avg={file}:commit:AVERAGE",
+    "DEF:null_max={file}:null:MAX",
+    "DEF:getattr_max={file}:getattr:MAX",
+    "DEF:setattr_max={file}:setattr:MAX",
+    "DEF:lookup_max={file}:lookup:MAX",
+    "DEF:access_max={file}:access:MAX",
+    "DEF:readlink_max={file}:readlink:MAX",
+    "DEF:read_max={file}:read:MAX",
+    "DEF:write_max={file}:write:MAX",
+    "DEF:create_max={file}:create:MAX",
+    "DEF:mkdir_max={file}:mkdir:MAX",
+    "DEF:symlink_max={file}:symlink:MAX",
+    "DEF:mknod_max={file}:mknod:MAX",
+    "DEF:remove_max={file}:remove:MAX",
+    "DEF:rmdir_max={file}:rmdir:MAX",
+    "DEF:rename_max={file}:rename:MAX",
+    "DEF:link_max={file}:link:MAX",
+    "DEF:readdir_max={file}:readdir:MAX",
+    "DEF:readdirplus_max={file}:readdirplus:MAX",
+    "DEF:fsstat_max={file}:fsstat:MAX",
+    "DEF:fsinfo_max={file}:fsinfo:MAX",
+    "DEF:pathconf_max={file}:pathconf:MAX",
+    "DEF:commit_max={file}:commit:MAX",
+    "CDEF:other_avg=null_avg,readlink_avg,create_avg,mkdir_avg,symlink_avg,mknod_avg,remove_avg,rmdir_avg,rename_avg,link_avg,readdir_avg,readdirplus_avg,fsstat_avg,fsinfo_avg,pathconf_avg,+,+,+,+,+,+,+,+,+,+,+,+,+,+",
+    "CDEF:other_max=null_max,readlink_max,create_max,mkdir_max,symlink_max,mknod_max,remove_max,rmdir_max,rename_max,link_max,readdir_max,readdirplus_max,fsstat_max,fsinfo_max,pathconf_max,+,+,+,+,+,+,+,+,+,+,+,+,+,+",
+    "CDEF:stack_read=read_avg",
+    "CDEF:stack_getattr=stack_read,getattr_avg,+",
+    "CDEF:stack_access=stack_getattr,access_avg,+",
+    "CDEF:stack_lookup=stack_access,lookup_avg,+",
+    "CDEF:stack_write=stack_lookup,write_avg,+",
+    "CDEF:stack_commit=stack_write,commit_avg,+",
+    "CDEF:stack_setattr=stack_commit,setattr_avg,+",
+    "CDEF:stack_other=stack_setattr,other_avg,+",
+    "AREA:stack_other#$HalfRed",
+    "AREA:stack_setattr#$HalfGreen",
+    "AREA:stack_commit#$HalfYellow",
+    "AREA:stack_write#$HalfGreen",
+    "AREA:stack_lookup#$HalfBlue",
+    "AREA:stack_access#$HalfMagenta",
+    "AREA:stack_getattr#$HalfCyan",
+    "AREA:stack_read#$HalfBlue",
+    "LINE1:stack_other#$FullRed:Other  ",
+    'GPRINT:other_max:MAX:%5.1lf Max,',
+    'GPRINT:other_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:other_avg:LAST:%5.1lf Last\l',
+    "LINE1:stack_setattr#$FullGreen:setattr",
+    'GPRINT:setattr_max:MAX:%5.1lf Max,',
+    'GPRINT:setattr_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:setattr_avg:LAST:%5.1lf Last\l',
+    "LINE1:stack_commit#$FullYellow:commit ",
+    'GPRINT:commit_max:MAX:%5.1lf Max,',
+    'GPRINT:commit_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:commit_avg:LAST:%5.1lf Last\l',
+    "LINE1:stack_write#$FullGreen:write  ",
+    'GPRINT:write_max:MAX:%5.1lf Max,',
+    'GPRINT:write_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:write_avg:LAST:%5.1lf Last\l',
+    "LINE1:stack_lookup#$FullBlue:lookup ",
+    'GPRINT:lookup_max:MAX:%5.1lf Max,',
+    'GPRINT:lookup_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:lookup_avg:LAST:%5.1lf Last\l',
+    "LINE1:stack_access#$FullMagenta:access ",
+    'GPRINT:access_max:MAX:%5.1lf Max,',
+    'GPRINT:access_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:access_avg:LAST:%5.1lf Last\l',
+    "LINE1:stack_getattr#$FullCyan:getattr",
+    'GPRINT:getattr_max:MAX:%5.1lf Max,',
+    'GPRINT:getattr_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:getattr_avg:LAST:%5.1lf Last\l',
+    "LINE1:stack_read#$FullBlue:read   ",
+    'GPRINT:read_max:MAX:%5.1lf Max,',
+    'GPRINT:read_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:read_avg:LAST:%5.1lf Last\l'
+    ],
+    opcode => [
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Queries/s",
+    'GPRINT:min:MIN:%9.3lf Min,',
+    'GPRINT:avg:AVERAGE:%9.3lf Average,',
+    'GPRINT:max:MAX:%9.3lf Max,',
+    'GPRINT:avg:LAST:%9.3lf Last\l'
+    ],
+    partition => [
+    "DEF:rbyte_avg={file}:rbytes:AVERAGE",
+    "DEF:rbyte_min={file}:rbytes:MIN",
+    "DEF:rbyte_max={file}:rbytes:MAX",
+    "DEF:wbyte_avg={file}:wbytes:AVERAGE",
+    "DEF:wbyte_min={file}:wbytes:MIN",
+    "DEF:wbyte_max={file}:wbytes:MAX",
+    'CDEF:overlap=wbyte_avg,rbyte_avg,GT,rbyte_avg,wbyte_avg,IF',
+    "AREA:wbyte_avg#$HalfGreen",
+    "AREA:rbyte_avg#$HalfBlue",
+    "AREA:overlap#$HalfBlueGreen",
+    "LINE1:wbyte_avg#$FullGreen:Write",
+    'GPRINT:wbyte_min:MIN:%5.1lf%s Min,',
+    'GPRINT:wbyte_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:wbyte_max:MAX:%5.1lf%s Max,',
+    'GPRINT:wbyte_avg:LAST:%5.1lf%s Last\l',
+    "LINE1:rbyte_avg#$FullBlue:Read ",
+    'GPRINT:rbyte_min:MIN:%5.1lf%s Min,',
+    'GPRINT:rbyte_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:rbyte_max:MAX:%5.1lf%s Max,',
+    'GPRINT:rbyte_avg:LAST:%5.1lf%s Last\l'
+    ],
+    percent => ['-v', 'Percent',
+    'DEF:avg={file}:percent:AVERAGE',
+    'DEF:min={file}:percent:MIN',
+    'DEF:max={file}:percent:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Percent",
+    'GPRINT:min:MIN:%5.1lf%% Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%% Avg,',
+    'GPRINT:max:MAX:%5.1lf%% Max,',
+    'GPRINT:avg:LAST:%5.1lf%% Last\l'
+    ],
+    ping => ['DEF:ping_avg={file}:ping:AVERAGE',
+    'DEF:ping_min={file}:ping:MIN',
+    'DEF:ping_max={file}:ping:MAX',
+    "AREA:ping_max#$HalfBlue",
+    "AREA:ping_min#$Canvas",
+    "LINE1:ping_avg#$FullBlue:Ping",
+    'GPRINT:ping_min:MIN:%4.1lf ms Min,',
+    'GPRINT:ping_avg:AVERAGE:%4.1lf ms Avg,',
+    'GPRINT:ping_max:MAX:%4.1lf ms Max,',
+    'GPRINT:ping_avg:LAST:%4.1lf ms Last'],
+    processes => [
+    "DEF:running_avg={file}:running:AVERAGE",
+    "DEF:running_min={file}:running:MIN",
+    "DEF:running_max={file}:running:MAX",
+    "DEF:sleeping_avg={file}:sleeping:AVERAGE",
+    "DEF:sleeping_min={file}:sleeping:MIN",
+    "DEF:sleeping_max={file}:sleeping:MAX",
+    "DEF:zombies_avg={file}:zombies:AVERAGE",
+    "DEF:zombies_min={file}:zombies:MIN",
+    "DEF:zombies_max={file}:zombies:MAX",
+    "DEF:stopped_avg={file}:stopped:AVERAGE",
+    "DEF:stopped_min={file}:stopped:MIN",
+    "DEF:stopped_max={file}:stopped:MAX",
+    "DEF:paging_avg={file}:paging:AVERAGE",
+    "DEF:paging_min={file}:paging:MIN",
+    "DEF:paging_max={file}:paging:MAX",
+    "DEF:blocked_avg={file}:blocked:AVERAGE",
+    "DEF:blocked_min={file}:blocked:MIN",
+    "DEF:blocked_max={file}:blocked:MAX",
+    'CDEF:paging_acc=sleeping_avg,running_avg,stopped_avg,zombies_avg,blocked_avg,paging_avg,+,+,+,+,+',
+    'CDEF:blocked_acc=sleeping_avg,running_avg,stopped_avg,zombies_avg,blocked_avg,+,+,+,+',
+    'CDEF:zombies_acc=sleeping_avg,running_avg,stopped_avg,zombies_avg,+,+,+',
+    'CDEF:stopped_acc=sleeping_avg,running_avg,stopped_avg,+,+',
+    'CDEF:running_acc=sleeping_avg,running_avg,+',
+    'CDEF:sleeping_acc=sleeping_avg',
+    "AREA:paging_acc#$HalfYellow",
+    "AREA:blocked_acc#$HalfCyan",
+    "AREA:zombies_acc#$HalfRed",
+    "AREA:stopped_acc#$HalfMagenta",
+    "AREA:running_acc#$HalfGreen",
+    "AREA:sleeping_acc#$HalfBlue",
+    "LINE1:paging_acc#$FullYellow:Paging  ",
+    'GPRINT:paging_min:MIN:%5.1lf Min,',
+    'GPRINT:paging_avg:AVERAGE:%5.1lf Average,',
+    'GPRINT:paging_max:MAX:%5.1lf Max,',
+    'GPRINT:paging_avg:LAST:%5.1lf Last\l',
+    "LINE1:blocked_acc#$FullCyan:Blocked ",
+    'GPRINT:blocked_min:MIN:%5.1lf Min,',
+    'GPRINT:blocked_avg:AVERAGE:%5.1lf Average,',
+    'GPRINT:blocked_max:MAX:%5.1lf Max,',
+    'GPRINT:blocked_avg:LAST:%5.1lf Last\l',
+    "LINE1:zombies_acc#$FullRed:Zombies ",
+    'GPRINT:zombies_min:MIN:%5.1lf Min,',
+    'GPRINT:zombies_avg:AVERAGE:%5.1lf Average,',
+    'GPRINT:zombies_max:MAX:%5.1lf Max,',
+    'GPRINT:zombies_avg:LAST:%5.1lf Last\l',
+    "LINE1:stopped_acc#$FullMagenta:Stopped ",
+    'GPRINT:stopped_min:MIN:%5.1lf Min,',
+    'GPRINT:stopped_avg:AVERAGE:%5.1lf Average,',
+    'GPRINT:stopped_max:MAX:%5.1lf Max,',
+    'GPRINT:stopped_avg:LAST:%5.1lf Last\l',
+    "LINE1:running_acc#$FullGreen:Running ",
+    'GPRINT:running_min:MIN:%5.1lf Min,',
+    'GPRINT:running_avg:AVERAGE:%5.1lf Average,',
+    'GPRINT:running_max:MAX:%5.1lf Max,',
+    'GPRINT:running_avg:LAST:%5.1lf Last\l',
+    "LINE1:sleeping_acc#$FullBlue:Sleeping",
+    'GPRINT:sleeping_min:MIN:%5.1lf Min,',
+    'GPRINT:sleeping_avg:AVERAGE:%5.1lf Average,',
+    'GPRINT:sleeping_max:MAX:%5.1lf Max,',
+    'GPRINT:sleeping_avg:LAST:%5.1lf Last\l'
+    ],
+    ps_rss => [
+    'DEF:avg={file}:byte:AVERAGE',
+    'DEF:min={file}:byte:MIN',
+    'DEF:max={file}:byte:MAX',
+    "AREA:avg#$HalfBlue",
+    "LINE1:avg#$FullBlue:RSS",
+    'GPRINT:min:MIN:%5.1lf%s Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:max:MAX:%5.1lf%s Max,',
+    'GPRINT:avg:LAST:%5.1lf%s Last\l'
+    ],
+    ps_cputime => [
+    'DEF:user_avg_raw={file}:user:AVERAGE',
+    'DEF:user_min_raw={file}:user:MIN',
+    'DEF:user_max_raw={file}:user:MAX',
+    'DEF:syst_avg_raw={file}:syst:AVERAGE',
+    'DEF:syst_min_raw={file}:syst:MIN',
+    'DEF:syst_max_raw={file}:syst:MAX',
+    'CDEF:user_avg=user_avg_raw,1000000,/',
+    'CDEF:user_min=user_min_raw,1000000,/',
+    'CDEF:user_max=user_max_raw,1000000,/',
+    'CDEF:syst_avg=syst_avg_raw,1000000,/',
+    'CDEF:syst_min=syst_min_raw,1000000,/',
+    'CDEF:syst_max=syst_max_raw,1000000,/',
+    'CDEF:user_syst=syst_avg,UN,0,syst_avg,IF,user_avg,+',
+    "AREA:user_syst#$HalfBlue",
+    "AREA:syst_avg#$HalfRed",
+    "LINE1:user_syst#$FullBlue:User  ",
+    'GPRINT:user_min:MIN:%5.1lf%s Min,',
+    'GPRINT:user_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:user_max:MAX:%5.1lf%s Max,',
+    'GPRINT:user_avg:LAST:%5.1lf%s Last\l',
+    "LINE1:syst_avg#$FullRed:System",
+    'GPRINT:syst_min:MIN:%5.1lf%s Min,',
+    'GPRINT:syst_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:syst_max:MAX:%5.1lf%s Max,',
+    'GPRINT:syst_avg:LAST:%5.1lf%s Last\l'
+    ],
+    ps_count => [
+    'DEF:procs_avg={file}:processes:AVERAGE',
+    'DEF:procs_min={file}:processes:MIN',
+    'DEF:procs_max={file}:processes:MAX',
+    'DEF:thrds_avg={file}:threads:AVERAGE',
+    'DEF:thrds_min={file}:threads:MIN',
+    'DEF:thrds_max={file}:threads:MAX',
+    "AREA:thrds_avg#$HalfBlue",
+    "AREA:procs_avg#$HalfRed",
+    "LINE1:thrds_avg#$FullBlue:Threads  ",
+    'GPRINT:thrds_min:MIN:%5.1lf Min,',
+    'GPRINT:thrds_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:thrds_max:MAX:%5.1lf Max,',
+    'GPRINT:thrds_avg:LAST:%5.1lf Last\l',
+    "LINE1:procs_avg#$FullRed:Processes",
+    'GPRINT:procs_min:MIN:%5.1lf Min,',
+    'GPRINT:procs_avg:AVERAGE:%5.1lf Avg,',
+    'GPRINT:procs_max:MAX:%5.1lf Max,',
+    'GPRINT:procs_avg:LAST:%5.1lf Last\l'
+    ],
+    ps_pagefaults => [
+    'DEF:minor_avg={file}:minflt:AVERAGE',
+    'DEF:minor_min={file}:minflt:MIN',
+    'DEF:minor_max={file}:minflt:MAX',
+    'DEF:major_avg={file}:majflt:AVERAGE',
+    'DEF:major_min={file}:majflt:MIN',
+    'DEF:major_max={file}:majflt:MAX',
+    'CDEF:minor_major=major_avg,UN,0,major_avg,IF,minor_avg,+',
+    "AREA:minor_major#$HalfBlue",
+    "AREA:major_avg#$HalfRed",
+    "LINE1:minor_major#$FullBlue:Minor",
+    'GPRINT:minor_min:MIN:%5.1lf%s Min,',
+    'GPRINT:minor_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:minor_max:MAX:%5.1lf%s Max,',
+    'GPRINT:minor_avg:LAST:%5.1lf%s Last\l',
+    "LINE1:major_avg#$FullRed:Major",
+    'GPRINT:major_min:MIN:%5.1lf%s Min,',
+    'GPRINT:major_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:major_max:MAX:%5.1lf%s Max,',
+    'GPRINT:major_avg:LAST:%5.1lf%s Last\l'
+    ],
+    ps_state => ['-v', 'Processes',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Processes",
+    'GPRINT:min:MIN:%6.2lf Min,',
+    'GPRINT:avg:AVERAGE:%6.2lf Avg,',
+    'GPRINT:max:MAX:%6.2lf Max,',
+    'GPRINT:avg:LAST:%6.2lf Last\l'
+    ],
+    qtype => [
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Queries/s",
+    'GPRINT:min:MIN:%9.3lf Min,',
+    'GPRINT:avg:AVERAGE:%9.3lf Average,',
+    'GPRINT:max:MAX:%9.3lf Max,',
+    'GPRINT:avg:LAST:%9.3lf Last\l'
+    ],
+    rcode => [
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Queries/s",
+    'GPRINT:min:MIN:%9.3lf Min,',
+    'GPRINT:avg:AVERAGE:%9.3lf Average,',
+    'GPRINT:max:MAX:%9.3lf Max,',
+    'GPRINT:avg:LAST:%9.3lf Last\l'
+    ],
+    swap => ['-v', 'Bytes', '-b', '1024',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Bytes",
+    'GPRINT:min:MIN:%6.2lf%sByte Min,',
+    'GPRINT:avg:AVERAGE:%6.2lf%sByte Avg,',
+    'GPRINT:max:MAX:%6.2lf%sByte Max,',
+    'GPRINT:avg:LAST:%6.2lf%sByte Last\l'
+    ],
+    ols_swap => [
+    'DEF:used_avg={file}:used:AVERAGE',
+    'DEF:used_min={file}:used:MIN',
+    'DEF:used_max={file}:used:MAX',
+    'DEF:free_avg={file}:free:AVERAGE',
+    'DEF:free_min={file}:free:MIN',
+    'DEF:free_max={file}:free:MAX',
+    'DEF:cach_avg={file}:cached:AVERAGE',
+    'DEF:cach_min={file}:cached:MIN',
+    'DEF:cach_max={file}:cached:MAX',
+    'DEF:resv_avg={file}:resv:AVERAGE',
+    'DEF:resv_min={file}:resv:MIN',
+    'DEF:resv_max={file}:resv:MAX',
+    'CDEF:cach_avg_notnull=cach_avg,UN,0,cach_avg,IF',
+    'CDEF:resv_avg_notnull=resv_avg,UN,0,resv_avg,IF',
+    'CDEF:used_acc=used_avg',
+    'CDEF:resv_acc=used_acc,resv_avg_notnull,+',
+    'CDEF:cach_acc=resv_acc,cach_avg_notnull,+',
+    'CDEF:free_acc=cach_acc,free_avg,+',
+    "AREA:free_acc#$HalfGreen",
+    "AREA:cach_acc#$HalfBlue",
+    "AREA:resv_acc#$HalfYellow",
+    "AREA:used_acc#$HalfRed",
+    "LINE1:free_acc#$FullGreen:Free    ",
+    'GPRINT:free_min:MIN:%5.1lf%s Min,',
+    'GPRINT:free_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:free_max:MAX:%5.1lf%s Max,',
+    'GPRINT:free_avg:LAST:%5.1lf%s Last\n',
+    "LINE1:cach_acc#$FullBlue:Cached  ",
+    'GPRINT:cach_min:MIN:%5.1lf%s Min,',
+    'GPRINT:cach_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:cach_max:MAX:%5.1lf%s Max,',
+    'GPRINT:cach_avg:LAST:%5.1lf%s Last\l',
+    "LINE1:resv_acc#$FullYellow:Reserved",
+    'GPRINT:resv_min:MIN:%5.1lf%s Min,',
+    'GPRINT:resv_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:resv_max:MAX:%5.1lf%s Max,',
+    'GPRINT:resv_avg:LAST:%5.1lf%s Last\n',
+    "LINE1:used_acc#$FullRed:Used    ",
+    'GPRINT:used_min:MIN:%5.1lf%s Min,',
+    'GPRINT:used_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:used_max:MAX:%5.1lf%s Max,',
+    'GPRINT:used_avg:LAST:%5.1lf%s Last\l'
+    ],
+    temperature => ['-v', 'Celsius',
+    'DEF:temp_avg={file}:value:AVERAGE',
+    'DEF:temp_min={file}:value:MIN',
+    'DEF:temp_max={file}:value:MAX',
+    "AREA:temp_max#$HalfRed",
+    "AREA:temp_min#$Canvas",
+    "LINE1:temp_avg#$FullRed:Temperature",
+    'GPRINT:temp_min:MIN:%4.1lf Min,',
+    'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
+    'GPRINT:temp_max:MAX:%4.1lf Max,',
+    'GPRINT:temp_avg:LAST:%4.1lf Last\l'
+    ],
+    timeleft => [
+    'DEF:avg={file}:timeleft:AVERAGE',
+    'DEF:min={file}:timeleft:MIN',
+    'DEF:max={file}:timeleft:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Time left [min]",
+    'GPRINT:min:MIN:%5.1lf%s Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:max:MAX:%5.1lf%s Max,',
+    'GPRINT:avg:LAST:%5.1lf%s Last\l'
+    ],
+    time_offset => [ # NTPd
+    'DEF:s_avg={file}:seconds:AVERAGE',
+    'DEF:s_min={file}:seconds:MIN',
+    'DEF:s_max={file}:seconds:MAX',
+    "AREA:s_max#$HalfBlue",
+    "AREA:s_min#$Canvas",
+    "LINE1:s_avg#$FullBlue:{inst}",
+    'GPRINT:s_min:MIN:%7.3lf%s Min,',
+    'GPRINT:s_avg:AVERAGE:%7.3lf%s Avg,',
+    'GPRINT:s_max:MAX:%7.3lf%s Max,',
+    'GPRINT:s_avg:LAST:%7.3lf%s Last'
+    ],
+    if_octets => ['-v', 'Bits/s',
+    'DEF:out_min_raw={file}:tx:MIN',
+    'DEF:out_avg_raw={file}:tx:AVERAGE',
+    'DEF:out_max_raw={file}:tx:MAX',
+    'DEF:inc_min_raw={file}:rx:MIN',
+    'DEF:inc_avg_raw={file}:rx:AVERAGE',
+    'DEF:inc_max_raw={file}:rx:MAX',
+    'CDEF:out_min=out_min_raw,8,*',
+    'CDEF:out_avg=out_avg_raw,8,*',
+    'CDEF:out_max=out_max_raw,8,*',
+    'CDEF:inc_min=inc_min_raw,8,*',
+    'CDEF:inc_avg=inc_avg_raw,8,*',
+    'CDEF:inc_max=inc_max_raw,8,*',
+    'CDEF:overlap=out_avg,inc_avg,GT,inc_avg,out_avg,IF',
+    'CDEF:mytime=out_avg_raw,TIME,TIME,IF',
+    'CDEF:sample_len_raw=mytime,PREV(mytime),-',
+    'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
+    'CDEF:out_avg_sample=out_avg_raw,UN,0,out_avg_raw,IF,sample_len,*',
+    'CDEF:out_avg_sum=PREV,UN,0,PREV,IF,out_avg_sample,+',
+    'CDEF:inc_avg_sample=inc_avg_raw,UN,0,inc_avg_raw,IF,sample_len,*',
+    'CDEF:inc_avg_sum=PREV,UN,0,PREV,IF,inc_avg_sample,+',
+    "AREA:out_avg#$HalfGreen",
+    "AREA:inc_avg#$HalfBlue",
+    "AREA:overlap#$HalfBlueGreen",
+    "LINE1:out_avg#$FullGreen:Outgoing",
+    'GPRINT:out_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:out_max:MAX:%5.1lf%s Max,',
+    'GPRINT:out_avg:LAST:%5.1lf%s Last',
+    'GPRINT:out_avg_sum:LAST:(ca. %5.1lf%sB Total)\l',
+    "LINE1:inc_avg#$FullBlue:Incoming",
+    #'GPRINT:inc_min:MIN:%5.1lf %s Min,',
+    'GPRINT:inc_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:inc_max:MAX:%5.1lf%s Max,',
+    'GPRINT:inc_avg:LAST:%5.1lf%s Last',
+    'GPRINT:inc_avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
+    ],
+    cpufreq => [
+    'DEF:cpufreq_avg={file}:value:AVERAGE',
+    'DEF:cpufreq_min={file}:value:MIN',
+    'DEF:cpufreq_max={file}:value:MAX',
+    "AREA:cpufreq_max#$HalfBlue",
+    "AREA:cpufreq_min#$Canvas",
+    "LINE1:cpufreq_avg#$FullBlue:Frequency",
+    'GPRINT:cpufreq_min:MIN:%5.1lf%s Min,',
+    'GPRINT:cpufreq_avg:AVERAGE:%5.1lf%s Avg,',
+    'GPRINT:cpufreq_max:MAX:%5.1lf%s Max,',
+    'GPRINT:cpufreq_avg:LAST:%5.1lf%s Last\l'
+    ],
+    multimeter => [
+    'DEF:multimeter_avg={file}:value:AVERAGE',
+    'DEF:multimeter_min={file}:value:MIN',
+    'DEF:multimeter_max={file}:value:MAX',
+    "AREA:multimeter_max#$HalfBlue",
+    "AREA:multimeter_min#$Canvas",
+    "LINE1:multimeter_avg#$FullBlue:Multimeter",
+    'GPRINT:multimeter_min:MIN:%4.1lf Min,',
+    'GPRINT:multimeter_avg:AVERAGE:%4.1lf Average,',
+    'GPRINT:multimeter_max:MAX:%4.1lf Max,',
+    'GPRINT:multimeter_avg:LAST:%4.1lf Last\l'
+    ],
+    users => [
+    'DEF:users_avg={file}:users:AVERAGE',
+    'DEF:users_min={file}:users:MIN',
+    'DEF:users_max={file}:users:MAX',
+    "AREA:users_max#$HalfBlue",
+    "AREA:users_min#$Canvas",
+    "LINE1:users_avg#$FullBlue:Users",
+    'GPRINT:users_min:MIN:%4.1lf Min,',
+    'GPRINT:users_avg:AVERAGE:%4.1lf Average,',
+    'GPRINT:users_max:MAX:%4.1lf Max,',
+    'GPRINT:users_avg:LAST:%4.1lf Last\l'
+    ],
+    voltage => ['-v', 'Voltage',
+    'DEF:avg={file}:value:AVERAGE',
+    'DEF:min={file}:value:MIN',
+    'DEF:max={file}:value:MAX',
+    "AREA:max#$HalfBlue",
+    "AREA:min#$Canvas",
+    "LINE1:avg#$FullBlue:Voltage",
+    'GPRINT:min:MIN:%5.1lf%sV Min,',
+    'GPRINT:avg:AVERAGE:%5.1lf%sV Avg,',
+    'GPRINT:max:MAX:%5.1lf%sV Max,',
+    'GPRINT:avg:LAST:%5.1lf%sV Last\l'
+    ],
+    vs_threads => [
+    "DEF:total_avg={file}:total:AVERAGE",
+    "DEF:total_min={file}:total:MIN",
+    "DEF:total_max={file}:total:MAX",
+    "DEF:running_avg={file}:running:AVERAGE",
+    "DEF:running_min={file}:running:MIN",
+    "DEF:running_max={file}:running:MAX",
+    "DEF:uninterruptible_avg={file}:uninterruptible:AVERAGE",
+    "DEF:uninterruptible_min={file}:uninterruptible:MIN",
+    "DEF:uninterruptible_max={file}:uninterruptible:MAX",
+    "DEF:onhold_avg={file}:onhold:AVERAGE",
+    "DEF:onhold_min={file}:onhold:MIN",
+    "DEF:onhold_max={file}:onhold:MAX",
+    "LINE1:total_avg#$FullYellow:Total   ",
+    'GPRINT:total_min:MIN:%5.1lf Min,',
+    'GPRINT:total_avg:AVERAGE:%5.1lf Avg.,',
+    'GPRINT:total_max:MAX:%5.1lf Max,',
+    'GPRINT:total_avg:LAST:%5.1lf Last\l',
+    "LINE1:running_avg#$FullRed:Running ",
+    'GPRINT:running_min:MIN:%5.1lf Min,',
+    'GPRINT:running_avg:AVERAGE:%5.1lf Avg.,',          
+    'GPRINT:running_max:MAX:%5.1lf Max,',
+    'GPRINT:running_avg:LAST:%5.1lf Last\l',
+    "LINE1:uninterruptible_avg#$FullGreen:Unintr  ",
+    'GPRINT:uninterruptible_min:MIN:%5.1lf Min,',
+    'GPRINT:uninterruptible_avg:AVERAGE:%5.1lf Avg.,',
+    'GPRINT:uninterruptible_max:MAX:%5.1lf Max,',
+    'GPRINT:uninterruptible_avg:LAST:%5.1lf Last\l',
+    "LINE1:onhold_avg#$FullBlue:Onhold  ",
+    'GPRINT:onhold_min:MIN:%5.1lf Min,',
+    'GPRINT:onhold_avg:AVERAGE:%5.1lf Avg.,',
+    'GPRINT:onhold_max:MAX:%5.1lf Max,',
+    'GPRINT:onhold_avg:LAST:%5.1lf Last\l'
+    ],
+    vs_memory => [
+    'DEF:vm_avg={file}:vm:AVERAGE',
+    'DEF:vm_min={file}:vm:MIN',
+    'DEF:vm_max={file}:vm:MAX',
+    'DEF:vml_avg={file}:vml:AVERAGE',
+    'DEF:vml_min={file}:vml:MIN',
+    'DEF:vml_max={file}:vml:MAX',
+    'DEF:rss_avg={file}:rss:AVERAGE',
+    'DEF:rss_min={file}:rss:MIN',
+    'DEF:rss_max={file}:rss:MAX',
+    'DEF:anon_avg={file}:anon:AVERAGE',
+    'DEF:anon_min={file}:anon:MIN',
+    'DEF:anon_max={file}:anon:MAX',
+    "LINE1:vm_avg#$FullYellow:VM     ",
+    'GPRINT:vm_min:MIN:%5.1lf%s Min,',
+    'GPRINT:vm_avg:AVERAGE:%5.1lf%s Avg.,',
+    'GPRINT:vm_max:MAX:%5.1lf%s Avg.,',
+    'GPRINT:vm_avg:LAST:%5.1lf%s Last\l',
+    "LINE1:vml_avg#$FullRed:Locked ",
+    'GPRINT:vml_min:MIN:%5.1lf%s Min,',
+    'GPRINT:vml_avg:AVERAGE:%5.1lf%s Avg.,',
+    'GPRINT:vml_max:MAX:%5.1lf%s Avg.,',
+    'GPRINT:vml_avg:LAST:%5.1lf%s Last\l',
+    "LINE1:rss_avg#$FullGreen:RSS    ",
+    'GPRINT:rss_min:MIN:%5.1lf%s Min,',
+    'GPRINT:rss_avg:AVERAGE:%5.1lf%s Avg.,',
+    'GPRINT:rss_max:MAX:%5.1lf%s Avg.,',
+    'GPRINT:rss_avg:LAST:%5.1lf%s Last\l',
+    "LINE1:anon_avg#$FullBlue:Anon.  ",
+    'GPRINT:anon_min:MIN:%5.1lf%s Min,',
+    'GPRINT:anon_avg:AVERAGE:%5.1lf%s Avg.,',
+    'GPRINT:anon_max:MAX:%5.1lf%s Avg.,',
+    'GPRINT:anon_avg:LAST:%5.1lf%s Last\l',
+    ],
+    vs_processes => [
+    'DEF:proc_avg={file}:total:AVERAGE',
+    'DEF:proc_min={file}:total:MIN',
+    'DEF:proc_max={file}:total:MAX',
+    "AREA:proc_max#$HalfBlue",
+    "AREA:proc_min#$Canvas",
+    "LINE1:proc_avg#$FullBlue:Processes",
+    'GPRINT:proc_min:MIN:%4.1lf Min,',
+    'GPRINT:proc_avg:AVERAGE:%4.1lf Avg.,',
+    'GPRINT:proc_max:MAX:%4.1lf Max,',
+    'GPRINT:proc_avg:LAST:%4.1lf Last\l'
+    ],
+  };
+} # load_graph_definitions
+# vim: shiftwidth=2:softtabstop=2:tabstop=8
diff --git a/contrib/extractDS.px b/contrib/extractDS.px
new file mode 100755 (executable)
index 0000000..bdc4b3d
--- /dev/null
@@ -0,0 +1,252 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+=head1 NAME
+
+extractDS.px - Extract a single data-source from an RRD-file
+
+=head1 SYNOPSYS
+
+  extractDS.px -i input.rrd -s source_ds -o output.rrd -d destination_ds
+
+=head1 DEPENDENCIES
+
+extractDS.px requires Perl and the included L<Getopt::Long> module, as well as
+the L<XML::Simple> module.
+
+=cut
+
+use Getopt::Long ('GetOptions');
+use Data::Dumper ();
+
+our $InFile;
+our $InDS = [];
+our $OutFile;
+our $OutDS = [];
+
+GetOptions ("infile|i=s" => \$InFile,
+       "inds|s=s" => sub { push (@$InDS, $_[1]); },
+       "outfile|o=s" => \$OutFile,
+       "outds|d=s" => sub { push (@$OutDS, $_[1]); })
+       or exit (1);
+
+if (!$InFile || !$OutFile || !@$InDS || !@$OutDS)
+{
+       print STDERR "Usage: $0 -i <infile> -s <inds> -o <outfile> -d <outds>\n";
+       exit (1);
+}
+if (!-f $InFile)
+{
+       print STDERR "Input file does not exist\n";
+       exit (1);
+}
+if (-f $OutFile)
+{
+       print STDERR "Output file does exist\n";
+       exit (1);
+}
+if ((1 + @$InDS) != (1 + @$OutDS))
+{
+       print STDERR "You need the same amount of in- and out-DSes\n";
+       exit (1);
+}
+
+extract_ds ($InFile, $OutFile);
+exit (0);
+
+{
+my $ds_index;
+my $current_index;
+# state 0 == searching for DS index
+# state 1 == parse RRA header
+# state 2 == parse values
+my $state;
+my $out_cache;
+sub handle_line
+{
+       my $fh = shift;
+       my $line = shift;
+
+       if (!defined ($state))
+       {
+               $current_index = -1;
+               $state = 0;
+               $out_cache = [];
+
+               # $ds_index->[new_index] = old_index
+               $ds_index = [];
+               for (my $i = 0; $i < @$InDS; $i++)
+               {
+                       $ds_index->[$i] = -1;
+               }
+       }
+
+       if ($state == 0)
+       {
+               if ($line =~ m/<ds>/)
+               {
+                       $current_index++;
+                       $out_cache->[$current_index] = $line;
+               }
+               elsif ($line =~ m#<name>\s*([^<\s]+)\s*</name>#)
+               {
+                       # old_index == $current_index
+                       # new_index == $i
+                       for (my $i = 0; $i < @$InDS; $i++)
+                       {
+                               next if ($ds_index->[$i] >= 0);
+
+                               if ($1 eq $InDS->[$i])
+                               {
+                                       $line =~ s#<name>\s*([^<\s]+)\s*</name>#<name> $OutDS->[$i] </name>#;
+                                       $ds_index->[$i] = $current_index;
+                                       last;
+                               }
+                       }
+
+                       $out_cache->[$current_index] .= $line;
+               }
+               elsif ($line =~ m#</ds>#)
+               {
+                       $out_cache->[$current_index] .= $line;
+               }
+               elsif ($line =~ m#<rra>#)
+               {
+                       # Print out all the DS definitions we need
+                       for (my $new_index = 0; $new_index < @$InDS; $new_index++)
+                       {
+                               my $old_index = $ds_index->[$new_index];
+                               print $fh $out_cache->[$old_index];
+                       }
+
+                       # Clear the cache - it's used in state1, too.
+                       for (my $i = 0; $i <= $current_index; $i++)
+                       {
+                               $out_cache->[$i] = '';
+                       }
+
+                       print $fh $line;
+                       $current_index = -1;
+                       $state = 1;
+               }
+               elsif ($current_index == -1)
+               {
+                       # Print all the lines before the first DS definition
+                       print $fh $line;
+               }
+               else
+               {
+                       # Something belonging to a DS-definition
+                       $out_cache->[$current_index] .= $line;
+               }
+       }
+       elsif ($state == 1)
+       {
+               if ($line =~ m#<ds>#)
+               {
+                       $current_index++;
+                       $out_cache->[$current_index] .= $line;
+               }
+               elsif ($line =~ m#</cdp_prep>#)
+               {
+                       # Print out all the DS definitions we need
+                       for (my $new_index = 0; $new_index < @$InDS; $new_index++)
+                       {
+                               my $old_index = $ds_index->[$new_index];
+                               print $fh $out_cache->[$old_index];
+                       }
+
+                       # Clear the cache
+                       for (my $i = 0; $i <= $current_index; $i++)
+                       {
+                               $out_cache->[$i] = '';
+                       }
+
+                       print $fh $line;
+                       $current_index = -1;
+               }
+               elsif ($line =~ m#<database>#)
+               {
+                       print $fh $line;
+                       $state = 2;
+               }
+               elsif ($current_index == -1)
+               {
+                       # Print all the lines before the first DS definition
+                       # and after cdp_prep
+                       print $fh $line;
+               }
+               else
+               {
+                       # Something belonging to a DS-definition
+                       $out_cache->[$current_index] .= $line;
+               }
+       }
+       elsif ($state == 2)
+       {
+               if ($line =~ m#</database>#)
+               {
+                       print $fh $line;
+                       $current_index = -1;
+                       $state = 1;
+               }
+               else
+               {
+                       my @values = ();
+                       my $i;
+                       my $output = "\t\t";
+
+                       if ($line =~ m#(<!-- .*? -->)#)
+                       {
+                               $output .= "$1 ";
+                       }
+                       $output .= "<row> ";
+
+                       $i = 0;
+                       while ($line =~ m#<v>\s*([^<\s]+)\s*</v>#g)
+                       {
+                               $values[$i] = $1;
+                               $i++;
+                       }
+
+                       for (my $new_index = 0; $new_index < @$InDS; $new_index++)
+                       {
+                               my $old_index = $ds_index->[$new_index];
+                               $output .= '<v> ' . $values[$old_index] . ' </v> ';
+                       }
+                       $output .= "</row>\n";
+                       print $fh $output;
+               }
+       }
+       else
+       {
+               die;
+       }
+}} # handle_line
+
+sub extract_ds
+{
+       my $in_file = shift;
+       my $out_file = shift;
+
+       my $in_fh;
+       my $out_fh;
+
+       open ($in_fh,  '-|', 'rrdtool', 'dump', $in_file) or die ("open (rrdtool): $!");
+       open ($out_fh, '|-', 'rrdtool', 'restore', '-', $out_file) or die ("open (rrdtool): $!");
+
+       while (my $line = <$in_fh>)
+       {
+               handle_line ($out_fh, $line);
+       }
+
+       close ($in_fh);
+       close ($out_fh);
+} # extract_ds
+
+=head1 AUTHOR
+
+Florian octo Forster E<lt>octo at verplant.orgE<gt>
+
diff --git a/contrib/fedora/collectd.spec b/contrib/fedora/collectd.spec
new file mode 100644 (file)
index 0000000..9cc9f2b
--- /dev/null
@@ -0,0 +1,238 @@
+Summary:       Statistics collection daemon for filling RRD files.
+Name:           collectd
+Version:       3.11.2
+Release:       0.fc0
+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
+Requires:      rrdtool
+Packager:      Florian octo Forster <octo@verplant.org>
+Vendor:                Florian octo Forster <octo@verplant.org>
+
+%description
+collectd is a small daemon written in C for performance.  It reads various
+system  statistics  and updates  RRD files,  creating  them if neccessary.
+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.
+
+%package apache
+Summary:       apache-plugin for collectd.
+Group:         System Environment/Daemons
+Requires:      collectd = %{version}, libcurl3
+%description apache
+This plugin collectd data provided by Apache's `mod_status'.
+
+#%package email
+#Summary:      email-plugin for collectd.
+#Group:                System Environment/Daemons
+#Requires:     collectd = %{version}, spamassassin
+#%description email
+#This plugin collectd data provided by spamassassin.
+
+%package mysql
+Summary:       mysql-module for collectd.
+Group:         System Environment/Daemons
+Requires:      collectd = %{version}, mysql
+%description mysql
+MySQL  querying  plugin.  This plugins  provides data of  issued commands,
+called handlers and database traffic.
+
+%package sensors
+Summary:       libsensors-module for collectd.
+Group:         System Environment/Daemons
+Requires:      collectd = %{version}, lm_sensors
+%description sensors
+This  plugin  for  collectd  provides  querying  of sensors  supported  by
+lm_sensors.
+
+%prep
+rm -rf $RPM_BUILD_ROOT
+%setup
+
+%build
+./configure --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir}
+make
+
+%install
+make install DESTDIR=$RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+mkdir -p $RPM_BUILD_ROOT/var/www/cgi-bin
+cp src/collectd.conf $RPM_BUILD_ROOT/etc/collectd.conf
+cp contrib/fedora/init.d-collectd $RPM_BUILD_ROOT/etc/rc.d/init.d/collectd
+cp contrib/collection.cgi $RPM_BUILD_ROOT/var/www/cgi-bin
+mkdir -p $RPM_BUILD_ROOT/var/lib/collectd
+rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/*.a
+rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/*.la
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/sbin/chkconfig --add collectd
+/sbin/chkconfig collectd on
+
+#%postun
+#/sbin/chkconfig collectd off
+#/sbin/chkconfig --del collectd
+
+%files
+%defattr(-,root,root)
+%doc AUTHORS COPYING ChangeLog INSTALL NEWS README
+%config /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(0444,root,root) %{_mandir}/man1/*
+%attr(0444,root,root) %{_mandir}/man5/*
+%attr(0444,root,root) %{_libdir}/%{name}/apcups.so*
+%attr(0444,root,root) %{_libdir}/%{name}/apple_sensors.so*
+%attr(0444,root,root) %{_libdir}/%{name}/battery.so*
+%attr(0444,root,root) %{_libdir}/%{name}/cpu.so*
+%attr(0444,root,root) %{_libdir}/%{name}/cpufreq.so*
+%attr(0444,root,root) %{_libdir}/%{name}/df.so*
+%attr(0444,root,root) %{_libdir}/%{name}/disk.so*
+%attr(0444,root,root) %{_libdir}/%{name}/dns.so
+%attr(0444,root,root) %{_libdir}/%{name}/email.so
+%attr(0444,root,root) %{_libdir}/%{name}/hddtemp.so*
+%attr(0444,root,root) %{_libdir}/%{name}/irq.so*
+%attr(0444,root,root) %{_libdir}/%{name}/load.so*
+%attr(0444,root,root) %{_libdir}/%{name}/mbmon.so
+%attr(0444,root,root) %{_libdir}/%{name}/memory.so*
+%attr(0444,root,root) %{_libdir}/%{name}/multimeter.so*
+%attr(0444,root,root) %{_libdir}/%{name}/nfs.so*
+%attr(0444,root,root) %{_libdir}/%{name}/ntpd.so*
+%attr(0444,root,root) %{_libdir}/%{name}/ping.so*
+%attr(0444,root,root) %{_libdir}/%{name}/processes.so*
+%attr(0444,root,root) %{_libdir}/%{name}/serial.so*
+%attr(0444,root,root) %{_libdir}/%{name}/swap.so*
+%attr(0444,root,root) %{_libdir}/%{name}/tape.so*
+%attr(0444,root,root) %{_libdir}/%{name}/traffic.so*
+%attr(0444,root,root) %{_libdir}/%{name}/users.so*
+%attr(0444,root,root) %{_libdir}/%{name}/vserver.so*
+%attr(0444,root,root) %{_libdir}/%{name}/wireless.so*
+
+%dir /var/lib/collectd
+
+%files apache
+%attr(0444,root,root) %{_libdir}/%{name}/apache.so*
+
+#%files email
+#%attr(0444,root,root) %{_libdir}/%{name}/email.so
+
+%files mysql
+%attr(0444,root,root) %{_libdir}/%{name}/mysql.so*
+
+%files sensors
+%attr(0444,root,root) %{_libdir}/%{name}/sensors.so*
+
+%changelog
+* Wed Jan 11 2007 Iain Lea <iain@bricbrac.de> 3.11.0-0
+- fixed spec file to build correctly on fedora core
+- added improved init.d script to work with chkconfig
+- added %post and %postun to call chkconfig automatically
+
+* Sun Jul 09 2006 Florian octo Forster <octo@verplant.org> 3.10.0-1
+- New upstream version
+
+* Tue Jun 25 2006 Florian octo Forster <octo@verplant.org> 3.9.4-1
+- New upstream version
+
+* Tue Jun 01 2006 Florian octo Forster <octo@verplant.org> 3.9.3-1
+- New upstream version
+
+* Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.9.2-1
+- New upstream version
+
+* Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.8.5-1
+- New upstream version
+
+* Fri Apr 21 2006 Florian octo Forster <octo@verplant.org> 3.9.1-1
+- New upstream version
+
+* Fri Apr 14 2006 Florian octo Forster <octo@verplant.org> 3.9.0-1
+- New upstream version
+- Added the `apache' package.
+
+* Thu Mar 14 2006 Florian octo Forster <octo@verplant.org> 3.8.2-1
+- New upstream version
+
+* Thu Mar 13 2006 Florian octo Forster <octo@verplant.org> 3.8.1-1
+- New upstream version
+
+* Thu Mar 09 2006 Florian octo Forster <octo@verplant.org> 3.8.0-1
+- New upstream version
+
+* Sat Feb 18 2006 Florian octo Forster <octo@verplant.org> 3.7.2-1
+- Include `tape.so' so the build doesn't terminate because of missing files..
+- New upstream version
+
+* Sat Feb 04 2006 Florian octo Forster <octo@verplant.org> 3.7.1-1
+- New upstream version
+
+* Mon Jan 30 2006 Florian octo Forster <octo@verplant.org> 3.7.0-1
+- New upstream version
+- Removed the extra `hddtemp' package
+
+* Tue Jan 24 2006 Florian octo Forster <octo@verplant.org> 3.6.2-1
+- New upstream version
+
+* Fri Jan 20 2006 Florian octo Forster <octo@verplant.org> 3.6.1-1
+- New upstream version
+
+* Fri Jan 20 2006 Florian octo Forster <octo@verplant.org> 3.6.0-1
+- New upstream version
+- Added config file, `collectd.conf(5)', `df.so'
+- Added package `collectd-mysql', dependency on `mysqlclient10 | mysql'
+
+* Wed Dec 07 2005 Florian octo Forster <octo@verplant.org> 3.5.0-1
+- New upstream version
+
+* Sat Nov 26 2005 Florian octo Forster <octo@verplant.org> 3.4.0-1
+- New upstream version
+
+* Sat Nov 05 2005 Florian octo Forster <octo@verplant.org> 3.3.0-1
+- New upstream version
+
+* Tue Oct 26 2005 Florian octo Forster <octo@verplant.org> 3.2.0-1
+- New upstream version
+- Added statement to remove the `*.la' files. This fixes a problem when
+  `Unpackaged files terminate build' is in effect.
+- Added `processes.so*' to the main package
+
+* Fri Oct 14 2005 Florian octo Forster <octo@verplant.org> 3.1.0-1
+- New upstream version
+- Added package `collectd-hddtemp'
+
+* Fri Sep 30 2005 Florian octo Forster <octo@verplant.org> 3.0.0-1
+- New upstream version
+- Split the package into `collectd' and `collectd-sensors'
+
+* Fri Sep 16 2005 Florian octo Forster <octo@verplant.org> 2.1.0-1
+- New upstream version
+
+* Mon Sep 10 2005 Florian octo Forster <octo@verplant.org> 2.0.0-1
+- New upstream version
+
+* Mon Aug 29 2005 Florian octo Forster <octo@verplant.org> 1.8.0-1
+- New upstream version
+
+* Sun Aug 25 2005 Florian octo Forster <octo@verplant.org> 1.7.0-1
+- New upstream version
+
+* Sun Aug 21 2005 Florian octo Forster <octo@verplant.org> 1.6.0-1
+- New upstream version
+
+* Sun Jul 17 2005 Florian octo Forster <octo@verplant.org> 1.5.1-1
+- New upstream version
+
+* Sun Jul 17 2005 Florian octo Forster <octo@verplant.org> 1.5-1
+- New upstream version
+
+* Mon Jul 11 2005 Florian octo Forster <octo@verplant.org> 1.4.2-1
+- New upstream version
+
+* Sat Jul 09 2005 Florian octo Forster <octo@verplant.org> 1.4-1
+- Built on RedHat 7.3
diff --git a/contrib/fedora/init.d-collectd b/contrib/fedora/init.d-collectd
new file mode 100644 (file)
index 0000000..2bf877c
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/bash
+#
+# collectd    Startup script for the Collectd statistics gathering daemon
+# chkconfig: - 86 15
+# description: Collectd is a statistics gathering daemon used to collect \
+#   system information ie. cpu, memory, disk, network
+# processname: collectd
+# config: /etc/collectd.conf
+# config: /etc/sysconfig/collectd
+# pidfile: /var/run/collectd.pid
+
+# Source function library.
+. /etc/init.d/functions
+
+RETVAL=0
+ARGS=""
+prog="collectd"
+CONFIG=/etc/collectd.conf
+
+if [ -r /etc/default/$prog ]; then
+       . /etc/default/$prog
+fi
+
+start () {
+       echo -n $"Starting $prog: "
+       if [ -r "$CONFIG" ]
+       then
+               daemon /usr/sbin/collectd -C "$CONFIG"
+               RETVAL=$?
+               echo
+               [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
+       fi
+}
+stop () {
+       echo -n $"Stopping $prog: "
+       killproc $prog
+       RETVAL=$?
+       echo
+       [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
+}
+# See how we were called.
+case "$1" in
+  start)
+       start
+       ;;
+  stop)
+       stop
+       ;;
+  status)
+       status $prog
+       ;;
+  restart|reload)
+       stop
+       start
+       ;;
+  condrestart)
+       [ -f /var/lock/subsys/$prog ] && restart || :
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
+       exit 1
+esac
+
+exit $?
+
+# vim:syntax=sh
diff --git a/contrib/init.d-rh7 b/contrib/init.d-rh7
deleted file mode 100755 (executable)
index c7fe270..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/bin/bash
-
-# Source function library.
-. /etc/init.d/functions
-
-RETVAL=0
-ARGS=""
-prog="collectd"
-CONFIG=/etc/collectd.conf
-
-if [ -r /etc/default/$prog ]; then
-       . /etc/default/$prog
-fi
-
-start () {
-       echo -n $"Starting $prog: "
-       if [ -r "$CONFIG" ]
-       then
-               daemon /usr/sbin/collectd -C "$CONFIG"
-               RETVAL=$?
-               echo
-               [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
-       fi
-}
-stop () {
-       echo -n $"Stopping $prog: "
-       killproc $prog
-       RETVAL=$?
-       echo
-       [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
-}
-# See how we were called.
-case "$1" in
-  start)
-       start
-       ;;
-  stop)
-       stop
-       ;;
-  status)
-       status $prog
-       ;;
-  restart|reload)
-       stop
-       sleep 1
-       start
-       ;;
-  condrestart)
-       [ -f /var/lock/subsys/$prog ] && restart || :
-       ;;
-  *)
-       echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
-       exit 1
-esac
-
-exit $?
-
-# vim:syntax=sh
diff --git a/contrib/iptables/accounting.sh b/contrib/iptables/accounting.sh
new file mode 100755 (executable)
index 0000000..a9a3be9
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+#Simple script that sets up some chains in mangle table to do global logging of all 
+#traffic going in and out of an interface
+#Could also use the regular input/output tree but this also catches the forwarded nat traffic
+
+IT="iptables -t mangle"
+
+#First clear the old stuff
+$IT -F incoming
+$IT -F outgoing
+$IT -N incoming
+$IT -N outgoing
+
+$IT -D PREROUTING -i eth0 -j incoming
+$IT -D POSTROUTING -o eth0 -j outgoing
+
+#should add some arg == stop exit here...
+
+$IT -A PREROUTING -i eth0 -j incoming
+$IT -A POSTROUTING -o eth0 -j outgoing
+
+$IT -A incoming -p tcp -m comment --comment "tcp"
+$IT -A incoming -p udp -m comment --comment "udp"
+$IT -A incoming -p icmp -m comment --comment "icmp"
+
+$IT -A outgoing -p tcp -m comment --comment "tcp"
+$IT -A outgoing -p udp -m comment --comment "udp"
+$IT -A outgoing -p icmp -m comment --comment "icmp"
+
diff --git a/contrib/migrate-3-4.px b/contrib/migrate-3-4.px
new file mode 100755 (executable)
index 0000000..31abae4
--- /dev/null
@@ -0,0 +1,384 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Getopt::Long ('GetOptions');
+use Data::Dumper ();
+use File::Basename ('dirname');
+
+our $InDir = '/var/lib/collectd';
+our $OutDir = '/tmp/collectd-4';
+our $Hostname = 'localhost';
+
+# Types:
+# +------------+----------------------+----+----+----+
+# ! Subdir     ! Type                 ! ti ! pi ! ex !
+# +------------+----------------------+----+----+----+
+# ! apache     ! apache_bytes         !    !    !    !
+# ! apache     ! apache_requests      !    !    !    !
+# ! apache     ! apache_scoreboard    ! x  !    !    !
+# ! battery    ! charge               !    ! x  !    !
+# ! apcups     ! charge_percent       !    !    !    !
+# !            ! cpu                  ! x  !    ! x  !
+# !            ! cpufreq              ! x  !    !    !
+# ! battery    ! current              !    ! x  !    !
+# ! ntpd       ! delay                ! x  !    !    !
+# !            ! df                   ! x  !    !    !
+# !            ! disk                 ! x  !    !    !
+# ! dns        ! dns_traffic          !    !    !    !
+# ! apple_se.. ! fanspeed             ! x  !    !    !
+# ! mbmon      ! fanspeed             ! x  !    !    !
+# ! apcups     ! frequency            ! x  !    !    !
+# ! ntpd       ! frequency_offset     ! x  !    !    !
+# !            ! hddtemp              ! x  !    !    !
+# ! interface  ! if_errors            !    ! x  !    !
+# ! interface  ! if_packets           !    ! x  !    !
+# !            ! lm_sensors           !    !    !    !
+# !            ! load                 !    !    !    !
+# ! apcups     ! load_percent         !    !    !    !
+# !            ! memory               !    !    !    !
+# !            ! multimeter           !    !    !    !
+# ! mysql      ! mysql_commands       ! x  !    !    !
+# ! mysql      ! mysql_handler        ! x  !    !    !
+# ! mysql      ! mysql_qcache         !    !    !    !
+# ! mysql      ! mysql_threads        !    !    !    !
+# !            ! nfs2_procedures      ! x  !    ! x  !
+# !            ! nfs3_procedures      ! x  !    ! x  !
+# ! dns        ! opcode               ! x  !    !    !
+# !            ! partition            ! x  !    !    !
+# !            ! ping                 ! x  !    !    !
+# !            ! processes            !    !    !    !
+# ! processes  ! ps_count             ! x  !    !    !
+# ! processes  ! ps_cputime           ! x  !    !    !
+# ! processes  ! ps_pagefaults        ! x  !    !    !
+# ! processes  ! ps_rss               ! x  !    !    !
+# ! dns        ! qtype                ! x  !    !    !
+# ! dns        ! rcode                ! x  !    !    !
+# ! (*)        ! sensors              ! x  !    !    !
+# !            ! serial               ! x  !    !    !
+# !            ! swap                 !    !    !    !
+# !            ! tape                 ! x  !    !    !
+# ! apple_se.. ! temperature          ! x  !    !    !
+# ! mbmon      ! temperature          ! x  !    !    !
+# ! ntpd       ! time_dispersion      ! x  !    !    !
+# ! ntpd       ! time_offset          ! x  !    !    !
+# ! apcups     ! timeleft             !    !    !    !
+# !            ! traffic              ! x  !    !    ! ->rx,tx
+# ! vserver    ! traffic              ! x  ! x  !    ! ->rx.tx
+# !            ! users                !    !    !    !
+# ! apucups    ! voltage              ! x  !    !    !
+# ! battery    ! voltage              !    ! x  !    !
+# ! mbmon      ! voltage              ! x  !    !    !
+# ! vserver    ! vs_memory            !    ! x  !    !
+# ! vserver    ! vs_processes         !    ! x  !    !
+# ! vserver    ! vs_threads           !    ! x  !    !
+# !            ! wireless             ! x  !    !    !
+# +------------+----------------------+----+----+----+
+
+our %Subdirs =
+(
+       apache => 0,
+       apcups => 0,
+       apple_sensors => 0,
+       battery => 1,
+       dns => 0,
+       interface => 1,
+       mbmon => 0,
+       mysql => 0,
+       ntpd => 0,
+       processes => 0,
+       sensors => 1,
+       vserver => 1
+);
+
+our %TypeTranslate =
+(
+       cpu => sub { $_ = shift; $_->{'plugin_instance'} = $_->{'type_instance'}; $_->{'type_instance'} = undef; $_; },
+       if_errors => sub { $_ = shift; $_->{'type_instance'} = $_->{'plugin_instance'}; $_->{'plugin_instance'} = undef; $_; },
+       if_packets => sub { $_ = shift; $_->{'type_instance'} = $_->{'plugin_instance'}; $_->{'plugin_instance'} = undef; $_; },
+       nfs2_procedures => sub { $_ = shift; @$_{qw(plugin plugin_instance type type_instance)} = ('nfs', 'v2' . $_->{'type_instance'}, 'nfs_procedure', undef); $_; },
+       nfs3_procedures => sub { $_ = shift; @$_{qw(plugin plugin_instance type type_instance)} = ('nfs', 'v3' . $_->{'type_instance'}, 'nfs_procedure', undef); $_; },
+       partition => sub { $_ = shift; $_->{'plugin'} = 'disk'; $_; },
+       processes => sub { $_ = shift; $_->{'type'} = 'ps_state'; $_; },
+       traffic => sub { $_ = shift; $_->{'plugin'} =~ s/^traffic$/interface/; @$_{qw(plugin_instance type)} = (undef, 'if_octets'); $_; }
+);
+
+our %TypeSplit =
+(
+       cpu => { from => [qw(user nice syst idle wait)], to => 'value', type_instance => [qw(user nice system idle wait)] },
+       memory => { from => [qw(used free buffers cached)], to => 'value', type_instance => [qw(used free buffered cached)] },
+       nfs3_procedures => { from => [qw(null getattr lookup access readlink
+               read write create mkdir symlink mknod remove rmdir rename link
+               readdir readdirplus fsstat fsinfo pathconf commit)], to => 'value' },
+       nfs2_procedures => { from => [qw(create fsstat getattr link lookup
+               mkdir null read readdir readlink remove rename rmdir root
+               setattr symlink wrcache write)], to => 'value' },
+       processes => { from => [qw(running sleeping zombies stopped paging blocked)], to => 'value' },
+       swap => { from => [qw(cached free used resv)], to => 'value', type_instance => [qw(cached free used reserved)] }
+);
+
+our %TypeRename =
+(
+       traffic => { from => [qw(incoming outgoing)], to => [qw(rx tx)] }
+);
+
+GetOptions ("indir|i=s" => \$InDir,
+       "outdir|o=s" => \$OutDir,
+       "hostname=s" => \$Hostname) or exit_usage ();
+
+die "No such directory: $InDir" if (!-d $InDir);
+
+our @Files = ();
+our %OutDirs = ();
+
+@Files = find_files ();
+
+for (@Files)
+{
+       my $orig_filename = $_;
+       my $orig = parse_file ($orig_filename);
+       my $dest = translate_file ($orig);
+       my $dest_filename = get_filename ($dest);
+
+       my $dest_directory = dirname ($dest_filename);
+       if (!exists ($OutDirs{$dest_directory}))
+       {
+               print "[ -d '$OutDir/$dest_directory' ] || mkdir -p '$OutDir/$dest_directory'\n";
+               $OutDirs{$dest_directory} = 1;
+       }
+
+       if (($orig->{'type'} eq 'disk') || ($orig->{'type'} eq 'partition'))
+       {
+               special_disk ($orig_filename, $orig, $dest_filename, $dest);
+       }
+       elsif (exists ($TypeSplit{$orig->{'type'}}))
+       {
+               my $src_dses = $TypeSplit{$orig->{'type'}}->{'from'};
+               my $dst_ds = $TypeSplit{$orig->{'type'}}->{'to'};
+               my $type_instances = exists ($TypeSplit{$orig->{'type'}}->{'type_instance'})
+                       ? $TypeSplit{$orig->{'type'}}->{'type_instance'}
+                       : $TypeSplit{$orig->{'type'}}->{'from'};
+
+               for (my $i = 0; $i < @$src_dses; $i++)
+               {
+                       my $src_ds = $src_dses->[$i];
+                       $dest->{'type_instance'} = $type_instances->[$i];
+                       $dest_filename = get_filename ($dest);
+                       print "./extractDS.px -i '$InDir/$orig_filename' -s '$src_ds' -o '$OutDir/$dest_filename' -d '$dst_ds'\n";
+               }
+       }
+       elsif (exists ($TypeRename{$orig->{'type'}}))
+       {
+               my $src_dses = $TypeRename{$orig->{'type'}}->{'from'};
+               my $dst_dses = $TypeRename{$orig->{'type'}}->{'to'};
+               my @sed_prog = ();
+
+               for (my $i = 0; $i < @$src_dses; $i++)
+               {
+                       push (@sed_prog, 's/^' . $src_dses->[$i] . '$/' . $dst_dses->[$i] . '/g;');
+               }
+
+               print "rrdtool dump '$InDir/$orig_filename' | sed -e '" . join (' ', @sed_prog) . "' | rrdtool restore - '$OutDir/$dest_filename'\n";
+       }
+       else
+       {
+               print "cp '$InDir/$orig_filename' '$OutDir/$dest_filename'\n";
+       }
+}
+
+exit (0);
+
+sub translate_file
+{
+       my $orig = shift;
+       my $dest = {};
+       %$dest = %$orig;
+
+       if (defined ($TypeTranslate{$orig->{'type'}}))
+       {
+               $TypeTranslate{$orig->{'type'}}->($dest);
+       }
+
+       return ($dest);
+} # translate_file
+
+sub get_filename
+{
+       my $args = shift;
+       my $filename = $args->{'host'}
+       . '/' . $args->{'plugin'} . (defined ($args->{'plugin_instance'}) ? '-'.$args->{'plugin_instance'} : '')
+       . '/' . $args->{'type'} . (defined ($args->{'type_instance'}) ? '-'.$args->{'type_instance'} : '') . '.rrd';
+
+       return ($filename);
+}
+
+sub parse_file
+{
+       my $fullname = shift;
+       my @parts = split ('/', $fullname);
+
+       my $filename;
+
+       my $host;
+       my $plugin;
+       my $plugin_instance;
+       my $type;
+       my $type_instance;
+
+       $filename = pop (@parts);
+
+       if ($filename =~ m/^([^-]+)(?:-(.*))?\.rrd$/)
+       {
+               $type = $1;
+               $type_instance = $2;
+       }
+       else
+       {
+               return;
+       }
+
+       if (@parts)
+       {
+               my $dirname = pop (@parts);
+               my $regex_str = join ('|', keys (%Subdirs));
+               if ($dirname =~ m/^($regex_str)(?:-(.*))?$/)
+               {
+                       $plugin = $1;
+                       $plugin_instance = $2;
+               }
+               else
+               {
+                       push (@parts, $dirname);
+               }
+       }
+       if (!$plugin)
+       {
+               $plugin = $type;
+       }
+
+       if (@parts)
+       {
+               $host = pop (@parts);
+       }
+       else
+       {
+               $host = $Hostname;
+       }
+
+       return
+       ({
+               host => $host,
+               plugin => $plugin,
+               plugin_instance => $plugin_instance,
+               type => $type,
+               type_instance => $type_instance
+       });
+} # parse_file
+
+sub find_files
+{
+       my $reldir = @_ ? shift : '';
+       my $absdir = $InDir . ($reldir ? "/$reldir" : '');
+
+       my $dh;
+
+       my @files = ();
+       my @dirs = ();
+
+       opendir ($dh, $absdir) or die ("opendir ($absdir): $!");
+       while (my $file = readdir ($dh))
+       {
+               next if ($file =~ m/^\./);
+               next if (-l "$absdir/$file");
+               if (-d "$absdir/$file")
+               {
+                       push (@dirs, ($reldir ? "$reldir/" : '') . $file);
+               }
+               elsif ($file =~ m/\.rrd$/)
+               {
+                       push (@files, ($reldir ? "$reldir/" : '') . $file);
+               }
+       }
+       closedir ($dh);
+
+       for (my $i = 0; $i < @dirs; $i++)
+       {
+               push (@files, find_files ($dirs[$i]));
+       }
+
+       return (@files);
+} # find_files
+
+{my $cache;
+sub _special_disk_instance
+{
+       my $orig_instance = shift;
+
+       if (!defined ($cache))
+       {
+               my $fh;
+               open ($fh, "< /proc/diskstats") or die ("open (/proc/diststats): $!");
+
+               $cache = {};
+               while (my $line = <$fh>)
+               {
+                       chomp ($line);
+                       my @fields = split (' ', $line);
+                       $cache->{$fields[0] . '-' . $fields[1]} = $fields[2];
+               }
+               close ($fh);
+       }
+
+       return (defined ($cache->{$orig_instance})
+               ? $cache->{$orig_instance}
+               : $orig_instance);
+}}
+
+sub special_disk
+{
+       my $orig_filename = shift;
+       my $orig = shift;
+       my $dest_filename = shift;
+       my $dest = shift;
+       my $dest_directory;
+
+       $dest->{'type_instance'} = undef;
+       $dest->{'plugin_instance'} = _special_disk_instance ($orig->{'type_instance'});
+       if ($dest->{'plugin_instance'} eq $orig->{'type_instance'})
+       {
+               print qq(echo "You may need to rename these files" >&2\n);
+       }
+
+       $dest->{'type'} = 'disk_merged';
+       $dest_filename = get_filename ($dest);
+
+       $dest_directory = dirname ($dest_filename);
+       if (!exists ($OutDirs{$dest_directory}))
+       {
+               print "[ -d '$OutDir/$dest_directory' ] || mkdir -p '$OutDir/$dest_directory'\n";
+               $OutDirs{$dest_directory} = 1;
+       }
+
+       print "./extractDS.px -i '$InDir/$orig_filename' -s 'rmerged' -s 'wmerged' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
+
+       $dest->{'type'} = 'disk_octets';
+       $dest_filename = get_filename ($dest);
+       print "./extractDS.px -i '$InDir/$orig_filename' -s 'rbytes' -s 'wbytes' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
+
+       $dest->{'type'} = 'disk_ops';
+       $dest_filename = get_filename ($dest);
+       print "./extractDS.px -i '$InDir/$orig_filename' -s 'rcount' -s 'wcount' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
+
+       $dest->{'type'} = 'disk_time';
+       $dest_filename = get_filename ($dest);
+       print "./extractDS.px -i '$InDir/$orig_filename' -s 'rtime' -s 'wtime' -o '$OutDir/$dest_filename' -d 'read' -d 'write'\n";
+}
+
+sub exit_usage
+{
+       print <<EOF;
+Usage: $0 [-i indir] [-o outdir] [--hostname myhostname]
+EOF
+       exit (1);
+}
diff --git a/contrib/sles10.1/collectd.spec b/contrib/sles10.1/collectd.spec
new file mode 100644 (file)
index 0000000..2d558bd
--- /dev/null
@@ -0,0 +1,231 @@
+Summary:       Statistics collection daemon for filling RRD files.
+Name:           collectd
+Version:       3.11.1
+Release:       0.sl10.1
+Source:                http://collectd.org/files/%{name}-%{version}.tar.gz
+Source1:       collectd-init.d
+License:       GPL
+Group:         System Environment/Daemons
+BuildRoot:     %{_tmppath}/%{name}-%{version}-root
+BuildPrereq:   curl-devel, sensors, mysql-devel, rrdtool, libpcap
+Requires:      rrdtool
+Packager:      Florian octo Forster <octo@verplant.org>
+Vendor:                Florian octo Forster <octo@verplant.org>
+
+%description
+collectd is a small daemon written in C for performance.  It reads various
+system  statistics  and updates  RRD files,  creating  them if neccessary.
+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.
+
+%package apache
+Summary:       apache-plugin for collectd.
+Group:         System Environment/Daemons
+Requires:      collectd = %{version}, curl
+%description apache
+This plugin collects data provided by Apache's `mod_status'.
+
+%package dns
+Summary:       dns-plugin for collectd.
+Group:         System Environment/Daemons
+Requires:      collectd = %{version}, libpcap
+%description dns
+This plugin collects information about DNS traffic, queries and responses.
+
+%package mysql
+Summary:       mysql-module for collectd.
+Group:         System Environment/Daemons
+Requires:      collectd = %{version}, mysql
+%description mysql
+MySQL  querying  plugin.  This plugins  provides data of  issued commands,
+called handlers and database traffic.
+
+%package sensors
+Summary:       libsensors-module for collectd.
+Group:         System Environment/Daemons
+Requires:      collectd = %{version}, sensors
+%description sensors
+This  plugin  for  collectd  provides  querying  of sensors  supported  by
+lm_sensors.
+
+%prep
+rm -rf $RPM_BUILD_ROOT
+%setup
+
+%build
+./configure --prefix=%{_prefix} --sbindir=%{_sbindir} --mandir=%{_mandir} --libdir=%{_libdir} --sysconfdir=%{_sysconfdir} --localstatedir=%{_localstatedir}
+make
+
+%install
+make install DESTDIR=$RPM_BUILD_ROOT
+cp src/collectd.conf $RPM_BUILD_ROOT/etc/collectd.conf
+mkdir -p $RPM_BUILD_ROOT/var/lib/collectd
+rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/*.a
+rm -f $RPM_BUILD_ROOT%{_libdir}/%{name}/*.la
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/init.d
+cp %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/init.d/collectd
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+chkconfig collectd on
+/etc/init.d/collectd start
+
+%preun
+/etc/init.d/collectd stop
+chkconfig collectd off
+
+%files
+%defattr(-,root,root)
+%doc AUTHORS COPYING ChangeLog INSTALL NEWS README
+%doc contrib
+%config /etc/collectd.conf
+%attr(0755,root,root) /etc/init.d/collectd
+%attr(0755,root,root) %{_sbindir}/collectd
+%attr(0444,root,root) %{_mandir}/man1/*
+%attr(0444,root,root) %{_mandir}/man5/*
+%attr(0444,root,root) %{_libdir}/%{name}/apcups.so
+%attr(0444,root,root) %{_libdir}/%{name}/apple_sensors.so
+%attr(0444,root,root) %{_libdir}/%{name}/battery.so
+%attr(0444,root,root) %{_libdir}/%{name}/cpu.so
+%attr(0444,root,root) %{_libdir}/%{name}/cpufreq.so
+%attr(0444,root,root) %{_libdir}/%{name}/df.so
+%attr(0444,root,root) %{_libdir}/%{name}/disk.so
+%attr(0444,root,root) %{_libdir}/%{name}/email.so
+%attr(0444,root,root) %{_libdir}/%{name}/hddtemp.so
+%attr(0444,root,root) %{_libdir}/%{name}/irq.so
+%attr(0444,root,root) %{_libdir}/%{name}/load.so
+%attr(0444,root,root) %{_libdir}/%{name}/mbmon.so
+%attr(0444,root,root) %{_libdir}/%{name}/memory.so
+%attr(0444,root,root) %{_libdir}/%{name}/multimeter.so
+%attr(0444,root,root) %{_libdir}/%{name}/nfs.so
+%attr(0444,root,root) %{_libdir}/%{name}/ntpd.so
+%attr(0444,root,root) %{_libdir}/%{name}/ping.so
+%attr(0444,root,root) %{_libdir}/%{name}/processes.so
+%attr(0444,root,root) %{_libdir}/%{name}/serial.so
+%attr(0444,root,root) %{_libdir}/%{name}/swap.so
+%attr(0444,root,root) %{_libdir}/%{name}/tape.so
+%attr(0444,root,root) %{_libdir}/%{name}/traffic.so
+%attr(0444,root,root) %{_libdir}/%{name}/users.so
+%attr(0444,root,root) %{_libdir}/%{name}/vserver.so
+%attr(0444,root,root) %{_libdir}/%{name}/wireless.so
+
+%dir /var/lib/collectd
+
+%files apache
+%attr(0444,root,root) %{_libdir}/%{name}/apache.so
+
+%files dns
+%attr(0444,root,root) %{_libdir}/%{name}/dns.so
+
+%files mysql
+%attr(0444,root,root) %{_libdir}/%{name}/mysql.so
+
+%files sensors
+%attr(0444,root,root) %{_libdir}/%{name}/sensors.so
+
+%changelog
+* Sun Jul 09 2006 Florian octo Forster <octo@verplant.org> 3.10.0-1
+- New upstream version
+
+* Tue Jun 25 2006 Florian octo Forster <octo@verplant.org> 3.9.4-1
+- New upstream version
+
+* Tue Jun 01 2006 Florian octo Forster <octo@verplant.org> 3.9.3-1
+- New upstream version
+
+* Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.9.2-1
+- New upstream version
+
+* Tue May 09 2006 Florian octo Forster <octo@verplant.org> 3.8.5-1
+- New upstream version
+
+* Fri Apr 21 2006 Florian octo Forster <octo@verplant.org> 3.9.1-1
+- New upstream version
+
+* Fri Apr 14 2006 Florian octo Forster <octo@verplant.org> 3.9.0-1
+- New upstream version
+- Added the `apache' package.
+
+* Thu Mar 14 2006 Florian octo Forster <octo@verplant.org> 3.8.2-1
+- New upstream version
+
+* Thu Mar 13 2006 Florian octo Forster <octo@verplant.org> 3.8.1-1
+- New upstream version
+
+* Thu Mar 09 2006 Florian octo Forster <octo@verplant.org> 3.8.0-1
+- New upstream version
+
+* Sat Feb 18 2006 Florian octo Forster <octo@verplant.org> 3.7.2-1
+- Include `tape.so' so the build doesn't terminate because of missing files..
+- New upstream version
+
+* Sat Feb 04 2006 Florian octo Forster <octo@verplant.org> 3.7.1-1
+- New upstream version
+
+* Mon Jan 30 2006 Florian octo Forster <octo@verplant.org> 3.7.0-1
+- New upstream version
+- Removed the extra `hddtemp' package
+
+* Tue Jan 24 2006 Florian octo Forster <octo@verplant.org> 3.6.2-1
+- New upstream version
+
+* Fri Jan 20 2006 Florian octo Forster <octo@verplant.org> 3.6.1-1
+- New upstream version
+
+* Fri Jan 20 2006 Florian octo Forster <octo@verplant.org> 3.6.0-1
+- New upstream version
+- Added config file, `collectd.conf(5)', `df.so'
+- Added package `collectd-mysql', dependency on `mysqlclient10 | mysql'
+
+* Wed Dec 07 2005 Florian octo Forster <octo@verplant.org> 3.5.0-1
+- New upstream version
+
+* Sat Nov 26 2005 Florian octo Forster <octo@verplant.org> 3.4.0-1
+- New upstream version
+
+* Sat Nov 05 2005 Florian octo Forster <octo@verplant.org> 3.3.0-1
+- New upstream version
+
+* Tue Oct 26 2005 Florian octo Forster <octo@verplant.org> 3.2.0-1
+- New upstream version
+- Added statement to remove the `*.la' files. This fixes a problem when
+  `Unpackaged files terminate build' is in effect.
+- Added `processes.so*' to the main package
+
+* Fri Oct 14 2005 Florian octo Forster <octo@verplant.org> 3.1.0-1
+- New upstream version
+- Added package `collectd-hddtemp'
+
+* Fri Sep 30 2005 Florian octo Forster <octo@verplant.org> 3.0.0-1
+- New upstream version
+- Split the package into `collectd' and `collectd-sensors'
+
+* Fri Sep 16 2005 Florian octo Forster <octo@verplant.org> 2.1.0-1
+- New upstream version
+
+* Mon Sep 10 2005 Florian octo Forster <octo@verplant.org> 2.0.0-1
+- New upstream version
+
+* Mon Aug 29 2005 Florian octo Forster <octo@verplant.org> 1.8.0-1
+- New upstream version
+
+* Sun Aug 25 2005 Florian octo Forster <octo@verplant.org> 1.7.0-1
+- New upstream version
+
+* Sun Aug 21 2005 Florian octo Forster <octo@verplant.org> 1.6.0-1
+- New upstream version
+
+* Sun Jul 17 2005 Florian octo Forster <octo@verplant.org> 1.5.1-1
+- New upstream version
+
+* Sun Jul 17 2005 Florian octo Forster <octo@verplant.org> 1.5-1
+- New upstream version
+
+* Mon Jul 11 2005 Florian octo Forster <octo@verplant.org> 1.4.2-1
+- New upstream version
+
+* Sat Jul 09 2005 Florian octo Forster <octo@verplant.org> 1.4-1
+- Built on RedHat 7.3
diff --git a/contrib/sles10.1/init.d-collectd b/contrib/sles10.1/init.d-collectd
new file mode 100755 (executable)
index 0000000..edc415e
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+### BEGIN INIT INFO
+# Provides:                    collectd
+# Required-Start:              $local_fs $remote_fs $network 
+# X-UnitedLinux-Should-Start:  $named $time apache mysql
+# Required-Stop:               $local_fs $remote_fs $network
+# X-UnitedLinux-Should-Stop:   
+# Default-Start:               3 5
+# Default-Stop:                        0 1 2 6
+# Short-Description:           Statistics daemon collectd
+# Description:                 Start the statistics daemon collectd
+### END INIT INFO
+
+
+#
+# load the configuration
+#
+test -s /etc/rc.status && . /etc/rc.status && rc_reset
+
+RETVAL=0
+ARGS=""
+prog="collectd"
+CONFIG=/etc/collectd.conf
+
+if [ -r /etc/default/$prog ]; then
+       . /etc/default/$prog
+fi
+
+start () {
+       echo -n $"Starting $prog: "
+       RETVAL=1
+       if [ -r "$CONFIG" ]
+       then
+               eval startproc /usr/sbin/collectd -C "$CONFIG"
+               RETVAL=$?
+               [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$prog
+       fi
+       rc_failed $RETVAL
+       rc_status -v
+}
+stop () {
+       echo -n $"Stopping $prog: "
+       killproc $prog
+       RETVAL=$?
+       rc_failed $RETVAL
+       rc_status -v
+       [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
+}
+# See how we were called.
+case "$1" in
+  start)
+       start
+       ;;
+  stop)
+       stop
+       ;;
+  status)
+       status $prog
+       ;;
+  restart|reload)
+       stop
+       sleep 1
+       start
+       ;;
+  condrestart)
+       [ -f /var/lock/subsys/$prog ] && restart || :
+       ;;
+  *)
+       echo $"Usage: $0 {start|stop|status|restart|reload|condrestart}"
+       exit 1
+esac
+
+rc_exit
+# vim:syntax=sh
index 58cc0fe..6fd6562 100644 (file)
@@ -1,4 +1,7 @@
-SUBDIRS = libconfig
+SUBDIRS =
+if BUILD_WITH_OWN_LIBOCONFIG
+SUBDIRS += liboconfig
+endif
 if BUILD_WITH_OWN_LIBOPING
 SUBDIRS += liboping
 endif
@@ -8,31 +11,28 @@ AM_CFLAGS = -Wall -Werror
 endif
 
 sbin_PROGRAMS = collectd
+bin_PROGRAMS = collectd-nagios
 
 collectd_SOURCES = collectd.c collectd.h \
-                  utils_debug.c utils_debug.h \
+                  utils_avltree.c utils_avltree.h \
                   utils_mount.c utils_mount.h \
+                  utils_llist.c utils_llist.h \
                   utils_ignorelist.c utils_ignorelist.h \
                   common.c common.h \
-                  network.c network.h \
                   plugin.c plugin.h \
-                  configfile.c configfile.h
+                  configfile.c configfile.h \
+                  types_list.c types_list.h
 collectd_CPPFLAGS = $(LTDLINCL)
+collectd_CPPFLAGS += -DPREFIX='"${prefix}"'
 collectd_CPPFLAGS += -DCONFIGFILE='"${sysconfdir}/${PACKAGE_NAME}.conf"'
 collectd_CPPFLAGS += -DPKGLOCALSTATEDIR='"${localstatedir}/lib/${PACKAGE_NAME}"'
 if BUILD_FEATURE_DAEMON
 collectd_CPPFLAGS += -DPIDFILE='"${localstatedir}/run/${PACKAGE_NAME}.pid"'
 endif
 collectd_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"'
-if BUILD_FEATURE_DEBUG
-collectd_CPPFLAGS += -DLOGFILE='"${localstatedir}/log/${PACKAGE_NAME}/collectd.log"'
-endif
 
 # Link to these libraries..
 collectd_LDFLAGS = -export-dynamic
-if BUILD_WITH_RRDTOOL
-collectd_LDFLAGS += -lm -lrrd
-endif
 if BUILD_WITH_LIBRT
 collectd_LDFLAGS += -lrt
 endif
@@ -42,6 +42,9 @@ endif
 if BUILD_WITH_LIBRESOLV
 collectd_LDFLAGS += -lresolv
 endif
+if BUILD_WITH_LIBPTHREAD
+collectd_LDFLAGS += -lpthread
+endif
 if BUILD_WITH_LIBKSTAT
 collectd_LDFLAGS += -lkstat
 endif
@@ -60,8 +63,20 @@ collectd_LDFLAGS += -lstatgrab
 endif
 
 
-collectd_LDADD = $(LIBLTDL) libconfig/libconfig.la "-dlopen" self
-collectd_DEPENDENCIES = $(LIBLTDL) libconfig/libconfig.la
+collectd_LDADD =
+collectd_DEPENDENCIES =
+if BUILD_WITH_OWN_LIBOCONFIG
+collectd_LDADD += $(LIBLTDL) liboconfig/liboconfig.la
+collectd_DEPENDENCIES += $(LIBLTDL) liboconfig/liboconfig.la
+else
+collectd_LDFLAGS += -loconfig
+endif
+
+collectd_nagios_SOURCES = collectd-nagios.c
+collectd_nagios_LDFLAGS =
+if BUILD_WITH_LIBSOCKET
+collectd_nagios_LDFLAGS += -lsocket
+endif
 
 pkglib_LTLIBRARIES = 
 
@@ -131,6 +146,14 @@ collectd_LDADD += "-dlopen" cpufreq.la
 collectd_DEPENDENCIES += cpufreq.la
 endif
 
+if BUILD_MODULE_CSV
+pkglib_LTLIBRARIES += csv.la
+csv_la_SOURCES = csv.c
+csv_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" csv.la
+collectd_DEPENDENCIES += csv.la
+endif
+
 if BUILD_MODULE_DF
 pkglib_LTLIBRARIES += df.la
 df_la_SOURCES = df.c
@@ -186,16 +209,24 @@ collectd_LDADD += "-dlopen" email.la
 collectd_DEPENDENCIES += email.la
 endif
 
-#if BUILD_MODULE_QUOTA
-#pkglib_LTLIBRARIES += quota.la
-#quota_la_SOURCES = quota_plugin.c quota_plugin.h
-#quota_la_SOURCES += quota_fs.c quota_fs.h
-#quota_la_SOURCES += quota_mnt.c quota_mnt.h
-#quota_la_LDFLAGS = -module -avoid-version
-#quota_la_CFLAGS = -Werror
-#collectd_LDADD += "-dlopen" quota.la
-#collectd_DEPENDENCIES += quota.la
-#endif
+if BUILD_MODULE_ENTROPY
+pkglib_LTLIBRARIES += entropy.la
+entropy_la_SOURCES = entropy.c
+entropy_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" entropy.la
+collectd_DEPENDENCIES += entropy.la
+endif
+
+if BUILD_MODULE_EXEC
+pkglib_LTLIBRARIES += exec.la
+exec_la_SOURCES = exec.c
+exec_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBPTHREAD
+exec_la_LDFLAGS += -lpthread
+endif
+collectd_LDADD += "-dlopen" exec.la
+collectd_DEPENDENCIES += exec.la
+endif
 
 if BUILD_MODULE_HDDTEMP
 pkglib_LTLIBRARIES += hddtemp.la
@@ -208,6 +239,48 @@ collectd_LDADD += "-dlopen" hddtemp.la
 collectd_DEPENDENCIES += hddtemp.la
 endif
 
+if BUILD_MODULE_INTERFACE
+pkglib_LTLIBRARIES += interface.la
+interface_la_SOURCES = interface.c
+interface_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" interface.la
+collectd_DEPENDENCIES += interface.la
+if BUILD_WITH_LIBKSTAT
+interface_la_LDFLAGS += -lkstat
+endif
+if BUILD_WITH_LIBDEVINFO
+interface_la_LDFLAGS += -ldevinfo
+endif
+if BUILD_WITH_LIBSTATGRAB
+if BUILD_WITH_LIBKVM
+interface_la_LDFLAGS += -lkvm
+endif
+if BUILD_WITH_LIBDEVSTAT
+interface_la_LDFLAGS += -ldevstat
+endif
+interface_la_LDFLAGS += -lstatgrab
+endif
+endif
+
+if BUILD_MODULE_IPTABLES
+pkglib_LTLIBRARIES += iptables.la
+iptables_la_SOURCES = iptables.c
+iptables_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBIPTC
+iptables_la_LDFLAGS += -liptc
+endif
+collectd_LDADD += "-dlopen" iptables.la
+collectd_DEPENDENCIES += iptables.la
+endif
+
+if BUILD_MODULE_IRQ
+pkglib_LTLIBRARIES += irq.la
+irq_la_SOURCES = irq.c
+irq_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" irq.la
+collectd_DEPENDENCIES += irq.la
+endif
+
 if BUILD_MODULE_LOAD
 pkglib_LTLIBRARIES += load.la
 load_la_SOURCES = load.c
@@ -225,6 +298,14 @@ load_la_LDFLAGS += -lstatgrab
 endif
 endif
 
+if BUILD_MODULE_LOGFILE
+pkglib_LTLIBRARIES += logfile.la
+logfile_la_SOURCES = logfile.c
+logfile_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" logfile.la
+collectd_DEPENDENCIES += logfile.la
+endif
+
 if BUILD_MODULE_MBMON
 pkglib_LTLIBRARIES += mbmon.la
 mbmon_la_SOURCES = mbmon.c
@@ -278,6 +359,20 @@ collectd_LDADD += "-dlopen" mysql.la
 collectd_DEPENDENCIES += mysql.la
 endif
 
+if BUILD_MODULE_NETWORK
+pkglib_LTLIBRARIES += network.la
+network_la_SOURCES = network.c network.h
+network_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBSOCKET
+network_la_LDFLAGS += -lsocket
+endif
+if BUILD_WITH_LIBPTHREAD
+network_la_LDFLAGS += -lpthread
+endif
+collectd_LDADD += "-dlopen" network.la
+collectd_DEPENDENCIES += network.la
+endif
+
 if BUILD_MODULE_NFS
 pkglib_LTLIBRARIES += nfs.la
 nfs_la_SOURCES = nfs.c
@@ -297,6 +392,34 @@ collectd_LDADD += "-dlopen" ntpd.la
 collectd_DEPENDENCIES += ntpd.la
 endif
 
+if BUILD_MODULE_NUT
+pkglib_LTLIBRARIES += nut.la
+nut_la_SOURCES = nut.c
+nut_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBUPSCLIENT
+nut_la_LDFLAGS += -lupsclient
+if BUILD_WITH_LIBPTHREAD
+nut_la_LDFLAGS += -lpthread
+endif
+endif
+collectd_LDADD += "-dlopen" nut.la
+collectd_DEPENDENCIES += nut.la
+endif
+
+if BUILD_WITH_LIBPERL
+if BUILD_MODULE_PERL
+pkglib_LTLIBRARIES += perl.la
+perl_la_SOURCES = perl.c
+perl_la_CFLAGS  = $(AM_CFLAGS) \
+                 $(PERL_CFLAGS) \
+                 -DXS_VERSION=\"$(VERSION)\" -DVERSION=\"$(VERSION)\"
+perl_la_LDFLAGS = -module -avoid-version \
+                 $(PERL_LDFLAGS)
+collectd_LDADD += "-dlopen" perl.la
+collectd_DEPENDENCIES += perl.la
+endif
+endif
+
 if BUILD_MODULE_PING
 pkglib_LTLIBRARIES += ping.la
 ping_la_SOURCES = ping.c
@@ -321,6 +444,14 @@ collectd_LDADD += "-dlopen" processes.la
 collectd_DEPENDENCIES += processes.la
 endif
 
+if BUILD_WITH_RRDTOOL
+pkglib_LTLIBRARIES += rrdtool.la
+rrdtool_la_SOURCES = rrdtool.c
+rrdtool_la_LDFLAGS = -module -avoid-version -lrrd
+collectd_LDADD += "-dlopen" rrdtool.la
+collectd_DEPENDENCIES += rrdtool.la
+endif
+
 if BUILD_MODULE_SENSORS
 pkglib_LTLIBRARIES += sensors.la
 sensors_la_SOURCES = sensors.c
@@ -363,6 +494,14 @@ swap_la_LDFLAGS += -lstatgrab
 endif
 endif
 
+if BUILD_MODULE_SYSLOG
+pkglib_LTLIBRARIES += syslog.la
+syslog_la_SOURCES = syslog.c
+syslog_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" syslog.la
+collectd_DEPENDENCIES += syslog.la
+endif
+
 if BUILD_MODULE_TAPE
 pkglib_LTLIBRARIES += tape.la
 tape_la_SOURCES = tape.c
@@ -377,27 +516,13 @@ collectd_LDADD += "-dlopen" tape.la
 collectd_DEPENDENCIES += tape.la
 endif
 
-if BUILD_MODULE_TRAFFIC
-pkglib_LTLIBRARIES += traffic.la
-traffic_la_SOURCES = traffic.c
-traffic_la_LDFLAGS = -module -avoid-version
-collectd_LDADD += "-dlopen" traffic.la
-collectd_DEPENDENCIES += traffic.la
-if BUILD_WITH_LIBKSTAT
-traffic_la_LDFLAGS += -lkstat
-endif
-if BUILD_WITH_LIBDEVINFO
-traffic_la_LDFLAGS += -ldevinfo
-endif
-if BUILD_WITH_LIBSTATGRAB
-if BUILD_WITH_LIBKVM
-traffic_la_LDFLAGS += -lkvm
-endif
-if BUILD_WITH_LIBDEVSTAT
-traffic_la_LDFLAGS += -ldevstat
-endif
-traffic_la_LDFLAGS += -lstatgrab
-endif
+if BUILD_MODULE_UNIXSOCK
+pkglib_LTLIBRARIES += unixsock.la
+unixsock_la_SOURCES = unixsock.c
+unixsock_la_CPPFLAGS = -DPREFIX='"${prefix}"'
+unixsock_la_LDFLAGS = -module -avoid-version -lpthread
+collectd_LDADD += "-dlopen" unixsock.la
+collectd_DEPENDENCIES += unixsock.la
 endif
 
 if BUILD_MODULE_USERS
@@ -424,10 +549,13 @@ collectd_LDADD += "-dlopen" wireless.la
 collectd_DEPENDENCIES += wireless.la
 endif
 
-dist_man_MANS = collectd.1 collectd.conf.5
+dist_man_MANS = collectd.1 collectd-nagios.1 collectd.conf.5 \
+               collectd-email.5 collectd-exec.5 collectd-perl.5 \
+               collectd-unixsock.5
+
 #collectd_1_SOURCES = collectd.pod
 
-#EXTRA_DIST = $(man_MANS)
+EXTRA_DIST = types.db
 
 .pod.1:
        pod2man --release=$(VERSION) --center=$(PACKAGE) $< >$@
@@ -442,4 +570,5 @@ install-exec-hook:
                $(INSTALL) -m 0640 collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf.pkg-orig; \
        else \
                $(INSTALL) -m 0640 collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
-       fi
+       fi; \
+       cp -f types.db $(DESTDIR)$(pkglibdir)/;
index 530481b..573a116 100644 (file)
@@ -1,11 +1,11 @@
 /**
  * collectd - src/apache.c
  * Copyright (C) 2006  Florian octo Forster
+ * Copyright (C) 2007  Florent EppO Monbillard
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -18,6 +18,8 @@
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   Florent EppO Monbillard <eppo at darox.net>
+ *   - connections/lighttpd extension
  **/
 
 #include "collectd.h"
@@ -25,8 +27,6 @@
 #include "plugin.h"
 #include "configfile.h"
 
-#define MODULE_NAME "apache"
-
 #if HAVE_LIBCURL && HAVE_CURL_CURL_H
 #  define APACHE_HAVE_READ 1
 #  include <curl/curl.h>
 #  define APACHE_HAVE_READ 0
 #endif
 
+#if APACHE_HAVE_READ
 static char *url    = NULL;
 static char *user   = NULL;
 static char *pass   = NULL;
 static char *cacert = NULL;
 
-#if HAVE_LIBCURL
 static CURL *curl = NULL;
 
 #define ABUFFER_SIZE 16384
 static char apache_buffer[ABUFFER_SIZE];
 static int  apache_buffer_len = 0;
 static char apache_curl_error[CURL_ERROR_SIZE];
-#endif /* HAVE_LIBCURL */
-
-/* Limit to 2^27 bytes/s. That's what a gigabit-ethernet link can handle, in
- * theory. */
-static char *bytes_file = "apache/apache_bytes.rrd";
-static char *bytes_ds_def[] =
-{
-       "DS:count:COUNTER:"COLLECTD_HEARTBEAT":0:134217728",
-       NULL
-};
-static int bytes_ds_num = 1;
 
-/* Limit to 2^20 requests/s */
-static char *requests_file = "apache/apache_requests.rrd";
-static char *requests_ds_def[] =
-{
-       "DS:count:COUNTER:"COLLECTD_HEARTBEAT":0:1048576",
-       NULL
-};
-static int requests_ds_num = 1;
-
-static char *scoreboard_file = "apache/apache_scoreboard-%s.rrd";
-static char *scoreboard_ds_def[] =
-{
-       "DS:count:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int scoreboard_ds_num = 1;
-
-static char *config_keys[] =
+static const char *config_keys[] =
 {
        "URL",
        "User",
@@ -85,7 +57,6 @@ static char *config_keys[] =
 };
 static int config_keys_num = 4;
 
-#if HAVE_LIBCURL
 static size_t apache_curl_callback (void *buf, size_t size, size_t nmemb, void *stream)
 {
        size_t len = size * nmemb;
@@ -104,9 +75,8 @@ static size_t apache_curl_callback (void *buf, size_t size, size_t nmemb, void *
 
        return (len);
 }
-#endif /* HAVE_LIBCURL */
 
-static int config_set (char **var, char *value)
+static int config_set (char **var, const char *value)
 {
        if (*var != NULL)
        {
@@ -120,7 +90,7 @@ static int config_set (char **var, char *value)
                return (0);
 }
 
-static int config (char *key, char *value)
+static int config (const char *key, const char *value)
 {
        if (strcasecmp (key, "url") == 0)
                return (config_set (&url, value));
@@ -134,9 +104,8 @@ static int config (char *key, char *value)
                return (-1);
 }
 
-static void init (void)
+static int init (void)
 {
-#if HAVE_LIBCURL
        static char credentials[1024];
 
        if (curl != NULL)
@@ -146,8 +115,8 @@ static void init (void)
 
        if ((curl = curl_easy_init ()) == NULL)
        {
-               syslog (LOG_ERR, "apache: `curl_easy_init' failed.");
-               return;
+               ERROR ("apache: `curl_easy_init' failed.");
+               return (-1);
        }
 
        curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, apache_curl_callback);
@@ -158,8 +127,8 @@ static void init (void)
        {
                if (snprintf (credentials, 1024, "%s:%s", user, pass == NULL ? "" : pass) >= 1024)
                {
-                       syslog (LOG_ERR, "apache: Credentials would have been truncated.");
-                       return;
+                       ERROR ("apache: Credentials would have been truncated.");
+                       return (-1);
                }
 
                curl_easy_setopt (curl, CURLOPT_USERPWD, credentials);
@@ -174,49 +143,56 @@ static void init (void)
        {
                curl_easy_setopt (curl, CURLOPT_CAINFO, cacert);
        }
-#endif /* HAVE_LIBCURL */
-}
 
-static void bytes_write (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, bytes_file, val, bytes_ds_def, bytes_ds_num);
-}
+       return (0);
+} /* int init */
 
-static void requests_write (char *host, char *inst, char *val)
+static void submit_counter (const char *type, const char *type_instance,
+               unsigned long long value)
 {
-       rrd_update_file (host, requests_file, val, requests_ds_def, requests_ds_num);
-}
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-static void scoreboard_write (char *host, char *inst, char *val)
-{
-       char buf[1024];
+       DEBUG ("type = %s; type_instance = %s; value = %llu;",
+                       type, type_instance, value);
 
-       if (snprintf (buf, 1024, scoreboard_file, inst) >= 1024)
-               return;
+       values[0].counter = value;
 
-       rrd_update_file (host, buf, val, scoreboard_ds_def, scoreboard_ds_num);
-}
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "apache");
+       strcpy (vl.plugin_instance, "");
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-#if APACHE_HAVE_READ
-static void submit (char *type, char *inst, long long value)
+       plugin_dispatch_values (type, &vl);
+} /* void submit_counter */
+
+static void submit_gauge (const char *type, const char *type_instance,
+               double value)
 {
-       char buf[1024];
-       int  status;
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       status = snprintf (buf, 1024, "%u:%lli", (unsigned int) curtime, value);
-       if (status < 0)
-       {
-               syslog (LOG_ERR, "apache: bytes_submit: snprintf failed");
-               return;
-       }
-       else if (status >= 1024)
-       {
-               syslog (LOG_WARNING, "apache: bytes_submit: snprintf was truncated");
-               return;
-       }
+       DEBUG ("type = %s; type_instance = %s; value = %lf;",
+                       type, type_instance, value);
 
-       plugin_submit (type, inst, buf);
-}
+       values[0].gauge = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "apache");
+       strcpy (vl.plugin_instance, "");
+
+       if (type_instance != NULL)
+               strncpy (vl.type_instance, type_instance,
+                               sizeof (vl.type_instance));
+
+       plugin_dispatch_values (type, &vl);
+} /* void submit_counter */
 
 static void submit_scoreboard (char *buf)
 {
@@ -256,24 +232,25 @@ static void submit_scoreboard (char *buf)
                else if (buf[i] == 'I') idle_cleanup++;
        }
 
-       submit ("apache_scoreboard", "open"     , open);
-       submit ("apache_scoreboard", "waiting"  , waiting);
-       submit ("apache_scoreboard", "starting" , starting);
-       submit ("apache_scoreboard", "reading"  , reading);
-       submit ("apache_scoreboard", "sending"  , sending);
-       submit ("apache_scoreboard", "keepalive", keepalive);
-       submit ("apache_scoreboard", "dnslookup", dnslookup);
-       submit ("apache_scoreboard", "closing"  , closing);
-       submit ("apache_scoreboard", "logging"  , logging);
-       submit ("apache_scoreboard", "finishing", finishing);
-       submit ("apache_scoreboard", "idle_cleanup", idle_cleanup);
+       submit_gauge ("apache_scoreboard", "open"     , open);
+       submit_gauge ("apache_scoreboard", "waiting"  , waiting);
+       submit_gauge ("apache_scoreboard", "starting" , starting);
+       submit_gauge ("apache_scoreboard", "reading"  , reading);
+       submit_gauge ("apache_scoreboard", "sending"  , sending);
+       submit_gauge ("apache_scoreboard", "keepalive", keepalive);
+       submit_gauge ("apache_scoreboard", "dnslookup", dnslookup);
+       submit_gauge ("apache_scoreboard", "closing"  , closing);
+       submit_gauge ("apache_scoreboard", "logging"  , logging);
+       submit_gauge ("apache_scoreboard", "finishing", finishing);
+       submit_gauge ("apache_scoreboard", "idle_cleanup", idle_cleanup);
 }
 
-static void apache_read (void)
+static int apache_read (void)
 {
        int i;
 
        char *ptr;
+       char *saveptr;
        char *lines[16];
        int   lines_num = 0;
 
@@ -281,19 +258,21 @@ static void apache_read (void)
        int   fields_num;
 
        if (curl == NULL)
-               return;
+               return (-1);
        if (url == NULL)
-               return;
+               return (-1);
 
        apache_buffer_len = 0;
        if (curl_easy_perform (curl) != 0)
        {
-               syslog (LOG_WARNING, "apache: curl_easy_perform failed: %s", apache_curl_error);
-               return;
+               ERROR ("apache: curl_easy_perform failed: %s",
+                               apache_curl_error);
+               return (-1);
        }
 
        ptr = apache_buffer;
-       while ((lines[lines_num] = strtok (ptr, "\n\r")) != NULL)
+       saveptr = NULL;
+       while ((lines[lines_num] = strtok_r (ptr, "\n\r", &saveptr)) != NULL)
        {
                ptr = NULL;
                lines_num++;
@@ -310,31 +289,34 @@ static void apache_read (void)
                {
                        if ((strcmp (fields[0], "Total") == 0)
                                        && (strcmp (fields[1], "Accesses:") == 0))
-                               submit ("apache_requests", NULL, atoll (fields[2]));
+                               submit_counter ("apache_requests", "",
+                                               atoll (fields[2]));
                        else if ((strcmp (fields[0], "Total") == 0)
                                        && (strcmp (fields[1], "kBytes:") == 0))
-                               submit ("apache_bytes", NULL, 1024LL * atoll (fields[2]));
+                               submit_counter ("apache_bytes", "",
+                                               1024LL * atoll (fields[2]));
                }
                else if (fields_num == 2)
                {
                        if (strcmp (fields[0], "Scoreboard:") == 0)
                                submit_scoreboard (fields[1]);
+                       else if (strcmp (fields[0], "BusyServers:") == 0)
+                               submit_gauge ("apache_connections", NULL, atol (fields[1]));
                }
        }
 
        apache_buffer_len = 0;
-}
-#else
-#  define apache_read NULL
+
+       return (0);
+} /* int apache_read */
 #endif /* APACHE_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, init, apache_read, NULL);
-       plugin_register ("apache_requests",   NULL, NULL, requests_write);
-       plugin_register ("apache_bytes",      NULL, NULL, bytes_write);
-       plugin_register ("apache_scoreboard", NULL, NULL, scoreboard_write);
-       cf_register (MODULE_NAME, config, config_keys, config_keys_num);
-}
-
-#undef MODULE_NAME
+#if APACHE_HAVE_READ
+       plugin_register_config ("apache", config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("apache", init);
+       plugin_register_read ("apache", apache_read);
+#endif
+} /* void module_register */
index cd64426..75de03e 100644 (file)
@@ -33,7 +33,6 @@
 #include "common.h"      /* rrd_update_file */
 #include "plugin.h"      /* plugin_register, plugin_submit */
 #include "configfile.h"  /* cf_register */
-#include "utils_debug.h"
 
 #if HAVE_SYS_TYPES_H
 # include <sys/types.h>
 # include <netinet/in.h>
 #endif
 
-#ifndef APCMAIN
-# define APCMAIN 0
-#endif
-
 #define NISPORT 3551
 #define MAXSTRING               256
 #define MODULE_NAME "apcups"
 
 #define APCUPS_DEFAULT_HOST "localhost"
 
-/* Default values for contacting daemon */
-static char *global_host = NULL;
-static int   global_port = NISPORT;
-
-/* 
- * The following are only if not compiled to test the module with its own main.
-*/
-#if !APCMAIN
-static char *bvolt_file_template = "apcups/voltage-%s.rrd";
-static char *bvolt_ds_def[] = 
-{
-       "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-};
-static int bvolt_ds_num = 1;
-
-static char *load_file_template = "apcups/load_percent.rrd";
-static char *load_ds_def[] = 
-{
-       "DS:percent:GAUGE:"COLLECTD_HEARTBEAT":0:110",
-};
-static int load_ds_num = 1;
-
-static char *charge_file_template = "apcups/charge_percent.rrd";
-static char *charge_ds_def[] = 
-{
-       "DS:percent:GAUGE:"COLLECTD_HEARTBEAT":0:110",
-};
-static int charge_ds_num = 1;
-
-static char *time_file_template = "apcups/timeleft.rrd";
-static char *time_ds_def[] = 
-{
-       "DS:timeleft:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-};
-static int time_ds_num = 1;
-
-static char *temp_file_template = "apcups/temperature.rrd";
-static char *temp_ds_def[] = 
-{
-       /* -273.15 is absolute zero */
-       "DS:value:GAUGE:"COLLECTD_HEARTBEAT":-274:U",
-};
-static int temp_ds_num = 1;
-
-static char *freq_file_template = "apcups/frequency-%s.rrd";
-static char *freq_ds_def[] = 
-{
-       "DS:frequency:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-};
-static int freq_ds_num = 1;
-
-static char *config_keys[] =
-{
-       "Host",
-       "Port",
-       NULL
-};
-static int config_keys_num = 2;
-
-#endif /* if APCMAIN */
-
+/*
+ * Private data types
+ */
 struct apc_detail_s
 {
        double linev;
@@ -132,25 +69,41 @@ struct apc_detail_s
        double linefreq;
 };
 
-#define BIG_BUF 4096
+/*
+ * Private variables
+ */
+/* Default values for contacting daemon */
+static char *conf_host = NULL;
+static int   conf_port = NISPORT;
+
+static int global_sockfd = -1;
+
+static const char *config_keys[] =
+{
+       "Host",
+       "Port",
+       NULL
+};
+static int config_keys_num = 2;
 
-#if APCMAIN
 /* Close the network connection */
-static void net_close (int *fd)
+static int apcups_shutdown (void)
 {
        uint16_t packet_size = 0;
 
-       assert (*fd >= 0);
+       if (global_sockfd < 0)
+               return (0);
 
-       DBG ("Gracefully shutting down socket %i.", *fd);
+       DEBUG ("Gracefully shutting down socket %i.", global_sockfd);
 
        /* send EOF sentinel */
-       swrite (*fd, (void *) &packet_size, sizeof (packet_size));
+       swrite (global_sockfd, (void *) &packet_size, sizeof (packet_size));
 
-       close (*fd);
-       *fd = -1;
-}
-#endif /* APCMAIN */
+       close (global_sockfd);
+       global_sockfd = -1;
+
+       return (0);
+} /* int apcups_shutdown */
 
 /*     
  * Open a TCP connection to the UPS network server
@@ -180,7 +133,11 @@ static int net_open (char *host, char *service, int port)
        status = getaddrinfo (host, port_str, &ai_hints, &ai_return);
        if (status != 0)
        {
-               DBG ("getaddrinfo failed: %s", status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
+               char errbuf[1024];
+               DEBUG ("getaddrinfo failed: %s",
+                               (status == EAI_SYSTEM)
+                               ? sstrerror (errno, errbuf, sizeof (errbuf))
+                               : gai_strerror (status));
                return (-1);
        }
 
@@ -196,7 +153,7 @@ static int net_open (char *host, char *service, int port)
 
        if (sd < 0)
        {
-               DBG ("Unable to open a socket");
+               DEBUG ("Unable to open a socket");
                freeaddrinfo (ai_return);
                return (-1);
        }
@@ -207,12 +164,14 @@ static int net_open (char *host, char *service, int port)
 
        if (status != 0) /* `connect(2)' failed */
        {
-               DBG ("connect failed: %s", strerror (errno));
+               char errbuf[1024];
+               DEBUG ("connect failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                close (sd);
                return (-1);
        }
 
-       DBG ("Done opening a socket %i", sd);
+       DEBUG ("Done opening a socket %i", sd);
 
        return (sd);
 } /* int net_open (char *host, char *service, int port) */
@@ -240,7 +199,7 @@ static int net_recv (int *sockfd, char *buf, int buflen)
        packet_size = ntohs (packet_size);
        if (packet_size > buflen)
        {
-               DBG ("record length too large");
+               DEBUG ("record length too large");
                return (-2);
        }
 
@@ -297,10 +256,10 @@ static int apc_query_server (char *host, int port,
        int     n;
        char    recvline[1024];
        char   *tokptr;
+       char   *toksaveptr;
        char   *key;
        double  value;
 
-       static int sockfd   = -1;
        static complain_t compl;
 
 #if APCMAIN
@@ -309,9 +268,9 @@ static int apc_query_server (char *host, int port,
 # define PRINT_VALUE(name, val) /**/
 #endif
 
-       if (sockfd < 0)
+       if (global_sockfd < 0)
        {
-               if ((sockfd = net_open (host, NULL, port)) < 0)
+               if ((global_sockfd = net_open (host, NULL, port)) < 0)
                {
                        plugin_complain (LOG_ERR, &compl, "apcups plugin: "
                                        "Connecting to the apcupsd failed.");
@@ -324,13 +283,13 @@ static int apc_query_server (char *host, int port,
                }
        }
 
-       if (net_send (&sockfd, "status", 6) < 0)
+       if (net_send (&global_sockfd, "status", 6) < 0)
        {
-               syslog (LOG_ERR, "apcups plugin: Writing to the socket failed.");
+               ERROR ("apcups plugin: Writing to the socket failed.");
                return (-1);
        }
 
-       while ((n = net_recv (&sockfd, recvline, sizeof (recvline) - 1)) > 0)
+       while ((n = net_recv (&global_sockfd, recvline, sizeof (recvline) - 1)) > 0)
        {
                assert (n < sizeof (recvline));
                recvline[n] = '\0';
@@ -338,11 +297,12 @@ static int apc_query_server (char *host, int port,
                printf ("net_recv = `%s';\n", recvline);
 #endif /* if APCMAIN */
 
-               tokptr = strtok (recvline, " :\t");
+               toksaveptr = NULL;
+               tokptr = strtok_r (recvline, " :\t", &toksaveptr);
                while (tokptr != NULL)
                {
                        key = tokptr;
-                       if ((tokptr = strtok (NULL, " :\t")) == NULL)
+                       if ((tokptr = strtok_r (NULL, " :\t", &toksaveptr)) == NULL)
                                continue;
                        value = atof (tokptr);
 
@@ -365,60 +325,29 @@ static int apc_query_server (char *host, int port,
                        else if (strcmp ("TIMELEFT", key) == 0)
                                apcups_detail->timeleft = value;
 
-                       tokptr = strtok (NULL, ":");
+                       tokptr = strtok_r (NULL, ":", &toksaveptr);
                } /* while (tokptr != NULL) */
        }
        
        if (n < 0)
        {
-               syslog (LOG_WARNING, "apcups plugin: Error reading from socket");
+               WARNING ("apcups plugin: Error reading from socket");
                return (-1);
        }
-#if APCMAIN
-       else
-       {
-               /* close the opened socket */
-               net_close (&sockfd);
-       }
-#endif /* APCMAIN */
 
        return (0);
 }
 
-#if APCMAIN
-/*
- * This is used for testing apcups in a standalone mode.
- * Usefull for debugging.
- */
-int main (int argc, char **argv)
-{
-       /* we are not really going to use this */
-       struct apc_detail_s apcups_detail;
-
-       openlog ("apcups", LOG_PID | LOG_NDELAY | LOG_LOCAL1, LOG_USER);
-
-       if (global_host == NULL || strcmp (global_host, "0.0.0.0") == 0)
-               global_host = "localhost";
-
-       if(apc_query_server (global_host, global_port, &apcups_detail) < 0)
-       {
-               printf("apcups: Failed...\n");
-               return(-1);
-       }
-
-       return 0;
-}
-#else
-static int apcups_config (char *key, char *value)
+static int apcups_config (const char *key, const char *value)
 {
        if (strcasecmp (key, "host") == 0)
        {
-               if (global_host != NULL)
+               if (conf_host != NULL)
                {
-                       free (global_host);
-                       global_host = NULL;
+                       free (conf_host);
+                       conf_host = NULL;
                }
-               if ((global_host = strdup (value)) == NULL)
+               if ((conf_host = strdup (value)) == NULL)
                        return (1);
        }
        else if (strcasecmp (key, "Port") == 0)
@@ -426,10 +355,10 @@ static int apcups_config (char *key, char *value)
                int port_tmp = atoi (value);
                if (port_tmp < 1 || port_tmp > 65535)
                {
-                       syslog (LOG_WARNING, "apcups plugin: Invalid port: %i", port_tmp);
+                       WARNING ("apcups plugin: Invalid port: %i", port_tmp);
                        return (1);
                }
-               global_port = port_tmp;
+               conf_port = port_tmp;
        }
        else
        {
@@ -438,68 +367,22 @@ static int apcups_config (char *key, char *value)
        return (0);
 }
 
-static void apcups_init (void)
-{
-       return;
-}
-
-static void apc_write_voltage (char *host, char *inst, char *val)
-{
-       char file[512];
-       int  status;
-
-       status = snprintf (file, 512, bvolt_file_template, inst);
-       if ((status < 1) || (status >= 512))
-               return;
-
-       rrd_update_file (host, file, val, bvolt_ds_def, bvolt_ds_num);
-}
-
-static void apc_write_charge (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, charge_file_template, val, charge_ds_def, charge_ds_num);
-}
-
-static void apc_write_percent (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, load_file_template, val, load_ds_def, load_ds_num);
-}
-
-static void apc_write_timeleft (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, time_file_template, val, time_ds_def, time_ds_num);
-}
-
-static void apc_write_temperature (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, temp_file_template, val, temp_ds_def, temp_ds_num);
-}
-
-static void apc_write_frequency (char *host, char *inst, char *val)
+static void apc_submit_generic (char *type, char *type_inst, double value)
 {
-       char file[512];
-       int  status;
-
-       status = snprintf (file, 512, freq_file_template, inst);
-       if ((status < 1) || (status >= 512))
-               return;
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       rrd_update_file (host, file, val, freq_ds_def, freq_ds_num);
-}
-
-static void apc_submit_generic (char *type, char *inst,
-               double value)
-{
-       char buf[512];
-       int  status;
+       values[0].gauge = value;
 
-       status = snprintf (buf, 512, "%u:%f",
-                       (unsigned int) curtime, value);
-       if ((status < 1) || (status >= 512))
-               return;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "apcups");
+       strcpy (vl.plugin_instance, "");
+       strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 
-       DBG ("plugin_submit (%s, %s, %s);", type, inst, buf);
-       plugin_submit (type, inst, buf);
+       plugin_dispatch_values (type, &vl);
 }
 
 static void apc_submit (struct apc_detail_s *apcups_detail)
@@ -507,14 +390,14 @@ static void apc_submit (struct apc_detail_s *apcups_detail)
        apc_submit_generic ("apcups_voltage",    "input",   apcups_detail->linev);
        apc_submit_generic ("apcups_voltage",    "output",  apcups_detail->outputv);
        apc_submit_generic ("apcups_voltage",    "battery", apcups_detail->battv);
-       apc_submit_generic ("apcups_charge",     "-",       apcups_detail->bcharge);
-       apc_submit_generic ("apcups_charge_pct", "-",       apcups_detail->loadpct);
-       apc_submit_generic ("apcups_timeleft",   "-",       apcups_detail->timeleft);
-       apc_submit_generic ("apcups_temp",       "-",       apcups_detail->itemp);
+       apc_submit_generic ("apcups_charge",     "",        apcups_detail->bcharge);
+       apc_submit_generic ("apcups_charge_pct", "",        apcups_detail->loadpct);
+       apc_submit_generic ("apcups_timeleft",   "",        apcups_detail->timeleft);
+       apc_submit_generic ("apcups_temp",       "",        apcups_detail->itemp);
        apc_submit_generic ("apcups_frequency",  "input",   apcups_detail->linefreq);
 }
 
-static void apcups_read (void)
+static int apcups_read (void)
 {
        struct apc_detail_s apcups_detail;
        int status;
@@ -528,10 +411,10 @@ static void apcups_read (void)
        apcups_detail.itemp    = -300.0;
        apcups_detail.linefreq =   -1.0;
   
-       status = apc_query_server (global_host == NULL
+       status = apc_query_server (conf_host == NULL
                        ? APCUPS_DEFAULT_HOST
-                       : global_host,
-                       global_port, &apcups_detail);
+                       : conf_host,
+                       conf_port, &apcups_detail);
  
        /*
         * if we did not connect then do not bother submitting
@@ -539,28 +422,23 @@ static void apcups_read (void)
         */
        if (status != 0)
        {
-               DBG ("apc_query_server (%s, %i) = %i",
-                               global_host == NULL
+               DEBUG ("apc_query_server (%s, %i) = %i",
+                               conf_host == NULL
                                ? APCUPS_DEFAULT_HOST
-                               : global_host,
-                               global_port, status);
-               return;
+                               : conf_host,
+                               conf_port, status);
+               return (-1);
        }
 
        apc_submit (&apcups_detail);
+
+       return (0);
 } /* apcups_read */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, apcups_init, apcups_read, NULL);
-       plugin_register ("apcups_voltage",    NULL, NULL, apc_write_voltage);
-       plugin_register ("apcups_charge",     NULL, NULL, apc_write_charge);
-       plugin_register ("apcups_charge_pct", NULL, NULL, apc_write_percent);
-       plugin_register ("apcups_timeleft",   NULL, NULL, apc_write_timeleft);
-       plugin_register ("apcups_temp",       NULL, NULL, apc_write_temperature);
-       plugin_register ("apcups_frequency",  NULL, NULL, apc_write_frequency);
-       cf_register (MODULE_NAME, apcups_config, config_keys, config_keys_num);
-}
-
-#endif /* if APCMAIN */
-#undef MODULE_NAME
+       plugin_register_config ("apcups", apcups_config, config_keys,
+                       config_keys_num);
+       plugin_register_read ("apcups", apcups_read);
+       plugin_register_shutdown ("apcups", apcups_shutdown);
+} /* void module_register */
index 37bd5c3..e13bdc8 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/apple_sensors.c
- * Copyright (C) 2006  Florian octo Forster
+ * Copyright (C) 2006,2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -23,9 +22,6 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
-#include "utils_debug.h"
-
-#define MODULE_NAME "apple_sensors"
 
 #if HAVE_CTYPE_H
 #  include <ctype.h>
 static mach_port_t io_master_port = MACH_PORT_NULL;
 #endif
 
-static char *temperature_file = "apple_sensors/temperature-%s.rrd";
-static char *fanspeed_file    = "apple_sensors/fanspeed-%s.rrd";
-
-static char *ds_def[] =
-{
-       "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int ds_num = 1;
-
-static void as_init (void)
-{
 #if IOKIT_HAVE_READ
+static int as_init (void)
+{
        kern_return_t status;
        
        if (io_master_port != MACH_PORT_NULL)
@@ -88,51 +74,38 @@ static void as_init (void)
        status = IOMasterPort (MACH_PORT_NULL, &io_master_port);
        if (status != kIOReturnSuccess)
        {
-               syslog (LOG_ERR, "IOMasterPort failed: %s",
+               ERROR ("IOMasterPort failed: %s",
                                mach_error_string (status));
                io_master_port = MACH_PORT_NULL;
-               return;
+               return (-1);
        }
-#endif /* IOKIT_HAVE_READ */
-
-       return;
-}
-
-static void as_write (char *host, char *inst, char *val, const char *template)
-{
-       char filename[256];
-       int  status;
-
-       status = snprintf (filename, 256, template, inst);
-       if ((status < 1) || (status >= 256))
-               return;
 
-       rrd_update_file (host, filename, val, ds_def, ds_num);
+       return (0);
 }
 
-static void temperature_write (char *host, char *inst, char *val)
+static void as_submit (const char *type, const char *type_instance,
+               double val)
 {
-       as_write (host, inst, val, temperature_file);
-}
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-static void fanspeed_write (char *host, char *inst, char *val)
-{
-       as_write (host, inst, val, fanspeed_file);
-}
+       DEBUG ("type = %s; type_instance = %s; val = %f;",
+                       type, type_instance, val);
 
-#if IOKIT_HAVE_READ
-static void as_submit (char *type, char *inst, double value)
-{
-       char buf[128];
+       values[0].gauge = val;
 
-       if (snprintf (buf, 1024, "%u:%f", (unsigned int) curtime,
-                               value) >= 128)
-               return;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "apple_sensors");
+       strcpy (vl.plugin_instance, "");
+       strcpy (vl.type_instance, type_instance);
 
-       plugin_submit (type, inst, buf);
+       plugin_dispatch_values (type, &vl);
 }
 
-static void as_read (void)
+static int as_read (void)
 {
        kern_return_t   status;
        io_iterator_t   iterator;
@@ -147,16 +120,16 @@ static void as_read (void)
        int    i;
 
        if (!io_master_port || (io_master_port == MACH_PORT_NULL))
-               return;
+               return (-1);
 
        status = IOServiceGetMatchingServices (io_master_port,
                        IOServiceNameMatching("IOHWSensor"),
                        &iterator);
        if (status != kIOReturnSuccess)
                {
-               syslog (LOG_ERR, "IOServiceGetMatchingServices failed: %s",
+               ERROR ("IOServiceGetMatchingServices failed: %s",
                                mach_error_string (status));
-               return;
+               return (-1);
        }
 
        while ((io_obj = IOIteratorNext (iterator)))
@@ -168,7 +141,7 @@ static void as_read (void)
                                kNilOptions);
                if (status != kIOReturnSuccess)
                {
-                       DBG ("IORegistryEntryCreateCFProperties failed: %s",
+                       DEBUG ("IORegistryEntryCreateCFProperties failed: %s",
                                        mach_error_string (status));
                        continue;
                }
@@ -228,17 +201,17 @@ static void as_read (void)
                if (strcmp (type, "temperature") == 0)
                {
                        value_double = ((double) value_int) / 65536.0;
-                       strncpy (type, "apple_temperature", 128);
+                       strcpy (type, "temperature");
                }
                else if (strcmp (type, "temp") == 0)
                {
                        value_double = ((double) value_int) / 10.0;
-                       strncpy (type, "apple_temperature", 128);
+                       strcpy (type, "temperature");
                }
                else if (strcmp (type, "fanspeed") == 0)
                {
                        value_double = ((double) value_int) / 65536.0;
-                       strncpy (type, "apple_fanspeed", 128);
+                       strcpy (type, "fanspeed");
                }
                else if (strcmp (type, "voltage") == 0)
                {
@@ -248,11 +221,11 @@ static void as_read (void)
                else if (strcmp (type, "adc") == 0)
                {
                        value_double = ((double) value_int) / 10.0;
-                       strncpy (type, "apple_temperature", 128);
+                       strcpy (type, "fanspeed");
                }
                else
                {
-                       DBG ("apple_sensors: Read unknown sensor type: %s",
+                       DEBUG ("apple_sensors: Read unknown sensor type: %s",
                                        type);
                        value_double = (double) value_int;
                }
@@ -264,16 +237,15 @@ static void as_read (void)
        } /* while (iterator) */
 
        IOObjectRelease (iterator);
-}
-#else
-# define as_read NULL
+
+       return (0);
+} /* int as_read */
 #endif /* IOKIT_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, as_init, as_read, NULL);
-       plugin_register ("apple_temperature", NULL, NULL, temperature_write);
-       plugin_register ("apple_fanspeed",    NULL, NULL, fanspeed_write);
-}
-
-#undef MODULE_NAME
+#if IOKIT_HAVE_READ
+       plugin_register_init ("apple_sensors", as_init);
+       plugin_register_read ("apple_sensors", as_read);
+#endif /* IOKIT_HAVE_READ */
+} /* void module_register */
index 2141a58..952c56c 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/battery.c
- * Copyright (C) 2006  Florian octo Forster
+ * Copyright (C) 2006,2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
-#include "utils_debug.h"
-
-#define MODULE_NAME "battery"
-#define BUFSIZE 512
 
 #if HAVE_MACH_MACH_TYPES_H
 #  include <mach/mach_types.h>
 
 #define INVALID_VALUE 47841.29
 
-static char *battery_current_file = "battery-%s/current.rrd";
-static char *battery_voltage_file = "battery-%s/voltage.rrd";
-static char *battery_charge_file  = "battery-%s/charge.rrd";
-
-static char *ds_def_current[] =
-{
-       "DS:current:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int ds_num_current = 1;
-
-static char *ds_def_voltage[] =
-{
-       "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int ds_num_voltage = 1;
-
-static char *ds_def_charge[] =
-{
-       "DS:charge:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int ds_num_charge = 1;
-
+#if BATTERY_HAVE_READ
 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
        /* No global variables */
 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
@@ -95,7 +66,7 @@ static int   battery_pmu_num = 0;
 static char *battery_pmu_file = "/proc/pmu/battery_%i";
 #endif /* KERNEL_LINUX */
 
-static void battery_init (void)
+static int battery_init (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
        /* No init neccessary */
@@ -103,13 +74,13 @@ static void battery_init (void)
 
 #elif KERNEL_LINUX
        int len;
-       char filename[BUFSIZE];
+       char filename[128];
 
        for (battery_pmu_num = 0; ; battery_pmu_num++)
        {
-               len = snprintf (filename, BUFSIZE, battery_pmu_file, battery_pmu_num);
+               len = snprintf (filename, sizeof (filename), battery_pmu_file, battery_pmu_num);
 
-               if ((len >= BUFSIZE) || (len < 0))
+               if ((len >= sizeof (filename)) || (len < 0))
                        break;
 
                if (access (filename, R_OK))
@@ -117,90 +88,25 @@ static void battery_init (void)
        }
 #endif /* KERNEL_LINUX */
 
-       return;
+       return (0);
 }
 
-static void battery_current_write (char *host, char *inst, char *val)
+static void battery_submit (const char *plugin_instance, const char *type, double value)
 {
-       char filename[BUFSIZE];
-       int len;
-
-       len = snprintf (filename, BUFSIZE, battery_current_file, inst);
-       if ((len >= BUFSIZE) || (len < 0))
-               return;
-
-       rrd_update_file (host, filename, val,
-                       ds_def_current, ds_num_current);
-}
-
-static void battery_voltage_write (char *host, char *inst, char *val)
-{
-       char filename[BUFSIZE];
-       int len;
-
-       len = snprintf (filename, BUFSIZE, battery_voltage_file, inst);
-       if ((len >= BUFSIZE) || (len < 0))
-               return;
-
-       rrd_update_file (host, filename, val,
-                       ds_def_voltage, ds_num_voltage);
-}
-
-static void battery_charge_write (char *host, char *inst, char *val)
-{
-       char filename[BUFSIZE];
-       int len;
-
-       len = snprintf (filename, BUFSIZE, battery_charge_file, inst);
-       if ((len >= BUFSIZE) || (len < 0))
-               return;
-
-       rrd_update_file (host, filename, val,
-                       ds_def_charge, ds_num_charge);
-}
-
-#if BATTERY_HAVE_READ
-static void battery_submit (char *inst, double current, double voltage, double charge)
-{
-       int len;
-       char buffer[BUFSIZE];
-
-       if (current != INVALID_VALUE)
-       {
-               len = snprintf (buffer, BUFSIZE, "N:%.3f", current);
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-               if ((len > 0) && (len < BUFSIZE))
-                       plugin_submit ("battery_current", inst, buffer);
-       }
-       else
-       {
-               plugin_submit ("battery_current", inst, "N:U");
-       }
+       values[0].gauge = value;
 
-       if (voltage != INVALID_VALUE)
-       {
-               len = snprintf (buffer, BUFSIZE, "N:%.3f", voltage);
-
-               if ((len > 0) && (len < BUFSIZE))
-                       plugin_submit ("battery_voltage", inst, buffer);
-       }
-       else
-       {
-               plugin_submit ("battery_voltage", inst, "N:U");
-       }
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "battery");
+       strcpy (vl.plugin_instance, plugin_instance);
 
-       if (charge != INVALID_VALUE)
-       {
-               len = snprintf (buffer, BUFSIZE, "N:%.3f", charge);
-
-               if ((len > 0) && (len < BUFSIZE))
-                       plugin_submit ("battery_charge", inst, buffer);
-       }
-       else
-       {
-               plugin_submit ("battery_charge", inst, "N:U");
-       }
-}
+       plugin_dispatch_values (type, &vl);
+} /* void battery_submit */
 
 #if HAVE_IOKIT_PS_IOPOWERSOURCES_H || HAVE_IOKIT_IOKITLIB_H
 double dict_get_double (CFDictionaryRef dict, char *key_string)
@@ -214,13 +120,13 @@ double dict_get_double (CFDictionaryRef dict, char *key_string)
                        kCFStringEncodingASCII);
        if (key_obj == NULL)
        {
-               DBG ("CFStringCreateWithCString (%s) failed.\n", key_string);
+               DEBUG ("CFStringCreateWithCString (%s) failed.\n", key_string);
                return (INVALID_VALUE);
        }
 
        if ((val_obj = CFDictionaryGetValue (dict, key_obj)) == NULL)
        {
-               DBG ("CFDictionaryGetValue (%s) failed.", key_string);
+               DEBUG ("CFDictionaryGetValue (%s) failed.", key_string);
                CFRelease (key_obj);
                return (INVALID_VALUE);
        }
@@ -244,7 +150,7 @@ double dict_get_double (CFDictionaryRef dict, char *key_string)
        }
        else
        {
-               DBG ("CFGetTypeID (val_obj) = %i", (int) CFGetTypeID (val_obj));
+               DEBUG ("CFGetTypeID (val_obj) = %i", (int) CFGetTypeID (val_obj));
                return (INVALID_VALUE);
        }
 
@@ -270,7 +176,7 @@ static void get_via_io_power_sources (double *ret_charge,
        ps_array     = IOPSCopyPowerSourcesList (ps_raw);
        ps_array_len = CFArrayGetCount (ps_array);
 
-       DBG ("ps_array_len == %i", ps_array_len);
+       DEBUG ("ps_array_len == %i", ps_array_len);
 
        for (i = 0; i < ps_array_len; i++)
        {
@@ -279,13 +185,13 @@ static void get_via_io_power_sources (double *ret_charge,
 
                if (ps_dict == NULL)
                {
-                       DBG ("IOPSGetPowerSourceDescription failed.");
+                       DEBUG ("IOPSGetPowerSourceDescription failed.");
                        continue;
                }
 
                if (CFGetTypeID (ps_dict) != CFDictionaryGetTypeID ())
                {
-                       DBG ("IOPSGetPowerSourceDescription did not return a CFDictionaryRef");
+                       DEBUG ("IOPSGetPowerSourceDescription did not return a CFDictionaryRef");
                        continue;
                }
 
@@ -346,7 +252,7 @@ static void get_via_generic_iokit (double *ret_charge,
                        &iterator);
        if (status != kIOReturnSuccess)
        {
-               DBG ("IOServiceGetMatchingServices failed.");
+               DEBUG ("IOServiceGetMatchingServices failed.");
                return;
        }
 
@@ -358,7 +264,7 @@ static void get_via_generic_iokit (double *ret_charge,
                                kNilOptions);
                if (status != kIOReturnSuccess)
                {
-                       DBG ("IORegistryEntryCreateCFProperties failed.");
+                       DEBUG ("IORegistryEntryCreateCFProperties failed.");
                        continue;
                }
 
@@ -409,7 +315,7 @@ static void get_via_generic_iokit (double *ret_charge,
 }
 #endif /* HAVE_IOKIT_IOKITLIB_H */
 
-static void battery_read (void)
+static int battery_read (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H
        double charge  = INVALID_VALUE; /* Current charge in Ah */
@@ -429,16 +335,18 @@ static void battery_read (void)
        if ((charge_rel != INVALID_VALUE) && (charge_abs != INVALID_VALUE))
                charge = charge_abs * charge_rel / 100.0;
 
-       if ((charge != INVALID_VALUE)
-                       || (current != INVALID_VALUE)
-                       || (voltage != INVALID_VALUE))
-               battery_submit ("0", current, voltage, charge);
+       if (charge != INVALID_VALUE)
+               battery_submit ("0", "charge", charge);
+       if (current != INVALID_VALUE)
+               battery_submit ("0", "current", current);
+       if (voltage != INVALID_VALUE)
+               battery_submit ("0", "voltage", voltage);
 /* #endif HAVE_IOKIT_IOKITLIB_H || HAVE_IOKIT_PS_IOPOWERSOURCES_H */
 
 #elif KERNEL_LINUX
        FILE *fh;
-       char buffer[BUFSIZE];
-       char filename[BUFSIZE];
+       char buffer[1024];
+       char filename[256];
        
        char *fields[8];
        int numfields;
@@ -448,24 +356,24 @@ static void battery_read (void)
 
        for (i = 0; i < battery_pmu_num; i++)
        {
-               char    batnum_str[BUFSIZE];
+               char    batnum_str[256];
                double  current = INVALID_VALUE;
                double  voltage = INVALID_VALUE;
                double  charge  = INVALID_VALUE;
                double *valptr = NULL;
 
-               len = snprintf (filename, BUFSIZE, battery_pmu_file, i);
-               if ((len >= BUFSIZE) || (len < 0))
+               len = snprintf (filename, sizeof (filename), battery_pmu_file, i);
+               if ((len >= sizeof (filename)) || (len < 0))
                        continue;
 
-               len = snprintf (batnum_str, BUFSIZE, "%i", i);
-               if ((len >= BUFSIZE) || (len < 0))
+               len = snprintf (batnum_str, sizeof (batnum_str), "%i", i);
+               if ((len >= sizeof (batnum_str)) || (len < 0))
                        continue;
 
                if ((fh = fopen (filename, "r")) == NULL)
                        continue;
 
-               while (fgets (buffer, BUFSIZE, fh) != NULL)
+               while (fgets (buffer, sizeof (buffer), fh) != NULL)
                {
                        numfields = strsplit (buffer, fields, 8);
 
@@ -495,13 +403,15 @@ static void battery_read (void)
                        }
                }
 
-               if ((current != INVALID_VALUE)
-                               || (voltage != INVALID_VALUE)
-                               || (charge  != INVALID_VALUE))
-                       battery_submit (batnum_str, current, voltage, charge);
-
                fclose (fh);
                fh = NULL;
+
+               if (charge != INVALID_VALUE)
+                       battery_submit ("0", "charge", charge);
+               if (current != INVALID_VALUE)
+                       battery_submit ("0", "current", current);
+               if (voltage != INVALID_VALUE)
+                       battery_submit ("0", "voltage", voltage);
        }
 
        if (access ("/proc/acpi/battery", R_OK | X_OK) == 0)
@@ -517,8 +427,10 @@ static void battery_read (void)
 
                if ((dh = opendir ("/proc/acpi/battery")) == NULL)
                {
-                       syslog (LOG_ERR, "Cannot open `/proc/acpi/battery': %s", strerror (errno));
-                       return;
+                       char errbuf[1024];
+                       ERROR ("Cannot open `/proc/acpi/battery': %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       return (-1);
                }
 
                while ((ent = readdir (dh)) != NULL)
@@ -526,13 +438,18 @@ static void battery_read (void)
                        if (ent->d_name[0] == '.')
                                continue;
 
-                       len = snprintf (filename, BUFSIZE, "/proc/acpi/battery/%s/state", ent->d_name);
-                       if ((len >= BUFSIZE) || (len < 0))
+                       len = snprintf (filename, sizeof (filename),
+                                       "/proc/acpi/battery/%s/state",
+                                       ent->d_name);
+                       if ((len >= sizeof (filename)) || (len < 0))
                                continue;
 
                        if ((fh = fopen (filename, "r")) == NULL)
                        {
-                               syslog (LOG_ERR, "Cannot open `%s': %s", filename, strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("Cannot open `%s': %s", filename,
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
                                continue;
                        }
 
@@ -545,7 +462,7 @@ static void battery_read (void)
                         * [11:00] <@tokkee> remaining capacity:      4136 mAh
                         * [11:00] <@tokkee> present voltage:         12428 mV
                         */
-                       while (fgets (buffer, BUFSIZE, fh) != NULL)
+                       while (fgets (buffer, sizeof (buffer), fh) != NULL)
                        {
                                numfields = strsplit (buffer, fields, 8);
 
@@ -585,34 +502,33 @@ static void battery_read (void)
                                        if ((fields[2] == endptr) || (errno != 0))
                                                *valptr = INVALID_VALUE;
                                }
-                       }
+                       } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
+
+                       fclose (fh);
 
                        if ((current != INVALID_VALUE) && (charging == 0))
                                        current *= -1;
 
-                       if ((current != INVALID_VALUE)
-                                       || (voltage != INVALID_VALUE)
-                                       || (charge  != INVALID_VALUE))
-                               battery_submit (ent->d_name, current, voltage, charge);
-
-                       fclose (fh);
+                       if (charge != INVALID_VALUE)
+                               battery_submit ("0", "charge", charge);
+                       if (current != INVALID_VALUE)
+                               battery_submit ("0", "current", current);
+                       if (voltage != INVALID_VALUE)
+                               battery_submit ("0", "voltage", voltage);
                }
 
                closedir (dh);
        }
 #endif /* KERNEL_LINUX */
+
+       return (0);
 }
-#else
-# define battery_read NULL
 #endif /* BATTERY_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, battery_init, battery_read, NULL);
-       plugin_register ("battery_current", NULL, NULL, battery_current_write);
-       plugin_register ("battery_voltage", NULL, NULL, battery_voltage_write);
-       plugin_register ("battery_charge",  NULL, NULL, battery_charge_write);
-}
-
-#undef BUFSIZE
-#undef MODULE_NAME
+#if BATTERY_HAVE_READ
+       plugin_register_init ("battery", battery_init);
+       plugin_register_read ("battery", battery_read);
+#endif /* BATTERY_HAVE_READ */
+} /* void module_register */
diff --git a/src/collectd-email.pod b/src/collectd-email.pod
new file mode 100644 (file)
index 0000000..e19d13e
--- /dev/null
@@ -0,0 +1,72 @@
+=head1 NAME
+
+collectd-email - Documentation of collectd's C<email plugin>
+
+=head1 SYNOPSIS
+
+  # See collectd.conf(5)
+  LoadPlugin email
+  # ...
+  <Plugin email>
+    SocketGroup "collectd"
+    SocketPerms "0770"
+    MaxConns 5
+  </Plugin>
+
+=head1 DESCRIPTION
+
+The C<email plugin> opens an UNIX-socket over which one can submit email
+statistics, such as the number of "ham", "spam", "virus", etc. mails
+received/handled, spam scores and matched spam checks.
+
+This plugin is intended to be used with the
+L<Mail::SpamAssassin::Plugin::Collectd> SpamAssassin-plugin which is included
+in F<contrib/>, but is of course not limited to that use.
+
+=head1 OPERATION
+
+This plugin collects data indirectly by providing a UNIX-socket that external
+programs can connect to. A simple line based protocol is used to communicate
+with the plugin:
+
+=over 4
+
+=item
+
+E-Mail type (e.g. "ham", "spam", "virus", ...) and size (bytes):
+
+  e:<type>:<size>
+
+If C<size> is less than or equal to zero, C<size> is ignored.
+
+=item
+
+Spam score:
+
+  s:<value>
+
+=item
+
+Successful spam checks (e.g. "BAYES_99", "SUBJECT_DRUG_GAP_C", ...):
+
+  c:<type1>[,<type2>,...]
+
+Each line is limited to 256 characters (including the newline character). 
+Longer lines will be ignored.
+
+=back
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>
+
+=head1 AUTHOR
+
+The C<email plugin> has been written by Sebastian Harl E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
+
+The SpamAssassin-plugin has been written by Alexander Wirt E<lt>formorerE<nbsp>atE<nbsp>formorer.deE<gt>.
+
+This manpage has been written by Florian Forster E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>.
+
+=cut
diff --git a/src/collectd-exec.pod b/src/collectd-exec.pod
new file mode 100644 (file)
index 0000000..cb6069c
--- /dev/null
@@ -0,0 +1,81 @@
+=head1 NAME
+
+collectd-exec - Documentation of collectd's C<exec plugin>
+
+=head1 SYNOPSIS
+
+  # See collectd.conf(5)
+  LoadPlugin exec
+  # ...
+  <Plugin exec>
+    Exec myuser myprog
+    Exec otheruser /path/to/another/binary
+  </Plugin>
+
+=head1 DESCRIPTION
+
+The C<exec plugin> forks of an executable and reads back values that it writes
+to C<STDOUT>. The executable is forked in a fassion similar to L<init>: It is
+forked once and not again until it exits. If it exited, it will be forked again
+after at most I<Interval> seconds. It is perfectly legal for the executable to
+run for a long time and continuously write values to C<STDOUT>.
+
+If you want/need better performance or more functionality you should take a
+long look at the C<perl plugin>, L<collectd-perl(5)>.
+
+=head1 DATA FORMAT
+
+The forked executable is expected to print values to C<STDOUT>. The expected
+format is as follows:
+
+=over 4
+
+=item
+
+Each line beginning with a C<#> (hash mark) is ignored.
+
+=item
+
+Any other line must be of the form C<I<type>,I<type-instance>,I<value>>, where
+I<type> is either B<counter> or B<gauge>, I<type-instance> may not contain
+C<,> (comma), C</> (slash) and C<\0> (null byte) and I<value> is either an
+integer (if I<type> is B<counter>) or a floating-point number (if I<type> is
+B<gauge>).
+
+=back
+
+The values are always considered to be "fresh", i.E<nbsp>e. the time is set to
+"now".
+
+When collectd exits it sends a B<SIGTERM> to all still running
+child-processes upon which they have to quit.
+
+=head1 CAVEATS
+
+=over 4
+
+=item
+
+If the executable only writes one value and then exits I will be executed every
+I<Interval> seconds. If I<Interval> is short (the default is 10 seconds) this
+may result in serious system load.
+
+=item
+
+The user, the binary is executed as, may not have root privileges, i.E<nbsp>e.
+must have an UID that is non-zero.
+
+=back
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+L<collectd-perl(5)>,
+L<fork(2)>, L<exec(3)>
+
+=head1 AUTHOR
+
+Florian Forster E<lt>octo@verplant.orgE<gt>
+
+=cut
diff --git a/src/collectd-nagios.c b/src/collectd-nagios.c
new file mode 100644 (file)
index 0000000..534559c
--- /dev/null
@@ -0,0 +1,494 @@
+#include "config.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/*
+ * This weird macro cascade forces the glibc to define `NAN'. I don't know
+ * another way to solve this, so more intelligent solutions are welcome. -octo
+ */
+#ifndef __USE_ISOC99
+# define DISABLE__USE_ISOC99 1
+# define __USE_ISOC99 1
+#endif
+#include <math.h>
+#ifdef DISABLE__USE_ISOC99
+# undef DISABLE__USE_ISOC99
+# undef __USE_ISOC99
+#endif
+
+#define RET_OKAY     0
+#define RET_WARNING  1
+#define RET_CRITICAL 2
+#define RET_UNKNOWN  3
+
+#define CON_NONE     0
+#define CON_AVERAGE  1
+#define CON_SUM      2
+
+struct range_s
+{
+       double min;
+       double max;
+       int    invert;
+};
+typedef struct range_s range_t;
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+static char *socket_file_g = NULL;
+static char *value_string_g = NULL;
+static char *hostname_g = NULL;
+
+static range_t range_critical_g;
+static range_t range_warning_g;
+static int consolitation_g = CON_NONE;
+
+static char **match_ds_g = NULL;
+static int    match_ds_num_g = 0;
+
+static int ignore_ds (const char *name)
+{
+       int i;
+
+       if (match_ds_g == NULL)
+               return (0);
+
+       for (i = 0; i < match_ds_num_g; i++)
+               if (strcasecmp (match_ds_g[i], name) == 0)
+                       return (0);
+
+       return (1);
+} /* int ignore_ds */
+
+static void parse_range (char *string, range_t *range)
+{
+       char *min_ptr;
+       char *max_ptr;
+
+       if (*string == '@')
+       {
+               range->invert = 1;
+               string++;
+       }
+
+       max_ptr = strchr (string, ':');
+       if (max_ptr == NULL)
+       {
+               min_ptr = NULL;
+               max_ptr = string;
+       }
+       else
+       {
+               min_ptr = string;
+               *max_ptr = '\0';
+               max_ptr++;
+       }
+
+       assert (max_ptr != NULL);
+
+       /* `10' == `0:10' */
+       if (min_ptr == NULL)
+               range->min = 0.0;
+       /* :10 == ~:10 == -inf:10 */
+       else if ((*min_ptr == '\0') || (*min_ptr == '~'))
+               range->min = NAN;
+       else
+               range->min = atof (min_ptr);
+
+       if ((*max_ptr == '\0') || (*max_ptr == '~'))
+               range->max = NAN;
+       else
+               range->max = atof (max_ptr);
+} /* void parse_range */
+
+int match_range (range_t *range, double value)
+{
+       int ret = 0;
+
+       if (!isnan (range->min) && (range->min > value))
+               ret = 1;
+       if (!isnan (range->max) && (range->max < value))
+               ret = 1;
+
+       return (((ret - range->invert) == 0) ? 0 : 1);
+}
+
+static int get_values (int *ret_values_num, double **ret_values,
+               char ***ret_values_names)
+{
+       struct sockaddr_un sa;
+       int status;
+       int fd;
+       FILE *fh;
+       char buffer[4096];
+
+       int values_num;
+       double *values;
+       char **values_names;
+
+       int i;
+
+       fd = socket (PF_UNIX, SOCK_STREAM, 0);
+       if (fd < 0)
+       {
+               fprintf (stderr, "socket failed: %s\n",
+                               strerror (errno));
+               return (-1);
+       }
+
+       memset (&sa, '\0', sizeof (sa));
+       sa.sun_family = AF_UNIX;
+       strncpy (sa.sun_path, socket_file_g,
+                       sizeof (sa.sun_path) - 1);
+
+       status = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
+       if (status != 0)
+       {
+               fprintf (stderr, "connect failed: %s\n",
+                               strerror (errno));
+               return (-1);
+       }
+
+       fh = fdopen (fd, "r+");
+       if (fh == NULL)
+       {
+               fprintf (stderr, "fdopen failed: %s\n",
+                               strerror (errno));
+               close (fd);
+               return (-1);
+       }
+
+       fprintf (fh, "GETVAL %s/%s\n", hostname_g, value_string_g);
+       fflush (fh);
+
+       if (fgets (buffer, sizeof (buffer), fh) == NULL)
+       {
+               fprintf (stderr, "fgets failed: %s\n",
+                               strerror (errno));
+               close (fd);
+               return (-1);
+       }
+       close (fd); fd = -1;
+
+       values_num = atoi (buffer);
+       if (values_num < 1)
+               return (-1);
+
+       values = (double *) malloc (values_num * sizeof (double));
+       if (values == NULL)
+       {
+               fprintf (stderr, "malloc failed: %s\n",
+                               strerror (errno));
+               return (-1);
+       }
+
+       values_names = (char **) malloc (values_num * sizeof (char *));
+       if (values_names == NULL)
+       {
+               fprintf (stderr, "malloc failed: %s\n",
+                               strerror (errno));
+               free (values);
+               return (-1);
+       }
+
+       {
+               char *ptr = strchr (buffer, ' ') + 1;
+               char *key;
+               char *value;
+
+               i = 0;
+               while ((key = strtok (ptr, " \t")) != NULL)
+               {
+                       ptr = NULL;
+                       value = strchr (key, '=');
+                       if (value == NULL)
+                               continue;
+                       *value = '\0'; value++;
+
+                       if (ignore_ds (key) != 0)
+                               continue;
+
+                       values_names[i] = strdup (key);
+                       values[i] = atof (value);
+
+                       i++;
+                       if (i >= values_num)
+                               break;
+               }
+               values_num = i;
+       }
+
+       *ret_values_num = values_num;
+       *ret_values = values;
+       *ret_values_names = values_names;
+
+       return (0);
+} /* int get_values */
+
+static void usage (const char *name)
+{
+       fprintf (stderr, "Usage: %s <-s socket> <-n value_spec> <-H hostname> [options]\n"
+                       "\n"
+                       "Valid options are:\n"
+                       "  -s <socket>    Path to collectd's UNIX-socket.\n"
+                       "  -n <v_spec>    Value specification to get from collectd.\n"
+                       "                 Format: `plugin-instance/type-instance'\n"
+                       "  -d <ds>        Select the DS to examine. May be repeated to examine multiple\n"
+                       "                 DSes. By default all DSes are used.\n"
+                       "  -g <consol>    Method to use to consolidate several DSes.\n"
+                       "                 Valid arguments are `none', `average' and `sum'\n"
+                       "  -H <host>      Hostname to query the values for.\n"
+                       "  -c <range>     Critical range\n"
+                       "  -w <range>     Warning range\n"
+                       "\n"
+                       "Consolidation functions:\n"
+                       "  none:          Apply the warning- and critical-ranges to each data-source\n"
+                       "                 individually.\n"
+                       "  average:       Calculate the average of all matching DSes and apply the\n"
+                       "                 warning- and critical-ranges to the calculated average.\n"
+                       "  sum:           Apply the ranges to the sum of all DSes.\n"
+                       "\n", name);
+       exit (1);
+} /* void usage */
+
+int do_check_con_none (int values_num, double *values, char **values_names)
+{
+       int i;
+
+       int num_critical = 0;
+       int num_warning  = 0;
+       int num_okay = 0;
+
+       for (i = 0; i < values_num; i++)
+       {
+               if (isnan (values[i]))
+                       num_warning++;
+               else if (match_range (&range_critical_g, values[i]) != 0)
+                       num_critical++;
+               else if (match_range (&range_warning_g, values[i]) != 0)
+                       num_warning++;
+               else
+                       num_okay++;
+       }
+
+       if ((num_critical != 0) || (values_num == 0))
+       {
+               printf ("CRITICAL: %i critical, %i warning, %i okay\n",
+                               num_critical, num_warning, num_okay);
+               return (RET_CRITICAL);
+       }
+       else if (num_warning != 0)
+       {
+               printf ("WARNING: %i warning, %i okay\n",
+                               num_warning, num_okay);
+               return (RET_WARNING);
+       }
+       else
+       {
+               printf ("OKAY: %i okay\n", num_okay);
+               return (RET_OKAY);
+       }
+
+       return (RET_UNKNOWN);
+} /* int do_check_con_none */
+
+int do_check_con_average (int values_num, double *values, char **values_names)
+{
+       int i;
+       double total;
+       int total_num;
+
+       total = 0.0;
+       total_num = 0;
+       for (i = 0; i < values_num; i++)
+       {
+               if (!isnan (values[i]))
+               {
+                       total += values[i];
+                       total_num++;
+               }
+       }
+
+       if (total_num == 0)
+       {
+               printf ("WARNING: No defined values found\n");
+               return (RET_WARNING);
+       }
+
+       if (match_range (&range_critical_g, total / total_num) != 0)
+       {
+               printf ("CRITICAL: Average = %lf\n",
+                               (double) (total / total_num));
+               return (RET_CRITICAL);
+       }
+       else if (match_range (&range_warning_g, total / total_num) != 0)
+       {
+               printf ("WARNING: Average = %lf\n",
+                               (double) (total / total_num));
+               return (RET_WARNING);
+       }
+       else
+       {
+               printf ("OKAY: Average = %lf\n",
+                               (double) (total / total_num));
+               return (RET_OKAY);
+       }
+
+       return (RET_UNKNOWN);
+} /* int do_check_con_average */
+
+int do_check_con_sum (int values_num, double *values, char **values_names)
+{
+       int i;
+       double total;
+       int total_num;
+
+       total = 0.0;
+       total_num = 0;
+       for (i = 0; i < values_num; i++)
+       {
+               if (!isnan (values[i]))
+               {
+                       total += values[i];
+                       total_num++;
+               }
+       }
+
+       if (total_num == 0)
+       {
+               printf ("WARNING: No defined values found\n");
+               return (RET_WARNING);
+       }
+
+       if (match_range (&range_critical_g, total) != 0)
+       {
+               printf ("CRITICAL: Sum = %lf\n", total);
+               return (RET_CRITICAL);
+       }
+       else if (match_range (&range_warning_g, total) != 0)
+       {
+               printf ("WARNING: Sum = %lf\n", total);
+               return (RET_WARNING);
+       }
+       else
+       {
+               printf ("OKAY: Sum = %lf\n", total);
+               return (RET_OKAY);
+       }
+
+       return (RET_UNKNOWN);
+} /* int do_check_con_sum */
+
+int do_check (void)
+{
+       double  *values;
+       char   **values_names;
+       int      values_num;
+
+       if (get_values (&values_num, &values, &values_names) != 0)
+       {
+               fputs ("ERROR: Cannot get values from daemon\n", stdout);
+               return (RET_CRITICAL);
+       }
+
+       if (consolitation_g == CON_NONE)
+               return (do_check_con_none (values_num, values, values_names));
+       else if (consolitation_g == CON_AVERAGE)
+               return (do_check_con_average (values_num, values, values_names));
+       else if (consolitation_g == CON_SUM)
+               return (do_check_con_sum (values_num, values, values_names));
+
+       free (values);
+       free (values_names);
+
+       return (RET_UNKNOWN);
+}
+
+int main (int argc, char **argv)
+{
+       range_critical_g.min = NAN;
+       range_critical_g.max = NAN;
+       range_critical_g.invert = 0;
+
+       range_warning_g.min = NAN;
+       range_warning_g.max = NAN;
+       range_warning_g.invert = 0;
+
+       while (42)
+       {
+               int c;
+
+               c = getopt (argc, argv, "w:c:s:n:H:g:d:h");
+               if (c < 0)
+                       break;
+
+               switch (c)
+               {
+                       case 'c':
+                               parse_range (optarg, &range_critical_g);
+                               break;
+                       case 'w':
+                               parse_range (optarg, &range_warning_g);
+                               break;
+                       case 's':
+                               socket_file_g = optarg;
+                               break;
+                       case 'n':
+                               value_string_g = optarg;
+                               break;
+                       case 'H':
+                               hostname_g = optarg;
+                               break;
+                       case 'g':
+                               if (strcasecmp (optarg, "none") == 0)
+                                       consolitation_g = CON_NONE;
+                               else if (strcasecmp (optarg, "average") == 0)
+                                       consolitation_g = CON_AVERAGE;
+                               else if (strcasecmp (optarg, "sum") == 0)
+                                       consolitation_g = CON_SUM;
+                               else
+                                       usage (argv[0]);
+                               break;
+                       case 'd':
+                       {
+                               char **tmp;
+                               tmp = (char **) realloc (match_ds_g,
+                                               (match_ds_num_g + 1)
+                                               * sizeof (char *));
+                               if (tmp == NULL)
+                               {
+                                       fprintf (stderr, "realloc failed: %s\n",
+                                                       strerror (errno));
+                                       return (RET_UNKNOWN);
+                               }
+                               match_ds_g = tmp;
+                               match_ds_g[match_ds_num_g] = strdup (optarg);
+                               if (match_ds_g[match_ds_num_g] == NULL)
+                               {
+                                       fprintf (stderr, "strdup failed: %s\n",
+                                                       strerror (errno));
+                                       return (RET_UNKNOWN);
+                               }
+                               match_ds_num_g++;
+                               break;
+                       }
+                       default:
+                               usage (argv[0]);
+               } /* switch (c) */
+       }
+
+       if ((socket_file_g == NULL) || (value_string_g == NULL)
+                       || (hostname_g == NULL))
+               usage (argv[0]);
+
+       return (do_check ());
+} /* int main */
diff --git a/src/collectd-nagios.pod b/src/collectd-nagios.pod
new file mode 100644 (file)
index 0000000..e69f18e
--- /dev/null
@@ -0,0 +1,102 @@
+=head1 NAME
+
+collectd-nagios - Nagios plugin for querying collectd
+
+=head1 SYNOPSIS
+
+collectd-nagios B<-s> I<socket> B<-n> I<value_spec> B<-H> I<hostname> I<[options]>
+
+=head1 DESCRIPTION
+
+This small program is the glue between collectd and nagios. collectd collects
+various performance statistics which is provides via the C<unixsock plugin>,
+see L<collectd-unixsock(5)>. This program is called by Nagios, connects to the
+UNIX socket and reads the values from collectd. It then returns B<OKAY>,
+B<WARNING> or B<CRITICAL> depending on the values and the ranges provided by
+Nagios.
+
+=head1 ARGUMENTS AND OPTIONS
+
+The following arguments and options are required and understood by
+collectd-nagios. The order of the arguments generally doesn't matter, as long
+as no argument is passed more than once.
+
+=over 4
+
+=item B<-s> I<socket>
+
+Path of the UNIX socket opened by collectd's C<unixsock plugin>.
+
+=item B<-n> I<value_spec>
+
+The value to read from collectd. The argument is in the form
+C<plugin[-instance]/type[-instance]>.
+
+=item B<-H> I<hostname>
+
+Hostname to query the values for.
+
+=item B<-d> I<data_source>
+
+Each I<value_spec> may be made of multiple "data sources". With this option you
+can select one or more data sources. To select multiple data sources simply
+specify this option again. If multiple data sources are examined they are
+handled according to the consolidation function given with the B<-g> option.
+
+=item B<-g> B<none>I<|>B<average>I<|>B<sum>
+
+When multiple data sources are selected from a value spec they can be handled
+differently dependin on this option. The values of the following meaning:
+
+=over 4
+
+=item B<none>
+
+No consolidation if done and the warning and critical regions are applied to
+each value independently.
+
+=item B<average>
+
+The warning and critical ranges are applied to the average of all values.
+
+=item B<sum>
+
+The warning and critical ranges are applied to the sum of all values.
+
+=back
+
+=item B<-c> I<range>
+
+=item B<-w> I<range>
+
+Set the critical (B<-c>) and warning (B<-w>) ranges. These options mostly
+follow the normal syntax of Nagios plugins. The general format is
+"I<min>B<:>I<max>". If a value if smaller than I<min> or bigger than I<max> a
+warning or critical is returned, otherwise okay is returned. I<min> (and the
+colon) may be omitted and are then assumed to be zero. If I<max> (but not the
+colon) is omitted I<max> is set to positive infinity. If either I<min> or
+I<max> if set to B<~> they are set to negative and positive infinity,
+respectively.
+
+=back
+
+=head1 RETURN VALUE
+
+As usual for Nagios plugins this program writes a short, one line status
+message to STDOUT and signals success or failure with it's return value. It
+exists with a return value of B<0> for success or B<1> or B<2> for warning and
+critical, respectively. If the values is not available or some other error
+occured it returnes B<3> for "unknown". 
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+L<collectd-unixsock(5)>,
+L<http://nagios.org/>
+
+=head1 AUTHOR
+
+Florian Forster E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>
+
+=cut
diff --git a/src/collectd-perl.pod b/src/collectd-perl.pod
new file mode 100644 (file)
index 0000000..90a35da
--- /dev/null
@@ -0,0 +1,164 @@
+=head1 NAME
+
+collectd-perl - Documentation of collectd's C<perl plugin>
+
+=head1 SYNOPSIS
+
+  # See collectd.conf(5)
+  LoadPlugin perl
+  # ...
+  <Plugin perl>
+    IncludeDir "/path/to/perl/plugins"
+    BaseName "Collectd::Plugin"
+    LoadPlugin "FooBar"
+  </Plugin>
+
+=head1 DESCRIPTION
+
+The C<perl plugin> includes a Perl-interpreter in collectd and provides
+Perl-equivalents of the plugin-functions. This makes it possible to write
+plugins for collectd in Perl. This is a lot more performant than executing a
+Perl-script every time you want to read a value with the C<exec plugin> (see
+L<collectd-exec(5)>) and provides a lot more functionality, too.
+
+=head1 DATA TYPES
+
+There are two more complex types you need to know about:
+
+=over 4
+
+=item Data-Set
+
+A data-set is a list of one or more data-sources. Each data-source defines a
+name, type, min- and max-value and the data-set wraps them up into one
+structure. The general layout looks like this:
+
+  [{
+    name => 'data_source_name',
+    type => DS_TYPE_COUNTER || DS_TYPE_GAUGE
+    min  => value || undef,
+    max  => value || undef
+  }, ...]
+
+=item Value-List
+
+A value-list is one structure which features an array of values and fields to
+identify the values, i. e. time and host, plugin name and plugin-instance as
+well as a type and type-instance. Since the "type" is not included in the
+value-list but is passed as an extra argument, the general layout looks like
+this:
+
+  {
+    values => [123, 0.5],
+    time   => time (),
+    host   => 'localhost',
+    plugin => 'myplugin',
+    plugin_instance => '',
+    type_instance   => ''
+  }
+
+=back
+
+=head1 METHODS
+
+The following functions provide the C-interface to Perl-modules. They are
+automatically exported into the module's namespace. You don't need to C<use>
+any special Modules to access them.
+
+=over 4
+
+=item B<plugin_register> (I<type>, I<name>, I<data>)
+
+Registers a callback-function or data-set.
+
+I<type> can be one of:
+
+=over 4
+
+=item TYPE_INIT
+
+=item TYPE_READ
+
+=item TYPE_WRITE
+
+=item TYPE_LOG
+
+=item TYPE_SHUTDOWN
+
+=item TYPE_DATASET
+
+=back
+
+I<name> is the name of the callback-function or the type of the data-set,
+depending on the value of I<type>. (Please note that the type of the data-set
+is the value passed as I<name> here and has nothing to do with the I<type>
+argument which simply tells B<plugin_register> what is being registered.)
+
+The last argument, I<data>, is either a function- or an array-reference. If
+I<type> is B<TYPE_DATASET>, then the I<data> argument must be an
+array-reference which points to an array of hashes. Each hash describes one
+data-source. For the exact layout see B<Data-Set> above.
+
+If the I<type> argument is any of the other types (B<TYPE_INIT>, B<TYPE_READ>,
+...) then I<data> is expected to be a function reference. These functions are
+called in the various stages of the daemon and are passed the following
+arguments:
+
+=over 4
+
+=item TYPE_INIT
+
+=item TYPE_READ
+
+=item TYPE_SHUTDOWN
+
+No arguments are passed
+
+=item TYPE_WRITE
+
+The arguments passed are I<type>, I<data-set>, and I<value-list>. I<type> is a
+string. For the layout of I<data-set> and I<value-list> see above.
+
+=item TYPE_LOG
+
+The arguments are I<log-level> and I<message>. The log level is small for
+important messages and high for less important messages. The least important
+level is B<LOG_DEBUG>, the most important level is B<LOG_ERR>. In between there
+are (from least to most important): B<LOG_INFO>, B<LOG_NOTICE>, and
+B<LOG_WARNING>. I<message> is simply a string B<without> a newline at the end.
+
+=back
+
+=item B<plugin_unregister> (I<type>, I<plugin>)
+
+Removes a callback or data-set from collectd's internal list of
+functionsE<nbsp>/ datasets.
+
+=item B<plugin_dispatch_values> (I<type>, I<value-list>)
+
+Submits a I<value-list> of type I<type> to the daemon. If the data-set I<type>
+is found (and the number of values matches the number of data-sources) then the
+type, data-set and value-list is passed to all write-callbacks that are
+registered with the daemon.
+
+=item B<plugin_log> (I<log-level>, I<message>)
+
+Submits a I<message> of level I<log-level> to collectd's logging mechanism.
+The message is passed to all log-callbacks that are registered with collectd.
+
+=back
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+L<collectd-exec(5)>,
+L<perl(1)>
+
+=head1 AUTHOR
+
+The C<perl plugin> has been written by Sebastian Harl E<lt>shE<nbsp>atE<nbsp>tokkee.orgE<gt>.
+
+This manpage has been written by Florian Forster E<lt>octoE<nbsp>atE<nbsp>verplant.orgE<gt>.
+
+=cut
diff --git a/src/collectd-unixsock.pod b/src/collectd-unixsock.pod
new file mode 100644 (file)
index 0000000..55e46b8
--- /dev/null
@@ -0,0 +1,115 @@
+=head1 NAME
+
+collectd-unixsock - Documentation of collectd's C<unixsock plugin>
+
+=head1 SYNOPSIS
+
+  # See collectd.conf(5)
+  LoadPlugin unixsock
+  # ...
+  <Plugin unixsock>
+    SocketFile "/path/to/socket"
+    SocketGroup "collectd"
+    SocketPerms "0770"
+  </Plugin>
+
+=head1 DESCRIPTION
+
+The C<unixsock plugin> opens an UNIX-socket over which one can interact with
+the daemon. This can be used to use the values collected by collectd in other
+applications, such as monitoring, or submit externally collected values to
+collectd.
+
+This plugin is used by L<collectd-nagios(1)> to check if some value is in a
+certain range and exit with a Nagios-compatible exit code.
+
+=head1 COMMANDS
+
+Upon start the C<unixsock plugin> opens a UNIX-socket and waits for
+connections. Once a connection is established the client can send commands to
+the daemon which it will answer, if it understand them.
+
+The following commands are implemented:
+
+=over 4
+
+=item B<GETVAL> I<Identifier>
+
+If the value identified by I<Identifier> (see below) is found the complete
+value-list is returned. The response is a space seperated list of
+name-value-pairs:
+
+I<num> I<name>B<=>I<value>[ I<name>B<=>I<value>[ ...]]
+
+If I<num> is less then zero, an error occured. Otherwise it contains the
+number of values that follow. Each value is of the form I<name>B<=>I<value>.
+Counter-values are converted to a rate, e.E<nbsp>g. bytes per second.
+Undefined values are returned as B<NaN>.
+
+Example:
+  -> | GETVAL myhost/cpu-0/cpu-user
+  <- | 1 value=1.260000e+00
+
+=item B<PUTVAL> I<Identifier> I<Valuelist>
+
+Submits a value (identified by I<Identifier>, see below) to the daemon which
+will dispatch it to all it's write-plugins. The I<Valuelist> is a
+colon-seperated list of values, either an integer if the data-source is a
+counter, of a double if the data-source if of type "gauge". You can submit an
+undefined gauge-value by using B<U>. When submitting B<U> to a counter the
+behavior is undefined.
+
+Example:
+  -> | PUTVAL testhost/interface/if_octets-test0 1179574444:123:456
+  <- | 0 Success
+
+=back
+
+=head2 Identifiers
+
+Value or value-lists are identified in a uniform fassion:
+
+I<Hostname>/I<Plugin>/I<Type>
+
+Where I<Plugin> and I<Type> are both either of type "I<Name>" or
+"I<Name>-I<Instance>". This sounds more complicated than it is, so here are
+some examples:
+
+  myhost/cpu-0/cpu-user
+  myhost/load/load
+  myhost/memory/memory-used
+  myhost/disk-sda/disk_octets
+
+=head2 Return values
+
+Unless otherwise noted the plugin answers with a line of the following form:
+
+I<Num> I<Message>
+
+If I<Num> is zero the message indicates success, if I<Num> is non-zero the
+message indicates failure. I<Message> is a human-readable string that describes
+the return value further.
+
+Commands that return values may use I<Num> to return the number of values that
+follow, such as the B<GETVAL> command. These commands usually return a negative
+value on failure and never return zero.
+
+=head1 ABSTRACTION LAYER
+
+Shipped with the sourcecode comes the Perl-Module L<Collectd::Unixsock> which
+provides an abstraction layer over the actual socket connection. It can be
+found in the directory F<contrib/PerlLib>. If you want to use Perl to
+communicate with the daemon, you're encouraged to use and expand this module.
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+L<collectd-nagios(1)>,
+L<unix(7)>
+
+=head1 AUTHOR
+
+Florian Forster E<lt>octo@verplant.orgE<gt>
+
+=cut
index a93cc28..4fbd5c0 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/collectd.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 
 #include "collectd.h"
 #include "common.h"
-#include "utils_debug.h"
 
-#include "network.h"
 #include "plugin.h"
 #include "configfile.h"
+#include "types_list.h"
 
-static int loop = 0;
-
+/*
+ * Global variables
+ */
+char hostname_g[DATA_MAX_NAME_LEN];
+int  interval_g;
 #if HAVE_LIBKSTAT
 kstat_ctl_t *kc;
 #endif /* HAVE_LIBKSTAT */
 
-/*
- * exported variables
- */
-time_t curtime;
-int    operating_mode;
+static int loop = 0;
 
 static void sigIntHandler (int signal)
 {
@@ -51,34 +48,92 @@ static void sigTermHandler (int signal)
        loop++;
 }
 
-static int change_basedir (char *dir)
+static int init_global_variables (void)
 {
-       int dirlen = strlen (dir);
+       const char *str;
+
+       str = global_option_get ("Hostname");
+       if (str != NULL)
+       {
+               strncpy (hostname_g, str, sizeof (hostname_g));
+       }
+       else
+       {
+               if (gethostname (hostname_g, sizeof (hostname_g)) != 0)
+               {
+                       fprintf (stderr, "`gethostname' failed and no "
+                                       "hostname was configured.\n");
+                       return (-1);
+               }
+       }
+       DEBUG ("hostname_g = %s;", hostname_g);
+
+       str = global_option_get ("Interval");
+       if (str == NULL)
+               str = "10";
+       interval_g = atoi (str);
+       if (interval_g <= 0)
+       {
+               fprintf (stderr, "Cannot set the interval to a correct value.\n"
+                               "Please check your settings.\n");
+               return (-1);
+       }
+       DEBUG ("interval_g = %i;", interval_g);
+
+       return (0);
+} /* int init_global_variables */
+
+static int change_basedir (const char *orig_dir)
+{
+       char *dir = strdup (orig_dir);
+       int dirlen;
+       int status;
+
+       if (dir == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("strdup failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
        
+       dirlen = strlen (dir);
        while ((dirlen > 0) && (dir[dirlen - 1] == '/'))
                dir[--dirlen] = '\0';
 
        if (dirlen <= 0)
                return (-1);
 
-       if (chdir (dir) == -1)
+       status = chdir (dir);
+       free (dir);
+
+       if (status != 0)
        {
                if (errno == ENOENT)
                {
-                       if (mkdir (dir, 0755) == -1)
+                       if (mkdir (orig_dir, 0755) == -1)
                        {
-                               syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("mkdir (%s): %s", orig_dir,
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
                                return (-1);
                        }
-                       else if (chdir (dir) == -1)
+                       else if (chdir (orig_dir) == -1)
                        {
-                               syslog (LOG_ERR, "chdir (%s): %s", dir, strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("chdir (%s): %s", orig_dir,
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
                                return (-1);
                        }
                }
                else
                {
-                       syslog (LOG_ERR, "chdir: %s", strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("chdir (%s): %s", orig_dir,
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        return (-1);
                }
        }
@@ -92,7 +147,7 @@ static void update_kstat (void)
        if (kc == NULL)
        {
                if ((kc = kstat_open ()) == NULL)
-                       syslog (LOG_ERR, "Unable to open kstat control structure");
+                       ERROR ("Unable to open kstat control structure");
        }
        else
        {
@@ -100,11 +155,11 @@ static void update_kstat (void)
                kid = kstat_chain_update (kc);
                if (kid > 0)
                {
-                       syslog (LOG_INFO, "kstat chain has been updated");
+                       INFO ("kstat chain has been updated");
                        plugin_init_all ();
                }
                else if (kid < 0)
-                       syslog (LOG_ERR, "kstat chain update failed");
+                       ERROR ("kstat chain update failed");
                /* else: everything works as expected */
        }
 
@@ -132,29 +187,14 @@ static void exit_usage (char *name)
                        "  Config-File       "CONFIGFILE"\n"
                        "  PID-File          "PIDFILE"\n"
                        "  Data-Directory    "PKGLOCALSTATEDIR"\n"
-#if COLLECT_DEBUG
-                       "  Log-File          "LOGFILE"\n"
-#endif
-                       "  Step              "COLLECTD_STEP" seconds\n"
-                       "  Heartbeat         "COLLECTD_HEARTBEAT" seconds\n"
                        "\n"PACKAGE" "VERSION", http://collectd.org/\n"
                        "by Florian octo Forster <octo@verplant.org>\n"
                        "for contributions see `AUTHORS'\n");
        exit (0);
 } /* static void exit_usage (char *name) */
 
-static int start_client (void)
+static int do_init (void)
 {
-       int step;
-
-       struct timeval tv_now;
-       struct timeval tv_next;
-       struct timespec ts_wait;
-
-       step = atoi (COLLECTD_STEP);
-       if (step <= 0)
-               step = 10;
-
 #if HAVE_LIBKSTAT
        kc = NULL;
        update_kstat ();
@@ -163,47 +203,63 @@ static int start_client (void)
 #if HAVE_LIBSTATGRAB
        if (sg_init ())
        {
-               syslog (LOG_ERR, "sg_init: %s", sg_str_error (sg_get_error ()));
+               ERROR ("sg_init: %s", sg_str_error (sg_get_error ()));
                return (-1);
        }
 
        if (sg_drop_privileges ())
        {
-               syslog (LOG_ERR, "sg_drop_privileges: %s", sg_str_error (sg_get_error ()));
+               ERROR ("sg_drop_privileges: %s", sg_str_error (sg_get_error ()));
                return (-1);
        }
 #endif
 
+       read_types_list ();
        plugin_init_all ();
 
+       return (0);
+} /* int do_init () */
+
+
+static int do_loop (void)
+{
+       struct timeval tv_now;
+       struct timeval tv_next;
+       struct timespec ts_wait;
+
        while (loop == 0)
        {
                if (gettimeofday (&tv_next, NULL) < 0)
                {
-                       syslog (LOG_ERR, "gettimeofday failed: %s", strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("gettimeofday failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        return (-1);
                }
-               tv_next.tv_sec += step;
+               tv_next.tv_sec += interval_g;
 
 #if HAVE_LIBKSTAT
                update_kstat ();
 #endif
-               /* `curtime' is used by many (all?) plugins as the
-                * data-sample-time passed to RRDTool */
-               curtime = time (NULL);
 
                /* Issue all plugins */
                plugin_read_all (&loop);
 
                if (gettimeofday (&tv_now, NULL) < 0)
                {
-                       syslog (LOG_ERR, "gettimeofday failed: %s", strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("gettimeofday failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        return (-1);
                }
 
                if (timeval_sub_timespec (&tv_next, &tv_now, &ts_wait) != 0)
                {
-                       syslog (LOG_WARNING, "Not sleeping because `timeval_sub_timespec' returned non-zero!");
+                       WARNING ("Not sleeping because "
+                                       "`timeval_sub_timespec' returned "
+                                       "non-zero!");
                        continue;
                }
 
@@ -211,61 +267,36 @@ static int start_client (void)
                {
                        if (errno != EINTR)
                        {
-                               syslog (LOG_ERR, "nanosleep failed: %s", strerror (errno));
-                               break;
+                               char errbuf[1024];
+                               ERROR ("nanosleep failed: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
+                               return (-1);
                        }
                }
-       }
+       } /* while (loop == 0) */
 
+       DEBUG ("return (0);");
        return (0);
-} /* static int start_client (void) */
+} /* int do_loop */
 
-#if HAVE_LIBRRD
-static int start_server (void)
+static int do_shutdown (void)
 {
-       char *host;
-       char *type;
-       char *instance;
-       char *values;
-
-       int  error_counter = 0;
-       int  status;
-
-       while ((loop == 0) && (error_counter < 3))
-       {
-               status = network_receive (&host, &type, &instance, &values);
-
-               if (status != 0)
-               {
-                       if (status < 0)
-                               error_counter++;
-                       continue;
-               }
-               error_counter = 0;
-
-               plugin_write (host, type, instance, values);
-
-               if (host     != NULL) free (host);     host     = NULL;
-               if (type     != NULL) free (type);     type     = NULL;
-               if (instance != NULL) free (instance); instance = NULL;
-               if (values   != NULL) free (values);   values   = NULL;
-       }
-       
+       plugin_shutdown_all ();
        return (0);
-} /* static int start_server (void) */
-#endif /* HAVE_LIBRRD */
+} /* int do_shutdown */
 
 #if COLLECT_DAEMON
-static int pidfile_create (const char *file)
+static int pidfile_create (void)
 {
        FILE *fh;
-
-       if (file == NULL)
-               file = PIDFILE;
+       const char *file = global_option_get ("PIDFile");
 
        if ((fh = fopen (file, "w")) == NULL)
        {
-               syslog (LOG_ERR, "fopen (%s): %s", file, strerror (errno));
+               char errbuf[1024];
+               ERROR ("fopen (%s): %s", file,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                return (1);
        }
 
@@ -274,14 +305,12 @@ static int pidfile_create (const char *file)
 
        return (0);
 } /* static int pidfile_create (const char *file) */
-#endif /* COLLECT_DAEMON */
 
-#if COLLECT_DAEMON
-static int pidfile_remove (const char *file)
+static int pidfile_remove (void)
 {
-       if (file == NULL) {
-               file = PIDFILE;
-       }
+       const char *file = global_option_get ("PIDFile");
+
+       DEBUG ("unlink (%s)", (file != NULL) ? file : "<null>");
        return (unlink (file));
 } /* static int pidfile_remove (const char *file) */
 #endif /* COLLECT_DAEMON */
@@ -290,33 +319,21 @@ int main (int argc, char **argv)
 {
        struct sigaction sigIntAction;
        struct sigaction sigTermAction;
-       char *datadir    = PKGLOCALSTATEDIR;
        char *configfile = CONFIGFILE;
+       int test_config  = 0;
+       const char *basedir;
 #if COLLECT_DAEMON
        struct sigaction sigChldAction;
-       char *pidfile    = NULL;
        pid_t pid;
        int daemonize    = 1;
 #endif
-#if COLLECT_DEBUG
-       char *logfile    = LOGFILE;
-#endif
-
-#if HAVE_LIBRRD
-       operating_mode = MODE_LOCAL;
-#else
-       operating_mode = MODE_CLIENT;
-#endif
-
-       /* open syslog */
-       openlog (PACKAGE, LOG_CONS | LOG_PID, LOG_DAEMON);
 
        /* read options */
        while (1)
        {
                int c;
 
-               c = getopt (argc, argv, "hC:"
+               c = getopt (argc, argv, "htC:"
 #if COLLECT_DAEMON
                                "fP:"
 #endif
@@ -330,9 +347,12 @@ int main (int argc, char **argv)
                        case 'C':
                                configfile = optarg;
                                break;
+                       case 't':
+                               test_config = 1;
+                               break;
 #if COLLECT_DAEMON
                        case 'P':
-                               pidfile = optarg;
+                               global_option_set ("PIDFile", optarg);
                                break;
                        case 'f':
                                daemonize = 0;
@@ -344,11 +364,6 @@ int main (int argc, char **argv)
                } /* switch (c) */
        } /* while (1) */
 
-#if COLLECT_DEBUG
-       if ((logfile = cf_get_option ("LogFile", LOGFILE)) != NULL)
-               DBG_STARTFILE (logfile, "Debug file opened.");
-#endif
-
        /*
         * Read options from the config file, the environment and the command
         * line (in that order, with later options overwriting previous ones in
@@ -366,37 +381,46 @@ int main (int argc, char **argv)
         * Change directory. We do this _after_ reading the config and loading
         * modules to relative paths work as expected.
         */
-       if ((datadir = cf_get_option ("DataDir", PKGLOCALSTATEDIR)) == NULL)
+       if ((basedir = global_option_get ("BaseDir")) == NULL)
        {
-               fprintf (stderr, "Don't have a datadir to use. This should not happen. Ever.");
+               fprintf (stderr, "Don't have a basedir to use. This should not happen. Ever.");
                return (1);
        }
-       if (change_basedir (datadir))
+       else if (change_basedir (basedir))
        {
-               fprintf (stderr, "Error: Unable to change to directory `%s'.\n", datadir);
+               fprintf (stderr, "Error: Unable to change to directory `%s'.\n", basedir);
                return (1);
        }
 
+       /*
+        * Set global variables or, if that failes, exit. We cannot run with
+        * them being uninitialized. If nothing is configured, then defaults
+        * are being used. So this means that the user has actually done
+        * something wrong.
+        */
+       if (init_global_variables () != 0)
+               return (1);
+
+       if (test_config)
+               return (0);
+
 #if COLLECT_DAEMON
        /*
         * fork off child
         */
+       memset (&sigChldAction, '\0', sizeof (sigChldAction));
        sigChldAction.sa_handler = SIG_IGN;
        sigaction (SIGCHLD, &sigChldAction, NULL);
 
-       if ((pidfile == NULL)
-                       && ((pidfile = cf_get_option ("PIDFile", PIDFILE)) == NULL))
-       {
-               fprintf (stderr, "Cannot obtain pidfile. This shoud not happen. Ever.");
-               return (1);
-       }
-
        if (daemonize)
        {
                if ((pid = fork ()) == -1)
                {
                        /* error */
-                       fprintf (stderr, "fork: %s", strerror (errno));
+                       char errbuf[1024];
+                       fprintf (stderr, "fork: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        return (1);
                }
                else if (pid != 0)
@@ -410,7 +434,7 @@ int main (int argc, char **argv)
                setsid ();
 
                /* Write pidfile */
-               if (pidfile_create (pidfile))
+               if (pidfile_create ())
                        exit (2);
 
                /* close standard descriptors */
@@ -420,17 +444,17 @@ int main (int argc, char **argv)
 
                if (open ("/dev/null", O_RDWR) != 0)
                {
-                       syslog (LOG_ERR, "Error: Could not connect `STDIN' to `/dev/null'");
+                       ERROR ("Error: Could not connect `STDIN' to `/dev/null'");
                        return (1);
                }
                if (dup (0) != 1)
                {
-                       syslog (LOG_ERR, "Error: Could not connect `STDOUT' to `/dev/null'");
+                       ERROR ("Error: Could not connect `STDOUT' to `/dev/null'");
                        return (1);
                }
                if (dup (0) != 2)
                {
-                       syslog (LOG_ERR, "Error: Could not connect `STDERR' to `/dev/null'");
+                       ERROR ("Error: Could not connect `STDERR' to `/dev/null'");
                        return (1);
                }
        } /* if (daemonize) */
@@ -439,37 +463,29 @@ int main (int argc, char **argv)
        /*
         * install signal handlers
         */
+       memset (&sigIntAction, '\0', sizeof (sigIntAction));
        sigIntAction.sa_handler = sigIntHandler;
        sigaction (SIGINT, &sigIntAction, NULL);
 
+       memset (&sigTermAction, '\0', sizeof (sigTermAction));
        sigTermAction.sa_handler = sigTermHandler;
        sigaction (SIGTERM, &sigTermAction, NULL);
 
        /*
         * run the actual loops
         */
-#if HAVE_LIBRRD
-       if (operating_mode == MODE_SERVER)
-               start_server ();
-       else /* if (operating_mode == MODE_CLIENT || operating_mode == MODE_LOCAL || operating_mode == MODE_LOG) */
-#endif
-               start_client ();
-
-       plugin_shutdown_all ();
-
-#if COLLECT_DEBUG
-       if (logfile != NULL)
-               DBG_STOPFILE("debug file closed.");
-#endif
+       do_init ();
+       do_loop ();
 
        /* close syslog */
-       syslog (LOG_INFO, "Exiting normally");
-       closelog ();
+       INFO ("Exiting normally");
+
+       do_shutdown ();
 
 #if COLLECT_DAEMON
        if (daemonize)
-               pidfile_remove (pidfile);
+               pidfile_remove ();
 #endif /* COLLECT_DAEMON */
 
        return (0);
-} /* int main (int argc, char **argv) */
+} /* int main */
index 977dde2..94c60ee 100644 (file)
@@ -4,21 +4,13 @@
 # http://collectd.org/
 #
 
-# Select one of the following modes:
-@BUILD_WITH_RRDTOOL_TRUE@Mode Local
-#Mode Server
-@BUILD_WITH_RRDTOOL_FALSE@Mode Client
-#Mode Log
-
-# Set the IP-address(es) and UDP-port(s) to send packets to. `Listen' can be
-# used as a synonym for `Server' to make the config file more readable when in
-# server mode. The port option is optional.
-#Server ff18::efc0:4a42 25826
-#Server 239.192.74.66 25826
-
-#DataDir   /opt/collectd/var/lib/collectd
-#PIDFile   /opt/collectd/var/run/collectd.pid
-#PluginDir /opt/collectd/lib/collectd
+#Hostname    "localhost"
+#BaseDir     "@prefix@/var/lib/@PACKAGE_NAME@"
+#PIDFile     "@prefix@/var/run/@PACKAGE_NAME@.pid"
+#PluginDir   "@prefix@/lib/@PACKAGE_NAME@"
+#TypesDB     "@prefix@/lib/@PACKAGE_NAME@/types.db"
+#Interval     10
+#ReadThreads  5
 
 @BUILD_MODULE_APACHE_TRUE@LoadPlugin apache
 @BUILD_MODULE_APCUPS_TRUE@LoadPlugin apcups
 @BUILD_MODULE_BATTERY_TRUE@LoadPlugin battery
 @BUILD_MODULE_CPU_TRUE@LoadPlugin cpu
 @BUILD_MODULE_CPUFREQ_TRUE@LoadPlugin cpufreq
+@BUILD_MODULE_CSV_TRUE@LoadPlugin csv
 @BUILD_MODULE_DF_TRUE@LoadPlugin df
 @BUILD_MODULE_DISK_TRUE@LoadPlugin disk
 @BUILD_MODULE_DNS_TRUE@LoadPlugin dns
+@BUILD_MODULE_EMAIL_TRUE@LoadPlugin email
+@BUILD_MODULE_ENTROPY_TRUE@LoadPlugin entropy
+@BUILD_MODULE_EXEC_TRUE@LoadPlugin exec
 @BUILD_MODULE_HDDTEMP_TRUE@LoadPlugin hddtemp
+@BUILD_MODULE_INTERFACE_TRUE@LoadPlugin interface
+@BUILD_MODULE_IPTABLES_TRUE@LoadPlugin iptables
+@BUILD_MODULE_IRQ_TRUE@LoadPlugin irq
 @BUILD_MODULE_LOAD_TRUE@LoadPlugin load
+@BUILD_MODULE_LOGFILE_TRUE@LoadPlugin logfile
 @BUILD_MODULE_MBMON_TRUE@LoadPlugin mbmon
 @BUILD_MODULE_MEMORY_TRUE@LoadPlugin memory
 @BUILD_MODULE_MULTIMETER_TRUE@LoadPlugin multimeter
 @BUILD_MODULE_MYSQL_TRUE@LoadPlugin mysql
+@BUILD_MODULE_NETWORK_TRUE@LoadPlugin network
 @BUILD_MODULE_NFS_TRUE@LoadPlugin nfs
 @BUILD_MODULE_NTPD_TRUE@LoadPlugin ntpd
+@BUILD_MODULE_NUT_TRUE@LoadPlugin nut
+@BUILD_MODULE_PERL_TRUE@LoadPlugin perl
 @BUILD_MODULE_PING_TRUE@LoadPlugin ping
 @BUILD_MODULE_PROCESSES_TRUE@LoadPlugin processes
+@BUILD_WITH_RRDTOOL_TRUE@LoadPlugin rrdtool
 @BUILD_MODULE_SENSORS_TRUE@LoadPlugin sensors
 @BUILD_MODULE_SERIAL_TRUE@LoadPlugin serial
 @BUILD_MODULE_SWAP_TRUE@LoadPlugin swap
+@BUILD_MODULE_SYSLOG_TRUE@LoadPlugin syslog
 @BUILD_MODULE_TAPE_TRUE@LoadPlugin tape
-@BUILD_MODULE_TRAFFIC_TRUE@LoadPlugin traffic
+@BUILD_MODULE_UNIXSOCK_TRUE@LoadPlugin unixsock
 @BUILD_MODULE_USERS_TRUE@LoadPlugin users
 @BUILD_MODULE_VSERVER_TRUE@LoadPlugin vserver
 @BUILD_MODULE_WIRELESS_TRUE@LoadPlugin wireless
 
 #<Plugin apache>
-#      URL http://localhost/status?auto
-#      User www-user
-#      Password secret
-#      CACert /etc/ssl/ca.crt
+#      URL "http://localhost/status?auto"
+#      User "www-user"
+#      Password "secret"
+#      CACert "/etc/ssl/ca.crt"
 #</Plugin>
 
 #<Plugin apcups>
-#      Host localhost
-#      Port 3551
+#      Host "localhost"
+#      Port "3551"
+#</Plugin>
+
+#<Plugin csv>
+#      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/csv"
+#</Plugin>
+
+#<Plugin df>
+#      Device "/dev/hda1"
+#      Device "192.168.0.2:/mnt/nfs"
+#      MountPoint "/home"
+#      FSType "ext3"
+#      IgnoreSelected false
 #</Plugin>
 
 #<Plugin dns>
-#      Interface eth0
-#      IgnoreSource 192.168.0.1
+#      Interface "eth0"
+#      IgnoreSource "192.168.0.1"
+#</Plugin>
+
+#<Plugin email>
+#      SocketGroup "collectd"
+#      SocketPerms "0770"
+#      MaxConns 5
+#</Plugin>
+
+#<Plugin exec>
+#      Exec user "/path/to/exec"
 #</Plugin>
 
 #<Plugin hddtemp>
-#      Host 127.0.0.1
+#      Host "127.0.0.1"
 #      Port 7634
 #</Plugin>
 
+#<Plugin interface>
+#      Interface "eth0"
+#      IgnoreSelected false
+#</Plugin>
+
+#<Plugin iptables>
+#      Chain table chain
+#</Plugin>
+
+#<Plugin irq>
+#      Irq 7
+#      Irq 8
+#      Irq 9
+#      IgnoreSelected true
+#</Plugin>
+
+#<Plugin logfile>
+#      LogLevel info
+#      File STDOUT
+#</Plugin>
+
 #<Plugin mbmon>
-#      Host 127.0.0.1
+#      Host "127.0.0.1"
 #      Port 411
 #</Plugin>
 
+#<Plugin mysql>
+#      Host "database.serv.er"
+#      User "db_user"
+#      Password "secret"
+#      Database "db_name"
+#</Plugin>
+
+#<Plugin network>
+#      Server "ff18::efc0:4a42" "25826"
+#      Server "239.192.74.66" "25826"
+#      Listen "ff18::efc0:4a42" "25826"
+#      Listen "239.192.74.66" "25826"
+#      TimeToLive "128"
+#      Forward false
+#      CacheFlush 1800
+#</Plugin>
+
 #<Plugin ntpd>
-#      Host localhost
+#      Host "localhost"
 #      Port 123
 #</Plugin>
 
-#<Plugin mysql>
-#      Host database.serv.er
-#      User db_user
-#      Password secret
-#      Database db_name
+#<Plugin nut>
+#      UPS "upsname@hostname:port"
+#</Plugin>
+
+#<Plugin perl>
+#      IncludeDir "/my/include/path"
+#      BaseName "Collectd::Plugin"
+#      LoadPlugin foo
 #</Plugin>
 
 #<Plugin ping>
-#      Host host.foo.bar
+#      Host "host.foo.bar"
 #</Plugin>
 
 #<Plugin processes>
-#      Process name
+#      Process "name"
+#</Plugin>
+
+#<Plugin rrdtool>
+#      DataDir "@prefix@/var/lib/@PACKAGE_NAME@/rrd"
+#      StepSize      10
+#      HeartBeat     20
+#      RRARows     1200
+#      XFF            0.1
+#      CacheTimeout 120
+#      CacheFlush   900
 #</Plugin>
 
-## There are two different naming schemes for the sensors plugin, to preserver
-## backwards compatibility. The `extended naming' is prefered, but not the
-## default for this reason. See `collectd.conf(5)' for more information.
 #<Plugin sensors>
-#      ExtendedSensorNaming false
-#      Sensor it8712-temp1
-#      Sensor it8712-fan3
-#      Sensor it8712-in8
-## -- OR --
-#      ExtendedSensorNaming true
-#      Sensor it8712-isa-0290/temperature-temp1
-#      Sensor it8712-isa-0290/fanspeed-fan3
-#      Sensor it8712-isa-0290/voltage-in8
-#
+#      Sensor "it8712-isa-0290/temperature-temp1"
+#      Sensor "it8712-isa-0290/fanspeed-fan3"
+#      Sensor "it8712-isa-0290/voltage-in8"
 #      IgnoreSelected false
 #</Plugin>
 
-#<Plugin traffic>
-#      Interface eth0
-#      IgnoreSelected false
+#<Plugin syslog>
+#      LogLevel info
+#</Plugin>
+
+#<Plugin unixsock>
+#      SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock"
+#      SocketGroup "collectd"
+#      SocketPerms "0660"
 #</Plugin>
index cffb7ad..939c72a 100644 (file)
@@ -4,105 +4,90 @@ collectd.conf - Configuration for the system statistics collection daemon B<coll
 
 =head1 SYNOPSIS
 
-  Mode    Client
-  DataDir /path/to/data/
-  PIDFile /path/to/pidfile/collectd.pid
-  LogFile /path/to/logfile/collectd.log
-  Server  123.123.123.123 12345
+  BaseDir "/path/to/data/"
+  PIDFile "/path/to/pidfile/collectd.pid"
+  Server  "123.123.123.123" 12345
 
   LoadPlugin cpu
   LoadPlugin load
   LoadPlugin ping
 
   <Plugin ping>
-    Host example.org
-    Host provider.net
+    Host "example.org"
+    Host "provider.net"
   </Plugin>
 
 =head1 DESCRIPTION
 
 This config file controls how the system statistics collection daemon
-B<collectd> behaves. The most significant options are B<Mode>, which controlls
-if the daemon will act as client, server or will be independent in local mode,
-and B<LoadPlugin> which controls which plugins to load.
+B<collectd> behaves. The most significant option is B<LoadPlugin>, which
+controls which plugins to load. These plugins ultimately define collectd's
+behavior.
 
 The syntax of this config file is similar to the config file of the famos
 B<Apache Webserver>. Each line containes either a key-value-pair or a
 section-start or -end. Empty lines and everything after the hash-symbol `#' is
-ignored.
+ignored. Values are either string, enclosed in double-quotes,
+(floating-point-)numbers or a boolean extression, i.E<nbsp>e. either B<true> or
+B<false>. String containing of only alphanumeric characters and underscores do
+not need to be quoted.
 
 =head1 GLOBAL OPTIONS
 
 =over 4
 
-=item B<Mode> (B<Local>|B<Client>|B<Server>|B<Log>)
+=item B<BaseDir> I<Directory>
 
-Sets the operating mode. See the section B<MODES> in L<collectd(1)> for a
-description. This option determines which other options are allowed. Defaults
-to B<Local>.
+Sets the base directory. This is the directory beneath all RRD-files are
+created. Possibly more subdirectories are created. This is also the working
+directory for the daemon.
 
 =item B<LoadPlugin> I<Plugin>
 
 Loads the plugin I<Plugin>. There must be at least one such line or B<collectd>
-will be mostly useless. The names of the plugins are listed in L<collectd(1)>.
+will be mostly useless.
 
 =item B<PIDFile> I<File>
 
 Sets where to write the PID file to. This file is overwritten when it exists
 and deleted when the program ist stopped. Some init-scripts might override this
-setting using the B<-P> commandline option. Available in B<all modes>.
-
-=item B<DataDir> I<Directory>
-
-Sets the data directory. This is the directory beneath all RRD-files are
-created. Possibly more subdirectories are created. This is also the working
-directory for the daemon. Available in B<all modes>, though the B<Client> mode
-won't write to this directory.
-
-=item B<LogFile> I<File>
+setting using the B<-P> commandline option.
 
-Sets the file to write debugging output to. This is only used if compiled with
-debugging enabled. It's ignored otherwise. Available in B<all modes>.
+=item B<PluginDir> I<Directory>
 
-=item B<Listen> I<Host> [I<Port>]
+Path to the plugins (shared objects) of collectd.
 
-=item B<Server> I<Host> [I<Port>]
+=item B<TypesDB> I<File>
 
-In B<client mode> the B<Server> statement sets the server to send datagrams to.
-The statement may occur multiple times to send each datagram to multiple
-destinations.
+Set the file that contains the data-set descriptions.
 
-In B<server mode> the B<Listen> statement sets the interfaces to bind to. When
-multiple statements are found the daemon will bind to multiple interfaces.
-
-The argument I<Host> may be a hostname, an IPv4 address or an IPv6 address. If
-the argument is a multicast address the daemon will join that multicast group.
-
-If no B<Listen> statement is found the server tries join both, the IPv6
-multicast group and the IPv4 multicast group. If no B<Server> statement is
-found the client will try to send data to the IPv6 multicast group first. If
-that failes the client will try the IPv4 multicast group.
-
-The default IPv6 multicast group is C<ff18::efc0:4a42>. The default IPv4
-multicast group is C<239.192.74.66>.
+=item B<Interval> I<Seconds>
 
-The optional I<Port> argument sets the port to use. It can either be given
-using a numeric port number or a service name. If the argument is omited the
-default port B<25826> is assumed.
+Configures the interval in which to query the read plugins. Obviously smaller
+values lead to a higher system load produces by collectd, while higher values
+lead to more coarse statistics.
 
-=item B<TimeToLive> I<1-255>
+=item B<ReadThreads> I<Num>
 
-Set the time-to-live of sent packets. This applies to all, unicast and
-multicast, and IPv4 and IPv6 packets. The default is to not change this value.
-That means that multicast packets will be sent with a TTL of C<1> (one) on most
-operating systems.
+Number of threads to start for reading plugins. The default value if B<5>, but
+you may want to increase this if you have more than five plugins that take a
+long time to read. Mostly those are plugin that do network-IO. Setting this to
+a value higher than the number of plugins you've loaded is totally useless.
 
 =back
 
 =head1 PLUGIN OPTIONS
 
 Some Plugins may register own options. These options must be inclosed in a
-C<Plugin>-Section. Which options exist depends on the plugin used:
+C<Plugin>-Section. Which options exist depends on the plugin used. Some plugins
+require external configuration, too. The C<apache plugin>, for example,
+required C<mod_status> to be configured in the webserver you're going to
+collect data from. These plugins are listed below as well, even if they don't
+require any configuration within collectd's configfile.
+
+A list of all plugins and a short summary for each plugin can be found in the
+F<README> file shipped with the sourcecode and hopefully binary packets as
+well.
 
 =head2 Plugin C<apache>
 
@@ -118,6 +103,10 @@ the following snipped to base your Apache config upon:
     </Location>
   </IfModule>
 
+Since it's C<mod_status> module is very similar to Apache's, B<lighttpd> is
+also supported. It introduces a new field, called C<BusyServers>, to count the
+number of currently connected clients. This field is also supported.
+
 The following options are accepted by the C<apache>-plugin:
 
 =over 4
@@ -160,6 +149,25 @@ TCP-Port to connect to. Defaults to B<3551>.
 
 =back
 
+=head2 Plugin C<cpufreq>
+
+This plugin doesn't have any options. It reads
+F</sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq> (for the first CPU
+installed) to get the current CPU frequency. If this file does not exist make
+sure B<cpufreqd> (L<http://cpufreqd.sourceforge.net/>) or a similar tool is
+installed and an "cpu governor" (that's a kernel module) is loaded.
+
+=head2 Plugin C<csv>
+
+=over 4
+
+=item B<DataDir> I<Directory>
+
+Set the directory to store RRD-files under. Per default RRD-files are generated
+beneath the daemon's working directory, i.E<nbsp>e. the B<BaseDir>.
+
+=back
+
 =head2 Plugin C<df>
 
 =over 4
@@ -226,8 +234,31 @@ at most B<16384> to prevent typos and dumb mistakes.
 
 =back
 
+=head2 Plugin C<exec>
+
+Please make sure to read L<collectd-exec(5)> before using this plugin. It
+contains valueable information on when the executable is executed and the
+output that is expected from it.
+
+=over 4
+
+=item B<Exec> I<User> I<Executable>
+
+Execute the executable I<Executable> as user I<User>.
+
+=back
+
 =head2 Plugin C<hddtemp>
 
+To get values from B<hddtemp> collectd connects to B<localhost> (127.0.0.1),
+port B<7634/tcp>. The B<Host> and B<Port> options can be used to change these
+default values, see below. C<hddtemp> has to be running to work correctly. If
+C<hddtemp> is not running timeouts may appear which may interfere with other
+statistics..
+
+The B<hddtemp> homepage can be found at
+L<http://www.guzu.net/linux/hddtemp.php>.
+
 =over 4
 
 =item B<Host> I<Hostname>
@@ -240,36 +271,127 @@ TCP-Port to connect to. Defaults to B<7634>.
 
 =back
 
-=head2 Plugin C<mbmon>
+=head2 Plugin C<interface>
 
 =over 4
 
-=item B<Host> I<Hostname>
+=item B<Interface> I<Interface>
 
-Hostname to connect to. Defaults to B<127.0.0.1>.
+Select this interface. By default these interfaces will then be collected. For
+a more detailed description see B<IgnoreSelected> below.
 
-=item B<Port> I<Port>
+=item B<IgnoreSelected> I<true>|I<false>
 
-TCP-Port to connect to. Defaults to B<411>.
+If no configuration if given, the B<traffic>-plugin will collect data from
+all interfaces. This may not be practical, especially for loopback- and
+similar interfaces. Thus, you can use the B<Interface>-option to pick the
+interfaces you're interested in. Sometimes, however, it's easier/prefered
+to collect all interfaces I<except> a few ones. This option enables you to
+do that: By setting B<IgnoreSelected> to I<true> the effect of
+B<Interface> is inversed: All selected interfaces are ignored and all
+other interfaces are collected.
 
 =back
 
-=head2 Plugin C<ntpd>
+=head2 Plugin C<iptables>
+
+=over 4
+
+=item B<Chain> I<Table> I<Chain> [I<Comment|Number> [I<Name>]]
+
+Select the rules to count. If only I<Table> and I<Chain> are given, this plugin
+will collect the counters of all rules which have a comment-match. The comment
+is then used as type-instance.
+
+If I<Comment> or I<Number> is given, only the rule with the matching comment or
+the I<n>th rule will be collected. Again, the comment (or the number) will be
+used as the type-instance.
+
+If I<Name> is supplied, it will be used as the type-instance instead of the
+comment or the number.
+
+=back
+
+=head2 Plugin C<irq>
+
+=over 4
+
+=item B<Irq> I<Irq>
+
+Select this irq. By default these irqs will then be collected. For a more
+detailed description see B<IgnoreSelected> below.
+
+=item B<IgnoreSelected> I<true>|I<false>
+
+If no configuration if given, the B<irq>-plugin will collect data from all
+irqs. This may not be practical, especially if no interrupts happen. Thus, you
+can use the B<Irq>-option to pick the interupt you're interested in.
+Sometimes, however, it's easier/prefered to collect all interupts I<except> a
+few ones. This option enables you to do that: By setting B<IgnoreSelected> to
+I<true> the effect of B<Irq> is inversed: All selected interupts are ignored
+and all other interupts are collected.
+
+=back
+
+=head2 Plugin C<logfile>
+
+=over 4
+
+=item B<LogLevel> B<debug|info|notice|warning|err>
+
+Sets the log-level. If, for example, set to B<notice>, then all events with
+severity B<notice>, B<warning>, or B<err> will be written to the logfile.
+
+=item B<File> I<File>
+
+Sets the file to write log messages to. The special strings B<stdout> and
+B<stderr> can be used to write to the standard output and standard error
+channels, respectively. This, of course, only makes much sense when collectd is
+running in foreground- or non-daemon-mode.
+
+=back
+
+=head2 Plugin C<mbmon>
+
+The C<mbmon plugin> uses mbmon to retrieve temperature, voltage, etc.
+
+Be default collectd connects to B<localhost> (127.0.0.1), port B<411/tcp>.  The
+B<Host> and B<Port> options can be used to change these values, see below.
+C<mbmon> has to be running to work correctly. If C<mbmon> is not running
+timeouts may appear which may interfere with other statistics..
+
+C<mbmon> must be run with the -r option ("print TAG and Value format");
+Debian's F</etc/init.d/mbmon> script already does this, other people
+will need to ensure that this is the case.
 
 =over 4
 
 =item B<Host> I<Hostname>
 
-Hostname of the host running B<ntpd>. Defaults to B<localhost>.
+Hostname to connect to. Defaults to B<127.0.0.1>.
 
 =item B<Port> I<Port>
 
-UDP-Port to connect to. Defaults to B<123>.
+TCP-Port to connect to. Defaults to B<411>.
 
 =back
 
 =head2 Plugin C<mysql>
 
+The C<mysql plugin> requires B<mysqlclient> to be installed. It connects to the
+database when started and keeps the connection up as long as possible. When the
+connection is interrupted for whatever reason it will try to re-connect. The
+plugin logs loud complaints in case anything goes wrong.
+
+This plugin issues C<SHOW STATUS> and evaluates C<Bytes_{received,sent}>,
+C<Com_*> and C<Handler_*> which correspond to F<mysql_octets.rrd>,
+F<mysql_commands-*.rrd> and F<mysql_handler-*.rrd>. Also, the values of
+C<Qcache_*> are put in F<mysql_qcache.rrd> and values of C<Threads_*> are put
+in F<mysql_threads.rrd>. Please refer to the B<MySQL reference manual>,
+I<5.2.4. Server Status Variables> for an explanation of these values.
+
+Use the following options to configure the plugin:
+
 =over 4
 
 =item B<Host> I<Hostname>
@@ -291,6 +413,109 @@ option for what this plugin does.
 
 =back
 
+=head2 Plugin C<network>
+
+=over 4
+
+=item B<Listen> I<Host> [I<Port>]
+
+=item B<Server> I<Host> [I<Port>]
+
+The B<Server> statement sets the server to send datagrams B<to>.  The statement
+may occur multiple times to send each datagram to multiple destinations.
+
+The B<Listen> statement sets the interfaces to bind to. When multiple
+statements are found the daemon will bind to multiple interfaces.
+
+The argument I<Host> may be a hostname, an IPv4 address or an IPv6 address. If
+the argument is a multicast address the daemon will join that multicast group.
+
+If no B<Listen> statement is found the server tries join both, the default IPv6
+multicast group and the default IPv4 multicast group. If no B<Server> statement
+is found the client will try to send data to the IPv6 multicast group first. If
+that failes the client will try the IPv4 multicast group.
+
+The default IPv6 multicast group is C<ff18::efc0:4a42>. The default IPv4
+multicast group is C<239.192.74.66>.
+
+The optional I<Port> argument sets the port to use. It can either be given
+using a numeric port number or a service name. If the argument is omited the
+default port B<25826> is assumed.
+
+=item B<TimeToLive> I<1-255>
+
+Set the time-to-live of sent packets. This applies to all, unicast and
+multicast, and IPv4 and IPv6 packets. The default is to not change this value.
+That means that multicast packets will be sent with a TTL of C<1> (one) on most
+operating systems.
+
+=item B<Forward> I<true|false>
+
+If set to I<true>, write packets that were received via the network plugin to
+the sending sockets. This should only be activated when the B<Listen>- and
+B<Server>-statements differ. Otherwise packets may be send multiple times to
+the same multicast group. While this results in more network traffic than
+neccessary it's not a huge problem since the plugin has a duplicate detection,
+so the values will not loop.
+
+=item B<CacheFlush> I<Seconds>
+
+For each host/plugin/type combination the C<network plugin> caches the time of
+the last value being sent or received. Every I<Seconds> seconds the plugin
+searches and removes all entries that are older than I<Seconds> seconds, thus
+freeing the unused memory again. Since this process is somewhat expensive and
+normally doesn't do much, this value should not be too small. The default is
+1800 seconds, but setting this to 86400 seconds (one day) will not do much harm
+either.
+
+=back
+
+=head2 Plugin C<ntpd>
+
+=over 4
+
+=item B<Host> I<Hostname>
+
+Hostname of the host running B<ntpd>. Defaults to B<localhost>.
+
+=item B<Port> I<Port>
+
+UDP-Port to connect to. Defaults to B<123>.
+
+=back
+
+=head2 Plugin C<nut>
+
+=over 4
+
+=item B<UPS> I<upsname>B<@>I<hostname>[B<:>I<port>]
+
+Add a UPS to collect data from. The format is identical to the one accepted by
+L<upsc(8)>.
+
+=back
+
+=head2 Plugin C<perl>
+
+=over 4
+
+=item B<LoadPlugin> I<Plugin>
+
+Loads the Perl plugin I<Plugin>. This does basically the same as B<use> would
+do in a Perl program.
+
+=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.
+
+=item B<IncludeDir> I<Dir>
+
+Adds I<Dir> to the B<@INC> array. This is the same as using the B<-IDir>
+command line option or B<use lib Dir> in the source code.
+
+=back
+
 =head2 Plugin C<ping>
 
 =over 4
@@ -306,92 +531,179 @@ Sets the Time-To-Live of generated ICMP packets.
 
 =back
 
+=head2 Plugin C<processes>
+
+=over 4
+
+=item B<Process> I<Name>
+
+Select more detailed statistics of processes matching this name. The statistics
+collected for these selected processes are size of the resident segment size
+(RSS), user- and system-time used, number of processes and number of threads,
+and minor and major pagefaults.
+
+=back
+
+=head2 Plugin C<rrdtool>
+
+You can use the settings B<StepSize>, B<HeartBeat>, B<RRARows>, and B<XFF> to
+finetune your RRD-files. Please read L<rrdcreate(1)> if you encounter problems
+using these settings. If you don't want to dive into the depths of RRDTool, you
+can savely ignore these settings.
+
+=over 4
+
+=item B<DataDir> I<Directory>
+
+Set the directory to store CSV-files under. Per default CSV-files are generated
+beneath the daemon's working directory, i.E<nbsp>e. the B<BaseDir>.
+
+=item B<StepSize> I<Seconds>
+
+Sets the stepsize of newly created RRD-files. Ideally (and per default) this
+setting is identical to the global B<Interval>-option and should not be
+smaller. If unsure, don't set this option.
+
+=item B<HeartBeat> I<Seconds>
+
+Sets the heartbeat of newly created RRD-files. Ideally this setting is bigger
+than the B<Interval>-setting, by default it's twice the B<Interval>-setting. If
+unsure, don't set this option.
+
+=item B<RRARows> I<NumRows>
+
+The C<rrdtool plugin> calculates the number of PDPs per CDP based on the
+B<StepSize>, this setting and a timespan. This plugin creates RRD-files with
+three times five RRAs, i. e. five RRAs with the CFs B<MIN>, B<AVERAGE>, and
+B<MAX>. The five RRAs are optimized for graphs covering one hour, one day, one
+week, one month, and one year.
+
+So for each timespan, it calculates how many PDPs need to be consolidated into
+one CDP by calculating:
+  number of PDPs = timespan / (stepsize * rrarows)
+
+Bottom line is, set this no smaller than the width of you graphs in pixels. The
+default is 1200.
+
+=item B<RRATimespan> I<Seconds>
+
+Adds an RRA-timespan, given in seconds. Use this option mulitple times to have
+more then one RRA. If this option is never used, the built-in default of (3600,
+86400, 604800, 2678400, 31622400) is used.
+
+For more information on how RRA-sizes are calculated see B<RRARows> above.
+
+=item B<XFF> I<Factor>
+
+Set the "XFiles Factor". The default is 0.1. If unsure, don't set this option.
+
+=item B<CacheFlush> I<Seconds>
+
+When the C<rrdtool plugin> uses a cache (by setting B<CacheTimeout>, see below)
+it writes all values for a certain RRD-file if the oldest value is older than
+(or equal to) the number of seconds specified. If some RRD-file is not updated
+anymore for some reason (the computer was shut down, the network is broken,
+etc.) some values may still be in the cache. If B<CacheFlush> is set, then the
+entire cache is searched for entries older than B<CacheTimeout> seconds and
+written to disk every I<Seconds> seconds. Since this is kind of expensive and
+does nothing under normal circumstances, this value should not be too small.
+900 seconds might be a good value, though setting this to 7200 seconds doesn't
+normally do much harm either.
+
+=item B<CacheTimeout> I<Seconds>
+
+If this option is set to a value greater than zero, the C<rrdtool plugin> will
+save values in a cache, as described above. Writing multiple values at once
+reduces IO-operations and thus lessens the load produced by updating the files.
+The tradeoff is that the graphs kind of "drag behind" and that more memory is
+used.
+
+=back
+
 =head2 Plugin C<sensors>
 
+The C<sensors plugin> uses B<lm_sensors> to retrieve sensor-values. This means
+that all the needed modules have to be loaded and lm_sensors has to be
+configured (most likely by editing F</etc/sensors.conf>. Read
+L<sensors.conf(5)> for details.
+
+The B<lm_sensors> homepage can be found at
+L<http://secure.netroedge.com/~lm78/>.
+
 =over 4
 
-=item B<ExtendedSensorNaming> I<true>|I<false>
-
-If set to I<true> this option switches on the extended sensors and RRD-files
-naming. This option exists to preserve backwards compatibility. It is
-recommended that you set this option to I<true>. The default is I<false> to
-maintain compatibility only.
-
-Sensors get names like I<chip-bus-address/type-feature> (e.g.
-I<it8712-isa-0290/voltage-in1>) and RRD files are therefore stored in a
-standalone directory inside the B<DataDir> directory and get names like
-I<lm_sensors-chip-bus-address/type-feature.rrd> (e.g.
-I<lm_sensors-it8712-isa-0290/voltage-in1.rrd>).
-
-The B<ExtendedSensorNaming> option breaks the compatibility with previous
-sensors and RRD files naming and the place where RRDs are stored. If you turn
-it on, the plugin will create new RRD files in a standalone directory inside
-the B<DataDir> directory and without previous history. You can rename ``old''
-RRD-files to preserve already collected statistics, because the file layout
-hasn't changed. If you have two chips of the same type, you need to use
-B<ExtendedSensorNaming> in order to collect information from both chips.
-
-If not set or set to I<false>, the extended naming is not active. Sensors get
-names like I<chip-feature> (e.g. I<it8712-in1>) and RRD files are stored in the
-main B<DataDir> directory and get names like I<sensors-chip-feature.rrd> (e.g.
-I<sensors-it8712-in1.rrd>).  You simply continue using the plugin the old way
-and additionally also getting data for newly added sensors in this mode.
-
-=item B<Sensor> I<chip-feature> or B<Sensor> I<chip-bus-address/type-feature>
-
-Both option modes select the name of the sensor which you want to collect.
-The naming scheme is dependent on the state of the B<ExtendedSensorNaming>
-option (see previous option). Both option modes can also deselect the
-sensor according to the B<IgnoreSelected> option (see below).
-
-For example the option "B<Sensor> I<it8712-in1>" will cause the collectd
-to gather data for the voltage sensor I<in1> of the I<it8712> chip in case
-of the B<ExtendedSensorNaming> option is set to I<false>.
-
-And likewise the option "B<Sensor> I<it8712-isa-0290/voltage-in1>" will
-cause the collectd to gather data for the voltage sensor I<in1> of the I<it8712>
-on the isa bus at the address 0290 in case of the B<ExtendedSensorNaming>
-option set to I<true>.
+=item B<Sensor> I<chip-bus-address/type-feature>
+
+Selects the name of the sensor which you want to collect or ignore, depending
+on the B<IgnoreSelected> below. For example, the option "B<Sensor>
+I<it8712-isa-0290/voltage-in1>" will cause collectd to gather data for the
+voltage sensor I<in1> of the I<it8712> on the isa bus at the address 0290.
 
 =item B<IgnoreSelected> I<true>|I<false>
 
-If no configuration if given, the B<sensors>-plugin will collect data from
-all sensors. This may not be practical, especially for uninteresting sensors.
-Thus, you can use the B<Sensor>-option to pick the sensors you're
-interested in. Sometimes, however, it's easier/prefered to collect all
-sensors I<except> a few ones. This option enables you to
-do that: By setting B<IgnoreSelected> to I<true> the effect of
-B<Sensor> is inversed: All selected sensors are ignored and all
-other sensors are collected.
+If no configuration if given, the B<sensors>-plugin will collect data from all
+sensors. This may not be practical, especially for uninteresting sensors.
+Thus, you can use the B<Sensor>-option to pick the sensors you're interested
+in. Sometimes, however, it's easier/prefered to collect all sensors I<except> a
+few ones. This option enables you to do that: By setting B<IgnoreSelected> to
+I<true> the effect of B<Sensor> is inversed: All selected sensors are ignored
+and all other sensors are collected.
+
+=back
+
+=head2 Plugin C<syslog>
 
-back
+=over 4
+
+=item B<LogLevel> B<debug|info|notice|warning|err>
+
+Sets the log-level. If, for example, set to B<notice>, then all events with
+severity B<notice>, B<warning>, or B<err> will be submitted to the
+syslog-daemon.
 
 =back
 
-=head2 Plugin C<traffic>
+=head2 Plugin C<unixsock>
 
 =over 4
 
-=item B<Interface> I<Interface>
+=item B<SocketFile> I<Path>
 
-Select this interface. By default these interfaces will then be collected. For a more detailed description see B<IgnoreSelected> below.
+Sets the socket-file which is to be created.
 
-=item B<IgnoreSelected> I<true>|I<false>
+=item B<SocketGroup> I<Group>
 
-If no configuration if given, the B<traffic>-plugin will collect data from
-all interfaces. This may not be practical, especially for loopback- and
-similar interfaces. Thus, you can use the B<Interface>-option to pick the
-interfaces you're interested in. Sometimes, however, it's easier/prefered
-to collect all interfaces I<except> a few ones. This option enables you to
-do that: By setting B<IgnoreSelected> to I<true> the effect of
-B<Interface> is inversed: All selected interfaces are ignored and all
-other interfaces are collected.
+If running as root change the group of the UNIX-socket after it has been 
+created. Defaults to B<collectd>.
+
+=item B<SocketPerms> I<Permissions>
+
+Change the file permissions of the UNIX-socket after it has been created. The
+permissions must be given as a numeric, octal value as you would pass to
+L<chmod(1)>. Defaults to B<0770>.
 
 =back
 
+=head2 Plugin C<vserver>
+
+This plugin doesn't have any options. B<VServer> support is only available for
+Linux. It cannot yet be found in a vanilla kernel, though. To make use of this
+plugin you need a kernel that has B<VServer> support built in, i.E<nbsp>e. you
+need to apply the patches and compile your own kernel, which will then provide
+the F</proc/virtual> filesystem that is required by this plugin.
+
+The B<VServer> homepage can be found at L<http://linux-vserver.org/>.
+
 =head1 SEE ALSO
 
-L<collectd(1)>
+L<collectd(1)>,
+L<collectd-exec(5)>,
+L<collectd-unixsock(5)>,
+L<hddtemp(8)>,
+L<kstat(3KSTAT)>,
+L<mbmon(1)>,
+L<rrdtool(1)>,
+L<sensors(1)>
 
 =head1 AUTHOR
 
index 61edcd5..ac75516 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -78,9 +77,6 @@
 #if HAVE_ERRNO_H
 # include <errno.h>
 #endif
-#if HAVE_SYSLOG_H
-# include <syslog.h>
-#endif
 #if HAVE_LIMITS_H
 # include <limits.h>
 #endif
 # define assert(...) /* nop */
 #endif
 
+#if NAN_STATIC_DEFAULT
+# include <math.h>
+/* #endif NAN_STATIC_DEFAULT*/
+#elif NAN_STATIC_ISOC
+# ifndef __USE_ISOC99
+#  define DISABLE_ISOC99 1
+#  define __USE_ISOC99 1
+# endif /* !defined(__USE_ISOC99) */
+# include <math.h>
+# if DISABLE_ISOC99
+#  undef DISABLE_ISOC99
+#  undef __USE_ISOC99
+# endif /* DISABLE_ISOC99 */
+/* #endif NAN_STATIC_ISOC */
+#elif NAN_ZERO_ZERO
+# include <math.h>
+# ifdef NAN
+#  undef NAN
+# endif
+# define NAN (0.0 / 0.0)
+# ifndef isnan
+#  define isnan(f) ((f) != (f))
+# endif /* !defined(isnan) */
+#endif /* NAN_ZERO_ZERO */
+
 #if HAVE_DIRENT_H
 # include <dirent.h>
 # define NAMLEN(dirent) strlen((dirent)->d_name)
 # include <sys/param.h>
 #endif
 
-#if !HAVE_SYSLOG
-# define syslog(...) /**/
-# define openlog(...) /**/
-# define closelog(...) /**/
-#endif
-
 #if HAVE_KSTAT_H
 # include <kstat.h>
 #endif
 #define PIDFILE PREFIX "/var/run/" PACKAGE_NAME ".pid"
 #endif
 
-#ifndef LOGFILE
-#define LOGFILE PREFIX"/var/log/"PACKAGE_NAME"/"PACKAGE_NAME".log"
-#endif
-
 #ifndef PLUGINDIR
 #define PLUGINDIR PREFIX "/lib/" PACKAGE_NAME
 #endif
 # define COLLECTD_GRP_NAME "collectd"
 #endif
 
-#ifndef COLLECTD_STEP
-#  define COLLECTD_STEP "10"
-#endif
-
-#ifndef COLLECTD_HEARTBEAT
-#  define COLLECTD_HEARTBEAT "25"
-#endif
-
-#ifndef COLLECTD_ROWS
-#  define COLLECTD_ROWS "1200"
-#endif
-
-#ifndef COLLECTD_XFF
-#  define COLLECTD_XFF 0.1
-#endif
-
-extern time_t curtime;
-
-#ifdef HAVE_LIBRRD
-extern int operating_mode;
-#endif
+#define STATIC_ARRAY_LEN(array) (sizeof (array) / sizeof ((array)[0]))
 
-/* int main (int argc, char **argv); */
+extern char hostname_g[];
+extern int  interval_g;
 
 #endif /* COLLECTD_H */
index 8ee77ea..aae4315 100644 (file)
@@ -8,112 +8,16 @@ collectd I<[options]>
 
 =head1 DESCRIPTION
 
-collectd is a daemon that collects various system statistics periodically and
-stores them into RRD-files. Which data is collected depends on compile-time
-settings. The following features may be available:
-
-=over 4
-
-=item
-
-Apache server stats (I<apache>)
-
-=item
-
-Apple hardware sensors (I<apple_sensors>, Darwin only)
-
-=item
-
-Battery status (I<battery>)
-
-=item
-
-CPU utilization (I<cpu>)
-
-=item
-
-Mountpoint usage (I<df>)
-
-=item
-
-Disk and partition usage/throughput (I<disk>)
-
-=item
-
-DNS traffic (I<dns>)
-
-=item
-
-Email usage (I<email>)
-
-=item
-
-Harddisk temperatures (I<hddtemp>)
-
-=item
-
-System load averages (I<load>)
-
-=item
-
-Motherboard monitor (I<mbmon>)
-
-=item
-
-Memory usage (I<memory>)
-
-=item
-
-MySQL statistics (I<mysql>)
-
-=item
-
-NFS utilization (I<nfs>, Linux only)
-
-=item
-
-Network latency (I<ping>)
-
-=item
-
-Number of processes (I<processes>, Linux only)
-
-=item
-
-lm_sensors information (I<sensors>, Linux only)
-
-=item
-
-Serial port traffic (I<serial>, Linux only)
-
-=item
-
-Swap usage (I<swap>)
-
-=item
-
-Tape drive usage (I<tape>, Solaris only)
-
-=item
-
-Network traffic (I<traffic>)
-
-=item
-
-Number of users logged into the system (I<users>)
-
-=item
-
-System ressources used by VServers (I<vserver>)
-
-=item
-
-Wireless network stats (I<wireless>)
-
-=back
+collectd is a daemon that receives system statistics and makes them available
+in a number of ways. The main daemon itself doesn't have any real functionality
+apart from loading, querying and submitting to plugins. For a description of
+available plugins please see L</PLUGINS> below.
 
 =head1 OPTIONS
 
+Most of collectd's configuration is done using using a configfile. See
+L<collectd.conf(5)> for an in-depth description of all options.
+
 =over 4
 
 =item B<-C> I<E<lt>config-fileE<gt>>
@@ -122,6 +26,11 @@ Specify an alternative config file. This is the place to go when you wish to
 change B<collectd>'s behavior. The path may be relative to the current working
 directory.
 
+=item B<-t>
+
+Test the configuration only. The program immediately exits after parsing the
+config file. A return code not equal to zero indicates an error.
+
 =item B<-P> I<E<lt>pid-fileE<gt>>
 
 Specify an alternative pid file. This overwrites any settings in the config 
@@ -141,448 +50,58 @@ Output usage information and exit.
 
 =back
 
-=head1 MODES
-
-collectd can operate in four different operating modes. The modes are described
-below.
-
-The simplest mode is the so called B<local mode>. Data is collected locally and
-written in RRD files that reside in I<DataDir>. This is the default mode when
-collectd is linked against C<librrd>.
-
-The modes B<client mode> and B<server mode> are used to send data over a
-network and receive it again.
-
-In B<client mode> the daemon collects the data locally and sends its results
-to one or more network addresses. No RRD files are written locally in this
-case. If collectd is not linked against C<librrd> this is the default mode.
-
-If started in B<server mode> the daemon will listen on one or more interfaces
-and write the data it receives to RRD files. No data is collected locally.
-
-In the last mode, B<log mode>, data is collected locally and written in
-text files that reside in I<DataDir>.
-
-Please refer to L<collectd.conf(5)> for the configuration options and default
-values.
-
-=head1 SPECIAL PLUGINS
-
-=head2 apache
-
-This module connects to an Apache webserver and expects the output produced by
-B<mod_status.c>. If requires B<libcurl> to set up the HTTP connection and issue
-the request(s). The following is a sample config for the Apache webserver. The
-use of C<ExtendedStatus on> is mandatory.
-
-  ExtendedStatus on
-  <IfModule mod_status.c>
-    <Location /mod_status>
-      SetHandler server-status
-    </Location>
-  </IfModule>
-
-This plugin requires further configuration. Please read L<collectd.conf(5)>.
-
-=head2 cpufreq
-
-This module reads F</sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq> (for
-the first CPU installed) to get the current CPU frequency. If this file does
-not exist make sure B<cpufreqd> (L<http://cpufreqd.sourceforge.net/>) or a
-similar tool is installed and an "cpu governor" (that's kernel module) is
-loaded.
-
-=head2 email
-
-This plugin collects data indirectly by providing a UNIX socket that external
-programs can connect to. A simple line based protocol is used to communicate
-with the plugin:
-
-E-Mail type (e.g. "ham", "spam", "virus", ...) and size (bytes):
-
-  e:<type>:<size>
-
-If C<size> is less than or equal to zero, C<size> is ignored.
-
-Spam score:
-
-  s:<value>
-
-Successful spam checks (e.g. "BAYES_99", "SUBJECT_DRUG_GAP_C", ...):
-
-  c:<type1>[,<type2>,...]
-
-Each line is limited to 256 characters (including the newline character). 
-Longer lines will be ignored.
+=head1 PLUGINS
 
-=head2 mysql
+As noted above, the real power of collectd lies within it's plugins. A
+(hopefully complete) list of plugins and short descriptions can be found in the
+F<README> file that is distributed with the sourcecode. If you're using a
+package it's a good bet to search somewhere near F</usr/share/doc/collectd>.
 
-Requires B<mysqlclient> to be installed. It connects to the database when
-started and keeps the connection up as long as possible. When the connection is
-interrupted for whatever reason it will try to re-connect. The syslog will
-contain loud complaints in case anything goes wrong.
-
-This plugin issues C<SHOW STATUS> and evaluates C<Bytes_{received,sent}>,
-C<Com_*> and C<Handler_*> which correspond to F<traffic-mysql.rrd>,
-F<mysql_commands-*.rrd> and F<mysql_handler-*.rrd>. Also, the values of
-C<Qcache_*> are put in F<mysql_qcache.rrd> and values of C<Threads_*> are put
-in F<mysql_threads.rrd>. Please refer to the B<MySQL reference manual>,
-I<5.2.4. Server Status Variables> for an explanation of these values.
-
-=head2 sensors
-
-The B<sensors> module uses lm_sensors to retrieve sensor-values. This means
-that all the needed modules have to be loaded and lm_sensors has to be
-configured (most likely by editing F</etc/sensors.conf>. Read
-L<sensors.conf(5)> for details.
-
-The B<lm_sensors> homepage can be found at
-L<http://secure.netroedge.com/~lm78/>.
-
-=head2 mbmon
-
-The B<mbmon> module uses mbmon to retrieve temperature, voltage, etc.
-
-collectd connects to B<localhost> (127.0.0.1), port B<411/tcp>.
-The B<Host> and B<Port> options can be used to change these
-default values. See L<collectd.conf(5)> for details. C<mbmon> has to be
-running to work correctly. If C<mbmon> is not running timeouts may appear
-which may interfere with other statistics..
-
-C<mbmon> must be run with the -r option ("print TAG and Value format");
-Debian's /etc/init.d/mbmon script already does this, other people
-will need to ensure that this is the case.
-
-=head2 hddtemp
-
-To get values from B<hddtemp> collectd connects to B<localhost> (127.0.0.1),
-port B<7634/tcp>. The B<Host> and B<Port> options can be used to change these
-default values. See L<collectd.conf(5)> for details. C<hddtemp> has to be
-running to work correctly. If C<hddtemp> is not running timeouts may appear
-which may interfere with other statistics..
-
-The B<hddtemp> homepage can be found at
-L<http://www.guzu.net/linux/hddtemp.php>.
-
-=head2 vserver
-
-B<VServer> support is only available for Linux. It cannot yet be found in a 
-vanilla kernel, though. To make use of this plugin you need a kernel that has 
-B<VServer> support built in, i.e. you need to apply the patches and compile 
-your own kernel, which will then provide the /proc/virtual filesystem that is
-required by this plugin.
-
-The B<VServer> homepage can be found at L<http://linux-vserver.org/>.
-
-=head1 RRD FILES
-
-The RRD files are created automatically. The size of the RRAs depend on the
-compile time settings of I<step> and I<width>. With the default values (I<step>
-= B<10>, I<width> = B<1200>) the following RRAs are created:
-
-  RRA:AVERAGE:0.1:1:8640
-  RRA:AVERAGE:0.1:50:1210
-  RRA:AVERAGE:0.1:223:1202
-  RRA:AVERAGE:0.1:2635:1201
-  RRA:MIN:0.1:1:8640
-  RRA:MIN:0.1:50:1210
-  RRA:MIN:0.1:223:1202
-  RRA:MIN:0.1:2635:1201
-  RRA:MAX:0.1:1:8640
-  RRA:MAX:0.1:50:1210
-  RRA:MAX:0.1:223:1202
-  RRA:MAX:0.1:2635:1201
-
-By default collectd uses a 10 second I<step>. Thus the RRAs contain the
-following timespans. If you've changed the I<step> at compile time you will
-have calculate resolution and timespan yourself.
-
-  PDP per CDP |  Resolution  | Data points | Timespan
-  ------------+--------------+-------------+---------
-            1 | 10.0 seconds !        8640 ! 1 day
-           50 |  8.3 minutes |        1210 | 1 week
-          223 | 37.2 minutes |        1202 | 1 month
-         2635 |  7.3 hours   |        1201 | 1 year
-
-The DS'es depend on the module creating the RRD files:
+There are two big groups of plugins, B<input> and B<output> plugins:
 
 =over 4
 
-=item Apache traffic (F<apache/apache_bytes.rrd>)
-
-  DS:count:COUNTER:HEARTBEAT:0:134217728
-
-=item Apache requests (F<apache/apache_requests.rrd>)
-
-  DS:count:COUNTER:HEARTBEAT:0:1048576
-
-=item Apache scoreboard (F<apache/apache_scoreboard-I<E<lt>nameE<gt>>.rrd>)
-
-  DS:count:GAUGE:HEARTBEAT:0:U
-
-=item Apple temperature sensor (F<apple_sensors/temperature-I<E<lt>nameE<gt>>.rrd>)
-
-  DS:value:GAUGE:HEARTBEAT:U:U
-
-=item Apple fanspeed sensor (F<apple_sensors/fanspeed-I<E<lt>nameE<gt>>.rrd>)
-
-  DS:value:GAUGE:HEARTBEAT:U:U
-
-=item Battery charge (F<battery-I<E<lt>nameE<gt>>/charge.rrd>)
-
-  DS:charge:GAUGE:HEARTBEAT:0:U
-
-=item Battery current (F<battery-I<E<lt>nameE<gt>>/current.rrd>)
-
-  DS:current:GAUGE:HEARTBEAT:U:U
-
-=item Battery voltage (F<battery-I<E<lt>nameE<gt>>/voltage.rrd>)
-
-  DS:voltage:GAUGE:HEARTBEAT:U:U
-
-=item CPU (F<cpu-I<E<lt>numE<gt>>.rrd>)
-
-  DS:user:COUNTER:HEARTBEAT:0:100
-  DS:nice:COUNTER:HEARTBEAT:0:100
-  DS:syst:COUNTER:HEARTBEAT:0:100
-  DS:idle:COUNTER:HEARTBEAT:0:100
-  DS:wait:COUNTER:HEARTBEAT:0:100
-
-=item CPU frequency (F<cpufreq-I<E<lt>numE<gt>>.rrd>)
-
-  DS:value:GAUGE:HEARTBEAT:0:U
-
-=item Mountpoints (F<df-I<E<lt>pathE<gt>>.rrd>)
-
-  DS:used:GAUGE:HEARTBEAT:0:U
-  DS:free:GAUGE:HEARTBEAT:0:U
-
-=item Diskstats (F<disk-I<E<lt>majorE<gt>>-I<E<lt>minorE<gt>>.rrd>)
-
-  DS:rcount:COUNTER:HEARTBEAT:0:U
-  DS:rmerged:COUNTER:HEARTBEAT:0:U
-  DS:rbytes:COUNTER:HEARTBEAT:0:U
-  DS:rtime:COUNTER:HEARTBEAT:0:U
-  DS:wcount:COUNTER:HEARTBEAT:0:U
-  DS:wmerged:COUNTER:HEARTBEAT:0:U
-  DS:wbytes:COUNTER:HEARTBEAT:0:U
-  DS:wtime:COUNTER:HEARTBEAT:0:U
-
-=item Diskstats (F<partition-I<E<lt>majorE<gt>>-I<E<lt>minorE<gt>>.rrd>)
-
-  DS:rcount:COUNTER:HEARTBEAT:0:U
-  DS:rbytes:COUNTER:HEARTBEAT:0:U
-  DS:wcount:COUNTER:HEARTBEAT:0:U
-  DS:wbytes:COUNTER:HEARTBEAT:0:U
-
-=item E-Mail count (F<email/email-I<E<lt>typeE<gt>>.rrd>)
-
-  DS:count:GAUGE:HEARTBEAT:0:U
-
-=item E-Mail size (F<email/email_size-I<E<lt>typeE<gt>>.rrd>)
-
-  DS:size:GAUGE:HEARTBEAT:0:U
-
-=item HDD Temperature (F<hddtemp-I<E<lt>majorE<gt>>-I<E<lt>minorE<gt>>.rrd>)
-
-  DS:value:GAUGE:HEARTBEAT:U:U
-
-=item System load (F<load.rrd>)
-
-  DS:shortterm:GAUGE:HEARTBEAT:0:100
-  DS:midterm:GAUGE:HEARTBEAT:0:100
-  DS:longterm:GAUGE:HEARTBEAT:0:100
-
-=item Memory usage (F<memory.rrd>)
-
-  DS:used:GAUGE:HEARTBEAT:0:9223372036854775807
-  DS:free:GAUGE:HEARTBEAT:0:9223372036854775807
-  DS:buffers:GAUGE:HEARTBEAT:0:9223372036854775807
-  DS:cached:GAUGE:HEARTBEAT:0:9223372036854775807
-
-=item MySQL commands and handlers (F<mysql_commands-I<E<lt>commandE<gt>>.rrd> and F<mysql_handler-I<E<lt>handlerE<gt>>.rrd>)
-
-  DS:value:COUNTER:HEARTBEAT:0:U
-
-=item MySQL query cache (F<mysql_qcache.rrd>)
-
-  DS:hits:COUNTER:HEARTBEAT:0:U
-  DS:inserts:COUNTER:HEARTBEAT:0:U
-  DS:not_cached:COUNTER:HEARTBEAT:0:U
-  DS:lowmem_prunes:COUNTER:HEARTBEAT:0:U
-  DS:queries_in_cache:GAUGE:HEARTBEAT:0:U
-
-=item MySQL threads (F<mysql_threads.rrd>)
-
-  DS:running:GAUGE:HEARTBEAT:0:U
-  DS:connected:GAUGE:HEARTBEAT:0:U
-  DS:cached:GAUGE:HEARTBEAT:0:U
-  DS:created:COUNTER:HEARTBEAT:0:U
-
-=item NFSv2 Procedures (F<nfs2_procedures-I<(client|server)>.rrd>)
-
-  DS:null:COUNTER:HEARTBEAT:0:U
-  DS:getattr:COUNTER:HEARTBEAT:0:U
-  DS:setattr:COUNTER:HEARTBEAT:0:U
-  DS:root:COUNTER:HEARTBEAT:0:U
-  DS:lookup:COUNTER:HEARTBEAT:0:U
-  DS:readlink:COUNTER:HEARTBEAT:0:U
-  DS:read:COUNTER:HEARTBEAT:0:U
-  DS:wrcache:COUNTER:HEARTBEAT:0:U
-  DS:write:COUNTER:HEARTBEAT:0:U
-  DS:create:COUNTER:HEARTBEAT:0:U
-  DS:remove:COUNTER:HEARTBEAT:0:U
-  DS:rename:COUNTER:HEARTBEAT:0:U
-  DS:link:COUNTER:HEARTBEAT:0:U
-  DS:symlink:COUNTER:HEARTBEAT:0:U
-  DS:mkdir:COUNTER:HEARTBEAT:0:U
-  DS:rmdir:COUNTER:HEARTBEAT:0:U
-  DS:readdir:COUNTER:HEARTBEAT:0:U
-  DS:fsstat:COUNTER:HEARTBEAT:0:U
-
-=item NFSv3 Procedures (F<nfs3_procedures-I<(client|server)>.rrd>)
-
-  DS:null:COUNTER:HEARTBEAT:0:U
-  DS:getattr:COUNTER:HEARTBEAT:0:U
-  DS:setattr:COUNTER:HEARTBEAT:0:U
-  DS:lookup:COUNTER:HEARTBEAT:0:U
-  DS:access:COUNTER:HEARTBEAT:0:U
-  DS:readlink:COUNTER:HEARTBEAT:0:U
-  DS:read:COUNTER:HEARTBEAT:0:U
-  DS:write:COUNTER:HEARTBEAT:0:U
-  DS:create:COUNTER:HEARTBEAT:0:U
-  DS:mkdir:COUNTER:HEARTBEAT:0:U
-  DS:symlink:COUNTER:HEARTBEAT:0:U
-  DS:mknod:COUNTER:HEARTBEAT:0:U
-  DS:remove:COUNTER:HEARTBEAT:0:U
-  DS:rmdir:COUNTER:HEARTBEAT:0:U
-  DS:rename:COUNTER:HEARTBEAT:0:U
-  DS:link:COUNTER:HEARTBEAT:0:U
-  DS:readdir:COUNTER:HEARTBEAT:0:U
-  DS:readdirplus:COUNTER:HEARTBEAT:0:U
-  DS:fsstat:COUNTER:HEARTBEAT:0:U
-  DS:fsinfo:COUNTER:HEARTBEAT:0:U
-  DS:pathconf:COUNTER:HEARTBEAT:0:U
-  DS:commit:COUNTER:HEARTBEAT:0:U
-
-=item Network latency / Ping (F<ping-I<E<lt>hostnameE<gt>>.rrd>)
-
-  DS:ping:GAUGE:HEARTBEAT:0:65535
-
-=item Processes (F<processes.rrd>)
-
-  DS:running:GAUGE:HEARTBEAT:0:65535
-  DS:sleeping:GAUGE:HEARTBEAT:0:65535
-  DS:zombies:GAUGE:HEARTBEAT:0:65535
-  DS:stopped:GAUGE:HEARTBEAT:0:65535
-  DS:paging:GAUGE:HEARTBEAT:0:65535
-  DS:blocked:GAUGE:HEARTBEAT:0:65535
-
-=item lm_sensors fanspeed sensor (F<sensors-I<E<lt>instE<gt>>.rrd> or F<lm_sensors-I<E<lt>chipE<gt>>/fanspeed-I<E<lt>instE<gt>>.rrd>)
-
-  DS:value:GAUGE:HEARTBEAT:U:U
-
-=item lm_sensors temperature sensor (F<sensors-I<E<lt>instE<gt>>.rrd> or F<lm_sensors-I<E<lt>chipE<gt>>/temperature-I<E<lt>instE<gt>>.rrd>)
-
-  DS:value:GAUGE:HEARTBEAT:U:U
-
-=item lm_sensors voltage sensor (F<sensors-I<E<lt>instE<gt>>.rrd> or F<lm_sensors-I<E<lt>chipE<gt>>/voltage-I<E<lt>instE<gt>>.rrd>)
-
-  DS:voltage:GAUGE:HEARTBEAT:U:U
-
-=item Serial port traffic (F<serial-I<E<lt>numE<gt>>.rrd>)
-
-  DS:incoming:COUNTER:HEARTBEAT:0:U
-  DS:outgoing:COUNTER:HEARTBEAT:0:U
-
-=item Spam score (F<email/spam_score.rrd>)
-
-  DS:score:GAUGE:HEARTBEAT:0:U
-
-=item Spam checks (F<email/spam_check-I<E<lt>typeE<gt>>.rrd>)
-
-  DS:hits:GAUGE:HEARTBEAT:0:U
-
-=item Swap usage (F<swap.rrd>)
-
-  DS:used:GAUGE:HEARTBEAT:0:1099511627776
-  DS:free:GAUGE:HEARTBEAT:0:1099511627776
-  DS:cached:GAUGE:HEARTBEAT:0:1099511627776
-  DS:resv:GAUGE:HEARTBEAT:0:1099511627776
-
-=item Tape drive usage (F<tape-I<E<lt>nameE<gt>>.rrd>)
-
-  DS:rcount:COUNTER:HEARTBEAT:0:
-  DS:rmerged:COUNTER:HEARTBEAT:0:U
-  DS:rbytes:COUNTER:HEARTBEAT:0:U
-  DS:rtime:COUNTER:HEARTBEAT:0:U
-  DS:wcount:COUNTER:HEARTBEAT:0:U
-  DS:wmerged:COUNTER:HEARTBEAT:0:U
-  DS:wbytes:COUNTER:HEARTBEAT:0:U
-  DS:wtime:COUNTER:HEARTBEAT:0:U
-
-=item Network traffic (F<traffic-I<E<lt>interfaceE<gt>>.rrd>)
-
-  DS:incoming:COUNTER:HEARTBEAT:0:U
-  DS:outgoing:COUNTER:HEARTBEAT:0:U
-
-=item Interface packets (F<if_packets-I<E<lt>interfaceE<gt>>.rrd>)
-
-  DS:rx:COUNTER:HEARTBEAT:0:U
-  DS:tx:COUNTER:HEARTBEAT:0:U
-
-=item Interface errors (F<if_errors-I<E<lt>interfaceE<gt>>.rrd>)
-
-  DS:rx:COUNTER:HEARTBEAT:0:U
-  DS:tx:COUNTER:HEARTBEAT:0:U
-
-=item Users (F<users.rrd>)
-
-  DS:users:GAUGE:HEARTBEAT:0:65535
-
-=item VServer load (F<vserver-I<E<lt>xidE<gt>>/load.rrd>)
-
-  DS:shortterm:GAUGE:HEARTBEAT:0:100
-  DS:midterm:GAUGE:HEARTBEAT:0:100
-  DS:longterm:GAUGE:HEARTBEAT:0:100
-
-=item VServer threads (F<vserver-I<E<lt>xidE<gt>>/threads.rrd>)
-
-  DS:total:GAUGE:HEARTBEAT:0:65535
-  DS:running:GAUGE:HEARTBEAT:0:65535
-  DS:uninterruptible:GAUGE:HEARTBEAT:0:65535
-  DS:onhold:GAUGE:HEARTBEAT:0:65535
-
-=item VServer network traffic (F<vserver-I<E<lt>xidE<gt>>/traffic-I<E<lt>nameE<gt>>.rrd>)
-
-  DS:incoming:COUNTER:HEARTBEAT:0:9223372036854775807
-  DS:outgoing:COUNTER:HEARTBEAT:0:9223372036854775807
-  DS:failed:COUNTER:HEARTBEAT:0:9223372036854775807
+=item
 
-=item VServer processes (F<vserver-I<E<lt>xidE<gt>>/vs_processes.rrd>)
+Input plugins are queried periodically. They somehow aquire the current value
+of whatever they where designed to work with and submit these values back to
+the daemon, i. e. they "dispatch" the values. As an example, the C<cpu plugin>
+reads the current cpu-counters of time spent in the various modes (user,
+system, nice, ...) and dispatches these counters to the daemon.
 
-  DS:total:GAUGE:HEARTBEAT:0:65535
+=item
 
-=item VServer memory usage (F<vserver-I<E<lt>xidE<gt>>/vs_memory.rrd>)
+Output plugins get the dispatched values from the daemon and does something
+with them. Common applications are writing to RRD-files, CSV-files or sending
+the data over a network link to a remote box.
 
-  DS:vm:GAUGE:HEARTBEAT:0:9223372036854775807
-  DS:vml:GAUGE:HEARTBEAT:0:9223372036854775807
-  DS:rss:GAUGE:HEARTBEAT:0:9223372036854775807
-  DS:anon:GAUGE:HEARTBEAT:0:9223372036854775807
+=back
 
-=item Wireless link quality (F<wireless-I<E<lt>interfaceE<gt>>.rrd>)
+Of course not all plugins fit neatly into one of the two above categories. The
+C<network plugin>, for example, is able to send (i.E<nbsp>e. "write") B<and>
+receive (i.E<nbsp>e. "dispatch") values. Also, it opens a socket upon
+initialization and dispatches the values when it receives them and isn't
+triggered at the same time the input plugins are being read. You can think if
+the network receive part as working asynchronous if it helps.
 
-  DS:quality:GAUGE:HEARTBEAT:0:U
-  DS:power:GAUGE:HEARTBEAT:U:0
-  DS:noise:GAUGE:HEARTBEAT:U:0
+In addition to the above, there are "logging plugins". Right now those are the
+C<logfile plugin> and the C<syslog plugin>. With these plugins collectd can
+provide information about issues and significant situations to the user.
+Several loglevels let you suppress uninteresting messages.
 
-=back
+Please note that some plugins, that provide other means of communicating with
+the daemon, have manpages of their own to describe their functionality in more
+detail. In particular those are L<collectd-email(5)>, L<collectd-exec(5)>,
+L<collectd-perl(5)>, and L<collectd-unixsock(5)>
 
 =head1 SEE ALSO
 
-L<collectd.conf(5)>, L<rrdtool(1)>, L<sensors(1)>, L<hddtemp(8)>,
-L<mbmon(1)>,
-L<kstat(3KSTAT)>
+L<collectd.conf(5)>,
+L<collectd-email(5)>,
+L<collectd-exec(5)>,
+L<collectd-perl(5)>,
+L<collectd-unixsock(5)>,
+L<http://collectd.org/>
 
 =head1 AUTHOR
 
index fd1fbd4..9a28cdb 100644 (file)
@@ -1,21 +1,19 @@
 /**
  * collectd - src/common.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
+ * 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 use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence for more details.
+ * 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
- * Licence along with this program; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
- * USA.
+ * 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>
 #endif
 
 #include "common.h"
-#include "utils_debug.h"
+#include "plugin.h"
 
 #ifdef HAVE_MATH_H
 #  include <math.h>
 #endif
 
-extern int operating_mode;
+/* for ntohl and htonl */
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
 
 #ifdef HAVE_LIBKSTAT
 extern kstat_ctl_t *kc;
 #endif
 
-#ifdef HAVE_LIBRRD
-#if 0
-static char *rra_def[] =
-{
-               "RRA:AVERAGE:0.0:1:1500",
-               "RRA:AVERAGE:0.2:6:1500",
-               "RRA:AVERAGE:0.1:180:1680",
-               "RRA:AVERAGE:0.1:2160:1520",
-               "RRA:MIN:0.0:1:1500",
-               "RRA:MIN:0.2:6:1500",
-               "RRA:MIN:0.1:180:1680",
-               "RRA:MIN:0.1:2160:1520",
-               "RRA:MAX:0.0:1:1500",
-               "RRA:MAX:0.2:6:1500",
-               "RRA:MAX:0.1:180:1680",
-               "RRA:MAX:0.1:2160:1520",
-               NULL
-};
-static int rra_num = 12;
-#endif
-
-static int rra_timespans[] =
-{
-       3600,
-       86400,
-       604800,
-       2678400,
-       31622400,
-       0
-};
-static int rra_timespans_num = 5;
-
-static char *rra_types[] =
-{
-       "AVERAGE",
-       "MIN",
-       "MAX",
-       NULL
-};
-static int rra_types_num = 3;
-#endif /* HAVE_LIBRRD */
-
 void sstrncpy (char *d, const char *s, int len)
 {
        strncpy (d, s, len);
@@ -96,20 +55,29 @@ char *sstrdup (const char *s)
 
        if((r = strdup (s)) == NULL)
        {
-               DBG ("Not enough memory.");
+               DEBUG ("Not enough memory.");
                exit(3);
        }
 
        return (r);
 }
 
+/* Don't use the return value of `strerror_r', because the GNU-people got
+ * inventive there.. -octo */
+char *sstrerror (int errnum, char *buf, size_t buflen)
+{
+       buf[0] = '\0';
+       strerror_r (errnum, buf, buflen);
+       return (buf);
+} /* char *sstrerror */
+
 void *smalloc (size_t size)
 {
        void *r;
 
        if ((r = malloc (size)) == NULL)
        {
-               DBG("Not enough memory.");
+               DEBUG("Not enough memory.");
                exit(3);
        }
 
@@ -150,7 +118,7 @@ ssize_t sread (int fd, void *buf, size_t count)
 
                if (status == 0)
                {
-                       DBG ("Received EOF from fd %i. "
+                       DEBUG ("Received EOF from fd %i. "
                                        "Closing fd and returning error.",
                                        fd);
                        close (fd);
@@ -197,10 +165,12 @@ int strsplit (char *string, char **fields, size_t size)
 {
        size_t i;
        char *ptr;
+       char *saveptr;
 
        i = 0;
        ptr = string;
-       while ((fields[i] = strtok (ptr, " \t")) != NULL)
+       saveptr = NULL;
+       while ((fields[i] = strtok_r (ptr, " \t", &saveptr)) != NULL)
        {
                ptr = NULL;
                i++;
@@ -271,7 +241,7 @@ int strsubstitute (char *str, char c_from, char c_to)
        }
 
        return (ret);
-}
+} /* int strsubstitute */
 
 int escape_slashes (char *buf, int buf_len)
 {
@@ -286,8 +256,12 @@ int escape_slashes (char *buf, int buf_len)
                return (0);
        }
 
+       if (buf_len <= 1)
+               return (0);
+
        /* Move one to the left */
-       memmove (buf, buf + 1, buf_len - 1);
+       if (buf[0] == '/')
+               memmove (buf, buf + 1, buf_len - 1);
 
        for (i = 0; i < buf_len - 1; i++)
        {
@@ -299,7 +273,7 @@ int escape_slashes (char *buf, int buf_len)
        buf[i] = '\0';
 
        return (0);
-}
+} /* int escape_slashes */
 
 int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret)
 {
@@ -324,7 +298,7 @@ int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct times
        return (0);
 }
 
-static int check_create_dir (const char *file_orig)
+int check_create_dir (const char *file_orig)
 {
        struct stat statbuf;
 
@@ -334,7 +308,9 @@ static int check_create_dir (const char *file_orig)
        char *fields[16];
        int   fields_num;
        char *ptr;
+       char *saveptr;
        int   last_is_file = 1;
+       int   path_is_absolute = 0;
        int   len;
        int   i;
 
@@ -355,9 +331,11 @@ static int check_create_dir (const char *file_orig)
         */
        if (file_orig[len - 1] == '/')
                last_is_file = 0;
+       if (file_orig[0] == '/')
+               path_is_absolute = 1;
 
        /*
-        * Create a copy for `strtok' to destroy
+        * Create a copy for `strtok_r' to destroy
         */
        strncpy (file_copy, file_orig, 512);
        file_copy[511] = '\0';
@@ -367,8 +345,9 @@ static int check_create_dir (const char *file_orig)
         * remove leading and trailing slashes..
         */
        ptr = file_copy;
+       saveptr = NULL;
        fields_num = 0;
-       while ((fields[fields_num] = strtok (ptr, "/")) != NULL)
+       while ((fields[fields_num] = strtok_r (ptr, "/", &saveptr)) != NULL)
        {
                ptr = NULL;
                fields_num++;
@@ -389,16 +368,18 @@ static int check_create_dir (const char *file_orig)
                 */
                if (fields[i][0] == '.')
                {
-                       syslog (LOG_ERR, "Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
+                       ERROR ("Cowardly refusing to create a directory that begins with a `.' (dot): `%s'", file_orig);
                        return (-2);
                }
 
                /*
                 * Join the components together again
                 */
-               if (strjoin (dir, dir_len, fields, i + 1, "/") < 0)
+               dir[0] = '/';
+               if (strjoin (dir + path_is_absolute, dir_len - path_is_absolute,
+                                       fields, i + 1, "/") < 0)
                {
-                       syslog (LOG_ERR, "strjoin failed: `%s', component #%i", file_orig, i);
+                       ERROR ("strjoin failed: `%s', component #%i", file_orig, i);
                        return (-1);
                }
 
@@ -408,19 +389,25 @@ static int check_create_dir (const char *file_orig)
                        {
                                if (mkdir (dir, 0755) == -1)
                                {
-                                       syslog (LOG_ERR, "mkdir (%s): %s", dir, strerror (errno));
+                                       char errbuf[1024];
+                                       ERROR ("mkdir (%s): %s", dir,
+                                                       sstrerror (errno,
+                                                               errbuf, sizeof (errbuf)));
                                        return (-1);
                                }
                        }
                        else
                        {
-                               syslog (LOG_ERR, "stat (%s): %s", dir, strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("stat (%s): %s", dir,
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
                                return (-1);
                        }
                }
                else if (!S_ISDIR (statbuf.st_mode))
                {
-                       syslog (LOG_ERR, "stat (%s): Not a directory!", dir);
+                       ERROR ("stat (%s): Not a directory!", dir);
                        return (-1);
                }
        }
@@ -428,338 +415,6 @@ static int check_create_dir (const char *file_orig)
        return (0);
 }
 
-/* * * * *
- * Magic *
- * * * * */
-#if HAVE_LIBRRD
-static int rra_get (char ***ret)
-{
-       static char **rra_def = NULL;
-       static int rra_num = 0;
-
-       int rra_max = rra_timespans_num * rra_types_num;
-
-       int step;
-       int rows;
-       int span;
-
-       int cdp_num;
-       int cdp_len;
-       int i, j;
-
-       char buffer[64];
-
-       if ((rra_num != 0) && (rra_def != NULL))
-       {
-               *ret = rra_def;
-               return (rra_num);
-       }
-
-       if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
-               return (-1);
-       memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
-
-       step = atoi (COLLECTD_STEP);
-       rows = atoi (COLLECTD_ROWS);
-
-       if ((step <= 0) || (rows <= 0))
-       {
-               *ret = NULL;
-               return (-1);
-       }
-
-       cdp_len = 0;
-       for (i = 0; i < rra_timespans_num; i++)
-       {
-               span = rra_timespans[i];
-
-               if ((span / step) < rows)
-                       continue;
-
-               if (cdp_len == 0)
-                       cdp_len = 1;
-               else
-                       cdp_len = (int) floor (((double) span) / ((double) (rows * step)));
-
-               cdp_num = (int) ceil (((double) span) / ((double) (cdp_len * step)));
-
-               for (j = 0; j < rra_types_num; j++)
-               {
-                       if (rra_num >= rra_max)
-                               break;
-
-                       if (snprintf (buffer, sizeof(buffer), "RRA:%s:%3.1f:%u:%u",
-                                               rra_types[j], COLLECTD_XFF,
-                                               cdp_len, cdp_num) >= sizeof (buffer))
-                       {
-                               syslog (LOG_ERR, "rra_get: Buffer would have been truncated.");
-                               continue;
-                       }
-
-                       rra_def[rra_num++] = sstrdup (buffer);
-               }
-       }
-
-#if COLLECT_DEBUG
-       DBG ("rra_num = %i", rra_num);
-       for (i = 0; i < rra_num; i++)
-               DBG ("  %s", rra_def[i]);
-#endif
-
-       *ret = rra_def;
-       return (rra_num);
-}
-#endif /* HAVE_LIBRRD */
-
-static int log_create_file (char *filename, char **ds_def, int ds_num)
-{
-       FILE *log;
-       int i;
-
-       if (check_create_dir (filename))
-               return (-1);
-
-       log = fopen (filename, "w");
-       if (log == NULL)
-       {
-               syslog (LOG_WARNING, "Failed to create %s: %s", filename,
-                               strerror(errno));
-               return (-1);
-       }
-
-       fprintf (log, "epoch");
-       for (i = 0; i < ds_num; i++)
-       {
-               char *name;
-               char *tmp;
-
-               name = strchr (ds_def[i], ':');
-               if (name == NULL)
-               {
-                       syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
-                                       ds_def[i], filename);
-                       fclose(log);
-                       remove(filename);
-                       return (-1);
-               }
-
-               name += 1;
-               tmp = strchr (name, ':');
-               if (tmp == NULL)
-               {
-                       syslog (LOG_WARNING, "Invalid DS definition '%s' for %s",
-                                       ds_def[i], filename);
-                       fclose(log);
-                       remove(filename);
-                       return (-1);
-               }
-
-               /* The `%.*s' is needed because there is no null-byte behind
-                * the name. */
-               fprintf(log, ",%.*s", (int) (tmp - name), name);
-       }
-       fprintf(log, "\n");
-       fclose(log);
-
-       return 0;
-}
-
-static int log_update_file (char *host, char *file, char *values,
-               char **ds_def, int ds_num)
-{
-       char *tmp;
-       FILE *fp;
-       struct stat statbuf;
-       char full_file[1024];
-
-       /* Cook the values a bit: Substitute colons with commas */
-       strsubstitute (values, ':', ',');
-
-       /* host == NULL => local mode */
-       if (host != NULL)
-       {
-               if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
-                       return (-1);
-       }
-       else
-       {
-               if (snprintf (full_file, 1024, "%s", file) >= 1024)
-                       return (-1);
-       }
-
-       strncpy (full_file, file, 1024);
-
-       tmp = full_file + strlen (full_file) - 4;
-       assert ((tmp != NULL) && (tmp > full_file));
-
-       /* Change the filename for logfiles. */
-       if (strncmp (tmp, ".rrd", 4) == 0)
-       {
-               time_t now;
-               struct tm *tm;
-
-               /* TODO: Find a way to minimize the calls to `localtime', since
-                * they are pretty expensive.. */
-               now = time (NULL);
-               tm = localtime (&now);
-
-               strftime (tmp, 1024 - (tmp - full_file), "-%Y-%m-%d", tm);
-
-               /* `localtime(3)' returns a pointer to static data,
-                * therefore the pointer may not be free'd. */
-       }
-       else
-               DBG ("The filename ends with `%s' which is unexpected.", tmp);
-
-       if (stat (full_file, &statbuf) == -1)
-       {
-               if (errno == ENOENT)
-               {
-                       if (log_create_file (full_file, ds_def, ds_num))
-                               return (-1);
-               }
-               else
-               {
-                       syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
-                       return (-1);
-               }
-       }
-       else if (!S_ISREG (statbuf.st_mode))
-       {
-               syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
-               return (-1);
-       }
-
-
-       fp = fopen (full_file, "a");
-       if (fp == NULL)
-       {
-               syslog (LOG_WARNING, "Failed to append to %s: %s", full_file,
-                               strerror(errno));
-               return (-1);
-       }
-       fprintf(fp, "%s\n", values);
-       fclose(fp);
-
-       return (0);
-} /* int log_update_file */
-
-#if HAVE_LIBRRD
-static int rrd_create_file (char *filename, char **ds_def, int ds_num)
-{
-       char **argv;
-       int argc;
-       char **rra_def;
-       int rra_num;
-       int i, j;
-       int status = 0;
-
-       if (check_create_dir (filename))
-               return (-1);
-
-       if ((rra_num = rra_get (&rra_def)) < 1)
-       {
-               syslog (LOG_ERR, "rra_create failed: Could not calculate RRAs");
-               return (-1);
-       }
-
-       argc = ds_num + rra_num + 4;
-
-       if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
-       {
-               syslog (LOG_ERR, "rrd_create failed: %s", strerror (errno));
-               return (-1);
-       }
-
-       argv[0] = "create";
-       argv[1] = filename;
-       argv[2] = "-s";
-       argv[3] = COLLECTD_STEP;
-
-       j = 4;
-       for (i = 0; i < ds_num; i++)
-               argv[j++] = ds_def[i];
-       for (i = 0; i < rra_num; i++)
-               argv[j++] = rra_def[i];
-       argv[j] = NULL;
-
-       optind = 0; /* bug in librrd? */
-       rrd_clear_error ();
-       if (rrd_create (argc, argv) == -1)
-       {
-               syslog (LOG_ERR, "rrd_create failed: %s: %s", filename, rrd_get_error ());
-               status = -1;
-       }
-
-       free (argv);
-
-       return (status);
-}
-#endif /* HAVE_LIBRRD */
-
-int rrd_update_file (char *host, char *file, char *values,
-               char **ds_def, int ds_num)
-{
-#if HAVE_LIBRRD
-       struct stat statbuf;
-       char full_file[1024];
-       char *argv[4] = { "update", full_file, values, NULL };
-#endif /* HAVE_LIBRRD */
-
-       /* I'd rather have a function `common_update_file' to make this
-        * decission, but for that we'd need to touch all plugins.. */
-       if (operating_mode == MODE_LOG)
-               return (log_update_file (host, file, values,
-                                       ds_def, ds_num));
-
-#if HAVE_LIBRRD
-       /* host == NULL => local mode */
-       if (host != NULL)
-       {
-               if (snprintf (full_file, 1024, "%s/%s", host, file) >= 1024)
-                       return (-1);
-       }
-       else
-       {
-               if (snprintf (full_file, 1024, "%s", file) >= 1024)
-                       return (-1);
-       }
-
-       if (stat (full_file, &statbuf) == -1)
-       {
-               if (errno == ENOENT)
-               {
-                       if (rrd_create_file (full_file, ds_def, ds_num))
-                               return (-1);
-               }
-               else
-               {
-                       syslog (LOG_ERR, "stat %s: %s", full_file, strerror (errno));
-                       return (-1);
-               }
-       }
-       else if (!S_ISREG (statbuf.st_mode))
-       {
-               syslog (LOG_ERR, "stat %s: Not a regular file!", full_file);
-               return (-1);
-       }
-
-       optind = 0; /* bug in librrd? */
-       rrd_clear_error ();
-       if (rrd_update (3, argv) == -1)
-       {
-               syslog (LOG_WARNING, "rrd_update failed: %s: %s", full_file, rrd_get_error ());
-               return (-1);
-       }
-       return (0);
-/* #endif HAVE_LIBRRD */
-
-#else
-       syslog (LOG_ERR, "`rrd_update_file' was called, but collectd isn't linked against librrd!");
-       return (-1);
-#endif
-}
-
 #ifdef HAVE_LIBKSTAT
 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
 {
@@ -775,13 +430,13 @@ int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
        {
                if ((*ksp_ptr = kstat_lookup (kc, module, instance, name)) == NULL)
                {
-                       syslog (LOG_ERR, "Cound not find kstat %s", ident);
+                       ERROR ("Cound not find kstat %s", ident);
                        return (-1);
                }
 
                if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
                {
-                       syslog (LOG_WARNING, "kstat %s has wrong type", ident);
+                       WARNING ("kstat %s has wrong type", ident);
                        *ksp_ptr = NULL;
                        return (-1);
                }
@@ -794,13 +449,13 @@ int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name)
 
        if (kstat_read (kc, *ksp_ptr, NULL) == -1)
        {
-               syslog (LOG_WARNING, "kstat %s could not be read", ident);
+               WARNING ("kstat %s could not be read", ident);
                return (-1);
        }
 
        if ((*ksp_ptr)->ks_type != KSTAT_TYPE_NAMED)
        {
-               syslog (LOG_WARNING, "kstat %s has wrong type", ident);
+               WARNING ("kstat %s has wrong type", ident);
                return (-1);
        }
 
@@ -840,8 +495,63 @@ long long get_kstat_value (kstat_t *ksp, char *name)
        else if (kn->data_type == KSTAT_DATA_UINT64)
                retval = (long long) kn->value.ui64; /* XXX: Might overflow! */
        else
-               syslog (LOG_WARNING, "get_kstat_value: Not a numeric value: %s", name);
+               WARNING ("get_kstat_value: Not a numeric value: %s", name);
                 
        return (retval);
 }
 #endif /* HAVE_LIBKSTAT */
+
+unsigned long long ntohll (unsigned long long n)
+{
+#if __BYTE_ORDER == __BIG_ENDIAN
+       return (n);
+#else
+       return (((unsigned long long) ntohl (n)) << 32) + ntohl (n >> 32);
+#endif
+} /* unsigned long long ntohll */
+
+unsigned long long htonll (unsigned long long n)
+{
+#if __BYTE_ORDER == __BIG_ENDIAN
+       return (n);
+#else
+       return (((unsigned long long) htonl (n)) << 32) + htonl (n >> 32);
+#endif
+} /* unsigned long long htonll */
+
+int format_name (char *ret, int ret_len,
+               const char *hostname,
+               const char *plugin, const char *plugin_instance,
+               const char *type, const char *type_instance)
+{
+       int  status;
+
+       assert (plugin != NULL);
+       assert (type != NULL);
+
+       if ((plugin_instance == NULL) || (strlen (plugin_instance) == 0))
+       {
+               if ((type_instance == NULL) || (strlen (type_instance) == 0))
+                       status = snprintf (ret, ret_len, "%s/%s/%s",
+                                       hostname, plugin, type);
+               else
+                       status = snprintf (ret, ret_len, "%s/%s/%s-%s",
+                                       hostname, plugin, type,
+                                       type_instance);
+       }
+       else
+       {
+               if ((type_instance == NULL) || (strlen (type_instance) == 0))
+                       status = snprintf (ret, ret_len, "%s/%s-%s/%s",
+                                       hostname, plugin, plugin_instance,
+                                       type);
+               else
+                       status = snprintf (ret, ret_len, "%s/%s-%s/%s-%s",
+                                       hostname, plugin, plugin_instance,
+                                       type, type_instance);
+       }
+
+       if ((status < 1) || (status >= ret_len))
+               return (-1);
+       return (0);
+} /* int format_name */
index 2be2ff5..7808ac9 100644 (file)
@@ -1,21 +1,19 @@
 /**
  * collectd - src/common.h
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
+ * 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 use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence for more details.
+ * 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
- * Licence along with this program; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
- * USA.
+ * 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>
        } \
        (ptr) = NULL
 
+#define STATIC_ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a)))
+
 void sstrncpy(char *d, const char *s, int len);
 char *sstrdup(const char *s);
 void *smalloc(size_t size);
+char *sstrerror (int errnum, char *buf, size_t buflen);
 
 /*
  * NAME
@@ -142,15 +143,27 @@ int strjoin (char *dst, size_t dst_len, char **fields, size_t fields_num, const
  */
 int escape_slashes (char *buf, int buf_len);
 
+int strsubstitute (char *str, char c_from, char c_to);
+
 /* FIXME: `timeval_sub_timespec' needs a description */
 int timeval_sub_timespec (struct timeval *tv0, struct timeval *tv1, struct timespec *ret);
 
-int rrd_update_file (char *host, char *file, char *values,
-               char **ds_def, int ds_num);
+int check_create_dir (const char *file_orig);
 
 #ifdef HAVE_LIBKSTAT
 int get_kstat (kstat_t **ksp_ptr, char *module, int instance, char *name);
 long long get_kstat_value (kstat_t *ksp, char *name);
 #endif
 
+unsigned long long ntohll (unsigned long long n);
+unsigned long long htonll (unsigned long long n);
+
+int format_name (char *ret, int ret_len,
+               const char *hostname,
+               const char *plugin, const char *plugin_instance,
+               const char *type, const char *type_instance);
+#define FORMAT_VL(ret, ret_len, vl, ds) \
+       format_name (ret, ret_len, (vl)->host, (vl)->plugin, (vl)->plugin_instance, \
+                       (ds)->type, (vl)->type_instance)
+
 #endif /* COMMON_H */
index 40b46fa..4b6803e 100644 (file)
 
 #include "collectd.h"
 
-#include "libconfig/libconfig.h"
+#include "liboconfig/oconfig.h"
 
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
-#include "network.h"
-#include "utils_debug.h"
-
-#define SHORTOPT_NONE 0
-
-#define ERR_NOT_NESTED "Sections cannot be nested.\n"
-#define ERR_SECTION_ONLY "`%s' can only be used as section.\n"
-#define ERR_NEEDS_ARG "Section `%s' needs an argument.\n"
-#define ERR_NEEDS_SECTION "`%s' can only be used within a section.\n"
 
 #define ESCAPE_NULL(str) ((str) == NULL ? "(null)" : (str))
 
-#define DEBUG_CALLBACK(shortvar, var, arguments, value) \
-       DBG("shortvar = %s, var = %s, arguments = %s, value = %s, ...", \
-                       ESCAPE_NULL(shortvar), \
-                       ESCAPE_NULL(var), \
-                       ESCAPE_NULL(arguments), \
-                       ESCAPE_NULL(value))
-
-extern int operating_mode;
-
+/*
+ * Private types
+ */
 typedef struct cf_callback
 {
-       char  *type;
-       int  (*callback) (char *, char *);
-       char **keys;
+       const char  *type;
+       int  (*callback) (const char *, const char *);
+       const char **keys;
        int    keys_num;
        struct cf_callback *next;
 } cf_callback_t;
 
-static cf_callback_t *first_callback = NULL;
+typedef struct cf_value_map_s
+{
+       char *key;
+       int (*func) (const oconfig_item_t *);
+} cf_value_map_t;
 
-typedef struct cf_mode_item
+typedef struct cf_global_option_s
 {
        char *key;
        char *value;
-       int   mode;
-} cf_mode_item_t;
+       char *def;
+} cf_global_option_t;
+
+/*
+ * Prototypes of callback functions
+ */
+static int dispatch_value_plugindir (const oconfig_item_t *ci);
+static int dispatch_value_loadplugin (const oconfig_item_t *ci);
 
-/* TODO
- * - LogFile
+/*
+ * Private variables
  */
-static cf_mode_item_t cf_mode_list[] =
+static cf_callback_t *first_callback = NULL;
+
+static cf_value_map_t cf_value_map[] =
 {
-       {"TimeToLive",  NULL, MODE_CLIENT                           },
-       {"PIDFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG },
-       {"DataDir",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG },
-       {"LogFile",     NULL, MODE_CLIENT | MODE_SERVER | MODE_LOCAL | MODE_LOG }
+       {"PluginDir",  dispatch_value_plugindir},
+       {"LoadPlugin", dispatch_value_loadplugin}
 };
-static int cf_mode_num = 4;
+static int cf_value_map_num = STATIC_ARRAY_LEN (cf_value_map);
 
-static int nesting_depth = 0;
-static char *current_module = NULL;
-
-/* `cf_register' needs this prototype */
-static int cf_callback_plugin_dispatch (const char *, const char *,
-               const char *, const char *, lc_flags_t, void *);
+static cf_global_option_t cf_global_options[] =
+{
+       {"BaseDir",   NULL, PKGLOCALSTATEDIR},
+       {"PIDFile",   NULL, PIDFILE},
+       {"Hostname",  NULL, NULL},
+       {"Interval",  NULL, "10"},
+       {"ReadThreads", NULL, "5"},
+       {"TypesDB",   NULL, PLUGINDIR"/types.db"} /* FIXME: Configure path */
+};
+static int cf_global_options_num = STATIC_ARRAY_LEN (cf_global_options);
 
 /*
  * Functions to handle register/unregister, search, and other plugin related
  * stuff
  */
-static cf_callback_t *cf_search (char *type)
+static cf_callback_t *cf_search (const char *type)
 {
        cf_callback_t *cf_cb;
 
@@ -103,7 +102,8 @@ static cf_callback_t *cf_search (char *type)
        return (cf_cb);
 }
 
-static int cf_dispatch (char *type, const char *orig_key, const char *orig_value)
+static int cf_dispatch (const char *type, const char *orig_key,
+               const char *orig_value)
 {
        cf_callback_t *cf_cb;
        char *key;
@@ -111,14 +111,14 @@ static int cf_dispatch (char *type, const char *orig_key, const char *orig_value
        int ret;
        int i;
 
-       DBG ("type = %s, key = %s, value = %s",
+       DEBUG ("type = %s, key = %s, value = %s",
                        ESCAPE_NULL(type),
                        ESCAPE_NULL(orig_key),
                        ESCAPE_NULL(orig_value));
 
        if ((cf_cb = cf_search (type)) == NULL)
        {
-               syslog (LOG_WARNING, "Plugin `%s' did not register a callback.", type);
+               WARNING ("Plugin `%s' did not register a callback.", type);
                return (-1);
        }
 
@@ -142,423 +142,252 @@ static int cf_dispatch (char *type, const char *orig_key, const char *orig_value
        }
 
        if (i >= cf_cb->keys_num)
-               syslog (LOG_WARNING, "Plugin `%s' did not register for value `%s'.", type, key);
+               WARNING ("Plugin `%s' did not register for value `%s'.", type, key);
 
        free (key);
        free (value);
 
-       DBG ("return (%i)", ret);
+       DEBUG ("return (%i)", ret);
 
        return (ret);
-}
-
-void cf_unregister (char *type)
-{
-       cf_callback_t *this, *prev;
-
-       for (prev = NULL, this = first_callback;
-                       this != NULL;
-                       prev = this, this = this->next)
-               if (strcasecmp (this->type, type) == 0)
-               {
-                       if (prev == NULL)
-                               first_callback = this->next;
-                       else
-                               prev->next = this->next;
+} /* int cf_dispatch */
 
-                       free (this);
-                       break;
-               }
-}
-
-void cf_register (char *type,
-               int (*callback) (char *, char *),
-               char **keys, int keys_num)
+static int dispatch_global_option (const oconfig_item_t *ci)
 {
-       cf_callback_t *cf_cb;
-       char buf[64];
-       int i;
-
-       /* Remove this module from the list, if it already exists */
-       cf_unregister (type);
-
-       /* This pointer will be free'd in `cf_unregister' */
-       if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
-               return;
-
-       cf_cb->type     = type;
-       cf_cb->callback = callback;
-       cf_cb->keys     = keys;
-       cf_cb->keys_num = keys_num;
-
-       cf_cb->next = first_callback;
-       first_callback = cf_cb;
-
-       for (i = 0; i < keys_num; i++)
-       {
-               if (snprintf (buf, 64, "Plugin.%s", keys[i]) < 64)
-               {
-                       /* This may be called multiple times for the same
-                        * `key', but apparently `lc_register_*' can handle
-                        * it.. */
-                       lc_register_callback (buf, SHORTOPT_NONE,
-                                       LC_VAR_STRING, cf_callback_plugin_dispatch,
-                                       NULL);
-               }
-               else
-               {
-                       DBG ("Key was truncated: `%s'", ESCAPE_NULL(keys[i]));
-               }
-       }
-}
-
-/*
- * Other query functions
- */
-char *cf_get_option (const char *key, char *def)
-{
-       int i;
-
-       for (i = 0; i < cf_mode_num; i++)
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type == OCONFIG_TYPE_STRING)
+               return (global_option_set (ci->key, ci->values[0].value.string));
+       else if (ci->values[0].type == OCONFIG_TYPE_NUMBER)
        {
-               if ((cf_mode_list[i].mode & operating_mode) == 0)
-                       continue;
-
-               if (strcasecmp (cf_mode_list[i].key, key) != 0)
-                       continue;
-
-               if (cf_mode_list[i].value != NULL)
-                       return (cf_mode_list[i].value);
-               return (def);
+               char tmp[128];
+               snprintf (tmp, sizeof (tmp), "%lf", ci->values[0].value.number);
+               tmp[127] = '\0';
+               return (global_option_set (ci->key, tmp));
        }
 
-       return (NULL);
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Functions for the actual parsing                                    *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+       return (-1);
+} /* int dispatch_global_option */
 
-/*
- * `cf_callback_mode'
- *   Chose the `operating_mode'
- *
- * Mode `value'
- */
-static int cf_callback_mode (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+static int dispatch_value_plugindir (const oconfig_item_t *ci)
 {
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       if (strcasecmp (value, "Client") == 0)
-               operating_mode = MODE_CLIENT;
-#if HAVE_LIBRRD
-       else if (strcasecmp (value, "Server") == 0)
-               operating_mode = MODE_SERVER;
-       else if (strcasecmp (value, "Local") == 0)
-               operating_mode = MODE_LOCAL;
-#else /* !HAVE_LIBRRD */
-       else if (strcasecmp (value, "Server") == 0)
-       {
-               fprintf (stderr, "Invalid mode `Server': "
-                               "You need to link against librrd for this "
-                               "mode to be available.\n");
-               syslog (LOG_ERR, "Invalid mode `Server': "
-                               "You need to link against librrd for this "
-                               "mode to be available.");
-               return (LC_CBRET_ERROR);
-       }
-       else if (strcasecmp (value, "Local") == 0)
-       {
-               fprintf (stderr, "Invalid mode `Local': "
-                               "You need to link against librrd for this "
-                               "mode to be available.\n");
-               syslog (LOG_ERR, "Invalid mode `Local': "
-                               "You need to link against librrd for this "
-                               "mode to be available.");
-               return (LC_CBRET_ERROR);
-       }
-#endif
-       else if (strcasecmp (value, "Log") == 0)
-               operating_mode = MODE_LOG;
-       else
-       {
-               syslog (LOG_ERR, "Invalid value for config option `Mode': `%s'", value);
-               return (LC_CBRET_ERROR);
-       }
+       assert (strcasecmp (ci->key, "PluginDir") == 0);
+       
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
 
-       return (LC_CBRET_OKAY);
+       plugin_set_dir (ci->values[0].value.string);
+       return (0);
 }
 
-/*
- * `cf_callback_mode_plugindir'
- *   Change the plugin directory
- *
- * <Mode xxx>
- *   PluginDir `value'
- * </Mode>
- */
-static int cf_callback_mode_plugindir (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+static int dispatch_value_loadplugin (const oconfig_item_t *ci)
 {
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
+       assert (strcasecmp (ci->key, "LoadPlugin") == 0);
 
-       plugin_set_dir (value);
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
 
-       return (LC_CBRET_OKAY);
-}
+       return (plugin_load (ci->values[0].value.string));
+} /* int dispatch_value_loadplugin */
 
-static int cf_callback_mode_option (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+static int dispatch_value_plugin (const char *plugin, oconfig_item_t *ci)
 {
-       cf_mode_item_t *item;
-
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       if (extra == NULL)
-       {
-               fprintf (stderr, "No extra..?\n");
-               return (LC_CBRET_ERROR);
-       }
-
-       item = (cf_mode_item_t *) extra;
-
-       if (strcasecmp (item->key, shortvar))
-       {
-               fprintf (stderr, "Wrong extra..\n");
-               return (LC_CBRET_ERROR);
-       }
-
-       if ((operating_mode & item->mode) == 0)
-       {
-               fprintf (stderr, "Option `%s' is not valid in this mode!\n", shortvar);
-               return (LC_CBRET_ERROR);
-       }
+       char  buffer[4096];
+       char *buffer_ptr;
+       int   buffer_free;
+       int i;
 
-       if (item->value != NULL)
-       {
-               free (item->value);
-               item->value = NULL;
-       }
+       buffer_ptr = buffer;
+       buffer_free = sizeof (buffer);
 
-       if ((item->value = strdup (value)) == NULL)
+       for (i = 0; i < ci->values_num; i++)
        {
-               perror ("strdup");
-               return (LC_CBRET_ERROR);
+               int status = -1;
+
+               if (ci->values[i].type == OCONFIG_TYPE_STRING)
+                       status = snprintf (buffer_ptr, buffer_free, " %s",
+                                       ci->values[i].value.string);
+               else if (ci->values[i].type == OCONFIG_TYPE_NUMBER)
+                       status = snprintf (buffer_ptr, buffer_free, " %lf",
+                                       ci->values[i].value.number);
+               else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN)
+                       status = snprintf (buffer_ptr, buffer_free, " %s",
+                                       ci->values[i].value.boolean
+                                       ? "true" : "false");
+
+               if ((status < 0) || (status >= buffer_free))
+                       return (-1);
+               buffer_free -= status;
+               buffer_ptr  += status;
        }
+       /* skip the initial space */
+       buffer_ptr = buffer + 1;
 
-       return (LC_CBRET_OKAY);
-}
+       return (cf_dispatch (plugin, ci->key, buffer_ptr));
+} /* int plugin_conf_dispatch */
 
-/*
- * `cf_callback_mode_loadmodule':
- *   Load a plugin.
- *
- * <Mode xxx>
- *   LoadPlugin `value'
- * </Mode>
- */
-static int cf_callback_mode_loadmodule (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+static int dispatch_value (const oconfig_item_t *ci)
 {
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       if (plugin_load (value))
-               syslog (LOG_ERR, "plugin_load (%s): failed to load plugin", value);
-
-       /* Return `okay' even if there was an error, because it's not a syntax
-        * problem.. */
-       return (LC_CBRET_OKAY);
-}
+       int ret = -2;
+       int i;
 
-static int cf_callback_socket (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
-{
-       char *buffer;
+       for (i = 0; i < cf_value_map_num; i++)
+               if (strcasecmp (cf_value_map[i].key, ci->key) == 0)
+               {
+                       ret = cf_value_map[i].func (ci);
+                       break;
+               }
 
-       char *fields[3];
-       int   numfields;
+       for (i = 0; i < cf_global_options_num; i++)
+               if (strcasecmp (cf_global_options[i].key, ci->key) == 0)
+               {
+                       ret = dispatch_global_option (ci);
+                       break;
+               }
 
-       char *node;
-       char *service = NET_DEFAULT_PORT;
+       return (ret);
+} /* int dispatch_value */
 
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
+static int dispatch_block_plugin (oconfig_item_t *ci)
+{
+       int i;
+       char *name;
 
-       buffer = strdup (value);
-       if (buffer == NULL)
-               return (LC_CBRET_ERROR);
+       if (strcasecmp (ci->key, "Plugin") != 0)
+               return (-1);
+       if (ci->values_num != 1)
+               return (-1);
+       if (ci->values[0].type != OCONFIG_TYPE_STRING)
+               return (-1);
 
-       numfields = strsplit (buffer, fields, 3);
+       name = ci->values[0].value.string;
 
-       if ((numfields != 1) && (numfields != 2))
+       for (i = 0; i < ci->children_num; i++)
        {
-               syslog (LOG_ERR, "Invalid number of arguments to `%s'",
-                               shortvar);
-               free (buffer);
-               return (LC_CBRET_ERROR);
+               if (ci->children[i].children == NULL)
+                       dispatch_value_plugin (name, ci->children + i);
+               else
+                       {DEBUG ("No nested config blocks allow for plugins. Yet.");}
        }
 
-       node = fields[0];
-       if (numfields == 2)
-               service = fields[1];
+       return (0);
+}
 
-       /* Still return `LC_CBRET_OKAY' because this is not an syntax error */
-       if (network_create_socket (node, service) < 1)
-               syslog (LOG_ERR, "network_create_socket (%s, %s) failed",
-                               node, service);
 
-       free (buffer);
+static int dispatch_block (oconfig_item_t *ci)
+{
+       if (strcasecmp (ci->key, "Plugin") == 0)
+               return (dispatch_block_plugin (ci));
 
-       return (LC_CBRET_OKAY);
+       return (0);
 }
 
-/*
- * `cf_callback_plugin'
- *   Start/end section `plugin'
- *
- * <Plugin `arguments'>
- *   ...
- * </Plugin>
+/* 
+ * Public functions
  */
-static int cf_callback_plugin (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+int global_option_set (const char *option, const char *value)
 {
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       if (flags == LC_FLAGS_SECTIONSTART)
-       {
-               if (nesting_depth != 0)
-               {
-                       fprintf (stderr, ERR_NOT_NESTED);
-                       return (LC_CBRET_ERROR);
-               }
-
-               if (arguments == NULL)
-               {
-                       fprintf (stderr, ERR_NEEDS_ARG, shortvar);
-                       return (LC_CBRET_ERROR);
-               }
+       int i;
 
-               if ((current_module = strdup (arguments)) == NULL)
-               {
-                       perror ("strdup");
-                       return (LC_CBRET_ERROR);
-               }
+       DEBUG ("option = %s; value = %s;", option, value);
 
-               nesting_depth++;
+       for (i = 0; i < cf_global_options_num; i++)
+               if (strcasecmp (cf_global_options[i].key, option) == 0)
+                       break;
 
-               if (cf_search (current_module) != NULL)
-                       return (LC_CBRET_OKAY);
-               else
-                       return (LC_CBRET_IGNORESECTION);
-       }
-       else if (flags == LC_FLAGS_SECTIONEND)
-       {
-               if (current_module != NULL)
-               {
-                       free (current_module);
-                       current_module = NULL;
-               }
+       if (i >= cf_global_options_num)
+               return (-1);
 
-               nesting_depth--;
+       sfree (cf_global_options[i].value);
 
-               return (LC_CBRET_OKAY);
-       }
+       if (value != NULL)
+               cf_global_options[i].value = strdup (value);
        else
-       {
-               fprintf (stderr, ERR_SECTION_ONLY, shortvar);
-               return (LC_CBRET_ERROR);
-       }
+               cf_global_options[i].value = NULL;
+
+       return (0);
 }
 
-/*
- * `cf_callback_plugin_dispatch'
- *   Send options within `plugin' sections to the plugin that requests it.
- *
- * <Plugin `current_module'>
- *   `var' `value'
- * </Plugin>
- */
-static int cf_callback_plugin_dispatch (const char *shortvar, const char *var,
-               const char *arguments, const char *value, lc_flags_t flags,
-               void *extra)
+const char *global_option_get (const char *option)
 {
-       DEBUG_CALLBACK (shortvar, var, arguments, value);
-
-       if ((nesting_depth == 0) || (current_module == NULL))
-       {
-               fprintf (stderr, ERR_NEEDS_SECTION, shortvar);
-               return (LC_CBRET_ERROR);
-       }
+       int i;
 
-       /* Send the data to the plugin */
-       if (cf_dispatch (current_module, shortvar, value) < 0)
-               return (LC_CBRET_ERROR);
+       for (i = 0; i < cf_global_options_num; i++)
+               if (strcasecmp (cf_global_options[i].key, option) == 0)
+                       break;
 
-       return (LC_CBRET_OKAY);
-}
+       if (i >= cf_global_options_num)
+               return (NULL);
+       
+       return ((cf_global_options[i].value != NULL)
+                       ? cf_global_options[i].value
+                       : cf_global_options[i].def);
+} /* char *global_option_get */
 
-static void cf_init (void)
+void cf_unregister (const char *type)
 {
-       static int run_once = 0;
-       int i;
+       cf_callback_t *this, *prev;
 
-       if (run_once != 0)
-               return;
-       run_once = 1;
+       for (prev = NULL, this = first_callback;
+                       this != NULL;
+                       prev = this, this = this->next)
+               if (strcasecmp (this->type, type) == 0)
+               {
+                       if (prev == NULL)
+                               first_callback = this->next;
+                       else
+                               prev->next = this->next;
 
-       lc_register_callback ("Mode", SHORTOPT_NONE, LC_VAR_STRING,
-                       cf_callback_mode, NULL);
-       lc_register_callback ("Plugin", SHORTOPT_NONE, LC_VAR_SECTION,
-                       cf_callback_plugin, NULL);
+                       free (this);
+                       break;
+               }
+} /* void cf_unregister */
 
-       lc_register_callback ("PluginDir", SHORTOPT_NONE,
-                       LC_VAR_STRING, cf_callback_mode_plugindir, NULL);
-       lc_register_callback ("LoadPlugin", SHORTOPT_NONE,
-                       LC_VAR_STRING, cf_callback_mode_loadmodule, NULL);
+void cf_register (const char *type,
+               int (*callback) (const char *, const char *),
+               const char **keys, int keys_num)
+{
+       cf_callback_t *cf_cb;
 
-       lc_register_callback ("Listen", SHORTOPT_NONE,
-                       LC_VAR_STRING, cf_callback_socket, NULL);
-       lc_register_callback ("Server", SHORTOPT_NONE,
-                       LC_VAR_STRING, cf_callback_socket, NULL);
+       /* Remove this module from the list, if it already exists */
+       cf_unregister (type);
 
-       for (i = 0; i < cf_mode_num; i++)
-       {
-               cf_mode_item_t *item;
+       /* This pointer will be free'd in `cf_unregister' */
+       if ((cf_cb = (cf_callback_t *) malloc (sizeof (cf_callback_t))) == NULL)
+               return;
 
-               item = &cf_mode_list[i];
+       cf_cb->type     = type;
+       cf_cb->callback = callback;
+       cf_cb->keys     = keys;
+       cf_cb->keys_num = keys_num;
 
-               lc_register_callback (item->key, SHORTOPT_NONE, LC_VAR_STRING,
-                               cf_callback_mode_option, (void *) item);
-       }
-}
+       cf_cb->next = first_callback;
+       first_callback = cf_cb;
+} /* void cf_register */
 
 int cf_read (char *filename)
 {
-       cf_init ();
-
-       if (filename == NULL)
-               filename = CONFIGFILE;
-
-       DBG ("Starting to parse file `%s'", filename);
+       oconfig_item_t *conf;
+       int i;
 
-       /* int lc_process_file(const char *appname, const char *pathname, lc_conf_type_t type); */
-       if (lc_process_file ("collectd", filename, LC_CONF_APACHE))
+       conf = oconfig_parse_file (filename);
+       if (conf == NULL)
        {
-               syslog (LOG_ERR, "lc_process_file (%s): %s", filename, lc_geterrstr ());
+               ERROR ("Unable to read config file %s.", filename);
                return (-1);
        }
 
-       DBG ("Done parsing file `%s'", filename);
-
-       /* free memory and stuff */
-       lc_cleanup ();
+       for (i = 0; i < conf->children_num; i++)
+       {
+               if (conf->children[i].children == NULL)
+                       dispatch_value (conf->children + i);
+               else
+                       dispatch_block (conf->children + i);
+       }
 
        return (0);
-}
+} /* int cf_read */
index 0bb11b2..0ee8f33 100644 (file)
@@ -31,7 +31,7 @@
  *  `type'      Name of the plugin (must be the same as passed to
  *              `plugin_register'
  */
-void cf_unregister (char *type);
+void cf_unregister (const char *type);
 
 /*
  * DESCRIPTION
@@ -57,24 +57,9 @@ void cf_unregister (char *type);
  *  exists for each `type' at any time. This means that `cf_register' may be
  *  called multiple times, but only the last call will have an effect.
  */
-void cf_register (char *type,
-               int (*callback) (char *, char *),
-               char **keys, int keys_num);
-
-/*
- * DESCRIPTION
- *  `cf_get_option' returns various general options.
- *
- * PARAMETERS
- *  `key'       Name of the option to query.
- *  `def'       Pointer to return as default value.
- *
- * RETURN VALUE
- *  The pointer returned is part of an internal structure and may not be
- *  changed. If the option is not found for whatever reason (wrong key, option
- *  not allowed for currently selected mode, ...) `NULL' is returned.
- */
-char *cf_get_option (const char *key, char *def);
+void cf_register (const char *type,
+               int (*callback) (const char *, const char *),
+               const char **keys, int keys_num);
 
 /*
  * DESCRIPTION
@@ -93,4 +78,7 @@ char *cf_get_option (const char *key, char *def);
  */
 int cf_read (char *filename);
 
+int global_option_set (const char *option, const char *value);
+const char *global_option_get (const char *option);
+
 #endif /* defined(CONFIGFILE_H) */
index b087d66..99e7a66 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -1,11 +1,10 @@
 /**
  * collectd - src/cpu.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -23,9 +22,6 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
-#include "utils_debug.h"
-
-#define MODULE_NAME "cpu"
 
 #ifdef HAVE_MACH_KERN_RETURN_H
 # include <mach/kern_return.h>
@@ -78,6 +74,7 @@
 # define CPU_HAVE_READ 0
 #endif
 
+#if CPU_HAVE_READ
 #ifdef PROCESSOR_CPU_LOAD_INFO
 static mach_port_t port_host;
 static processor_port_array_t cpu_list;
@@ -106,42 +103,25 @@ static int numcpu;
 static int numcpu;
 #endif /* HAVE_SYSCTLBYNAME */
 
-static char *cpu_filename = "cpu-%s.rrd";
-
-static char *ds_def[] =
-{
-       "DS:user:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:nice:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:syst:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:idle:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wait:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int ds_num = 5;
-
-static void cpu_init (void)
+static int init (void)
 {
 #if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
        kern_return_t status;
-       int collectd_step;
 
        port_host = mach_host_self ();
 
        /* FIXME: Free `cpu_list' if it's not NULL */
        if ((status = host_processors (port_host, &cpu_list, &cpu_list_len)) != KERN_SUCCESS)
        {
-               syslog (LOG_ERR, "cpu-plugin: host_processors returned %i\n", (int) status);
+               ERROR ("cpu plugin: host_processors returned %i", (int) status);
                cpu_list_len = 0;
-               return;
+               return (-1);
        }
 
-       DBG ("host_processors returned %i %s", (int) cpu_list_len, cpu_list_len == 1 ? "processor" : "processors");
-       syslog (LOG_INFO, "cpu-plugin: Found %i processor%s.", (int) cpu_list_len, cpu_list_len == 1 ? "" : "s");
+       DEBUG ("host_processors returned %i %s", (int) cpu_list_len, cpu_list_len == 1 ? "processor" : "processors");
+       INFO ("cpu plugin: Found %i processor%s.", (int) cpu_list_len, cpu_list_len == 1 ? "" : "s");
 
-       collectd_step = atoi (COLLECTD_STEP);
-       if ((collectd_step > 0) && (collectd_step <= 86400))
-               cpu_temp_retry_max = 86400 / collectd_step;
-               
+       cpu_temp_retry_max = 86400 / interval_g;
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
 #elif defined(HAVE_LIBKSTAT)
@@ -150,7 +130,7 @@ static void cpu_init (void)
        numcpu = 0;
 
        if (kc == NULL)
-               return;
+               return (-1);
 
        /* Solaris doesn't count linear.. *sigh* */
        for (numcpu = 0, ksp_chain = kc->kc_chain;
@@ -167,50 +147,40 @@ static void cpu_init (void)
 
        if (sysctlbyname ("hw.ncpu", &numcpu, &numcpu_size, NULL, 0) < 0)
        {
-               syslog (LOG_WARNING, "cpu: sysctlbyname: %s", strerror (errno));
-               return;
+               char errbuf[1024];
+               WARNING ("cpu plugin: sysctlbyname: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
        if (numcpu != 1)
-               syslog (LOG_NOTICE, "cpu: Only one processor supported when using `sysctlbyname' (found %i)", numcpu);
+               NOTICE ("cpu: Only one processor supported when using `sysctlbyname' (found %i)", numcpu);
 #endif
 
-       return;
-}
+       return (0);
+} /* int init */
 
-static void cpu_write (char *host, char *inst, char *val)
+static void submit (int cpu_num, const char *type_instance, counter_t value)
 {
-       char file[512];
-       int status;
-
-       status = snprintf (file, 512, cpu_filename, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, ds_def, ds_num);
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "cpu");
+       snprintf (vl.plugin_instance, sizeof (vl.type_instance),
+                       "%i", cpu_num);
+       vl.plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+       strcpy (vl.type_instance, type_instance);
+
+       plugin_dispatch_values ("cpu", &vl);
 }
 
-#if CPU_HAVE_READ
-#define BUFSIZE 512
-static void cpu_submit (int cpu_num, unsigned long long user,
-               unsigned long long nice, unsigned long long syst,
-               unsigned long long idle, unsigned long long wait)
-{
-       char buf[BUFSIZE];
-       char cpu[16];
-
-       if (snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu", (unsigned int) curtime,
-                               user, nice, syst, idle, wait) >= BUFSIZE)
-               return;
-       snprintf (cpu, 16, "%i", cpu_num);
-
-       plugin_submit (MODULE_NAME, cpu, buf);
-}
-#undef BUFSIZE
-
-static void cpu_read (void)
+static int cpu_read (void)
 {
 #if PROCESSOR_CPU_LOAD_INFO || PROCESSOR_TEMPERATURE
        int cpu;
@@ -238,21 +208,20 @@ static void cpu_read (void)
                                                PROCESSOR_CPU_LOAD_INFO, &cpu_host,
                                                (processor_info_t) &cpu_info, &cpu_info_len)) != KERN_SUCCESS)
                {
-                       syslog (LOG_ERR, "cpu-plugin: processor_info failed with status %i\n", (int) status);
+                       ERROR ("cpu plugin: processor_info failed with status %i", (int) status);
                        continue;
                }
 
                if (cpu_info_len < CPU_STATE_MAX)
                {
-                       syslog (LOG_ERR, "cpu-plugin: processor_info returned only %i elements..\n", cpu_info_len);
+                       ERROR ("cpu plugin: processor_info returned only %i elements..", cpu_info_len);
                        continue;
                }
 
-               cpu_submit (cpu, cpu_info.cpu_ticks[CPU_STATE_USER],
-                               cpu_info.cpu_ticks[CPU_STATE_NICE],
-                               cpu_info.cpu_ticks[CPU_STATE_SYSTEM],
-                               cpu_info.cpu_ticks[CPU_STATE_IDLE],
-                               0ULL);
+               submit (cpu, "user", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
+               submit (cpu, "nice", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
+               submit (cpu, "system", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
+               submit (cpu, "idle", (counter_t) cpu_info.cpu_ticks[CPU_STATE_USER]);
 #endif /* PROCESSOR_CPU_LOAD_INFO */
 #if PROCESSOR_TEMPERATURE
                /*
@@ -275,7 +244,7 @@ static void cpu_read (void)
                                cpu_temp, &cpu_temp_len);
                if (status != KERN_SUCCESS)
                {
-                       syslog (LOG_ERR, "cpu-plugin: processor_info failed: %s",
+                       ERROR ("cpu plugin: processor_info failed: %s",
                                        mach_error_string (status));
 
                        cpu_temp_retry_counter = cpu_temp_retry_step;
@@ -288,7 +257,7 @@ static void cpu_read (void)
 
                if (cpu_temp_len != 1)
                {
-                       DBG ("processor_info (PROCESSOR_TEMPERATURE) returned %i elements..?",
+                       DEBUG ("processor_info (PROCESSOR_TEMPERATURE) returned %i elements..?",
                                        (int) cpu_temp_len);
                        continue;
                }
@@ -296,18 +265,17 @@ static void cpu_read (void)
                cpu_temp_retry_counter = 0;
                cpu_temp_retry_step    = 1;
 
-               DBG ("cpu_temp = %i", (int) cpu_temp);
+               DEBUG ("cpu_temp = %i", (int) cpu_temp);
 #endif /* PROCESSOR_TEMPERATURE */
        }
 /* #endif PROCESSOR_CPU_LOAD_INFO */
 
 #elif defined(KERNEL_LINUX)
-# define BUFSIZE 1024
        int cpu;
-       unsigned long long user, nice, syst, idle;
-       unsigned long long wait, intr, sitr; /* sitr == soft interrupt */
+       counter_t user, nice, syst, idle;
+       counter_t wait, intr, sitr; /* sitr == soft interrupt */
        FILE *fh;
-       char buf[BUFSIZE];
+       char buf[1024];
 
        char *fields[9];
        int numfields;
@@ -316,16 +284,17 @@ static void cpu_read (void)
 
        if ((fh = fopen ("/proc/stat", "r")) == NULL)
        {
+               char errbuf[1024];
                plugin_complain (LOG_ERR, &complain_obj, "cpu plugin: "
                                "fopen (/proc/stat) failed: %s",
-                               strerror (errno));
-               return;
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
        plugin_relief (LOG_NOTICE, &complain_obj, "cpu plugin: "
                        "fopen (/proc/stat) succeeded.");
 
-       while (fgets (buf, BUFSIZE, fh) != NULL)
+       while (fgets (buf, 1024, fh) != NULL)
        {
                if (strncmp (buf, "cpu", 3))
                        continue;
@@ -342,31 +311,32 @@ static void cpu_read (void)
                syst = atoll (fields[3]);
                idle = atoll (fields[4]);
 
+               submit (cpu, "user", user);
+               submit (cpu, "nice", nice);
+               submit (cpu, "system", syst);
+               submit (cpu, "idle", idle);
+
                if (numfields >= 8)
                {
                        wait = atoll (fields[5]);
                        intr = atoll (fields[6]);
                        sitr = atoll (fields[7]);
 
-                       /* I doubt anyone cares about the time spent in
-                        * interrupt handlers.. */
-                       syst += intr + sitr;
-               }
-               else
-               {
-                       wait = 0LL;
-               }
+                       submit (cpu, "wait", wait);
+                       submit (cpu, "interrupt", intr);
+                       submit (cpu, "softirq", sitr);
 
-               cpu_submit (cpu, user, nice, syst, idle, wait);
+                       if (numfields >= 9)
+                               submit (cpu, "steal", atoll (fields[8]));
+               }
        }
 
        fclose (fh);
-#undef BUFSIZE
 /* #endif defined(KERNEL_LINUX) */
 
 #elif defined(HAVE_LIBKSTAT)
        int cpu;
-       unsigned long long user, syst, idle, wait;
+       counter_t user, syst, idle, wait;
        static cpu_stat_t cs;
 
        if (kc == NULL)
@@ -377,13 +347,15 @@ static void cpu_read (void)
                if (kstat_read (kc, ksp[cpu], &cs) == -1)
                        continue; /* error message? */
 
-               idle = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_IDLE];
-               user = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_USER];
-               syst = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_KERNEL];
-               wait = (unsigned long long) cs.cpu_sysinfo.cpu[CPU_WAIT];
+               idle = (counter_t) cs.cpu_sysinfo.cpu[CPU_IDLE];
+               user = (counter_t) cs.cpu_sysinfo.cpu[CPU_USER];
+               syst = (counter_t) cs.cpu_sysinfo.cpu[CPU_KERNEL];
+               wait = (counter_t) cs.cpu_sysinfo.cpu[CPU_WAIT];
 
-               cpu_submit (ksp[cpu]->ks_instance,
-                               user, 0LL, syst, idle, wait);
+               submit (ksp[cpu]->ks_instance, "user", user);
+               submit (ksp[cpu]->ks_instance, "system", syst);
+               submit (ksp[cpu]->ks_instance, "idle", idle);
+               submit (ksp[cpu]->ks_instance, "wait", wait);
        }
 /* #endif defined(HAVE_LIBKSTAT) */
 
@@ -397,10 +369,11 @@ static void cpu_read (void)
 
        if (sysctlbyname("kern.cp_time", &cpuinfo, &cpuinfo_size, NULL, 0) < 0)
        {
+               char errbuf[1024];
                plugin_complain (LOG_ERR, &complain_obj, "cpu plugin: "
                                "sysctlbyname failed: %s.",
-                               strerror (errno));
-               return;
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
        plugin_relief (LOG_NOTICE, &complain_obj, "cpu plugin: "
@@ -408,19 +381,20 @@ static void cpu_read (void)
 
        cpuinfo[CP_SYS] += cpuinfo[CP_INTR];
 
-       /* FIXME: Instance is always `0' */
-       cpu_submit (0, cpuinfo[CP_USER], cpuinfo[CP_NICE], cpuinfo[CP_SYS], cpuinfo[CP_IDLE], 0LL);
+       submit (0, "user", cpuinfo[CP_USER]);
+       submit (0, "nice", cpuinfo[CP_NICE]);
+       submit (0, "system", cpuinfo[CP_SYS]);
+       submit (0, "idle", cpuinfo[CP_IDLE]);
 #endif
 
-       return;
+       return (0);
 }
-#else
-# define cpu_read NULL
 #endif /* CPU_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, cpu_init, cpu_read, cpu_write);
-}
-
-#undef MODULE_NAME
+#if CPU_HAVE_READ
+       plugin_register_init ("cpu", init);
+       plugin_register_read ("cpu", cpu_read);
+#endif /* CPU_HAVE_READ */
+} /* void module_register */
index ba0149a..7a5c8fd 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/cpufreq.c
- * Copyright (C) 2005,2006  Peter Holik
+ * Copyright (C) 2005-2007  Peter Holik
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
 # define CPUFREQ_HAVE_READ 0
 #endif
 
-static char *cpufreq_file = "cpufreq-%s.rrd";
-
-static char *ds_def[] =
-{
-       "DS:value:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int ds_num = 1;
-
+#if CPUFREQ_HAVE_READ
 #ifdef KERNEL_LINUX
 static int num_cpu = 0;
 #endif
 
-#define BUFSIZE 256
-
-static void cpufreq_init (void)
+static int cpufreq_init (void)
 {
 #ifdef KERNEL_LINUX
         int status;
-       char filename[BUFSIZE];
+       char filename[256];
 
        num_cpu = 0;
 
        while (1)
        {
-               status = snprintf (filename, BUFSIZE, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", num_cpu);
-               if (status < 1 || status >= BUFSIZE)
+               status = snprintf (filename, sizeof (filename),
+                               "/sys/devices/system/cpu/cpu%d/cpufreq/"
+                               "scaling_cur_freq", num_cpu);
+               if (status < 1 || status >= sizeof (filename))
                        break;
 
-               if (access(filename, R_OK))
+               if (access (filename, R_OK))
                        break;
 
                num_cpu++;
        }
 
-       syslog (LOG_INFO, MODULE_NAME" found %d cpu(s)", num_cpu);
+       INFO ("cpufreq plugin: Found %d CPU%s", num_cpu,
+                       (num_cpu == 1) ? "" : "s");
+
+       if (num_cpu == 0)
+               plugin_unregister_read ("cpufreq");
 #endif /* defined(KERNEL_LINUX) */
 
-       return;
-}
+       return (0);
+} /* int cpufreq_init */
 
-static void cpufreq_write (char *host, char *inst, char *val)
+static void cpufreq_submit (int cpu_num, double value)
 {
-        int status;
-        char file[BUFSIZE];
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-        status = snprintf (file, BUFSIZE, cpufreq_file, inst);
-        if (status < 1 || status >= BUFSIZE)
-                return;
+       values[0].gauge = value;
 
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "cpufreq");
+       snprintf (vl.type_instance, sizeof (vl.type_instance),
+                       "%i", cpu_num);
 
-#if CPUFREQ_HAVE_READ
-static void cpufreq_submit (int cpu_num, unsigned long long val)
-{
-       char buf[BUFSIZE];
-       char cpu[16];
-
-       if (snprintf (buf, BUFSIZE, "%u:%llu", (unsigned int) curtime, val) >= BUFSIZE)
-               return;
-        snprintf (cpu, 16, "%i", cpu_num);
-
-       plugin_submit (MODULE_NAME, cpu, buf);
+       plugin_dispatch_values ("cpufreq", &vl);
 }
 
-static void cpufreq_read (void)
+static int cpufreq_read (void)
 {
 #ifdef KERNEL_LINUX
         int status;
        unsigned long long val;
        int i = 0;
        FILE *fp;
-       char filename[BUFSIZE];
+       char filename[256];
        char buffer[16];
 
        for (i = 0; i < num_cpu; i++)
        {
-               status = snprintf (filename, BUFSIZE, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
-               if (status < 1 || status >= BUFSIZE)
-                       return;
+               status = snprintf (filename, sizeof (filename),
+                               "/sys/devices/system/cpu/cpu%d/cpufreq/"
+                               "scaling_cur_freq", i);
+               if (status < 1 || status >= sizeof (filename))
+                       return (-1);
 
                if ((fp = fopen (filename, "r")) == NULL)
                {
-                       syslog (LOG_WARNING, "cpufreq: fopen: %s", strerror (errno));
-                       return;
+                       char errbuf[1024];
+                       WARNING ("cpufreq: fopen (%s): %s", filename,
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+                       return (-1);
                }
 
                if (fgets (buffer, 16, fp) == NULL)
                {
-                       syslog (LOG_WARNING, "cpufreq: fgets: %s", strerror (errno));
+                       char errbuf[1024];
+                       WARNING ("cpufreq: fgets: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        fclose (fp);
-                       return;
+                       return (-1);
                }
 
                if (fclose (fp))
-                       syslog (LOG_WARNING, "cpufreq: fclose: %s", strerror (errno));
+               {
+                       char errbuf[1024];
+                       WARNING ("cpufreq: fclose: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+               }
+
 
                /* You're seeing correctly: The file is reporting kHz values.. */
                val = atoll (buffer) * 1000;
@@ -137,16 +140,15 @@ static void cpufreq_read (void)
        }
 #endif /* defined(KERNEL_LINUX) */
 
-       return;
-}
-#else
-#define cpufreq_read NULL
-#endif
+       return (0);
+} /* int cpufreq_read */
+#endif /* CPUFREQ_HAVE_READ */
 #undef BUFSIZE
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, cpufreq_init, cpufreq_read, cpufreq_write);
+#if CPUFREQ_HAVE_READ
+       plugin_register_init ("cpufreq", cpufreq_init);
+       plugin_register_read ("cpufreq", cpufreq_read);
+#endif /* CPUFREQ_HAVE_READ */
 }
-
-#undef MODULE_NAME
diff --git a/src/csv.c b/src/csv.c
new file mode 100644 (file)
index 0000000..fcd707e
--- /dev/null
+++ b/src/csv.c
@@ -0,0 +1,273 @@
+/**
+ * collectd - src/csv.c
+ * Copyright (C) 2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+
+/*
+ * Private variables
+ */
+static const char *config_keys[] =
+{
+       "DataDir"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static char *datadir   = NULL;
+
+static int value_list_to_string (char *buffer, int buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       int offset;
+       int status;
+       int i;
+
+       memset (buffer, '\0', sizeof (buffer_len));
+
+       status = snprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
+       if ((status < 1) || (status >= buffer_len))
+               return (-1);
+       offset = status;
+
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if ((ds->ds[i].type != DS_TYPE_COUNTER)
+                               && (ds->ds[i].type != DS_TYPE_GAUGE))
+                       return (-1);
+
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+                       status = snprintf (buffer + offset, buffer_len - offset,
+                                       ",%llu", vl->values[i].counter);
+               else
+                       status = snprintf (buffer + offset, buffer_len - offset,
+                                       ",%lf", vl->values[i].gauge);
+
+               if ((status < 1) || (status >= (buffer_len - offset)))
+                       return (-1);
+
+               offset += status;
+       } /* for ds->ds_num */
+
+       return (0);
+} /* int value_list_to_string */
+
+static int value_list_to_filename (char *buffer, int buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       int offset = 0;
+       int status;
+
+       if (datadir != NULL)
+       {
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s/", datadir);
+               if ((status < 1) || (status >= buffer_len - offset))
+                       return (-1);
+               offset += status;
+       }
+
+       status = snprintf (buffer + offset, buffer_len - offset,
+                       "%s/", vl->host);
+       if ((status < 1) || (status >= buffer_len - offset))
+               return (-1);
+       offset += status;
+
+       if (strlen (vl->plugin_instance) > 0)
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s-%s/", vl->plugin, vl->plugin_instance);
+       else
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s/", vl->plugin);
+       if ((status < 1) || (status >= buffer_len - offset))
+               return (-1);
+       offset += status;
+
+       if (strlen (vl->type_instance) > 0)
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s-%s", ds->type, vl->type_instance);
+       else
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s", ds->type);
+       if ((status < 1) || (status >= buffer_len - offset))
+               return (-1);
+       offset += status;
+
+       {
+               time_t now;
+               struct tm stm;
+
+               /* TODO: Find a way to minimize the calls to `localtime_r',
+                * since they are pretty expensive.. */
+               now = time (NULL);
+               if (localtime_r (&now, &stm) == NULL)
+               {
+                       ERROR ("csv plugin: localtime_r failed");
+                       return (1);
+               }
+
+               strftime (buffer + offset, buffer_len - offset,
+                               "-%Y-%m-%d", &stm);
+       }
+
+       return (0);
+} /* int value_list_to_filename */
+
+static int csv_create_file (const char *filename, const data_set_t *ds)
+{
+       FILE *csv;
+       int i;
+
+       if (check_create_dir (filename))
+               return (-1);
+
+       csv = fopen (filename, "w");
+       if (csv == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("csv plugin: fopen (%s) failed: %s",
+                               filename,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       fprintf (csv, "epoch");
+       for (i = 0; i < ds->ds_num; i++)
+               fprintf (csv, ",%s", ds->ds[i].name);
+
+       fprintf (csv, "\n");
+       fclose (csv);
+
+       return 0;
+} /* int csv_create_file */
+
+static int csv_config (const char *key, const char *value)
+{
+       if (strcasecmp ("DataDir", key) == 0)
+       {
+               if (datadir != NULL)
+                       free (datadir);
+               datadir = strdup (value);
+               if (datadir != NULL)
+               {
+                       int len = strlen (datadir);
+                       while ((len > 0) && (datadir[len - 1] == '/'))
+                       {
+                               len--;
+                               datadir[len] = '\0';
+                       }
+                       if (len <= 0)
+                       {
+                               free (datadir);
+                               datadir = NULL;
+                       }
+               }
+       }
+       else
+       {
+               return (-1);
+       }
+       return (0);
+} /* int csv_config */
+
+static int csv_write (const data_set_t *ds, const value_list_t *vl)
+{
+       struct stat  statbuf;
+       char         filename[512];
+       char         values[512];
+       FILE        *csv;
+       int          csv_fd;
+       struct flock fl;
+       int          status;
+
+       if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+               return (-1);
+
+       DEBUG ("csv plugin: csv_write: filename = %s;", filename);
+
+       if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
+               return (-1);
+
+       if (stat (filename, &statbuf) == -1)
+       {
+               if (errno == ENOENT)
+               {
+                       if (csv_create_file (filename, ds))
+                               return (-1);
+               }
+               else
+               {
+                       char errbuf[1024];
+                       ERROR ("stat(%s) failed: %s", filename,
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+                       return (-1);
+               }
+       }
+       else if (!S_ISREG (statbuf.st_mode))
+       {
+               ERROR ("stat(%s): Not a regular file!",
+                               filename);
+               return (-1);
+       }
+
+       csv = fopen (filename, "a");
+       if (csv == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("csv plugin: fopen (%s) failed: %s", filename,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+       csv_fd = fileno (csv);
+
+       memset (&fl, '\0', sizeof (fl));
+       fl.l_start  = 0;
+       fl.l_len    = 0; /* till end of file */
+       fl.l_pid    = getpid ();
+       fl.l_type   = F_WRLCK;
+       fl.l_whence = SEEK_SET;
+
+       status = fcntl (csv_fd, F_SETLK, &fl);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("csv plugin: flock (%s) failed: %s", filename,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               fclose (csv);
+               return (-1);
+       }
+
+       fprintf (csv, "%s\n", values);
+
+       /* The lock is implicitely released. I we don't release it explicitely
+        * because the `FILE *' may need to flush a cache first */
+       fclose (csv);
+
+       return (0);
+} /* int csv_write */
+
+void module_register (void)
+{
+       plugin_register_config ("csv", csv_config,
+                       config_keys, config_keys_num);
+       plugin_register_write ("csv", csv_write);
+} /* void module_register */
index d327164..433f4e2 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -1,11 +1,10 @@
 /**
  * collectd - src/df.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -27,8 +26,6 @@
 #include "utils_mount.h"
 #include "utils_ignorelist.h"
 
-#define MODULE_NAME "df"
-
 #if HAVE_STATFS || HAVE_STATVFS
 # define DF_HAVE_READ 1
 #else
 # define BLOCKSIZE(s) (s).f_bsize
 #endif
 
-static char *filename_template = "df-%s.rrd";
-
-static char *ds_def[] =
-{
-       "DS:used:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       "DS:free:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int ds_num = 2;
-
-static char *config_keys[] =
+#if DF_HAVE_READ
+static const char *config_keys[] =
 {
        "Device",
        "MountPoint",
@@ -73,9 +61,7 @@ static ignorelist_t *il_device = NULL;
 static ignorelist_t *il_mountpoint = NULL;
 static ignorelist_t *il_fstype = NULL;
 
-#define BUFSIZE 512
-
-static void df_init (void)
+static int df_init (void)
 {
        if (il_device == NULL)
                il_device = ignorelist_create (1);
@@ -84,10 +70,10 @@ static void df_init (void)
        if (il_fstype == NULL)
                il_fstype = ignorelist_create (1);
 
-       return;
+       return (0);
 }
 
-static int df_config (char *key, char *value)
+static int df_config (const char *key, const char *value)
 {
        df_init ();
 
@@ -131,35 +117,28 @@ static int df_config (char *key, char *value)
        return (-1);
 }
 
-static void df_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE];
-       int status;
-
-       status = snprintf (file, BUFSIZE, filename_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= BUFSIZE)
-               return;
-
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
-
-#if DF_HAVE_READ
 static void df_submit (char *df_name,
-               unsigned long long df_used,
-               unsigned long long df_free)
+               gauge_t df_used,
+               gauge_t df_free)
 {
-       char buf[BUFSIZE];
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (buf, BUFSIZE, "%u:%llu:%llu", (unsigned int) curtime,
-                               df_used, df_free) >= BUFSIZE)
-               return;
+       values[0].gauge = df_used;
+       values[1].gauge = df_free;
 
-       plugin_submit (MODULE_NAME, df_name, buf);
-}
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "df");
+       strcpy (vl.plugin_instance, "");
+       strncpy (vl.type_instance, df_name, sizeof (vl.type_instance));
+
+       plugin_dispatch_values ("df", &vl);
+} /* void df_submit */
 
-static void df_read (void)
+static int df_read (void)
 {
 #if HAVE_STATVFS
        struct statvfs statbuf;
@@ -171,19 +150,22 @@ static void df_read (void)
        cu_mount_t *mnt_ptr;
 
        unsigned long long blocksize;
-       unsigned long long df_free;
-       unsigned long long df_used;
-       char mnt_name[BUFSIZE];
+       gauge_t df_free;
+       gauge_t df_used;
+       char mnt_name[256];
 
        mnt_list = NULL;
        if (cu_mount_getlist (&mnt_list) == NULL)
-               return;
+               return (-1);
 
        for (mnt_ptr = mnt_list; mnt_ptr != NULL; mnt_ptr = mnt_ptr->next)
        {
                if (STATANYFS (mnt_ptr->dir, &statbuf) < 0)
                {
-                       syslog (LOG_ERR, "statv?fs failed: %s", strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("statv?fs failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        continue;
                }
 
@@ -196,13 +178,13 @@ static void df_read (void)
 
                if (strcmp (mnt_ptr->dir, "/") == 0)
                {
-                       strncpy (mnt_name, "root", BUFSIZE);
+                       strncpy (mnt_name, "root", sizeof (mnt_name));
                }
                else
                {
                        int i, len;
 
-                       strncpy (mnt_name, mnt_ptr->dir + 1, BUFSIZE);
+                       strncpy (mnt_name, mnt_ptr->dir + 1, sizeof (mnt_name));
                        len = strlen (mnt_name);
 
                        for (i = 0; i < len; i++)
@@ -224,16 +206,17 @@ static void df_read (void)
        }
 
        cu_mount_freelist (mnt_list);
-} /* static void df_read (void) */
-#else
-# define df_read NULL
+
+       return (0);
+} /* int df_read */
 #endif /* DF_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, df_init, df_read, df_write);
-       cf_register (MODULE_NAME, df_config, config_keys, config_keys_num);
-}
-
-#undef BUFSIZE
-#undef MODULE_NAME
+#if DF_HAVE_READ
+       plugin_register_config ("df", df_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("df", df_init);
+       plugin_register_read ("df", df_read);
+#endif
+} /* void module_register */
index c809fdb..38c161f 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/disk.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -23,9 +22,6 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
-#include "utils_debug.h"
-
-#define MODULE_NAME "disk"
 
 #if HAVE_MACH_MACH_TYPES_H
 #  include <mach/mach_types.h>
 #  include <IOKit/IOBSD.h>
 #endif
 
+#if HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#ifndef UINT_MAX
+#  define UINT_MAX 4294967295U
+#endif
+
 #if HAVE_IOKIT_IOKITLIB_H || KERNEL_LINUX || HAVE_LIBKSTAT
 # define DISK_HAVE_READ 1
 #else
 # define DISK_HAVE_READ 0
 #endif
 
-static char *disk_filename_template = "disk-%s.rrd";
-static char *part_filename_template = "partition-%s.rrd";
-
-/* 104857600 == 100 MB */
-static char *disk_ds_def[] =
-{
-       "DS:rcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rmerged:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600",
-       "DS:rtime:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wmerged:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600",
-       "DS:wtime:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int disk_ds_num = 8;
-
-static char *part_ds_def[] =
-{
-       "DS:rcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600",
-       "DS:wcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wbytes:COUNTER:"COLLECTD_HEARTBEAT":0:104857600",
-       NULL
-};
-static int part_ds_num = 4;
-
+#if DISK_HAVE_READ
 #if HAVE_IOKIT_IOKITLIB_H
 static mach_port_t io_master_port = MACH_PORT_NULL;
 /* #endif HAVE_IOKIT_IOKITLIB_H */
@@ -98,20 +74,27 @@ typedef struct diskstats
 {
        char *name;
 
-       /* This overflows in roughly 1361 year */
+       /* This overflows in roughly 1361 years */
        unsigned int poll_count;
 
-       unsigned long long read_sectors;
-       unsigned long long write_sectors;
+       counter_t read_sectors;
+       counter_t write_sectors;
 
-       unsigned long long read_bytes;
-       unsigned long long write_bytes;
+       counter_t read_bytes;
+       counter_t write_bytes;
+
+       counter_t read_ops;
+       counter_t write_ops;
+       counter_t read_time;
+       counter_t write_time;
+
+       counter_t avg_read_time;
+       counter_t avg_write_time;
 
        struct diskstats *next;
 } diskstats_t;
 
 static diskstats_t *disklist;
-static int min_poll_count;
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
@@ -121,7 +104,7 @@ static kstat_t *ksp[MAX_NUMDISK];
 static int numdisk = 0;
 #endif /* HAVE_LIBKSTAT */
 
-static void disk_init (void)
+static int disk_init (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H
        kern_return_t status;
@@ -136,25 +119,15 @@ static void disk_init (void)
        status = IOMasterPort (MACH_PORT_NULL, &io_master_port);
        if (status != kIOReturnSuccess)
        {
-               syslog (LOG_ERR, "IOMasterPort failed: %s",
+               ERROR ("IOMasterPort failed: %s",
                                mach_error_string (status));
                io_master_port = MACH_PORT_NULL;
-               return;
+               return (-1);
        }
 /* #endif HAVE_IOKIT_IOKITLIB_H */
 
 #elif KERNEL_LINUX
-       int step;
-       int heartbeat;
-
-       step = atoi (COLLECTD_STEP);
-       heartbeat = atoi (COLLECTD_HEARTBEAT);
-
-       assert (step > 0);
-       assert (heartbeat >= step);
-
-       min_poll_count = 1 + (heartbeat / step);
-       DBG ("min_poll_count = %i;", min_poll_count);
+       /* do nothing */
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
@@ -163,7 +136,7 @@ static void disk_init (void)
        numdisk = 0;
 
        if (kc == NULL)
-               return;
+               return (-1);
 
        for (numdisk = 0, ksp_chain = kc->kc_chain;
                        (numdisk < MAX_NUMDISK) && (ksp_chain != NULL);
@@ -178,83 +151,29 @@ static void disk_init (void)
        }
 #endif /* HAVE_LIBKSTAT */
 
-       return;
-}
-
-static void disk_write (char *host, char *inst, char *val)
-{
-       char file[512];
-       int status;
-
-       status = snprintf (file, 512, disk_filename_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, disk_ds_def, disk_ds_num);
-}
+       return (0);
+} /* int disk_init */
 
-static void partition_write (char *host, char *inst, char *val)
+static void disk_submit (const char *plugin_instance,
+               const char *type,
+               counter_t read, counter_t write)
 {
-       char file[512];
-       int status;
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       status = snprintf (file, 512, part_filename_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
+       values[0].counter = read;
+       values[1].counter = write;
 
-       rrd_update_file (host, file, val, part_ds_def, part_ds_num);
-}
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "disk");
+       strncpy (vl.plugin_instance, plugin_instance,
+                       sizeof (vl.plugin_instance));
 
-#if DISK_HAVE_READ
-#define BUFSIZE 512
-static void disk_submit (char *disk_name,
-               unsigned long long read_count,
-               unsigned long long read_merged,
-               unsigned long long read_bytes,
-               unsigned long long read_time,
-               unsigned long long write_count,
-               unsigned long long write_merged,
-               unsigned long long write_bytes,
-               unsigned long long write_time)
-{
-       char buf[BUFSIZE];
-
-       if (snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu",
-                               (unsigned int) curtime,
-                               read_count, read_merged, read_bytes, read_time,
-                               write_count, write_merged, write_bytes,
-                               write_time) >= BUFSIZE)
-               return;
-
-       DBG ("disk_name = %s; buf = %s;",
-                       disk_name, buf);
-
-       plugin_submit (MODULE_NAME, disk_name, buf);
-}
-
-#if KERNEL_LINUX || HAVE_LIBKSTAT
-static void partition_submit (char *part_name,
-               unsigned long long read_count,
-               unsigned long long read_bytes,
-               unsigned long long write_count,
-               unsigned long long write_bytes)
-{
-       char buf[BUFSIZE];
-
-       if (snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu",
-                               (unsigned int) curtime,
-                               read_count, read_bytes, write_count,
-                               write_bytes) >= BUFSIZE)
-               return;
-
-       plugin_submit ("partition", part_name, buf);
-}
-#endif /* KERNEL_LINUX || HAVE_LIBKSTAT */
-#undef BUFSIZE
+       plugin_dispatch_values (type, &vl);
+} /* void disk_submit */
 
 #if HAVE_IOKIT_IOKITLIB_H
 static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
@@ -268,7 +187,7 @@ static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
                        kCFStringEncodingASCII);
        if (key_obj == NULL)
        {
-               DBG ("CFStringCreateWithCString (%s) failed.", key);
+               DEBUG ("CFStringCreateWithCString (%s) failed.", key);
                return (-1LL);
        }
        
@@ -279,13 +198,13 @@ static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
 
        if (val_obj == NULL)
        {
-               DBG ("CFDictionaryGetValue (%s) failed.", key);
+               DEBUG ("CFDictionaryGetValue (%s) failed.", key);
                return (-1LL);
        }
 
        if (!CFNumberGetValue (val_obj, kCFNumberSInt64Type, &val_int))
        {
-               DBG ("CFNumberGetValue (%s) failed.", key);
+               DEBUG ("CFNumberGetValue (%s) failed.", key);
                return (-1LL);
        }
 
@@ -293,7 +212,7 @@ static signed long long dict_get_value (CFDictionaryRef dict, const char *key)
 }
 #endif /* HAVE_IOKIT_IOKITLIB_H */
 
-static void disk_read (void)
+static int disk_read (void)
 {
 #if HAVE_IOKIT_IOKITLIB_H
        io_registry_entry_t     disk;
@@ -324,7 +243,7 @@ static void disk_read (void)
        {
                plugin_complain (LOG_ERR, &complain_obj, "disk plugin: "
                                "IOServiceGetMatchingServices failed.");
-               return;
+               return (-1);
        }
        else if (complain_obj.interval != 0)
        {
@@ -343,7 +262,7 @@ static void disk_read (void)
                                != kIOReturnSuccess)
                {
                        /* This fails for example for DVD/CD drives.. */
-                       DBG ("IORegistryEntryGetChildEntry (disk) failed: 0x%08x", status);
+                       DEBUG ("IORegistryEntryGetChildEntry (disk) failed: 0x%08x", status);
                        IOObjectRelease (disk);
                        continue;
                }
@@ -355,7 +274,7 @@ static void disk_read (void)
                                        kNilOptions)
                                != kIOReturnSuccess)
                {
-                       syslog (LOG_ERR, "disk-plugin: IORegistryEntryCreateCFProperties failed.");
+                       ERROR ("disk-plugin: IORegistryEntryCreateCFProperties failed.");
                        IOObjectRelease (disk_child);
                        IOObjectRelease (disk);
                        continue;
@@ -363,7 +282,7 @@ static void disk_read (void)
 
                if (props_dict == NULL)
                {
-                       DBG ("IORegistryEntryCreateCFProperties (disk) failed.");
+                       DEBUG ("IORegistryEntryCreateCFProperties (disk) failed.");
                        IOObjectRelease (disk_child);
                        IOObjectRelease (disk);
                        continue;
@@ -374,7 +293,7 @@ static void disk_read (void)
 
                if (stats_dict == NULL)
                {
-                       DBG ("CFDictionaryGetValue (%s) failed.",
+                       DEBUG ("CFDictionaryGetValue (%s) failed.",
                                        kIOBlockStorageDriverStatisticsKey);
                        CFRelease (props_dict);
                        IOObjectRelease (disk_child);
@@ -388,13 +307,14 @@ static void disk_read (void)
                                        kNilOptions)
                                != kIOReturnSuccess)
                {
-                       DBG ("IORegistryEntryCreateCFProperties (disk_child) failed.");
+                       DEBUG ("IORegistryEntryCreateCFProperties (disk_child) failed.");
                        IOObjectRelease (disk_child);
                        CFRelease (props_dict);
                        IOObjectRelease (disk);
                        continue;
                }
 
+               /* kIOBSDNameKey */
                disk_major = (int) dict_get_value (child_dict,
                                kIOBSDMajorKey);
                disk_minor = (int) dict_get_value (child_dict,
@@ -409,29 +329,33 @@ static void disk_read (void)
                                kIOBlockStorageDriverStatisticsWritesKey);
                write_byt = dict_get_value (stats_dict,
                                kIOBlockStorageDriverStatisticsBytesWrittenKey);
+               /* This property describes the number of nanoseconds spent
+                * performing writes since the block storage driver was
+                * instantiated. It is one of the statistic entries listed
+                * under the top-level kIOBlockStorageDriverStatisticsKey
+                * property table. It has an OSNumber value. */
                write_tme = dict_get_value (stats_dict,
                                kIOBlockStorageDriverStatisticsTotalWriteTimeKey);
 
                if (snprintf (disk_name, 64, "%i-%i", disk_major, disk_minor) >= 64)
                {
-                       DBG ("snprintf (major, minor) failed.");
+                       DEBUG ("snprintf (major, minor) failed.");
                        CFRelease (child_dict);
                        IOObjectRelease (disk_child);
                        CFRelease (props_dict);
                        IOObjectRelease (disk);
                        continue;
                }
-               DBG ("disk_name = %s", disk_name);
-
-               if ((read_ops != -1LL)
-                               || (read_byt != -1LL)
-                               || (read_tme != -1LL)
-                               || (write_ops != -1LL)
-                               || (write_byt != -1LL)
-                               || (write_tme != -1LL))
-                       disk_submit (disk_name,
-                                       read_ops, 0ULL, read_byt, read_tme,
-                                       write_ops, 0ULL, write_byt, write_tme);
+               DEBUG ("disk_name = %s", disk_name);
+
+               if ((read_byt != -1LL) || (write_byt != -1LL))
+                       disk_submit (disk_name, "disk_octets", read_byt, write_byt);
+               if ((read_ops != -1LL) || (write_ops != -1LL))
+                       disk_submit (disk_name, "disk_ops", read_ops, write_ops);
+               if ((read_tme != -1LL) || (write_tme != -1LL))
+                       disk_submit (disk_name, "disk_time",
+                                       read_tme / 1000,
+                                       write_tme / 1000);
 
                CFRelease (child_dict);
                IOObjectRelease (disk_child);
@@ -444,7 +368,6 @@ static void disk_read (void)
 #elif KERNEL_LINUX
        FILE *fh;
        char buffer[1024];
-       char disk_name[128];
        
        char *fields[32];
        int numfields;
@@ -453,17 +376,15 @@ static void disk_read (void)
        int major = 0;
        int minor = 0;
 
-       unsigned long long read_sectors  = 0ULL;
-       unsigned long long write_sectors = 0ULL;
-
-       unsigned long long read_count    = 0ULL;
-       unsigned long long read_merged   = 0ULL;
-       unsigned long long read_bytes    = 0ULL;
-       unsigned long long read_time     = 0ULL;
-       unsigned long long write_count   = 0ULL;
-       unsigned long long write_merged  = 0ULL;
-       unsigned long long write_bytes   = 0ULL;
-       unsigned long long write_time    = 0ULL;
+       counter_t read_sectors  = 0;
+       counter_t write_sectors = 0;
+
+       counter_t read_ops      = 0;
+       counter_t read_merged   = 0;
+       counter_t read_time     = 0;
+       counter_t write_ops     = 0;
+       counter_t write_merged  = 0;
+       counter_t write_time    = 0;
        int is_disk = 0;
 
        diskstats_t *ds, *pre_ds;
@@ -474,18 +395,23 @@ static void disk_read (void)
        {
                if ((fh = fopen ("/proc/partitions", "r")) == NULL)
                {
-                       plugin_complain (LOG_ERR, &complain_obj, "disk plugin: Failed to open /proc/{diskstats,partitions}.");
-                       return;
+                       plugin_complain (LOG_ERR, &complain_obj,
+                                       "disk plugin: Failed to open /proc/"
+                                       "{diskstats,partitions}.");
+                       return (-1);
                }
 
                /* Kernel is 2.4.* */
                fieldshift = 1;
        }
 
-       plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: Succeeded to open /proc/{diskstats,partitions}.");
+       plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: "
+                       "Succeeded to open /proc/{diskstats,partitions}.");
 
-       while (fgets (buffer, 1024, fh) != NULL)
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
+               char *disk_name;
+
                numfields = strsplit (buffer, fields, 32);
 
                if ((numfields != (14 + fieldshift)) && (numfields != 7))
@@ -494,9 +420,7 @@ static void disk_read (void)
                major = atoll (fields[0]);
                minor = atoll (fields[1]);
 
-               if (snprintf (disk_name, 128, "%i-%i", major, minor) < 1)
-                       continue;
-               disk_name[127] = '\0';
+               disk_name = fields[2];
 
                for (ds = disklist, pre_ds = disklist; ds != NULL; pre_ds = ds, ds = ds->next)
                        if (strcmp (disk_name, ds->name) == 0)
@@ -523,15 +447,15 @@ static void disk_read (void)
                if (numfields == 7)
                {
                        /* Kernel 2.6, Partition */
-                       read_count    = atoll (fields[3]);
+                       read_ops      = atoll (fields[3]);
                        read_sectors  = atoll (fields[4]);
-                       write_count   = atoll (fields[5]);
+                       write_ops     = atoll (fields[5]);
                        write_sectors = atoll (fields[6]);
                }
                else if (numfields == (14 + fieldshift))
                {
-                       read_count  =  atoll (fields[3 + fieldshift]);
-                       write_count =  atoll (fields[7 + fieldshift]);
+                       read_ops  =  atoll (fields[3 + fieldshift]);
+                       write_ops =  atoll (fields[7 + fieldshift]);
 
                        read_sectors  = atoll (fields[5 + fieldshift]);
                        write_sectors = atoll (fields[9 + fieldshift]);
@@ -547,57 +471,146 @@ static void disk_read (void)
                }
                else
                {
-                       DBG ("numfields = %i; => unknown file format.", numfields);
+                       DEBUG ("numfields = %i; => unknown file format.", numfields);
                        continue;
                }
 
+               {
+                       counter_t diff_read_sectors;
+                       counter_t diff_write_sectors;
+
                /* If the counter wraps around, it's only 32 bits.. */
-               if (read_sectors < ds->read_sectors)
-                       ds->read_bytes += 512 * ((0xFFFFFFFF - ds->read_sectors) + read_sectors);
-               else
-                       ds->read_bytes += 512 * (read_sectors - ds->read_sectors);
+                       if (read_sectors < ds->read_sectors)
+                               diff_read_sectors = 1 + read_sectors
+                                       + (UINT_MAX - ds->read_sectors);
+                       else
+                               diff_read_sectors = read_sectors - ds->read_sectors;
+                       if (write_sectors < ds->write_sectors)
+                               diff_write_sectors = 1 + write_sectors
+                                       + (UINT_MAX - ds->write_sectors);
+                       else
+                               diff_write_sectors = write_sectors - ds->write_sectors;
 
-               if (write_sectors < ds->write_sectors)
-                       ds->write_bytes += 512 * ((0xFFFFFFFF - ds->write_sectors) + write_sectors);
-               else
-                       ds->write_bytes += 512 * (write_sectors - ds->write_sectors);
+                       ds->read_bytes += 512 * diff_read_sectors;
+                       ds->write_bytes += 512 * diff_write_sectors;
+                       ds->read_sectors = read_sectors;
+                       ds->write_sectors = write_sectors;
+               }
 
-               ds->read_sectors  = read_sectors;
-               ds->write_sectors = write_sectors;
-               read_bytes  = ds->read_bytes;
-               write_bytes = ds->write_bytes;
+               /* Calculate the average time an io-op needs to complete */
+               if (is_disk)
+               {
+                       counter_t diff_read_ops;
+                       counter_t diff_write_ops;
+                       counter_t diff_read_time;
+                       counter_t diff_write_time;
+
+                       if (read_ops < ds->read_ops)
+                               diff_read_ops = 1 + read_ops
+                                       + (UINT_MAX - ds->read_ops);
+                       else
+                               diff_read_ops = read_ops - ds->read_ops;
+                       DEBUG ("disk plugin: disk_name = %s; read_ops = %llu; "
+                                       "ds->read_ops = %llu; diff_read_ops = %llu;",
+                                       disk_name,
+                                       read_ops, ds->read_ops, diff_read_ops);
+
+                       if (write_ops < ds->write_ops)
+                               diff_write_ops = 1 + write_ops
+                                       + (UINT_MAX - ds->write_ops);
+                       else
+                               diff_write_ops = write_ops - ds->write_ops;
+
+                       if (read_time < ds->read_time)
+                               diff_read_time = 1 + read_time
+                                       + (UINT_MAX - ds->read_time);
+                       else
+                               diff_read_time = read_time - ds->read_time;
+
+                       if (write_time < ds->write_time)
+                               diff_write_time = 1 + write_time
+                                       + (UINT_MAX - ds->write_time);
+                       else
+                               diff_write_time = write_time - ds->write_time;
+
+                       if (diff_read_ops != 0)
+                               ds->avg_read_time += (diff_read_time
+                                               + (diff_read_ops / 2))
+                                       / diff_read_ops;
+                       if (diff_write_ops != 0)
+                               ds->avg_write_time += (diff_write_time
+                                               + (diff_write_ops / 2))
+                                       / diff_write_ops;
+
+                       ds->read_ops = read_ops;
+                       ds->read_time = read_time;
+                       ds->write_ops = write_ops;
+                       ds->write_time = write_time;
+               } /* if (is_disk) */
 
                /* Don't write to the RRDs if we've just started.. */
                ds->poll_count++;
-               if (ds->poll_count <= min_poll_count)
+               if (ds->poll_count <= 2)
                {
-                       DBG ("(ds->poll_count = %i) <= (min_poll_count = %i); => Not writing.",
-                                       ds->poll_count, min_poll_count);
+                       DEBUG ("disk plugin: (ds->poll_count = %i) <= "
+                                       "(min_poll_count = 2); => Not writing.",
+                                       ds->poll_count);
                        continue;
                }
 
-               if ((read_count == 0) && (write_count == 0))
+               if ((read_ops == 0) && (write_ops == 0))
                {
-                       DBG ("((read_count == 0) && (write_count == 0)); => Not writing.");
+                       DEBUG ("disk plugin: ((read_ops == 0) && "
+                                       "(write_ops == 0)); => Not writing.");
                        continue;
                }
 
+               if ((ds->read_bytes != 0) || (ds->write_bytes != 0))
+                       disk_submit (disk_name, "disk_octets",
+                                       ds->read_bytes, ds->write_bytes);
+
+               if ((ds->read_ops != 0) || (ds->write_ops != 0))
+                       disk_submit (disk_name, "disk_ops",
+                                       read_ops, write_ops);
+
+               if ((ds->avg_read_time != 0) || (ds->avg_write_time != 0))
+                       disk_submit (disk_name, "disk_time",
+                                       ds->avg_read_time, ds->avg_write_time);
+
                if (is_disk)
-                       disk_submit (disk_name, read_count, read_merged, read_bytes, read_time,
-                                       write_count, write_merged, write_bytes, write_time);
-               else
-                       partition_submit (disk_name, read_count, read_bytes, write_count, write_bytes);
-       }
+               {
+                       if ((read_merged != -1LL) || (write_merged != -1LL))
+                               disk_submit (disk_name, "disk_merged",
+                                               read_merged, write_merged);
+               } /* if (is_disk) */
+       } /* while (fgets (buffer, sizeof (buffer), fh) != NULL) */
 
        fclose (fh);
 /* #endif defined(KERNEL_LINUX) */
 
 #elif HAVE_LIBKSTAT
+# if HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_NWRITES && HAVE_KSTAT_IO_T_WTIME
+#  define KIO_ROCTETS reads
+#  define KIO_WOCTETS writes
+#  define KIO_ROPS    nreads
+#  define KIO_WOPS    nwrites
+#  define KIO_RTIME   rtime
+#  define KIO_WTIME   wtime
+# elif HAVE_KSTAT_IO_T_NWRITTEN && HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_WTIME
+#  define KIO_ROCTETS nread
+#  define KIO_WOCTETS nwritten
+#  define KIO_ROPS    reads
+#  define KIO_WOPS    writes
+#  define KIO_RTIME   rtime
+#  define KIO_WTIME   wtime
+# else
+#  error "kstat_io_t does not have the required members"
+# endif
        static kstat_io_t kio;
        int i;
 
        if (kc == NULL)
-               return;
+               return (-1);
 
        for (i = 0; i < numdisk; i++)
        {
@@ -605,24 +618,33 @@ static void disk_read (void)
                        continue;
 
                if (strncmp (ksp[i]->ks_class, "disk", 4) == 0)
-                       disk_submit (ksp[i]->ks_name,
-                                       kio.reads,  0LL, kio.nread,    kio.rtime,
-                                       kio.writes, 0LL, kio.nwritten, kio.wtime);
+               {
+                       disk_submit (ksp[i]->ks_name, "disk_octets",
+                                       kio.KIO_ROCTETS, kio.KIO_WOCTETS);
+                       disk_submit (ksp[i]->ks_name, "disk_ops",
+                                       kio.KIO_ROPS, kio.KIO_WOPS);
+                       /* FIXME: Convert this to microseconds if necessary */
+                       disk_submit (ksp[i]->ks_name, "disk_time",
+                                       kio.KIO_RTIME, kio.KIO_WTIME);
+               }
                else if (strncmp (ksp[i]->ks_class, "partition", 9) == 0)
-                       partition_submit (ksp[i]->ks_name,
-                                       kio.reads, kio.nread,
-                                       kio.writes,kio.nwritten);
+               {
+                       disk_submit (ksp[i]->ks_name, "disk_octets",
+                                       kio.KIO_ROCTETS, kio.KIO_WOCTETS);
+                       disk_submit (ksp[i]->ks_name, "disk_ops",
+                                       kio.KIO_ROPS, kio.KIO_WOPS);
+               }
        }
 #endif /* defined(HAVE_LIBKSTAT) */
-} /* static void disk_read (void) */
-#else
-# define disk_read NULL
+
+       return (0);
+} /* int disk_read */
 #endif /* DISK_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register ("partition", NULL, NULL, partition_write);
-       plugin_register (MODULE_NAME, disk_init, disk_read, disk_write);
-}
-
-#undef MODULE_NAME
+#if DISK_HAVE_READ
+       plugin_register_init ("disk", disk_init);
+       plugin_register_read ("disk", disk_read);
+#endif /* DISK_HAVE_READ */
+} /* void module_register */
index cd73a7e..7ce7aad 100644 (file)
--- a/src/dns.c
+++ b/src/dns.c
@@ -1,11 +1,10 @@
 /**
  * collectd - src/dns.c
- * Copyright (C) 2006  Florian octo Forster
+ * Copyright (C) 2006,2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
-#include "utils_debug.h"
-
-#define MODULE_NAME "dns"
 
 #if HAVE_LIBPCAP && HAVE_LIBPTHREAD
 # include "utils_dns.h"
 # include <pthread.h>
 # include <pcap.h>
-# include <sys/poll.h>
+# include <poll.h>
 # define DNS_HAVE_READ 1
 #else
 # define DNS_HAVE_READ 0
@@ -54,43 +50,8 @@ typedef struct counter_list_s counter_list_t;
 /*
  * Private variables
  */
-static char *traffic_file   = "dns/dns_traffic.rrd";
-static char *qtype_file   = "dns/qtype-%s.rrd";
-static char *opcode_file  = "dns/opcode-%s.rrd";
-static char *rcode_file   = "dns/rcode-%s.rrd";
-
-static char *traffic_ds_def[] =
-{
-       /* Limit to 1GBit/s */
-       "DS:queries:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
-       "DS:responses:COUNTER:"COLLECTD_HEARTBEAT":0:125000000",
-       NULL
-};
-static int traffic_ds_num = 2;
-
-static char *qtype_ds_def[] =
-{
-       "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
-       NULL
-};
-static int qtype_ds_num = 1;
-
-static char *opcode_ds_def[] =
-{
-       "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
-       NULL
-};
-static int opcode_ds_num = 1;
-
-static char *rcode_ds_def[] =
-{
-       "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:65535",
-       NULL
-};
-static int rcode_ds_num = 1;
-
 #if DNS_HAVE_READ
-static char *config_keys[] =
+static const char *config_keys[] =
 {
        "Interface",
        "IgnoreSource",
@@ -101,8 +62,8 @@ static int config_keys_num = 2;
 #define PCAP_SNAPLEN 1460
 static char   *pcap_device = NULL;
 
-static unsigned int    tr_queries;
-static unsigned int    tr_responses;
+static counter_t       tr_queries;
+static counter_t       tr_responses;
 static counter_list_t *qtype_list;
 static counter_list_t *opcode_list;
 static counter_list_t *rcode_list;
@@ -124,14 +85,14 @@ static counter_list_t *counter_list_search (counter_list_t **list, unsigned int
 {
        counter_list_t *entry;
 
-       DBG ("counter_list_search (list = %p, key = %u)",
+       DEBUG ("counter_list_search (list = %p, key = %u)",
                        (void *) *list, key);
 
        for (entry = *list; entry != NULL; entry = entry->next)
                if (entry->key == key)
                        break;
 
-       DBG ("return (%p)", (void *) entry);
+       DEBUG ("return (%p)", (void *) entry);
        return (entry);
 }
 
@@ -140,7 +101,7 @@ static counter_list_t *counter_list_create (counter_list_t **list,
 {
        counter_list_t *entry;
 
-       DBG ("counter_list_create (list = %p, key = %u, value = %u)",
+       DEBUG ("counter_list_create (list = %p, key = %u, value = %u)",
                        (void *) *list, key, value);
 
        entry = (counter_list_t *) malloc (sizeof (counter_list_t));
@@ -166,7 +127,7 @@ static counter_list_t *counter_list_create (counter_list_t **list,
                last->next = entry;
        }
 
-       DBG ("return (%p)", (void *) entry);
+       DEBUG ("return (%p)", (void *) entry);
        return (entry);
 }
 
@@ -175,7 +136,7 @@ static void counter_list_add (counter_list_t **list,
 {
        counter_list_t *entry;
 
-       DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
+       DEBUG ("counter_list_add (list = %p, key = %u, increment = %u)",
                        (void *) *list, key, increment);
 
        entry = counter_list_search (list, key);
@@ -188,10 +149,10 @@ static void counter_list_add (counter_list_t **list,
        {
                counter_list_create (list, key, increment);
        }
-       DBG ("return ()");
+       DEBUG ("return ()");
 }
 
-static int dns_config (char *key, char *value)
+static int dns_config (const char *key, const char *value)
 {
        if (strcasecmp (key, "Interface") == 0)
        {
@@ -260,15 +221,15 @@ static void *dns_child_loop (void *dummy)
        }
 
        /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
-       DBG ("Creating PCAP object..");
+       DEBUG ("Creating PCAP object..");
        pcap_obj = pcap_open_live (pcap_device,
                        PCAP_SNAPLEN,
                        0 /* Not promiscuous */,
-                       atoi (COLLECTD_STEP),
+                       interval_g,
                        pcap_error);
        if (pcap_obj == NULL)
        {
-               syslog (LOG_ERR, "dns plugin: Opening interface `%s' "
+               ERROR ("dns plugin: Opening interface `%s' "
                                "failed: %s",
                                (pcap_device != NULL) ? pcap_device : "any",
                                pcap_error);
@@ -278,18 +239,18 @@ static void *dns_child_loop (void *dummy)
        memset (&fp, 0, sizeof (fp));
        if (pcap_compile (pcap_obj, &fp, "udp port 53", 1, 0) < 0)
        {
-               DBG ("pcap_compile failed");
-               syslog (LOG_ERR, "dns plugin: pcap_compile failed");
+               DEBUG ("pcap_compile failed");
+               ERROR ("dns plugin: pcap_compile failed");
                return (NULL);
        }
        if (pcap_setfilter (pcap_obj, &fp) < 0)
        {
-               DBG ("pcap_setfilter failed");
-               syslog (LOG_ERR, "dns plugin: pcap_setfilter failed");
+               DEBUG ("pcap_setfilter failed");
+               ERROR ("dns plugin: pcap_setfilter failed");
                return (NULL);
        }
 
-       DBG ("PCAP object created.");
+       DEBUG ("PCAP object created.");
 
        dnstop_set_pcap_obj (pcap_obj);
        dnstop_set_callback (dns_child_callback);
@@ -299,10 +260,10 @@ static void *dns_child_loop (void *dummy)
                        handle_pcap /* callback */,
                        NULL /* Whatever this means.. */);
        if (status < 0)
-               syslog (LOG_ERR, "dns plugin: Listener thread is exiting "
+               ERROR ("dns plugin: Listener thread is exiting "
                                "abnormally: %s", pcap_geterr (pcap_obj));
 
-       DBG ("child is exiting");
+       DEBUG ("child is exiting");
 
        pcap_close (pcap_obj);
        listen_thread_init = 0;
@@ -310,11 +271,9 @@ static void *dns_child_loop (void *dummy)
 
        return (NULL);
 } /* static void dns_child_loop (void) */
-#endif /* DNS_HAVE_READ */
 
-static void dns_init (void)
+static int dns_init (void)
 {
-#if DNS_HAVE_READ
        /* clean up an old thread */
        int status;
 
@@ -324,131 +283,59 @@ static void dns_init (void)
        pthread_mutex_unlock (&traffic_mutex);
 
        if (listen_thread_init != 0)
-               return;
+               return (-1);
 
        status = pthread_create (&listen_thread, NULL, dns_child_loop,
                        (void *) 0);
        if (status != 0)
        {
-               syslog (LOG_ERR, "dns plugin: pthread_create failed: %s",
-                               strerror (status));
-               return;
+               char errbuf[1024];
+               ERROR ("dns plugin: pthread_create failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
        listen_thread_init = 1;
-#endif /* DNS_HAVE_READ */
-}
-
-static void traffic_write (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, traffic_file, val,
-                       traffic_ds_def, traffic_ds_num);
-}
-
-static void qtype_write (char *host, char *inst, char *val)
-{
-       char file[512];
-       int status;
-
-       status = snprintf (file, 512, qtype_file, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
-}
 
-static void rcode_write (char *host, char *inst, char *val)
-{
-       char file[512];
-       int status;
-
-       status = snprintf (file, 512, rcode_file, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, rcode_ds_def, rcode_ds_num);
-}
-
-static void opcode_write (char *host, char *inst, char *val)
-{
-       char file[512];
-       int status;
-
-       status = snprintf (file, 512, opcode_file, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, opcode_ds_def, opcode_ds_num);
-}
-
-#if DNS_HAVE_READ
-static void traffic_submit (unsigned int queries, unsigned int replies)
-{
-       char buffer[64];
-       int  status;
-
-       status = snprintf (buffer, 64, "N:%u:%u", queries, replies);
-       if ((status < 1) || (status >= 64))
-               return;
-
-       plugin_submit ("dns_traffic", "-", buffer);
-}
-
-static void qtype_submit (int qtype, unsigned int counter)
-{
-       char inst[32];
-       char buffer[32];
-       int  status;
-
-       strncpy (inst, qtype_str (qtype), 32);
-       inst[31] = '\0';
-
-       status = snprintf (buffer, 32, "N:%u", counter);
-       if ((status < 1) || (status >= 32))
-               return;
-
-       plugin_submit ("dns_qtype", inst, buffer);
-}
+       return (0);
+} /* int dns_init */
 
-static void rcode_submit (int rcode, unsigned int counter)
+static void submit_counter (const char *type, const char *type_instance,
+               counter_t value)
 {
-       char inst[32];
-       char buffer[32];
-       int  status;
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       strncpy (inst, rcode_str (rcode), 32);
-       inst[31] = '\0';
+       values[0].counter = value;
 
-       status = snprintf (buffer, 32, "N:%u", counter);
-       if ((status < 1) || (status >= 32))
-               return;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "dns");
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-       plugin_submit ("dns_rcode", inst, buffer);
-}
+       plugin_dispatch_values (type, &vl);
+} /* void submit_counter */
 
-static void opcode_submit (int opcode, unsigned int counter)
+static void submit_octets (counter_t queries, counter_t responses)
 {
-       char inst[32];
-       char buffer[32];
-       int  status;
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       strncpy (inst, opcode_str (opcode), 32);
-       inst[31] = '\0';
+       values[0].counter = queries;
+       values[1].counter = responses;
 
-       status = snprintf (buffer, 32, "N:%u", counter);
-       if ((status < 1) || (status >= 32))
-               return;
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "dns");
 
-       plugin_submit ("dns_opcode", inst, buffer);
-}
+       plugin_dispatch_values ("dns_octets", &vl);
+} /* void submit_counter */
 
-static void dns_read (void)
+static int dns_read (void)
 {
        unsigned int keys[T_MAX];
        unsigned int values[T_MAX];
@@ -461,7 +348,9 @@ static void dns_read (void)
        values[0] = tr_queries;
        values[1] = tr_responses;
        pthread_mutex_unlock (&traffic_mutex);
-       traffic_submit (values[0], values[1]);
+
+       if ((values[0] != 0) || (values[1] != 0))
+               submit_octets (values[0], values[1]);
 
        pthread_mutex_lock (&qtype_mutex);
        for (ptr = qtype_list, len = 0;
@@ -475,8 +364,8 @@ static void dns_read (void)
 
        for (i = 0; i < len; i++)
        {
-               DBG ("qtype = %u; counter = %u;", keys[i], values[i]);
-               qtype_submit (keys[i], values[i]);
+               DEBUG ("qtype = %u; counter = %u;", keys[i], values[i]);
+               submit_counter ("dns_qtype", qtype_str (keys[i]), values[i]);
        }
 
        pthread_mutex_lock (&opcode_mutex);
@@ -491,8 +380,8 @@ static void dns_read (void)
 
        for (i = 0; i < len; i++)
        {
-               DBG ("opcode = %u; counter = %u;", keys[i], values[i]);
-               opcode_submit (keys[i], values[i]);
+               DEBUG ("opcode = %u; counter = %u;", keys[i], values[i]);
+               submit_counter ("dns_opcode", opcode_str (keys[i]), values[i]);
        }
 
        pthread_mutex_lock (&rcode_mutex);
@@ -507,24 +396,19 @@ static void dns_read (void)
 
        for (i = 0; i < len; i++)
        {
-               DBG ("rcode = %u; counter = %u;", keys[i], values[i]);
-               rcode_submit (keys[i], values[i]);
+               DEBUG ("rcode = %u; counter = %u;", keys[i], values[i]);
+               submit_counter ("dns_rcode", rcode_str (keys[i]), values[i]);
        }
-}
-#else /* if !DNS_HAVE_READ */
-# define dns_read NULL
+
+       return (0);
+} /* int dns_read */
 #endif
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, dns_init, dns_read, NULL);
-       plugin_register ("dns_traffic", NULL, NULL, traffic_write);
-       plugin_register ("dns_qtype", NULL, NULL, qtype_write);
-       plugin_register ("dns_rcode", NULL, NULL, rcode_write);
-       plugin_register ("dns_opcode", NULL, NULL, opcode_write);
 #if DNS_HAVE_READ
-       cf_register (MODULE_NAME, dns_config, config_keys, config_keys_num);
+       plugin_register_config ("dns", dns_config, config_keys, config_keys_num);
+       plugin_register_init ("dns", dns_init);
+       plugin_register_read ("dns", dns_read);
 #endif
-}
-
-#undef MODULE_NAME
+} /* void module_register */
index bbee9bb..594ef96 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/email.c
- * Copyright (C) 2006  Sebastian Harl
+ * Copyright (C) 2006,2007  Sebastian Harl
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 
 #if HAVE_LIBPTHREAD
 # include <pthread.h>
-# define EMAIL_HAVE_READ 1
-#else
-# define EMAIL_HAVE_READ 0
 #endif
 
-#if HAVE_SYS_SELECT_H
-#      include <sys/select.h>
-#endif /* HAVE_SYS_SELECT_H */
-
-#if HAVE_SYS_SOCKET_H
-#      include <sys/socket.h>
-#endif /* HAVE_SYS_SOCKET_H */
-
-/* *sigh* glibc does not define UNIX_PATH_MAX in sys/un.h ... */
-#if HAVE_LINUX_UN_H
-#      include <linux/un.h>
-#elif HAVE_SYS_UN_H
-#      include <sys/un.h>
-#endif /* HAVE_LINUX_UN_H | HAVE_SYS_UN_H */
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/select.h>
 
 /* some systems (e.g. Darwin) seem to not define UNIX_PATH_MAX at all */
 #ifndef UNIX_PATH_MAX
 #define MAX_CONNS 5
 #define MAX_CONNS_LIMIT 16384
 
-#define log_err(...) syslog (LOG_ERR, MODULE_NAME": "__VA_ARGS__)
-#define log_warn(...) syslog (LOG_WARNING, MODULE_NAME": "__VA_ARGS__)
+#define log_err(...) ERROR (MODULE_NAME": "__VA_ARGS__)
+#define log_warn(...) WARNING (MODULE_NAME": "__VA_ARGS__)
 
 /*
  * Private data structures
  */
-#if EMAIL_HAVE_READ
 /* linked list of email and check types */
 typedef struct type {
        char        *name;
@@ -128,21 +112,18 @@ typedef struct {
        conn_t *head;
        conn_t *tail;
 } conn_list_t;
-#endif /* EMAIL_HAVE_READ */
 
 /*
  * Private variables
  */
-#if EMAIL_HAVE_READ
 /* valid configuration file keys */
-static char *config_keys[] =
+static const char *config_keys[] =
 {
        "SocketGroup",
        "SocketPerms",
-       "MaxConns",
-       NULL
+       "MaxConns"
 };
-static int config_keys_num = 3;
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
 /* socket configuration */
 static char *sock_group = COLLECTD_GRP_NAME;
@@ -153,8 +134,8 @@ static int  max_conns   = MAX_CONNS;
 static int disabled = 0;
 
 /* thread managing "client" connections */
-static pthread_t connector;
-static int connector_socket;
+static pthread_t connector = (pthread_t) 0;
+static int connector_socket = -1;
 
 /* tell the collector threads that a new connection is available */
 static pthread_cond_t conn_available = PTHREAD_COND_INITIALIZER;
@@ -167,7 +148,7 @@ static conn_list_t conns;
 static pthread_cond_t collector_available = PTHREAD_COND_INITIALIZER;
 
 /* collector threads */
-static collector_t **collectors;
+static collector_t **collectors = NULL;
 
 static pthread_mutex_t available_mutex = PTHREAD_MUTEX_INITIALIZER;
 static int available_collectors;
@@ -184,42 +165,11 @@ static int score_count;
 
 static pthread_mutex_t check_mutex = PTHREAD_MUTEX_INITIALIZER;
 static type_list_t check;
-#endif /* EMAIL_HAVE_READ */
-
-#define COUNT_FILE "email/email-%s.rrd"
-static char *count_ds_def[] =
-{
-       "DS:count:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int count_ds_num = 1;
-
-#define SIZE_FILE  "email/email_size-%s.rrd"
-static char *size_ds_def[] =
-{
-       "DS:size:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int size_ds_num = 1;
-
-#define SCORE_FILE "email/spam_score.rrd"
-static char *score_ds_def[] =
-{
-       "DS:score:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int score_ds_num = 1;
-
-#define CHECK_FILE "email/spam_check-%s.rrd"
-static char *check_ds_def[] =
-{
-       "DS:hits:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int check_ds_num = 1;
 
-#if EMAIL_HAVE_READ
-static int email_config (char *key, char *value)
+/*
+ * Private functions
+ */
+static int email_config (const char *key, const char *value)
 {
        if (0 == strcasecmp (key, "SocketGroup")) {
                sock_group = sstrdup (value);
@@ -300,7 +250,9 @@ static char read_char (conn_t *src)
        FD_SET (src->socket, &fdset);
 
        if (-1 == select (src->socket + 1, &fdset, NULL, NULL, NULL)) {
-               log_err ("select() failed: %s", strerror (errno));
+               char errbuf[1024];
+               log_err ("select() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                return '\0';
        }
 
@@ -312,7 +264,9 @@ static char read_char (conn_t *src)
                errno = 0;
                if (0 > (len = read (src->socket, (void *)&ret, 1))) {
                        if (EINTR != errno) {
-                               log_err ("read() failed: %s", strerror (errno));
+                               char errbuf[1024];
+                               log_err ("read() failed: %s",
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
                                return '\0';
                        }
                }
@@ -361,7 +315,9 @@ static char *read_line (conn_t *src)
                FD_SET (src->socket, &fdset);
 
                if (-1 == select (src->socket + 1, &fdset, NULL, NULL, NULL)) {
-                       log_err ("select() failed: %s", strerror (errno));
+                       char errbuf[1024];
+                       log_err ("select() failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                        return NULL;
                }
 
@@ -373,7 +329,9 @@ static char *read_line (conn_t *src)
                                                        (void *)(src->buffer + src->idx),
                                                        BUFSIZE - src->idx))) {
                                if (EINTR != errno) {
-                                       log_err ("read() failed: %s", strerror (errno));
+                                       char errbuf[1024];
+                                       log_err ("read() failed: %s",
+                                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                                        return NULL;
                                }
                        }
@@ -445,13 +403,17 @@ static void *collect (void *arg)
 
                        errno = 0;
                        if (-1 == fcntl (connection->socket, F_GETFL, &flags)) {
-                               log_err ("fcntl() failed: %s", strerror (errno));
+                               char errbuf[1024];
+                               log_err ("fcntl() failed: %s",
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
                                loop = 0;
                        }
 
                        errno = 0;
                        if (-1 == fcntl (connection->socket, F_SETFL, flags | O_NONBLOCK)) {
-                               log_err ("fcntl() failed: %s", strerror (errno));
+                               char errbuf[1024];
+                               log_err ("fcntl() failed: %s",
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
                                loop = 0;
                        }
                }
@@ -515,9 +477,10 @@ static void *collect (void *arg)
                } /* while (loop) */
 
                close (connection->socket);
-
                free (connection);
 
+               this->socket = -1;
+
                pthread_mutex_lock (&available_mutex);
                ++available_collectors;
                pthread_mutex_unlock (&available_mutex);
@@ -536,8 +499,10 @@ static void *open_connection (void *arg)
        /* create UNIX socket */
        errno = 0;
        if (-1 == (connector_socket = socket (PF_UNIX, SOCK_STREAM, 0))) {
+               char errbuf[1024];
                disabled = 1;
-               log_err ("socket() failed: %s", strerror (errno));
+               log_err ("socket() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                pthread_exit ((void *)1);
        }
 
@@ -551,39 +516,65 @@ static void *open_connection (void *arg)
        if (-1 == bind (connector_socket, (struct sockaddr *)&addr,
                                offsetof (struct sockaddr_un, sun_path)
                                        + strlen(addr.sun_path))) {
+               char errbuf[1024];
                disabled = 1;
-               log_err ("bind() failed: %s", strerror (errno));
+               connector_socket = -1; /* TODO: close? */
+               log_err ("bind() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                pthread_exit ((void *)1);
        }
 
        errno = 0;
        if (-1 == listen (connector_socket, 5)) {
+               char errbuf[1024];
                disabled = 1;
-               log_err ("listen() failed: %s", strerror (errno));
+               connector_socket = -1; /* TODO: close? */
+               log_err ("listen() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                pthread_exit ((void *)1);
        }
 
-       if ((uid_t)0 == geteuid ()) {
+       if ((uid_t) 0 == geteuid ())
+       {
+               struct group sg;
                struct group *grp;
-
-               errno = 0;
-               if (NULL != (grp = getgrnam (sock_group))) {
-                       errno = 0;
-                       if (0 != chown (SOCK_PATH, (uid_t)-1, grp->gr_gid)) {
-                               log_warn ("chown() failed: %s", strerror (errno));
-                       }
+               char grbuf[2048];
+               int status;
+
+               grp = NULL;
+               status = getgrnam_r (sock_group, &sg, grbuf, sizeof (grbuf), &grp);
+               if (status != 0)
+               {
+                       char errbuf[1024];
+                       log_warn ("getgrnam_r (%s) failed: %s", sock_group,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                }
-               else {
-                       log_warn ("getgrnam() failed: %s", strerror (errno));
+               else if (grp == NULL)
+               {
+                       log_warn ("No such group: `%s'", sock_group);
+               }
+               else
+               {
+                       status = chown (SOCK_PATH, (uid_t) -1, grp->gr_gid);
+                       if (status != 0)
+                       {
+                               char errbuf[1024];
+                               log_warn ("chown (%s, -1, %i) failed: %s",
+                                               SOCK_PATH, (int) grp->gr_gid,
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                       }
                }
        }
-       else {
+       else /* geteuid != 0 */
+       {
                log_warn ("not running as root");
        }
 
        errno = 0;
        if (0 != chmod (SOCK_PATH, sock_perms)) {
-               log_warn ("chmod() failed: %s", strerror (errno));
+               char errbuf[1024];
+               log_warn ("chmod() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
        }
 
        { /* initialize collector threads */
@@ -605,11 +596,14 @@ static void *open_connection (void *arg)
 
                for (i = 0; i < max_conns; ++i) {
                        collectors[i] = (collector_t *)smalloc (sizeof (collector_t));
-                       collectors[i]->socket = 0;
+                       collectors[i]->socket = -1;
 
                        if (0 != (err = pthread_create (&collectors[i]->thread, &ptattr,
                                                        collect, collectors[i]))) {
-                               log_err ("pthread_create() failed: %s", strerror (err));
+                               char errbuf[1024];
+                               log_err ("pthread_create() failed: %s",
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
+                               collectors[i]->thread = (pthread_t) 0;
                        }
                }
 
@@ -635,8 +629,11 @@ static void *open_connection (void *arg)
                        errno = 0;
                        if (-1 == (remote = accept (connector_socket, NULL, NULL))) {
                                if (EINTR != errno) {
+                                       char errbuf[1024];
                                        disabled = 1;
-                                       log_err ("accept() failed: %s", strerror (errno));
+                                       connector_socket = -1; /* TODO: close? */
+                                       log_err ("accept() failed: %s",
+                                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                                        pthread_exit ((void *)1);
                                }
                        }
@@ -664,120 +661,81 @@ static void *open_connection (void *arg)
        }
        pthread_exit ((void *)0);
 } /* static void *open_connection (void *) */
-#endif /* EMAIL_HAVE_READ */
 
-static void email_init (void)
+static int email_init (void)
 {
-#if EMAIL_HAVE_READ
        int err = 0;
 
        if (0 != (err = pthread_create (&connector, NULL,
                                open_connection, NULL))) {
+               char errbuf[1024];
                disabled = 1;
-               log_err ("pthread_create() failed: %s", strerror (err));
-               return;
+               log_err ("pthread_create() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
-#endif /* EMAIL_HAVE_READ */
-       return;
-} /* static void email_init (void) */
 
-#if EMAIL_HAVE_READ
-static void email_shutdown (void)
+       return (0);
+} /* int email_init */
+
+static int email_shutdown (void)
 {
        int i = 0;
 
-       if (disabled)
-               return;
+       if (connector != ((pthread_t) 0)) {
+               pthread_kill (connector, SIGTERM);
+               connector = (pthread_t) 0;
+       }
 
-       pthread_kill (connector, SIGTERM);
-       close (connector_socket);
+       if (connector_socket >= 0) {
+               close (connector_socket);
+               connector_socket = -1;
+       }
 
        /* don't allow any more connections to be processed */
        pthread_mutex_lock (&conns_mutex);
 
-       for (i = 0; i < max_conns; ++i) {
-               pthread_kill (collectors[i]->thread, SIGTERM);
-               close (collectors[i]->socket);
-       }
-
-       pthread_mutex_unlock (&conns_mutex);
-
-       unlink (SOCK_PATH);
-       return;
-} /* static void email_shutdown (void) */
-#endif /* EMAIL_HAVE_READ */
-
-static void count_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE] = "";
-       int  len           = 0;
-
-       len = snprintf (file, BUFSIZE, COUNT_FILE, inst);
-       if ((len < 0) || (len >= BUFSIZE))
-               return;
-
-       rrd_update_file (host, file, val, count_ds_def, count_ds_num);
-       return;
-} /* static void email_write (char *host, char *inst, char *val) */
-
-static void size_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE] = "";
-       int  len           = 0;
-
-       len = snprintf (file, BUFSIZE, SIZE_FILE, inst);
-       if ((len < 0) || (len >= BUFSIZE))
-               return;
+       if (collectors != NULL) {
+               for (i = 0; i < max_conns; ++i) {
+                       if (collectors[i] == NULL)
+                               continue;
 
-       rrd_update_file (host, file, val, size_ds_def, size_ds_num);
-       return;
-} /* static void size_write (char *host, char *inst, char *val) */
+                       if (collectors[i]->thread != ((pthread_t) 0)) {
+                               pthread_kill (collectors[i]->thread, SIGTERM);
+                               collectors[i]->thread = (pthread_t) 0;
+                       }
 
-static void score_write (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, SCORE_FILE, val, score_ds_def, score_ds_num);
-       return;
-} /* static void score_write (char *host, char *inst, char *val) */
+                       if (collectors[i]->socket >= 0) {
+                               close (collectors[i]->socket);
+                               collectors[i]->socket = -1;
+                       }
+               }
+       } /* if (collectors != NULL) */
 
-static void check_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE] = "";
-       int  len           = 0;
+       pthread_mutex_unlock (&conns_mutex);
 
-       len = snprintf (file, BUFSIZE, CHECK_FILE, inst);
-       if ((len < 0) || (len >= BUFSIZE))
-               return;
+       unlink (SOCK_PATH);
+       errno = 0;
 
-       rrd_update_file (host, file, val, check_ds_def, check_ds_num);
-       return;
-} /* static void check_write (char *host, char *inst, char *val) */
+       return (0);
+} /* static void email_shutdown (void) */
 
-#if EMAIL_HAVE_READ
-static void type_submit (char *plugin, char *inst, int value)
+static void email_submit (const char *type, const char *type_instance, gauge_t value)
 {
-       char buf[BUFSIZE] = "";
-       int  len          = 0;
-
-       len = snprintf (buf, BUFSIZE, "%u:%i", (unsigned int)curtime, value);
-       if ((len < 0) || (len >= BUFSIZE))
-               return;
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       plugin_submit (plugin, inst, buf);
-       return;
-} /* static void type_submit (char *, char *, int) */
+       values[0].gauge = value;
 
-static void score_submit (double value)
-{
-       char buf[BUFSIZE] = "";
-       int  len          = 0;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "email");
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-       len = snprintf (buf, BUFSIZE, "%u:%.2f", (unsigned int)curtime, value);
-       if ((len < 0) || (len >= BUFSIZE))
-               return;
-
-       plugin_submit ("email_spam_score", "-", buf);
-       return;
-} /* static void score_submit (double) */
+       plugin_dispatch_values (type, &vl);
+} /* void email_submit */
 
 /* Copy list l1 to list l2. l2 may partly exist already, but it is assumed
  * that neither the order nor the name of any element of either list is
@@ -817,18 +775,19 @@ static void copy_type_list (type_list_t *l1, type_list_t *l2)
        return;
 }
 
-static void email_read (void)
+static int email_read (void)
 {
        type_t *ptr;
 
-       double sc;
+       double score_old;
+       int score_count_old;
 
        static type_list_t *cnt;
        static type_list_t *sz;
        static type_list_t *chk;
 
        if (disabled)
-               return;
+               return (-1);
 
        if (NULL == cnt) {
                cnt = (type_list_t *)smalloc (sizeof (type_list_t));
@@ -853,7 +812,7 @@ static void email_read (void)
        pthread_mutex_unlock (&count_mutex);
 
        for (ptr = cnt->head; NULL != ptr; ptr = ptr->next) {
-               type_submit ("email_count", ptr->name, ptr->value);
+               email_submit ("email_count", ptr->name, ptr->value);
        }
 
        /* email size */
@@ -864,19 +823,21 @@ static void email_read (void)
        pthread_mutex_unlock (&size_mutex);
 
        for (ptr = sz->head; NULL != ptr; ptr = ptr->next) {
-               type_submit ("email_size", ptr->name, ptr->value);
+               email_submit ("email_size", ptr->name, ptr->value);
        }
 
        /* spam score */
        pthread_mutex_lock (&score_mutex);
 
-       sc = score;
+       score_old = score;
+       score_count_old = score_count;
        score = 0.0;
        score_count = 0;
 
        pthread_mutex_unlock (&score_mutex);
 
-       score_submit (sc);
+       if (score_count_old > 0)
+               email_submit ("spam_score", "", score_old);
 
        /* spam checks */
        pthread_mutex_lock (&check_mutex);
@@ -885,28 +846,18 @@ static void email_read (void)
 
        pthread_mutex_unlock (&check_mutex);
 
-       for (ptr = chk->head; NULL != ptr; ptr = ptr->next) {
-               type_submit ("email_spam_check", ptr->name, ptr->value);
-       }
-       return;
-} /* static void read (void) */
-#else /* if !EMAIL_HAVE_READ */
-# define email_read NULL
-#endif
+       for (ptr = chk->head; NULL != ptr; ptr = ptr->next)
+               email_submit ("spam_check", ptr->name, ptr->value);
+
+       return (0);
+} /* int email_read */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, email_init, email_read, NULL);
-       plugin_register ("email_count", NULL, NULL, count_write);
-       plugin_register ("email_size", NULL, NULL, size_write);
-       plugin_register ("email_spam_score", NULL, NULL, score_write);
-       plugin_register ("email_spam_check", NULL, NULL, check_write);
-#if EMAIL_HAVE_READ
-       plugin_register_shutdown (MODULE_NAME, email_shutdown);
-       cf_register (MODULE_NAME, email_config, config_keys, config_keys_num);
-#endif /* EMAIL_HAVE_READ */
-       return;
-} /* void module_register (void) */
+       plugin_register_config ("email", email_config, config_keys, config_keys_num);
+       plugin_register_init ("email", email_init);
+       plugin_register_read ("email", email_read);
+       plugin_register_shutdown ("email", email_shutdown);
+} /* void module_register */
 
 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
-
diff --git a/src/entropy.c b/src/entropy.c
new file mode 100644 (file)
index 0000000..7e90395
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ * collectd - src/entropy.c
+ * Copyright (C) 2005,2006  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if KERNEL_LINUX
+# define ENTROPY_HAVE_READ 1
+#else
+# define ENTROPY_HAVE_READ 0
+#endif
+
+#define ENTROPY_FILE "/proc/sys/kernel/random/entropy_avail"
+
+#if ENTROPY_HAVE_READ
+static void entropy_submit (double entropy)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = entropy;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "entropy");
+       strcpy (vl.plugin_instance, "");
+       strcpy (vl.type_instance, "");
+
+       plugin_dispatch_values ("entropy", &vl);
+}
+
+static int entropy_read (void)
+{
+#if KERNEL_LINUX
+       double entropy;
+       FILE *fh;
+       char buffer[64];
+
+       fh = fopen (ENTROPY_FILE, "r");
+       if (fh == NULL)
+               return (-1);
+
+       if (fgets (buffer, sizeof (buffer), fh) == NULL)
+       {
+               fclose (fh);
+               return (-1);
+       }
+       fclose (fh);
+
+       entropy = atof (buffer);
+       
+       if (entropy > 0.0)
+               entropy_submit (entropy);
+#endif /* KERNEL_LINUX */
+
+       return (0);
+}
+#endif /* ENTROPY_HAVE_READ */
+
+void module_register (void)
+{
+#if ENTROPY_HAVE_READ
+       plugin_register_read ("entropy", entropy_read);
+#endif
+} /* void module_register */
diff --git a/src/exec.c b/src/exec.c
new file mode 100644 (file)
index 0000000..bd96e07
--- /dev/null
@@ -0,0 +1,386 @@
+/**
+ * collectd - src/exec.c
+ * Copyright (C) 2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <signal.h>
+
+#include <pthread.h>
+
+/*
+ * Private data types
+ */
+struct program_list_s;
+typedef struct program_list_s program_list_t;
+struct program_list_s
+{
+  char           *user;
+  char           *exec;
+  int             pid;
+  program_list_t *next;
+};
+
+/*
+ * Private variables
+ */
+static const char *config_keys[] =
+{
+  "Exec"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static program_list_t *pl_head = NULL;
+
+/*
+ * Functions
+ */
+static int exec_config (const char *key, const char *value)
+{
+  if (strcasecmp ("Exec", key) == 0)
+  {
+    program_list_t *pl;
+    pl = (program_list_t *) malloc (sizeof (program_list_t));
+    if (pl == NULL)
+      return (1);
+    memset (pl, '\0', sizeof (program_list_t));
+
+    pl->user = strdup (value);
+    if (pl->user == NULL)
+    {
+      sfree (pl);
+      return (1);
+    }
+
+    pl->exec = strchr (pl->user, ' ');
+    if (pl->exec == NULL)
+    {
+      sfree (pl->user);
+      sfree (pl);
+      return (1);
+    }
+    while (*pl->exec == ' ')
+    {
+      *pl->exec = '\0';
+      pl->exec++;
+    }
+
+    if (*pl->exec == '\0')
+    {
+      sfree (pl->user);
+      sfree (pl);
+      return (1);
+    }
+
+    pl->next = pl_head;
+    pl_head = pl;
+  }
+  else
+  {
+    return (-1);
+  }
+
+  return (0);
+} /* int exec_config */
+
+static void submit_counter (const char *type_instance, counter_t value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  DEBUG ("type_instance = %s; value = %llu;", type_instance, value);
+
+  values[0].counter = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  vl.time = time (NULL);
+  strcpy (vl.host, hostname_g);
+  strcpy (vl.plugin, "exec");
+  strcpy (vl.plugin_instance, "");
+  strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+  plugin_dispatch_values ("counter", &vl);
+} /* void submit_counter */
+
+static void submit_gauge (const char *type_instance, gauge_t value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  DEBUG ("type_instance = %s; value = %lf;", type_instance, value);
+
+  values[0].gauge = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  vl.time = time (NULL);
+  strcpy (vl.host, hostname_g);
+  strcpy (vl.plugin, "exec");
+  strcpy (vl.plugin_instance, "");
+  strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+  plugin_dispatch_values ("gauge", &vl);
+} /* void submit_counter */
+
+static void exec_child (program_list_t *pl)
+{
+  int status;
+  int uid;
+  char *arg0;
+
+  struct passwd *sp_ptr;
+  struct passwd sp;
+  char pwnambuf[2048];
+  char errbuf[1024];
+
+  sp_ptr = NULL;
+  status = getpwnam_r (pl->user, &sp, pwnambuf, sizeof (pwnambuf), &sp_ptr);
+  if (status != 0)
+  {
+    ERROR ("exec plugin: getpwnam_r failed: %s",
+       sstrerror (errno, errbuf, sizeof (errbuf)));
+    exit (-1);
+  }
+  if (sp_ptr == NULL)
+  {
+    ERROR ("exec plugin: No such user: `%s'", pl->user);
+    exit (-1);
+  }
+
+  uid = sp.pw_uid;
+  if (uid == 0)
+  {
+    ERROR ("exec plugin: Cowardly refusing to exec program as root.");
+    exit (-1);
+  }
+
+  status = setuid (uid);
+  if (status != 0)
+  {
+    ERROR ("exec plugin: setuid failed: %s",
+       sstrerror (errno, errbuf, sizeof (errbuf)));
+    exit (-1);
+  }
+
+  arg0 = strrchr (pl->exec, '/');
+  if (arg0 != NULL)
+    arg0++;
+  if ((arg0 == NULL) || (*arg0 == '\0'))
+    arg0 = pl->exec;
+
+  status = execlp (pl->exec, arg0, (char *) 0);
+
+  ERROR ("exec plugin: exec failed: %s",
+      sstrerror (errno, errbuf, sizeof (errbuf)));
+  exit (-1);
+} /* void exec_child */
+
+static int fork_child (program_list_t *pl)
+{
+  int fd_pipe[2];
+  int status;
+
+  if (pl->pid != 0)
+    return (-1);
+
+  status = pipe (fd_pipe);
+  if (status != 0)
+  {
+    char errbuf[1024];
+    ERROR ("exec plugin: pipe failed: %s",
+       sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  pl->pid = fork ();
+  if (pl->pid < 0)
+  {
+    char errbuf[1024];
+    ERROR ("exec plugin: fork failed: %s",
+       sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+  else if (pl->pid == 0)
+  {
+    close (fd_pipe[0]);
+
+    /* Connect the pipe to STDOUT and STDERR */
+    if (fd_pipe[1] != STDOUT_FILENO)
+      dup2 (fd_pipe[1], STDOUT_FILENO);
+    if (fd_pipe[1] != STDERR_FILENO)
+      dup2 (fd_pipe[1], STDERR_FILENO);
+    if ((fd_pipe[1] != STDOUT_FILENO) && (fd_pipe[1] != STDERR_FILENO))
+      close (fd_pipe[1]);
+
+    exec_child (pl);
+    /* does not return */
+  }
+
+  close (fd_pipe[1]);
+  return (fd_pipe[0]);
+} /* int fork_child */
+
+static void *exec_read_one (void *arg)
+{
+  program_list_t *pl = (program_list_t *) arg;
+  int fd;
+  FILE *fh;
+  char buffer[1024];
+
+  fd = fork_child (pl);
+  if (fd < 0)
+    pthread_exit ((void *) 1);
+
+  assert (pl->pid != 0);
+
+  fh = fdopen (fd, "r");
+  if (fh == NULL)
+  {
+    char errbuf[1024];
+    ERROR ("exec plugin: fdopen (%i) failed: %s", fd,
+       sstrerror (errno, errbuf, sizeof (errbuf)));
+    kill (pl->pid, SIGTERM);
+    close (fd);
+    pthread_exit ((void *) 1);
+  }
+
+  while (fgets (buffer, sizeof (buffer), fh) != NULL)
+  {
+    int len;
+    char *type;
+    char *type_instance;
+    char *value;
+
+    len = strlen (buffer);
+
+    /* Remove newline from end. */
+    while ((len > 0) && ((buffer[len - 1] == '\n')
+         || (buffer[len - 1] == '\r')))
+      buffer[--len] = '\0';
+
+    DEBUG ("exec plugin: exec_read_one: buffer = %s", buffer);
+
+    if (len < 5)
+      continue;
+
+    if (buffer[0] == '#')
+      continue;
+
+    type = buffer;
+
+    type_instance = strchr (type, ',');
+    if (type_instance == NULL)
+      continue;
+    *type_instance = '\0';
+    type_instance++;
+
+    if ((strcasecmp ("counter", type) != 0)
+       && (strcasecmp ("gauge", type) != 0))
+    {
+      WARNING ("exec plugin: Received invalid type: %s", type);
+      continue;
+    }
+
+    value = strchr (type_instance, ',');
+    if (value == NULL)
+    {
+      WARNING ("exec plugin: type-instance is missing.");
+      continue;
+    }
+    *value = '\0';
+    value++;
+
+    DEBUG ("exec plugin: exec_read_one: type = %s; type_instance = %s; "
+       "value = %s;", type, type_instance, value);
+
+    if (strcasecmp ("counter", type) == 0)
+      submit_counter (type_instance, atoll (value));
+    else
+      submit_gauge (type_instance, atof (value));
+  } /* while (fgets) */
+
+  fclose (fh);
+  pl->pid = 0;
+
+  pthread_exit ((void *) 0);
+  return (NULL);
+} /* void *exec_read_one */
+
+static int exec_read (void)
+{
+  program_list_t *pl;
+
+  for (pl = pl_head; pl != NULL; pl = pl->next)
+  {
+    pthread_t t;
+    pthread_attr_t attr;
+
+    if (pl->pid != 0)
+      continue;
+
+    pthread_attr_init (&attr);
+    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+    pthread_create (&t, &attr, exec_read_one, (void *) pl);
+  } /* for (pl) */
+
+  return (0);
+} /* int exec_read */
+
+static int exec_shutdown (void)
+{
+  program_list_t *pl;
+  program_list_t *next;
+
+  pl = pl_head;
+  while (pl != NULL)
+  {
+    next = pl->next;
+
+    if (pl->pid > 0)
+    {
+      kill (pl->pid, SIGTERM);
+      INFO ("exec plugin: Sent SIGTERM to %hu", (unsigned short int) pl->pid);
+    }
+
+    sfree (pl->user);
+    sfree (pl);
+
+    pl = next;
+  } /* while (pl) */
+  pl_head = NULL;
+
+  return (0);
+} /* int exec_shutdown */
+
+void module_register (void)
+{
+  plugin_register_config ("exec", exec_config, config_keys, config_keys_num);
+  plugin_register_read ("exec", exec_read);
+  plugin_register_shutdown ("exec", exec_shutdown);
+} /* void module_register */
+
+/*
+ * vim:shiftwidth=2:softtabstop=2:tabstop=8
+ */
index 80b3419..00814bd 100644 (file)
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
-#include "utils_debug.h"
 
-#define MODULE_NAME "hddtemp"
-
-#include <netdb.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <libgen.h> /* for basename */
+#if HAVE_NETDB_H && HAVE_SYS_SOCKET_H && HAVE_NETINET_IN_H \
+       && HAVE_NETINET_TCP_H && HAVE_LIBGEN_H
+# include <netdb.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <netinet/tcp.h>
+# include <libgen.h> /* for basename */
+# define HDDTEMP_HAVE_READ 1
+#else
+# define HDDTEMP_HAVE_READ 0
+#endif
 
 #if HAVE_LINUX_MAJOR_H
 # include <linux/major.h>
 #define HDDTEMP_DEF_HOST "127.0.0.1"
 #define HDDTEMP_DEF_PORT "7634"
 
-/* BUFFER_SIZE
-   Size of the buffer we use to receive from the hddtemp daemon. */
-#define BUFFER_SIZE 1024
-
-static char *filename_format = "hddtemp-%s.rrd";
-
-static char *ds_def[] =
-{
-       "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int ds_num = 1;
-
-static char *config_keys[] =
+#if HDDTEMP_HAVE_READ
+static const char *config_keys[] =
 {
        "Host",
        "Port",
@@ -132,9 +123,12 @@ static int hddtemp_query_daemon (char *buffer, int buffer_size)
 
        if ((ai_return = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0)
        {
-               syslog (LOG_ERR, "hddtemp: getaddrinfo (%s, %s): %s",
+               char errbuf[1024];
+               ERROR ("hddtemp: getaddrinfo (%s, %s): %s",
                                host, port,
-                               ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
+                               (ai_return == EAI_SYSTEM)
+                               ? sstrerror (errno, errbuf, sizeof (errbuf))
+                               : gai_strerror (ai_return));
                return (-1);
        }
 
@@ -144,16 +138,18 @@ static int hddtemp_query_daemon (char *buffer, int buffer_size)
                /* create our socket descriptor */
                if ((fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol)) < 0)
                {
-                       syslog (LOG_ERR, "hddtemp: socket: %s",
-                                       strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("hddtemp: socket: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                        continue;
                }
 
                /* connect to the hddtemp daemon */
                if (connect (fd, (struct sockaddr *) ai_ptr->ai_addr, ai_ptr->ai_addrlen))
                {
-                       DBG ("hddtemp: connect (%s, %s): %s", host, port,
-                                       strerror (errno));
+                       char errbuf[1024];
+                       DEBUG ("hddtemp: connect (%s, %s): %s", host, port,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                        close (fd);
                        fd = -1;
                        continue;
@@ -168,7 +164,7 @@ static int hddtemp_query_daemon (char *buffer, int buffer_size)
 
        if (fd < 0)
        {
-               syslog (LOG_ERR, "hddtemp: Could not connect to daemon.");
+               ERROR ("hddtemp: Could not connect to daemon.");
                return (-1);
        }
 
@@ -180,11 +176,13 @@ static int hddtemp_query_daemon (char *buffer, int buffer_size)
        {
                if (status == -1)
                {
+                       char errbuf[1024];
+
                        if ((errno == EAGAIN) || (errno == EINTR))
                                continue;
 
-                       syslog (LOG_ERR, "hddtemp: Error reading from socket: %s",
-                                               strerror (errno));
+                       ERROR ("hddtemp: Error reading from socket: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                        close (fd);
                        return (-1);
                }
@@ -197,11 +195,11 @@ static int hddtemp_query_daemon (char *buffer, int buffer_size)
        if (buffer_fill >= buffer_size)
        {
                buffer[buffer_size - 1] = '\0';
-               syslog (LOG_WARNING, "hddtemp: Message from hddtemp has been truncated.");
+               WARNING ("hddtemp: Message from hddtemp has been truncated.");
        }
        else if (buffer_fill == 0)
        {
-               syslog (LOG_WARNING, "hddtemp: Peer has unexpectedly shut down the socket. "
+               WARNING ("hddtemp: Peer has unexpectedly shut down the socket. "
                                "Buffer: `%s'", buffer);
                close (fd);
                return (-1);
@@ -211,7 +209,7 @@ static int hddtemp_query_daemon (char *buffer, int buffer_size)
        return (0);
 }
 
-static int hddtemp_config (char *key, char *value)
+static int hddtemp_config (const char *key, const char *value)
 {
        if (strcasecmp (key, "host") == 0)
        {
@@ -236,11 +234,11 @@ static int hddtemp_config (char *key, char *value)
 /* In the init-function we initialize the `hddname_t' list used to translate
  * disk-names. Under Linux that's done using `/proc/partitions'. Under other
  * operating-systems, it's not done at all. */
-static void hddtemp_init (void)
+static int hddtemp_init (void)
 {
 #if KERNEL_LINUX
        FILE *fh;
-       char buf[BUFFER_SIZE];
+       char buf[1024];
        int buflen;
 
        char *fields[16];
@@ -265,9 +263,9 @@ static void hddtemp_init (void)
 
        if ((fh = fopen ("/proc/partitions", "r")) != NULL)
        {
-               DBG ("Looking at /proc/partitions...");
+               DEBUG ("Looking at /proc/partitions...");
 
-               while (fgets (buf, BUFFER_SIZE, fh) != NULL)
+               while (fgets (buf, sizeof (buf), fh) != NULL)
                {
                        /* Delete trailing newlines */
                        buflen = strlen (buf);
@@ -339,25 +337,25 @@ static void hddtemp_init (void)
 
                                /* Skip all other majors. */
                                default:
-                                       DBG ("Skipping unknown major %i", major);
+                                       DEBUG ("Skipping unknown major %i", major);
                                        continue;
                        } /* switch (major) */
 
                        if ((name = strdup (fields[3])) == NULL)
                        {
-                               syslog (LOG_ERR, "hddtemp: strdup(%s) == NULL", fields[3]);
+                               ERROR ("hddtemp: strdup(%s) == NULL", fields[3]);
                                continue;
                        }
 
                        if ((entry = (hddname_t *) malloc (sizeof (hddname_t))) == NULL)
                        {
-                               syslog (LOG_ERR, "hddtemp: malloc (%u) == NULL",
+                               ERROR ("hddtemp: malloc (%u) == NULL",
                                                (unsigned int) sizeof (hddname_t));
                                free (name);
                                continue;
                        }
 
-                       DBG ("Found disk: %s (%u:%u).", name, major, minor);
+                       DEBUG ("Found disk: %s (%u:%u).", name, major, minor);
 
                        entry->major = major;
                        entry->minor = minor;
@@ -376,26 +374,18 @@ static void hddtemp_init (void)
                }
                fclose (fh);
        }
+#if COLLECT_DEBUG
        else
-               DBG ("Could not open /proc/partitions: %s",
-                               strerror (errno));
+       {
+               char errbuf[1024];
+               DEBUG ("Could not open /proc/partitions: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
+#endif /* COLLECT_DEBUG */
 #endif /* KERNEL_LINUX */
-}
-
-static void hddtemp_write (char *host, char *inst, char *val)
-{
-       char filename[BUFFER_SIZE];
-       int status;
-
-       /* construct filename */
-       status = snprintf (filename, BUFFER_SIZE, filename_format, inst);
-       if (status < 1)
-               return;
-       else if (status >= BUFFER_SIZE)
-               return;
 
-       rrd_update_file (host, filename, val, ds_def, ds_num);
-}
+       return (0);
+} /* int hddtemp_init */
 
 /*
  * hddtemp_get_name
@@ -416,7 +406,7 @@ static char *hddtemp_get_name (char *drive)
 
        if (list == NULL)
        {
-               DBG ("Don't know %s, keeping name as-is.", drive);
+               DEBUG ("Don't know %s, keeping name as-is.", drive);
                return (strdup (drive));
        }
 
@@ -432,60 +422,42 @@ static char *hddtemp_get_name (char *drive)
        return (ret);
 }
 
-static void hddtemp_submit (char *inst, double temperature)
+static void hddtemp_submit (char *type_instance, double value)
 {
-       char buf[BUFFER_SIZE];
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (buf, BUFFER_SIZE, "%u:%.3f", (unsigned int) curtime, temperature)
-            >= BUFFER_SIZE)
-               return;
+       values[0].gauge = value;
 
-       plugin_submit (MODULE_NAME, inst, buf);
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "hddtemp");
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+       plugin_dispatch_values ("temperature", &vl);
 }
 
-static void hddtemp_read (void)
+static int hddtemp_read (void)
 {
-       char buf[BUFFER_SIZE];
+       char buf[1024];
        char *fields[128];
        char *ptr;
+       char *saveptr;
        int num_fields;
        int num_disks;
        int i;
 
-       static int wait_time = 1;
-       static int wait_left = 0;
-
-       if (wait_left >= 10)
-       {
-               wait_left -= 10;
-               return;
-       }
-
        /* get data from daemon */
-       if (hddtemp_query_daemon (buf, BUFFER_SIZE) < 0)
-       {
-               /* This limit is reached in log2(86400) =~ 17 steps. Since
-                * there is a 2^n seconds wait between each step it will need
-                * roughly one day to reach this limit. -octo */
-               
-               wait_time *= 2;
-               if (wait_time > 86400)
-                       wait_time = 86400;
-
-               wait_left = wait_time;
-
-               return;
-       }
-       else
-       {
-               wait_time = 1;
-               wait_left = 0;
-       }
+       if (hddtemp_query_daemon (buf, sizeof (buf)) < 0)
+               return (-1);
 
-       /* NB: strtok will eat up "||" and leading "|"'s */
+       /* NB: strtok_r will eat up "||" and leading "|"'s */
        num_fields = 0;
        ptr = buf;
-       while ((fields[num_fields] = strtok (ptr, "|")) != NULL)
+       saveptr = NULL;
+       while ((fields[num_fields] = strtok_r (ptr, "|", &saveptr)) != NULL)
        {
                ptr = NULL;
                num_fields++;
@@ -525,14 +497,19 @@ static void hddtemp_read (void)
                        hddtemp_submit (name, temperature);
                }
        }
-}
+       
+       return (0);
+} /* int hddtemp_read */
+#endif /* HDDTEMP_HAVE_READ */
 
 /* module_register
    Register collectd plugin. */
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, hddtemp_init, hddtemp_read, hddtemp_write);
-       cf_register (MODULE_NAME, hddtemp_config, config_keys, config_keys_num);
+#if HDDTEMP_HAVE_READ
+       plugin_register_config ("hddtemp", hddtemp_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("hddtemp", hddtemp_init);
+       plugin_register_read ("hddtemp", hddtemp_read);
+#endif /* HDDTEMP_HAVE_READ */
 }
-
-#undef MODULE_NAME
diff --git a/src/interface.c b/src/interface.c
new file mode 100644 (file)
index 0000000..c439ebd
--- /dev/null
@@ -0,0 +1,368 @@
+/**
+ * collectd - src/interface.c
+ * Copyright (C) 2005-2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ *   Sune Marcher <sm at flork.dk>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#if HAVE_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#  include <sys/socket.h>
+#endif
+
+/* One cannot include both. This sucks. */
+#if HAVE_LINUX_IF_H
+#  include <linux/if.h>
+#elif HAVE_NET_IF_H
+#  include <net/if.h>
+#endif
+
+#if HAVE_LINUX_NETDEVICE_H
+#  include <linux/netdevice.h>
+#endif
+#if HAVE_IFADDRS_H
+#  include <ifaddrs.h>
+#endif
+
+/*
+ * Various people have reported problems with `getifaddrs' and varying versions
+ * of `glibc'. That's why it's disabled by default. Since more statistics are
+ * available this way one may enable it using the `--enable-getifaddrs' option
+ * of the configure script. -octo
+ */
+#if KERNEL_LINUX
+# if !COLLECT_GETIFADDRS
+#  undef HAVE_GETIFADDRS
+# endif /* !COLLECT_GETIFADDRS */
+#endif /* KERNEL_LINUX */
+
+#if HAVE_GETIFADDRS || KERNEL_LINUX || HAVE_LIBKSTAT || HAVE_LIBSTATGRAB
+# define INTERFACE_HAVE_READ 1
+#else
+# define INTERFACE_HAVE_READ 0
+#endif
+
+/*
+ * (Module-)Global variables
+ */
+static const char *config_keys[] =
+{
+       "Interface",
+       "IgnoreSelected",
+       NULL
+};
+static int config_keys_num = 2;
+
+static char **if_list = NULL;
+static int    if_list_num = 0;
+/* 
+ * if_list_action:
+ * 0 => default is to collect selected interface
+ * 1 => ignore selcted interfaces
+ */
+static int    if_list_action = 0;
+
+#ifdef HAVE_LIBKSTAT
+#define MAX_NUMIF 256
+extern kstat_ctl_t *kc;
+static kstat_t *ksp[MAX_NUMIF];
+static int numif = 0;
+#endif /* HAVE_LIBKSTAT */
+
+static int interface_config (const char *key, const char *value)
+{
+       char **temp;
+
+       if (strcasecmp (key, "Interface") == 0)
+       {
+               temp = (char **) realloc (if_list, (if_list_num + 1) * sizeof (char *));
+               if (temp == NULL)
+               {
+                       ERROR ("Cannot allocate more memory.");
+                       return (1);
+               }
+               if_list = temp;
+
+               if ((if_list[if_list_num] = strdup (value)) == NULL)
+               {
+                       ERROR ("Cannot allocate memory.");
+                       return (1);
+               }
+               if_list_num++;
+       }
+       else if (strcasecmp (key, "IgnoreSelected") == 0)
+       {
+               if ((strcasecmp (value, "True") == 0)
+                               || (strcasecmp (value, "Yes") == 0)
+                               || (strcasecmp (value, "On") == 0))
+                       if_list_action = 1;
+               else
+                       if_list_action = 0;
+       }
+       else
+       {
+               return (-1);
+       }
+
+       return (0);
+}
+
+#if HAVE_LIBKSTAT
+static int interface_init (void)
+{
+#if HAVE_LIBKSTAT
+       kstat_t *ksp_chain;
+       unsigned long long val;
+
+       numif = 0;
+
+       if (kc == NULL)
+               return (-1);
+
+       for (numif = 0, ksp_chain = kc->kc_chain;
+                       (numif < MAX_NUMIF) && (ksp_chain != NULL);
+                       ksp_chain = ksp_chain->ks_next)
+       {
+               if (strncmp (ksp_chain->ks_class, "net", 3))
+                       continue;
+               if (ksp_chain->ks_type != KSTAT_TYPE_NAMED)
+                       continue;
+               if (kstat_read (kc, ksp_chain, NULL) == -1)
+                       continue;
+               if ((val = get_kstat_value (ksp_chain, "obytes")) == -1LL)
+                       continue;
+               ksp[numif++] = ksp_chain;
+       }
+#endif /* HAVE_LIBKSTAT */
+
+       return (0);
+} /* int interface_init */
+#endif /* HAVE_LIBKSTAT */
+
+/*
+ * Check if this interface/instance should be ignored. This is called from
+ * both, `submit' and `write' to give client and server the ability to
+ * ignore certain stuff..
+ */
+static int check_ignore_if (const char *interface)
+{
+       int i;
+
+       /* If no interfaces are given collect all interfaces. Mostly to be
+        * backwards compatible, but also because this is much easier. */
+       if (if_list_num < 1)
+               return (0);
+
+       for (i = 0; i < if_list_num; i++)
+               if (strcasecmp (interface, if_list[i]) == 0)
+                       return (if_list_action);
+       return (1 - if_list_action);
+} /* int check_ignore_if */
+
+#if INTERFACE_HAVE_READ
+static void if_submit (const char *dev, const char *type,
+               unsigned long long rx,
+               unsigned long long tx)
+{
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       if (check_ignore_if (dev))
+               return;
+
+       values[0].counter = rx;
+       values[1].counter = tx;
+
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "interface");
+       strncpy (vl.type_instance, dev, sizeof (vl.type_instance));
+
+       plugin_dispatch_values (type, &vl);
+} /* void if_submit */
+
+static int interface_read (void)
+{
+#if HAVE_GETIFADDRS
+       struct ifaddrs *if_list;
+       struct ifaddrs *if_ptr;
+
+/* Darin/Mac OS X and possible other *BSDs */
+#if HAVE_STRUCT_IF_DATA
+#  define IFA_DATA if_data
+#  define IFA_RX_BYTES ifi_ibytes
+#  define IFA_TX_BYTES ifi_obytes
+#  define IFA_RX_PACKT ifi_ipackets
+#  define IFA_TX_PACKT ifi_opackets
+#  define IFA_RX_ERROR ifi_ierrors
+#  define IFA_TX_ERROR ifi_oerrors
+/* #endif HAVE_STRUCT_IF_DATA */
+
+#elif HAVE_STRUCT_NET_DEVICE_STATS
+#  define IFA_DATA net_device_stats
+#  define IFA_RX_BYTES rx_bytes
+#  define IFA_TX_BYTES tx_bytes
+#  define IFA_RX_PACKT rx_packets
+#  define IFA_TX_PACKT tx_packets
+#  define IFA_RX_ERROR rx_errors
+#  define IFA_TX_ERROR tx_errors
+#else
+#  error "No suitable type for `struct ifaddrs->ifa_data' found."
+#endif
+
+       struct IFA_DATA *if_data;
+
+       if (getifaddrs (&if_list) != 0)
+               return (-1);
+
+       for (if_ptr = if_list; if_ptr != NULL; if_ptr = if_ptr->ifa_next)
+       {
+               if ((if_data = (struct IFA_DATA *) if_ptr->ifa_data) == NULL)
+                       continue;
+
+               if_submit (if_ptr->ifa_name, "if_octets",
+                               if_data->IFA_RX_BYTES,
+                               if_data->IFA_TX_BYTES);
+               if_submit (if_ptr->ifa_name, "if_packets",
+                               if_data->IFA_RX_PACKT,
+                               if_data->IFA_TX_PACKT);
+               if_submit (if_ptr->ifa_name, "if_errors",
+                               if_data->IFA_RX_ERROR,
+                               if_data->IFA_TX_ERROR);
+       }
+
+       freeifaddrs (if_list);
+/* #endif HAVE_GETIFADDRS */
+
+#elif KERNEL_LINUX
+       FILE *fh;
+       char buffer[1024];
+       unsigned long long incoming, outgoing;
+       char *device;
+       
+       char *dummy;
+       char *fields[16];
+       int numfields;
+
+       if ((fh = fopen ("/proc/net/dev", "r")) == NULL)
+       {
+               char errbuf[1024];
+               WARNING ("interface plugin: fopen: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       while (fgets (buffer, 1024, fh) != NULL)
+       {
+               if (!(dummy = strchr(buffer, ':')))
+                       continue;
+               dummy[0] = '\0';
+               dummy++;
+
+               device = buffer;
+               while (device[0] == ' ')
+                       device++;
+
+               if (device[0] == '\0')
+                       continue;
+               
+               numfields = strsplit (dummy, fields, 16);
+
+               if (numfields < 11)
+                       continue;
+
+               incoming = atoll (fields[0]);
+               outgoing = atoll (fields[8]);
+               if_submit (device, "if_octets", incoming, outgoing);
+
+               incoming = atoll (fields[1]);
+               outgoing = atoll (fields[9]);
+               if_submit (device, "if_packets", incoming, outgoing);
+
+               incoming = atoll (fields[2]);
+               outgoing = atoll (fields[10]);
+               if_submit (device, "if_errors", incoming, outgoing);
+       }
+
+       fclose (fh);
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_LIBKSTAT
+       int i;
+       unsigned long long rx;
+       unsigned long long tx;
+
+       if (kc == NULL)
+               return;
+
+       for (i = 0; i < numif; i++)
+       {
+               if (kstat_read (kc, ksp[i], NULL) == -1)
+                       continue;
+
+               rx = get_kstat_value (ksp[i], "rbytes");
+               tx = get_kstat_value (ksp[i], "obytes");
+               if ((rx != -1LL) || (tx != -1LL))
+                       if_submit (ksp[i]->ks_name, "if_octets", rx, tx);
+
+               rx = get_kstat_value (ksp[i], "ipackets");
+               tx = get_kstat_value (ksp[i], "opackets");
+               if ((rx != -1LL) || (tx != -1LL))
+                       if_submit (ksp[i]->ks_name, "if_packets", rx, tx);
+
+               rx = get_kstat_value (ksp[i], "ierrors");
+               tx = get_kstat_value (ksp[i], "oerrors");
+               if ((rx != -1LL) || (tx != -1LL))
+                       if_submit (ksp[i]->ks_name, "if_errors", rx, tx);
+       }
+/* #endif HAVE_LIBKSTAT */
+
+#elif defined(HAVE_LIBSTATGRAB)
+       sg_network_io_stats *ios;
+       int i, num;
+
+       ios = sg_get_network_io_stats (&num);
+
+       for (i = 0; i < num; i++)
+               if_submit (ios[i].interface_name, "if_octets", ios[i].rx, ios[i].tx);
+#endif /* HAVE_LIBSTATGRAB */
+
+       return (0);
+} /* int interface_read */
+#endif /* INTERFACE_HAVE_READ */
+
+void module_register (void)
+{
+       plugin_register_config ("interface", interface_config,
+                       config_keys, config_keys_num);
+#if HAVE_LIBKSTAT
+       plugin_register_init ("interface", interface_init);
+#endif
+#if INTERFACE_HAVE_READ
+       plugin_register_read ("interface", interface_read);
+#endif
+} /* void module_register */
diff --git a/src/iptables.c b/src/iptables.c
new file mode 100644 (file)
index 0000000..c48199a
--- /dev/null
@@ -0,0 +1,362 @@
+/**
+ * collectd - src/iptables.c
+ * Copyright (C) 2007 Sjoerd van der Berg
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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:
+ *  Sjoerd van der Berg <harekiet at users.sourceforge.net>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#if HAVE_LIBIPTC_LIBIPTC_H
+# include <libiptc/libiptc.h>
+#endif
+
+#if HAVE_LIBIPTC_LIBIPTC_H
+# define IPTABLES_HAVE_READ 1
+#else
+# define IPTABLES_HAVE_READ 0
+#endif
+
+#define MODULE_NAME "iptables"
+#define BUFSIZE 512
+
+/*
+ * (Module-)Global variables
+ */
+
+/*
+ * Removed packet count for now, should have config option if you want to save
+ * them Although other collectd models don't seem to care much for options
+ * eitherway for what to log
+ */
+#if IPTABLES_HAVE_READ
+/*
+ * Config format should be `Chain table chainname',
+ * e. g. `Chain mangle incoming'
+ */
+static const char *config_keys[] =
+{
+       "Chain",
+       NULL
+};
+static int config_keys_num = 1;
+/*
+    Each table/chain combo that will be queried goes into this list
+*/
+#ifndef XT_TABLE_MAXNAMELEN
+# define XT_TABLE_MAXNAMELEN 32
+#endif
+typedef struct {
+    char table[XT_TABLE_MAXNAMELEN];
+    char chain[XT_TABLE_MAXNAMELEN];
+    union
+    {
+       int   num;
+       char *comment;
+    } rule;
+    enum
+    {
+       RTYPE_NUM,
+       RTYPE_COMMENT,
+       RTYPE_COMMENT_ALL
+    } rule_type;
+    char name[64];
+} ip_chain_t;
+
+static ip_chain_t **chain_list = NULL;
+static int chain_num = 0;
+
+static int iptables_config (const char *key, const char *value)
+{
+       if (strcasecmp (key, "Chain") == 0)
+       {
+               ip_chain_t temp, *final, **list;
+               char *table;
+               int   table_len;
+               char *chain;
+               int   chain_len;
+
+               char *value_copy;
+               char *fields[4];
+               int   fields_num;
+               
+               memset (&temp, 0, sizeof (temp));
+
+               value_copy = strdup (value);
+               if (value_copy == NULL)
+               {
+                   char errbuf[1024];
+                   ERROR ("strdup failed: %s",
+                           sstrerror (errno, errbuf, sizeof (errbuf)));
+                   return (1);
+               }
+
+               /* Chain <table> <chain> [<comment|num> [name]] */
+               fields_num = strsplit (value_copy, fields, 4);
+               if (fields_num < 2)
+               {
+                   free (value_copy);
+                   return (1);
+               }
+
+               table = fields[0];
+               chain = fields[1];
+
+               table_len = strlen (table);
+               if (table_len >= sizeof(temp.table))
+               {
+                       ERROR ("Table `%s' too long.", table);
+                       free (value_copy);
+                       return (1);
+               }
+               strncpy (temp.table, table, table_len);
+               temp.table[table_len] = '\0';
+
+               chain_len = strlen (chain);
+               if (chain_len >= sizeof(temp.chain))
+               {
+                       ERROR ("Chain `%s' too long.", chain);
+                       free (value_copy);
+                       return (1);
+               }
+               strncpy (temp.chain, chain, chain_len);
+               temp.chain[chain_len] = '\0'; 
+
+               if (fields_num >= 3)
+               {
+                   char *comment = fields[2];
+                   int   rule = atoi (comment);
+
+                   if (rule)
+                   {
+                       temp.rule.num = rule;
+                       temp.rule_type = RTYPE_NUM;
+                   }
+                   else
+                   {
+                       strncpy (temp.rule.comment, comment,
+                               sizeof (temp.rule.comment) - 1);
+                       temp.rule_type = RTYPE_COMMENT;
+                   }
+               }
+               else
+               {
+                   temp.rule_type = RTYPE_COMMENT_ALL;
+               }
+
+               if (fields_num >= 4)
+                   strncpy (temp.name, fields[3], sizeof (temp.name) - 1);
+
+               free (value_copy);
+               value_copy = NULL;
+               table = NULL;
+               chain = NULL;
+
+               list = (ip_chain_t **) realloc (chain_list, (chain_num + 1) * sizeof (ip_chain_t *));
+               if (list == NULL)
+               {
+                   char errbuf[1024];
+                   ERROR ("realloc failed: %s",
+                           sstrerror (errno, errbuf, sizeof (errbuf)));
+                   return (1);
+               }
+
+               chain_list = list;
+               final = (ip_chain_t *) malloc( sizeof(temp) );
+               if (final == NULL) 
+               {
+                   char errbuf[1024];
+                   ERROR ("malloc failed: %s",
+                           sstrerror (errno, errbuf, sizeof (errbuf)));
+                   return (1);
+               }
+               memcpy (final, &temp, sizeof (temp));
+               chain_list[chain_num] = final;
+               chain_num++;
+
+               DEBUG ("Chain #%i: table = %s; chain = %s;", chain_num, final->table, final->chain);
+       }
+       else 
+       {
+               return (-1);
+       }
+
+       return (0);
+} /* int iptables_config */
+#endif /* IPTABLES_HAVE_READ */
+
+#if IPTABLES_HAVE_READ
+/* This needs to return `int' for IPT_MATCH_ITERATE to work. */
+static int submit_match (const struct ipt_entry_match *match,
+               const struct ipt_entry *entry,
+               const ip_chain_t *chain,
+               int rule_num) 
+{
+    int status;
+    value_t values[1];
+    value_list_t vl = VALUE_LIST_INIT;
+
+    /* Select the rules to collect */
+    if (chain->rule_type == RTYPE_NUM)
+    {
+       if (chain->rule.num != rule_num)
+           return (0);
+    }
+    else
+    {
+       if (strcmp (match->u.user.name, "comment") != 0)
+           return (0);
+       if ((chain->rule_type == RTYPE_COMMENT)
+               && (strcmp (chain->rule.comment, (char *) match->data) != 0))
+           return (0);
+    }
+
+    vl.values = values;
+    vl.values_len = 1;
+    vl.time = time (NULL);
+    strcpy (vl.host, hostname_g);
+    strcpy (vl.plugin, "iptables");
+
+    status = snprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
+           "%s-%s", chain->table, chain->chain);
+    if ((status >= sizeof (vl.plugin_instance)) || (status < 1))
+       return (0);
+
+    if (chain->name[0] != '\0')
+    {
+       strncpy (vl.type_instance, chain->name, sizeof (vl.type_instance));
+    }
+    else
+    {
+       if (chain->rule_type == RTYPE_NUM)
+           snprintf (vl.type_instance, sizeof (vl.type_instance),
+                   "%i", chain->rule.num);
+       else
+           strncpy (vl.type_instance, (char *) match->data,
+                   sizeof (vl.type_instance));
+    }
+    vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+
+    values[0].counter = (counter_t) entry->counters.bcnt;
+    plugin_dispatch_values ("ipt_bytes", &vl);
+
+    values[0].counter = (counter_t) entry->counters.pcnt;
+    plugin_dispatch_values ("ipt_packets", &vl);
+
+    return (0);
+} /* void submit_match */
+
+static void submit_chain( iptc_handle_t *handle, ip_chain_t *chain ) {
+    const struct ipt_entry *entry;
+    int rule_num;
+
+    /* Find first rule for chain and use the iterate macro */    
+    entry = iptc_first_rule( chain->chain, handle );
+    if (entry == NULL)
+    {
+       DEBUG ("iptc_first_rule failed: %s", iptc_strerror (errno));
+       return;
+    }
+
+    rule_num = 1;
+    while (entry)
+    {
+       if (chain->rule_type == RTYPE_NUM)
+       {
+           submit_match (NULL, entry, chain, rule_num);
+       }
+       else
+       {
+           IPT_MATCH_ITERATE( entry, submit_match, entry, chain, rule_num );
+       }
+
+       entry = iptc_next_rule( entry, handle );
+       rule_num++;
+    } /* while (entry) */
+}
+
+
+static int iptables_read (void)
+{
+    int i;
+    static complain_t complaint;
+
+    /* Init the iptc handle structure and query the correct table */    
+    for (i = 0; i < chain_num; i++)
+    {
+       iptc_handle_t handle;
+       ip_chain_t *chain;
+       
+       chain = chain_list[i];
+       if (!chain)
+       {
+           DEBUG ("chain == NULL");
+           continue;
+       }
+
+       handle = iptc_init( chain->table );
+       if (!handle)
+       {
+           DEBUG ("iptc_init (%s) failed: %s", chain->table, iptc_strerror (errno));
+           plugin_complain (LOG_ERR, &complaint, "iptc_init (%s) failed: %s",
+                   chain->table, iptc_strerror (errno));
+           continue;
+       }
+       plugin_relief (LOG_INFO, &complaint, "iptc_init (%s) succeeded",
+               chain->table);
+
+       submit_chain (&handle, chain);
+       iptc_free (&handle);
+    }
+
+    return (0);
+} /* int iptables_read */
+
+static int iptables_shutdown (void)
+{
+    int i;
+
+    for (i = 0; i < chain_num; i++)
+    {
+       sfree (chain_list[i]);
+    }
+    sfree (chain_list);
+
+    return (0);
+} /* int iptables_shutdown */
+#endif /* IPTABLES_HAVE_READ */
+
+void module_register (void)
+{
+#if IPTABLES_HAVE_READ
+    plugin_register_config ("iptables", iptables_config,
+           config_keys, config_keys_num);
+    plugin_register_read ("iptables", iptables_read);
+    plugin_register_shutdown ("iptables", iptables_shutdown);
+#endif
+} /* void module_register */
+
+#undef BUFSIZE
+#undef MODULE_NAME
+
+/*
+ * vim:shiftwidth=4:softtabstop=4:tabstop=8
+ */
diff --git a/src/irq.c b/src/irq.c
new file mode 100644 (file)
index 0000000..1c7b5ae
--- /dev/null
+++ b/src/irq.c
@@ -0,0 +1,216 @@
+/**
+ * collectd - src/irq.c
+ * Copyright (C) 2007  Peter Holik
+ *
+ * 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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * 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:
+ *   Peter Holik <peter at holik.at>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#if KERNEL_LINUX
+# define IRQ_HAVE_READ 1
+#else
+# define IRQ_HAVE_READ 0
+#endif
+
+#define BUFSIZE 128
+
+/*
+ * (Module-)Global variables
+ */
+#if IRQ_HAVE_READ
+static const char *config_keys[] =
+{
+       "Irq",
+       "IgnoreSelected"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static unsigned int *irq_list;
+static unsigned int irq_list_num;
+
+/* 
+ * irq_list_action:
+ * 0 => default is to collect selected irqs
+ * 1 => ignore selcted irqs
+ */
+static int irq_list_action;
+
+static int irq_config (const char *key, const char *value)
+{
+       if (strcasecmp (key, "Irq") == 0)
+       {
+               unsigned int *temp;
+               unsigned int irq;
+               char *endptr;
+
+               temp = (unsigned int *) realloc (irq_list, (irq_list_num + 1) * sizeof (unsigned int *));
+               if (temp == NULL)
+               {
+                       fprintf (stderr, "irq plugin: Cannot allocate more memory.\n");
+                       ERROR ("irq plugin: Cannot allocate more memory.");
+                       return (1);
+               }
+               irq_list = temp;
+
+               /* Clear errno, because we need it to see if an error occured. */
+               errno = 0;
+
+               irq = strtol(value, &endptr, 10);
+               if ((endptr == value) || (errno != 0))
+               {
+                       fprintf (stderr, "irq plugin: Irq value is not a "
+                                       "number: `%s'\n", value);
+                       ERROR ("irq plugin: Irq value is not a "
+                                       "number: `%s'", value);
+                       return (1);
+               }
+               irq_list[irq_list_num] = irq;
+               irq_list_num++;
+       }
+       else if (strcasecmp (key, "IgnoreSelected") == 0)
+       {
+               if ((strcasecmp (value, "True") == 0)
+                               || (strcasecmp (value, "Yes") == 0)
+                               || (strcasecmp (value, "On") == 0))
+                       irq_list_action = 1;
+               else
+                       irq_list_action = 0;
+       }
+       else
+       {
+               return (-1);
+       }
+       return (0);
+}
+
+/*
+ * Check if this interface/instance should be ignored. This is called from
+ * both, `submit' and `write' to give client and server the ability to
+ * ignore certain stuff..
+ */
+static int check_ignore_irq (const unsigned int irq)
+{
+       int i;
+
+       if (irq_list_num < 1)
+               return (0);
+
+       for (i = 0; i < irq_list_num; i++)
+               if (irq == irq_list[i])
+                       return (irq_list_action);
+
+       return (1 - irq_list_action);
+}
+
+static void irq_submit (unsigned int irq, counter_t value)
+{
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+       int status;
+
+       if (check_ignore_irq (irq))
+               return;
+
+       values[0].counter = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "irq");
+
+       status = snprintf (vl.type_instance, sizeof (vl.type_instance),
+                       "%u", irq);
+       if ((status < 1) || (status >= sizeof (vl.type_instance)))
+               return;
+
+       plugin_dispatch_values ("irq", &vl);
+} /* void irq_submit */
+
+static int irq_read (void)
+{
+#if KERNEL_LINUX
+
+#undef BUFSIZE
+#define BUFSIZE 256
+
+       FILE *fh;
+       char buffer[BUFSIZE];
+       unsigned int irq;
+       unsigned int irq_value;
+       long value;
+       char *endptr;
+       int i;
+
+       char *fields[64];
+       int fields_num;
+
+       if ((fh = fopen ("/proc/interrupts", "r")) == NULL)
+       {
+               char errbuf[1024];
+               WARNING ("irq plugin: fopen (/proc/interrupts): %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+       while (fgets (buffer, BUFSIZE, fh) != NULL)
+       {
+               fields_num = strsplit (buffer, fields, 64);
+               if (fields_num < 2)
+                       continue;
+
+               errno = 0;    /* To distinguish success/failure after call */
+               irq = strtol (fields[0], &endptr, 10);
+
+               if ((endptr == fields[0]) || (errno != 0) || (*endptr != ':'))
+                       continue;
+
+               irq_value = 0;
+               for (i = 1; i < fields_num; i++)
+               {
+                       errno = 0;
+                       value = strtol (fields[i], &endptr, 10);
+
+                       if ((*endptr != '\0') || (errno != 0))
+                               break;
+
+                       irq_value += value;
+               } /* for (i) */
+
+               irq_submit (irq, irq_value);
+       }
+       fclose (fh);
+#endif /* KERNEL_LINUX */
+
+       return (0);
+} /* int irq_read */
+#endif /* IRQ_HAVE_READ */
+
+void module_register (void)
+{
+#if IRQ_HAVE_READ
+       plugin_register_config ("irq", irq_config,
+                       config_keys, config_keys_num);
+       plugin_register_read ("irq", irq_read);
+#endif /* IRQ_HAVE_READ */
+} /* void module_register */
+
+#undef BUFSIZE
diff --git a/src/libconfig/AUTHORS b/src/libconfig/AUTHORS
deleted file mode 100644 (file)
index 53f27bf..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-                                libconfig 0.1.21
-
-Release information:
-  pkg:  libconfig version 0.1.21
-  url:  http://www.rkeene.org/files/oss/libconfig/devel/libconfig-0.1.21.tar.gz
-  web:  http://www.rkeene.org/oss/libconfig/
-  date: Tue Oct 18 16:03:55 CDT 2005
-  mail: libconfig@rkeene.org
---------------------------------------------------------------------------
-
-Primary Authors:
-  * Roy Keene <libconfig@rkeene.org>
-    Primary maintainer.
-
-People who have submitted patches:
-  * Mirko Buffoni <mirko.buffoni@synthesys.it>
-    Fixed installation issues and corrected mistakes in RPM spec file.
diff --git a/src/libconfig/LICENSE b/src/libconfig/LICENSE
deleted file mode 100644 (file)
index 4af06fb..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2004  Roy Keene, Keene Enterprises
-
-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.
diff --git a/src/libconfig/Makefile.am b/src/libconfig/Makefile.am
deleted file mode 100644 (file)
index b5a558a..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-AUTOMAKE_OPTIONS = foreign no-dependencies
-
-EXTRA_DIST = AUTHORS LICENSE README TODO
-
-noinst_LTLIBRARIES = libconfig.la
-
-libconfig_la_CFLAGS =
-libconfig_la_LDFLAGS = -avoid-version
-libconfig_la_SOURCES = \
-       libconfig.c libconfig.h \
-       conf_apache.c conf_apache.h \
-       conf_colon.c conf_colon.h \
-       conf_equal.c conf_equal.h \
-       conf_section.c conf_section.h \
-       conf_space.c conf_space.h \
-       conf_xml.c conf_xml.h \
-       compat.h libconfig_private.h
diff --git a/src/libconfig/README b/src/libconfig/README
deleted file mode 100644 (file)
index 7274223..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-                                libconfig 0.1.21
-
-Release information:
-  pkg:  libconfig version 0.1.21
-  url:  http://www.rkeene.org/files/oss/libconfig/devel/libconfig-0.1.21.tar.gz
-  web:  http://www.rkeene.org/oss/libconfig/
-  date: Tue Oct 18 16:03:55 CDT 2005
-  mail: libconfig@rkeene.org
---------------------------------------------------------------------------
-
-Tested platforms:
-  Mac OS X 10.1
-  HP-UX 11.11
-  NetBSD 1.6.1
-  FreeBSD 4.8
-  OpenBSD 3.4
-  SunOS 5.9
-  Linux (Slackware, RedHat, Fedora Core, Debian)
-  IRIX 6.5
diff --git a/src/libconfig/TODO b/src/libconfig/TODO
deleted file mode 100644 (file)
index 6ba4ed3..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-                                libconfig 0.1.21
-
-Release information:
-  pkg:  libconfig version 0.1.21
-  url:  http://www.rkeene.org/files/oss/libconfig/devel/libconfig-0.1.21.tar.gz
-  web:  http://www.rkeene.org/oss/libconfig/
-  date: Tue Oct 18 16:03:55 CDT 2005
-  mail: libconfig@rkeene.org
---------------------------------------------------------------------------
-
-  More manual pages.
diff --git a/src/libconfig/compat.h b/src/libconfig/compat.h
deleted file mode 100644 (file)
index 48d0b07..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-#ifndef RSK_COMPAT_H
-#define RSK_COMPAT_H
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#ifdef __WIN32__
-#include "win32.h"
-#endif
-
-#ifndef LC_LINEBUF_LEN
-#define LC_LINEBUF_LEN 1024
-#endif
-
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-#ifdef HAVE_MEMORY_H
-#include <memory.h>
-#endif
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#ifdef HAVE_STRINGS_H
-#include <strings.h>
-#endif
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_STDARG_H
-#include <stdarg.h>
-#endif
-#ifdef HAVE_STDIO_H
-#include <stdio.h>
-#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#ifdef TIME_WITH_SYS_TIME
-#include <sys/time.h>
-#include <time.h>
-#else
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#else
-#include <time.h>
-#endif
-#endif
-
-#ifdef HAVE_OPENNET_H
-#include <opennet.h>
-#endif
-#ifdef HAVE_LIBOPENNET
-#define lc_fopen(path, mode) fopen_net(path, mode)
-#define lc_fgets(buf, size, stream) fgets_net(buf, size, stream)
-#define lc_feof(stream) feof_net(stream)
-#define lc_fclose(stream) fclose_net(stream)
-#define LC_FILE NETFILE
-#else
-#define lc_fopen(path, mode) fopen(path, mode)
-#define lc_fgets(buf, size, stream) fgets(buf, size, stream)
-#define lc_feof(stream) feof(stream)
-#define lc_fclose(stream) fclose(stream)
-#define LC_FILE FILE
-#endif
-
-#endif
diff --git a/src/libconfig/conf_apache.c b/src/libconfig/conf_apache.c
deleted file mode 100644 (file)
index 1497d1e..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-#include "compat.h"
-#include "libconfig.h"
-#include "libconfig_private.h"
-#include "conf_apache.h"
-
-#ifdef HAVE_CTYPE_H
-#include <ctype.h>
-#endif
-
-#ifdef HAVE_STDIO_H
-#include <stdio.h>
-#endif
-
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
-#endif
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifdef HAVE_DIRENT_H
-#include <dirent.h>
-#endif
-
-static int lc_process_conf_apache_file(const char *configfile, const char *pathprefix);
-
-static int lc_process_conf_apache_include(const char *pathname, const char *pathprefix) {
-       struct stat pathinfo;
-       struct dirent *dinfo = NULL;
-       char includepath[LC_LINEBUF_LEN] = {0};
-       DIR *dh = NULL;
-       int statret = -1, lcpcafret = -1;
-       int retval = 0;
-
-       statret = stat(pathname, &pathinfo);
-       if (statret < 0) {
-               return(-1);
-       }
-
-       if (S_ISDIR(pathinfo.st_mode)) {
-               dh = opendir(pathname);
-               if (dh == NULL) {
-                       return(-1);
-               }
-
-               while (1) {
-                       dinfo = readdir(dh);
-                       if (dinfo == NULL) {
-                               break;
-                       }
-
-                       /* Skip files that begin with a dot ('.') */
-                       if (dinfo->d_name[0] == '.') continue;
-
-                       snprintf(includepath, sizeof(includepath) - 1, "%s/%s", pathname, dinfo->d_name);
-                       lcpcafret = lc_process_conf_apache_include(includepath, pathprefix);
-                       if (lcpcafret < 0) {
-                               retval = -1;
-                               /* XXX: should we break here (abort further including of files from a directory if one fails ?) */
-                       }
-               }
-
-               closedir(dh);
-       } else {
-               lcpcafret = lc_process_conf_apache_file(pathname, pathprefix);
-               if (lcpcafret < 0) {
-                       retval = -1;
-               }
-       }
-
-       return(retval);
-}
-
-static int lc_process_conf_apache_file(const char *configfile, const char *pathprefix) {
-       LC_FILE *configfp = NULL;
-       const char *local_lc_errfile;
-       char linebuf[LC_LINEBUF_LEN] = {0}, *linebuf_ptr = NULL, *tmp_ptr = NULL;
-       char *lastsection = NULL;
-       char qualifbuf[LC_LINEBUF_LEN] = {0};
-       char *cmd = NULL, *value = NULL, *sep = NULL, *cmdend = NULL;
-       char *fgetsret = NULL;
-       int lcpvret = -1, lpcafret = -1;
-       int invalid_section = 0, ignore_section = 0;
-       int local_lc_errline;
-       int retval = 0;
-       lc_err_t save_lc_errno = LC_ERR_NONE;
-
-       if (pathprefix != NULL) {
-               /* Copy the prefix, if specified. */
-               strncpy(qualifbuf, pathprefix, sizeof(qualifbuf) - 1);
-       }
-
-       local_lc_errfile = configfile;
-       local_lc_errline = 0;
-
-       if (configfile == NULL) {
-               lc_errfile = local_lc_errfile;
-               lc_errline = local_lc_errline;
-               lc_errno = LC_ERR_INVDATA;
-               return(-1);
-       }
-
-       configfp = lc_fopen(configfile, "r");
-
-       if (configfp == NULL) {
-               lc_errfile = local_lc_errfile;
-               lc_errline = local_lc_errline;
-               lc_errno = LC_ERR_CANTOPEN;
-               return(-1);
-       }
-
-       while (1) {
-               fgetsret = lc_fgets(linebuf, sizeof(linebuf) - 1, configfp);
-               if (fgetsret == NULL) {
-                       break;
-               }
-               if (lc_feof(configfp)) {
-                       break;
-               }
-
-               local_lc_errline++;
-
-               /* Remove trailing crap (but not spaces). */
-               linebuf_ptr = &linebuf[strlen(linebuf) - 1];
-               while (*linebuf_ptr < ' ' && linebuf_ptr >= linebuf) {
-                       *linebuf_ptr = '\0';
-                       linebuf_ptr--;
-               }
-
-               /* Remove leading spaces. */
-               linebuf_ptr = &linebuf[0];
-               while (*linebuf_ptr == ' ' || *linebuf_ptr == '\t') {
-                       linebuf_ptr++;
-               }
-
-               /* Handle section header. */
-               if (linebuf_ptr[0] == '<' && linebuf_ptr[strlen(linebuf_ptr) - 1] == '>') {
-                       /* Remove < and > from around the data. */
-                       linebuf_ptr[strlen(linebuf_ptr) - 1] = '\0';
-                       linebuf_ptr++;
-
-                       /* Lowercase the command part of the section. */
-                       tmp_ptr = linebuf_ptr;
-                       while (*tmp_ptr != '\0' && *tmp_ptr != ' ') {
-                               *tmp_ptr = tolower(*tmp_ptr);
-                               tmp_ptr++;
-                       }
-
-                       /* If this is a close section command, handle it */
-                       if (linebuf_ptr[0] == '/') {
-                               linebuf_ptr++;
-                               cmd = linebuf_ptr; 
-
-                               /* Find the last section closed. */
-                               tmp_ptr = strrchr(qualifbuf, '.');
-                               if (tmp_ptr == NULL) {
-                                       lastsection = qualifbuf;
-                                       tmp_ptr = qualifbuf;
-                               } else {
-                                       lastsection = tmp_ptr + 1;
-                               }
-
-                               if (strcmp(cmd, lastsection) != 0) {
-#ifdef DEBUG
-                                       fprintf(stderr, "Section closing does not match last opened section.\n");
-                                       fprintf(stderr, "Last opened = \"%s\", Closing = \"%s\"\n", lastsection, cmd);
-#endif
-                                       retval = -1;
-                                       lc_errfile = local_lc_errfile;
-                                       lc_errline = local_lc_errline;
-                                       lc_errno = LC_ERR_BADFORMAT;
-
-                                       /* For this error, we abort immediately. */
-                                       break;
-                               }
-
-                               lcpvret = lc_process_var(qualifbuf, NULL, NULL, LC_FLAGS_SECTIONEND);
-                               if (lcpvret < 0) {
-#ifdef DEBUG
-                                       fprintf(stderr, "Invalid section terminating: \"%s\"\n", qualifbuf);
-#endif
-                               }
-
-                               /* Remove the "lastsection" part.. */
-                               *tmp_ptr = '\0';
-
-                               /* We just sucessfully closed the last section opened,
-                                  we must be in a valid section now since we only open
-                                  sections from within valid sections. */
-                               invalid_section = 0;
-                               ignore_section = 0;
-
-                               continue;
-                       }
-                       /* Otherwise, open a new section. */
-
-                       /* Don't open a section from an invalid section. */
-                       if (invalid_section == 1 || ignore_section == 1) {
-                               continue;
-                       }
-
-                       /* Parse out any argument passed. */
-                       sep = strpbrk(linebuf_ptr, " \t");
-
-                       if (sep != NULL) {
-                               cmdend = sep;
-                               /* Delete space at the end of the command. */
-                               cmdend--; /* It currently derefs to the seperator.. */
-                               while (*cmdend <= ' ') {
-                                       *cmdend = '\0';
-                                       cmdend--;
-                               }
-
-                               /* Delete the seperator char and any leading space. */
-                               *sep = '\0';
-                               sep++;
-                               while (*sep == ' ' || *sep == '\t') {
-                                       sep++;
-                               }
-                               value = sep;
-                       } else {
-                               /* XXX: should this be "" or NULL ? */
-                               value = "";
-                       }
-
-                       cmd = linebuf_ptr;
-
-                       if (qualifbuf[0] != '\0') {
-                               strncat(qualifbuf, ".", sizeof(qualifbuf) - strlen(qualifbuf) - 1);
-                       }
-                       strncat(qualifbuf, cmd, sizeof(qualifbuf) - strlen(qualifbuf) - 1);
-
-                       lcpvret = lc_process_var(qualifbuf, value, NULL, LC_FLAGS_SECTIONSTART);
-                       if (lcpvret < 0) {
-#ifdef DEBUG
-                               fprintf(stderr, "Invalid section: \"%s\"\n", qualifbuf);
-#endif
-                               invalid_section = 1;
-                               lc_errfile = local_lc_errfile;
-                               lc_errline = local_lc_errline;
-                               lc_errno = LC_ERR_INVSECTION;
-                               retval = -1;
-                       }
-                       if (lcpvret == LC_CBRET_IGNORESECTION) {
-                               ignore_section = 1;
-                       }
-                       continue;
-               }
-
-               /* Drop comments and blank lines. */
-               if (*linebuf_ptr == '#' || *linebuf_ptr == '\0') {
-                       continue;
-               }
-
-               /* Don't handle things for a section that doesn't exist. */
-               if (invalid_section == 1) {
-#ifdef DEBUG
-                       fprintf(stderr, "Ignoring line (because invalid section): %s\n", linebuf);
-#endif
-                       continue;
-               }
-               if (ignore_section == 1) {
-#ifdef DEBUG
-                       fprintf(stderr, "Ignoring line (because ignored section): %s\n", linebuf);
-#endif
-                       continue;
-               }
-
-               /* Find the command and the data in the line. */
-               sep = strpbrk(linebuf_ptr, " \t");
-               if (sep != NULL) {
-                       cmdend = sep;
-
-                       /* Delete space at the end of the command. */
-                       cmdend--; /* It currently derefs to the seperator.. */
-                       while (*cmdend <= ' ') {
-                               *cmdend = '\0';
-                               cmdend--;
-                       }
-
-                       /* Delete the seperator char and any leading space. */
-                       *sep = '\0';
-                       sep++;
-                       while (*sep == ' ' || *sep == '\t') {
-                               sep++;
-                       }
-                       value = sep;
-               } else {
-                       value = NULL;
-               }
-
-               cmd = linebuf_ptr;
-
-               /* Handle special commands. */
-               if (strcasecmp(cmd, "include") == 0) {
-                       if (value == NULL) {
-                               lc_errfile = local_lc_errfile;
-                               lc_errline = local_lc_errline;
-                               lc_errno = LC_ERR_BADFORMAT;
-                               retval = -1;
-#ifdef DEBUG
-                               fprintf(stderr, "Invalid include command.\n");
-#endif
-                               continue;
-                       }
-
-                       lpcafret = lc_process_conf_apache_include(value, qualifbuf);
-                       if (lpcafret < 0) {
-#ifdef DEBUG
-                               fprintf(stderr, "Error in included file.\n");
-#endif
-                               retval = -1;
-                       }
-                       continue;
-               }
-
-               /* Create the fully qualified variable name. */
-               if (qualifbuf[0] != '\0') {
-                       strncat(qualifbuf, ".", sizeof(qualifbuf) - strlen(qualifbuf) - 1);
-               }
-               strncat(qualifbuf, cmd, sizeof(qualifbuf) - strlen(qualifbuf) - 1);
-
-               /* Call the parent and tell them we have data. */
-               save_lc_errno = lc_errno;
-               lc_errno = LC_ERR_NONE;
-               lcpvret = lc_process_var(qualifbuf, NULL, value, LC_FLAGS_VAR);
-               if (lcpvret < 0) {
-                       if (lc_errno == LC_ERR_NONE) {
-#ifdef DEBUG
-                               fprintf(stderr, "Invalid command: \"%s\"\n", cmd);
-#endif
-                               lc_errfile = local_lc_errfile;
-                               lc_errline = local_lc_errline;
-                               lc_errno = LC_ERR_INVCMD;
-                       } else {
-#ifdef DEBUG
-                               fprintf(stderr, "Error processing command (command was valid, but an error occured, errno was set)\n");
-#endif
-                       }
-                       lc_errfile = local_lc_errfile;
-                       lc_errline = local_lc_errline;
-                       retval = -1;
-               } else {
-                       lc_errno = save_lc_errno;
-               }
-
-               /* Remove the "cmd" part of the buffer. */
-               tmp_ptr = strrchr(qualifbuf, '.');
-               if (tmp_ptr == NULL) {
-                       tmp_ptr = qualifbuf;
-               }
-               *tmp_ptr = '\0';
-       }
-
-       lc_fclose(configfp);
-
-       return(retval);
-}
-
-int lc_process_conf_apache(const char *appname, const char *configfile) {
-       return(lc_process_conf_apache_file(configfile, NULL));
-}
diff --git a/src/libconfig/conf_apache.h b/src/libconfig/conf_apache.h
deleted file mode 100644 (file)
index 36c556e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef LC_CONF_APACHE_H
-#define LC_CONF_APACHE_H
-#include "libconfig.h"
-#include "libconfig_private.h"
-
-int lc_process_conf_apache(const char *appname, const char *configfile);
-
-#endif
diff --git a/src/libconfig/conf_colon.c b/src/libconfig/conf_colon.c
deleted file mode 100644 (file)
index f184309..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "libconfig.h"
-#include "libconfig_private.h"
-#include "conf_colon.h"
-
-int lc_process_conf_colon(const char *appname, const char *configfile) {
-       return(-1);
-}
diff --git a/src/libconfig/conf_colon.h b/src/libconfig/conf_colon.h
deleted file mode 100644 (file)
index 7d2323c..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef LC_CONF_COLON_H
-#define LC_CONF_COLON_H
-#include "libconfig.h"
-#include "libconfig_private.h"
-
-int lc_process_conf_colon(const char *appname, const char *configfile);
-
-#endif
diff --git a/src/libconfig/conf_equal.c b/src/libconfig/conf_equal.c
deleted file mode 100644 (file)
index a214330..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "libconfig.h"
-#include "libconfig_private.h"
-#include "conf_equal.h"
-
-int lc_process_conf_equal(const char *appname, const char *configfile) {
-       return(-1);
-}
diff --git a/src/libconfig/conf_equal.h b/src/libconfig/conf_equal.h
deleted file mode 100644 (file)
index 82e0f04..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef LC_CONF_EQUAL_H
-#define LC_CONF_EQUAL_H
-#include "libconfig.h"
-#include "libconfig_private.h"
-
-int lc_process_conf_equal(const char *appname, const char *configfile);
-
-#endif
diff --git a/src/libconfig/conf_section.c b/src/libconfig/conf_section.c
deleted file mode 100644 (file)
index fb948c1..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-#include "compat.h"
-#include "libconfig.h"
-#include "libconfig_private.h"
-#include "conf_section.h"
-
-#ifdef HAVE_STDIO_H
-#include <stdio.h>
-#endif
-
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-
-int lc_process_conf_section(const char *appname, const char *configfile) {
-       LC_FILE *configfp = NULL;
-       const char *local_lc_errfile;
-       char linebuf[LC_LINEBUF_LEN] = {0}, *linebuf_ptr = NULL;
-       char qualifbuf[LC_LINEBUF_LEN] = {0};
-       char *cmd = NULL, *value = NULL, *sep = NULL, *cmdend = NULL;
-       char *currsection = NULL;
-       char *fgetsret = NULL;
-       int lcpvret = -1;
-       int invalid_section = 1, ignore_section = 0;
-       int local_lc_errline;
-       int retval = 0;
-       lc_err_t save_lc_errno = LC_ERR_NONE;
-
-       local_lc_errfile = configfile;
-       local_lc_errline = 0;
-
-       if (appname == NULL || configfile == NULL) {
-               lc_errfile = local_lc_errfile;
-               lc_errline = local_lc_errline;
-               lc_errno = LC_ERR_INVDATA;
-               return(-1);
-       }
-
-       configfp = lc_fopen(configfile, "r");
-
-       if (configfp == NULL) {
-               lc_errfile = local_lc_errfile;
-               lc_errline = local_lc_errline;
-               lc_errno = LC_ERR_CANTOPEN;
-               return(-1);
-       }
-
-       while (1) {
-               fgetsret = lc_fgets(linebuf, sizeof(linebuf) - 1, configfp);
-               if (fgetsret == NULL) {
-                       break;
-               }
-               if (lc_feof(configfp)) {
-                       break;
-               }
-
-               local_lc_errline++;
-
-               /* Remove trailing crap (but not spaces). */
-               linebuf_ptr = &linebuf[strlen(linebuf) - 1];
-               while (*linebuf_ptr < ' ' && linebuf_ptr >= linebuf) {
-                       *linebuf_ptr = '\0';
-                       linebuf_ptr--;
-               }
-
-               /* Handle section header. */
-               if (linebuf[0] == '[' && linebuf[strlen(linebuf) - 1] == ']') {
-                       linebuf[strlen(linebuf) - 1] = '\0';
-                       linebuf_ptr = &linebuf[1];
-
-                       /* If a section was open, close it. */
-                       if (currsection != NULL) {
-                               lcpvret = lc_process_var(currsection, NULL, NULL, LC_FLAGS_SECTIONEND);
-                               if (lcpvret < 0) {
-#ifdef DEBUG
-                                       fprintf(stderr, "Invalid section terminating: \"%s\"\n", currsection);
-#endif
-                               }
-                               free(currsection);
-                       }
-
-                       /* Open new section. */
-                       currsection = strdup(linebuf_ptr);
-                       lcpvret = lc_process_var(currsection, NULL, NULL, LC_FLAGS_SECTIONSTART);
-                       if (lcpvret < 0) {
-#ifdef DEBUG
-                               fprintf(stderr, "Invalid section: \"%s\"\n", currsection);
-#endif
-                               invalid_section = 1;
-                               lc_errfile = local_lc_errfile;
-                               lc_errline = local_lc_errline;
-                               lc_errno = LC_ERR_INVSECTION;
-                               retval = -1;
-                       } else {
-                               invalid_section = 0;
-                               ignore_section = 0;
-                       }
-
-                       if (lcpvret == LC_CBRET_IGNORESECTION) {
-                               ignore_section = 1;
-                       }
-                       continue;
-               }
-
-               /* Remove leading spaces. */
-               linebuf_ptr = &linebuf[0];
-               while (*linebuf_ptr == ' ') {
-                       linebuf_ptr++;
-               }
-
-               /* Drop comments and blank lines. */
-               if (*linebuf_ptr == ';' || *linebuf_ptr == '\0') {
-                       continue;
-               }
-
-               /* Don't handle things for a section that doesn't exist. */
-               if (invalid_section == 1) {
-#ifdef DEBUG
-                       fprintf(stderr, "Ignoring line (because invalid section): %s\n", linebuf);
-#endif
-                       continue;
-               }
-
-               /* Don't process commands if this section is specifically ignored. */
-               if (ignore_section == 1) {
-#ifdef DEBUG
-                       fprintf(stderr, "Ignoring line (because ignored section): %s\n", linebuf);
-#endif
-                       continue;
-               }
-
-               /* Find the command and the data in the line. */
-               cmdend = sep = strpbrk(linebuf_ptr, "=");
-               if (sep == NULL) {
-#ifdef DEBUG
-                       fprintf(stderr, "Invalid line: \"%s\"\n", linebuf);
-#endif
-                       continue;
-               }
-
-               /* Delete space at the end of the command. */
-               cmdend--; /* It currently derefs to the seperator.. */
-               while (*cmdend <= ' ') {
-                       *cmdend = '\0';
-                       cmdend--;
-               }
-
-               cmd = linebuf_ptr;
-
-               /* Delete the seperator char and any leading space. */
-               *sep = '\0';
-               sep++;
-               while (*sep == ' ' || *sep == '\t') {
-                       sep++;
-               }
-               value = sep;
-
-               /* Create the fully qualified variable name. */
-               if (currsection == NULL) {
-                       strncpy(qualifbuf, cmd, sizeof(qualifbuf) - 1);
-               } else {
-                       snprintf(qualifbuf, sizeof(qualifbuf) - 1, "%s.%s", currsection, cmd);
-               }
-
-               /* Call the parent and tell them we have data. */
-               save_lc_errno = lc_errno;
-               lc_errno = LC_ERR_NONE;
-               lcpvret = lc_process_var(qualifbuf, NULL, value, LC_FLAGS_VAR);
-               if (lcpvret < 0) {
-                       if (lc_errno == LC_ERR_NONE) {
-#ifdef DEBUG
-                               fprintf(stderr, "Invalid command: \"%s\"\n", cmd);
-#endif
-                               lc_errno = LC_ERR_INVCMD;
-                       } else {
-#ifdef DEBUG
-                               fprintf(stderr, "Error processing command (command was valid, but an error occured, errno was set)\n");
-#endif
-                       }
-                       lc_errfile = local_lc_errfile;
-                       lc_errline = local_lc_errline;
-                       retval = -1;
-               } else {
-                       lc_errno = save_lc_errno;
-               }
-       }
-
-       /* Close any open section, and clean-up. */
-       if (currsection != NULL) {
-               lcpvret = lc_process_var(currsection, NULL, NULL, LC_FLAGS_SECTIONEND);
-               if (lcpvret < 0) {
-#ifdef DEBUG
-                       fprintf(stderr, "Invalid section terminating: \"%s\"\n", currsection);
-#endif
-               }
-               free(currsection);
-       }
-
-       lc_fclose(configfp);
-
-       return(retval);
-}
diff --git a/src/libconfig/conf_section.h b/src/libconfig/conf_section.h
deleted file mode 100644 (file)
index 1d1a94c..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef LC_CONF_SECTION_H
-#define LC_CONF_SECTION_H
-#include "libconfig.h"
-#include "libconfig_private.h"
-
-int lc_process_conf_section(const char *appname, const char *configfile);
-
-#endif
diff --git a/src/libconfig/conf_space.c b/src/libconfig/conf_space.c
deleted file mode 100644 (file)
index 50a9b23..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#include "compat.h"
-#include "libconfig.h"
-#include "libconfig_private.h"
-#include "conf_space.h"
-
-#ifdef HAVE_STDIO_H
-#include <stdio.h>
-#endif
-
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-
-int lc_process_conf_space(const char *appname, const char *configfile) {
-       LC_FILE *configfp = NULL;
-       const char *local_lc_errfile;
-       char linebuf[LC_LINEBUF_LEN] = {0}, *linebuf_ptr = NULL;
-       char *cmd = NULL, *value = NULL, *sep = NULL;
-       char *fgetsret = NULL;
-       int local_lc_errline;
-       int lcpvret = -1;
-       int retval = 0;
-       lc_err_t save_lc_errno = LC_ERR_NONE;
-
-       local_lc_errfile = configfile;
-       local_lc_errline = 0;
-
-       if (appname == NULL || configfile == NULL) {
-               lc_errfile = local_lc_errfile;
-               lc_errline = local_lc_errline;
-               lc_errno = LC_ERR_INVDATA;
-               return(-1);
-       }
-
-       configfp = lc_fopen(configfile, "r");
-
-       if (configfp == NULL) {
-               lc_errfile = local_lc_errfile;
-               lc_errline = local_lc_errline;
-               lc_errno = LC_ERR_CANTOPEN;
-               return(-1);
-       }
-
-       while (1) {
-               fgetsret = lc_fgets(linebuf, sizeof(linebuf) - 1, configfp);
-               if (fgetsret == NULL) {
-                       break;
-               }
-               if (lc_feof(configfp)) {
-                       break;
-               }
-
-               local_lc_errline++;
-
-               linebuf_ptr = &linebuf[strlen(linebuf) - 1];
-               while (*linebuf_ptr < ' ' && linebuf_ptr >= linebuf) {
-                       *linebuf_ptr = '\0';
-                       linebuf_ptr--;
-               }
-
-               linebuf_ptr = &linebuf[0];
-               while (*linebuf_ptr == ' ') {
-                       linebuf_ptr++;
-               }
-
-               if (*linebuf_ptr == '#' || *linebuf_ptr == '\0') {
-                       continue;
-               }
-
-
-               sep = strpbrk(linebuf_ptr, " \t");
-               cmd = linebuf_ptr;
-               if (sep != NULL) {
-                       *sep = '\0';
-                       sep++;
-                       while (*sep == ' ' || *sep == '\t') {
-                               sep++;
-                       }
-                       value = sep;
-               } else {
-                       value = NULL;
-               }
-
-               save_lc_errno = lc_errno;
-               lc_errno = LC_ERR_NONE;
-               lcpvret = lc_process_var(cmd, NULL, value, LC_FLAGS_VAR);
-               if (lcpvret < 0) {
-                       if (lc_errno == LC_ERR_NONE) {
-#ifdef DEBUG
-                               fprintf(stderr, "Invalid command: \"%s\"\n", cmd);
-#endif
-                               lc_errno = LC_ERR_INVCMD;
-                       } else {
-#ifdef DEBUG
-                               fprintf(stderr, "Error processing command (command was valid, but an error occured, errno was set)\n");
-#endif
-                       }
-                       lc_errfile = local_lc_errfile;
-                       lc_errline = local_lc_errline;
-                       retval = -1;
-               } else {
-                       lc_errno = save_lc_errno;
-               }
-       }
-
-       lc_fclose(configfp);
-
-       return(retval);
-}
diff --git a/src/libconfig/conf_space.h b/src/libconfig/conf_space.h
deleted file mode 100644 (file)
index 6cf0462..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef LC_CONF_SPACE_H
-#define LC_CONF_SPACE_H
-#include "libconfig.h"
-#include "libconfig_private.h"
-
-int lc_process_conf_space(const char *appname, const char *configfile);
-
-#endif
diff --git a/src/libconfig/conf_xml.c b/src/libconfig/conf_xml.c
deleted file mode 100644 (file)
index 85edd51..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "libconfig.h"
-#include "libconfig_private.h"
-#include "conf_xml.h"
-
-int lc_process_conf_xml(const char *appname, const char *configfile) {
-       return(-1);
-}
diff --git a/src/libconfig/conf_xml.h b/src/libconfig/conf_xml.h
deleted file mode 100644 (file)
index f0deffb..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef LC_CONF_XML_H
-#define LC_CONF_XML_H
-#include "libconfig.h"
-#include "libconfig_private.h"
-
-int lc_process_conf_xml(const char *appname, const char *configfile);
-
-#endif
diff --git a/src/libconfig/libconfig.c b/src/libconfig/libconfig.c
deleted file mode 100644 (file)
index 9f80a76..0000000
+++ /dev/null
@@ -1,1073 +0,0 @@
-#include "compat.h"
-#include "libconfig.h"
-#include "libconfig_private.h"
-#include "conf_section.h"
-#include "conf_apache.h"
-#include "conf_colon.h"
-#include "conf_equal.h"
-#include "conf_space.h"
-#include "conf_xml.h"
-
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-
-#ifdef HAVE_CTYPE_H
-#include <ctype.h>
-#endif
-
-#ifdef HAVE_STDIO_H
-#include <stdio.h>
-#endif
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
-#ifdef HAVE_PWD_H
-#include <pwd.h>
-#endif
-
-struct lc_varhandler_st *varhandlers = NULL;
-lc_err_t lc_errno = LC_ERR_NONE;
-const char *lc_errfile = NULL;
-int lc_optind = 0;
-int lc_errline = 0;
-
-extern char **environ;
-
-static int lc_process_var_string(void *data, const char *value, const char **endptr) {
-       char **dataval;
-
-       dataval = data;
-       *dataval = strdup(value);
-
-       *endptr = NULL;
-
-       return(0);
-}
-
-static int lc_process_var_cidr(void *data, const char *value, const char **endptr) {
-       return(-1);
-}
-
-static int lc_process_var_hostname6(void *data, const char *value, const char **endptr) {
-       return(-1);
-}
-
-static int lc_process_var_hostname4(void *data, const char *value, const char **endptr) {
-       return(-1);
-}
-
-static int lc_process_var_ip6(void *data, const char *value, const char **endptr) {
-       return(-1);
-}
-
-static int lc_process_var_ip4(void *data, const char *value, const char **endptr) {
-       uint32_t *dataval, retval = 0;
-       const char *dotptr = NULL;
-       int tmpval = -1;
-//     int dotcount
-
-       dataval = data;
-
-       dotptr = value;
-
-       while (1) {
-               tmpval = atoi(dotptr);
-               if (tmpval < 0) {
-                       break;
-               }
-
-               retval <<= 8;
-               retval |= tmpval;
-
-               dotptr = strpbrk(dotptr, "./ \t");
-               if (dotptr == NULL) {
-                       break;
-               }
-               if (*dotptr != '.') {
-                       break;
-               }
-               dotptr++;
-       }
-
-       *dataval = retval;
-
-       *endptr = (char *) dotptr;
-
-       return(0);
-}
-
-static int lc_process_var_longlong(void *data, const char *value, const char **endptr) {
-       long long *dataval;
-
-       dataval = data;
-       *dataval = strtoll(value, (char **) endptr, 10);
-
-       return(0);
-}
-
-static int lc_process_var_long(void *data, const char *value, const char **endptr) {
-       long *dataval;
-
-       dataval = data;
-       *dataval = strtoll(value, (char **) endptr, 10);
-
-       return(0);
-}
-
-static int lc_process_var_int(void *data, const char *value, const char **endptr) {
-       int *dataval;
-
-       dataval = data;
-       *dataval = strtoll(value, (char **) endptr, 10);
-
-       return(0);
-}
-
-static int lc_process_var_short(void *data, const char *value, const char **endptr) {
-       short *dataval;
-
-       dataval = data;
-       *dataval = strtoll(value, (char **) endptr, 10);
-
-       return(0);
-}
-
-static int lc_process_var_bool_byexistance(void *data, const char *value, const char **endptr) {
-       int *dataval;
-
-       dataval = data;
-
-       *dataval = 1;
-
-       *endptr = NULL;
-
-       return(0);
-}
-
-static int lc_process_var_bool(void *data, const char *value, const char **endptr) {
-       char *trueval[] = {"enable", "true", "yes", "on", "y", "1"};
-       char *falseval[] = {"disable", "false", "no", "off", "n", "0"};
-       size_t chkvallen, vallen;
-       int *dataval;
-       int i;
-
-       dataval = data;
-
-       *dataval = -1;
-
-       vallen = strlen(value);
-
-       for (i = 0; i < (sizeof(trueval) / sizeof(*trueval)); i++) {
-               chkvallen = strlen(trueval[i]);
-
-               /*
-                * Skip if there's no way we could find a match here.
-                */
-               if (chkvallen > vallen) {
-                       continue;
-               }
-
-               /*
-                * Skip if there is no partial match.
-                */
-               if (strncasecmp(value, trueval[i], chkvallen) != 0) {
-                       continue;
-               }
-
-               if (value[chkvallen] == '\0' || value[chkvallen] == ',' || \
-                   value[chkvallen] == ' ') {
-                       /* Declare a winner and set the next token. */
-                       *endptr = value + chkvallen;
-                       *dataval = 1;
-                       return(0);
-               }
-       }
-
-       for (i = 0; i < (sizeof(falseval) / sizeof(*falseval)); i++) {
-               chkvallen = strlen(falseval[i]);
-
-               /*
-                * Skip if there's no way we could find a match here.
-                */
-               if (chkvallen > vallen) {
-                       continue;
-               }
-
-               /*
-                * Skip if there is no partial match.
-                */
-               if (strncasecmp(value, falseval[i], chkvallen) != 0) {
-                       continue;
-               }
-
-               if (value[chkvallen] == '\0' || value[chkvallen] == ',' || \
-                   value[chkvallen] == ' ') {
-                       /* Declare a winner and set the next token. */
-                       *endptr = value + chkvallen;
-                       *dataval = 0;
-                       return(0);
-               }
-       }
-
-       lc_errno = LC_ERR_BADFORMAT;
-       return(-1);
-}
-
-static unsigned long long lc_process_size(const char *value, const char **endptr) {
-       unsigned long long retval = 0;
-       char *mult = NULL;
-
-       retval = strtoll(value, &mult, 10);
-       if (mult != NULL) {
-               switch (tolower(mult[0])) {
-                       case 'p':
-                               retval *= 1125899906842624LLU;
-                               break;
-                       case 't':
-                               retval *= 1958505086976LLU;
-                               break;
-                       case 'g':
-                               retval *= 1073741824;
-                               break;
-                       case 'm':
-                               retval *= 1048576;
-                               break;
-                       case 'k':
-                               retval *= 1024;
-                               break;
-                       default:
-                               break;
-               }
-       }
-
-       return(retval);
-}
-
-static int lc_process_var_sizelonglong(void *data, const char *value, const char **endptr) {
-       long long *dataval;
-
-       dataval = data;
-       *dataval = lc_process_size(value, endptr);
-
-       return(0);
-}
-
-static int lc_process_var_sizelong(void *data, const char *value, const char **endptr) {
-       long *dataval;
-
-       dataval = data;
-       *dataval = lc_process_size(value, endptr);
-
-       return(0);
-}
-
-static int lc_process_var_sizeint(void *data, const char *value, const char **endptr) {
-       int *dataval;
-
-       dataval = data;
-       *dataval = lc_process_size(value, endptr);
-
-       return(0);
-}
-
-static int lc_process_var_sizeshort(void *data, const char *value, const char **endptr) {
-       short *dataval;
-
-       dataval = data;
-       *dataval = lc_process_size(value, endptr);
-
-       return(0);
-}
-
-static int lc_process_var_sizesizet(void *data, const char *value, const char **endptr) {
-       size_t *dataval;
-
-       dataval = data;
-       *dataval = lc_process_size(value, endptr);
-
-       return(0);
-}
-
-
-static int lc_handle_type(lc_var_type_t type, const char *value, void *data) {
-       const char *next;
-       int is_list;
-
-       is_list = type & LC_VAR_LIST;
-
-       if (is_list == LC_VAR_LIST) {
-       }
-
-       switch (type) {
-               case LC_VAR_STRING:
-                       return(lc_process_var_string(data, value, &next));
-                       break;
-               case LC_VAR_LONG_LONG:
-                       return(lc_process_var_longlong(data, value, &next));
-                       break;
-               case LC_VAR_LONG:
-                       return(lc_process_var_long(data, value, &next));
-                       break;
-               case LC_VAR_INT:
-                       return(lc_process_var_int(data, value, &next));
-                       break;
-               case LC_VAR_SHORT:
-                       return(lc_process_var_short(data, value, &next));
-                       break;
-               case LC_VAR_BOOL:
-                       return(lc_process_var_bool(data, value, &next));
-                       break;
-               case LC_VAR_SIZE_LONG_LONG:
-                       return(lc_process_var_sizelonglong(data, value, &next));
-                       break;
-               case LC_VAR_SIZE_LONG:
-                       return(lc_process_var_sizelong(data, value, &next));
-                       break;
-               case LC_VAR_SIZE_INT:
-                       return(lc_process_var_sizeint(data, value, &next));
-                       break;
-               case LC_VAR_SIZE_SHORT:
-                       return(lc_process_var_sizeshort(data, value, &next));
-                       break;
-               case LC_VAR_BOOL_BY_EXISTANCE:
-                       return(lc_process_var_bool_byexistance(data, value, &next));
-                       break;
-               case LC_VAR_SIZE_SIZE_T:
-                       return(lc_process_var_sizesizet(data, value, &next));
-                       break;
-               case LC_VAR_IP:
-               case LC_VAR_IP4:
-                       return(lc_process_var_ip4(data, value, &next));
-                       break;
-               case LC_VAR_IP6:
-                       return(lc_process_var_ip6(data, value, &next));
-                       break;
-               case LC_VAR_HOSTNAME4:
-                       return(lc_process_var_hostname4(data, value, &next));
-                       break;
-               case LC_VAR_HOSTNAME6:
-                       return(lc_process_var_hostname6(data, value, &next));
-                       break;
-               case LC_VAR_CIDR:
-                       return(lc_process_var_cidr(data, value, &next));
-                       break;
-               case LC_VAR_TIME:
-               case LC_VAR_DATE:
-               case LC_VAR_FILENAME:
-               case LC_VAR_DIRECTORY:
-#ifdef DEBUG
-                       fprintf(stderr, "Not implemented yet!\n");
-#endif
-                       return(-1);
-               case LC_VAR_NONE:
-               case LC_VAR_UNKNOWN:
-               case LC_VAR_SECTION:
-               case LC_VAR_SECTIONSTART:
-               case LC_VAR_SECTIONEND:
-                       return(0);
-                       break;
-       }
-
-       return(-1);
-}
-
-static int lc_handle(struct lc_varhandler_st *handler, const char *var, const char *varargs, const char *value, lc_flags_t flags) {
-       const char *localvar = NULL;
-       int retval;
-
-       if (var != NULL) {
-               localvar = strrchr(var, '.');
-               if (localvar == NULL) {
-                       localvar = var;
-               } else {
-                       localvar++;
-               }
-       } else {
-               localvar = NULL;
-       }
-
-       switch (handler->mode) {
-               case LC_MODE_CALLBACK:
-                       if (handler->callback != NULL) {
-                               retval = handler->callback(localvar, var, varargs, value, flags, handler->extra);
-                               if (retval < 0) {
-                                       lc_errno = LC_ERR_CALLBACK;
-                               }
-                               return(retval);
-                       }
-                       break;
-               case LC_MODE_VAR:
-                       return(lc_handle_type(handler->type, value, handler->data));
-                       break;
-       }
-
-       return(-1);
-}
-
-static int lc_process_environment(const char *appname) {
-#ifndef ENABLE_SMALL
-       struct lc_varhandler_st *handler = NULL;
-       size_t appnamelen = 0;
-       char varnamebuf[128] = {0};
-       char **currvar;
-       char *sep = NULL, *value = NULL, *cmd = NULL;
-       char *ucase_appname = NULL, *ucase_appname_itr = NULL;
-       char *lastcomponent_handler = NULL;
-       int varnamelen = 0;
-       char *local_lc_errfile;
-       int local_lc_errline;
-
-       /* Make sure we have an environment to screw with, if not,
-          no arguments were found to be in error */
-       if (environ == NULL || appname == NULL) {
-               return(0);
-       }
-
-       local_lc_errfile = "<environment>";
-       local_lc_errline = 0;
-
-       /* Allocate and create our uppercase appname. */
-       ucase_appname = strdup(appname);
-       if (ucase_appname == NULL) {
-               lc_errfile = local_lc_errfile;
-               lc_errline = local_lc_errline;
-               lc_errno = LC_ERR_ENOMEM;
-               return(-1);
-       }
-       for (ucase_appname_itr = ucase_appname; *ucase_appname_itr != '\0'; ucase_appname_itr++) {
-               *ucase_appname_itr = toupper(*ucase_appname_itr);
-       }
-
-       appnamelen = strlen(ucase_appname);
-
-       for (currvar = environ; *currvar != NULL; currvar++) {
-               /* If it doesn't begin with our appname ignore it completely. */
-               if (strncmp(*currvar, ucase_appname, appnamelen) != 0) {
-                       continue;
-               }
-
-               /* Find our seperator. */
-               sep = strchr(*currvar, '=');
-               if (sep == NULL) {
-                       continue;
-               }
-
-               varnamelen = sep - *currvar;
-
-               /* Skip variables that would overflow our buffer. */
-               if (varnamelen >= sizeof(varnamebuf)) {
-                       continue;
-               }
-
-               strncpy(varnamebuf, *currvar, varnamelen);
-
-               varnamebuf[varnamelen] = '\0';
-               value = sep + 1;
-
-               /* We ignore APPNAME by itself. */
-               if (strlen(varnamebuf) <= appnamelen) {
-                       continue;
-               }
-
-               /* Further it must be <APPNAME>_ */
-               if (varnamebuf[appnamelen] != '_') {
-                       continue;
-               }
-
-               cmd = varnamebuf + appnamelen + 1;
-
-               /* We don't allow section specifiers, for reasons see notes in
-                  the cmdline processor (below). */
-               if (strchr(cmd, '.') != NULL) {
-                       continue;
-               }
-
-               for (handler = varhandlers; handler != NULL; handler = handler->_next) {
-                       if (handler->var == NULL) {
-                               continue;
-                       }
-
-                       /* Skip handlers which don't agree with being
-                          processed outside a config file */
-                       if (handler->type == LC_VAR_SECTION ||
-                           handler->type == LC_VAR_SECTIONSTART ||
-                           handler->type == LC_VAR_SECTIONEND ||
-                           handler->type == LC_VAR_UNKNOWN) {
-                               continue;
-                       }
-
-                       /* Find the last part of the variable and compare it with 
-                          the option being processed, if a wildcard is given. */
-                       if (handler->var[0] == '*' && handler->var[1] == '.') {
-                               lastcomponent_handler = strrchr(handler->var, '.');
-                               if (lastcomponent_handler == NULL) {
-                                       lastcomponent_handler = handler->var;
-                               } else {
-                                       lastcomponent_handler++;
-                               }
-                       } else {
-                               lastcomponent_handler = handler->var;
-                       }
-
-                       /* Ignore this handler if they don't match. */
-                       if (strcasecmp(lastcomponent_handler, cmd) != 0) {
-                               continue;
-                       }
-
-                       if (handler->type == LC_VAR_NONE || handler->type == LC_VAR_BOOL_BY_EXISTANCE) {
-                               value = NULL;
-                       }
-
-                       /* We ignore errors from the environment variables,
-                          they're mostly insignificant. */
-                       lc_handle(handler, cmd, NULL, value, LC_FLAGS_ENVIRON);
-
-                       break;
-               }
-       }
-
-       free(ucase_appname);
-
-#endif
-       return(0);
-}
-
-static int lc_process_cmdline(int argc, char **argv) {
-       struct lc_varhandler_st *handler = NULL;
-       char *cmdarg = NULL, *cmdoptarg = NULL;
-       char *lastcomponent_handler = NULL;
-       char **newargv = NULL;
-       char *usedargv = NULL;
-       int cmdargidx = 0;
-       int newargvidx = 0;
-       int retval = 0, chkretval = 0;
-       int ch = 0;
-       char *local_lc_errfile;
-       int local_lc_errline;
-
-       local_lc_errfile = "<cmdline>";
-       local_lc_errline = 0;
-
-       /* Allocate "argc + 1" (+1 for the NULL terminator) elements. */
-       newargv = malloc((argc + 1) * sizeof(*newargv));
-       if (newargv == NULL) {
-               lc_errfile = local_lc_errfile;
-               lc_errline = local_lc_errline;
-               lc_errno = LC_ERR_ENOMEM;
-               return(-1);
-       }
-       newargv[newargvidx++] = argv[0];
-       newargv[argc] = NULL;
-
-       /* Allocate space to indicate which arguments have been used. */
-       usedargv = malloc(argc * sizeof(*usedargv));
-       if (usedargv == NULL) {
-               lc_errfile = local_lc_errfile;
-               lc_errline = local_lc_errline;
-               lc_errno = LC_ERR_ENOMEM;
-               free(newargv);
-               return(-1);
-       }
-       for (cmdargidx = 0; cmdargidx < argc; cmdargidx++) {
-               usedargv[cmdargidx] = 0;
-       }
-
-       for (cmdargidx = 1; cmdargidx < argc; cmdargidx++) {
-               cmdarg = argv[cmdargidx];
-
-               /* Make sure we have an argument here. */
-               if (cmdarg == NULL) {
-                       break;
-               }
-
-               /* If the argument isn't an option, skip. */
-               if (cmdarg[0] != '-') {
-                       continue;
-               }
-
-               /* Setup a pointer in the new array for the actual argument. */
-               newargv[newargvidx++] = cmdarg;
-               usedargv[cmdargidx] = 1;
-
-               /* Then shift the argument past the '-' so we can ignore it. */
-               cmdarg++;
-
-               /* Handle long options. */
-               if (cmdarg[0] == '-') {
-                       cmdarg++;
-
-                       /* Don't process arguments after the '--' option. */
-                       if (cmdarg[0] == '\0') {
-                               break;
-                       }
-
-                       /* Look for a variable name that matches */
-                       for (handler = varhandlers; handler != NULL; handler = handler->_next) {
-                               /* Skip handlers with no variable name. */
-                               if (handler->var == NULL) {
-                                       continue;
-                               }
-                               /* Skip handlers which don't agree with being
-                                  processed on the command line. */
-                               if (handler->type == LC_VAR_SECTION ||
-                                   handler->type == LC_VAR_SECTIONSTART ||
-                                   handler->type == LC_VAR_SECTIONEND ||
-                                   handler->type == LC_VAR_UNKNOWN) {
-                                       continue;
-                               }
-
-                               /* Find the last part of the variable and compare it with 
-                                  the option being processed, if a wildcard is given. */
-                               if (handler->var[0] == '*' && handler->var[1] == '.') {
-                                       lastcomponent_handler = strrchr(handler->var, '.');
-                                       if (lastcomponent_handler == NULL) {
-                                               lastcomponent_handler = handler->var;
-                                       } else {
-                                               lastcomponent_handler++;
-                                       }
-                               } else {
-                                       /* Disallow use of the fully qualified name
-                                          since there was no sectionstart portion
-                                          we cannot allow it to handle children of it. */
-                                       if (strchr(cmdarg, '.') != NULL) {
-                                               continue;
-                                       }
-                                       lastcomponent_handler = handler->var;
-                               }
-
-                               /* Ignore this handler if they don't match. */
-                               if (strcasecmp(lastcomponent_handler, cmdarg) != 0) {
-                                       continue;
-                               }
-
-                               if (handler->type == LC_VAR_NONE || handler->type == LC_VAR_BOOL_BY_EXISTANCE) {
-                                       cmdoptarg = NULL;
-                               } else {
-                                       cmdargidx++;
-                                       if (cmdargidx >= argc) {
-                                               fprintf(stderr, "Argument required.\n");
-                                               lc_errfile = local_lc_errfile;
-                                               lc_errline = local_lc_errline;
-                                               lc_errno = LC_ERR_BADFORMAT;
-                                               free(usedargv);
-                                               free(newargv);
-                                               return(-1);
-                                       }
-                                       cmdoptarg = argv[cmdargidx];
-                                       newargv[newargvidx++] = cmdoptarg;
-                                       usedargv[cmdargidx] = 1;
-                               }
-
-                               chkretval = lc_handle(handler, handler->var, NULL, cmdoptarg, LC_FLAGS_CMDLINE);
-                               if (chkretval < 0) {
-                                       retval = -1;
-                               }
-
-                               break;
-                       }
-
-                       if (handler == NULL) {
-                               fprintf(stderr, "Unknown option: --%s\n", cmdarg);
-                               lc_errfile = local_lc_errfile;
-                               lc_errline = local_lc_errline;
-                               lc_errno = LC_ERR_INVCMD;
-                               free(usedargv);
-                               free(newargv);
-                               return(-1);
-                       }
-               } else {
-                       for (; *cmdarg != '\0'; cmdarg++) {
-                               ch = *cmdarg;
-
-                               for (handler = varhandlers; handler != NULL; handler = handler->_next) {
-                                       if (handler->opt != ch || handler->opt == '\0') {
-                                               continue;
-                                       }
-                                       /* Skip handlers which don't agree with being
-                                          processed on the command line. */
-                                       if (handler->type == LC_VAR_SECTION ||
-                                           handler->type == LC_VAR_SECTIONSTART ||
-                                           handler->type == LC_VAR_SECTIONEND ||
-                                           handler->type == LC_VAR_UNKNOWN) {
-                                               continue;
-                                       }
-
-                                       if (handler->type == LC_VAR_NONE || handler->type == LC_VAR_BOOL_BY_EXISTANCE) {
-                                               cmdoptarg = NULL;
-                                       } else {
-                                               cmdargidx++;
-                                               if (cmdargidx >= argc) {
-                                                       fprintf(stderr, "Argument required.\n");
-                                                       lc_errfile = local_lc_errfile;
-                                                       lc_errline = local_lc_errline;
-                                                       lc_errno = LC_ERR_BADFORMAT;
-                                                       free(usedargv);
-                                                       free(newargv);
-                                                       return(-1);
-                                               }
-                                               cmdoptarg = argv[cmdargidx];
-                                               newargv[newargvidx++] = cmdoptarg;
-                                               usedargv[cmdargidx] = 1;
-                                       }
-
-                                       chkretval = lc_handle(handler, handler->var, NULL, cmdoptarg, LC_FLAGS_CMDLINE);
-                                       if (chkretval < 0) {
-                                               lc_errfile = local_lc_errfile;
-                                               lc_errline = local_lc_errline;
-                                               retval = -1;
-                                       }
-
-                                       break;
-                               }
-
-                               if (handler == NULL) {
-                                       fprintf(stderr, "Unknown option: -%c\n", ch);
-                                       lc_errfile = local_lc_errfile;
-                                       lc_errline = local_lc_errline;
-                                       lc_errno = LC_ERR_INVCMD;
-                                       free(usedargv);
-                                       free(newargv);
-                                       return(-1);
-                               }
-                       }
-               }
-       }
-
-       if (retval >= 0) {
-               lc_optind = newargvidx;
-               for (cmdargidx = 1; cmdargidx < argc; cmdargidx++) {
-                       if (usedargv[cmdargidx] != 0) {
-                               continue;
-                       }
-                       
-                       cmdarg = argv[cmdargidx];
-
-                       newargv[newargvidx++] = cmdarg;
-               }
-               for (cmdargidx = 1; cmdargidx < argc; cmdargidx++) {
-                       argv[cmdargidx] = newargv[cmdargidx];
-               }
-       }
-
-       free(usedargv);
-       free(newargv);
-
-       return(retval);
-}
-
-
-int lc_process_var(const char *var, const char *varargs, const char *value, lc_flags_t flags) {
-       struct lc_varhandler_st *handler = NULL;
-       const char *lastcomponent_handler = NULL, *lastcomponent_var = NULL;
-
-       lastcomponent_var = strrchr(var, '.');
-       if (lastcomponent_var == NULL) {
-               lastcomponent_var = var;
-       } else {
-               lastcomponent_var++;
-       }
-
-       for (handler = varhandlers; handler != NULL; handler = handler->_next) {
-               /* If either handler->var or var is NULL, skip, unless both are NULL. */
-               if (handler->var != var && (handler->var == NULL || var == NULL)) {
-                       continue;
-               }
-
-               /* If both are not-NULL, compare them. */
-               if (handler->var != NULL) {
-                       /* Wild-card-ish match. */
-                       if (handler->var[0] == '*' && handler->var[1] == '.') {
-                               /* Only compare the last components */
-
-                               lastcomponent_handler = strrchr(handler->var, '.') + 1; /* strrchr() won't return NULL, because we already checked it. */
-
-                               if (strcasecmp(lastcomponent_handler, lastcomponent_var) != 0) {
-                                       continue;
-                               }
-                       } else if (strcasecmp(handler->var, var) != 0) {
-                               /* Exact (case-insensitive comparison) failed. */
-                               continue;
-                       }
-               }
-
-               if (value == NULL &&
-                   handler->type != LC_VAR_NONE &&
-                   handler->type != LC_VAR_BOOL_BY_EXISTANCE &&
-                   handler->type != LC_VAR_SECTION &&
-                   handler->type != LC_VAR_SECTIONSTART &&
-                   handler->type != LC_VAR_SECTIONEND) {
-                       lc_errno = LC_ERR_BADFORMAT;
-                       break;
-               }
-
-               return(lc_handle(handler, var, varargs, value, flags));
-       }
-
-       return(-1);
-}
-
-int lc_register_callback(const char *var, char opt, lc_var_type_t type, int (*callback)(const char *, const char *, const char *, const char *, lc_flags_t, void *), void *extra) {
-       struct lc_varhandler_st *newhandler = NULL;
-
-       newhandler = malloc(sizeof(*newhandler));
-
-       if (newhandler == NULL) {
-               return(-1);
-       }
-
-       if (var == NULL) {
-               newhandler->var = NULL;
-       } else {
-               newhandler->var = strdup(var);
-       }
-       newhandler->mode = LC_MODE_CALLBACK;
-       newhandler->type = type;
-       newhandler->callback = callback;
-       newhandler->opt = opt;
-       newhandler->extra = extra;
-       newhandler->_next = varhandlers;
-
-       varhandlers = newhandler;
-
-       return(0);
-}
-
-int lc_register_var(const char *var, lc_var_type_t type, void *data, char opt) {
-       struct lc_varhandler_st *newhandler = NULL;
-
-       newhandler = malloc(sizeof(*newhandler));
-
-       if (newhandler == NULL) {
-               return(-1);
-       }
-
-       if (var == NULL) {
-               newhandler->var = NULL;
-       } else {
-               newhandler->var = strdup(var);
-       }
-       newhandler->mode = LC_MODE_VAR;
-       newhandler->type = type;
-       newhandler->data = data;
-       newhandler->opt = opt;
-       newhandler->extra = NULL;
-       newhandler->_next = varhandlers;
-
-       varhandlers = newhandler;
-
-       return(0);
-}
-
-int lc_process_file(const char *appname, const char *pathname, lc_conf_type_t type) {
-       int chkretval = 0;
-
-       switch (type) {
-               case LC_CONF_SECTION:
-                       chkretval = lc_process_conf_section(appname, pathname);
-                       break;
-               case LC_CONF_APACHE:
-                       chkretval = lc_process_conf_apache(appname, pathname);
-                       break;
-               case LC_CONF_COLON:
-                       chkretval = lc_process_conf_colon(appname, pathname);
-                       break;
-               case LC_CONF_EQUAL:
-                       chkretval = lc_process_conf_equal(appname, pathname);
-                       break;
-               case LC_CONF_SPACE:
-                       chkretval = lc_process_conf_space(appname, pathname);
-                       break;
-               case LC_CONF_XML:
-                       chkretval = lc_process_conf_xml(appname, pathname);
-                       break;
-               default:
-                       chkretval = -1;
-                       lc_errno = LC_ERR_INVDATA;
-                       break;
-       }
-
-       return(chkretval);
-}
-
-static int lc_process_files(const char *appname, lc_conf_type_t type, const char *extraconfig) {
-#ifdef HAVE_GETPWUID
-       struct passwd *pwinfo = NULL;
-#endif
-       char configfiles[3][13][512] = {{{0}}};
-       char *configfile = NULL;
-       char *homedir = NULL;
-       int configsetidx = 0, configidx = 0;
-       int chkretval = 0, retval = 0;
-
-       if (extraconfig != NULL) {
-               snprintf(configfiles[0][0], sizeof(**configfiles) - 1, "%s", extraconfig);
-       }
-       snprintf(configfiles[1][0], sizeof(**configfiles) - 1, "/etc/%s.cfg", appname);
-       snprintf(configfiles[1][1], sizeof(**configfiles) - 1, "/etc/%s.conf", appname);
-       snprintf(configfiles[1][2], sizeof(**configfiles) - 1, "/etc/%s/%s.cfg", appname, appname);
-       snprintf(configfiles[1][3], sizeof(**configfiles) - 1, "/etc/%s/%s.conf", appname, appname);
-       snprintf(configfiles[1][4], sizeof(**configfiles) - 1, "/usr/etc/%s.cfg", appname);
-       snprintf(configfiles[1][5], sizeof(**configfiles) - 1, "/usr/etc/%s.conf", appname);
-       snprintf(configfiles[1][6], sizeof(**configfiles) - 1, "/usr/etc/%s/%s.cfg", appname, appname);
-       snprintf(configfiles[1][7], sizeof(**configfiles) - 1, "/usr/etc/%s/%s.conf", appname, appname);
-       snprintf(configfiles[1][8], sizeof(**configfiles) - 1, "/usr/local/etc/%s.cfg", appname);
-       snprintf(configfiles[1][9], sizeof(**configfiles) - 1, "/usr/local/etc/%s.conf", appname);
-       snprintf(configfiles[1][10], sizeof(**configfiles) - 1, "/usr/local/etc/%s/%s.cfg", appname, appname);
-       snprintf(configfiles[1][11], sizeof(**configfiles) - 1, "/usr/local/etc/%s/%s.conf", appname, appname);
-       if (getuid() != 0) {
-               homedir = getenv("HOME");
-#ifdef HAVE_GETPWUID
-               if (homedir == NULL) {
-                       pwinfo = getpwuid(getuid());
-                       if (pwinfo != NULL) {
-                               homedir = pwinfo->pw_dir;
-                       }
-               }
-#endif
-               if (homedir != NULL) {
-                       if (strcmp(homedir, "/") != 0 && access(homedir, R_OK|W_OK|X_OK) == 0) {
-                               snprintf(configfiles[2][0], sizeof(**configfiles) - 1, "%s/.%src", homedir, appname);
-                               snprintf(configfiles[2][1], sizeof(**configfiles) - 1, "%s/.%s.cfg", homedir, appname);
-                               snprintf(configfiles[2][2], sizeof(**configfiles) - 1, "%s/.%s.conf", homedir, appname);
-                               snprintf(configfiles[2][3], sizeof(**configfiles) - 1, "%s/.%s/%s.cfg", homedir, appname, appname);
-                               snprintf(configfiles[2][4], sizeof(**configfiles) - 1, "%s/.%s/%s.conf", homedir, appname, appname);
-                               snprintf(configfiles[2][5], sizeof(**configfiles) - 1, "%s/.%s/config", homedir, appname);
-                       }
-               }
-       }
-
-       for (configsetidx = 0; configsetidx < 3; configsetidx++) {
-               for (configidx = 0; configidx < 13; configidx++) {
-                       configfile = configfiles[configsetidx][configidx];
-                       if (configfile[0] == '\0') {
-                               break;
-                       }
-                       if (access(configfile, R_OK) == 0) {
-                               chkretval = lc_process_file(appname, configfile, type);
-                               if (chkretval < 0) {
-                                       retval = -1;
-                               }
-                               break;
-                       }
-               }
-       }
-
-       return(retval);
-}
-
-void lc_cleanup(void) {
-       struct lc_varhandler_st *handler = NULL, *next = NULL;
-
-       handler = varhandlers;
-       while (handler != NULL) {
-               if (handler->var != NULL) {
-                       free(handler->var);
-               }
-
-               next = handler->_next;
-
-               free(handler);
-
-               handler = next;
-       }
-
-       varhandlers = NULL;
-
-       return;
-}
-
-int lc_process(int argc, char **argv, const char *appname, lc_conf_type_t type, const char *extra) {
-       int retval = 0, chkretval = 0;
-
-       /* Handle config files. */
-       chkretval = lc_process_files(appname, type, extra);
-       if (chkretval < 0) {
-               retval = -1;
-       }
-
-       /* Handle environment variables.*/
-       chkretval = lc_process_environment(appname);
-       if (chkretval < 0) {
-               retval = -1;
-       }
-
-       /* Handle command line arguments */
-       chkretval = lc_process_cmdline(argc, argv);
-       if (chkretval < 0) {
-               retval = -1;
-       }
-
-       return(retval);
-}
-
-
-lc_err_t lc_geterrno(void) {
-       lc_err_t retval;
-
-       retval = lc_errno;
-
-       lc_errno = LC_ERR_NONE;
-
-       return(retval);
-}
-
-char *lc_geterrstr(void) {
-       static char retval[512];
-       char *errmsg = NULL;
-
-       switch (lc_errno) {
-               case LC_ERR_NONE:
-                       errmsg = "Success";
-                       break;
-               case LC_ERR_INVCMD:
-                       errmsg = "Invalid command or option";
-                       break;
-               case LC_ERR_INVSECTION:
-                       errmsg = "Invalid section";
-                       break;
-               case LC_ERR_INVDATA:
-                       errmsg = "Invalid application data (internal error)";
-                       break;
-               case LC_ERR_BADFORMAT:
-                       errmsg = "Bad data specified or incorrect format.";
-                       break;
-               case LC_ERR_CANTOPEN:
-                       errmsg = "Can't open file.";
-                       break;
-               case LC_ERR_CALLBACK:
-                       errmsg = "Error return from application handler.";
-                       break;
-               case LC_ERR_ENOMEM:
-                       errmsg = "Insuffcient memory.";
-                       break;
-       }
-
-       /*
-        * This is not part of the switch statement so we will get warnings
-        * about unhandled enum values.
-        */
-       if (errmsg == NULL) {
-               errmsg = "Unknown error";
-       }
-
-       if (lc_errfile == NULL) {
-               snprintf(retval, sizeof(retval), "%s:%i: %s", "<no file>", lc_errline, errmsg);
-       } else {
-               snprintf(retval, sizeof(retval), "%s:%i: %s", lc_errfile, lc_errline, errmsg);
-       }
-
-       retval[sizeof(retval) - 1] = '\0';
-
-       return(retval);
-}
diff --git a/src/libconfig/libconfig.h b/src/libconfig/libconfig.h
deleted file mode 100644 (file)
index 8c2f361..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-#  ifndef _RSK_LIBCONFIG_H
-#  define _RSK_LIBCONFIG_H
-#  ifdef __cplusplus
-extern "C" {
-#  endif
-
-#  define LC_VAR_LIST 0x80
-
-typedef enum {
-        LC_CONF_SECTION,
-        LC_CONF_APACHE,
-        LC_CONF_COLON,
-        LC_CONF_EQUAL,
-        LC_CONF_SPACE,
-        LC_CONF_XML
-} lc_conf_type_t;
-
-typedef enum {
-        LC_VAR_UNKNOWN,
-        LC_VAR_NONE,
-        LC_VAR_STRING,
-        LC_VAR_LONG_LONG,
-        LC_VAR_LONG,
-        LC_VAR_INT,
-        LC_VAR_SHORT,
-        LC_VAR_BOOL,
-        LC_VAR_FILENAME,
-        LC_VAR_DIRECTORY,
-        LC_VAR_SIZE_LONG_LONG,
-        LC_VAR_SIZE_LONG,
-        LC_VAR_SIZE_INT,
-        LC_VAR_SIZE_SHORT,
-        LC_VAR_TIME,
-        LC_VAR_DATE,
-        LC_VAR_SECTION,
-        LC_VAR_SECTIONSTART,
-        LC_VAR_SECTIONEND,
-        LC_VAR_BOOL_BY_EXISTANCE,
-        LC_VAR_SIZE_SIZE_T,
-        LC_VAR_CIDR,
-        LC_VAR_IP,
-        LC_VAR_IP4,
-        LC_VAR_IP6,
-        LC_VAR_HOSTNAME4,
-        LC_VAR_HOSTNAME6,
-} lc_var_type_t;
-
-typedef enum {
-        LC_FLAGS_VAR,
-        LC_FLAGS_CMDLINE,
-        LC_FLAGS_ENVIRON,
-        LC_FLAGS_SECTIONSTART,
-        LC_FLAGS_SECTIONEND
-} lc_flags_t;
-
-typedef enum {
-        LC_ERR_NONE,
-        LC_ERR_INVCMD,
-        LC_ERR_INVSECTION,
-        LC_ERR_INVDATA,
-        LC_ERR_BADFORMAT,
-        LC_ERR_CANTOPEN,
-        LC_ERR_CALLBACK,
-        LC_ERR_ENOMEM
-} lc_err_t;
-
-int lc_process(int argc, char **argv, const char *appname, lc_conf_type_t type, const char *extra);
-int lc_register_callback(const char *var, char opt, lc_var_type_t type, int (*callback)(const char *, const char *, const char *, const char *, lc_flags_t, void *), void *extra);
-int lc_register_var(const char *var, lc_var_type_t type, void *data, char opt);
-lc_err_t lc_geterrno(void);
-char *lc_geterrstr(void);
-int lc_process_file(const char *appname, const char *pathname, lc_conf_type_t type);
-void lc_cleanup(void);
-
-#  define LC_CBRET_IGNORESECTION (255)
-#  define LC_CBRET_OKAY (0)
-#  define LC_CBRET_ERROR (-1)
-
-extern int lc_optind;
-
-#  ifdef __cplusplus
-}
-#  endif
-#  endif
diff --git a/src/libconfig/libconfig_private.h b/src/libconfig/libconfig_private.h
deleted file mode 100644 (file)
index 8310a85..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef _RSK_LIBCONFIG_PRIVATE_H
-#define _RSK_LIBCONFIG_PRIVATE_H
-
-#include "libconfig.h"
-
-extern lc_err_t lc_errno;
-extern int lc_errline;
-extern const char *lc_errfile;
-
-int lc_process_var(const char *var, const char *varargs, const char *value, lc_flags_t flags);
-
-struct lc_varhandler_st;
-
-struct lc_varhandler_st {
-       struct lc_varhandler_st *_next;
-       char *var;
-       char opt;
-       lc_var_type_t type;
-       enum {
-               LC_MODE_CALLBACK,
-               LC_MODE_VAR
-       } mode;
-       int (*callback)();
-       void *data;
-       void *extra;
-};
-
-#endif
diff --git a/src/libconfig/test-lc.c b/src/libconfig/test-lc.c
deleted file mode 100644 (file)
index ed492d8..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-#include "compat.h"
-#include "libconfig.h"
-
-int help_cmd(const char *partarg, const char *arg, const char *argarg, const char *val, lc_flags_t flags, void *extra) {
-       printf("Usage info goes here\n");
-       printf("\n");
-
-       exit(EXIT_FAILURE);
-}
-int sally_cmd(const char *partarg, const char *arg, const char *argarg, const char *val, lc_flags_t flags, void *extra) {
-       fprintf(stderr, "%s sets value: \"%s\" (flags=%i)\n", arg, val, flags);
-       return(0);
-}
-
-int cmd_ifmodule(const char *partarg, const char *arg, const char *argarg, const char *val, lc_flags_t flags, void *extra) {
-       if (flags == LC_FLAGS_SECTIONEND) {
-               return(LC_CBRET_OKAY);
-       }
-       if (flags != LC_FLAGS_SECTIONSTART) {
-               fprintf(stderr, "IfModule can only be used as a section.\n");
-               return(LC_CBRET_ERROR);
-       }
-       if (argarg == NULL) {
-               fprintf(stderr, "You must specify an argument to IfModule.\n");
-               return(LC_CBRET_ERROR);
-       }
-
-       fprintf(stderr, "IfModule (%s)\n", argarg);
-       return(LC_CBRET_IGNORESECTION);
-}
-
-int main(int argc, char **argv) {
-       char *joeval = NULL;
-       size_t xval = -1;
-       int onoff = -1;
-       int lcpret = -1;
-       int i = 0;
-       int onoff2 = 0;
-       uint32_t ipaddr = 0;
-
-       lc_register_var("Section", LC_VAR_SECTION, NULL, 0);
-       lc_register_var("Somesection", LC_VAR_SECTION, NULL, 0);
-       lc_register_var("Section.Test", LC_VAR_STRING, &joeval, 'j');
-       lc_register_var("bob", LC_VAR_SIZE_SIZE_T, &xval, 's');
-       lc_register_var("Somesection.Free", LC_VAR_BOOL, &onoff, 0);
-       lc_register_var("long", LC_VAR_BOOL_BY_EXISTANCE, &onoff2, 'l');
-       lc_register_var("ipaddr", LC_VAR_IP, &ipaddr, 'i');
-       lc_register_callback("sally", 0, LC_VAR_STRING, sally_cmd, NULL);
-       lc_register_callback("HELP", 'h', LC_VAR_NONE, help_cmd, NULL);
-       lc_register_callback("*.ifmodule", 0, LC_VAR_NONE, cmd_ifmodule, NULL);
-       lcpret = lc_process_file("testapp", "build/test.conf", LC_CONF_APACHE);
-       if (lcpret < 0) {
-               fprintf(stderr, "Error processing config file: %s\n", lc_geterrstr());
-               return(EXIT_FAILURE);
-       }
-
-       lcpret = lc_process(argc, argv, "testapp", LC_CONF_APACHE, "test.cfg");
-       if (lcpret < 0) {
-               fprintf(stderr, "Error processing config file: %s\n", lc_geterrstr());
-               return(EXIT_FAILURE);
-       }
-
-       lc_cleanup();
-
-       if (joeval != NULL) {
-               fprintf(stderr, "joeval = \"%s\"\n", joeval);
-       } else {
-               fprintf(stderr, "joeval = \"(null)\"\n");
-       }
-       fprintf(stderr, "xval = %llu\n", (unsigned long long) xval);
-       fprintf(stderr, "onoff = %i\n", onoff);
-       fprintf(stderr, "long = %i\n", onoff2);
-       fprintf(stderr, "ip = %08lx\n", (unsigned long) ipaddr);
-       for (i = lc_optind; i < argc; i++) {
-               fprintf(stderr, "argv[%i] = \"%s\"\n", i, argv[i]);
-       }
-
-       return(0);
-}
diff --git a/src/libconfig/win32.h b/src/libconfig/win32.h
deleted file mode 100644 (file)
index 06f3a0d..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#if !defined(_LOCAL_WIN32_H) && defined(__WIN32__) && !defined(__CYGWIN__)
-#define _LOCAL_WIN32_H
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifdef __MINGW32__
-     /* MingW32 specific stuff here. */
-#  if defined(HAVE_WINSOCK2_H) && defined(HAVE_LIBWSOCK32)
-     /* We have to override the detected configuration
-        because it can't detect the network libraries. */
-#    define HAVE_GETHOSTBYNAME 1
-#    define HAVE_INET_ADDR 1
-#    define HAVE_SOCKET 1
-#  endif
-#  define sleep(x) Sleep((x) * 1000)
-#else
-   /* MSVC++ configuration follows */
-#  undef HAVE_UNISTD_H
-#  define HAVE_STDLIB_H 1
-#  define HAVE_WINDOWS_H 1
-#  define HAVE_STDARG_H 1
-#  define HAVE_WINSVC_H 1
-#  define HAVE_WINSOCK2_H 1
-#  define HAVE_WINDOWSX_H 1
-#endif /* __MINGW32__ */
-
-#ifdef HAVE_WINDOWS_H
-#  include <windows.h>
-#endif
-#ifdef HAVE_WINDOWSX_H
-#  include <windowsx.h>
-#endif
-#ifdef HAVE_WINSOCK2_H
-#  include <winsock2.h>
-#endif
-#ifdef HAVE_WINSVC_H
-#  include <winsvc.h>
-#endif
-
-#define _USE_WIN32_ 1
-
-#endif /* _LOCAL_WIN32_H */
diff --git a/src/liboconfig/AUTHORS b/src/liboconfig/AUTHORS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/liboconfig/COPYING b/src/liboconfig/COPYING
new file mode 100644 (file)
index 0000000..623b625
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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; either version 2 of the License, or
+    (at your option) any later version.
+
+    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 Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/src/liboconfig/ChangeLog b/src/liboconfig/ChangeLog
new file mode 100644 (file)
index 0000000..390c507
--- /dev/null
@@ -0,0 +1,5 @@
+2007-02-15, Version 0.1.1
+       * src/parser.y: Fixes a memory leak.
+
+2007-02-11, Version 0.1.0
+       * Initial release.
diff --git a/src/liboconfig/Makefile.am b/src/liboconfig/Makefile.am
new file mode 100644 (file)
index 0000000..c3de92c
--- /dev/null
@@ -0,0 +1,10 @@
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+BUILT_SOURCES = parser.h
+#CLEANFILES = parser.[ch] scanner.c
+AM_YFLAGS = -d
+
+noinst_LTLIBRARIES = liboconfig.la
+
+liboconfig_la_LDFLAGS = -version-info 0:0:0 $(LEXLIB)
+liboconfig_la_SOURCES = oconfig.c oconfig.h aux_types.h scanner.l parser.y
diff --git a/src/liboconfig/aux_types.h b/src/liboconfig/aux_types.h
new file mode 100644 (file)
index 0000000..25b81ab
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef AUX_TYPES_H
+#define AUX_TYPES_H 1
+
+struct statement_list_s
+{
+       oconfig_item_t *statement;
+       int             statement_num;
+};
+typedef struct statement_list_s statement_list_t;
+
+struct argument_list_s
+{
+       oconfig_value_t *argument;
+       int              argument_num;
+};
+typedef struct argument_list_s argument_list_t;
+
+#endif /* AUX_TYPES_H */
diff --git a/src/liboconfig/oconfig.c b/src/liboconfig/oconfig.c
new file mode 100644 (file)
index 0000000..2bb80cc
--- /dev/null
@@ -0,0 +1,88 @@
+/**
+ * oconfig - src/oconfig.c
+ * Copyright (C) 2006,2007  Florian octo Forster <octo at verplant.org>
+ *
+ * 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
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "oconfig.h"
+
+extern FILE *yyin;
+
+oconfig_item_t *ci_root;
+
+static void yyset_in  (FILE *fd)
+{
+  yyin = fd;
+} /* void yyset_in */
+
+oconfig_item_t *oconfig_parse_fh (FILE *fh)
+{
+  int status;
+  oconfig_item_t *ret;
+
+  yyset_in (fh);
+
+  status = yyparse ();
+  if (status != 0)
+  {
+    fprintf (stderr, "yyparse returned error #%i\n", status);
+    return (NULL);
+  }
+
+  ret = ci_root;
+  ci_root = NULL;
+  yyset_in ((FILE *) 0);
+
+  return (ret);
+} /* oconfig_item_t *oconfig_parse_fh */
+
+oconfig_item_t *oconfig_parse_file (const char *file)
+{
+  FILE *fh;
+  oconfig_item_t *ret;
+
+  fh = fopen (file, "r");
+  if (fh == NULL)
+  {
+    fprintf (stderr, "fopen (%s) failed: %s\n", file, strerror (errno));
+    return (NULL);
+  }
+
+  ret = oconfig_parse_fh (fh);
+  fclose (fh);
+
+  return (ret);
+} /* oconfig_item_t *oconfig_parse_file */
+
+void oconfig_free (oconfig_item_t *ci)
+{
+  int i;
+
+  if (ci->values != NULL)
+    free (ci->values);
+
+  for (i = 0; i < ci->children_num; i++)
+    oconfig_free (ci->children + i);
+}
+
+/*
+ * vim:shiftwidth=2:tabstop=8:softtabstop=2
+ */
diff --git a/src/liboconfig/oconfig.h b/src/liboconfig/oconfig.h
new file mode 100644 (file)
index 0000000..e6e53e2
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef OCONFIG_H
+#define OCONFIG_H 1
+
+#include <stdio.h>
+
+/**
+ * oconfig - src/oconfig.h
+ * Copyright (C) 2006,2007  Florian octo Forster <octo at verplant.org>
+ *
+ * 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
+ */
+
+/*
+ * Types
+ */
+#define OCONFIG_TYPE_STRING  0
+#define OCONFIG_TYPE_NUMBER  1
+#define OCONFIG_TYPE_BOOLEAN 2
+
+struct oconfig_value_s
+{
+  union
+  {
+    char  *string;
+    double number;
+    int    boolean;
+  } value;
+  int type;
+};
+typedef struct oconfig_value_s oconfig_value_t;
+
+struct oconfig_item_s;
+typedef struct oconfig_item_s oconfig_item_t;
+struct oconfig_item_s
+{
+  char            *key;
+  oconfig_value_t *values;
+  int              values_num;
+
+  oconfig_item_t  *parent;
+  oconfig_item_t  *children;
+  int              children_num;
+};
+
+/*
+ * Functions
+ */
+oconfig_item_t *oconfig_parse_fh (FILE *fh);
+oconfig_item_t *oconfig_parse_file (const char *file);
+
+void oconfig_free (oconfig_item_t *ci);
+
+/*
+ * vim: shiftwidth=2:tabstop=8:softtabstop=2
+ */
+#endif /* OCONFIG_H */
diff --git a/src/liboconfig/parser.y b/src/liboconfig/parser.y
new file mode 100644 (file)
index 0000000..ea6ed0a
--- /dev/null
@@ -0,0 +1,221 @@
+/**
+ * oconfig - src/parser.y
+ * Copyright (C) 2007  Florian octo Forster <octo at verplant.org>
+ *
+ * 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
+ */
+
+%{
+#include <stdlib.h>
+#include <string.h>
+#include "oconfig.h"
+#include "aux_types.h"
+
+static char *unquote (const char *orig);
+static int yyerror (const char *s);
+
+/* Lexer variables */
+extern int yylineno;
+extern char *yytext;
+
+extern oconfig_item_t *ci_root;
+%}
+
+%start entire_file
+
+%union {
+       double  number;
+       int     boolean;
+       char   *string;
+       oconfig_value_t  cv;
+       oconfig_item_t   ci;
+       argument_list_t  al;
+       statement_list_t sl;
+}
+
+%token <number> NUMBER
+%token <boolean> TRUE FALSE
+%token <string> QUOTED_STRING UNQUOTED_STRING
+%token SLASH OPENBRAC CLOSEBRAC EOL
+
+%type <string> string
+%type <string> identifier
+/* arguments */
+%type <cv> argument
+%type <al> argument_list
+/* blocks */
+%type <ci> block_begin
+%type <ci> block
+%type <string> block_end
+/* statements */
+%type <ci> option
+%type <ci> statement
+%type <sl> statement_list
+%type <ci> entire_file
+
+%%
+string:
+       QUOTED_STRING           {$$ = unquote ($1);}
+       | UNQUOTED_STRING       {$$ = strdup ($1);}
+       ;
+
+argument:
+       NUMBER                  {$$.value.number = $1; $$.type = OCONFIG_TYPE_NUMBER;}
+       | TRUE                  {$$.value.boolean = 1; $$.type = OCONFIG_TYPE_BOOLEAN;}
+       | FALSE                 {$$.value.boolean = 0; $$.type = OCONFIG_TYPE_BOOLEAN;}
+       | string                {$$.value.string = $1; $$.type = OCONFIG_TYPE_STRING;}
+       ;
+
+argument_list:
+       argument_list argument
+       {
+        $$ = $1;
+        $$.argument_num++;
+        $$.argument = realloc ($$.argument, $$.argument_num * sizeof (oconfig_value_t));
+        $$.argument[$$.argument_num-1] = $2;
+       }
+       | argument
+       {
+        $$.argument = malloc (sizeof (oconfig_value_t));
+        $$.argument[0] = $1;
+        $$.argument_num = 1;
+       }
+       ;
+
+identifier:
+       UNQUOTED_STRING                 {$$ = strdup ($1);}
+       ;
+
+option:
+       identifier argument_list EOL
+       {
+        memset (&$$, '\0', sizeof ($$));
+        $$.key = $1;
+        $$.values = $2.argument;
+        $$.values_num = $2.argument_num;
+       }
+       ;
+
+block_begin:
+       OPENBRAC identifier argument_list CLOSEBRAC EOL
+       {
+        memset (&$$, '\0', sizeof ($$));
+        $$.key = $2;
+        $$.values = $3.argument;
+        $$.values_num = $3.argument_num;
+       }
+       ;
+
+block_end:
+       OPENBRAC SLASH identifier CLOSEBRAC EOL
+       {
+        $$ = $3;
+       }
+       ;
+
+block:
+       block_begin statement_list block_end
+       {
+        if (strcmp ($1.key, $3) != 0)
+        {
+               printf ("block_begin = %s; block_end = %s;\n", $1.key, $3);
+               yyerror ("Block not closed..\n");
+               exit (1);
+        }
+        free ($3); $3 = NULL;
+        $$ = $1;
+        $$.children = $2.statement;
+        $$.children_num = $2.statement_num;
+       }
+       ;
+
+statement:
+       option          {$$ = $1;}
+       | block         {$$ = $1;}
+       | EOL           {$$.values_num = 0;}
+       ;
+
+statement_list:
+       statement_list statement
+       {
+        $$ = $1;
+        if ($2.values_num > 0)
+        {
+                $$.statement_num++;
+                $$.statement = realloc ($$.statement, $$.statement_num * sizeof (oconfig_item_t));
+                $$.statement[$$.statement_num-1] = $2;
+        }
+       }
+       | statement
+       {
+        if ($1.values_num > 0)
+        {
+                $$.statement = malloc (sizeof (oconfig_item_t));
+                $$.statement[0] = $1;
+                $$.statement_num = 1;
+        }
+        else
+        {
+               $$.statement = NULL;
+               $$.statement_num = 0;
+        }
+       }
+       ;
+
+entire_file:
+       statement_list
+       {
+        ci_root = malloc (sizeof (oconfig_item_t));
+        memset (ci_root, '\0', sizeof (oconfig_item_t));
+        ci_root->children = $1.statement;
+        ci_root->children_num = $1.statement_num;
+       }
+       ;
+
+%%
+static int yyerror (const char *s)
+{
+       fprintf (stderr, "Error in line %i near `%s': %s\n", yylineno, yytext, s);
+       return (-1);
+} /* int yyerror */
+
+static char *unquote (const char *orig)
+{
+       char *ret = strdup (orig);
+       int len;
+       int i;
+
+       if (ret == NULL)
+               return (NULL);
+
+       len = strlen (ret);
+
+       if ((len < 2) || (ret[0] != '"') || (ret[len - 1] != '"'))
+               return (ret);
+
+       ret++;
+       len -= 2;
+       ret[len] = '\0';
+
+       for (i = 0; i < len; i++)
+       {
+               if (ret[i] == '\\')
+               {
+                       memmove (ret + i, ret + (i + 1), len - i);
+                       len--;
+               }
+       }
+
+       return (ret);
+} /* char *unquote */
diff --git a/src/liboconfig/scanner.l b/src/liboconfig/scanner.l
new file mode 100644 (file)
index 0000000..646bbc6
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * oconfig - src/scanner.l
+ * Copyright (C) 2007  Florian octo Forster <octo at verplant.org>
+ *
+ * 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
+ */
+
+%{
+#include <stdlib.h>
+#include "oconfig.h"
+#include "aux_types.h"
+#include "parser.h"
+%}
+%option yylineno
+WHITE_SPACE [\ \t\b]
+QUOTED_STRING \"([^\\"]+|\\.)*\"
+UNQUOTED_STRING [0-9A-Za-z_]+
+HEX_NUMBER 0[xX][0-9a-fA-F]+
+OCT_NUMBER 0[0-7]+
+DEC_NUMBER [\+\-]?[0-9]+
+FLOAT_NUMBER [\+\-]?[0-9]*\.[0-9]+([eE][\+\-][0-9]+)?
+NUMBER ({FLOAT_NUMBER}|{HEX_NUMBER}|{OCT_NUMBER}|{DEC_NUMBER})
+BOOL_TRUE (true|yes|on)
+BOOL_FALSE (false|no|off)
+COMMENT #.*
+PORT (6(5(5(3[0-5]|[0-2][0-9])|[0-4][0-9][0-9])|[0-4][0-9][0-9][0-9])|[1-5][0-9][0-9][0-9][0-9]|[1-9][0-9]?[0-9]?[0-9]?)
+IP_BYTE (2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])
+IPV4_ADDR {IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}(:{PORT})?
+
+%%
+{WHITE_SPACE}          |
+{COMMENT}              {/* ignore */}
+
+\n                     {return (EOL);}
+"/"                    {return (SLASH);}
+"<"                    {return (OPENBRAC);}
+">"                    {return (CLOSEBRAC);}
+{BOOL_TRUE}            {yylval.boolean = 1; return (TRUE);}
+{BOOL_FALSE}           {yylval.boolean = 0; return (FALSE);}
+
+{IPV4_ADDR}            {yylval.string = yytext; return (UNQUOTED_STRING);}
+
+{NUMBER}               {yylval.number = strtod (yytext, NULL); return (NUMBER);}
+
+{QUOTED_STRING}                {yylval.string = yytext; return (QUOTED_STRING);}
+{UNQUOTED_STRING}      {yylval.string = yytext; return (UNQUOTED_STRING);}
+%%
index 36b7943..1b00410 100644 (file)
@@ -9,7 +9,7 @@ EXTRA_DIST = AUTHORS COPYING ChangeLog README
 noinst_LTLIBRARIES = liboping.la
 
 #liboping_la_CFLAGS = 
-liboping_la_LDFLAGS = -avoid-version
+liboping_la_LDFLAGS = -version-info 0:3:0
 if BUILD_WITH_LIBSOCKET
 liboping_la_LDFLAGS += -lsocket
 endif
index 019f952..5e1d9d3 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -24,8 +23,6 @@
 #include "common.h"
 #include "plugin.h"
 
-#define MODULE_NAME "load"
-
 #if defined(HAVE_GETLOADAVG) || defined(KERNEL_LINUX) || defined(HAVE_LIBSTATGRAB)
 # define LOAD_HAVE_READ 1
 #else
 #endif
 #endif /* defined(HAVE_GETLOADAVG) */
 
-static char *load_file = "load.rrd";
-
-static char *ds_def[] =
-{
-       "DS:shortterm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-       "DS:midterm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-       "DS:longterm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-       NULL
-};
-static int ds_num = 3;
-
-static void load_init (void)
-{
-       return;
-}
-
-static void load_write (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, load_file, val, ds_def, ds_num);
-}
-
 #if LOAD_HAVE_READ
-#define BUFSIZE 256
-static void load_submit (double snum, double mnum, double lnum)
+static void load_submit (gauge_t snum, gauge_t mnum, gauge_t lnum)
 {
-       char buf[BUFSIZE];
+       value_t values[3];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (buf, BUFSIZE, "%u:%.2f:%.2f:%.2f", (unsigned int) curtime,
-                               snum, mnum, lnum) >= BUFSIZE)
-               return;
+       values[0].gauge = snum;
+       values[1].gauge = mnum;
+       values[2].gauge = lnum;
+
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "load");
 
-       plugin_submit (MODULE_NAME, "-", buf);
+       plugin_dispatch_values ("load", &vl);
 }
-#undef BUFSIZE
 
-static void load_read (void)
+static int load_read (void)
 {
 #if defined(HAVE_GETLOADAVG)
        double load[3];
@@ -87,11 +68,15 @@ static void load_read (void)
        if (getloadavg (load, 3) == 3)
                load_submit (load[LOADAVG_1MIN], load[LOADAVG_5MIN], load[LOADAVG_15MIN]);
        else
-               syslog (LOG_WARNING, "load: getloadavg failed: %s", strerror (errno));
+       {
+               char errbuf[1024];
+               WARNING ("load: getloadavg failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
 /* #endif HAVE_GETLOADAVG */
 
 #elif defined(KERNEL_LINUX)
-       double snum, mnum, lnum;
+       gauge_t snum, mnum, lnum;
        FILE *loadavg;
        char buffer[16];
 
@@ -100,19 +85,27 @@ static void load_read (void)
        
        if ((loadavg = fopen ("/proc/loadavg", "r")) == NULL)
        {
-               syslog (LOG_WARNING, "load: fopen: %s", strerror (errno));
+               char errbuf[1024];
+               WARNING ("load: fopen: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                return;
        }
 
        if (fgets (buffer, 16, loadavg) == NULL)
        {
-               syslog (LOG_WARNING, "load: fgets: %s", strerror (errno));
+               char errbuf[1024];
+               WARNING ("load: fgets: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                fclose (loadavg);
                return;
        }
 
        if (fclose (loadavg))
-               syslog (LOG_WARNING, "load: fclose: %s", strerror (errno));
+       {
+               char errbuf[1024];
+               WARNING ("load: fclose: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
 
        numfields = strsplit (buffer, fields, 8);
 
@@ -127,7 +120,7 @@ static void load_read (void)
 /* #endif KERNEL_LINUX */
 
 #elif defined(HAVE_LIBSTATGRAB)
-       double snum, mnum, lnum;
+       gauge_t snum, mnum, lnum;
        sg_load_stats *ls;
 
        if ((ls = sg_get_load_stats ()) == NULL)
@@ -139,14 +132,14 @@ static void load_read (void)
 
        load_submit (snum, mnum, lnum);
 #endif /* HAVE_LIBSTATGRAB */
+
+       return (0);
 }
-#else
-# define load_read NULL
 #endif /* LOAD_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, load_init, load_read, load_write);
-}
-
-#undef MODULE_NAME
+#if LOAD_HAVE_READ
+       plugin_register_read ("load", load_read);
+#endif
+} /* void module_register */
diff --git a/src/logfile.c b/src/logfile.c
new file mode 100644 (file)
index 0000000..102f12b
--- /dev/null
@@ -0,0 +1,124 @@
+/**
+ * collectd - src/logfile.c
+ * Copyright (C) 2007  Sebastian Harl
+ *
+ * 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:
+ *   Sebastian Harl <sh at tokkee.org>
+ *   Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include <pthread.h>
+
+#if COLLECT_DEBUG
+static int log_level = LOG_DEBUG;
+#else
+static int log_level = LOG_INFO;
+#endif /* COLLECT_DEBUG */
+
+static pthread_mutex_t file_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static char *log_file = NULL;
+
+static const char *config_keys[] =
+{
+       "LogLevel",
+       "File"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int logfile_config (const char *key, const char *value)
+{
+       if (0 == strcasecmp (key, "LogLevel")) {
+               if ((0 == strcasecmp (value, "emerg"))
+                               || (0 == strcasecmp (value, "alert"))
+                               || (0 == strcasecmp (value, "crit"))
+                               || (0 == strcasecmp (value, "err")))
+                       log_level = LOG_ERR;
+               else if (0 == strcasecmp (value, "warning"))
+                       log_level = LOG_WARNING;
+               else if (0 == strcasecmp (value, "notice"))
+                       log_level = LOG_NOTICE;
+               else if (0 == strcasecmp (value, "info"))
+                       log_level = LOG_INFO;
+#if COLLECT_DEBUG
+               else if (0 == strcasecmp (value, "debug"))
+                       log_level = LOG_DEBUG;
+#endif /* COLLECT_DEBUG */
+               else
+                       return 1;
+       }
+       else if (0 == strcasecmp (key, "File")) {
+               sfree (log_file);
+               log_file = strdup (value);
+       }
+       else {
+               return -1;
+       }
+       return 0;
+} /* int logfile_config (const char *, const char *) */
+
+static void logfile_log (int severity, const char *msg)
+{
+       FILE *fh;
+       int do_close = 0;
+
+       if (severity > log_level)
+               return;
+
+       pthread_mutex_lock (&file_lock);
+
+       if ((log_file == NULL) || (strcasecmp (log_file, "stderr") == 0))
+               fh = stderr;
+       else if (strcasecmp (log_file, "stdout") == 0)
+               fh = stdout;
+       else
+       {
+               fh = fopen (log_file, "a");
+               do_close = 1;
+       }
+
+       if (fh == NULL)
+       {
+                       char errbuf[1024];
+                       fprintf (stderr, "logfile plugin: fopen (%s) failed: %s\n",
+                                       (log_file == NULL) ? "<null>" : log_file,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
+       else
+       {
+               fprintf (fh, "%s\n", msg);
+               if (do_close != 0)
+                       fclose (fh);
+       }
+
+       pthread_mutex_unlock (&file_lock);
+
+       return;
+} /* void logfile_log (int, const char *) */
+
+void module_register (void)
+{
+       plugin_register_config ("logfile", logfile_config,
+                       config_keys, config_keys_num);
+       plugin_register_log ("logfile", logfile_log);
+} /* void module_register (void) */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
index a9bf100..68a0985 100644 (file)
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
-#include "utils_debug.h"
 
-#define MODULE_NAME "mbmon"
-
-#include <netdb.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
+#if HAVE_NETDB_H && HAVE_SYS_SOCKET_H && HAVE_NETINET_IN_H && HAVE_NETINET_TCP_H
+# include <netdb.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <netinet/tcp.h>
+# define MBMON_HAVE_READ 1
+#else
+# define MBMON_HAVE_READ 0
+#endif
 
 #define MBMON_DEF_HOST "127.0.0.1"
 #define MBMON_DEF_PORT "411" /* the default for Debian */
 
-/* BUFFER_SIZE
-   Size of the buffer we use to receive from the mbmon daemon. */
-#define BUFFER_SIZE 1024
-
-static char *filename_temperature = "mbmon/temperature-%s.rrd";
-static char *filename_fanspeed = "mbmon/fanspeed-%s.rrd";
-static char *filename_voltage = "mbmon/voltage-%s.rrd";
-
-/* temperature and fan sensors */
-static char *ds_def[] =
-{
-       "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int ds_num = 1;
-
-/* voltage sensors */
-static char *voltage_ds_def[] = 
-{
-       "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int voltage_ds_num = 1;
-
-static char *config_keys[] =
+static const char *config_keys[] =
 {
        "Host",
        "Port",
@@ -69,6 +47,7 @@ static char *config_keys[] =
 };
 static int config_keys_num = 2;
 
+#if MBMON_HAVE_READ
 static char *mbmon_host = NULL;
 static char *mbmon_port = NULL;
 
@@ -130,9 +109,12 @@ static int mbmon_query_daemon (char *buffer, int buffer_size)
 
        if ((ai_return = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0)
        {
-               syslog (LOG_ERR, "mbmon: getaddrinfo (%s, %s): %s",
+               char errbuf[1024];
+               ERROR ("mbmon: getaddrinfo (%s, %s): %s",
                                host, port,
-                               ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
+                               (ai_return == EAI_SYSTEM)
+                               ? sstrerror (errno, errbuf, sizeof (errbuf))
+                               : gai_strerror (ai_return));
                return (-1);
        }
 
@@ -142,16 +124,20 @@ static int mbmon_query_daemon (char *buffer, int buffer_size)
                /* create our socket descriptor */
                if ((fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol)) < 0)
                {
-                       syslog (LOG_ERR, "mbmon: socket: %s",
-                                       strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("mbmon: socket: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        continue;
                }
 
                /* connect to the mbmon daemon */
                if (connect (fd, (struct sockaddr *) ai_ptr->ai_addr, ai_ptr->ai_addrlen))
                {
-                       DBG ("mbmon: connect (%s, %s): %s", host, port,
-                                       strerror (errno));
+                       char errbuf[1024];
+                       DEBUG ("mbmon: connect (%s, %s): %s", host, port,
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        close (fd);
                        fd = -1;
                        continue;
@@ -166,7 +152,7 @@ static int mbmon_query_daemon (char *buffer, int buffer_size)
 
        if (fd < 0)
        {
-               syslog (LOG_ERR, "mbmon: Could not connect to daemon.");
+               ERROR ("mbmon: Could not connect to daemon.");
                return (-1);
        }
 
@@ -178,11 +164,14 @@ static int mbmon_query_daemon (char *buffer, int buffer_size)
        {
                if (status == -1)
                {
+                       char errbuf[1024];
+
                        if ((errno == EAGAIN) || (errno == EINTR))
                                continue;
 
-                       syslog (LOG_ERR, "mbmon: Error reading from socket: %s",
-                                               strerror (errno));
+                       ERROR ("mbmon: Error reading from socket: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        close (fd);
                        return (-1);
                }
@@ -195,11 +184,11 @@ static int mbmon_query_daemon (char *buffer, int buffer_size)
        if (buffer_fill >= buffer_size)
        {
                buffer[buffer_size - 1] = '\0';
-               syslog (LOG_WARNING, "mbmon: Message from mbmon has been truncated.");
+               WARNING ("mbmon: Message from mbmon has been truncated.");
        }
        else if (buffer_fill == 0)
        {
-               syslog (LOG_WARNING, "mbmon: Peer has unexpectedly shut down the socket. "
+               WARNING ("mbmon: Peer has unexpectedly shut down the socket. "
                                "Buffer: `%s'", buffer);
                close (fd);
                return (-1);
@@ -209,7 +198,7 @@ static int mbmon_query_daemon (char *buffer, int buffer_size)
        return (0);
 }
 
-static int mbmon_config (char *key, char *value)
+static int mbmon_config (const char *key, const char *value)
 {
        if (strcasecmp (key, "host") == 0)
        {
@@ -231,60 +220,23 @@ static int mbmon_config (char *key, char *value)
        return (0);
 }
 
-static void mbmon_init (void)
-{
-       return;
-}
-
-static void mbmon_write_temperature (char *host, char *inst, char *val)
-{
-       char filename[BUFFER_SIZE];
-       int status;
-
-       /* construct filename */
-       status = snprintf (filename, BUFFER_SIZE, filename_temperature, inst);
-       if ((status < 1) || (status >= BUFFER_SIZE))
-               return;
-
-       rrd_update_file (host, filename, val, ds_def, ds_num);
-}
-
-static void mbmon_write_fanspeed (char *host, char *inst, char *val)
-{
-       char filename[BUFFER_SIZE];
-       int status;
-
-       /* construct filename */
-       status = snprintf (filename, BUFFER_SIZE, filename_fanspeed, inst);
-       if ((status < 1) || (status >= BUFFER_SIZE))
-               return;
-
-       rrd_update_file (host, filename, val, ds_def, ds_num);
-}
-
-static void mbmon_write_voltage (char *host, char *inst, char *val)
+static void mbmon_submit (const char *type, const char *type_instance,
+               double value)
 {
-       char filename[BUFFER_SIZE];
-       int status;
-
-       /* construct filename */
-       status = snprintf (filename, BUFFER_SIZE, filename_voltage, inst);
-       if ((status < 1) || (status >= BUFFER_SIZE))
-               return;
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       rrd_update_file (host, filename, val, voltage_ds_def, voltage_ds_num);
-}
-
-static void mbmon_submit (char *type, char *inst, double value)
-{
-       char buf[BUFFER_SIZE];
+       values[0].gauge = value;
 
-       if (snprintf (buf, BUFFER_SIZE, "%u:%.3f", (unsigned int) curtime, value)
-            >= BUFFER_SIZE)
-               return;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "mbmon");
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-       plugin_submit (type, inst, buf);
-}
+       plugin_dispatch_values (type, &vl);
+} /* void mbmon_submit */
 
 /* Trim trailing whitespace from a string. */
 static void trim_spaces (char *s)
@@ -295,40 +247,14 @@ static void trim_spaces (char *s)
                s[l] = '\0';
 }
 
-static void mbmon_read (void)
+static int mbmon_read (void)
 {
-       char buf[BUFFER_SIZE];
+       char buf[1024];
        char *s, *t;
 
-       static int wait_time = 1;
-       static int wait_left = 0;
-
-       if (wait_left >= 10)
-       {
-               wait_left -= 10;
-               return;
-       }
-
        /* get data from daemon */
-       if (mbmon_query_daemon (buf, BUFFER_SIZE) < 0)
-       {
-               /* This limit is reached in log2(86400) =~ 17 steps. Since
-                * there is a 2^n seconds wait between each step it will need
-                * roughly one day to reach this limit. -octo */
-               
-               wait_time *= 2;
-               if (wait_time > 86400)
-                       wait_time = 86400;
-
-               wait_left = wait_time;
-
-               return;
-       }
-       else
-       {
-               wait_time = 1;
-               wait_left = 0;
-       }
+       if (mbmon_query_daemon (buf, sizeof (buf)) < 0)
+               return (-1);
 
        s = buf;
        while ((t = strchr (s, ':')) != NULL)
@@ -345,24 +271,24 @@ static void mbmon_read (void)
                value = strtod (t, &nextc);
                if ((*nextc != '\n') && (*nextc != '\0'))
                {
-                       syslog (LOG_ERR, "mbmon: value for `%s' contains invalid characters: `%s'", s, t);
+                       ERROR ("mbmon: value for `%s' contains invalid characters: `%s'", s, t);
                        break;
                }
 
                if (strncmp (s, "TEMP", 4) == 0)
                {
                        inst = s + 4;
-                       type = "mbmon_temperature";
+                       type = "temperature";
                }
                else if (strncmp (s, "FAN", 3) == 0)
                {
                        inst = s + 3;
-                       type = "mbmon_fanspeed";
+                       type = "fanspeed";
                }
                else if (strncmp (s, "V", 1) == 0)
                {
                        inst = s + 1;
-                       type = "mbmon_voltage";
+                       type = "voltage";
                }
                else
                {
@@ -376,17 +302,17 @@ static void mbmon_read (void)
 
                s = nextc + 1;
        }
+
+       return (0);
 } /* void mbmon_read */
+#endif /* MBMON_HAVE_READ */
 
 /* module_register
    Register collectd plugin. */
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, mbmon_init, mbmon_read, NULL);
-       plugin_register ("mbmon_temperature", NULL, NULL, mbmon_write_temperature);
-       plugin_register ("mbmon_fanspeed", NULL, NULL, mbmon_write_fanspeed);
-       plugin_register ("mbmon_voltage", NULL, NULL, mbmon_write_voltage);
-       cf_register (MODULE_NAME, mbmon_config, config_keys, config_keys_num);
-}
-
-#undef MODULE_NAME
+#if MBMON_HAVE_READ
+       plugin_register_config ("mbmon", mbmon_config, config_keys, config_keys_num);
+       plugin_register_read ("mbmon", mbmon_read);
+#endif /* MBMON_HAVE_READ */
+} /* void module_register */
index b8b7229..2acb412 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/memory.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -23,7 +22,6 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
-#include "utils_debug.h"
 
 #ifdef HAVE_SYS_SYSCTL_H
 # include <sys/sysctl.h>
 # define MEMORY_HAVE_READ 0
 #endif
 
-#define MODULE_NAME "memory"
-
-static char *memory_file = "memory.rrd";
-
-/* 9223372036854775807 == LLONG_MAX */
-static char *ds_def[] =
-{
-       "DS:used:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:free:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:buffers:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:cached:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       NULL
-};
-static int ds_num = 4;
-
 /* vm_statistics_data_t */
 #if defined(HOST_VM_INFO)
 static mach_port_t port_host;
@@ -85,7 +68,8 @@ static int pagesize;
 static kstat_t *ksp;
 #endif /* HAVE_LIBKSTAT */
 
-static void memory_init (void)
+#if MEMORY_HAVE_READ
+static int memory_init (void)
 {
 #if defined(HOST_VM_INFO)
        port_host = mach_host_self ();
@@ -107,31 +91,28 @@ static void memory_init (void)
                ksp = NULL;
 #endif /* HAVE_LIBKSTAT */
 
-       return;
-}
+       return (0);
+} /* int memory_init */
 
-static void memory_write (char *host, char *inst, char *val)
+static void memory_submit (const char *type_instance, gauge_t value)
 {
-       rrd_update_file (host, memory_file, val, ds_def, ds_num);
-}
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-#if MEMORY_HAVE_READ
-#define BUFSIZE 512
-static void memory_submit (long long mem_used, long long mem_buffered,
-               long long mem_cached, long long mem_free)
-{
-       char buf[BUFSIZE];
+       values[0].gauge = value;
 
-       if (snprintf (buf, BUFSIZE, "%u:%lli:%lli:%lli:%lli",
-                               (unsigned int) curtime, mem_used, mem_free,
-                               mem_buffered, mem_cached) >= BUFSIZE)
-               return;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "memory");
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+       vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
 
-       plugin_submit (MODULE_NAME, "-", buf);
+       plugin_dispatch_values ("memory", &vl);
 }
-#undef BUFSIZE
 
-static void memory_read (void)
+static int memory_read (void)
 {
 #if defined(HOST_VM_INFO)
        kern_return_t status;
@@ -144,15 +125,15 @@ static void memory_read (void)
        long long free;
 
        if (!port_host || !pagesize)
-               return;
+               return (-1);
 
        vm_data_len = sizeof (vm_data) / sizeof (natural_t);
        if ((status = host_statistics (port_host, HOST_VM_INFO,
                                        (host_info_t) &vm_data,
                                        &vm_data_len)) != KERN_SUCCESS)
        {
-               syslog (LOG_ERR, "memory-plugin: host_statistics failed and returned the value %i", (int) status);
-               return;
+               ERROR ("memory-plugin: host_statistics failed and returned the value %i", (int) status);
+               return (-1);
        }
 
        /*
@@ -180,7 +161,10 @@ static void memory_read (void)
        inactive = vm_data.inactive_count * pagesize;
        free     = vm_data.free_count     * pagesize;
 
-       memory_submit (wired + active, -1, inactive, free);
+       memory_submit ("wired",    wired);
+       memory_submit ("active",   active);
+       memory_submit ("inactive", inactive);
+       memory_submit ("free",     free);
 /* #endif HOST_VM_INFO */
 
 #elif HAVE_SYSCTLBYNAME
@@ -204,34 +188,37 @@ static void memory_read (void)
                "vm.stats.vm.v_cache_count",
                NULL
        };
-       int sysctl_vals[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+       double sysctl_vals[8];
 
-       size_t len;
        int    i;
-       int    status;
 
        for (i = 0; sysctl_keys[i] != NULL; i++)
        {
-               len = sizeof (int);
-               if ((status = sysctlbyname (sysctl_keys[i],
-                                               (void *) &sysctl_vals[i], &len,
-                                               NULL, 0)) < 0)
+               int value;
+               size_t value_len = sizeof (value);
+
+               if (sysctlbyname (sysctl_keys[i], (void *) &value, &value_len,
+                                       NULL, 0) == 0)
+               {
+                       sysctl_vals[i] = value;
+                       DEBUG ("memory plugin: %26s: %6i", sysctl_keys[i], sysctl_vals[i]);
+               }
+               else
                {
-                       syslog (LOG_ERR, "memory plugin: sysctlbyname (%s): %s",
-                                       sysctl_keys[i], strerror (errno));
-                       return;
+                       sysctl_vals[i] = NAN;
                }
-               DBG ("%26s: %6i", sysctl_keys[i], sysctl_vals[i]);
-       } /* for i */
+       } /* for (sysctl_keys) */
 
        /* multiply all all page counts with the pagesize */
        for (i = 1; sysctl_keys[i] != NULL; i++)
-               sysctl_vals[i] = sysctl_vals[i] * sysctl_vals[0];
-
-       memory_submit (sysctl_vals[3] + sysctl_vals[4], /* wired + active */
-                       sysctl_vals[6],                 /* cache */
-                       sysctl_vals[5],                 /* inactive */
-                       sysctl_vals[2]);                /* free */
+               if (!isnan (sysctl_vals[i]))
+                       sysctl_vals[i] *= sysctl_vals[0];
+
+       memory_submit ("free",     sysctl_vals[2]);
+       memory_submit ("wired",    sysctl_vals[3]);
+       memory_submit ("active",   sysctl_vals[4]);
+       memory_submit ("inactive", sysctl_vals[5]);
+       memory_submit ("cache",    sysctl_vals[6]);
 /* #endif HAVE_SYSCTLBYNAME */
 
 #elif defined(KERNEL_LINUX)
@@ -248,8 +235,10 @@ static void memory_read (void)
 
        if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
        {
-               syslog (LOG_WARNING, "memory: fopen: %s", strerror (errno));
-               return;
+               char errbuf[1024];
+               WARNING ("memory: fopen: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
        while (fgets (buffer, 1024, fh) != NULL)
@@ -276,12 +265,19 @@ static void memory_read (void)
        }
 
        if (fclose (fh))
-               syslog (LOG_WARNING, "memory: fclose: %s", strerror (errno));
+       {
+               char errbuf[1024];
+               WARNING ("memory: fclose: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
 
        if (mem_used >= (mem_free + mem_buffered + mem_cached))
        {
                mem_used -= mem_free + mem_buffered + mem_cached;
-               memory_submit (mem_used, mem_buffered, mem_cached, mem_free);
+               memory_submit ("used",     mem_used);
+               memory_submit ("buffered", mem_buffered);
+               memory_submit ("cached",   mem_cached);
+               memory_submit ("free",     mem_free);
        }
 /* #endif defined(KERNEL_LINUX) */
 
@@ -291,39 +287,46 @@ static void memory_read (void)
        long long mem_lock;
 
        if (ksp == NULL)
-               return;
+               return (-1);
 
        mem_used = get_kstat_value (ksp, "pagestotal");
        mem_free = get_kstat_value (ksp, "pagesfree");
        mem_lock = get_kstat_value (ksp, "pageslocked");
 
        if ((mem_used < 0LL) || (mem_free < 0LL) || (mem_lock < 0LL))
-               return;
+               return (-1);
        if (mem_used < (mem_free + mem_lock))
-               return;
+               return (-1);
 
        mem_used -= mem_free + mem_lock;
        mem_used *= pagesize; /* If this overflows you have some serious */
        mem_free *= pagesize; /* memory.. Why not call me up and give me */
        mem_lock *= pagesize; /* some? ;) */
 
-       memory_submit (mem_used, mem_lock, 0LL, mem_free);
+       memory_submit ("used",   mem_used);
+       memory_submit ("free",   mem_free);
+       memory_submit ("locked", mem_locked);
 /* #endif defined(HAVE_LIBKSTAT) */
 
 #elif defined(HAVE_LIBSTATGRAB)
        sg_mem_stats *ios;
 
        if ((ios = sg_get_mem_stats ()) != NULL)
-               memory_submit (ios->used, 0LL, ios->cache, ios->free);
+       {
+               memory_submit ("used",   ios->used);
+               memory_submit ("cached", ios->cached);
+               memory_submit ("free",   ios->free);
+       }
 #endif /* HAVE_LIBSTATGRAB */
+
+       return (0);
 }
-#else
-# define memory_read NULL
 #endif /* MEMORY_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, memory_init, memory_read, memory_write);
-}
-
-#undef MODULE_NAME
+#if MEMORY_HAVE_READ
+       plugin_register_init ("memory", memory_init);
+       plugin_register_read ("memory", memory_read);
+#endif /* MEMORY_HAVE_READ */
+} /* void module_register */
index 09073ab..21a2144 100644 (file)
  *   Peter Holik <peter at holik.at>
  *
  * Used multimeter: Metex M-4650CR
- *
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
 
-#include <termios.h>
-#include <sys/ioctl.h>
-#include <math.h>
-
-#define MODULE_NAME "multimeter"
-
-static char *multimeter_file = "multimeter.rrd";
-
-static char *ds_def[] =
-{
-       "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int ds_num = 1;
-
+#if HAVE_TERMIOS_H && HAVE_SYS_IOCTL_H && HAVE_MATH_H
+# include <termios.h>
+# include <sys/ioctl.h>
+# include <math.h>
+# define MULTIMETER_HAVE_READ 1
+#else
+# define MULTIMETER_HAVE_READ 0
+# error "multimeter cannot read!"
+#endif
+
+#if MULTIMETER_HAVE_READ
 static int fd = -1;
 
 static int multimeter_timeval_sub (struct timeval *tv1, struct timeval *tv2,
@@ -76,8 +71,10 @@ static int multimeter_read_value(double *value)
 
                if (gettimeofday (&time_end, NULL) < 0)
                {
-                       syslog (LOG_ERR, MODULE_NAME": gettimeofday failed: %s",
-                                strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("multimeter plugin: gettimeofday failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        return (-1);
                }
                time_end.tv_sec++;      
@@ -98,8 +95,11 @@ static int multimeter_read_value(double *value)
 
                        if (gettimeofday (&time_now, NULL) < 0)
                        {
-                               syslog (LOG_ERR, MODULE_NAME": gettimeofday failed: %s",
-                                        strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("multimeter plugin: "
+                                               "gettimeofday failed: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
                                return (-1);
                        }
                        if (multimeter_timeval_sub (&time_end, &time_now, &timeout) == -1)
@@ -151,17 +151,19 @@ static int multimeter_read_value(double *value)
                        }
                        else /* status == -1 */
                        {
-                               syslog (LOG_ERR, MODULE_NAME": select failed: %s",
-                                        strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("multimeter plugin: "
+                                               "select failed: %s",
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
                                break;
                        }
                }
        } while (--retry);
 
        return (-2);  /* no value received */
-}
+} /* int multimeter_read_value */
 
-static void multimeter_init (void)
+static int multimeter_init (void)
 {
        int i;
        char device[] = "/dev/ttyS ";
@@ -187,49 +189,72 @@ static void multimeter_init (void)
                        tcsetattr(fd, TCSANOW, &tios);
                        ioctl(fd, TIOCMBIC, &rts);
                        
-                       if (multimeter_read_value(&value) < -1)
+                       if (multimeter_read_value (&value) < -1)
                        {
-                               close(fd);
+                               close (fd);
                                fd = -1;
                        }
                        else
                        {
-                               syslog (LOG_INFO, MODULE_NAME" found (%s)", device);
-                               return;
+                               INFO ("multimeter plugin: Device "
+                                               "found at %s", device);
+                               return (0);
                        }
                }
        }
-       syslog (LOG_ERR, MODULE_NAME" not found");
+
+       ERROR ("multimeter plugin: No device found");
+       return (-1);
 }
 #undef LINE_LENGTH
 
-static void multimeter_write (char *host, char *inst, char *val)
+static void multimeter_submit (double value)
 {
-       rrd_update_file (host, multimeter_file, val, ds_def, ds_num);
-}
-#define BUFSIZE 128
-static void multimeter_submit (double *value)
-{
-       char buf[BUFSIZE];
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].gauge = value;
 
-       if (snprintf (buf, BUFSIZE, "%u:%f", (unsigned int) curtime, *value) >= BUFSIZE)
-               return;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "multimeter");
 
-       plugin_submit (MODULE_NAME, "-", buf);
+       plugin_dispatch_values ("multimeter", &vl);
 }
-#undef BUFSIZE
 
-static void multimeter_read (void)
+static int multimeter_read (void)
 {
        double value;
 
-       if (fd > -1 && !(multimeter_read_value(&value)))
-               multimeter_submit (&value);
-}
+       if (fd < 0)
+               return (-1);
 
-void module_register (void)
+       if (multimeter_read_value (&value) != 0)
+               return (-1);
+
+       multimeter_submit (value);
+       return (0);
+} /* int multimeter_read */
+
+static int multimeter_shutdown (void)
 {
-       plugin_register (MODULE_NAME, multimeter_init, multimeter_read, multimeter_write);
+       if (fd >= 0)
+       {
+               close (fd);
+               fd = -1;
+       }
+
+       return (0);
 }
+#endif /* MULTIMETER_HAVE_READ */
 
-#undef MODULE_NAME
+void module_register (void)
+{
+#if MULTIMETER_HAVE_READ
+       plugin_register_init ("multimeter", multimeter_init);
+       plugin_register_read ("multimeter", multimeter_read);
+       plugin_register_shutdown ("multimeter", multimeter_shutdown);
+#endif /* MULTIMETER_HAVE_READ */
+} /* void module_register */
index caebdf3..fccaedb 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/mysql.c
- * Copyright (C) 2006  Florian octo Forster
+ * Copyright (C) 2006,2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 #include <mysql/mysql.h>
 #endif
 
-#define MODULE_NAME "mysql"
-
 #if COLLECT_LIBMYSQL
 # define MYSQL_HAVE_READ 1
 #else
 # define MYSQL_HAVE_READ 0
 #endif
 
-#define BUFSIZE 512
-
-static char *host = "localhost";
-static char *user;
-static char *pass;
-static char *db = NULL;
-
-/* TODO
- * understand `Select_*' and possibly do that stuff as well..
- */
-
-static char *commands_file = "mysql/mysql_commands-%s.rrd";
-static char *handler_file  = "mysql/mysql_handler-%s.rrd";
-static char *qcache_file   = "mysql/mysql_qcache.rrd";
-static char *threads_file  = "mysql/mysql_threads.rrd";
-static char *traffic_file  = "traffic-mysql.rrd";
-
-static char *commands_ds_def[] =
-{
-       "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int commands_ds_num = 1;
-
-static char *handler_ds_def[] =
-{
-       "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int handler_ds_num = 1;
-
-static char *qcache_ds_def[] =
-{
-       "DS:hits:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:inserts:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:not_cached:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:lowmem_prunes:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:queries_in_cache:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int qcache_ds_num = 5;
-
-static char *threads_ds_def[] =
-{
-       "DS:running:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       "DS:connected:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       "DS:cached:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       "DS:created:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int threads_ds_num = 4;
-
-static char *traffic_ds_def[] =
-{
-       "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int traffic_ds_num = 2;
+/* TODO: Understand `Select_*' and possibly do that stuff as well.. */
 
-static char *config_keys[] =
+#if MYSQL_HAVE_READ
+static const char *config_keys[] =
 {
        "Host",
        "User",
@@ -107,7 +47,11 @@ static char *config_keys[] =
 };
 static int config_keys_num = 4;
 
-#if MYSQL_HAVE_READ
+static char *host = "localhost";
+static char *user;
+static char *pass;
+static char *db = NULL;
+
 static MYSQL *getconnection (void)
 {
        static MYSQL *con;
@@ -116,14 +60,12 @@ static MYSQL *getconnection (void)
        static int wait_for = 0;
        static int wait_increase = 60;
 
-       int step;
-
        if (state != 0)
        {
                int err;
                if ((err = mysql_ping (con)) != 0)
                {
-                       syslog (LOG_WARNING, "mysql_ping failed: %s", mysql_error (con));
+                       WARNING ("mysql_ping failed: %s", mysql_error (con));
                        state = 0;
                }
                else
@@ -133,11 +75,9 @@ static MYSQL *getconnection (void)
                }
        }
 
-       step = atoi (COLLECTD_STEP);
-
        if (wait_for > 0)
        {
-               wait_for -= step;
+               wait_for -= interval_g;
                return (NULL);
        }
 
@@ -148,14 +88,14 @@ static MYSQL *getconnection (void)
 
        if ((con = mysql_init (con)) == NULL)
        {
-               syslog (LOG_ERR, "mysql_init failed: %s", mysql_error (con));
+               ERROR ("mysql_init failed: %s", mysql_error (con));
                state = 0;
                return (NULL);
        }
 
        if (mysql_real_connect (con, host, user, pass, db, 0, NULL, 0) == NULL)
        {
-               syslog (LOG_ERR, "mysql_real_connect failed: %s", mysql_error (con));
+               ERROR ("mysql_real_connect failed: %s", mysql_error (con));
                state = 0;
                return (NULL);
        }
@@ -167,14 +107,8 @@ static MYSQL *getconnection (void)
                return (con);
        }
 } /* static MYSQL *getconnection (void) */
-#endif /* MYSQL_HAVE_READ */
-
-static void init (void)
-{
-       return;
-}
 
-static int config (char *key, char *value)
+static int config (const char *key, const char *value)
 {
        if (strcasecmp (key, "host") == 0)
                return ((host = strdup (value)) == NULL ? 1 : 0);
@@ -188,160 +122,84 @@ static int config (char *key, char *value)
                return (-1);
 }
 
-static void commands_write (char *host, char *inst, char *val)
-{
-       char buf[BUFSIZE];
-
-       if (snprintf (buf, BUFSIZE, commands_file, inst) >= BUFSIZE)
-               return;
-
-       rrd_update_file (host, buf, val, commands_ds_def, commands_ds_num);
-}
-
-static void handler_write (char *host, char *inst, char *val)
-{
-       char buf[BUFSIZE];
-
-       if (snprintf (buf, BUFSIZE, handler_file, inst) >= BUFSIZE)
-               return;
-
-       rrd_update_file (host, buf, val, handler_ds_def, handler_ds_num);
-}
-
-static void qcache_write (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, qcache_file, val,
-                       qcache_ds_def, qcache_ds_num);
-}
-
-static void threads_write (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, threads_file, val,
-                       threads_ds_def, threads_ds_num);
-}
-
-static void traffic_write (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, traffic_file, val,
-                       traffic_ds_def, traffic_ds_num);
-}
-
-#if MYSQL_HAVE_READ
-static void commands_submit (char *inst, unsigned long long value)
-{
-       char buf[BUFSIZE];
-       int  status;
-
-       status = snprintf (buf, BUFSIZE, "%u:%llu", (unsigned int) curtime, value);
-
-       if (status < 0)
-       {
-               syslog (LOG_ERR, "snprintf failed");
-               return;
-       }
-       else if (status >= BUFSIZE)
-       {
-               syslog (LOG_WARNING, "snprintf was truncated");
-               return;
-       }
-
-       plugin_submit ("mysql_commands", inst, buf);
-}
-
-static void handler_submit (char *inst, unsigned long long value)
+static void counter_submit (const char *type, const char *type_instance,
+               counter_t value)
 {
-       char buf[BUFSIZE];
-       int  status;
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       status = snprintf (buf, BUFSIZE, "%u:%llu", (unsigned int) curtime, value);
+       values[0].counter = value;
 
-       if (status < 0)
-       {
-               syslog (LOG_ERR, "snprintf failed");
-               return;
-       }
-       else if (status >= BUFSIZE)
-       {
-               syslog (LOG_WARNING, "snprintf was truncated");
-               return;
-       }
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "mysql");
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-       plugin_submit ("mysql_handler", inst, buf);
-}
+       plugin_dispatch_values (type, &vl);
+} /* void counter_submit */
 
-static void qcache_submit (unsigned long long hits, unsigned long long inserts,
-               unsigned long long not_cached, unsigned long long lowmem_prunes,
-               int queries_in_cache)
+static void qcache_submit (counter_t hits, counter_t inserts,
+               counter_t not_cached, counter_t lowmem_prunes,
+               gauge_t queries_in_cache)
 {
-       char buf[BUFSIZE];
-       int  status;
-
-       status = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%i",
-                       (unsigned int) curtime, hits, inserts, not_cached,
-                       lowmem_prunes, queries_in_cache);
-
-       if (status < 0)
-       {
-               syslog (LOG_ERR, "snprintf failed");
-               return;
-       }
-       else if (status >= BUFSIZE)
-       {
-               syslog (LOG_WARNING, "snprintf was truncated");
-               return;
-       }
-
-       plugin_submit ("mysql_qcache", "-", buf);
-}
-
-static void threads_submit (int running, int connected, int cached,
-               unsigned long long created)
+       value_t values[5];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       values[0].counter = hits;
+       values[1].counter = inserts;
+       values[2].counter = not_cached;
+       values[3].counter = lowmem_prunes;
+       values[4].gauge   = queries_in_cache;
+
+       vl.values = values;
+       vl.values_len = 5;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "mysql");
+
+       plugin_dispatch_values ("mysql_qcache", &vl);
+} /* void qcache_submit */
+
+static void threads_submit (gauge_t running, gauge_t connected, gauge_t cached,
+               counter_t created)
 {
-       char buf[BUFSIZE];
-       int  status;
+       value_t values[4];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       status = snprintf (buf, BUFSIZE, "%u:%i:%i:%i:%llu",
-                       (unsigned int) curtime,
-                       running, connected, cached, created);
+       values[0].gauge   = running;
+       values[1].gauge   = connected;
+       values[2].gauge   = cached;
+       values[3].counter = created;
 
-       if (status < 0)
-       {
-               syslog (LOG_ERR, "snprintf failed");
-               return;
-       }
-       else if (status >= BUFSIZE)
-       {
-               syslog (LOG_WARNING, "snprintf was truncated");
-               return;
-       }
+       vl.values = values;
+       vl.values_len = 4;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "mysql");
 
-       plugin_submit ("mysql_threads", "-", buf);
-}
+       plugin_dispatch_values ("mysql_threads", &vl);
+} /* void threads_submit */
 
-static void traffic_submit (unsigned long long incoming,
-               unsigned long long outgoing)
+static void traffic_submit (counter_t rx, counter_t tx)
 {
-       char buf[BUFSIZE];
-       int  status;
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       status = snprintf (buf, BUFSIZE, "%u:%llu:%llu", (unsigned int) curtime,
-                       incoming, outgoing);
+       values[0].counter = rx;
+       values[1].counter = tx;
 
-       if (status < 0)
-       {
-               syslog (LOG_ERR, "snprintf failed");
-               return;
-       }
-       else if (status >= BUFSIZE)
-       {
-               syslog (LOG_WARNING, "snprintf was truncated");
-               return;
-       }
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "mysql");
 
-       plugin_submit ("mysql_traffic", "-", buf);
-}
+       plugin_dispatch_values ("mysql_octets", &vl);
+} /* void traffic_submit */
 
-static void mysql_read (void)
+static int mysql_read (void)
 {
        MYSQL     *con;
        MYSQL_RES *res;
@@ -366,7 +224,7 @@ static void mysql_read (void)
 
        /* An error message will have been printed in this case */
        if ((con = getconnection ()) == NULL)
-               return;
+               return (-1);
 
        query = "SHOW STATUS";
        if (mysql_get_server_version (con) >= 50002)
@@ -376,16 +234,16 @@ static void mysql_read (void)
 
        if (mysql_real_query (con, query, query_len))
        {
-               syslog (LOG_ERR, "mysql_real_query failed: %s\n",
+               ERROR ("mysql_real_query failed: %s\n",
                                mysql_error (con));
-               return;
+               return (-1);
        }
 
        if ((res = mysql_store_result (con)) == NULL)
        {
-               syslog (LOG_ERR, "mysql_store_result failed: %s\n",
+               ERROR ("mysql_store_result failed: %s\n",
                                mysql_error (con));
-               return;
+               return (-1);
        }
 
        field_num = mysql_num_fields (res);
@@ -404,14 +262,14 @@ static void mysql_read (void)
 
                        /* Ignore `prepared statements' */
                        if (strncmp (key, "Com_stmt_", 9) != 0)
-                               commands_submit (key + 4, val);
+                               counter_submit ("mysql_commands", key + 4, val);
                }
                else if (strncmp (key, "Handler_", 8) == 0)
                {
                        if (val == 0ULL)
                                continue;
 
-                       handler_submit (key + 8, val);
+                       counter_submit ("mysql_handler", key + 8, val);
                }
                else if (strncmp (key, "Qcache_", 7) == 0)
                {
@@ -462,22 +320,14 @@ static void mysql_read (void)
 
        /* mysql_close (con); */
 
-       return;
-}
-#else
-# define mysql_read NULL
+       return (0);
+} /* int mysql_read */
 #endif /* MYSQL_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, init, mysql_read, NULL);
-       plugin_register ("mysql_commands", NULL, NULL, commands_write);
-       plugin_register ("mysql_handler",  NULL, NULL, handler_write);
-       plugin_register ("mysql_qcache",   NULL, NULL, qcache_write);
-       plugin_register ("mysql_threads",  NULL, NULL, threads_write);
-       plugin_register ("mysql_traffic",  NULL, NULL, traffic_write);
-       cf_register (MODULE_NAME, config, config_keys, config_keys_num);
-}
-
-#undef BUFSIZE
-#undef MODULE_NAME
+#if MYSQL_HAVE_READ
+       plugin_register_config ("mysql", config, config_keys, config_keys_num);
+       plugin_register_read ("mysql", mysql_read);
+#endif /* MYSQL_HAVE_READ */
+} /* void module_register */
index 32af404..34385bf 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/network.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
  *   Florian octo Forster <octo at verplant.org>
  **/
 
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <syslog.h>
-#include <errno.h>
-
-#include "network.h"
+#include "collectd.h"
+#include "plugin.h"
 #include "common.h"
 #include "configfile.h"
-#include "utils_debug.h"
+#include "utils_avltree.h"
+
+#include "network.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#if HAVE_POLL_H
+# include <poll.h>
+#endif
 
 /* 1500 - 40 - 8  =  Ethernet packet - IPv6 header - UDP header */
 /* #define BUFF_SIZE 1452 */
 # endif
 #endif /* !IP_ADD_MEMBERSHIP */
 
-#define BUFF_SIZE 4096
-
-extern int operating_mode;
+#define BUFF_SIZE 1024
 
+/*
+ * Private data types
+ */
 typedef struct sockent
 {
        int                      fd;
-       int                      mode;
        struct sockaddr_storage *addr;
        socklen_t                addrlen;
        struct sockent          *next;
 } sockent_t;
 
-static sockent_t *socklist_head = NULL;
+/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-------+-----------------------+-------------------------------+
+ * ! Ver.  !                       ! Length                        !
+ * +-------+-----------------------+-------------------------------+
+ */
+struct part_header_s
+{
+       uint16_t type;
+       uint16_t length;
+};
+typedef struct part_header_s part_header_t;
+
+/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-------------------------------+-------------------------------+
+ * ! Type                          ! Length                        !
+ * +-------------------------------+-------------------------------+
+ * : (Length - 4) Bytes                                            :
+ * +---------------------------------------------------------------+
+ */
+struct part_string_s
+{
+       part_header_t *head;
+       char *value;
+};
+typedef struct part_string_s part_string_t;
+
+/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-------------------------------+-------------------------------+
+ * ! Type                          ! Length                        !
+ * +-------------------------------+-------------------------------+
+ * : (Length - 4 == 2 || 4 || 8) Bytes                             :
+ * +---------------------------------------------------------------+
+ */
+struct part_number_s
+{
+       part_header_t *head;
+       uint64_t *value;
+};
+typedef struct part_number_s part_number_t;
+
+/*                      1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-------------------------------+-------------------------------+
+ * ! Type                          ! Length                        !
+ * +-------------------------------+---------------+---------------+
+ * ! Num of values                 ! Type0         ! Type1         !
+ * +-------------------------------+---------------+---------------+
+ * ! Value0                                                        !
+ * !                                                               !
+ * +---------------------------------------------------------------+
+ * ! Value1                                                        !
+ * !                                                               !
+ * +---------------------------------------------------------------+
+ */
+struct part_values_s
+{
+       part_header_t *head;
+       uint16_t *num_values;
+       uint8_t  *values_types;
+       value_t  *values;
+};
+typedef struct part_values_s part_values_t;
+
+/*
+ * Private variables
+ */
+static const char *config_keys[] =
+{
+       "CacheFlush",
+       "Listen",
+       "Server",
+       "TimeToLive",
+       "Forward"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static int network_config_ttl = 0;
+static int network_config_forward = 0;
+
+static sockent_t *sending_sockets = NULL;
+
+static struct pollfd *listen_sockets = NULL;
+static int listen_sockets_num = 0;
+static pthread_t listen_thread = 0;
+static int listen_loop = 0;
+
+static char         send_buffer[BUFF_SIZE];
+static char        *send_buffer_ptr;
+static int          send_buffer_fill;
+static value_list_t send_buffer_vl = VALUE_LIST_INIT;
+static char         send_buffer_type[DATA_MAX_NAME_LEN];
+static pthread_mutex_t send_buffer_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static avl_tree_t      *cache_tree = NULL;
+static pthread_mutex_t  cache_lock = PTHREAD_MUTEX_INITIALIZER;
+static time_t           cache_flush_last;
+static int              cache_flush_interval = 1800;
+
+/*
+ * Private functions
+ */
+static int cache_flush (void)
+{
+       char **keys = NULL;
+       int    keys_num = 0;
 
-static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
+       char **tmp;
+       int    i;
+
+       char   *key;
+       time_t *value;
+       avl_iterator_t *iter;
+
+       time_t curtime = time (NULL);
+
+       iter = avl_get_iterator (cache_tree);
+       while (avl_iterator_next (iter, (void *) &key, (void *) &value) == 0)
+       {
+               if ((curtime - *value) <= cache_flush_interval)
+                       continue;
+               tmp = (char **) realloc (keys,
+                               (keys_num + 1) * sizeof (char *));
+               if (tmp == NULL)
+               {
+                       sfree (keys);
+                       avl_iterator_destroy (iter);
+                       ERROR ("network plugin: cache_flush: realloc"
+                                       " failed.");
+                       return (-1);
+               }
+               keys = tmp;
+               keys[keys_num] = key;
+               keys_num++;
+       } /* while (avl_iterator_next) */
+       avl_iterator_destroy (iter);
+
+       for (i = 0; i < keys_num; i++)
+       {
+               if (avl_remove (cache_tree, keys[i], (void *) &key,
+                                       (void *) &value) != 0)
+               {
+                       WARNING ("network plugin: cache_flush: avl_remove"
+                                       " (%s) failed.", keys[i]);
+                       continue;
+               }
+
+               sfree (key);
+               sfree (value);
+       }
+
+       sfree (keys);
+
+       DEBUG ("network plugin: cache_flush: Removed %i %s",
+                       keys_num, (keys_num == 1) ? "entry" : "entries");
+       cache_flush_last = curtime;
+       return (0);
+} /* int cache_flush */
+
+static int cache_check (const char *type, const value_list_t *vl)
 {
-       char *ttl_str;
-       int   ttl_int;
+       char key[1024];
+       time_t *value = NULL;
+       int retval = -1;
+
+       if (cache_tree == NULL)
+               return (-1);
 
-       ttl_str = cf_get_option ("TimeToLive", NULL);
-       if (ttl_str == NULL)
+       if (format_name (key, sizeof (key), vl->host, vl->plugin,
+                               vl->plugin_instance, type, vl->type_instance))
                return (-1);
 
-       ttl_int = atoi (ttl_str);
-       if ((ttl_int < 1) || (ttl_int > 255))
+       pthread_mutex_lock (&cache_lock);
+
+       if (avl_get (cache_tree, key, (void *) &value) == 0)
+       {
+               if (*value < vl->time)
+               {
+                       *value = vl->time;
+                       retval = 0;
+               }
+               else
+               {
+                       DEBUG ("network plugin: cache_check: *value = %i >= vl->time = %i",
+                                       (int) *value, (int) vl->time);
+                       retval = 1;
+               }
+       }
+       else
+       {
+               char *key_copy = strdup (key);
+               value = malloc (sizeof (time_t));
+               if ((key_copy != NULL) && (value != NULL))
+               {
+                       *value = vl->time;
+                       avl_insert (cache_tree, key_copy, value);
+                       retval = 0;
+               }
+               else
+               {
+                       sfree (key_copy);
+                       sfree (value);
+               }
+       }
+
+       if ((time (NULL) - cache_flush_last) > cache_flush_interval)
+               cache_flush ();
+
+       pthread_mutex_unlock (&cache_lock);
+
+       DEBUG ("network plugin: cache_check: key = %s; time = %i; retval = %i",
+                       key, (int) vl->time, retval);
+
+       return (retval);
+} /* int cache_check */
+
+static int write_part_values (char **ret_buffer, int *ret_buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       part_values_t pv;
+       int i;
+
+       i = 6 + (9 * vl->values_len);
+       if (*ret_buffer_len < i)
+               return (-1);
+       *ret_buffer_len -= i;
+
+       pv.head = (part_header_t *) *ret_buffer;
+       pv.num_values = (uint16_t *) (pv.head + 1);
+       pv.values_types = (uint8_t *) (pv.num_values + 1);
+       pv.values = (value_t *) (pv.values_types + vl->values_len);
+       *ret_buffer = (void *) (pv.values + vl->values_len);
+
+       pv.head->type = htons (TYPE_VALUES);
+       pv.head->length = htons (6 + (9 * vl->values_len));
+       *pv.num_values = htons ((uint16_t) vl->values_len);
+       
+       for (i = 0; i < vl->values_len; i++)
        {
-               syslog (LOG_WARNING, "A TTL value of %i is invalid.", ttl_int);
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+               {
+                       pv.values_types[i] = DS_TYPE_COUNTER;
+                       pv.values[i].counter = htonll (vl->values[i].counter);
+               }
+               else
+               {
+                       pv.values_types[i] = DS_TYPE_GAUGE;
+                       pv.values[i].gauge = vl->values[i].gauge;
+               }
+       } /* for (values) */
+
+       return (0);
+} /* int write_part_values */
+
+static int write_part_number (char **ret_buffer, int *ret_buffer_len,
+               int type, uint64_t value)
+{
+       part_number_t pn;
+
+       if (*ret_buffer_len < 12)
+               return (-1);
+
+       pn.head = (part_header_t *) *ret_buffer;
+       pn.value = (uint64_t *) (pn.head + 1);
+
+       pn.head->type = htons (type);
+       pn.head->length = htons (12);
+       *pn.value = htonll (value);
+
+       *ret_buffer = (char *) (pn.value + 1);
+       *ret_buffer_len -= 12;
+
+       return (0);
+} /* int write_part_number */
+
+static int write_part_string (char **ret_buffer, int *ret_buffer_len,
+               int type, const char *str, int str_len)
+{
+       part_string_t ps;
+       int len;
+
+       len = 4 + str_len + 1;
+       if (*ret_buffer_len < len)
+               return (-1);
+       *ret_buffer_len -= len;
+
+       ps.head = (part_header_t *) *ret_buffer;
+       ps.value = (char *) (ps.head + 1);
+
+       ps.head->type = htons ((uint16_t) type);
+       ps.head->length = htons ((uint16_t) str_len + 5);
+       if (str_len > 0)
+               memcpy (ps.value, str, str_len);
+       ps.value[str_len] = '\0';
+       *ret_buffer = (void *) (ps.value + (str_len + 1));
+
+       return (0);
+} /* int write_part_string */
+
+static int parse_part_values (void **ret_buffer, int *ret_buffer_len,
+               value_t **ret_values, int *ret_num_values)
+{
+       char *buffer = *ret_buffer;
+       int   buffer_len = *ret_buffer_len;
+       part_values_t pv;
+       int   i;
+
+       uint16_t h_length;
+       uint16_t h_type;
+       uint16_t h_num;
+
+       if (buffer_len < (15))
+       {
+               DEBUG ("network plugin: packet is too short: buffer_len = %i",
+                               buffer_len);
                return (-1);
        }
 
-       DBG ("ttl = %i", ttl_int);
+       pv.head = (part_header_t *) buffer;
+       h_length = ntohs (pv.head->length);
+       h_type = ntohs (pv.head->type);
+
+       assert (h_type == TYPE_VALUES);
+
+       pv.num_values = (uint16_t *) (pv.head + 1);
+       h_num = ntohs (*pv.num_values);
+
+       if (h_num != ((h_length - 6) / 9))
+       {
+               DEBUG ("`length' and `num of values' don't match");
+               return (-1);
+       }
+
+       pv.values_types = (uint8_t *) (pv.num_values + 1);
+       pv.values = (value_t *) (pv.values_types + h_num);
+
+       for (i = 0; i < h_num; i++)
+               if (pv.values_types[i] == DS_TYPE_COUNTER)
+                       pv.values[i].counter = ntohll (pv.values[i].counter);
+
+       *ret_buffer     = (void *) (pv.values + h_num);
+       *ret_buffer_len = buffer_len - h_length;
+       *ret_num_values = h_num;
+       *ret_values     = pv.values;
+
+       return (0);
+} /* int parse_part_values */
+
+static int parse_part_number (void **ret_buffer, int *ret_buffer_len,
+               uint64_t *value)
+{
+       part_number_t pn;
+       uint16_t len;
+
+       pn.head = (part_header_t *) *ret_buffer;
+       pn.value = (uint64_t *) (pn.head + 1);
+
+       len = ntohs (pn.head->length);
+       if (len != 12)
+               return (-1);
+       if (len > *ret_buffer_len)
+               return (-1);
+       *value = ntohll (*pn.value);
+
+       *ret_buffer = (void *) (pn.value + 1);
+       *ret_buffer_len -= len;
+
+       return (0);
+} /* int parse_part_number */
+
+static int parse_part_string (void **ret_buffer, int *ret_buffer_len,
+               char *output, int output_len)
+{
+       char *buffer = *ret_buffer;
+       int   buffer_len = *ret_buffer_len;
+       part_string_t ps;
+
+       uint16_t h_length;
+       uint16_t h_type;
+
+       DEBUG ("network plugin: parse_part_string: ret_buffer = %p;"
+                       " ret_buffer_len = %i; output = %p; output_len = %i;",
+                       *ret_buffer, *ret_buffer_len,
+                       (void *) output, output_len);
+
+       ps.head = (part_header_t *) buffer;
+
+       h_length = ntohs (ps.head->length);
+       h_type = ntohs (ps.head->type);
+
+       DEBUG ("network plugin: parse_part_string: length = %hu; type = %hu;",
+                       h_length, h_type);
+
+       if (buffer_len < h_length)
+       {
+               DEBUG ("packet is too short");
+               return (-1);
+       }
+       assert ((h_type == TYPE_HOST)
+                       || (h_type == TYPE_PLUGIN)
+                       || (h_type == TYPE_PLUGIN_INSTANCE)
+                       || (h_type == TYPE_TYPE)
+                       || (h_type == TYPE_TYPE_INSTANCE));
+
+       ps.value = buffer + 4;
+       if (ps.value[h_length - 5] != '\0')
+       {
+               DEBUG ("String does not end with a nullbyte");
+               return (-1);
+       }
+
+       if (output_len < (h_length - 4))
+       {
+               DEBUG ("output buffer is too small");
+               return (-1);
+       }
+       strcpy (output, ps.value);
+
+       DEBUG ("network plugin: parse_part_string: output = %s", output);
+
+       *ret_buffer = (void *) (buffer + h_length);
+       *ret_buffer_len = buffer_len - h_length;
+
+       return (0);
+} /* int parse_part_string */
+
+static int parse_packet (void *buffer, int buffer_len)
+{
+       part_header_t *header;
+       int status;
+
+       value_list_t vl = VALUE_LIST_INIT;
+       char type[DATA_MAX_NAME_LEN];
+
+       DEBUG ("network plugin: parse_packet: buffer = %p; buffer_len = %i;",
+                       buffer, buffer_len);
+
+       memset (&vl, '\0', sizeof (vl));
+       memset (&type, '\0', sizeof (type));
+       status = 0;
+
+       while ((status == 0) && (buffer_len > sizeof (part_header_t)))
+       {
+               header = (part_header_t *) buffer;
+
+               if (ntohs (header->length) > buffer_len)
+                       break;
+               /* Assure that this loop terminates eventually */
+               if (ntohs (header->length) < 4)
+                       break;
+
+               if (ntohs (header->type) == TYPE_VALUES)
+               {
+                       status = parse_part_values (&buffer, &buffer_len,
+                                       &vl.values, &vl.values_len);
+
+                       if (status != 0)
+                       {
+                               DEBUG ("parse_part_values failed.");
+                               break;
+                       }
+
+                       if ((vl.time > 0)
+                                       && (strlen (vl.host) > 0)
+                                       && (strlen (vl.plugin) > 0)
+                                       && (strlen (type) > 0)
+                                       && (cache_check (type, &vl) == 0))
+                       {
+                               DEBUG ("network plugin: parse_packet:"
+                                               " dispatching values");
+                               plugin_dispatch_values (type, &vl);
+                       }
+                       else
+                       {
+                               DEBUG ("network plugin: parse_packet:"
+                                               " NOT dispatching values");
+                       }
+               }
+               else if (ntohs (header->type) == TYPE_TIME)
+               {
+                       uint64_t tmp = 0;
+                       status = parse_part_number (&buffer, &buffer_len, &tmp);
+                       if (status == 0)
+                               vl.time = (time_t) tmp;
+               }
+               else if (ntohs (header->type) == TYPE_HOST)
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       vl.host, sizeof (vl.host));
+                       DEBUG ("network plugin: parse_packet: vl.host = %s", vl.host);
+               }
+               else if (ntohs (header->type) == TYPE_PLUGIN)
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       vl.plugin, sizeof (vl.plugin));
+                       DEBUG ("network plugin: parse_packet: vl.plugin = %s", vl.plugin);
+               }
+               else if (ntohs (header->type) == TYPE_PLUGIN_INSTANCE)
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       vl.plugin_instance, sizeof (vl.plugin_instance));
+                       DEBUG ("network plugin: parse_packet: vl.plugin_instance = %s", vl.plugin_instance);
+               }
+               else if (ntohs (header->type) == TYPE_TYPE)
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       type, sizeof (type));
+                       DEBUG ("network plugin: parse_packet: type = %s", type);
+               }
+               else if (ntohs (header->type) == TYPE_TYPE_INSTANCE)
+               {
+                       status = parse_part_string (&buffer, &buffer_len,
+                                       vl.type_instance, sizeof (vl.type_instance));
+                       DEBUG ("network plugin: parse_packet: vl.type_instance = %s", vl.type_instance);
+               }
+               else
+               {
+                       DEBUG ("network plugin: parse_packet: Unknown part"
+                                       " type: 0x%0hx", ntohs (header->type));
+                       buffer = ((char *) buffer) + ntohs (header->length);
+               }
+       } /* while (buffer_len > sizeof (part_header_t)) */
+
+       return (0);
+} /* int parse_packet */
+
+static void free_sockent (sockent_t *se)
+{
+       sockent_t *next;
+       while (se != NULL)
+       {
+               next = se->next;
+               free (se->addr);
+               free (se);
+               se = next;
+       }
+} /* void free_sockent */
+
+/*
+ * int network_set_ttl
+ *
+ * Set the `IP_MULTICAST_TTL', `IP_TTL', `IPV6_MULTICAST_HOPS' or
+ * `IPV6_UNICAST_HOPS', depending on which option is applicable.
+ *
+ * The `struct addrinfo' is used to destinguish between unicast and multicast
+ * sockets.
+ */
+static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
+{
+       if ((network_config_ttl < 1) || (network_config_ttl > 255))
+               return (-1);
+
+       DEBUG ("ttl = %i", network_config_ttl);
 
        if (ai->ai_family == AF_INET)
        {
@@ -95,9 +637,12 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
                        optname = IP_TTL;
 
                if (setsockopt (se->fd, IPPROTO_IP, optname,
-                                       &ttl_int, sizeof (ttl_int)) == -1)
+                                       &network_config_ttl,
+                                       sizeof (network_config_ttl)) == -1)
                {
-                       syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("setsockopt: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (-1);
                }
        }
@@ -113,25 +658,31 @@ static int network_set_ttl (const sockent_t *se, const struct addrinfo *ai)
                        optname = IPV6_UNICAST_HOPS;
 
                if (setsockopt (se->fd, IPPROTO_IPV6, optname,
-                                       &ttl_int, sizeof (ttl_int)) == -1)
+                                       &network_config_ttl,
+                                       sizeof (network_config_ttl)) == -1)
                {
-                       syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("setsockopt: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        return (-1);
                }
        }
 
        return (0);
-}
+} /* int network_set_ttl */
 
 static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
 {
-       int loop = 1;
+       int loop = 0;
 
-       DBG ("fd = %i; calling `bind'", se->fd);
+       DEBUG ("fd = %i; calling `bind'", se->fd);
 
        if (bind (se->fd, ai->ai_addr, ai->ai_addrlen) == -1)
        {
-               syslog (LOG_ERR, "bind: %s", strerror (errno));
+               char errbuf[1024];
+               ERROR ("bind: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
 
@@ -142,7 +693,7 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
                {
                        struct ip_mreq mreq;
 
-                       DBG ("fd = %i; IPv4 multicast address found", se->fd);
+                       DEBUG ("fd = %i; IPv4 multicast address found", se->fd);
 
                        mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
                        mreq.imr_interface.s_addr = htonl (INADDR_ANY);
@@ -150,14 +701,20 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
                        if (setsockopt (se->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
                                                &loop, sizeof (loop)) == -1)
                        {
-                               syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("setsockopt: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
                                return (-1);
                        }
 
                        if (setsockopt (se->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                                                &mreq, sizeof (mreq)) == -1)
                        {
-                               syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("setsockopt: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
                                return (-1);
                        }
                }
@@ -170,7 +727,7 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
                {
                        struct ipv6_mreq mreq;
 
-                       DBG ("fd = %i; IPv6 multicast address found", se->fd);
+                       DEBUG ("fd = %i; IPv6 multicast address found", se->fd);
 
                        memcpy (&mreq.ipv6mr_multiaddr,
                                        &addr->sin6_addr,
@@ -190,44 +747,40 @@ static int network_bind_socket (const sockent_t *se, const struct addrinfo *ai)
                        if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
                                                &loop, sizeof (loop)) == -1)
                        {
-                               syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("setsockopt: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
                                return (-1);
                        }
 
                        if (setsockopt (se->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
                                                &mreq, sizeof (mreq)) == -1)
                        {
-                               syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("setsockopt: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
                                return (-1);
                        }
                }
        }
 
        return (0);
-}
+} /* int network_bind_socket */
 
-int network_create_socket (const char *node, const char *service)
+static sockent_t *network_create_socket (const char *node,
+               const char *service,
+               int listen)
 {
-       sockent_t *socklist_tail;
-
        struct addrinfo  ai_hints;
        struct addrinfo *ai_list, *ai_ptr;
        int              ai_return;
 
-       int num_added = 0;
-
-       DBG ("node = %s, service = %s", node, service);
-
-       if (operating_mode == MODE_LOCAL || operating_mode == MODE_LOG)
-       {
-               syslog (LOG_WARNING, "network_create_socket: There is no point opening a socket when you are in mode `%s'.",
-                               operating_mode == MODE_LOCAL ? "Local" : "Log");
-               return (-1);
-       }
+       sockent_t *se_head = NULL;
+       sockent_t *se_tail = NULL;
 
-       socklist_tail = socklist_head;
-       while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
-               socklist_tail = socklist_tail->next;
+       DEBUG ("node = %s, service = %s", node, service);
 
        memset (&ai_hints, '\0', sizeof (ai_hints));
        ai_hints.ai_flags    = 0;
@@ -237,17 +790,21 @@ int network_create_socket (const char *node, const char *service)
 #ifdef AI_ADDRCONFIG
        ai_hints.ai_flags |= AI_ADDRCONFIG;
 #endif
-       ai_hints.ai_family   = PF_UNSPEC;
+       ai_hints.ai_family   = AF_UNSPEC;
        ai_hints.ai_socktype = SOCK_DGRAM;
        ai_hints.ai_protocol = IPPROTO_UDP;
 
-       if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
+       ai_return = getaddrinfo (node, service, &ai_hints, &ai_list);
+       if (ai_return != 0)
        {
-               syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
-                               node == NULL ? "(null)" : node,
-                               service == NULL ? "(null)" : service,
-                               ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
-               return (-1);
+               char errbuf[1024];
+               ERROR ("getaddrinfo (%s, %s): %s",
+                               (node == NULL) ? "(null)" : node,
+                               (service == NULL) ? "(null)" : service,
+                               (ai_return == EAI_SYSTEM)
+                               ? sstrerror (errno, errbuf, sizeof (errbuf))
+                               : gai_strerror (ai_return));
+               return (NULL);
        }
 
        for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
@@ -256,13 +813,19 @@ int network_create_socket (const char *node, const char *service)
 
                if ((se = (sockent_t *) malloc (sizeof (sockent_t))) == NULL)
                {
-                       syslog (LOG_EMERG, "malloc: %s", strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("malloc: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        continue;
                }
 
                if ((se->addr = (struct sockaddr_storage *) malloc (sizeof (struct sockaddr_storage))) == NULL)
                {
-                       syslog (LOG_EMERG, "malloc: %s", strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("malloc: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        free (se);
                        continue;
                }
@@ -272,19 +835,23 @@ int network_create_socket (const char *node, const char *service)
                memcpy (se->addr, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
                se->addrlen = ai_ptr->ai_addrlen;
 
-               se->mode = operating_mode;
-               se->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
+               se->fd   = socket (ai_ptr->ai_family,
+                               ai_ptr->ai_socktype,
+                               ai_ptr->ai_protocol);
                se->next = NULL;
 
                if (se->fd == -1)
                {
-                       syslog (LOG_ERR, "socket: %s", strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("socket: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        free (se->addr);
                        free (se);
                        continue;
                }
 
-               if (operating_mode == MODE_SERVER)
+               if (listen != 0)
                {
                        if (network_bind_socket (se, ai_ptr) != 0)
                        {
@@ -294,260 +861,492 @@ int network_create_socket (const char *node, const char *service)
                                continue;
                        }
                }
-               else if (operating_mode == MODE_CLIENT)
+               else /* listen == 0 */
                {
                        network_set_ttl (se, ai_ptr);
                }
 
-               if (socklist_tail == NULL)
+               if (se_tail == NULL)
                {
-                       socklist_head = se;
-                       socklist_tail = se;
+                       se_head = se;
+                       se_tail = se;
                }
                else
                {
-                       socklist_tail->next = se;
-                       socklist_tail = se;
+                       se_tail->next = se;
+                       se_tail = se;
                }
 
-               num_added++;
-
                /* We don't open more than one write-socket per node/service pair.. */
-               if (operating_mode == MODE_CLIENT)
+               if (listen == 0)
                        break;
        }
 
        freeaddrinfo (ai_list);
 
-       return (num_added);
-}
+       return (se_head);
+} /* sockent_t *network_create_socket */
 
-static int network_connect_default (void)
+static sockent_t *network_create_default_socket (int listen)
 {
-       int ret;
+       sockent_t *se_ptr  = NULL;
+       sockent_t *se_head = NULL;
+       sockent_t *se_tail = NULL;
 
-       if (socklist_head != NULL)
-               return (0);
+       se_ptr = network_create_socket (NET_DEFAULT_V6_ADDR,
+                       NET_DEFAULT_PORT, listen);
 
-       DBG ("socklist_head is NULL");
+       /* Don't send to the same machine in IPv6 and IPv4 if both are available. */
+       if ((listen == 0) && (se_ptr != NULL))
+               return (se_ptr);
 
-       ret = 0;
+       if (se_ptr != NULL)
+       {
+               se_head = se_ptr;
+               se_tail = se_ptr;
+               while (se_tail->next != NULL)
+                       se_tail = se_tail->next;
+       }
 
-       if (network_create_socket (NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT) > 0)
-               ret++;
+       se_ptr = network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT, listen);
 
-       /* Don't use IPv4 and IPv6 in parallel by default.. */
-       if ((operating_mode == MODE_CLIENT) && (ret != 0))
-               return (ret);
+       if (se_tail == NULL)
+               return (se_ptr);
 
-       if (network_create_socket (NET_DEFAULT_V4_ADDR, NET_DEFAULT_PORT) > 0)
-               ret++;
+       se_tail->next = se_ptr;
+       return (se_head);
+} /* sockent_t *network_create_default_socket */
 
-       if (ret == 0)
-               ret = -1;
+static int network_add_listen_socket (const char *node, const char *service)
+{
+       sockent_t *se;
+       sockent_t *se_ptr;
+       int se_num = 0;
 
-       return (ret);
-}
+       if (service == NULL)
+               service = NET_DEFAULT_PORT;
 
-static int network_get_listen_socket (void)
-{
-       int fd;
-       int max_fd;
-       int status;
+       if (node == NULL)
+               se = network_create_default_socket (1 /* listen == true */);
+       else
+               se = network_create_socket (node, service, 1 /* listen == true */);
 
-       fd_set readfds;
-       sockent_t *se;
+       if (se == NULL)
+               return (-1);
 
-       if (socklist_head == NULL)
-               network_connect_default ();
+       for (se_ptr = se; se_ptr != NULL; se_ptr = se_ptr->next)
+               se_num++;
 
-       FD_ZERO (&readfds);
-       max_fd = -1;
-       for (se = socklist_head; se != NULL; se = se->next)
+       listen_sockets = (struct pollfd *) realloc (listen_sockets,
+                       (listen_sockets_num + se_num)
+                       * sizeof (struct pollfd));
+
+       for (se_ptr = se; se_ptr != NULL; se_ptr = se_ptr->next)
        {
-               if (se->mode != operating_mode)
-                       continue;
+               listen_sockets[listen_sockets_num].fd = se_ptr->fd;
+               listen_sockets[listen_sockets_num].events = POLLIN | POLLPRI;
+               listen_sockets[listen_sockets_num].revents = 0;
+               listen_sockets_num++;
+       } /* for (se) */
 
-               FD_SET (se->fd, &readfds);
-               if (se->fd >= max_fd)
-                       max_fd = se->fd + 1;
-       }
+       free_sockent (se);
+       return (0);
+} /* int network_add_listen_socket */
 
-       if (max_fd == -1)
-       {
-               syslog (LOG_WARNING, "No listen sockets found!");
+static int network_add_sending_socket (const char *node, const char *service)
+{
+       sockent_t *se;
+       sockent_t *se_ptr;
+
+       if (service == NULL)
+               service = NET_DEFAULT_PORT;
+
+       if (node == NULL)
+               se = network_create_default_socket (0 /* listen == false */);
+       else
+               se = network_create_socket (node, service, 0 /* listen == false */);
+
+       if (se == NULL)
                return (-1);
+
+       if (sending_sockets == NULL)
+       {
+               sending_sockets = se;
+               return (0);
        }
 
-       status = select (max_fd, &readfds, NULL, NULL, NULL);
+       for (se_ptr = sending_sockets; se_ptr->next != NULL; se_ptr = se_ptr->next)
+               /* seek end */;
 
-       if (status == -1)
+       se_ptr->next = se;
+       return (0);
+} /* int network_get_listen_socket */
+
+int network_receive (void)
+{
+       char buffer[BUFF_SIZE];
+       int  buffer_len;
+
+       int i;
+       int status;
+
+       if (listen_sockets_num == 0)
+               network_add_listen_socket (NULL, NULL);
+
+       if (listen_sockets_num == 0)
        {
-               if (errno != EINTR)
-                       syslog (LOG_ERR, "select: %s", strerror (errno));
+               ERROR ("network: Failed to open a listening socket.");
                return (-1);
        }
 
-       fd = -1;
-       for (se = socklist_head; se != NULL; se = se->next)
+       while (listen_loop == 0)
        {
-               if (se->mode != operating_mode)
-                       continue;
+               status = poll (listen_sockets, listen_sockets_num, -1);
 
-               if (FD_ISSET (se->fd, &readfds))
+               if (status <= 0)
                {
-                       fd = se->fd;
-                       break;
+                       char errbuf[1024];
+                       if (errno == EINTR)
+                               continue;
+                       ERROR ("poll failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       return (-1);
                }
-       }
 
-       if (fd == -1)
-               syslog (LOG_WARNING, "No socket ready..?");
+               for (i = 0; (i < listen_sockets_num) && (status > 0); i++)
+               {
+                       if ((listen_sockets[i].revents & (POLLIN | POLLPRI)) == 0)
+                               continue;
+                       status--;
+
+                       buffer_len = recv (listen_sockets[i].fd,
+                                       buffer, sizeof (buffer),
+                                       0 /* no flags */);
+                       if (buffer_len < 0)
+                       {
+                               char errbuf[1024];
+                               ERROR ("recv failed: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
+                               return (-1);
+                       }
+
+                       parse_packet (buffer, buffer_len);
+               } /* for (listen_sockets) */
+       } /* while (listen_loop == 0) */
 
-       DBG ("fd = %i", fd);
-       return (fd);
+       return (0);
 }
 
-int network_receive (char **host, char **type, char **inst, char **value)
+static void *receive_thread (void *arg)
 {
-       int fd;
-       char buffer[BUFF_SIZE];
+       return (network_receive () ? (void *) 1 : (void *) 0);
+} /* void *receive_thread */
 
-       struct sockaddr_storage addr;
-       socklen_t               addrlen;
+static void network_send_buffer (const char *buffer, int buffer_len)
+{
+       sockent_t *se;
        int status;
 
-       char *fields[4];
+       DEBUG ("network plugin: network_send_buffer: buffer_len = %i", buffer_len);
 
-       assert (operating_mode == MODE_SERVER);
+       for (se = sending_sockets; se != NULL; se = se->next)
+       {
+               while (42)
+               {
+                       status = sendto (se->fd, buffer, buffer_len, 0 /* no flags */,
+                                       (struct sockaddr *) se->addr, se->addrlen);
+                       if (status < 0)
+                       {
+                               char errbuf[1024];
+                               if (errno == EINTR)
+                                       continue;
+                               ERROR ("network plugin: sendto failed: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
+                               break;
+                       }
 
-       *host  = NULL;
-       *type  = NULL;
-       *inst  = NULL;
-       *value = NULL;
+                       break;
+               } /* while (42) */
+       } /* for (sending_sockets) */
+} /* void network_send_buffer */
 
-       if ((fd = network_get_listen_socket ()) < 0)
-               return (-1);
+static int add_to_buffer (char *buffer, int buffer_size,
+               value_list_t *vl_def, char *type_def,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       char *buffer_orig = buffer;
 
-       addrlen = sizeof (addr);
-       if (recvfrom (fd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addrlen) == -1)
+       if (strcmp (vl_def->host, vl->host) != 0)
        {
-               syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
-               return (-1);
+               if (write_part_string (&buffer, &buffer_size, TYPE_HOST,
+                                       vl->host, strlen (vl->host)) != 0)
+                       return (-1);
+               strcpy (vl_def->host, vl->host);
+               DEBUG ("network plugin: add_to_buffer: host = %s", vl->host);
        }
 
-       if ((*host = (char *) malloc (BUFF_SIZE)) == NULL)
+       if (vl_def->time != vl->time)
        {
-               syslog (LOG_EMERG, "malloc: %s", strerror (errno));
-               return (-1);
+               if (write_part_number (&buffer, &buffer_size, TYPE_TIME,
+                                       (uint64_t) vl->time))
+                       return (-1);
+               vl_def->time = vl->time;
+               DEBUG ("network plugin: add_to_buffer: time = %u",
+                               (unsigned int) vl->time);
        }
 
-       status = getnameinfo ((struct sockaddr *) &addr, addrlen,
-                       *host, BUFF_SIZE, NULL, 0, 0);
-       if (status != 0)
+       if (strcmp (vl_def->plugin, vl->plugin) != 0)
        {
-               free (*host); *host = NULL;
-               syslog (LOG_ERR, "getnameinfo: %s",
-                               status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
-               return (-1);
+               if (write_part_string (&buffer, &buffer_size, TYPE_PLUGIN,
+                                       vl->plugin, strlen (vl->plugin)) != 0)
+                       return (-1);
+               strcpy (vl_def->plugin, vl->plugin);
+               DEBUG ("network plugin: add_to_buffer: plugin = %s",
+                               vl->plugin);
        }
 
-       if (strsplit (buffer, fields, 4) != 3)
+       if (strcmp (vl_def->plugin_instance, vl->plugin_instance) != 0)
        {
-               syslog (LOG_WARNING, "Invalid message from `%s'", *host);
-               free (*host); *host = NULL;
-               return (1);
+               if (write_part_string (&buffer, &buffer_size, TYPE_PLUGIN_INSTANCE,
+                                       vl->plugin_instance,
+                                       strlen (vl->plugin_instance)) != 0)
+                       return (-1);
+               strcpy (vl_def->plugin_instance, vl->plugin_instance);
+               DEBUG ("network plugin: add_to_buffer: plugin_instance = %s",
+                               vl->plugin_instance);
        }
 
-       if ((*type = strdup (fields[0])) == NULL)
+       if (strcmp (type_def, ds->type) != 0)
        {
-               syslog (LOG_EMERG, "strdup: %s", strerror (errno));
-               free (*host); *host = NULL;
-               return (-1);
+               if (write_part_string (&buffer, &buffer_size, TYPE_TYPE,
+                                       ds->type, strlen (ds->type)) != 0)
+                       return (-1);
+               strcpy (type_def, ds->type);
+               DEBUG ("network plugin: add_to_buffer: type = %s", ds->type);
        }
 
-       if ((*inst = strdup (fields[1])) == NULL)
+       if (strcmp (vl_def->type_instance, vl->type_instance) != 0)
        {
-               syslog (LOG_EMERG, "strdup: %s", strerror (errno));
-               free (*host); *host = NULL;
-               free (*type); *type = NULL;
+               if (write_part_string (&buffer, &buffer_size, TYPE_TYPE_INSTANCE,
+                                       vl->type_instance,
+                                       strlen (vl->type_instance)) != 0)
+                       return (-1);
+               strcpy (vl_def->type_instance, vl->type_instance);
+               DEBUG ("network plugin: add_to_buffer: type_instance = %s",
+                               vl->type_instance);
+       }
+       
+       if (write_part_values (&buffer, &buffer_size, ds, vl) != 0)
                return (-1);
+
+       return (buffer - buffer_orig);
+} /* int add_to_buffer */
+
+static void flush_buffer (void)
+{
+       DEBUG ("network plugin: flush_buffer: send_buffer_fill = %i",
+                       send_buffer_fill);
+
+       network_send_buffer (send_buffer, send_buffer_fill);
+       send_buffer_ptr  = send_buffer;
+       send_buffer_fill = 0;
+       memset (&send_buffer_vl, '\0', sizeof (send_buffer_vl));
+       memset (send_buffer_type, '\0', sizeof (send_buffer_type));
+}
+
+static int network_write (const data_set_t *ds, const value_list_t *vl)
+{
+       int status;
+
+       /* If the value is already in the cache, we have received it via the
+        * network. We write it again if forwarding is activated. It's then in
+        * the cache and should we receive it again we will ignore it. */
+       status = cache_check (ds->type, vl);
+       if ((network_config_forward == 0)
+                       && (status != 0))
+               return (0);
+
+       pthread_mutex_lock (&send_buffer_lock);
+
+       status = add_to_buffer (send_buffer_ptr,
+                       sizeof (send_buffer) - send_buffer_fill,
+                       &send_buffer_vl, send_buffer_type,
+                       ds, vl);
+       if (status >= 0)
+       {
+               /* status == bytes added to the buffer */
+               send_buffer_fill += status;
+               send_buffer_ptr  += status;
        }
+       else
+       {
+               flush_buffer ();
+
+               status = add_to_buffer (send_buffer_ptr,
+                               sizeof (send_buffer) - send_buffer_fill,
+                               &send_buffer_vl, send_buffer_type,
+                               ds, vl);
 
-       if ((*value = strdup (fields[2])) == NULL)
+               if (status >= 0)
+               {
+                       send_buffer_fill += status;
+                       send_buffer_ptr  += status;
+               }
+       }
+
+       if (status < 0)
        {
-               syslog (LOG_EMERG, "strdup: %s", strerror (errno));
-               free (*host); *host = NULL;
-               free (*type); *type = NULL;
-               free (*inst); *inst = NULL;
-               return (-1);
+               ERROR ("network plugin: Unable to append to the "
+                               "buffer for some weird reason");
+       }
+       else if ((sizeof (send_buffer) - send_buffer_fill) < 15)
+       {
+               flush_buffer ();
        }
 
-       DBG ("host = %s, type = %s, inst = %s, value = %s",
-                       *host, *type, *inst, *value);
+       pthread_mutex_unlock (&send_buffer_lock);
 
-       return (0);
-}
+       return ((status < 0) ? -1 : 0);
+} /* int network_write */
 
-int network_send (char *type, char *inst, char *value)
+static int network_config (const char *key, const char *val)
 {
-       char buf[BUFF_SIZE];
-       int buflen;
+       char *node;
+       char *service;
 
-       sockent_t *se;
+       char *fields[3];
+       int   fields_num;
 
-       int ret;
-       int status;
+       if ((strcasecmp ("Listen", key) == 0)
+                       || (strcasecmp ("Server", key) == 0))
+       {
+               char *val_cpy = strdup (val);
+               if (val_cpy == NULL)
+                       return (1);
+
+               service = NET_DEFAULT_PORT;
+               fields_num = strsplit (val_cpy, fields, 3);
+               if ((fields_num != 1)
+                               && (fields_num != 2))
+                       return (1);
+               else if (fields_num == 2)
+                       service = fields[1];
+               node = fields[0];
+
+               if (strcasecmp ("Listen", key) == 0)
+                       network_add_listen_socket (node, service);
+               else
+                       network_add_sending_socket (node, service);
+       }
+       else if (strcasecmp ("TimeToLive", key) == 0)
+       {
+               int tmp = atoi (val);
+               if ((tmp > 0) && (tmp < 256))
+                       network_config_ttl = tmp;
+               else
+                       return (1);
+       }
+       else if (strcasecmp ("Forward", key) == 0)
+       {
+               if ((strcasecmp ("true", val) == 0)
+                               || (strcasecmp ("yes", val) == 0)
+                               || (strcasecmp ("on", val) == 0))
+                       network_config_forward = 1;
+               else
+                       network_config_forward = 0;
+       }
+       else if (strcasecmp ("CacheFlush", key) == 0)
+       {
+               int tmp = atoi (val);
+               if (tmp > 0)
+                       cache_flush_interval = tmp;
+               else return (1);
+       }
+       else
+       {
+               return (-1);
+       }
+       return (0);
+} /* int network_config */
 
-       DBG ("type = %s, inst = %s, value = %s", type, inst, value);
+static int network_shutdown (void)
+{
+       DEBUG ("Shutting down.");
 
-       assert (operating_mode == MODE_CLIENT);
+       listen_loop++;
 
-       buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, inst, value);
-       if ((buflen >= BUFF_SIZE) || (buflen < 1))
+       if (listen_thread != (pthread_t) 0)
        {
-               syslog (LOG_WARNING, "network_send: snprintf failed..");
-               return (-1);
+               pthread_kill (listen_thread, SIGTERM);
+               pthread_join (listen_thread, NULL /* no return value */);
+               listen_thread = (pthread_t) 0;
        }
-       buf[buflen] = '\0';
-       buflen++;
 
-       if (socklist_head == NULL)
-               network_connect_default ();
+       listen_thread = 0;
 
-       ret = 0;
-       for (se = socklist_head; se != NULL; se = se->next)
+       if (cache_tree != NULL)
        {
-               if (se->mode != operating_mode)
-                       continue;
+               void *key;
+               void *value;
 
-               while (1)
+               while (avl_pick (cache_tree, &key, &value) == 0)
                {
-                       status = sendto (se->fd, buf, buflen, 0,
-                                       (struct sockaddr *) se->addr, se->addrlen);
-
-                       if (status == -1)
-                       {
-                               if (errno == EINTR)
-                               {
-                                       DBG ("sendto was interrupted");
-                                       continue;
-                               }
-                               else
-                               {
-                                       syslog (LOG_ERR, "sendto: %s", strerror (errno));
-                                       ret = -1;
-                                       break;
-                               }
-                       }
-                       else if (ret >= 0)
-                               ret++;
-                       break;
+                       sfree (key);
+                       sfree (value);
                }
+               avl_destroy (cache_tree);
+               cache_tree = NULL;
        }
 
-       if (ret == 0)
-               syslog (LOG_WARNING, "Message wasn't sent to anybody..");
+       /* TODO: Close `sending_sockets' */
 
-       return (ret);
-}
+       plugin_unregister_config ("network");
+       plugin_unregister_init ("network");
+       plugin_unregister_write ("network");
+       plugin_unregister_shutdown ("network");
+
+       return (0);
+} /* int network_shutdown */
+
+static int network_init (void)
+{
+       plugin_register_shutdown ("network", network_shutdown);
+
+       send_buffer_ptr  = send_buffer;
+       send_buffer_fill = 0;
+       memset (&send_buffer_vl, '\0', sizeof (send_buffer_vl));
+       memset (send_buffer_type, '\0', sizeof (send_buffer_type));
+
+       cache_tree = avl_create ((int (*) (const void *, const void *)) strcmp);
+       cache_flush_last = time (NULL);
+
+       /* setup socket(s) and so on */
+       if (sending_sockets != NULL)
+               plugin_register_write ("network", network_write);
+
+       if ((listen_sockets_num != 0) && (listen_thread == 0))
+       {
+               int status;
+
+               status = pthread_create (&listen_thread, NULL /* no attributes */,
+                               receive_thread, NULL /* no argument */);
+
+               if (status != 0)
+               {
+                       char errbuf[1024];
+                       ERROR ("network: pthread_create failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+               }
+       }
+       return (0);
+} /* int network_init */
+
+void module_register (void)
+{
+       plugin_register_config ("network", network_config,
+                       config_keys, config_keys_num);
+       plugin_register_init   ("network", network_init);
+} /* void module_register */
index 2df989b..b20fc75 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 #define NET_DEFAULT_V6_ADDR "ff18::efc0:4a42"
 #define NET_DEFAULT_PORT    "25826"
 
-int network_create_socket (const char *node, const char *service);
-int network_receive (char **host, char **type, char **instance, char **value);
-int network_send (char *type, char *instance, char *value);
+#define TYPE_HOST            0x0000
+#define TYPE_TIME            0x0001
+#define TYPE_PLUGIN          0x0002
+#define TYPE_PLUGIN_INSTANCE 0x0003
+#define TYPE_TYPE            0x0004
+#define TYPE_TYPE_INSTANCE   0x0005
+#define TYPE_VALUES          0x0006
 
 #endif /* NETWORK_H */
index d0f33c5..299d63b 100644 (file)
--- a/src/nfs.c
+++ b/src/nfs.c
@@ -4,8 +4,7 @@
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -25,8 +24,6 @@
 #include "common.h"
 #include "plugin.h"
 
-#define MODULE_NAME "nfs"
-
 /* #if defined(KERNEL_LINUX) || defined(HAVE_LIBKSTAT) */
 #if KERNEL_LINUX
 # define NFS_HAVE_READ 1
@@ -34,9 +31,6 @@
 # define NFS_HAVE_READ 0
 #endif
 
-static char *nfs2_procedures_file  = "nfs2_procedures-%s.rrd";
-static char *nfs3_procedures_file  = "nfs3_procedures-%s.rrd";
-
 /*
 see /proc/net/rpc/nfs
 see http://www.missioncriticallinux.com/orph/NFS-Statistics
@@ -82,57 +76,58 @@ Number      Procedures  Procedures
 21                      commit
 */
 
-static char *nfs2_procedures_ds_def[] =
+#if NFS_HAVE_READ
+static const char *nfs2_procedures_names[] =
 {
-       "DS:null:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:getattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:setattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:root:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:lookup:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:read:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wrcache:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:write:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:create:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:remove:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rename:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:link:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:symlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:mkdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rmdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:fsstat:COUNTER:"COLLECTD_HEARTBEAT":0:U",
+       "null",
+       "getattr",
+       "setattr",
+       "root",
+       "lookup",
+       "readlink",
+       "read",
+       "wrcache",
+       "write",
+       "create",
+       "remove",
+       "rename",
+       "link",
+       "symlink",
+       "mkdir",
+       "rmdir",
+       "readdir",
+       "fsstat",
        NULL
 };
-static int nfs2_procedures_ds_num = 18;
+static int nfs2_procedures_names_num = 18;
 
-static char *nfs3_procedures_ds_def[] =
+static const char *nfs3_procedures_names[] =
 {
-       "DS:null:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:getattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:setattr:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:lookup:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:access:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:read:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:write:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:create:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:mkdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:symlink:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:mknod:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:remove:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rmdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rename:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:link:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readdir:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:readdirplus:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:fsstat:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:fsinfo:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:pathconf:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:commit:COUNTER:"COLLECTD_HEARTBEAT":0:U",
+       "null",
+       "getattr",
+       "setattr",
+       "lookup",
+       "access",
+       "readlink",
+       "read",
+       "write",
+       "create",
+       "mkdir",
+       "symlink",
+       "mknod",
+       "remove",
+       "rmdir",
+       "rename",
+       "link",
+       "readdir",
+       "readdirplus",
+       "fsstat",
+       "fsinfo",
+       "pathconf",
+       "commit",
        NULL
 };
-static int nfs3_procedures_ds_num = 22;
+static int nfs3_procedures_names_num = 22;
 
 #if HAVE_LIBKSTAT && 0
 extern kstat_ctl_t *kc;
@@ -146,7 +141,8 @@ static kstat_t *nfs4_ksp_server;
 
 /* Possibly TODO: NFSv4 statistics */
 
-static void nfs_init (void)
+#if 0
+static int nfs_init (void)
 {
 #if HAVE_LIBKSTAT && 0
        kstat_t *ksp_chain;
@@ -181,89 +177,45 @@ static void nfs_init (void)
        }
 #endif
 
-       return;
-}
+       return (0);
+} /* int nfs_init */
+#endif
 
 #define BUFSIZE 1024
-static void nfs2_procedures_write (char *host, char *inst, char *val)
-{
-       char filename[BUFSIZE];
-
-       if (snprintf (filename, BUFSIZE, nfs2_procedures_file, inst) > BUFSIZE)
-               return;
-
-       rrd_update_file (host, filename, val, nfs2_procedures_ds_def,
-                       nfs2_procedures_ds_num);
-}
-
-static void nfs3_procedures_write (char *host, char *inst, char *val)
-{
-       char filename[BUFSIZE];
-
-       if (snprintf (filename, BUFSIZE, nfs3_procedures_file, inst) > BUFSIZE)
-               return;
-
-       rrd_update_file (host, filename, val, nfs3_procedures_ds_def,
-                       nfs3_procedures_ds_num);
-}
-
-#if NFS_HAVE_READ
-static void nfs2_procedures_submit (unsigned long long *val, char *inst)
-{
-       char buf[BUFSIZE];
-       int retval = 0;
-
-       retval = snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
-                       "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
-                       "%llu:%llu:%llu", /* 18x %llu */
-                       (unsigned int) curtime,
-                       val[0], val[1], val[2], val[3], val[4], val[5], val[6],
-                       val[7], val[8], val[9], val[10], val[11], val[12],
-                       val[13], val[14], val[15], val[16], val[17]);
-
-
-       if (retval >= BUFSIZE)
-               return;
-       else if (retval < 0)
-       {
-               syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
-               return;
-       }
-
-       plugin_submit ("nfs2_procedures", inst, buf);
-}
-
-static void nfs3_procedures_submit (unsigned long long *val, char *inst)
+static void nfs_procedures_submit (const char *plugin_instance,
+               unsigned long long *val, const char **names, int len)
 {
-       char buf[BUFSIZE];
-       int retval = 0;
-
-       retval = snprintf(buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:"
-                       "%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu:"
-                       "%llu:%llu:%llu:%llu:%llu:%llu:%llu", /* 22x %llu */
-                       (unsigned int) curtime,
-                       val[0], val[1], val[2], val[3], val[4], val[5], val[6],
-                       val[7], val[8], val[9], val[10], val[11], val[12],
-                       val[13], val[14], val[15], val[16], val[17], val[18],
-                       val[19], val[20], val[21]);
-
-       if (retval >= BUFSIZE)
-               return;
-       else if (retval < 0)
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
+       int i;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "nfs");
+       strncpy (vl.plugin_instance, plugin_instance,
+                       sizeof (vl.plugin_instance));
+
+       for (i = 0; i < len; i++)
        {
-               syslog (LOG_ERR, "nfs: snprintf's format failed: %s", strerror (errno));
-               return;
+               values[0].counter = val[i];
+               strncpy (vl.type_instance, names[i],
+                               sizeof (vl.type_instance));
+               DEBUG ("%s-%s/nfs_procedure-%s = %llu",
+                               vl.plugin, vl.plugin_instance,
+                               vl.type_instance, val[i]);
+               plugin_dispatch_values ("nfs_procedure", &vl);
        }
-
-       plugin_submit("nfs3_procedures", inst, buf);
-}
-#endif /* NFS_HAVE_READ */
+} /* void nfs_procedures_submit */
 
 #if KERNEL_LINUX
 static void nfs_read_stats_file (FILE *fh, char *inst)
 {
        char buffer[BUFSIZE];
 
+       char plugin_instance[DATA_MAX_NAME_LEN];
+
        char *fields[48];
        int numfields = 0;
 
@@ -274,30 +226,45 @@ static void nfs_read_stats_file (FILE *fh, char *inst)
        {
                numfields = strsplit (buffer, fields, 48);
 
-               if (numfields < 2)
+               if (((numfields - 2) != nfs2_procedures_names_num)
+                               && ((numfields - 2)
+                                       != nfs3_procedures_names_num))
                        continue;
 
-               if (strncmp (fields[0], "proc2", 5) == 0)
+               if (strcmp (fields[0], "proc2") == 0)
                {
                        int i;
                        unsigned long long *values;
 
-                       if (numfields - 2 != nfs2_procedures_ds_num)
+                       if ((numfields - 2) != nfs2_procedures_names_num)
                        {
-                               syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS2 statistics.", numfields - 2);
+                               WARNING ("nfs plugin: Wrong "
+                                               "number of fields (= %i) "
+                                               "for NFSv2 statistics.",
+                                               numfields - 2);
                                continue;
                        }
 
-                       if ((values = (unsigned long long *) malloc (nfs2_procedures_ds_num * sizeof (unsigned long long))) == NULL)
+                       snprintf (plugin_instance, sizeof (plugin_instance),
+                                       "v2%s", inst);
+                       plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+
+                       values = (unsigned long long *) malloc (nfs2_procedures_names_num * sizeof (unsigned long long));
+                       if (values == NULL)
                        {
-                               syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("nfs plugin: malloc "
+                                               "failed: %s",
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
                                continue;
                        }
 
-                       for (i = 0; i < nfs2_procedures_ds_num; i++)
+                       for (i = 0; i < nfs2_procedures_names_num; i++)
                                values[i] = atoll (fields[i + 2]);
 
-                       nfs2_procedures_submit (values, inst);
+                       nfs_procedures_submit (plugin_instance, values,
+                                       nfs2_procedures_names,
+                                       nfs2_procedures_names_num);
 
                        free (values);
                }
@@ -306,27 +273,40 @@ static void nfs_read_stats_file (FILE *fh, char *inst)
                        int i;
                        unsigned long long *values;
 
-                       if (numfields - 2 != nfs3_procedures_ds_num)
+                       if ((numfields - 2) != nfs3_procedures_names_num)
                        {
-                               syslog (LOG_WARNING, "nfs: Wrong number of fields (= %i) for NFS3 statistics.", numfields - 2);
+                               WARNING ("nfs plugin: Wrong "
+                                               "number of fields (= %i) "
+                                               "for NFSv3 statistics.",
+                                               numfields - 2);
                                continue;
                        }
 
-                       if ((values = (unsigned long long *) malloc (nfs3_procedures_ds_num * sizeof (unsigned long long))) == NULL)
+                       snprintf (plugin_instance, sizeof (plugin_instance),
+                                       "v3%s", inst);
+                       plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+
+                       values = (unsigned long long *) malloc (nfs3_procedures_names_num * sizeof (unsigned long long));
+                       if (values == NULL)
                        {
-                               syslog (LOG_ERR, "nfs: malloc: %s", strerror (errno));
+                               char errbuf[1024];
+                               ERROR ("nfs plugin: malloc "
+                                               "failed: %s",
+                                               sstrerror (errno, errbuf, sizeof (errbuf)));
                                continue;
                        }
 
-                       for (i = 0; i < nfs3_procedures_ds_num; i++)
+                       for (i = 0; i < nfs3_procedures_names_num; i++)
                                values[i] = atoll (fields[i + 2]);
 
-                       nfs3_procedures_submit (values, inst);
+                       nfs_procedures_submit (plugin_instance, values,
+                                       nfs3_procedures_names,
+                                       nfs3_procedures_names_num);
 
                        free (values);
                }
-       }
-}
+       } /* while (fgets (buffer, BUFSIZE, fh) != NULL) */
+} /* void nfs_read_stats_file */
 #endif /* defined(KERNEL_LINUX) */
 #undef BUFSIZE
 
@@ -358,8 +338,7 @@ static void nfs2_read_kstat (kstat_t *ksp, char *inst)
 }
 #endif
 
-#if NFS_HAVE_READ
-static void nfs_read (void)
+static int nfs_read (void)
 {
 #if KERNEL_LINUX
        FILE *fh;
@@ -384,16 +363,14 @@ static void nfs_read (void)
        if (nfs2_ksp_server != NULL)
                nfs2_read_kstat (nfs2_ksp_server, "server");
 #endif /* defined(HAVE_LIBKSTAT) */
+
+       return (0);
 }
-#else
-# define nfs_read NULL
 #endif /* NFS_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, nfs_init, nfs_read, NULL);
-       plugin_register ("nfs2_procedures", NULL, NULL, nfs2_procedures_write);
-       plugin_register ("nfs3_procedures", NULL, NULL, nfs3_procedures_write);
-}
-
-#undef MODULE_NAME
+#if NFS_HAVE_READ
+       plugin_register_read ("nfs", nfs_read);
+#endif
+} /* void module_register */
index 08c0c9f..b5f1a46 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/ntpd.c
- * Copyright (C) 2006  Florian octo Forster
+ * Copyright (C) 2006-2007  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
@@ -23,9 +23,6 @@
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
-#include "utils_debug.h"
-
-#define MODULE_NAME "ntpd"
 
 #if HAVE_SYS_SOCKET_H
 # define NTPD_HAVE_READ 1
 #if HAVE_NETINET_TCP_H
 # include <netinet/tcp.h>
 #endif
-#if HAVE_SYS_POLL_H
-# include <sys/poll.h>
+#if HAVE_POLL_H
+# include <poll.h>
 #endif
 
-static char *config_keys[] =
+static const char *config_keys[] =
 {
        "Host",
        "Port",
@@ -63,34 +60,12 @@ static char *config_keys[] =
 };
 static int config_keys_num = 2;
 
-/* drift */
-static char *time_offset_file     = "ntpd/time_offset-%s.rrd";
-static char *time_dispersion_file = "ntpd/time_dispersion-%s.rrd";
-static char *time_delay_file      = "ntpd/delay-%s.rrd";
-
-/* used for `time_offset', `time_dispersion', and `delay' */
-static char *sec_ds_def[] =
-{
-       "DS:seconds:GAUGE:"COLLECTD_HEARTBEAT":-1000000:1000000",
-       NULL
-};
-static int sec_ds_num = 1;
-
-static char *frequency_offset_file = "ntpd/frequency_offset-%s.rrd";
-static char *frequency_offset_ds_def[] =
-{
-       "DS:ppm:GAUGE:"COLLECTD_HEARTBEAT":-1000000:1000000",
-       NULL
-};
-static int frequency_offset_ds_num = 1;
-
 #if NTPD_HAVE_READ
 # define NTPD_DEFAULT_HOST "localhost"
 # define NTPD_DEFAULT_PORT "123"
 static int   sock_descr = -1;
 static char *ntpd_host = NULL;
 static char *ntpd_port = NULL;
-#endif
 
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  * The following definitions were copied from the NTPd distribution  *
@@ -286,7 +261,7 @@ static int refclock_names_num = 45;
  * End of the copied stuff..                                         *
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
-static int ntpd_config (char *key, char *value)
+static int ntpd_config (const char *key, const char *value)
 {
        if (strcasecmp (key, "host") == 0)
        {
@@ -310,64 +285,22 @@ static int ntpd_config (char *key, char *value)
        return (0);
 }
 
-static void ntpd_init (void)
-{
-       return;
-}
-
-static void ntpd_write_sec (char *host, char *inst, char *val, char *file)
-{
-       char buf[256];
-       int  status;
-
-       status = snprintf (buf, 256, file, inst);
-       if ((status < 1) || (status >= 256))
-               return;
-
-       rrd_update_file (host, buf, val,
-                       sec_ds_def, sec_ds_num);
-}
-
-static void ntpd_write_time_offset (char *host, char *inst, char *val)
-{
-       ntpd_write_sec (host, inst, val, time_offset_file);
-}
-
-static void ntpd_write_time_dispersion (char *host, char *inst, char *val)
-{
-       ntpd_write_sec (host, inst, val, time_dispersion_file);
-}
-
-static void ntpd_write_delay (char *host, char *inst, char *val)
-{
-       ntpd_write_sec (host, inst, val, time_delay_file);
-}
-
-static void ntpd_write_frequency_offset (char *host, char *inst, char *val)
-{
-       char buf[256];
-       int  status;
-
-       status = snprintf (buf, 256, frequency_offset_file, inst);
-       if ((status < 1) || (status >= 256))
-               return;
-
-       rrd_update_file (host, buf, val,
-                       frequency_offset_ds_def, frequency_offset_ds_num);
-}
-
-#if NTPD_HAVE_READ
-static void ntpd_submit (char *type, char *inst, double value)
+static void ntpd_submit (char *type, char *type_inst, double value)
 {
-       char buf[256];
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (buf, 256, "%u:%.8f", (unsigned int) curtime, value) >= 256)
-               return;
+       values[0].gauge = value;
 
-       DBG ("type = %s; inst = %s; value = %s;",
-                       type, inst, buf);
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "ntpd");
+       strcpy (vl.plugin_instance, "");
+       strncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
 
-       plugin_submit (type, inst, buf);
+       plugin_dispatch_values (type, &vl);
 }
 
 /* returns `tv0 - tv1' in milliseconds or 0 if `tv1 > tv0' */
@@ -408,7 +341,7 @@ static int ntpd_connect (void)
        if (sock_descr >= 0)
                return (sock_descr);
 
-       DBG ("Opening a new socket");
+       DEBUG ("Opening a new socket");
 
        host = ntpd_host;
        if (host == NULL)
@@ -426,12 +359,12 @@ static int ntpd_connect (void)
 
        if ((status = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0)
        {
-               DBG ("getaddrinfo (%s, %s): %s",
-                               host, port,
-                               status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
-               syslog (LOG_ERR, "ntpd plugin: getaddrinfo (%s, %s): %s",
+               char errbuf[1024];
+               ERROR ("ntpd plugin: getaddrinfo (%s, %s): %s",
                                host, port,
-                               status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
+                               (status == EAI_SYSTEM)
+                               ? sstrerror (errno, errbuf, sizeof (errbuf))
+                               : gai_strerror (status));
                return (-1);
        }
 
@@ -458,8 +391,8 @@ static int ntpd_connect (void)
 
        if (sock_descr < 0)
        {
-               DBG ("Unable to connect to server.");
-               syslog (LOG_ERR, "ntpd plugin: Unable to connect to server.");
+               DEBUG ("Unable to connect to server.");
+               ERROR ("ntpd plugin: Unable to connect to server.");
        }
 
        return (sock_descr);
@@ -507,8 +440,9 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
 
        if (gettimeofday (&time_end, NULL) < 0)
        {
-               syslog (LOG_ERR, "ntpd plugin: gettimeofday failed: %s",
-                               strerror (errno));
+               char errbuf[1024];
+               ERROR ("ntpd plugin: gettimeofday failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
        time_end.tv_sec++; /* wait for a most one second */
@@ -518,8 +452,9 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
        {
                if (gettimeofday (&time_now, NULL) < 0)
                {
-                       syslog (LOG_ERR, "ntpd plugin: gettimeofday failed: %s",
-                                       strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("ntpd plugin: gettimeofday failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (-1);
                }
 
@@ -531,7 +466,7 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
                poll_s.events  = POLLIN | POLLPRI;
                poll_s.revents = 0;
                
-               DBG ("Polling for %ims", timeout);
+               DEBUG ("Polling for %ims", timeout);
                status = poll (&poll_s, 1, timeout);
 
                if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
@@ -539,15 +474,15 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
 
                if (status < 0)
                {
-                       DBG ("poll failed: %s", strerror (errno));
-                       syslog (LOG_ERR, "ntpd plugin: poll failed: %s",
-                                       strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("ntpd plugin: poll failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                        return (-1);
                }
 
                if (status == 0) /* timeout */
                {
-                       DBG ("timeout reached.");
+                       DEBUG ("timeout reached.");
                        break;
                }
 
@@ -559,50 +494,52 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
 
                if (status < 0)
                {
-                       DBG ("recv(2) failed: %s", strerror (errno));
-                       DBG ("Closing socket #%i", sd);
+                       char errbuf[1024];
+                       DEBUG ("recv(2) failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       DEBUG ("Closing socket #%i", sd);
                        close (sd);
                        sock_descr = sd = -1;
                        return (-1);
                }
 
-               DBG ("recv'd %i bytes", status);
+               DEBUG ("recv'd %i bytes", status);
 
                /* 
                 * Do some sanity checks first
                 */
                if (status < RESP_HEADER_SIZE)
                {
-                       syslog (LOG_WARNING, "ntpd plugin: Short (%i bytes) packet received",
+                       WARNING ("ntpd plugin: Short (%i bytes) packet received",
                                        (int) status);
                        continue;
                }
                if (INFO_MODE (res.rm_vn_mode) != MODE_PRIVATE)
                {
-                       syslog (LOG_NOTICE, "ntpd plugin: Packet received with mode %i",
+                       NOTICE ("ntpd plugin: Packet received with mode %i",
                                        INFO_MODE (res.rm_vn_mode));
                        continue;
                }
                if (INFO_IS_AUTH (res.auth_seq))
                {
-                       syslog (LOG_NOTICE, "ntpd plugin: Encrypted packet received");
+                       NOTICE ("ntpd plugin: Encrypted packet received");
                        continue;
                }
                if (!ISRESPONSE (res.rm_vn_mode))
                {
-                       syslog (LOG_NOTICE, "ntpd plugin: Received request packet, "
+                       NOTICE ("ntpd plugin: Received request packet, "
                                        "wanted response");
                        continue;
                }
                if (INFO_MBZ (res.mbz_itemsize))
                {
-                       syslog (LOG_WARNING, "ntpd plugin: Received packet with nonzero "
+                       WARNING ("ntpd plugin: Received packet with nonzero "
                                        "MBZ field!");
                        continue;
                }
                if (res.implementation != IMPL_XNTPD)
                {
-                       syslog (LOG_WARNING, "ntpd plugin: Asked for request of type %i, "
+                       WARNING ("ntpd plugin: Asked for request of type %i, "
                                        "got %i", (int) IMPL_XNTPD, (int) res.implementation);
                        continue;
                }
@@ -610,7 +547,7 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
                /* Check for error code */
                if (INFO_ERR (res.err_nitems) != 0)
                {
-                       syslog (LOG_ERR, "ntpd plugin: Received error code %i",
+                       ERROR ("ntpd plugin: Received error code %i",
                                        (int) INFO_ERR(res.err_nitems));
                        return ((int) INFO_ERR (res.err_nitems));
                }
@@ -618,13 +555,13 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
                /* extract number of items in this packet and the size of these items */
                pkt_item_num = INFO_NITEMS (res.err_nitems);
                pkt_item_len = INFO_ITEMSIZE (res.mbz_itemsize);
-               DBG ("pkt_item_num = %i; pkt_item_len = %i;",
+               DEBUG ("pkt_item_num = %i; pkt_item_len = %i;",
                                pkt_item_num, pkt_item_len);
 
                /* Check if the reported items fit in the packet */
                if ((pkt_item_num * pkt_item_len) > (status - RESP_HEADER_SIZE))
                {
-                       syslog (LOG_ERR, "ntpd plugin: %i items * %i bytes > "
+                       ERROR ("ntpd plugin: %i items * %i bytes > "
                                        "%i bytes - %i bytes header",
                                        (int) pkt_item_num, (int) pkt_item_len,
                                        (int) status, (int) RESP_HEADER_SIZE);
@@ -644,14 +581,14 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
                 * items have the same size. Discard invalid packets. */
                if (items_num == 0) /* first packet */
                {
-                       DBG ("*res_size = %i", pkt_item_len);
+                       DEBUG ("*res_size = %i", pkt_item_len);
                        *res_size = pkt_item_len;
                }
                else if (*res_size != pkt_item_len)
                {
-                       DBG ("Error: *res_size = %i; pkt_item_len = %i;",
+                       DEBUG ("Error: *res_size = %i; pkt_item_len = %i;",
                                        *res_size, pkt_item_len);
-                       syslog (LOG_ERR, "Item sizes differ.");
+                       ERROR ("Item sizes differ.");
                        continue;
                }
 
@@ -666,14 +603,14 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
                pkt_padding = 0;
                if (pkt_item_len < res_item_size)
                        pkt_padding = res_item_size - pkt_item_len;
-               DBG ("res_item_size = %i; pkt_padding = %i;",
+               DEBUG ("res_item_size = %i; pkt_padding = %i;",
                                res_item_size, pkt_padding);
 
                /* Extract the sequence number */
                pkt_sequence = INFO_SEQ (res.auth_seq);
                if ((pkt_sequence < 0) || (pkt_sequence > MAXSEQ))
                {
-                       syslog (LOG_ERR, "ntpd plugin: Received packet with sequence %i",
+                       ERROR ("ntpd plugin: Received packet with sequence %i",
                                        pkt_sequence);
                        continue;
                }
@@ -681,7 +618,7 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
                /* Check if this sequence has been received before. If so, discard it. */
                if (pkt_recvd[pkt_sequence] != '\0')
                {
-                       syslog (LOG_NOTICE, "ntpd plugin: Sequence %i received twice",
+                       NOTICE ("ntpd plugin: Sequence %i received twice",
                                        pkt_sequence);
                        continue;
                }
@@ -692,27 +629,27 @@ static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
                {
                        if (pkt_lastseq != -1)
                        {
-                               syslog (LOG_ERR, "ntpd plugin: Two packets which both "
+                               ERROR ("ntpd plugin: Two packets which both "
                                                "claim to be the last one in the "
                                                "sequence have been received.");
                                continue;
                        }
                        pkt_lastseq = pkt_sequence;
-                       DBG ("Last sequence = %i;", pkt_lastseq);
+                       DEBUG ("Last sequence = %i;", pkt_lastseq);
                }
 
                /*
                 * Enough with the checks. Copy the data now.
                 * We start by allocating some more memory.
                 */
-               DBG ("realloc (%p, %i)", (void *) *res_data,
+               DEBUG ("realloc (%p, %i)", (void *) *res_data,
                                (items_num + pkt_item_num) * res_item_size);
                items = realloc ((void *) *res_data,
                                (items_num + pkt_item_num) * res_item_size);
                if (items == NULL)
                {
                        items = *res_data;
-                       syslog (LOG_ERR, "ntpd plugin: realloc failed.");
+                       ERROR ("ntpd plugin: realloc failed.");
                        continue;
                }
                items_num += pkt_item_num;
@@ -782,13 +719,13 @@ static int ntpd_send_request (int req_code, int req_items, int req_size, char *r
        if (req_data != NULL)
                memcpy ((void *) req.data, (const void *) req_data, req_data_len);
 
-       DBG ("req_items = %i; req_size = %i; req_data = %p;",
+       DEBUG ("req_items = %i; req_size = %i; req_data = %p;",
                        req_items, req_size, (void *) req_data);
 
        status = swrite (sd, (const char *) &req, REQ_LEN_NOMAC);
        if (status < 0)
        {
-               DBG ("`swrite' failed. Closing socket #%i", sd);
+               DEBUG ("`swrite' failed. Closing socket #%i", sd);
                close (sd);
                sock_descr = sd = -1;
                return (status);
@@ -835,7 +772,7 @@ static double ntpd_read_fp (int32_t val_int)
        return (val_double);
 }
 
-static void ntpd_read (void)
+static int ntpd_read (void)
 {
        struct info_kernel *ik;
        int                 ik_num;
@@ -859,19 +796,19 @@ static void ntpd_read (void)
 
        if (status != 0)
        {
-               DBG ("ntpd_do_query failed with status %i", status);
-               return;
+               DEBUG ("ntpd_do_query failed with status %i", status);
+               return (-1);
        }
        if ((ik == NULL) || (ik_num == 0) || (ik_size == 0))
        {
-               DBG ("ntpd_do_query returned: ik = %p; ik_num = %i; ik_size = %i;",
+               DEBUG ("ntpd_do_query returned: ik = %p; ik_num = %i; ik_size = %i;",
                                (void *) ik, ik_num, ik_size);
-               return;
+               return (-1);
        }
 
        /* kerninfo -> estimated error */
 
-       DBG ("info_kernel:\n"
+       DEBUG ("info_kernel:\n"
                        "  pll offset    = %.8f\n"
                        "  pll frequency = %.8f\n" /* drift compensation */
                        "  est error     = %.8f\n",
@@ -879,9 +816,9 @@ static void ntpd_read (void)
                        ntpd_read_fp (ik->freq),
                        ntpd_read_fp (ik->esterror));
 
-       ntpd_submit ("ntpd_frequency_offset", "loop",  ntpd_read_fp (ik->freq));
-       ntpd_submit ("ntpd_time_offset",      "loop",  ntpd_read_fp (ik->offset));
-       ntpd_submit ("ntpd_time_offset",      "error", ntpd_read_fp (ik->esterror));
+       ntpd_submit ("frequency_offset", "loop",  ntpd_read_fp (ik->freq));
+       ntpd_submit ("time_offset",      "loop",  ntpd_read_fp (ik->offset));
+       ntpd_submit ("time_offset",      "error", ntpd_read_fp (ik->esterror));
 
        free (ik);
        ik = NULL;
@@ -892,14 +829,14 @@ static void ntpd_read (void)
                        sizeof (struct info_peer_summary));
        if (status != 0)
        {
-               DBG ("ntpd_do_query failed with status %i", status);
-               return;
+               DEBUG ("ntpd_do_query failed with status %i", status);
+               return (-1);
        }
        if ((ps == NULL) || (ps_num == 0) || (ps_size == 0))
        {
-               DBG ("ntpd_do_query returned: ps = %p; ps_num = %i; ps_size = %i;",
+               DEBUG ("ntpd_do_query returned: ps = %p; ps_num = %i; ps_size = %i;",
                                (void *) ps, ps_num, ps_size);
-               return;
+               return (-1);
        }
 
        for (i = 0; i < ps_num; i++)
@@ -936,9 +873,10 @@ static void ntpd_read (void)
                                        NULL, 0, 0 /* no flags */);
                        if (status != 0)
                        {
-                               syslog (LOG_ERR, "ntpd plugin: getnameinfo failed: %s",
-                                               status == EAI_SYSTEM
-                                               ? strerror (errno)
+                               char errbuf[1024];
+                               ERROR ("ntpd plugin: getnameinfo failed: %s",
+                                               (status == EAI_SYSTEM)
+                                               ? sstrerror (errno, errbuf, sizeof (errbuf))
                                                : gai_strerror (status));
                                continue;
                        }
@@ -986,7 +924,7 @@ static void ntpd_read (void)
                        }
                }
 
-               DBG ("peer %i:\n"
+               DEBUG ("peer %i:\n"
                                "  peername   = %s\n"
                                "  srcadr     = 0x%08x\n"
                                "  delay      = %f\n"
@@ -1004,29 +942,24 @@ static void ntpd_read (void)
                                ntpd_read_fp (ptr->dispersion));
 
                if (refclock_id != 1) /* not the system clock (offset will always be zero.. */
-                       ntpd_submit ("ntpd_time_offset", peername, offset);
-               ntpd_submit ("ntpd_time_dispersion", peername, ntpd_read_fp (ptr->dispersion));
+                       ntpd_submit ("time_offset", peername, offset);
+               ntpd_submit ("time_dispersion", peername, ntpd_read_fp (ptr->dispersion));
                if (refclock_id == 0) /* not a reference clock */
-                       ntpd_submit ("ntpd_delay", peername, ntpd_read_fp (ptr->delay));
+                       ntpd_submit ("delay", peername, ntpd_read_fp (ptr->delay));
        }
 
        free (ps);
        ps = NULL;
 
-       return;
-}
-#else
-# define ntpd_read NULL
+       return (0);
+} /* int ntpd_read */
 #endif /* NTPD_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, ntpd_init, ntpd_read, NULL);
-       plugin_register ("ntpd_time_offset", NULL, NULL, ntpd_write_time_offset);
-       plugin_register ("ntpd_time_dispersion", NULL, NULL, ntpd_write_time_dispersion);
-       plugin_register ("ntpd_delay", NULL, NULL, ntpd_write_delay);
-       plugin_register ("ntpd_frequency_offset", NULL, NULL, ntpd_write_frequency_offset);
-       cf_register (MODULE_NAME, ntpd_config, config_keys, config_keys_num);
-}
-
-#undef MODULE_NAME
+#if NTPD_HAVE_READ
+       plugin_register_config ("ntpd", ntpd_config,
+                       config_keys, config_keys_num);
+       plugin_register_read ("ntpd", ntpd_read);
+#endif /* NTPD_HAVE_READ */
+} /* void module_register */
diff --git a/src/nut.c b/src/nut.c
new file mode 100644 (file)
index 0000000..6af12b8
--- /dev/null
+++ b/src/nut.c
@@ -0,0 +1,300 @@
+/**
+ * collectd - src/nut.c
+ * Copyright (C) 2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#if HAVE_UPSCLIENT_H
+# include <upsclient.h>
+# define NUT_HAVE_READ 1
+#else
+# define NUT_HAVE_READ 0
+#endif
+
+#if NUT_HAVE_READ
+struct nut_ups_s;
+typedef struct nut_ups_s nut_ups_t;
+struct nut_ups_s
+{
+  UPSCONN   *conn;
+  char      *upsname;
+  char      *hostname;
+  int        port;
+  nut_ups_t *next;
+};
+
+static nut_ups_t *upslist_head = NULL;
+
+static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
+static int read_busy = 0;
+
+static const char *config_keys[] =
+{
+  "UPS"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
+
+static void free_nut_ups_t (nut_ups_t *ups)
+{
+  if (ups->conn != NULL)
+  {
+    upscli_disconnect (ups->conn);
+    sfree (ups->conn);
+  }
+  sfree (ups->hostname);
+  sfree (ups->upsname);
+  sfree (ups);
+} /* void free_nut_ups_t */
+
+static int nut_add_ups (const char *name)
+{
+  nut_ups_t *ups;
+  int status;
+
+  DEBUG ("nut plugin: nut_add_ups (name = %s);", name);
+
+  ups = (nut_ups_t *) malloc (sizeof (nut_ups_t));
+  if (ups == NULL)
+  {
+    ERROR ("nut plugin: nut_add_ups: malloc failed.");
+    return (1);
+  }
+  memset (ups, '\0', sizeof (nut_ups_t));
+
+  status = upscli_splitname (name, &ups->upsname, &ups->hostname,
+      &ups->port);
+  if (status != 0)
+  {
+    ERROR ("nut plugin: nut_add_ups: upscli_splitname (%s) failed.", name);
+    free_nut_ups_t (ups);
+    return (1);
+  }
+
+  if (upslist_head == NULL)
+    upslist_head = ups;
+  else
+  {
+    nut_ups_t *last = upslist_head;
+    while (last->next != NULL)
+      last = last->next;
+    last->next = ups;
+  }
+
+  return (0);
+} /* int nut_add_ups */
+
+static int nut_config (const char *key, const char *value)
+{
+  if (strcasecmp (key, "UPS") == 0)
+    return (nut_add_ups (value));
+  else
+    return (-1);
+} /* int nut_config */
+
+static void nut_submit (nut_ups_t *ups, const char *type,
+    const char *type_instance, gauge_t value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = value;
+
+  vl.values = values;
+  vl.values_len = STATIC_ARRAY_SIZE (values);
+  vl.time = time (NULL);
+  strncpy (vl.host,
+      (strcasecmp (ups->hostname, "localhost") == 0)
+      ? hostname_g
+      : ups->hostname,
+      sizeof (vl.host));
+  strcpy (vl.plugin, "nut");
+  strncpy (vl.plugin_instance, ups->upsname, sizeof (vl.plugin_instance));
+  strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+
+  vl.host[sizeof (vl.host) - 1] = '\0';
+  vl.plugin_instance[sizeof (vl.plugin_instance) - 1] = '\0';
+  vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+
+  plugin_dispatch_values (type, &vl);
+} /* void nut_submit */
+
+static int nut_read_one (nut_ups_t *ups)
+{
+  const char *query[3] = {"VAR", ups->upsname, NULL};
+  unsigned int query_num = 2;
+  char **answer;
+  unsigned int answer_num;
+  int status;
+
+  /* (Re-)Connect if we have no connection */
+  if (ups->conn == NULL)
+  {
+    ups->conn = (UPSCONN *) malloc (sizeof (UPSCONN));
+    if (ups->conn == NULL)
+    {
+      ERROR ("nut plugin: malloc failed.");
+      return (-1);
+    }
+
+    status = upscli_connect (ups->conn, ups->hostname, ups->port,
+       UPSCLI_CONN_TRYSSL);
+    if (status != 0)
+    {
+      ERROR ("nut plugin: nut_read_one: upscli_connect (%s, %i) failed: %s",
+         ups->hostname, ups->port, upscli_strerror (ups->conn));
+      sfree (ups->conn);
+      return (-1);
+    }
+
+    INFO ("nut plugin: Connection to (%s, %i) established.",
+       ups->hostname, ups->port);
+  } /* if (ups->conn == NULL) */
+
+  /* nut plugin: nut_read_one: upscli_list_start (adpos) failed: Protocol
+   * error */
+  status = upscli_list_start (ups->conn, query_num, query);
+  if (status != 0)
+  {
+    ERROR ("nut plugin: nut_read_one: upscli_list_start (%s) failed: %s",
+       ups->upsname, upscli_strerror (ups->conn));
+    upscli_disconnect (ups->conn);
+    sfree (ups->conn);
+    return (-1);
+  }
+
+  while ((status = upscli_list_next (ups->conn, query_num, query,
+         &answer_num, &answer)) == 1)
+  {
+    char  *key;
+    double value;
+
+    if (answer_num < 4)
+      continue;
+
+    key = answer[2];
+    value = atof (answer[3]);
+
+    if (strncmp ("ambient.", key, 8) == 0)
+    {
+      if (strcmp ("ambient.humidity", key) == 0)
+       nut_submit (ups, "humidity", "ambient", value);
+      else if (strcmp ("ambient.temperature", key) == 0)
+       nut_submit (ups, "temperature", "ambient", value);
+    }
+    else if (strncmp ("battery.", key, 8) == 0)
+    {
+      if (strcmp ("battery.charge", key) == 0)
+       nut_submit (ups, "percent", "charge", value);
+      else if (strcmp ("battery.current", key) == 0)
+       nut_submit (ups, "current", "battery", value);
+      else if (strcmp ("battery.runtime", key) == 0)
+       nut_submit (ups, "timeleft", "battery", value);
+      else if (strcmp ("battery.temperature", key) == 0)
+       nut_submit (ups, "temperature", "battery", value);
+      else if (strcmp ("battery.voltage", key) == 0)
+       nut_submit (ups, "voltage", "battery", value);
+    }
+    else if (strncmp ("input.", key, 6) == 0)
+    {
+      if (strcmp ("input.frequency", key) == 0)
+       nut_submit (ups, "frequency", "input", value);
+      else if (strcmp ("input.voltage", key) == 0)
+       nut_submit (ups, "voltage", "input", value);
+    }
+    else if (strncmp ("output.", key, 7) == 0)
+    {
+      if (strcmp ("output.current", key) == 0)
+       nut_submit (ups, "current", "output", value);
+      else if (strcmp ("output.frequency", key) == 0)
+       nut_submit (ups, "frequency", "output", value);
+      else if (strcmp ("output.voltage", key) == 0)
+       nut_submit (ups, "voltage", "output", value);
+    }
+    else if (strncmp ("ups.", key, 4) == 0)
+    {
+      if (strcmp ("ups.load", key) == 0)
+       nut_submit (ups, "percent", "load", value);
+      else if (strcmp ("ups.power", key) == 0)
+       nut_submit (ups, "power", "ups", value);
+      else if (strcmp ("ups.temperature", key) == 0)
+       nut_submit (ups, "temperature", "ups", value);
+    }
+  } /* while (upscli_list_next) */
+
+  return (0);
+} /* int nut_read_one */
+
+static int nut_read (void)
+{
+  nut_ups_t *ups;
+  int success = 0;
+
+  pthread_mutex_lock (&read_lock);
+  success = read_busy;
+  read_busy = 1;
+  pthread_mutex_unlock (&read_lock);
+
+  if (success != 0)
+    return (0);
+
+  for (ups = upslist_head; ups != NULL; ups = ups->next)
+    if (nut_read_one (ups) == 0)
+      success++;
+
+  pthread_mutex_lock (&read_lock);
+  read_busy = 0;
+  pthread_mutex_unlock (&read_lock);
+
+  return ((success != 0) ? 0 : -1);
+} /* int nut_read */
+
+static int nut_shutdown (void)
+{
+  nut_ups_t *this;
+  nut_ups_t *next;
+
+  this = upslist_head;
+  while (this != NULL)
+  {
+    next = this->next;
+    free_nut_ups_t (this);
+    this = next;
+  }
+
+  return (0);
+} /* int nut_shutdown */
+#endif /* NUT_HAVE_READ */
+
+void module_register (void)
+{
+#if NUT_HAVE_READ
+  plugin_register_config ("nut", nut_config, config_keys, config_keys_num);
+  plugin_register_read ("nut", nut_read);
+  plugin_register_shutdown ("nut", nut_shutdown);
+#endif
+} /* void module_register */
+
+/* vim: set sw=2 ts=8 sts=2 tw=78 : */
diff --git a/src/perl.c b/src/perl.c
new file mode 100644 (file)
index 0000000..bcf7bb1
--- /dev/null
@@ -0,0 +1,1142 @@
+/**
+ * collectd - src/perl.c
+ * Copyright (C) 2007  Sebastian Harl
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Sebastian Harl <sh at tokkee.org>
+ **/
+
+/*
+ * This plugin embeds a Perl interpreter into collectd and provides an
+ * interface for collectd plugins written in perl.
+ */
+
+#include "collectd.h"
+#include "common.h"
+
+#include "configfile.h"
+
+#include <EXTERN.h>
+#include <perl.h>
+
+#include <XSUB.h>
+
+/* Some versions of Perl define their own version of DEBUG... :-/ */
+#ifdef DEBUG
+# undef DEBUG
+#endif /* DEBUG */
+
+/* ... while we want the definition found in plugin.h. */
+#include "plugin.h"
+
+#define PLUGIN_INIT     0
+#define PLUGIN_READ     1
+#define PLUGIN_WRITE    2
+#define PLUGIN_SHUTDOWN 3
+#define PLUGIN_LOG      4
+
+#define PLUGIN_TYPES    5
+
+#define PLUGIN_DATASET  255
+
+#define log_debug(...) DEBUG ("perl: " __VA_ARGS__)
+#define log_warn(...) WARNING ("perl: " __VA_ARGS__)
+#define log_err(...) ERROR ("perl: " __VA_ARGS__)
+
+
+/* this is defined in DynaLoader.a */
+void boot_DynaLoader (PerlInterpreter *, CV *);
+
+static XS (Collectd_plugin_register);
+static XS (Collectd_plugin_unregister);
+static XS (Collectd_plugin_dispatch_values);
+static XS (Collectd_plugin_log);
+
+
+/*
+ * private data types
+ */
+
+typedef struct {
+       int len;
+       int *values;
+} ds_types_t;
+
+typedef struct {
+       int wait_time;
+       int wait_left;
+
+       SV  *sub;
+} pplugin_t;
+
+
+/*
+ * private variables
+ */
+
+/* valid configuration file keys */
+static const char *config_keys[] =
+{
+       "LoadPlugin",
+       "BaseName",
+       "IncludeDir"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static PerlInterpreter *perl = NULL;
+
+static char base_name[DATA_MAX_NAME_LEN] = "";
+
+static char *plugin_types[] = { "init", "read", "write", "shutdown" };
+static HV   *plugins[PLUGIN_TYPES];
+static HV   *data_sets;
+
+static struct {
+       char name[64];
+       XS ((*f));
+} api[] =
+{
+       { "Collectd::plugin_register",        Collectd_plugin_register },
+       { "Collectd::plugin_unregister",      Collectd_plugin_unregister },
+       { "Collectd::plugin_dispatch_values", Collectd_plugin_dispatch_values },
+       { "Collectd::plugin_log",             Collectd_plugin_log },
+       { "", NULL }
+};
+
+
+/*
+ * Helper functions for data type conversion.
+ */
+
+/*
+ * data source:
+ * [
+ *   {
+ *     name => $ds_name,
+ *     type => $ds_type,
+ *     min  => $ds_min,
+ *     max  => $ds_max
+ *   },
+ *   ...
+ * ]
+ */
+static int hv2data_source (HV *hash, data_source_t *ds)
+{
+       SV **tmp = NULL;
+
+       if ((NULL == hash) || (NULL == ds))
+               return -1;
+
+       if (NULL != (tmp = Perl_hv_fetch (perl, hash, "name", 4, 0))) {
+               strncpy (ds->name, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+               ds->name[DATA_MAX_NAME_LEN - 1] = '\0';
+       }
+       else {
+               log_err ("hv2data_source: No DS name given.");
+               return -1;
+       }
+
+       if (NULL != (tmp = Perl_hv_fetch (perl, hash, "type", 4, 0))) {
+               ds->type = SvIV (*tmp);
+
+               if ((DS_TYPE_COUNTER != ds->type) && (DS_TYPE_GAUGE != ds->type)) {
+                       log_err ("hv2data_source: Invalid DS type.");
+                       return -1;
+               }
+       }
+       else {
+               ds->type = DS_TYPE_COUNTER;
+       }
+
+       if (NULL != (tmp = Perl_hv_fetch (perl, hash, "min", 3, 0)))
+               ds->min = SvNV (*tmp);
+       else
+               ds->min = NAN;
+
+       if (NULL != (tmp = Perl_hv_fetch (perl, hash, "max", 3, 0)))
+               ds->max = SvNV (*tmp);
+       else
+               ds->max = NAN;
+       return 0;
+} /* static data_source_t *hv2data_source (HV *) */
+
+static int av2value (char *name, AV *array, value_t *value, int len)
+{
+       SV **tmp = NULL;
+
+       ds_types_t *ds = NULL;
+
+       int i = 0;
+
+       if ((NULL == name) || (NULL == array) || (NULL == value))
+               return -1;
+
+       if (Perl_av_len (perl, array) < len - 1)
+               len = Perl_av_len (perl, array) + 1;
+
+       if (0 >= len)
+               return -1;
+
+       tmp = Perl_hv_fetch (perl, data_sets, name, strlen (name), 0);
+       if (NULL == tmp) {
+               log_err ("av2value: No dataset for \"%s\".", name);
+               return -1;
+       }
+       ds = (ds_types_t *)SvIV ((SV *)SvRV (*tmp));
+
+       if (ds->len < len) {
+               log_warn ("av2value: Value length exceeds data set length.");
+               len = ds->len;
+       }
+
+       for (i = 0; i < len; ++i) {
+               SV **tmp = Perl_av_fetch (perl, array, i, 0);
+
+               if (NULL != tmp) {
+                       if (DS_TYPE_COUNTER == ds->values[i])
+                               value[i].counter = SvIV (*tmp);
+                       else
+                               value[i].gauge = SvNV (*tmp);
+               }
+               else {
+                       return -1;
+               }
+       }
+       return len;
+} /* static int av2value (char *, AV *, value_t *, int) */
+
+static int data_set2av (data_set_t *ds, AV *array)
+{
+       int i = 0;
+
+       if ((NULL == ds) || (NULL == array))
+               return -1;
+
+       Perl_av_extend (perl, array, ds->ds_num);
+
+       for (i = 0; i < ds->ds_num; ++i) {
+               HV *source = Perl_newHV (perl);
+
+               if (NULL == Perl_hv_store (perl, source, "name", 4,
+                               Perl_newSVpv (perl, ds->ds[i].name, 0), 0))
+                       return -1;
+
+               if (NULL == Perl_hv_store (perl, source, "type", 4,
+                               Perl_newSViv (perl, ds->ds[i].type), 0))
+                       return -1;
+
+               if (! isnan (ds->ds[i].min))
+                       if (NULL == Perl_hv_store (perl, source, "min", 3,
+                                       Perl_newSVnv (perl, ds->ds[i].min), 0))
+                               return -1;
+
+               if (! isnan (ds->ds[i].max))
+                       if (NULL == Perl_hv_store (perl, source, "max", 3,
+                                       Perl_newSVnv (perl, ds->ds[i].max), 0))
+                               return -1;
+
+               if (NULL == Perl_av_store (perl, array, i,
+                               Perl_newRV_noinc (perl, (SV *)source)))
+                       return -1;
+       }
+       return 0;
+} /* static int data_set2av (data_set_t *, AV *) */
+
+static int value_list2hv (value_list_t *vl, data_set_t *ds, HV *hash)
+{
+       AV *values = NULL;
+
+       int i   = 0;
+       int len = 0;
+
+       if ((NULL == vl) || (NULL == ds) || (NULL == hash))
+               return -1;
+
+       len = vl->values_len;
+
+       if (ds->ds_num < len) {
+               log_warn ("value2av: Value length exceeds data set length.");
+               len = ds->ds_num;
+       }
+
+       values = Perl_newAV (perl);
+       Perl_av_extend (perl, values, len - 1);
+
+       for (i = 0; i < len; ++i) {
+               SV *val = NULL;
+
+               if (DS_TYPE_COUNTER == ds->ds[i].type)
+                       val = Perl_newSViv (perl, vl->values[i].counter);
+               else
+                       val = Perl_newSVnv (perl, vl->values[i].gauge);
+
+               if (NULL == Perl_av_store (perl, values, i, val)) {
+                       Perl_av_undef (perl, values);
+                       return -1;
+               }
+       }
+
+       if (NULL == Perl_hv_store (perl, hash, "values", 6,
+                       Perl_newRV_noinc (perl, (SV *)values), 0))
+               return -1;
+
+       if (0 != vl->time)
+               if (NULL == Perl_hv_store (perl, hash, "time", 4,
+                               Perl_newSViv (perl, vl->time), 0))
+                       return -1;
+
+       if ('\0' != vl->host[0])
+               if (NULL == Perl_hv_store (perl, hash, "host", 4,
+                               Perl_newSVpv (perl, vl->host, 0), 0))
+                       return -1;
+
+       if ('\0' != vl->plugin[0])
+               if (NULL == Perl_hv_store (perl, hash, "plugin", 6,
+                               Perl_newSVpv (perl, vl->plugin, 0), 0))
+                       return -1;
+
+       if ('\0' != vl->plugin_instance[0])
+               if (NULL == Perl_hv_store (perl, hash, "plugin_instance", 15,
+                               Perl_newSVpv (perl, vl->plugin_instance, 0), 0))
+                       return -1;
+
+       if ('\0' != vl->type_instance[0])
+               if (NULL == Perl_hv_store (perl, hash, "type_instance", 13,
+                               Perl_newSVpv (perl, vl->type_instance, 0), 0))
+                       return -1;
+       return 0;
+} /* static int value2av (value_list_t *, data_set_t *, HV *) */
+
+
+/*
+ * Internal functions.
+ */
+
+static char *get_module_name (char *buf, size_t buf_len, const char *module) {
+       int status = 0;
+       if (base_name[0] == '\0')
+               status = snprintf (buf, buf_len, "%s", module);
+       else
+               status = snprintf (buf, buf_len, "%s::%s", base_name, module);
+       if ((status < 0) || (status >= buf_len))
+               return (NULL);
+       buf[buf_len] = '\0';
+       return (buf);
+} /* char *get_module_name */
+
+/*
+ * Add a new plugin with the given name.
+ */
+static int pplugin_register (int type, const char *name, SV *sub)
+{
+       pplugin_t *p = NULL;
+
+       if ((type < 0) || (type >= PLUGIN_TYPES))
+               return -1;
+
+       if (NULL == name)
+               return -1;
+
+       p = (pplugin_t *)smalloc (sizeof (pplugin_t));
+       /* this happens during parsing of config file,
+        * thus interval_g is not set correctly */
+       p->wait_time = 10;
+       p->wait_left = 0;
+       p->sub = Perl_newSVsv (perl, sub);
+
+       if (NULL == Perl_hv_store (perl, plugins[type], name, strlen (name),
+                               Perl_sv_setref_pv (perl, Perl_newSV (perl, 0), 0, p), 0)) {
+               log_debug ("pplugin_register: Failed to add plugin \"%s\" (\"%s\")",
+                               name, SvPV_nolen (sub));
+               Perl_sv_free (perl, p->sub);
+               sfree (p);
+               return -1;
+       }
+       return 0;
+} /* static int pplugin_register (int, char *, SV *) */
+
+/*
+ * Removes the plugin with the given name and frees any ressources.
+ */
+static int pplugin_unregister (int type, char *name)
+{
+       SV *tmp = NULL;
+
+       if ((type < 0) || (type >= PLUGIN_TYPES))
+               return -1;
+
+       if (NULL == name)
+               return -1;
+
+       /* freeing the allocated memory of the element itself (pplugin_t *) causes
+        * a segfault during perl_destruct () thus I assume perl somehow takes
+        * care of this... */
+
+       tmp = Perl_hv_delete (perl, plugins[type], name, strlen (name), 0);
+       if (NULL != tmp) {
+               pplugin_t *p = (pplugin_t *)SvIV ((SV *)SvRV (tmp));
+               Perl_sv_free (perl, p->sub);
+       }
+       return 0;
+} /* static int pplugin_unregister (char *) */
+
+/*
+ * Add a plugin's data set definition.
+ */
+static int pplugin_register_data_set (char *name, AV *dataset)
+{
+       int len = -1;
+       int i   = 0;
+
+       data_source_t *ds  = NULL;
+       data_set_t    *set = NULL;
+
+       ds_types_t *types = NULL;
+
+       if ((NULL == name) || (NULL == dataset))
+               return -1;
+
+       len = Perl_av_len (perl, dataset);
+
+       if (-1 == len)
+               return -1;
+
+       ds  = (data_source_t *)smalloc ((len + 1) * sizeof (data_source_t));
+       set = (data_set_t *)smalloc (sizeof (data_set_t));
+
+       types = (ds_types_t *)smalloc (sizeof (ds_types_t));
+       types->len = len + 1;
+       types->values = (int *)smalloc ((types->len) * sizeof (int));
+
+       for (i = 0; i <= len; ++i) {
+               SV **elem = Perl_av_fetch (perl, dataset, i, 0);
+
+               if (NULL == elem)
+                       return -1;
+
+               if (! (SvROK (*elem) && (SVt_PVHV == SvTYPE (SvRV (*elem))))) {
+                       log_err ("pplugin_register_data_set: Invalid data source.");
+                       return -1;
+               }
+
+               if (-1 == hv2data_source ((HV *)SvRV (*elem), &ds[i]))
+                       return -1;
+
+               types->values[i] = ds[i].type;
+               log_debug ("pplugin_register_data_set: "
+                               "DS.name = \"%s\", DS.type = %i, DS.min = %f, DS.max = %f",
+                               ds[i].name, ds[i].type, ds[i].min, ds[i].max);
+       }
+
+       if (NULL == Perl_hv_store (perl, data_sets, name, strlen (name),
+                       Perl_sv_setref_pv (perl, Perl_newSV (perl, 0), 0, types), 0))
+               return -1;
+
+       strncpy (set->type, name, DATA_MAX_NAME_LEN);
+       set->type[DATA_MAX_NAME_LEN - 1] = '\0';
+
+       set->ds_num = len + 1;
+       set->ds = ds;
+       return plugin_register_data_set (set);
+} /* static int pplugin_register_data_set (char *, SV *) */
+
+/*
+ * Remove a plugin's data set definition.
+ */
+static int pplugin_unregister_data_set (char *name)
+{
+       SV *tmp = NULL;
+
+       if (NULL == name)
+               return 0;
+
+       /* freeing the allocated memory of the element itself (ds_types_t *)
+        * causes a segfault during perl_destruct () thus I assume perl somehow
+        * takes care of this... */
+
+       tmp = Perl_hv_delete (perl, data_sets, name, strlen (name), 0);
+       if (NULL != tmp) {
+               ds_types_t *ds = (ds_types_t *)SvIV ((SV *)SvRV (tmp));
+               sfree (ds->values);
+       }
+       return plugin_unregister_data_set (name);
+} /* static int pplugin_unregister_data_set (char *) */
+
+/*
+ * Submit the values to the write functions.
+ *
+ * value list:
+ * {
+ *   values => [ @values ],
+ *   time   => $time,
+ *   host   => $host,
+ *   plugin => $plugin,
+ *   plugin_instance => $pinstance,
+ *   type_instance   => $tinstance,
+ * }
+ */
+static int pplugin_dispatch_values (char *name, HV *values)
+{
+       value_list_t list = VALUE_LIST_INIT;
+       value_t      *val = NULL;
+
+       SV **tmp = NULL;
+
+       int ret = 0;
+
+       if ((NULL == name) || (NULL == values))
+               return -1;
+
+       if ((NULL == (tmp = Perl_hv_fetch (perl, values, "values", 6, 0)))
+                       || (! (SvROK (*tmp) && (SVt_PVAV == SvTYPE (SvRV (*tmp)))))) {
+               log_err ("pplugin_dispatch_values: No valid values given.");
+               return -1;
+       }
+
+       {
+               AV  *array = (AV *)SvRV (*tmp);
+               int len    = Perl_av_len (perl, array) + 1;
+
+               val = (value_t *)smalloc (len * sizeof (value_t));
+
+               list.values_len = av2value (name, (AV *)SvRV (*tmp), val, len);
+               list.values = val;
+
+               if (-1 == list.values_len) {
+                       sfree (val);
+                       return -1;
+               }
+       }
+
+       if (NULL != (tmp = Perl_hv_fetch (perl, values, "time", 4, 0))) {
+               list.time = (time_t)SvIV (*tmp);
+       }
+       else {
+               list.time = time (NULL);
+       }
+
+       if (NULL != (tmp = Perl_hv_fetch (perl, values, "host", 4, 0))) {
+               strncpy (list.host, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+               list.host[DATA_MAX_NAME_LEN - 1] = '\0';
+       }
+       else {
+               strcpy (list.host, hostname_g);
+       }
+
+       if (NULL != (tmp = Perl_hv_fetch (perl, values, "plugin", 6, 0))) {
+               strncpy (list.plugin, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+               list.plugin[DATA_MAX_NAME_LEN - 1] = '\0';
+       }
+
+       if (NULL != (tmp = Perl_hv_fetch (perl, values,
+                       "plugin_instance", 15, 0))) {
+               strncpy (list.plugin_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+               list.plugin_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+       }
+
+       if (NULL != (tmp = Perl_hv_fetch (perl, values, "type_instance", 13, 0))) {
+               strncpy (list.type_instance, SvPV_nolen (*tmp), DATA_MAX_NAME_LEN);
+               list.type_instance[DATA_MAX_NAME_LEN - 1] = '\0';
+       }
+
+       ret = plugin_dispatch_values (name, &list);
+
+       sfree (val);
+       return ret;
+} /* static int pplugin_dispatch_values (char *, HV *) */
+
+/*
+ * Call a plugin's working function.
+ */
+static int pplugin_call (int type, char *name, SV *sub, va_list ap)
+{
+       int retvals = 0;
+       I32 xflags  = G_NOARGS;
+
+       int ret = 0;
+
+       dSP;
+
+       if ((type < 0) || (type >= PLUGIN_TYPES))
+               return -1;
+
+       ENTER;
+       SAVETMPS;
+
+       PUSHMARK (SP);
+
+       if (PLUGIN_WRITE == type) {
+               /*
+                * $_[0] = $plugin_type;
+                *
+                * $_[1] =
+                * [
+                *   {
+                *     name => $ds_name,
+                *     type => $ds_type,
+                *     min  => $ds_min,
+                *     max  => $ds_max
+                *   },
+                *   ...
+                * ];
+                *
+                * $_[2] =
+                * {
+                *   values => [ $v1, ... ],
+                *   time   => $time,
+                *   host   => $hostname,
+                *   plugin => $plugin,
+                *   plugin_instance => $instance,
+                *   type_instance   => $type_instance
+                * };
+                */
+               data_set_t   *ds;
+               value_list_t *vl;
+
+               AV *pds = Perl_newAV (perl);
+               HV *pvl = Perl_newHV (perl);
+
+               ds = va_arg (ap, data_set_t *);
+               vl = va_arg (ap, value_list_t *);
+
+               if (-1 == data_set2av (ds, pds))
+                       return -1;
+
+               if (-1 == value_list2hv (vl, ds, pvl))
+                       return -1;
+
+               XPUSHs (sv_2mortal (Perl_newSVpv (perl, ds->type, 0)));
+               XPUSHs (sv_2mortal (Perl_newRV_noinc (perl, (SV *)pds)));
+               XPUSHs (sv_2mortal (Perl_newRV_noinc (perl, (SV *)pvl)));
+
+               xflags = 0;
+       }
+       else if (PLUGIN_LOG == type) {
+               /*
+                * $_[0] = $level;
+                *
+                * $_[1] = $message;
+                */
+               XPUSHs (sv_2mortal (Perl_newSViv (perl, va_arg (ap, int))));
+               XPUSHs (sv_2mortal (Perl_newSVpv (perl, va_arg (ap, char *), 0)));
+
+               xflags = 0;
+       }
+
+       PUTBACK;
+
+       /* prevent an endless loop */
+       if (PLUGIN_LOG != type)
+               log_debug ("pplugin_call: executing %s::%s->%s()",
+                               base_name, name, plugin_types[type]);
+
+       retvals = Perl_call_sv (perl, sub, G_SCALAR | xflags);
+
+       SPAGAIN;
+       if (1 > retvals) {
+               if (PLUGIN_LOG != type)
+                       log_warn ("pplugin_call: "
+                                       "%s::%s->%s() returned void - assuming true",
+                                       base_name, name, plugin_types[type]);
+       }
+       else {
+               SV *tmp = POPs;
+               if (! SvTRUE (tmp))
+                       ret = -1;
+       }
+
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+       return ret;
+} /* static int pplugin_call (int, char *, SV *, va_list) */
+
+/*
+ * Call all working functions of the given type.
+ */
+static int pplugin_call_all (int type, ...)
+{
+       SV *tmp = NULL;
+
+       char *plugin;
+       I32  len;
+
+       if ((type < 0) || (type >= PLUGIN_TYPES))
+               return -1;
+
+       if (0 == Perl_hv_iterinit (perl, plugins[type]))
+               return 0;
+
+       while (NULL != (tmp = Perl_hv_iternextsv (perl, plugins[type],
+                       &plugin, &len))) {
+               pplugin_t *p;
+               va_list   ap;
+
+               int status;
+
+               va_start (ap, type);
+
+               p = (pplugin_t *)SvIV ((SV *)SvRV (tmp));
+
+               if (p->wait_left > 0)
+                       p->wait_left -= interval_g;
+
+               if (p->wait_left > 0)
+                       continue;
+
+               if (0 == (status = pplugin_call (type, plugin, p->sub, ap))) {
+                       p->wait_left = 0;
+                       p->wait_time = interval_g;
+               }
+               else if (PLUGIN_READ == type) {
+                       p->wait_left = p->wait_time;
+                       p->wait_time <<= 1;
+
+                       if (p->wait_time > 86400)
+                               p->wait_time = 86400;
+
+                       log_warn ("%s->read() failed. Will suspend it for %i seconds.",
+                                       plugin, p->wait_left);
+               }
+               else if (PLUGIN_INIT == type) {
+                       int i = 0;
+
+                       log_err ("%s->init() failed. Plugin will be disabled.",
+                                       plugin, status);
+
+                       for (i = 0; i < PLUGIN_TYPES; ++i)
+                               pplugin_unregister (i, plugin);
+               }
+               else if (PLUGIN_LOG != type) {
+                       log_warn ("%s->%s() failed with status %i.",
+                                       plugin, plugin_types[type], status);
+               }
+
+               va_end (ap);
+       }
+       return 0;
+} /* static int pplugin_call_all (int, ...) */
+
+
+/*
+ * Exported Perl API.
+ */
+
+/*
+ * Collectd::plugin_register (type, name, data).
+ *
+ * type:
+ *   init, read, write, shutdown, data set
+ *
+ * name:
+ *   name of the plugin
+ *
+ * data:
+ *   reference to the plugin's subroutine that does the work or the data set
+ *   definition
+ */
+static XS (Collectd_plugin_register)
+{
+       int type  = 0;
+       SV  *data = NULL;
+
+       int ret = 0;
+
+       dXSARGS;
+
+       if (3 != items) {
+               log_err ("Usage: Collectd::plugin_register(type, name, data)");
+               XSRETURN_EMPTY;
+       }
+
+       log_debug ("Collectd::plugin_register: "
+                       "type = \"%i\", name = \"%s\", \"%s\"",
+                       (int)SvIV (ST (0)), SvPV_nolen (ST (1)), SvPV_nolen (ST (2)));
+
+       type = (int)SvIV (ST (0));
+       data = ST (2);
+
+       if ((type >= 0) && (type < PLUGIN_TYPES)
+                       && SvROK (data) && (SVt_PVCV == SvTYPE (SvRV (data)))) {
+               ret = pplugin_register (type, SvPV_nolen (ST (1)), data);
+       }
+       else if ((type == PLUGIN_DATASET)
+                       && SvROK (data) && (SVt_PVAV == SvTYPE (SvRV (data)))) {
+               ret = pplugin_register_data_set (SvPV_nolen (ST (1)),
+                               (AV *)SvRV (data));
+       }
+       else {
+               log_err ("Collectd::plugin_register: Invalid data.");
+               XSRETURN_EMPTY;
+       }
+
+       if (0 == ret)
+               XSRETURN_YES;
+       else
+               XSRETURN_EMPTY;
+} /* static XS (Collectd_plugin_register) */
+
+/*
+ * Collectd::plugin_unregister (type, name).
+ *
+ * type:
+ *   init, read, write, shutdown, data set
+ *
+ * name:
+ *   name of the plugin
+ */
+static XS (Collectd_plugin_unregister)
+{
+       int type = 0;
+       int ret  = 0;
+
+       dXSARGS;
+
+       if (2 != items) {
+               log_err ("Usage: Collectd::plugin_unregister(type, name)");
+               XSRETURN_EMPTY;
+       }
+
+       log_debug ("Collectd::plugin_unregister: type = \"%i\", name = \"%s\"",
+                       (int)SvIV (ST (0)), SvPV_nolen (ST (1)));
+
+       type = (int)SvIV (ST (0));
+
+       if ((type >= 0) && (type < PLUGIN_TYPES)) {
+               ret = pplugin_unregister (type, SvPV_nolen (ST (1)));
+       }
+       else if (type == PLUGIN_DATASET) {
+               ret = pplugin_unregister_data_set (SvPV_nolen (ST (1)));
+       }
+       else {
+               log_err ("Collectd::plugin_unregister: Invalid type.");
+               XSRETURN_EMPTY;
+       }
+
+       if (0 == ret)
+               XSRETURN_YES;
+       else
+               XSRETURN_EMPTY;
+} /* static XS (Collectd_plugin_unregister) */
+
+/*
+ * Collectd::plugin_dispatch_values (name, values).
+ *
+ * name:
+ *   name of the plugin
+ *
+ * values:
+ *   value list to submit
+ */
+static XS (Collectd_plugin_dispatch_values)
+{
+       SV *values = NULL;
+
+       int ret = 0;
+
+       dXSARGS;
+
+       if (2 != items) {
+               log_err ("Usage: Collectd::plugin_dispatch_values(name, values)");
+               XSRETURN_EMPTY;
+       }
+
+       log_debug ("Collectd::plugin_dispatch_values: "
+                       "name = \"%s\", values=\"%s\"",
+                       SvPV_nolen (ST (0)), SvPV_nolen (ST (1)));
+
+       values = ST (1);
+
+       if (! (SvROK (values) && (SVt_PVHV == SvTYPE (SvRV (values))))) {
+               log_err ("Collectd::plugin_dispatch_values: Invalid values.");
+               XSRETURN_EMPTY;
+       }
+
+       if ((NULL == ST (0)) || (NULL == values))
+               XSRETURN_EMPTY;
+
+       ret = pplugin_dispatch_values (SvPV_nolen (ST (0)), (HV *)SvRV (values));
+
+       if (0 == ret)
+               XSRETURN_YES;
+       else
+               XSRETURN_EMPTY;
+} /* static XS (Collectd_plugin_dispatch_values) */
+
+/*
+ * Collectd::plugin_log (level, message).
+ *
+ * level:
+ *   log level (LOG_DEBUG, ... LOG_ERR)
+ *
+ * message:
+ *   log message
+ */
+static XS (Collectd_plugin_log)
+{
+       dXSARGS;
+
+       if (2 != items) {
+               log_err ("Usage: Collectd::plugin_log(level, message)");
+               XSRETURN_EMPTY;
+       }
+
+       log_debug ("Collectd::plugin_log: level = %i, message = \"%s\"",
+                       SvIV (ST (0)), SvPV_nolen (ST (1)));
+       plugin_log (SvIV (ST (0)), SvPV_nolen (ST (1)));
+       XSRETURN_YES;
+} /* static XS (Collectd_plugin_log) */
+
+/*
+ * Collectd::bootstrap ().
+ */
+static XS (boot_Collectd)
+{
+       HV   *stash = NULL;
+       char *file  = __FILE__;
+
+       struct {
+               char name[64];
+               SV   *value;
+       } consts[] =
+       {
+               { "Collectd::TYPE_INIT",       Perl_newSViv (perl, PLUGIN_INIT) },
+               { "Collectd::TYPE_READ",       Perl_newSViv (perl, PLUGIN_READ) },
+               { "Collectd::TYPE_WRITE",      Perl_newSViv (perl, PLUGIN_WRITE) },
+               { "Collectd::TYPE_SHUTDOWN",   Perl_newSViv (perl, PLUGIN_SHUTDOWN) },
+               { "Collectd::TYPE_LOG",        Perl_newSViv (perl, PLUGIN_LOG) },
+               { "Collectd::TYPE_DATASET",    Perl_newSViv (perl, PLUGIN_DATASET) },
+               { "Collectd::DS_TYPE_COUNTER", Perl_newSViv (perl, DS_TYPE_COUNTER) },
+               { "Collectd::DS_TYPE_GAUGE",   Perl_newSViv (perl, DS_TYPE_GAUGE) },
+               { "Collectd::LOG_ERR",         Perl_newSViv (perl, LOG_ERR) },
+               { "Collectd::LOG_WARNING",     Perl_newSViv (perl, LOG_WARNING) },
+               { "Collectd::LOG_NOTICE",      Perl_newSViv (perl, LOG_NOTICE) },
+               { "Collectd::LOG_INFO",        Perl_newSViv (perl, LOG_INFO) },
+               { "Collectd::LOG_DEBUG",       Perl_newSViv (perl, LOG_DEBUG) },
+               { "", NULL }
+       };
+
+       int i = 0;
+
+       dXSARGS;
+
+       if ((1 > items) || (2 < items)) {
+               log_err ("Usage: Collectd::bootstrap(name[, version])");
+               XSRETURN_EMPTY;
+       }
+
+       XS_VERSION_BOOTCHECK;
+
+       /* register API */
+       for (i = 0; NULL != api[i].f; ++i)
+               Perl_newXS (perl, api[i].name, api[i].f, file);
+
+       stash = Perl_gv_stashpv (perl, "Collectd", 1);
+
+       /* export "constants" */
+       for (i = 0; NULL != consts[i].value; ++i)
+               Perl_newCONSTSUB (perl, stash, consts[i].name, consts[i].value);
+       XSRETURN_YES;
+} /* static XS (boot_Collectd) */
+
+
+/*
+ * Interface to collectd.
+ */
+
+static int perl_config (const char *key, const char *value)
+{
+       assert (NULL != perl);
+
+       log_debug ("perl_config: key = \"%s\", value=\"%s\"", key, value);
+
+       if (0 == strcasecmp (key, "LoadPlugin")) {
+               char module_name[DATA_MAX_NAME_LEN];
+
+               if (get_module_name (module_name, sizeof (module_name), value)
+                               == NULL) {
+                       log_err ("Invalid module name %s", value);
+                       return (1);
+               } /* if (get_module_name == NULL) */
+
+               log_debug ("perl_config: loading perl plugin \"%s\"", value);
+               Perl_load_module (perl, PERL_LOADMOD_NOIMPORT,
+                               Perl_newSVpv (perl, module_name, strlen (module_name)),
+                               Nullsv);
+       }
+       else if (0 == strcasecmp (key, "BaseName")) {
+               log_debug ("perl_config: Setting plugin basename to \"%s\"", value);
+               strncpy (base_name, value, sizeof (base_name));
+               base_name[sizeof (base_name) - 1] = '\0';
+       }
+       else if (0 == strcasecmp (key, "IncludeDir")) {
+               Perl_av_unshift (perl, GvAVn (PL_incgv), 1);
+               Perl_av_store (perl, GvAVn (PL_incgv),
+                               0, Perl_newSVpv (perl, value, strlen (value)));
+       }
+       else {
+               return -1;
+       }
+       return 0;
+} /* static int perl_config (char *, char *) */
+
+static int perl_init (void)
+{
+       assert (NULL != perl);
+
+       PERL_SET_CONTEXT (perl);
+       return pplugin_call_all (PLUGIN_INIT);
+} /* static int perl_init (void) */
+
+static int perl_read (void)
+{
+       assert (NULL != perl);
+
+       PERL_SET_CONTEXT (perl);
+       return pplugin_call_all (PLUGIN_READ);
+} /* static int perl_read (void) */
+
+static int perl_write (const data_set_t *ds, const value_list_t *vl)
+{
+       assert (NULL != perl);
+
+       PERL_SET_CONTEXT (perl);
+       return pplugin_call_all (PLUGIN_WRITE, ds, vl);
+} /* static int perl_write (const data_set_t *, const value_list_t *) */
+
+static void perl_log (int level, const char *msg)
+{
+       assert (NULL != perl);
+
+       PERL_SET_CONTEXT (perl);
+       pplugin_call_all (PLUGIN_LOG, level, msg);
+       return;
+} /* static void perl_log (int, const char *) */
+
+static int perl_shutdown (void)
+{
+       int i   = 0;
+       int ret = 0;
+
+       plugin_unregister_log ("perl");
+       plugin_unregister_config ("perl");
+       plugin_unregister_init ("perl");
+       plugin_unregister_read ("perl");
+       plugin_unregister_write ("perl");
+
+       assert (NULL != perl);
+
+       PERL_SET_CONTEXT (perl);
+       ret = pplugin_call_all (PLUGIN_SHUTDOWN);
+
+       for (i = 0; i < PLUGIN_TYPES; ++i) {
+               if (0 < Perl_hv_iterinit (perl, plugins[i])) {
+                       char *k = NULL;
+                       I32  l  = 0;
+
+                       while (NULL != Perl_hv_iternextsv (perl, plugins[i], &k, &l)) {
+                               pplugin_unregister (i, k);
+                       }
+               }
+
+               Perl_hv_undef (perl, plugins[i]);
+       }
+
+       if (0 < Perl_hv_iterinit (perl, data_sets)) {
+               char *k = NULL;
+               I32  l  = 0;
+
+               while (NULL != Perl_hv_iternextsv (perl, data_sets, &k, &l)) {
+                       pplugin_unregister_data_set (k);
+               }
+       }
+
+       Perl_hv_undef (perl, data_sets);
+
+#if COLLECT_DEBUG
+       Perl_sv_report_used (perl);
+#endif /* COLLECT_DEBUG */
+
+       perl_destruct (perl);
+       perl_free (perl);
+       perl = NULL;
+
+       PERL_SYS_TERM ();
+
+       plugin_unregister_shutdown ("perl");
+       return ret;
+} /* static void perl_shutdown (void) */
+
+static void xs_init (pTHX)
+{
+       char *file = __FILE__;
+
+       dXSUB_SYS;
+
+       /* build the Collectd module into the perl interpreter */
+       Perl_newXS (perl, "Collectd::bootstrap", boot_Collectd, file);
+
+       /* enable usage of Perl modules using shared libraries */
+       Perl_newXS (perl, "DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+       return;
+} /* static void xs_init (pTHX) */
+
+/*
+ * Create the perl interpreter and register it with collectd.
+ */
+void module_register (void)
+{
+       char *embed_argv[] = { "", "-e", "bootstrap Collectd \""VERSION"\"", NULL };
+       int  embed_argc    = 3;
+
+       int i = 0;
+
+       log_debug ("module_register: Registering perl plugin...");
+
+       PERL_SYS_INIT3 (&argc, &argv, &environ);
+
+       if (NULL == (perl = perl_alloc ())) {
+               log_err ("module_register: Not enough memory.");
+               exit (3);
+       }
+       perl_construct (perl);
+
+       PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
+
+       if (0 != perl_parse (perl, xs_init, embed_argc, embed_argv, NULL)) {
+               log_err ("module_register: Unable to bootstrap Collectd.");
+               exit (1);
+       }
+       perl_run (perl);
+
+       for (i = 0; i < PLUGIN_TYPES; ++i)
+               plugins[i] = Perl_newHV (perl);
+
+       data_sets = Perl_newHV (perl);
+
+       plugin_register_log ("perl", perl_log);
+       plugin_register_config ("perl", perl_config, config_keys, config_keys_num);
+       plugin_register_init ("perl", perl_init);
+
+       plugin_register_read ("perl", perl_read);
+
+       plugin_register_write ("perl", perl_write);
+       plugin_register_shutdown ("perl", perl_shutdown);
+       return;
+} /* void module_register (void) */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
index 2a2f03e..3c70066 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 #include "common.h"
 #include "plugin.h"
 #include "configfile.h"
-#include "utils_debug.h"
-
-#define MODULE_NAME "ping"
 
 #include <netinet/in.h>
 #include "liboping/oping.h"
 
+/*
+ * Private data types
+ */
 struct hostlist_s
 {
        char *host;
@@ -40,19 +39,13 @@ struct hostlist_s
 };
 typedef struct hostlist_s hostlist_t;
 
+/*
+ * Private variables
+ */
 static pingobj_t *pingobj = NULL;
 static hostlist_t *hosts = NULL;
 
-static char *file_template = "ping-%s.rrd";
-
-static char *ds_def[] = 
-{
-       "DS:ping:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       NULL
-};
-static int ds_num = 1;
-
-static char *config_keys[] =
+static const char *config_keys[] =
 {
        "Host",
        "TTL",
@@ -60,25 +53,28 @@ static char *config_keys[] =
 };
 static int config_keys_num = 2;
 
+/*
+ * Private functions
+ */
 static void add_hosts (void)
 {
        hostlist_t *hl_this;
        hostlist_t *hl_prev;
 
-       int step = atoi (COLLECTD_STEP);
-
        hl_this = hosts;
        hl_prev = NULL;
        while (hl_this != NULL)
        {
-               DBG ("host = %s, wait_left = %i, wait_time = %i, next = %p",
-                               hl_this->host, hl_this->wait_left, hl_this->wait_time, (void *) hl_this->next);
+               DEBUG ("ping plugin: host = %s, wait_left = %i, "
+                               "wait_time = %i, next = %p",
+                               hl_this->host, hl_this->wait_left,
+                               hl_this->wait_time, (void *) hl_this->next);
 
                if (hl_this->wait_left <= 0)
                {
                        if (ping_host_add (pingobj, hl_this->host) == 0)
                        {
-                               DBG ("Successfully added host %s", hl_this->host);
+                               DEBUG ("ping plugin: Successfully added host %s", hl_this->host);
                                /* Remove the host from the linked list */
                                if (hl_prev != NULL)
                                        hl_prev->next = hl_this->next;
@@ -90,6 +86,9 @@ static void add_hosts (void)
                        }
                        else
                        {
+                               WARNING ("ping plugin: Failed adding host "
+                                               "`%s': %s", hl_this->host,
+                                               ping_get_error (pingobj));
                                hl_this->wait_left = hl_this->wait_time;
                                hl_this->wait_time *= 2;
                                if (hl_this->wait_time > 86400)
@@ -98,7 +97,7 @@ static void add_hosts (void)
                }
                else
                {
-                       hl_this->wait_left -= step;
+                       hl_this->wait_left -= interval_g;
                }
 
                if (hl_this != NULL)
@@ -109,20 +108,21 @@ static void add_hosts (void)
        }
 }
 
-static void ping_init (void)
+static int ping_init (void)
 {
        if (hosts != NULL)
                add_hosts ();
+
+       return (0);
 }
 
-static int ping_config (char *key, char *value)
+static int ping_config (const char *key, const char *value)
 {
        if (pingobj == NULL)
        {
                if ((pingobj = ping_construct ()) == NULL)
                {
-                       syslog (LOG_ERR, "ping: `ping_construct' failed: %s",
-                                       ping_get_error (pingobj));
+                       ERROR ("ping plugin: `ping_construct' failed.");
                        return (1);
                }
        }
@@ -131,24 +131,27 @@ static int ping_config (char *key, char *value)
        {
                hostlist_t *hl;
                char *host;
-               int step = atoi (COLLECTD_STEP);
 
                if ((hl = (hostlist_t *) malloc (sizeof (hostlist_t))) == NULL)
                {
-                       syslog (LOG_ERR, "ping plugin: malloc failed: %s",
-                                       strerror (errno));
+                       char errbuf[1024];
+                       ERROR ("ping plugin: malloc failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        return (1);
                }
                if ((host = strdup (value)) == NULL)
                {
+                       char errbuf[1024];
                        free (hl);
-                       syslog (LOG_ERR, "ping plugin: strdup failed: %s",
-                                       strerror (errno));
+                       ERROR ("ping plugin: strdup failed: %s",
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
                        return (1);
                }
 
                hl->host = host;
-               hl->wait_time = 2 * step;
+               hl->wait_time = 2 * interval_g;
                hl->wait_left = 0;
                hl->next = hosts;
                hosts = hl;
@@ -158,7 +161,7 @@ static int ping_config (char *key, char *value)
                int ttl = atoi (value);
                if (ping_setopt (pingobj, PING_DEF_TIMEOUT, (void *) &ttl))
                {
-                       syslog (LOG_WARNING, "ping: liboping did not accept the TTL value %i", ttl);
+                       WARNING ("ping: liboping did not accept the TTL value %i", ttl);
                        return (1);
                }
        }
@@ -170,53 +173,47 @@ static int ping_config (char *key, char *value)
        return (0);
 }
 
-static void ping_write (char *host, char *inst, char *val)
-{
-       char file[512];
-       int status;
-
-       status = snprintf (file, 512, file_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
-
-#define BUFSIZE 256
 static void ping_submit (char *host, double latency)
 {
-       char buf[BUFSIZE];
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (buf, BUFSIZE, "%u:%f", (unsigned int) curtime, latency) >= BUFSIZE)
-               return;
+       values[0].gauge = latency;
 
-       plugin_submit (MODULE_NAME, host, buf);
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "ping");
+       strcpy (vl.plugin_instance, "");
+       strncpy (vl.type_instance, host, sizeof (vl.type_instance));
+
+       plugin_dispatch_values ("ping", &vl);
 }
-#undef BUFSIZE
 
-static void ping_read (void)
+static int ping_read (void)
 {
        pingobj_iter_t *iter;
 
        char   host[512];
        double latency;
        size_t buf_len;
+       int    number_of_hosts;
 
        if (pingobj == NULL)
-               return;
+               return (-1);
 
        if (hosts != NULL)
                add_hosts ();
 
        if (ping_send (pingobj) < 0)
        {
-               syslog (LOG_ERR, "ping: `ping_send' failed: %s",
+               ERROR ("ping plugin: `ping_send' failed: %s",
                                ping_get_error (pingobj));
-               return;
+               return (-1);
        }
 
+       number_of_hosts = 0;
        for (iter = ping_iterator_get (pingobj);
                        iter != NULL;
                        iter = ping_iterator_next (iter))
@@ -224,22 +221,38 @@ static void ping_read (void)
                buf_len = sizeof (host);
                if (ping_iterator_get_info (iter, PING_INFO_HOSTNAME,
                                        host, &buf_len))
+               {
+                       WARNING ("ping plugin: ping_iterator_get_info "
+                                       "(PING_INFO_HOSTNAME) failed.");
                        continue;
+               }
 
                buf_len = sizeof (latency);
                if (ping_iterator_get_info (iter, PING_INFO_LATENCY,
                                        &latency, &buf_len))
+               {
+                       WARNING ("ping plugin: ping_iterator_get_info (%s, "
+                                       "PING_INFO_LATENCY) failed.", host);
                        continue;
+               }
 
-               DBG ("host = %s, latency = %f", host, latency);
+               DEBUG ("ping plugin: host = %s, latency = %f", host, latency);
                ping_submit (host, latency);
+               number_of_hosts++;
+       }
+
+       if ((number_of_hosts == 0) && (getuid != 0))
+       {
+               ERROR ("ping plugin: All hosts failed. Try starting collectd as root.");
        }
-}
+
+       return (number_of_hosts == 0 ? -1 : 0);
+} /* int ping_read */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, ping_init, ping_read, ping_write);
-       cf_register (MODULE_NAME, ping_config, config_keys, config_keys_num);
-}
-
-#undef MODULE_NAME
+       plugin_register_config ("ping", ping_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("ping", ping_init);
+       plugin_register_read ("ping", ping_read);
+} /* void module_register */
index d708b6b..da66204 100644 (file)
@@ -4,8 +4,7 @@
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 
 #include <ltdl.h>
 
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+#include "common.h"
 #include "plugin.h"
-#include "network.h"
-#include "utils_debug.h"
+#include "configfile.h"
+#include "utils_llist.h"
 
-typedef struct plugin
+/*
+ * Private structures
+ */
+struct read_func_s
 {
-       char *type;
-       void (*init) (void);
-       void (*read) (void);
-       void (*write) (char *host, char *inst, char *val);
-       void (*shutdown) (void);
-       struct plugin *next;
-} plugin_t;
+       int wait_time;
+       int wait_left;
+       int (*callback) (void);
+       enum { DONE = 0, TODO = 1, ACTIVE = 2 } needs_read;
+};
+typedef struct read_func_s read_func_t;
 
-static plugin_t *first_plugin = NULL;
-
-extern int operating_mode;
+/*
+ * Private variables
+ */
+static llist_t *list_init;
+static llist_t *list_read;
+static llist_t *list_write;
+static llist_t *list_shutdown;
+static llist_t *list_data_set;
+static llist_t *list_log;
 
 static char *plugindir = NULL;
 
-char *plugin_get_dir (void)
+static int             read_loop = 1;
+static pthread_mutex_t read_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  read_cond = PTHREAD_COND_INITIALIZER;
+static pthread_t      *read_threads = NULL;
+static int             read_threads_num = 0;
+
+/*
+ * Static functions
+ */
+static const char *plugin_get_dir (void)
 {
        if (plugindir == NULL)
                return (PLUGINDIR);
@@ -52,71 +73,57 @@ char *plugin_get_dir (void)
                return (plugindir);
 }
 
-void plugin_set_dir (const char *dir)
+static int register_callback (llist_t **list, const char *name, void *callback)
 {
-       if (plugindir != NULL)
-               free (plugindir);
+       llentry_t *le;
 
-       if (dir == NULL)
-               plugindir = NULL;
-       else if ((plugindir = strdup (dir)) == NULL)
-               syslog (LOG_ERR, "strdup: %s", strerror (errno));
-}
+       if ((*list == NULL)
+                       && ((*list = llist_create ()) == NULL))
+               return (-1);
 
-/*
- * Returns the number of plugins registered
- */
-int plugin_count (void)
-{
-       int i;
-       plugin_t *p;
+       le = llist_search (*list, name);
+       if (le == NULL)
+       {
+               le = llentry_create (name, callback);
+               if (le == NULL)
+                       return (-1);
 
-       for (i = 0, p = first_plugin; p != NULL; p = p->next)
-               i++;
+               llist_append (*list, le);
+       }
+       else
+       {
+               le->value = callback;
+       }
 
-       return (i);
-}
+       return (0);
+} /* int register_callback */
 
-/*
- * Returns the plugins with the type `type' or NULL if it's not found.
- */
-plugin_t *plugin_search (const char *type)
+static int plugin_unregister (llist_t *list, const char *name)
 {
-       plugin_t *ret;
+       llentry_t *e;
 
-       if (type == NULL)
-               return (NULL);
+       e = llist_search (list, name);
 
-       for (ret = first_plugin; ret != NULL; ret = ret->next)
-               if (strcmp (ret->type, type) == 0)
-                       break;
+       if (e == NULL)
+               return (-1);
 
-       return (ret);
-}
+       llist_remove (list, e);
+       llentry_destroy (e);
 
-/*
- * Returns true if the plugin is loaded (i.e. `exists') and false otherwise.
- * This is used in `configfile.c' to skip sections that are not needed..
- */
-int plugin_exists (char *type)
-{
-       if (plugin_search (type) == NULL)
-               return (0);
-       else
-               return (1);
-}
+       return (0);
+} /* int plugin_unregister */
 
 /*
  * (Try to) load the shared object `file'. Won't complain if it isn't a shared
  * object, but it will bitch about a shared object not having a
  * ``module_register'' symbol..
  */
-int plugin_load_file (char *file)
+static int plugin_load_file (char *file)
 {
        lt_dlhandle dlh;
        void (*reg_handle) (void);
 
-       DBG ("file = %s", file);
+       DEBUG ("file = %s", file);
 
        lt_dlinit ();
        lt_dlerror (); /* clear errors */
@@ -125,14 +132,14 @@ int plugin_load_file (char *file)
        {
                const char *error = lt_dlerror ();
 
-               syslog (LOG_ERR, "lt_dlopen failed: %s", error);
-               DBG ("lt_dlopen failed: %s", error);
+               ERROR ("lt_dlopen failed: %s", error);
+               DEBUG ("lt_dlopen failed: %s", error);
                return (1);
        }
 
        if ((reg_handle = (void (*) (void)) lt_dlsym (dlh, "module_register")) == NULL)
        {
-               syslog (LOG_WARNING, "Couldn't find symbol ``module_register'' in ``%s'': %s\n",
+               WARNING ("Couldn't find symbol ``module_register'' in ``%s'': %s\n",
                                file, lt_dlerror ());
                lt_dlclose (dlh);
                return (-1);
@@ -143,11 +150,153 @@ int plugin_load_file (char *file)
        return (0);
 }
 
+static void *plugin_read_thread (void *args)
+{
+       llentry_t   *le;
+       read_func_t *rf;
+       int          status;
+       int          done;
+
+       pthread_mutex_lock (&read_lock);
+
+       while (read_loop != 0)
+       {
+               le = llist_head (list_read);
+               done = 0;
+
+               while ((read_loop != 0) && (le != NULL))
+               {
+                       rf = (read_func_t *) le->value;
+
+                       if (rf->needs_read != TODO)
+                       {
+                               le = le->next;
+                               continue;
+                       }
+
+                       /* We will do this read function */
+                       rf->needs_read = ACTIVE;
+
+                       DEBUG ("[thread #%5lu] plugin: plugin_read_thread: Handling %s",
+                                       (unsigned long int) pthread_self (), le->key);
+                       pthread_mutex_unlock (&read_lock);
+
+                       status = rf->callback ();
+                       done++;
+
+                       if (status != 0)
+                       {
+                               if (rf->wait_time < interval_g)
+                                       rf->wait_time = interval_g;
+                               rf->wait_left = rf->wait_time;
+                               rf->wait_time = rf->wait_time * 2;
+                               if (rf->wait_time > 86400)
+                                       rf->wait_time = 86400;
+
+                               NOTICE ("read-function of plugin `%s' "
+                                               "failed. Will syspend it for %i "
+                                               "seconds.", le->key, rf->wait_left);
+                       }
+                       else
+                       {
+                               rf->wait_left = 0;
+                               rf->wait_time = interval_g;
+                       }
+
+                       pthread_mutex_lock (&read_lock);
+
+                       rf->needs_read = DONE;
+                       le = le->next;
+               } /* while (le != NULL) */
+
+               if ((read_loop != 0) && (done == 0))
+               {
+                       DEBUG ("[thread #%5lu] plugin: plugin_read_thread: Waiting on read_cond.",
+                                       (unsigned long int) pthread_self ());
+                       pthread_cond_wait (&read_cond, &read_lock);
+               }
+       } /* while (read_loop) */
+
+       pthread_mutex_unlock (&read_lock);
+
+       pthread_exit (NULL);
+} /* void *plugin_read_thread */
+
+static void start_threads (int num)
+{
+       int i;
+
+       if (read_threads != NULL)
+               return;
+
+       read_threads = (pthread_t *) calloc (num, sizeof (pthread_t));
+       if (read_threads == NULL)
+       {
+               ERROR ("plugin: start_threads: calloc failed.");
+               return;
+       }
+
+       read_threads_num = 0;
+       for (i = 0; i < num; i++)
+       {
+               if (pthread_create (read_threads + read_threads_num, NULL,
+                                       plugin_read_thread, NULL) == 0)
+               {
+                       read_threads_num++;
+               }
+               else
+               {
+                       ERROR ("plugin: start_threads: pthread_create failed.");
+                       return;
+               }
+       } /* for (i) */
+} /* void start_threads */
+
+static void stop_threads (void)
+{
+       int i;
+
+       pthread_mutex_lock (&read_lock);
+       read_loop = 0;
+       DEBUG ("plugin: stop_threads: Signalling `read_cond'");
+       pthread_cond_broadcast (&read_cond);
+       pthread_mutex_unlock (&read_lock);
+
+       for (i = 0; i < read_threads_num; i++)
+       {
+               if (pthread_join (read_threads[i], NULL) != 0)
+               {
+                       ERROR ("plugin: stop_threads: pthread_join failed.");
+               }
+               read_threads[i] = (pthread_t) 0;
+       }
+       sfree (read_threads);
+       read_threads_num = 0;
+} /* void stop_threads */
+
+/*
+ * Public functions
+ */
+void plugin_set_dir (const char *dir)
+{
+       if (plugindir != NULL)
+               free (plugindir);
+
+       if (dir == NULL)
+               plugindir = NULL;
+       else if ((plugindir = strdup (dir)) == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("strdup failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
+}
+
 #define BUFSIZE 512
 int plugin_load (const char *type)
 {
        DIR  *dh;
-       char *dir;
+       const char *dir;
        char  filename[BUFSIZE];
        char  typename[BUFSIZE];
        int   typename_len;
@@ -155,27 +304,25 @@ int plugin_load (const char *type)
        struct stat    statbuf;
        struct dirent *de;
 
-       DBG ("type = %s", type);
+       DEBUG ("type = %s", type);
 
        dir = plugin_get_dir ();
        ret = 1;
 
-       /* don't load twice */
-       if (plugin_search (type) != NULL)
-               return (0);
-
        /* `cpu' should not match `cpufreq'. To solve this we add `.so' to the
         * type when matching the filename */
        if (snprintf (typename, BUFSIZE, "%s.so", type) >= BUFSIZE)
        {
-               syslog (LOG_WARNING, "snprintf: truncated: `%s.so'", type);
+               WARNING ("snprintf: truncated: `%s.so'", type);
                return (-1);
        }
        typename_len = strlen (typename);
 
        if ((dh = opendir (dir)) == NULL)
        {
-               syslog (LOG_ERR, "opendir (%s): %s", dir, strerror (errno));
+               char errbuf[1024];
+               ERROR ("opendir (%s): %s", dir,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
 
@@ -186,13 +333,15 @@ int plugin_load (const char *type)
 
                if (snprintf (filename, BUFSIZE, "%s/%s", dir, de->d_name) >= BUFSIZE)
                {
-                       syslog (LOG_WARNING, "snprintf: truncated: `%s/%s'", dir, de->d_name);
+                       WARNING ("snprintf: truncated: `%s/%s'", dir, de->d_name);
                        continue;
                }
 
                if (lstat (filename, &statbuf) == -1)
                {
-                       syslog (LOG_WARNING, "stat %s: %s", filename, strerror (errno));
+                       char errbuf[1024];
+                       WARNING ("stat %s: %s", filename,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                        continue;
                }
                else if (!S_ISREG (statbuf.st_mode))
@@ -215,189 +364,349 @@ int plugin_load (const char *type)
 }
 
 /*
- * (Try to) load all plugins in `dir'. Returns the number of loaded plugins..
+ * The `register_*' functions follow
  */
-int plugin_load_all (char *dir)
+int plugin_register_config (const char *name,
+               int (*callback) (const char *key, const char *val),
+               const char **keys, int keys_num)
 {
-       DIR *dh;
-       struct dirent *de;
-       char filename[BUFSIZE];
-       struct stat statbuf;
+       cf_register (name, callback, keys, keys_num);
+       return (0);
+} /* int plugin_register_config */
 
-       if (dir == NULL)
-               dir = plugin_get_dir ();
-       else
-               plugin_set_dir (dir);
+int plugin_register_init (const char *name,
+               int (*callback) (void))
+{
+       return (register_callback (&list_init, name, (void *) callback));
+} /* plugin_register_init */
 
-       if ((dh = opendir (dir)) == NULL)
+int plugin_register_read (const char *name,
+               int (*callback) (void))
+{
+       read_func_t *rf;
+
+       rf = (read_func_t *) malloc (sizeof (read_func_t));
+       if (rf == NULL)
        {
-               syslog (LOG_ERR, "opendir (%s): %s", dir, strerror (errno));
-               return (0);
+               char errbuf[1024];
+               ERROR ("plugin_register_read: malloc failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
-       while ((de = readdir (dh)) != NULL)
+       memset (rf, '\0', sizeof (read_func_t));
+       rf->wait_time = interval_g;
+       rf->wait_left = 0;
+       rf->callback = callback;
+       rf->needs_read = DONE;
+
+       return (register_callback (&list_read, name, (void *) rf));
+} /* int plugin_register_read */
+
+int plugin_register_write (const char *name,
+               int (*callback) (const data_set_t *ds, const value_list_t *vl))
+{
+       return (register_callback (&list_write, name, (void *) callback));
+} /* int plugin_register_write */
+
+int plugin_register_shutdown (char *name,
+               int (*callback) (void))
+{
+       return (register_callback (&list_shutdown, name, (void *) callback));
+} /* int plugin_register_shutdown */
+
+int plugin_register_data_set (const data_set_t *ds)
+{
+       data_set_t *ds_copy;
+       int i;
+
+       if ((list_data_set != NULL)
+                       && (llist_search (list_data_set, ds->type) != NULL))
        {
-               if (snprintf (filename, BUFSIZE, "%s/%s", dir, de->d_name) >= BUFSIZE)
-               {
-                       syslog (LOG_WARNING, "snprintf: truncated: %s/%s", dir, de->d_name);
-                       continue;
-               }
+               NOTICE ("Replacing DS `%s' with another version.", ds->type);
+               plugin_unregister_data_set (ds->type);
+       }
 
-               if (lstat (filename, &statbuf) == -1)
-               {
-                       syslog (LOG_WARNING, "stat %s: %s", filename, strerror (errno));
-                       continue;
-               }
-               else if (!S_ISREG (statbuf.st_mode))
-               {
-                       continue;
-               }
+       ds_copy = (data_set_t *) malloc (sizeof (data_set_t));
+       if (ds_copy == NULL)
+               return (-1);
+       memcpy(ds_copy, ds, sizeof (data_set_t));
 
-               plugin_load_file (filename);
+       ds_copy->ds = (data_source_t *) malloc (sizeof (data_source_t)
+                       * ds->ds_num);
+       if (ds_copy->ds == NULL)
+       {
+               free (ds_copy);
+               return (-1);
        }
 
-       closedir (dh);
+       for (i = 0; i < ds->ds_num; i++)
+               memcpy (ds_copy->ds + i, ds->ds + i, sizeof (data_source_t));
+
+       return (register_callback (&list_data_set, ds->type, (void *) ds_copy));
+} /* int plugin_register_data_set */
+
+int plugin_register_log (char *name,
+               void (*callback) (int priority, const char *msg))
+{
+       return (register_callback (&list_log, name, (void *) callback));
+} /* int plugin_register_log */
+
+int plugin_unregister_config (const char *name)
+{
+       cf_unregister (name);
+       return (0);
+} /* int plugin_unregister_config */
 
-       return (plugin_count ());
+int plugin_unregister_init (const char *name)
+{
+       return (plugin_unregister (list_init, name));
 }
-#undef BUFSIZE
 
-/*
- * Call `init' on all plugins (if given)
- */
-void plugin_init_all (void)
+int plugin_unregister_read (const char *name)
 {
-       plugin_t *p;
+       llentry_t *e;
 
-       for (p = first_plugin; p != NULL; p = p->next)
-               if (p->init != NULL)
-                       (*p->init) ();
+       e = llist_search (list_read, name);
+
+       if (e == NULL)
+               return (-1);
+
+       llist_remove (list_read, e);
+       free (e->value);
+       llentry_destroy (e);
+
+       return (0);
 }
 
-/*
- * Call `read' on all plugins (if given)
- */
-void plugin_read_all (const int *loop)
+int plugin_unregister_write (const char *name)
 {
-       plugin_t *p;
+       return (plugin_unregister (list_write, name));
+}
 
-       for (p = first_plugin; (*loop == 0) && (p != NULL); p = p->next)
-               if (p->read != NULL)
-                       (*p->read) ();
+int plugin_unregister_shutdown (const char *name)
+{
+       return (plugin_unregister (list_shutdown, name));
 }
 
-/*
- * Call `shutdown' on all plugins (if given)
- */
-void plugin_shutdown_all (void)
+int plugin_unregister_data_set (const char *name)
 {
-       plugin_t *p;
+       llentry_t  *e;
+       data_set_t *ds;
+
+       if (list_data_set == NULL)
+               return (-1);
 
-       for (p = first_plugin; NULL != p; p = p->next)
-               if (NULL != p->shutdown)
-                       (*p->shutdown) ();
-       return;
+       e = llist_search (list_data_set, name);
+
+       if (e == NULL)
+               return (-1);
+
+       llist_remove (list_data_set, e);
+       ds = (data_set_t *) e->value;
+       llentry_destroy (e);
+
+       sfree (ds->ds);
+       sfree (ds);
+
+       return (0);
+} /* int plugin_unregister_data_set */
+
+int plugin_unregister_log (const char *name)
+{
+       return (plugin_unregister (list_log, name));
 }
 
-/*
- * Add plugin to the linked list of registered plugins.
- */
-void plugin_register (char *type,
-               void (*init) (void),
-               void (*read) (void),
-               void (*write) (char *, char *, char *))
+void plugin_init_all (void)
 {
-       plugin_t *p;
+       int (*callback) (void);
+       llentry_t *le;
+       int status;
+
+       /* Start read-threads */
+       if (list_read != NULL)
+       {
+               const char *rt;
+               int num;
+               rt = global_option_get ("ReadThreads");
+               num = atoi (rt);
+               start_threads ((num > 0) ? num : 5);
+       }
 
-       if (plugin_search (type) != NULL)
+       if (list_init == NULL)
                return;
 
-#ifdef HAVE_LIBRRD
-       if (operating_mode != MODE_SERVER)
-#endif
-               if ((init != NULL) && (read == NULL))
-                       syslog (LOG_NOTICE, "Plugin `%s' doesn't provide a read function.", type);
+       le = llist_head (list_init);
+       while (le != NULL)
+       {
+               callback = (int (*) (void)) le->value;
+               status = (*callback) ();
 
-       if ((p = (plugin_t *) malloc (sizeof (plugin_t))) == NULL)
+               if (status != 0)
+               {
+                       ERROR ("Initialization of plugin `%s' "
+                                       "failed with status %i. "
+                                       "Plugin will be unloaded.",
+                                       le->key, status);
+                       /* FIXME: Unload _all_ functions */
+                       plugin_unregister_read (le->key);
+               }
+
+               le = le->next;
+       }
+} /* void plugin_init_all */
+
+void plugin_read_all (const int *loop)
+{
+       llentry_t   *le;
+       read_func_t *rf;
+
+       if (list_read == NULL)
                return;
 
-       if ((p->type = strdup (type)) == NULL)
+       pthread_mutex_lock (&read_lock);
+
+       le = llist_head (list_read);
+       while (le != NULL)
        {
-               free (p);
-               return;
+               rf = (read_func_t *) le->value;
+
+               if (rf->needs_read != DONE)
+               {
+                       le = le->next;
+                       continue;
+               }
+
+               if (rf->wait_left > 0)
+                       rf->wait_left -= interval_g;
+
+               if (rf->wait_left <= 0)
+               {
+                       rf->needs_read = TODO;
+               }
+
+               le = le->next;
        }
 
-       p->init  = init;
-       p->read  = read;
-       p->write = write;
+       DEBUG ("plugin: plugin_read_all: Signalling `read_cond'");
+       pthread_cond_broadcast (&read_cond);
+       pthread_mutex_unlock (&read_lock);
+} /* void plugin_read_all */
 
-       p->shutdown = NULL;
+void plugin_shutdown_all (void)
+{
+       int (*callback) (void);
+       llentry_t *le;
 
-       p->next = first_plugin;
-       first_plugin = p;
-}
+       stop_threads ();
 
-/*
- * Register the shutdown function (optional).
- */
-int plugin_register_shutdown (char *type, void (*shutdown) (void))
+       if (list_shutdown == NULL)
+               return;
+
+       le = llist_head (list_shutdown);
+       while (le != NULL)
+       {
+               callback = (int (*) (void)) le->value;
+
+               /* Advance the pointer before calling the callback allows
+                * shutdown functions to unregister themselves. If done the
+                * other way around the memory `le' points to will be freed
+                * after callback returns. */
+               le = le->next;
+
+               (*callback) ();
+       }
+} /* void plugin_shutdown_all */
+
+int plugin_dispatch_values (const char *name, value_list_t *vl)
 {
-       plugin_t *p = plugin_search (type);
+       int (*callback) (const data_set_t *, const value_list_t *);
+       data_set_t *ds;
+       llentry_t *le;
 
-       if (NULL == p)
-               return -1;
+       if ((list_write == NULL) || (list_data_set == NULL))
+               return (-1);
 
-       p->shutdown = shutdown;
-       return 0;
-}
+       le = llist_search (list_data_set, name);
+       if (le == NULL)
+       {
+               DEBUG ("No such dataset registered: %s", name);
+               return (-1);
+       }
 
-/*
- * Send received data back to the plugin/module which will append DS
- * definitions and pass it on to ``rrd_update_file''.
- */
-void plugin_write (char *host, char *type, char *inst, char *val)
+       ds = (data_set_t *) le->value;
+
+       DEBUG ("plugin: plugin_dispatch_values: time = %u; host = %s; "
+                       "plugin = %s; plugin_instance = %s; type = %s; "
+                       "type_instance = %s;",
+                       (unsigned int) vl->time, vl->host,
+                       vl->plugin, vl->plugin_instance,
+                       ds->type, vl->type_instance);
+
+#if COLLECT_DEBUG
+       assert (ds->ds_num == vl->values_len);
+#else
+       if (ds->ds_num != vl->values_len)
+       {
+               ERROR ("plugin: ds->type = %s: (ds->ds_num = %i) != "
+                               "(vl->values_len = %i)",
+                               ds->type, ds->ds_num, vl->values_len);
+               return (-1);
+       }
+#endif
+
+       escape_slashes (vl->host, sizeof (vl->host));
+       escape_slashes (vl->plugin, sizeof (vl->plugin));
+       escape_slashes (vl->plugin_instance, sizeof (vl->plugin_instance));
+       escape_slashes (vl->type_instance, sizeof (vl->type_instance));
+
+       le = llist_head (list_write);
+       while (le != NULL)
+       {
+               callback = (int (*) (const data_set_t *, const value_list_t *)) le->value;
+               (*callback) (ds, vl);
+
+               le = le->next;
+       }
+
+       return (0);
+} /* int plugin_dispatch_values */
+
+void plugin_log (int level, const char *format, ...)
 {
-       plugin_t *p;
+       char msg[512];
+       va_list ap;
 
-       if ((p = plugin_search (type)) == NULL)
-               return;
+       void (*callback) (int, const char *);
+       llentry_t *le;
 
-       if (p->write == NULL)
+       if (list_log == NULL)
                return;
 
-       (*p->write) (host, inst, val);
-}
+#if !COLLECT_DEBUG
+       if (level >= LOG_DEBUG)
+               return;
+#endif
 
-/*
- * Receive data from the plugin/module and get it somehow to ``plugin_write'':
- * Either using ``network_send'' (when in network/client mode) or call it
- * directly (in local mode).
- */
-void plugin_submit (char *type, char *inst, char *val)
-{
-       if (inst == NULL)
-               inst = "-";
+       va_start (ap, format);
+       vsnprintf (msg, 512, format, ap);
+       msg[511] = '\0';
+       va_end (ap);
 
-       if ((type == NULL) || (val == NULL))
+       le = llist_head (list_log);
+       while (le != NULL)
        {
-               DBG ("Help! NULL-pointer! type = %s; inst = %s; val = %s;",
-                               (type == NULL) ? "(null)" : type,
-                               inst,
-                               (val == NULL) ? "(null)" : val);
-               return;
-       }
+               callback = (void (*) (int, const char *)) le->value;
+               (*callback) (level, msg);
 
-        if (operating_mode == MODE_CLIENT)
-               network_send (type, inst, val);
-       else
-               plugin_write (NULL, type, inst, val);
-}
+               le = le->next;
+       }
+} /* void plugin_log */
 
 void plugin_complain (int level, complain_t *c, const char *format, ...)
 {
        char message[512];
        va_list ap;
-       int step;
 
        if (c->delay > 0)
        {
@@ -405,25 +714,22 @@ void plugin_complain (int level, complain_t *c, const char *format, ...)
                return;
        }
 
-       step = atoi (COLLECTD_STEP);
-       assert (step > 0);
-
-       if (c->interval < step)
-               c->interval = step;
+       if (c->interval < interval_g)
+               c->interval = interval_g;
        else
                c->interval *= 2;
 
        if (c->interval > 86400)
                c->interval = 86400;
 
-       c->delay = c->interval / step;
+       c->delay = c->interval / interval_g;
 
        va_start (ap, format);
        vsnprintf (message, 512, format, ap);
        message[511] = '\0';
        va_end (ap);
 
-       syslog (level, message);
+       plugin_log (level, message);
 }
 
 void plugin_relief (int level, complain_t *c, const char *format, ...)
@@ -441,5 +747,22 @@ void plugin_relief (int level, complain_t *c, const char *format, ...)
        message[511] = '\0';
        va_end (ap);
 
-       syslog (level, message);
+       plugin_log (level, message);
 }
+
+const data_set_t *plugin_get_ds (const char *name)
+{
+       data_set_t *ds;
+       llentry_t *le;
+
+       le = llist_search (list_data_set, name);
+       if (le == NULL)
+       {
+               DEBUG ("No such dataset registered: %s", name);
+               return (NULL);
+       }
+
+       ds = (data_set_t *) le->value;
+
+       return (ds);
+} /* data_set_t *plugin_get_ds */
index 45e9b49..83c2109 100644 (file)
@@ -7,8 +7,7 @@
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
  *   Florian octo Forster <octo at verplant.org>
  **/
 
+#define DATA_MAX_NAME_LEN 64
+
+#define DS_TYPE_COUNTER 0
+#define DS_TYPE_GAUGE   1
+
+#ifndef LOG_ERR
+# define LOG_ERR 3
+#endif
+#ifndef LOG_WARNING
+# define LOG_WARNING 4
+#endif
+#ifndef LOG_NOTICE
+# define LOG_NOTICE 5
+#endif
+#ifndef LOG_INFO
+# define LOG_INFO 6
+#endif
+#ifndef LOG_DEBUG
+# define LOG_DEBUG 7
+#endif
+
 /*
- *
+ * Public data types
  */
+typedef unsigned long long counter_t;
+typedef double gauge_t;
+
+union value_u
+{
+       counter_t counter;
+       gauge_t   gauge;
+};
+typedef union value_u value_t;
+
+struct value_list_s
+{
+       value_t *values;
+       int      values_len;
+       time_t   time;
+       char     host[DATA_MAX_NAME_LEN];
+       char     plugin[DATA_MAX_NAME_LEN];
+       char     plugin_instance[DATA_MAX_NAME_LEN];
+       char     type_instance[DATA_MAX_NAME_LEN];
+};
+typedef struct value_list_s value_list_t;
+
+#define VALUE_LIST_INIT { NULL, 0, 0, "localhost", "", "", "" }
+
+struct data_source_s
+{
+       char   name[DATA_MAX_NAME_LEN];
+       int    type;
+       double min;
+       double max;
+};
+typedef struct data_source_s data_source_t;
+
+struct data_set_s
+{
+       char           type[DATA_MAX_NAME_LEN];
+       int            ds_num;
+       data_source_t *ds;
+};
+typedef struct data_set_s data_set_t;
+
 typedef struct complain_s
 {
        unsigned int interval; /* how long we wait for reporting this error again */
@@ -49,34 +110,6 @@ void plugin_set_dir (const char *dir);
 
 /*
  * NAME
- *  plugin_count
- *
- * DESCRIPTION
- *  trivial
- *
- * RETURN VALUE
- *  The number of currently loaded plugins
- */
-int plugin_count (void);
-
-/*
- * NAME
- *  plugin_exists
- *
- * DESCRIPTION
- *  trivial
- *
- * ARGUMENTS
- *  `type'      Name of the plugin.
- *
- * RETURN VALUE
- *  Returns non-zero if a plugin with the name $type is found and zero
- *  otherwise.
- */
-int plugin_exists (char *type);
-
-/*
- * NAME
  *  plugin_load
  *
  * DESCRIPTION
@@ -86,7 +119,8 @@ int plugin_exists (char *type);
  *  functions.
  *
  * ARGUMENTS
- *  `type'      Name of the plugin to load.
+ *  `name'      Name of the plugin to load.
+ *  `mr'        Types of functions to request from the plugin.
  *
  * RETURN VALUE
  *  Returns zero upon success, a value greater than zero if no plugin was found
@@ -95,43 +129,68 @@ int plugin_exists (char *type);
  * NOTES
  *  No attempt is made to re-load an already loaded module.
  */
-int  plugin_load (const char *type);
+int plugin_load (const char *name);
 
-int  plugin_load_all (char *dir);
 void plugin_init_all (void);
 void plugin_read_all (const int *loop);
-
 void plugin_shutdown_all (void);
 
-void plugin_register (char *type,
-               void (*init) (void),
-               void (*read) (void),
-               void (*write) (char *, char *, char *));
+/*
+ * The `plugin_register_*' functions are used to make `config', `init',
+ * `read', `write' and `shutdown' functions known to the plugin
+ * infrastructure. Also, the data-formats are made public like this.
+ */
+int plugin_register_config (const char *name,
+               int (*callback) (const char *key, const char *val),
+               const char **keys, int keys_num);
+int plugin_register_init (const char *name,
+               int (*callback) (void));
+int plugin_register_read (const char *name,
+               int (*callback) (void));
+int plugin_register_write (const char *name,
+               int (*callback) (const data_set_t *ds, const value_list_t *vl));
+int plugin_register_shutdown (char *name,
+               int (*callback) (void));
+int plugin_register_data_set (const data_set_t *ds);
+int plugin_register_log (char *name,
+               void (*callback) (int, const char *));
 
-int plugin_register_shutdown (char *, void (*) (void));
+int plugin_unregister_config (const char *name);
+int plugin_unregister_init (const char *name);
+int plugin_unregister_read (const char *name);
+int plugin_unregister_write (const char *name);
+int plugin_unregister_shutdown (const char *name);
+int plugin_unregister_data_set (const char *name);
+int plugin_unregister_log (const char *name);
 
 /*
  * NAME
- *  plugin_write
+ *  plugin_dispatch_values
  *
  * DESCRIPTION
- *  Searches the plugin for `type' in the plugin-list. If found, and a `write'
- *  function is registered, it's called. If either the plugin is not found or
- *  the plugin doesn't provide a `write' function this function will return
- *  without further notice.
+ *  This function is called by reading processes with the values they've
+ *  aquired. The function fetches the data-set definition (that has been
+ *  registered using `plugin_register_data_set') and calls _all_ registered
+ *  write-functions.
  *
  * ARGUMENTS
- *  `host'      Host(name) from which the data originates.
- *  `type'      Name of the plugin.
- *  `inst'      Instance (passed to the plugin's `write' function.
- *  `val'       Values for the RRD files. Also passed to the plugin.
+ *  `name'      Name/type of the data-set that describe the values in `vl'.
+ *  `vl'        Value list of the values that have been read by a `read'
+ *              function.
  */
-void plugin_write    (char *host, char *type, char *inst, char *val);
-
-void plugin_submit   (char *type, char *inst, char *val);
+int plugin_dispatch_values (const char *name, value_list_t *vl);
 
+void plugin_log (int level, const char *format, ...);
+#define ERROR(...)   plugin_log (LOG_ERR,     __VA_ARGS__)
+#define WARNING(...) plugin_log (LOG_WARNING, __VA_ARGS__)
+#define NOTICE(...)  plugin_log (LOG_NOTICE,  __VA_ARGS__)
+#define INFO(...)    plugin_log (LOG_INFO,    __VA_ARGS__)
+#define DEBUG(...)   plugin_log (LOG_DEBUG,   __VA_ARGS__)
 
+/* TODO: Move plugin_{complain,relief} into `utils_complain.[ch]'. -octo */
 void plugin_complain (int level, complain_t *c, const char *format, ...);
 void plugin_relief (int level, complain_t *c, const char *format, ...);
 
+const data_set_t *plugin_get_ds (const char *name);
+
 #endif /* PLUGIN_H */
index ccf1571..ff40126 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * collectd - src/processes.c
  * Copyright (C) 2005  Lyonel Vincent
- * Copyright (C) 2006  Florian Forster (Mach code)
+ * Copyright (C) 2006-2007  Florian Forster (Mach code)
  *
  * 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
@@ -25,7 +25,6 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
-#include "utils_debug.h"
 #include "configfile.h"
 
 /* Include header files for the mach system, if they exist.. */
 
 #define BUFSIZE 256
 
-static char *processes_file = "processes.rrd";
-static char *processes_ds_def[] =
-{
-       "DS:running:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       "DS:sleeping:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       "DS:zombies:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       "DS:stopped:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       "DS:paging:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       "DS:blocked:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       NULL
-};
-static int processes_ds_num = 6;
-
-static char *ps_rss_file = "processes/ps_rss-%s.rrd";
-static char *ps_rss_ds_def[] =
-{
-       /* max = 2^63 - 1 */
-       "DS:byte:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       NULL
-};
-static int ps_rss_ds_num = 1;
-
-static char *ps_cputime_file = "processes/ps_cputime-%s.rrd";
-static char *ps_cputime_ds_def[] =
-{
-       /* 1 second in user-mode per second ought to be enough.. */
-       "DS:user:COUNTER:"COLLECTD_HEARTBEAT":0:1000000",
-       "DS:syst:COUNTER:"COLLECTD_HEARTBEAT":0:1000000",
-       NULL
-};
-static int ps_cputime_ds_num = 2;
-
-static char *ps_count_file = "processes/ps_count-%s.rrd";
-static char *ps_count_ds_def[] =
-{
-       "DS:processes:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       "DS:threads:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       NULL
-};
-static int ps_count_ds_num = 2;
-
-static char *ps_pagefaults_file = "processes/ps_pagefaults-%s.rrd";
-static char *ps_pagefaults_ds_def[] =
-{
-       /* max = 2^63 - 1 */
-       "DS:minflt:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:majflt:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       NULL
-};
-static int ps_pagefaults_ds_num = 2;
-
+#if PROCESSES_HAVE_READ
 #if HAVE_THREAD_INFO | KERNEL_LINUX
-static char *config_keys[] =
+static const char *config_keys[] =
 {
        "Process",
        NULL
@@ -381,7 +330,7 @@ static void ps_list_reset (void)
                {
                        if (pse->age > 10)
                        {
-                               DBG ("Removing this procstat entry cause it's too old: "
+                               DEBUG ("Removing this procstat entry cause it's too old: "
                                                "id = %lu; name = %s;",
                                                pse->id, ps->name);
 
@@ -408,7 +357,7 @@ static void ps_list_reset (void)
        } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
 }
 
-static int ps_config (char *key, char *value)
+static int ps_config (const char *key, const char *value)
 {
        if (strcasecmp (key, "Process") == 0)
        {
@@ -423,7 +372,7 @@ static int ps_config (char *key, char *value)
 }
 #endif /* HAVE_THREAD_INFO | KERNEL_LINUX */
 
-static void ps_init (void)
+static int ps_init (void)
 {
 #if HAVE_THREAD_INFO
        kern_return_t status;
@@ -444,149 +393,79 @@ static void ps_init (void)
                                        &pset_list,
                                        &pset_list_len)) != KERN_SUCCESS)
        {
-               syslog (LOG_ERR, "host_processor_sets failed: %s\n",
+               ERROR ("host_processor_sets failed: %s\n",
                                mach_error_string (status));
                pset_list = NULL;
                pset_list_len = 0;
-               return;
+               return (-1);
        }
 /* #endif HAVE_THREAD_INFO */
 
 #elif KERNEL_LINUX
        pagesize_g = sysconf(_SC_PAGESIZE);
-       DBG ("pagesize_g = %li; CONFIG_HZ = %i;",
+       DEBUG ("pagesize_g = %li; CONFIG_HZ = %i;",
                        pagesize_g, CONFIG_HZ);
 #endif /* KERNEL_LINUX */
 
-       return;
-}
-
-static void ps_write (char *host, char *inst, char *val)
-{
-       rrd_update_file (host, processes_file, val,
-                       processes_ds_def, processes_ds_num);
-}
-
-static void ps_rss_write (char *host, char *inst, char *val)
-{
-       char filename[256];
-       int status;
-
-       status = snprintf (filename, 256, ps_rss_file, inst);
-       if ((status < 1) || (status >= 256))
-               return;
-
-       rrd_update_file (host, filename, val, ps_rss_ds_def, ps_rss_ds_num);
-}
-
-static void ps_cputime_write (char *host, char *inst, char *val)
-{
-       char filename[256];
-       int status;
-
-       status = snprintf (filename, 256, ps_cputime_file, inst);
-       if ((status < 1) || (status >= 256))
-               return;
-
-       DBG ("host = %s; filename = %s; val = %s;",
-                       host, filename, val);
-       rrd_update_file (host, filename, val,
-                       ps_cputime_ds_def, ps_cputime_ds_num);
-}
-
-static void ps_count_write (char *host, char *inst, char *val)
-{
-       char filename[256];
-       int status;
-
-       status = snprintf (filename, 256, ps_count_file, inst);
-       if ((status < 1) || (status >= 256))
-               return;
-
-       DBG ("host = %s; filename = %s; val = %s;",
-                       host, filename, val);
-       rrd_update_file (host, filename, val,
-                       ps_count_ds_def, ps_count_ds_num);
-}
-
-static void ps_pagefaults_write (char *host, char *inst, char *val)
-{
-       char filename[256];
-       int status;
-
-       status = snprintf (filename, 256, ps_pagefaults_file, inst);
-       if ((status < 1) || (status >= 256))
-               return;
-
-       DBG ("host = %s; filename = %s; val = %s;",
-                       host, filename, val);
-       rrd_update_file (host, filename, val,
-                       ps_pagefaults_ds_def, ps_pagefaults_ds_num);
-}
+       return (0);
+} /* int ps_init */
 
-#if PROCESSES_HAVE_READ
-static void ps_submit (int running,
-               int sleeping,
-               int zombies,
-               int stopped,
-               int paging,
-               int blocked)
+static void ps_submit_state (const char *state, double value)
 {
-       char buf[BUFSIZE];
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (buf, BUFSIZE, "%u:%i:%i:%i:%i:%i:%i",
-                               (unsigned int) curtime,
-                               running, sleeping, zombies, stopped, paging,
-                               blocked) >= BUFSIZE)
-               return;
+       values[0].gauge = value;
 
-       DBG ("running = %i; sleeping = %i; zombies = %i; stopped = %i; paging = %i; blocked = %i;",
-                       running, sleeping, zombies, stopped, paging, blocked);
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "processes");
+       strcpy (vl.plugin_instance, "");
+       strncpy (vl.type_instance, state, sizeof (vl.type_instance));
 
-       plugin_submit (MODULE_NAME, "-", buf);
+       plugin_dispatch_values ("ps_state", &vl);
 }
 
 static void ps_submit_proc_list (procstat_t *ps)
 {
-       char buffer[64];
-
-       if (ps == NULL)
-               return;
-
-       snprintf (buffer, 64, "%u:%lu",
-                       (unsigned int) curtime,
-                       ps->vmem_rss);
-       buffer[63] = '\0';
-       plugin_submit ("ps_rss", ps->name, buffer);
-
-       snprintf (buffer, 64, "%u:%u:%u",
-                       (unsigned int) curtime,
-                       /* Make the counter overflow */
-                       (unsigned int) (ps->cpu_user_counter   & 0xFFFFFFFF),
-                       (unsigned int) (ps->cpu_system_counter & 0xFFFFFFFF));
-       buffer[63] = '\0';
-       plugin_submit ("ps_cputime", ps->name, buffer);
-
-       snprintf (buffer, 64, "%u:%lu:%lu",
-                       (unsigned int) curtime,
-                       ps->num_proc, ps->num_lwp);
-       buffer[63] = '\0';
-       plugin_submit ("ps_count", ps->name, buffer);
-
-       snprintf (buffer, 64, "%u:%lu:%lu",
-                       (unsigned int) curtime,
-                       ps->vmem_minflt_counter, ps->vmem_majflt_counter);
-       buffer[63] = '\0';
-       plugin_submit ("ps_pagefaults", ps->name, buffer);
-
-       DBG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
+
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "processes");
+       strncpy (vl.plugin_instance, ps->name, sizeof (vl.plugin_instance));
+
+       vl.values[0].gauge = ps->vmem_rss;
+       vl.values_len = 1;
+       plugin_dispatch_values ("ps_rss", &vl);
+
+       vl.values[0].counter = ps->cpu_user_counter;
+       vl.values[1].counter = ps->cpu_system_counter;
+       vl.values_len = 2;
+       plugin_dispatch_values ("ps_cputime", &vl);
+
+       vl.values[0].gauge = ps->num_proc;
+       vl.values[1].gauge = ps->num_lwp;
+       vl.values_len = 2;
+       plugin_dispatch_values ("ps_count", &vl);
+
+       vl.values[0].counter = ps->vmem_minflt_counter;
+       vl.values[1].counter = ps->vmem_majflt_counter;
+       vl.values_len = 2;
+       plugin_dispatch_values ("ps_pagefaults", &vl);
+
+       DEBUG ("name = %s; num_proc = %lu; num_lwp = %lu; vmem_rss = %lu; "
                        "vmem_minflt_counter = %lu; vmem_majflt_counter = %lu; "
                        "cpu_user_counter = %lu; cpu_system_counter = %lu;",
                        ps->name, ps->num_proc, ps->num_lwp, ps->vmem_rss,
-                       ps->vmem_minflt_counter, ps->vmem_majflt_counter, ps->cpu_user_counter,
-                       ps->cpu_system_counter);
-
-}
+                       ps->vmem_minflt_counter, ps->vmem_majflt_counter,
+                       ps->cpu_user_counter, ps->cpu_system_counter);
+} /* void ps_submit_proc_list */
 
 #if KERNEL_LINUX
 static int *ps_read_tasks (int pid)
@@ -604,7 +483,7 @@ static int *ps_read_tasks (int pid)
 
        if ((dh = opendir (dirname)) == NULL)
        {
-               DBG ("Failed to open directory `%s'", dirname);
+               DEBUG ("Failed to open directory `%s'", dirname);
                return (NULL);
        }
 
@@ -624,7 +503,7 @@ static int *ps_read_tasks (int pid)
                        {
                                if (list != NULL)
                                        free (list);
-                               syslog (LOG_ERR, "processes plugin: "
+                               ERROR ("processes plugin: "
                                                "Failed to allocate more memory.");
                                return (NULL);
                        }
@@ -686,20 +565,17 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
        fields_len = strsplit (buffer, fields, 64);
        if (fields_len < 24)
        {
-               DBG ("`%s' has only %i fields..",
-                               filename, fields_len);
+               DEBUG ("processes plugin: ps_read_process (pid = %i):"
+                               " `%s' has only %i fields..",
+                               (int) pid, filename, fields_len);
                return (-1);
        }
-       else if (fields_len != 41)
-       {
-               DBG ("WARNING: (fields_len = %i) != 41", fields_len);
-       }
 
        /* copy the name, strip brackets in the process */
        name_len = strlen (fields[1]) - 2;
        if ((fields[1][0] != '(') || (fields[1][name_len + 1] != ')'))
        {
-               DBG ("No brackets found in process name: `%s'", fields[1]);
+               DEBUG ("No brackets found in process name: `%s'", fields[1]);
                return (-1);
        }
        fields[1] = fields[1] + 1;
@@ -708,19 +584,19 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
 
        ppid = atoi (fields[3]);
 
-       *state = fields[2][0];
-       if (*state == 'Z')
-       {
-               ps->num_lwp  = 0;
-               ps->num_proc = 0;
-       }
-       else if ((tasks = ps_read_tasks (pid)) == NULL)
-       {
-               /* Kernel 2.4 or so */
-               ps->num_lwp  = 1;
-               ps->num_proc = 1;
-       }
+       *state = fields[2][0];
+
+       if (*state == 'Z')
+       {
+               ps->num_lwp  = 0;
+               ps->num_proc = 0;
+       }
+       else if ((tasks = ps_read_tasks (pid)) == NULL)
+       {
+               /* Kernel 2.4 or so */
+               ps->num_lwp  = 1;
+               ps->num_proc = 1;
+       }
        else
        {
                ps->num_lwp  = 0;
@@ -732,10 +608,10 @@ int ps_read_process (int pid, procstat_t *ps, char *state)
                tasks = NULL;
        }
 
-       /* Leave the rest at zero if this is only a zombi */
+       /* Leave the rest at zero if this is only a zombi */
        if (ps->num_proc == 0)
        {
-               DBG ("This is only a zombi: pid = %i; name = %s;",
+               DEBUG ("This is only a zombi: pid = %i; name = %s;",
                                pid, ps->name);
                return (0);
        }
@@ -786,7 +662,7 @@ static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_l
        strncpy (name, kp.kp_proc.p_comm, name_max_len - 1);
        name[name_max_len - 1] = '\0';
 
-       DBG ("pid = %i; name = %s;", *pid, name);
+       DEBUG ("pid = %i; name = %s;", *pid, name);
 
        /* We don't do the special handling for `p_comm == "LaunchCFMApp"' as
         * `top' does it, because it is a lot of work and only used when
@@ -796,7 +672,7 @@ static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_l
 }
 #endif /* HAVE_THREAD_INFO */
 
-static void ps_read (void)
+static int ps_read (void)
 {
 #if HAVE_THREAD_INFO
        kern_return_t            status;
@@ -842,7 +718,7 @@ static void ps_read (void)
                                                pset_list[pset],
                                                &port_pset_priv)) != KERN_SUCCESS)
                {
-                       syslog (LOG_ERR, "host_processor_set_priv failed: %s\n",
+                       ERROR ("host_processor_set_priv failed: %s\n",
                                        mach_error_string (status));
                        continue;
                }
@@ -851,7 +727,7 @@ static void ps_read (void)
                                                &task_list,
                                                &task_list_len)) != KERN_SUCCESS)
                {
-                       syslog (LOG_ERR, "processor_set_tasks failed: %s\n",
+                       ERROR ("processor_set_tasks failed: %s\n",
                                        mach_error_string (status));
                        mach_port_deallocate (port_task_self, port_pset_priv);
                        continue;
@@ -885,7 +761,7 @@ static void ps_read (void)
                                                &task_basic_info_len);
                                if (status != KERN_SUCCESS)
                                {
-                                       syslog (LOG_ERR, "task_info failed: %s",
+                                       ERROR ("task_info failed: %s",
                                                        mach_error_string (status));
                                        continue; /* with next thread_list */
                                }
@@ -897,7 +773,7 @@ static void ps_read (void)
                                                &task_events_info_len);
                                if (status != KERN_SUCCESS)
                                {
-                                       syslog (LOG_ERR, "task_info failed: %s",
+                                       ERROR ("task_info failed: %s",
                                                        mach_error_string (status));
                                        continue; /* with next thread_list */
                                }
@@ -909,7 +785,7 @@ static void ps_read (void)
                                                &task_absolutetime_info_len);
                                if (status != KERN_SUCCESS)
                                {
-                                       syslog (LOG_ERR, "task_info failed: %s",
+                                       ERROR ("task_info failed: %s",
                                                        mach_error_string (status));
                                        continue; /* with next thread_list */
                                }
@@ -933,7 +809,7 @@ static void ps_read (void)
                                 * thread is nonsense, since the task/process
                                 * is dead. */
                                zombies++;
-                               DBG ("task_threads failed: %s",
+                               DEBUG ("task_threads failed: %s",
                                                mach_error_string (status));
                                if (task_list[task] != port_task_self)
                                        mach_port_deallocate (port_task_self,
@@ -950,7 +826,7 @@ static void ps_read (void)
                                                &thread_data_len);
                                if (status != KERN_SUCCESS)
                                {
-                                       syslog (LOG_ERR, "thread_info failed: %s",
+                                       ERROR ("thread_info failed: %s",
                                                        mach_error_string (status));
                                        if (task_list[task] != port_task_self)
                                                mach_port_deallocate (port_task_self,
@@ -982,8 +858,7 @@ static void ps_read (void)
                                         * There's only zombie tasks, which are
                                         * handled above. */
                                        default:
-                                               syslog (LOG_WARNING,
-                                                               "Unknown thread status: %s",
+                                               WARNING ("Unknown thread status: %s",
                                                                thread_data.run_state);
                                                break;
                                } /* switch (thread_data.run_state) */
@@ -993,7 +868,7 @@ static void ps_read (void)
                                        status = mach_port_deallocate (port_task_self,
                                                        thread_list[thread]);
                                        if (status != KERN_SUCCESS)
-                                               syslog (LOG_ERR, "mach_port_deallocate failed: %s",
+                                               ERROR ("mach_port_deallocate failed: %s",
                                                                mach_error_string (status));
                                }
                        } /* for (thread_list) */
@@ -1003,7 +878,7 @@ static void ps_read (void)
                                                        thread_list_len * sizeof (thread_act_t)))
                                        != KERN_SUCCESS)
                        {
-                               syslog (LOG_ERR, "vm_deallocate failed: %s",
+                               ERROR ("vm_deallocate failed: %s",
                                                mach_error_string (status));
                        }
                        thread_list = NULL;
@@ -1017,7 +892,7 @@ static void ps_read (void)
                                status = mach_port_deallocate (port_task_self,
                                                task_list[task]);
                                if (status != KERN_SUCCESS)
-                                       syslog (LOG_ERR, "mach_port_deallocate failed: %s",
+                                       ERROR ("mach_port_deallocate failed: %s",
                                                        mach_error_string (status));
                        }
 
@@ -1029,7 +904,7 @@ static void ps_read (void)
                                (vm_address_t) task_list,
                                task_list_len * sizeof (task_t))) != KERN_SUCCESS)
                {
-                       syslog (LOG_ERR, "vm_deallocate failed: %s",
+                       ERROR ("vm_deallocate failed: %s",
                                        mach_error_string (status));
                }
                task_list = NULL;
@@ -1038,12 +913,16 @@ static void ps_read (void)
                if ((status = mach_port_deallocate (port_task_self, port_pset_priv))
                                != KERN_SUCCESS)
                {
-                       syslog (LOG_ERR, "mach_port_deallocate failed: %s",
+                       ERROR ("mach_port_deallocate failed: %s",
                                        mach_error_string (status));
                }
        } /* for (pset_list) */
 
-       ps_submit (running, sleeping, zombies, stopped, -1, blocked);
+       ps_submit_state ("running", running);
+       ps_submit_state ("sleeping", sleeping);
+       ps_submit_state ("zombies", zombies);
+       ps_submit_state ("stopped", stopped);
+       ps_submit_state ("blocked", blocked);
 
        for (ps = list_head_g; ps != NULL; ps = ps->next)
                ps_submit_proc_list (ps);
@@ -1073,8 +952,10 @@ static void ps_read (void)
 
        if ((proc = opendir ("/proc")) == NULL)
        {
-               syslog (LOG_ERR, "Cannot open `/proc': %s", strerror (errno));
-               return;
+               char errbuf[1024];
+               ERROR ("Cannot open `/proc': %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
        while ((ent = readdir (proc)) != NULL)
@@ -1088,7 +969,7 @@ static void ps_read (void)
                status = ps_read_process (pid, &ps, &state);
                if (status != 0)
                {
-                       DBG ("ps_read_process failed: %i", status);
+                       DEBUG ("ps_read_process failed: %i", status);
                        continue;
                }
 
@@ -1124,27 +1005,29 @@ static void ps_read (void)
 
        closedir (proc);
 
-       ps_submit (running, sleeping, zombies, stopped, paging, blocked);
+       ps_submit_state ("running",  running);
+       ps_submit_state ("sleeping", sleeping);
+       ps_submit_state ("zombies",  zombies);
+       ps_submit_state ("stopped",  stopped);
+       ps_submit_state ("paging",   paging);
+       ps_submit_state ("blocked",  blocked);
 
        for (ps_ptr = list_head_g; ps_ptr != NULL; ps_ptr = ps_ptr->next)
                ps_submit_proc_list (ps_ptr);
 #endif /* KERNEL_LINUX */
-}
-#else
-# define ps_read NULL
+
+       return (0);
+} /* int ps_read */
 #endif /* PROCESSES_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, ps_init, ps_read, ps_write);
-       plugin_register ("ps_rss", NULL, NULL, ps_rss_write);
-       plugin_register ("ps_cputime", NULL, NULL, ps_cputime_write);
-       plugin_register ("ps_count", NULL, NULL, ps_count_write);
-       plugin_register ("ps_pagefaults", NULL, NULL, ps_pagefaults_write);
+#if PROCESSES_HAVE_READ
 #if HAVE_THREAD_INFO | KERNEL_LINUX
-       cf_register (MODULE_NAME, ps_config, config_keys, config_keys_num);
+       plugin_register_config ("processes", ps_config,
+                       config_keys, config_keys_num);
 #endif
-}
-
-#undef BUFSIZE
-#undef MODULE_NAME
+       plugin_register_init ("processes", ps_init);
+       plugin_register_read ("processes", ps_read);
+#endif /* PROCESSES_HAVE_READ */
+} /* void module_register */
diff --git a/src/rrdtool.c b/src/rrdtool.c
new file mode 100644 (file)
index 0000000..ea96290
--- /dev/null
@@ -0,0 +1,891 @@
+/**
+ * collectd - src/rrdtool.c
+ * Copyright (C) 2006,2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "plugin.h"
+#include "common.h"
+#include "utils_avltree.h"
+
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+
+/*
+ * Private types
+ */
+struct rrd_cache_s
+{
+       int    values_num;
+       char **values;
+       time_t first_value;
+       time_t last_value;
+};
+typedef struct rrd_cache_s rrd_cache_t;
+
+/*
+ * Private variables
+ */
+static int rra_timespans[] =
+{
+       3600,
+       86400,
+       604800,
+       2678400,
+       31622400
+};
+static int rra_timespans_num = STATIC_ARRAY_SIZE (rra_timespans);
+
+static int *rra_timespans_custom = NULL;
+static int rra_timespans_custom_num = 0;
+
+static char *rra_types[] =
+{
+       "AVERAGE",
+       "MIN",
+       "MAX"
+};
+static int rra_types_num = STATIC_ARRAY_SIZE (rra_types);
+
+static const char *config_keys[] =
+{
+       "CacheTimeout",
+       "CacheFlush",
+       "DataDir",
+       "StepSize",
+       "HeartBeat",
+       "RRARows",
+       "RRATimespan",
+       "XFF"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static char   *datadir   = NULL;
+static int     stepsize  = 0;
+static int     heartbeat = 0;
+static int     rrarows   = 1200;
+static double  xff       = 0.1;
+
+static int         cache_timeout = 0;
+static int         cache_flush_timeout = 0;
+static time_t      cache_flush_last;
+static avl_tree_t *cache = NULL;
+static pthread_mutex_t cache_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* * * * * * * * * *
+ * WARNING:  Magic *
+ * * * * * * * * * */
+static int rra_get (char ***ret)
+{
+       static char **rra_def = NULL;
+       static int rra_num = 0;
+
+       int *rts;
+       int  rts_num;
+
+       int rra_max;
+
+       int span;
+
+       int cdp_num;
+       int cdp_len;
+       int i, j;
+
+       char buffer[64];
+
+       if ((rra_num != 0) && (rra_def != NULL))
+       {
+               *ret = rra_def;
+               return (rra_num);
+       }
+
+       /* Use the configured timespans or fall back to the built-in defaults */
+       if (rra_timespans_custom_num != 0)
+       {
+               rts = rra_timespans_custom;
+               rts_num = rra_timespans_custom_num;
+       }
+       else
+       {
+               rts = rra_timespans;
+               rts_num = rra_timespans_num;
+       }
+
+       rra_max = rts_num * rra_types_num;
+
+       if ((rra_def = (char **) malloc ((rra_max + 1) * sizeof (char *))) == NULL)
+               return (-1);
+       memset (rra_def, '\0', (rra_max + 1) * sizeof (char *));
+
+       if ((stepsize <= 0) || (rrarows <= 0))
+       {
+               *ret = NULL;
+               return (-1);
+       }
+
+       cdp_len = 0;
+       for (i = 0; i < rts_num; i++)
+       {
+               span = rts[i];
+
+               if ((span / stepsize) < rrarows)
+                       continue;
+
+               if (cdp_len == 0)
+                       cdp_len = 1;
+               else
+                       cdp_len = (int) floor (((double) span)
+                                       / ((double) (rrarows * stepsize)));
+
+               cdp_num = (int) ceil (((double) span)
+                               / ((double) (cdp_len * stepsize)));
+
+               for (j = 0; j < rra_types_num; j++)
+               {
+                       if (rra_num >= rra_max)
+                               break;
+
+                       if (snprintf (buffer, sizeof (buffer), "RRA:%s:%3.1f:%u:%u",
+                                               rra_types[j], xff,
+                                               cdp_len, cdp_num) >= sizeof (buffer))
+                       {
+                               ERROR ("rra_get: Buffer would have been truncated.");
+                               continue;
+                       }
+
+                       rra_def[rra_num++] = sstrdup (buffer);
+               }
+       }
+
+#if COLLECT_DEBUG
+       DEBUG ("rra_num = %i", rra_num);
+       for (i = 0; i < rra_num; i++)
+               DEBUG ("  %s", rra_def[i]);
+#endif
+
+       *ret = rra_def;
+       return (rra_num);
+}
+
+static void ds_free (int ds_num, char **ds_def)
+{
+       int i;
+
+       for (i = 0; i < ds_num; i++)
+               if (ds_def[i] != NULL)
+                       free (ds_def[i]);
+       free (ds_def);
+}
+
+static int ds_get (char ***ret, const data_set_t *ds)
+{
+       char **ds_def;
+       int ds_num;
+
+       char min[32];
+       char max[32];
+       char buffer[128];
+
+       DEBUG ("ds->ds_num = %i", ds->ds_num);
+
+       ds_def = (char **) malloc (ds->ds_num * sizeof (char *));
+       if (ds_def == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("rrdtool plugin: malloc failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+       memset (ds_def, '\0', ds->ds_num * sizeof (char *));
+
+       for (ds_num = 0; ds_num < ds->ds_num; ds_num++)
+       {
+               data_source_t *d = ds->ds + ds_num;
+               char *type;
+               int status;
+
+               ds_def[ds_num] = NULL;
+
+               if (d->type == DS_TYPE_COUNTER)
+                       type = "COUNTER";
+               else if (d->type == DS_TYPE_GAUGE)
+                       type = "GAUGE";
+               else
+               {
+                       ERROR ("rrdtool plugin: Unknown DS type: %i",
+                                       d->type);
+                       break;
+               }
+
+               if (isnan (d->min))
+               {
+                       strcpy (min, "U");
+               }
+               else
+               {
+                       snprintf (min, sizeof (min), "%lf", d->min);
+                       min[sizeof (min) - 1] = '\0';
+               }
+
+               if (isnan (d->max))
+               {
+                       strcpy (max, "U");
+               }
+               else
+               {
+                       snprintf (max, sizeof (max), "%lf", d->max);
+                       max[sizeof (max) - 1] = '\0';
+               }
+
+               status = snprintf (buffer, sizeof (buffer),
+                               "DS:%s:%s:%i:%s:%s",
+                               d->name, type, heartbeat,
+                               min, max);
+               if ((status < 1) || (status >= sizeof (buffer)))
+                       break;
+
+               ds_def[ds_num] = sstrdup (buffer);
+       } /* for ds_num = 0 .. ds->ds_num */
+
+#if COLLECT_DEBUG
+{
+       int i;
+       DEBUG ("ds_num = %i", ds_num);
+       for (i = 0; i < ds_num; i++)
+               DEBUG ("  %s", ds_def[i]);
+}
+#endif
+
+       if (ds_num != ds->ds_num)
+       {
+               ds_free (ds_num, ds_def);
+               return (-1);
+       }
+
+       *ret = ds_def;
+       return (ds_num);
+}
+
+static int rrd_create_file (char *filename, const data_set_t *ds)
+{
+       char **argv;
+       int argc;
+       char **rra_def;
+       int rra_num;
+       char **ds_def;
+       int ds_num;
+       int i, j;
+       char stepsize_str[16];
+       int status = 0;
+
+       if (check_create_dir (filename))
+               return (-1);
+
+       if ((rra_num = rra_get (&rra_def)) < 1)
+       {
+               ERROR ("rrd_create_file failed: Could not calculate RRAs");
+               return (-1);
+       }
+
+       if ((ds_num = ds_get (&ds_def, ds)) < 1)
+       {
+               ERROR ("rrd_create_file failed: Could not calculate DSes");
+               return (-1);
+       }
+
+       argc = ds_num + rra_num + 4;
+
+       if ((argv = (char **) malloc (sizeof (char *) * (argc + 1))) == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("rrd_create failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       status = snprintf (stepsize_str, sizeof (stepsize_str),
+                       "%i", stepsize);
+       if ((status < 1) || (status >= sizeof (stepsize_str)))
+       {
+               ERROR ("rrdtool plugin: snprintf failed.");
+               return (-1);
+       }
+
+       argv[0] = "create";
+       argv[1] = filename;
+       argv[2] = "-s";
+       argv[3] = stepsize_str;
+
+       j = 4;
+       for (i = 0; i < ds_num; i++)
+               argv[j++] = ds_def[i];
+       for (i = 0; i < rra_num; i++)
+               argv[j++] = rra_def[i];
+       argv[j] = NULL;
+
+       optind = 0; /* bug in librrd? */
+       rrd_clear_error ();
+       if (rrd_create (argc, argv) == -1)
+       {
+               ERROR ("rrd_create failed: %s: %s", filename, rrd_get_error ());
+               status = -1;
+       }
+
+       free (argv);
+       ds_free (ds_num, ds_def);
+
+       return (status);
+}
+
+static int value_list_to_string (char *buffer, int buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       int offset;
+       int status;
+       int i;
+
+       memset (buffer, '\0', sizeof (buffer_len));
+
+       status = snprintf (buffer, buffer_len, "%u", (unsigned int) vl->time);
+       if ((status < 1) || (status >= buffer_len))
+               return (-1);
+       offset = status;
+
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if ((ds->ds[i].type != DS_TYPE_COUNTER)
+                               && (ds->ds[i].type != DS_TYPE_GAUGE))
+                       return (-1);
+
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+                       status = snprintf (buffer + offset, buffer_len - offset,
+                                       ":%llu", vl->values[i].counter);
+               else
+                       status = snprintf (buffer + offset, buffer_len - offset,
+                                       ":%lf", vl->values[i].gauge);
+
+               if ((status < 1) || (status >= (buffer_len - offset)))
+                       return (-1);
+
+               offset += status;
+       } /* for ds->ds_num */
+
+       return (0);
+} /* int value_list_to_string */
+
+static int value_list_to_filename (char *buffer, int buffer_len,
+               const data_set_t *ds, const value_list_t *vl)
+{
+       int offset = 0;
+       int status;
+
+       if (datadir != NULL)
+       {
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s/", datadir);
+               if ((status < 1) || (status >= buffer_len - offset))
+                       return (-1);
+               offset += status;
+       }
+
+       status = snprintf (buffer + offset, buffer_len - offset,
+                       "%s/", vl->host);
+       if ((status < 1) || (status >= buffer_len - offset))
+               return (-1);
+       offset += status;
+
+       if (strlen (vl->plugin_instance) > 0)
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s-%s/", vl->plugin, vl->plugin_instance);
+       else
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s/", vl->plugin);
+       if ((status < 1) || (status >= buffer_len - offset))
+               return (-1);
+       offset += status;
+
+       if (strlen (vl->type_instance) > 0)
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s-%s.rrd", ds->type, vl->type_instance);
+       else
+               status = snprintf (buffer + offset, buffer_len - offset,
+                               "%s.rrd", ds->type);
+       if ((status < 1) || (status >= buffer_len - offset))
+               return (-1);
+       offset += status;
+
+       return (0);
+} /* int value_list_to_filename */
+
+static rrd_cache_t *rrd_cache_insert (const char *filename,
+               const char *value, time_t value_time)
+{
+       rrd_cache_t *rc = NULL;
+       int new_rc = 0;
+
+       if (cache != NULL)
+               avl_get (cache, filename, (void *) &rc);
+
+       if (rc == NULL)
+       {
+               rc = (rrd_cache_t *) malloc (sizeof (rrd_cache_t));
+               if (rc == NULL)
+                       return (NULL);
+               rc->values_num = 0;
+               rc->values = NULL;
+               rc->first_value = 0;
+               rc->last_value = 0;
+               new_rc = 1;
+       }
+
+       if (rc->last_value >= value_time)
+       {
+               WARNING ("rrdtool plugin: (rc->last_value = %u) >= (value_time = %u)",
+                               (unsigned int) rc->last_value,
+                               (unsigned int) value_time);
+               return (NULL);
+       }
+
+       rc->values = (char **) realloc ((void *) rc->values,
+                       (rc->values_num + 1) * sizeof (char *));
+       if (rc->values == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("rrdtool plugin: realloc failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               if (cache != NULL)
+               {
+                       void *cache_key = NULL;
+                       avl_remove (cache, filename, &cache_key, NULL);
+                       sfree (cache_key);
+               }
+               free (rc);
+               return (NULL);
+       }
+
+       rc->values[rc->values_num] = strdup (value);
+       if (rc->values[rc->values_num] != NULL)
+               rc->values_num++;
+
+       if (rc->values_num == 1)
+               rc->first_value = value_time;
+       rc->last_value = value_time;
+
+       /* Insert if this is the first value */
+       if ((cache != NULL) && (new_rc == 1))
+       {
+               void *cache_key = strdup (filename);
+
+               if (cache_key == NULL)
+               {
+                       char errbuf[1024];
+                       ERROR ("rrdtool plugin: strdup failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       sfree (rc->values[0]);
+                       sfree (rc->values);
+                       sfree (rc);
+                       return (NULL);
+               }
+
+               avl_insert (cache, cache_key, rc);
+       }
+
+       DEBUG ("rrd_cache_insert (%s, %s, %u) = %p", filename, value,
+                       (unsigned int) value_time, (void *) rc);
+
+       return (rc);
+} /* rrd_cache_t *rrd_cache_insert */
+
+static int rrd_write_cache_entry (const char *filename, rrd_cache_t *rc)
+{
+       char **argv;
+       int    argc;
+
+       char *fn;
+       int status;
+
+       int i;
+
+       if (rc->values_num < 1)
+               return (0);
+
+       argc = rc->values_num + 2;
+       argv = (char **) malloc ((argc + 1) * sizeof (char *));
+       if (argv == NULL)
+               return (-1);
+
+       fn = strdup (filename);
+       if (fn == NULL)
+       {
+               free (argv);
+               return (-1);
+       }
+
+       argv[0] = "update";
+       argv[1] = fn;
+       memcpy (argv + 2, rc->values, rc->values_num * sizeof (char *));
+       argv[argc] = NULL;
+
+       DEBUG ("rrd_update (argc = %i, argv = %p)", argc, (void *) argv);
+
+       optind = 0; /* bug in librrd? */
+       rrd_clear_error ();
+       status = rrd_update (argc, argv);
+       if (status != 0)
+       {
+               WARNING ("rrd_update failed: %s: %s",
+                               filename, rrd_get_error ());
+               status = -1;
+       }
+
+       free (argv);
+       free (fn);
+       /* Free the value list of `rc' */
+       for (i = 0; i < rc->values_num; i++)
+               free (rc->values[i]);
+       free (rc->values);
+       rc->values = NULL;
+       rc->values_num = 0;
+
+       return (status);
+} /* int rrd_write_cache_entry */
+
+static void rrd_cache_flush (int timeout)
+{
+       rrd_cache_t *rc;
+       time_t       now;
+
+       char **keys = NULL;
+       int    keys_num = 0;
+
+       char *key;
+       avl_iterator_t *iter;
+       int i;
+
+       if (cache == NULL)
+               return;
+
+       DEBUG ("Flushing cache, timeout = %i", timeout);
+
+       now = time (NULL);
+
+       /* Build a list of entries to be flushed */
+       iter = avl_get_iterator (cache);
+       while (avl_iterator_next (iter, (void *) &key, (void *) &rc) == 0)
+       {
+               DEBUG ("key = %s; age = %i;", key, now - rc->first_value);
+               if ((now - rc->first_value) >= timeout)
+               {
+                       keys = (char **) realloc ((void *) keys,
+                                       (keys_num + 1) * sizeof (char *));
+                       if (keys == NULL)
+                       {
+                               char errbuf[1024];
+                               ERROR ("rrdtool plugin: "
+                                               "realloc failed: %s",
+                                               sstrerror (errno, errbuf,
+                                                       sizeof (errbuf)));
+                               avl_iterator_destroy (iter);
+                               return;
+                       }
+                       keys[keys_num] = key;
+                       keys_num++;
+               }
+       } /* while (avl_iterator_next) */
+       avl_iterator_destroy (iter);
+       
+       for (i = 0; i < keys_num; i++)
+       {
+               if (avl_remove (cache, keys[i], (void *) &key, (void *) &rc) != 0)
+               {
+                       DEBUG ("avl_remove (%s) failed.", keys[i]);
+                       continue;
+               }
+
+               rrd_write_cache_entry (keys[i], rc);
+               /* rc's value-list is free's by `rrd_write_cache_entry' */
+               sfree (rc);
+               sfree (key);
+               keys[i] = NULL;
+       } /* for (i = 0..keys_num) */
+
+       free (keys);
+       DEBUG ("Flushed %i value(s)", keys_num);
+
+       cache_flush_last = now;
+} /* void rrd_cache_flush */
+
+static int rrd_write (const data_set_t *ds, const value_list_t *vl)
+{
+       struct stat  statbuf;
+       char         filename[512];
+       char         values[512];
+       rrd_cache_t *rc;
+       time_t       now;
+
+       if (value_list_to_filename (filename, sizeof (filename), ds, vl) != 0)
+               return (-1);
+
+       if (value_list_to_string (values, sizeof (values), ds, vl) != 0)
+               return (-1);
+
+       if (stat (filename, &statbuf) == -1)
+       {
+               if (errno == ENOENT)
+               {
+                       if (rrd_create_file (filename, ds))
+                               return (-1);
+               }
+               else
+               {
+                       char errbuf[1024];
+                       ERROR ("stat(%s) failed: %s", filename,
+                                       sstrerror (errno, errbuf,
+                                               sizeof (errbuf)));
+                       return (-1);
+               }
+       }
+       else if (!S_ISREG (statbuf.st_mode))
+       {
+               ERROR ("stat(%s): Not a regular file!",
+                               filename);
+               return (-1);
+       }
+
+       pthread_mutex_lock (&cache_lock);
+       rc = rrd_cache_insert (filename, values, vl->time);
+       if (rc == NULL)
+       {
+               pthread_mutex_unlock (&cache_lock);
+               return (-1);
+       }
+
+       if (cache == NULL)
+       {
+               rrd_write_cache_entry (filename, rc);
+               /* rc's value-list is free's by `rrd_write_cache_entry' */
+               sfree (rc);
+               pthread_mutex_unlock (&cache_lock);
+               return (0);
+       }
+
+       now = time (NULL);
+
+       DEBUG ("age (%s) = %i", filename, now - rc->first_value);
+
+       /* `rc' is not free'd here, because we'll likely reuse it. If not, then
+        * the next flush will remove this entry.  */
+       if ((now - rc->first_value) >= cache_timeout)
+               rrd_write_cache_entry (filename, rc);
+
+       if ((now - cache_flush_last) >= cache_flush_timeout)
+               rrd_cache_flush (cache_flush_timeout);
+
+       pthread_mutex_unlock (&cache_lock);
+       return (0);
+} /* int rrd_write */
+
+static int rrd_config (const char *key, const char *value)
+{
+       if (strcasecmp ("CacheTimeout", key) == 0)
+       {
+               int tmp = atoi (value);
+               if (tmp < 0)
+               {
+                       fprintf (stderr, "rrdtool: `CacheTimeout' must "
+                                       "be greater than 0.\n");
+                       return (1);
+               }
+               cache_timeout = tmp;
+       }
+       else if (strcasecmp ("CacheFlush", key) == 0)
+       {
+               int tmp = atoi (value);
+               if (tmp < 0)
+               {
+                       fprintf (stderr, "rrdtool: `CacheFlush' must "
+                                       "be greater than 0.\n");
+                       return (1);
+               }
+               cache_flush_timeout = tmp;
+       }
+       else if (strcasecmp ("DataDir", key) == 0)
+       {
+               if (datadir != NULL)
+                       free (datadir);
+               datadir = strdup (value);
+               if (datadir != NULL)
+               {
+                       int len = strlen (datadir);
+                       while ((len > 0) && (datadir[len - 1] == '/'))
+                       {
+                               len--;
+                               datadir[len] = '\0';
+                       }
+                       if (len <= 0)
+                       {
+                               free (datadir);
+                               datadir = NULL;
+                       }
+               }
+       }
+       else if (strcasecmp ("StepSize", key) == 0)
+       {
+               int tmp = atoi (value);
+               if (tmp <= 0)
+               {
+                       fprintf (stderr, "rrdtool: `StepSize' must "
+                                       "be greater than 0.\n");
+                       return (1);
+               }
+               stepsize = tmp;
+       }
+       else if (strcasecmp ("HeartBeat", key) == 0)
+       {
+               int tmp = atoi (value);
+               if (tmp <= 0)
+               {
+                       fprintf (stderr, "rrdtool: `HeartBeat' must "
+                                       "be greater than 0.\n");
+                       return (1);
+               }
+               heartbeat = tmp;
+       }
+       else if (strcasecmp ("RRARows", key) == 0)
+       {
+               int tmp = atoi (value);
+               if (tmp <= 0)
+               {
+                       fprintf (stderr, "rrdtool: `RRARows' must "
+                                       "be greater than 0.\n");
+                       return (1);
+               }
+               rrarows = tmp;
+       }
+       else if (strcasecmp ("RRATimespan", key) == 0)
+       {
+               char *saveptr = NULL;
+               char *dummy;
+               char *ptr;
+               char *value_copy;
+               int *tmp_alloc;
+
+               value_copy = strdup (value);
+               if (value_copy == NULL)
+                       return (1);
+
+               dummy = value_copy;
+               while ((ptr = strtok_r (dummy, ", \t", &saveptr)) != NULL)
+               {
+                       dummy = NULL;
+                       
+                       tmp_alloc = realloc (rra_timespans_custom,
+                                       sizeof (int) * (rra_timespans_custom_num + 1));
+                       if (tmp_alloc == NULL)
+                       {
+                               fprintf (stderr, "rrdtool: realloc failed.\n");
+                               free (value_copy);
+                               return (1);
+                       }
+                       rra_timespans_custom = tmp_alloc;
+                       rra_timespans_custom[rra_timespans_custom_num] = atoi (ptr);
+                       if (rra_timespans_custom[rra_timespans_custom_num] != 0)
+                               rra_timespans_custom_num++;
+               } /* while (strtok_r) */
+               free (value_copy);
+       }
+       else if (strcasecmp ("XFF", key) == 0)
+       {
+               double tmp = atof (value);
+               if ((tmp < 0.0) || (tmp >= 1.0))
+               {
+                       fprintf (stderr, "rrdtool: `XFF' must "
+                                       "be in the range 0 to 1 (exclusive).");
+                       return (1);
+               }
+               xff = tmp;
+       }
+       else
+       {
+               return (-1);
+       }
+       return (0);
+} /* int rrd_config */
+
+static int rrd_shutdown (void)
+{
+       pthread_mutex_lock (&cache_lock);
+       rrd_cache_flush (-1);
+       if (cache != NULL)
+               avl_destroy (cache);
+       cache = NULL;
+       pthread_mutex_unlock (&cache_lock);
+
+       return (0);
+} /* int rrd_shutdown */
+
+static int rrd_init (void)
+{
+       if (stepsize <= 0)
+               stepsize = interval_g;
+       if (heartbeat <= 0)
+               heartbeat = 2 * interval_g;
+
+       if (heartbeat < interval_g)
+               WARNING ("rrdtool plugin: Your `heartbeat' is "
+                               "smaller than your `interval'. This will "
+                               "likely cause problems.");
+       else if (stepsize < interval_g)
+               WARNING ("rrdtool plugin: Your `stepsize' is "
+                               "smaller than your `interval'. This will "
+                               "create needlessly big RRD-files.");
+
+       pthread_mutex_lock (&cache_lock);
+       if (cache_timeout < 2)
+       {
+               cache_timeout = 0;
+               cache_flush_timeout = 0;
+       }
+       else
+       {
+               if (cache_flush_timeout < cache_timeout)
+                       cache_flush_timeout = 10 * cache_timeout;
+
+               cache = avl_create ((int (*) (const void *, const void *)) strcmp);
+               cache_flush_last = time (NULL);
+               plugin_register_shutdown ("rrdtool", rrd_shutdown);
+       }
+       pthread_mutex_unlock (&cache_lock);
+
+       DEBUG ("rrdtool plugin: rrd_init: datadir = %s; stepsize = %i;"
+                       " heartbeat = %i; rrarows = %i; xff = %lf;",
+                       (datadir == NULL) ? "(null)" : datadir,
+                       stepsize, heartbeat, rrarows, xff);
+
+       return (0);
+} /* int rrd_init */
+
+void module_register (void)
+{
+       plugin_register_config ("rrdtool", rrd_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("rrdtool", rrd_init);
+       plugin_register_write ("rrdtool", rrd_write);
+}
index 66cd5b2..e1bc7aa 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/sensors.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 #include "plugin.h"
 #include "configfile.h"
 #include "utils_ignorelist.h"
-#include "utils_debug.h"
-
-#define MODULE_NAME "sensors"
-#define MODULE_NAME_VOLTAGE MODULE_NAME"_voltage"
 
 #if defined(HAVE_SENSORS_SENSORS_H)
 # include <sensors/sensors.h>
 # define SENSORS_HAVE_READ 0
 #endif
 
-#define BUFSIZE 512
-
-/* temperature and fan sensors */
-static char *ds_def[] =
-{
-       "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int ds_num = 1;
-
-/* voltage sensors */
-static char *sensor_voltage_ds_def[] = 
-{
-       "DS:voltage:GAUGE:"COLLECTD_HEARTBEAT":U:U",
-       NULL
-};
-static int sensor_voltage_ds_num = 1;
-
-/* old naming */
-static char *old_filename_format = "sensors-%s.rrd";
-/* end old naming */
-
-/* new naming <chip-bus-address/type-feature */
-static char *extended_filename_format = "lm_sensors-%s.rrd";
-
-#define SENSOR_TYPE_UNKNOWN 0
-#define SENSOR_TYPE_VOLTAGE 1
-#define SENSOR_TYPE_FANSPEED 2
-#define SENSOR_TYPE_TEMPERATURE 3
-
 #if SENSORS_HAVE_READ
-static char *sensor_type_prefix[] =
+#define SENSOR_TYPE_VOLTAGE     0
+#define SENSOR_TYPE_FANSPEED    1
+#define SENSOR_TYPE_TEMPERATURE 2
+#define SENSOR_TYPE_UNKNOWN     3
+
+static char *sensor_to_type[] =
 {
-       "unknown",
        "voltage",
        "fanspeed",
        "temperature",
        NULL
 };
-#endif
 
-typedef struct sensors_labeltypes {
+struct sensors_labeltypes_s
+{
        char *label;
        int type;
-} sensors_labeltypes;
+};
+typedef struct sensors_labeltypes_s sensors_labeltypes_t;
 
 /*
  * finite list of known labels extracted from lm_sensors
  */
-#if SENSORS_HAVE_READ
-static sensors_labeltypes known_features[] = 
+static sensors_labeltypes_t known_features[] = 
 {
        { "fan1", SENSOR_TYPE_FANSPEED },
        { "fan2", SENSOR_TYPE_FANSPEED },
@@ -154,34 +123,24 @@ static sensors_labeltypes known_features[] =
        { "3.3V", SENSOR_TYPE_VOLTAGE },
        { "2.5V", SENSOR_TYPE_VOLTAGE },
        { "2.0V", SENSOR_TYPE_VOLTAGE },
-       { "12V", SENSOR_TYPE_VOLTAGE },
-       { 0, -1 }
+       { "12V", SENSOR_TYPE_VOLTAGE }
 };
-#endif
+static int known_features_num = STATIC_ARRAY_SIZE (known_features);
 /* end new naming */
 
-static char *config_keys[] =
+static const char *config_keys[] =
 {
        "Sensor",
        "IgnoreSelected",
-       "ExtendedSensorNaming",
        NULL
 };
-static int config_keys_num = 3;
+static int config_keys_num = 2;
 
 static ignorelist_t *sensor_list;
 
-/* 
- * sensor_extended_naming:
- * 0 => default is to create chip-feature
- * 1 => use new naming scheme chip-bus-address/type-feature
- */
-static int sensor_extended_naming = 0;
-
-#if SENSORS_HAVE_READ
-#  ifndef SENSORS_CONF_PATH
-#    define SENSORS_CONF_PATH "/etc/sensors.conf"
-#  endif
+#ifndef SENSORS_CONF_PATH
+# define SENSORS_CONF_PATH "/etc/sensors.conf"
+#endif
 
 static const char *conffile = SENSORS_CONF_PATH;
 /* SENSORS_CONF_PATH */
@@ -200,9 +159,8 @@ typedef struct featurelist
 } featurelist_t;
 
 featurelist_t *first_feature = NULL;
-#endif /* if SENSORS_HAVE_READ */
 
-static int sensors_config (char *key, char *value)
+static int sensors_config (const char *key, const char *value)
 {
        if (sensor_list == NULL)
                sensor_list = ignorelist_create (1);
@@ -211,7 +169,8 @@ static int sensors_config (char *key, char *value)
        {
                if (ignorelist_add (sensor_list, value))
                {
-                       syslog (LOG_EMERG, MODULE_NAME": Cannot add value to ignorelist.");
+                       ERROR ("sensors plugin: "
+                                       "Cannot add value to ignorelist.");
                        return (1);
                }
        }
@@ -223,15 +182,6 @@ static int sensors_config (char *key, char *value)
                                || (strcasecmp (value, "On") == 0))
                        ignorelist_set_invert (sensor_list, 0);
        }
-       else if (strcasecmp (key, "ExtendedSensorNaming") == 0)
-       {
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
-                       sensor_extended_naming = 1;
-               else
-                       sensor_extended_naming = 0;
-       }
        else
        {
                return (-1);
@@ -240,7 +190,6 @@ static int sensors_config (char *key, char *value)
        return (0);
 }
 
-#if SENSORS_HAVE_READ
 void sensors_free_features (void)
 {
        featurelist_t *thisft;
@@ -259,7 +208,7 @@ void sensors_free_features (void)
        first_feature = NULL;
 }
 
-static void sensors_load_conf (void)
+static int sensors_load_conf (void)
 {
        FILE *fh;
        featurelist_t *last_feature = NULL;
@@ -277,18 +226,19 @@ static void sensors_load_conf (void)
        status = stat (conffile, &statbuf);
        if (status != 0)
        {
-               syslog (LOG_ERR, MODULE_NAME": stat(%s) failed: %s",
-                               conffile, strerror (errno));
+               char errbuf[1024];
+               ERROR ("sensors plugin: stat (%s) failed: %s", conffile,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                sensors_config_mtime = 0;
        }
 
        if ((sensors_config_mtime != 0)
                        && (sensors_config_mtime == statbuf.st_mtime))
-               return;
+               return (0);
 
        if (sensors_config_mtime != 0)
        {
-               syslog (LOG_NOTICE, MODULE_NAME": Reloading config from %s",
+               NOTICE ("sensors plugin: Reloading config from %s",
                                conffile);
                sensors_free_features ();
                sensors_config_mtime = 0;
@@ -297,18 +247,19 @@ static void sensors_load_conf (void)
        fh = fopen (conffile, "r");
        if (fh == NULL)
        {
-               syslog (LOG_ERR, MODULE_NAME": fopen(%s) failed: %s",
-                               conffile, strerror(errno));
-               return;
+               char errbuf[1024];
+               ERROR ("sensors plugin: fopen(%s) failed: %s", conffile,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
        status = sensors_init (fh);
        fclose (fh);
        if (status != 0)
        {
-               syslog (LOG_ERR, MODULE_NAME": Cannot initialize sensors. "
+               ERROR ("sensors plugin: Cannot initialize sensors. "
                                "Data will not be collected.");
-               return;
+               return (-1);
        }
 
        sensors_config_mtime = statbuf.st_mtime;
@@ -329,7 +280,7 @@ static void sensors_load_conf (void)
                                continue;
 
                        /* Only known features */
-                       for (i = 0; known_features[i].type >= 0; i++)
+                       for (i = 0; i < known_features_num; i++)
                        {
                                if (strcmp (data->name, known_features[i].label) != 0)
                                        continue;
@@ -338,16 +289,16 @@ static void sensors_load_conf (void)
                                if (sensors_get_ignored (*chip, data->number) == 0)
                                        break;
 
-                               DBG ("Adding feature: %s-%s-%s",
+                               DEBUG ("Adding feature: %s-%s-%s",
                                                chip->prefix,
-                                               sensor_type_prefix[known_features[i].type],
+                                               sensor_to_type[known_features[i].type],
                                                data->name);
 
                                if ((new_feature = (featurelist_t *) malloc (sizeof (featurelist_t))) == NULL)
                                {
-                                       DBG ("malloc: %s", strerror (errno));
-                                       syslog (LOG_ERR, MODULE_NAME":  malloc: %s",
-                                                       strerror (errno));
+                                       char errbuf[1024];
+                                       ERROR ("sensors plugin: malloc: %s",
+                                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                                        break;
                                }
 
@@ -376,171 +327,108 @@ static void sensors_load_conf (void)
        if (first_feature == NULL)
        {
                sensors_cleanup ();
-               syslog (LOG_INFO, MODULE_NAME": lm_sensors reports no features. "
-                       "Data will not be collected.");
+               INFO ("sensors plugin: lm_sensors reports no "
+                               "features. Data will not be collected.");
+               return (-1);
        }
-} /* void sensors_load_conf */
-#endif /* if SENSORS_HAVE_READ */
 
-static void collectd_sensors_init (void)
-{
-       return;
-}
+       return (0);
+} /* int sensors_load_conf */
 
-static void sensors_shutdown (void)
+static int sensors_shutdown (void)
 {
-#if SENSORS_HAVE_READ
        sensors_free_features ();
-#endif /* if SENSORS_HAVE_READ */
-
-       if (NULL != sensor_list)
-               ignorelist_free (sensor_list);
-}
-
-static void sensors_voltage_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE];
-       int status;
-
-       /* skip ignored in our config */
-       if ((NULL != sensor_list) && ignorelist_match (sensor_list, inst))
-               return;
-
-       /* extended sensor naming */
-       if(sensor_extended_naming)
-               status = snprintf (file, BUFSIZE, extended_filename_format, inst);
-       else
-               status = snprintf (file, BUFSIZE, old_filename_format, inst);
-
-       if ((status < 1) || (status >= BUFSIZE))
-               return;
-
-       rrd_update_file (host, file, val, sensor_voltage_ds_def, sensor_voltage_ds_num);
-}
-
-static void sensors_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE];
-       int status;
-
-       /* skip ignored in our config */
-       if ((NULL != sensor_list) && ignorelist_match (sensor_list, inst))
-               return;
-
-       /* extended sensor naming */
-       if (sensor_extended_naming)
-               status = snprintf (file, BUFSIZE, extended_filename_format, inst);
-       else
-               status = snprintf (file, BUFSIZE, old_filename_format, inst);
+       ignorelist_free (sensor_list);
 
-       if ((status < 1) || (status >= BUFSIZE))
-               return;
-
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
+       return (0);
+} /* int sensors_shutdown */
 
-#if SENSORS_HAVE_READ
-static void sensors_submit (const char *feat_name,
-               const char *chip_prefix, double value, int type)
+static void sensors_submit (const char *plugin_instance,
+               const char *type, const char *type_instance,
+               double val)
 {
-       char buf[BUFSIZE];
-       char inst[BUFSIZE];
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (inst, BUFSIZE, "%s-%s", chip_prefix, feat_name)
-                       >= BUFSIZE)
+       if ((sensor_list != NULL)
+                       && (ignorelist_match (sensor_list, type_instance)))
                return;
 
-       /* skip ignored in our config */
-       if ((NULL != sensor_list) && ignorelist_match (sensor_list, inst))
-               return;
+       values[0].gauge = val;
 
-       if (snprintf (buf, BUFSIZE, "%u:%.3f", (unsigned int) curtime,
-                               value) >= BUFSIZE)
-               return;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "sensors");
+       strcpy (vl.plugin_instance, plugin_instance);
+       strcpy (vl.type_instance, type_instance);
 
-       if (type == SENSOR_TYPE_VOLTAGE)
-       {
-               DBG ("%s: %s/%s, %s", MODULE_NAME_VOLTAGE,
-                               sensor_type_prefix[type], inst, buf);
-               plugin_submit (MODULE_NAME_VOLTAGE, inst, buf);
-       }
-       else
-       {
-               DBG ("%s: %s/%s, %s", MODULE_NAME,
-                               sensor_type_prefix[type], inst, buf);
-               plugin_submit (MODULE_NAME, inst, buf);
-       }
-}
+       plugin_dispatch_values (type, &vl);
+} /* void sensors_submit */
 
-static void sensors_read (void)
+static int sensors_read (void)
 {
        featurelist_t *feature;
        double value;
-       char chip_fullprefix[BUFSIZE];
 
-       sensors_load_conf ();
+       char plugin_instance[DATA_MAX_NAME_LEN];
+       char type_instance[DATA_MAX_NAME_LEN];
+
+       if (sensors_load_conf () != 0)
+               return (-1);
 
        for (feature = first_feature; feature != NULL; feature = feature->next)
        {
                if (sensors_get_feature (*feature->chip, feature->data->number, &value) < 0)
                        continue;
 
-               if (sensor_extended_naming)
+               /* full chip name logic borrowed from lm_sensors */
+               if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
                {
-                       /* full chip name logic borrowed from lm_sensors */
-                       if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
-                       {
-                               if (snprintf (chip_fullprefix, BUFSIZE, "%s-isa-%04x/%s",
-                                                       feature->chip->prefix,
-                                                       feature->chip->addr,
-                                                       sensor_type_prefix[feature->type])
-                                               >= BUFSIZE)
-                                       continue;
-                       }
-                       else if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_DUMMY)
-                       {
-                               if (snprintf (chip_fullprefix, BUFSIZE, "%s-%s-%04x/%s",
-                                                       feature->chip->prefix,
-                                                       feature->chip->busname,
-                                                       feature->chip->addr,
-                                                       sensor_type_prefix[feature->type])
-                                               >= BUFSIZE)
-                                       continue;
-                       }
-                       else
-                       {
-                               if (snprintf (chip_fullprefix, BUFSIZE, "%s-i2c-%d-%02x/%s",
-                                                       feature->chip->prefix,
-                                                       feature->chip->bus,
-                                                       feature->chip->addr,
-                                                       sensor_type_prefix[feature->type])
-                                               >= BUFSIZE)
-                                       continue;
-                       }
-
-                       sensors_submit (feature->data->name,
-                                       chip_fullprefix,
-                                       value, feature->type);
+                       if (snprintf (plugin_instance, DATA_MAX_NAME_LEN, "%s-isa-%04x",
+                                               feature->chip->prefix,
+                                               feature->chip->addr)
+                                       >= 512)
+                               continue;
+               }
+               else if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_DUMMY)
+               {
+                       if (snprintf (plugin_instance, 512, "%s-%s-%04x",
+                                               feature->chip->prefix,
+                                               feature->chip->busname,
+                                               feature->chip->addr)
+                                       >= 512)
+                               continue;
                }
                else
                {
-                       sensors_submit (feature->data->name,
-                                       feature->chip->prefix,
-                                       value, feature->type);
+                       if (snprintf (plugin_instance, 512, "%s-i2c-%d-%02x",
+                                               feature->chip->prefix,
+                                               feature->chip->bus,
+                                               feature->chip->addr)
+                                       >= 512)
+                               continue;
                }
+
+               strncpy (type_instance, feature->data->name, DATA_MAX_NAME_LEN);
+
+               sensors_submit (plugin_instance,
+                               sensor_to_type[feature->type],
+                               type_instance,
+                               value);
        } /* for feature = first_feature .. NULL */
-} /* void sensors_read */
-#else
-# define sensors_read NULL
+
+       return (0);
+} /* int sensors_read */
 #endif /* SENSORS_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, collectd_sensors_init, sensors_read, sensors_write);
-       plugin_register (MODULE_NAME_VOLTAGE, NULL, NULL, sensors_voltage_write);
-       plugin_register_shutdown (MODULE_NAME, sensors_shutdown);
-       cf_register (MODULE_NAME, sensors_config, config_keys, config_keys_num);
-}
-
-#undef BUFSIZE
-#undef MODULE_NAME
+#if SENSORS_HAVE_READ
+       plugin_register_config ("sensors", sensors_config,
+                       config_keys, config_keys_num);
+       plugin_register_read ("sensors", sensors_read);
+       plugin_register_shutdown ("sensors", sensors_shutdown);
+#endif
+} /* void module_register */
index 0c6050d..dd063bb 100644 (file)
 #include "common.h"
 #include "plugin.h"
 
-#define MODULE_NAME "serial"
-
 #if defined(KERNEL_LINUX)
 # define SERIAL_HAVE_READ 1
 #else
 # define SERIAL_HAVE_READ 0
 #endif
 
-static char *serial_filename_template = "serial-%s.rrd";
-
-static char *ds_def[] =
-{
-       "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int ds_num = 2;
-
-static void serial_init (void)
-{
-       return;
-}
-
-static void serial_write (char *host, char *inst, char *val)
+#if SERIAL_HAVE_READ
+static void serial_submit (const char *type_instance,
+               counter_t rx, counter_t tx)
 {
-       char file[512];
-       int status;
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       status = snprintf (file, 512, serial_filename_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
+       values[0].counter = rx;
+       values[1].counter = tx;
 
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
-
-#if SERIAL_HAVE_READ
-#define BUFSIZE 512
-static void serial_submit (char *device,
-               unsigned long long incoming,
-               unsigned long long outgoing)
-{
-       char buf[BUFSIZE];
-        
-       if (snprintf (buf, BUFSIZE, "%u:%llu:%llu", (unsigned int) curtime,
-                               incoming, outgoing) >= BUFSIZE)
-               return;
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "serial");
+       strncpy (vl.type_instance, type_instance,
+                       sizeof (vl.type_instance));
 
-       plugin_submit (MODULE_NAME, device, buf);
+       plugin_dispatch_values ("serial_octets", &vl);
 }
-#undef BUFSIZE
 
-static void serial_read (void)
+static int serial_read (void)
 {
 #ifdef KERNEL_LINUX
-
        FILE *fh;
        char buffer[1024];
-       unsigned long long incoming, outgoing;
+
+       counter_t rx = 0;
+       counter_t tx = 0;
        
        char *fields[16];
        int i, numfields;
@@ -94,21 +69,16 @@ static void serial_read (void)
        if ((fh = fopen ("/proc/tty/driver/serial", "r")) == NULL &&
                (fh = fopen ("/proc/tty/driver/ttyS", "r")) == NULL)
        {
-               syslog (LOG_WARNING, "serial: fopen: %s", strerror (errno));
-               return;
+               char errbuf[1024];
+               WARNING ("serial: fopen: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
-       while (fgets (buffer, 1024, fh) != NULL)
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
                int have_rx = 0, have_tx = 0;
 
-               /* stupid compiler:
-                * serial.c:87: warning: 'incoming' may be used uninitialized in this function
-                * serial.c:87: warning: 'outgoing' may be used uninitialized in this function
-                */
-               incoming = 0ULL;
-               outgoing = 0ULL;
-
                numfields = strsplit (buffer, fields, 16);
 
                if (numfields < 6)
@@ -133,12 +103,12 @@ static void serial_read (void)
 
                        if (strncmp (fields[i], "tx:", 3) == 0)
                        {
-                               outgoing = atoll (fields[i] + 3);
+                               tx = atoll (fields[i] + 3);
                                have_tx++;
                        }
                        else if (strncmp (fields[i], "rx:", 3) == 0)
                        {
-                               incoming = atoll (fields[i] + 3);
+                               rx = atoll (fields[i] + 3);
                                have_rx++;
                        }
                }
@@ -146,19 +116,18 @@ static void serial_read (void)
                if ((have_rx == 0) || (have_tx == 0))
                        continue;
 
-               serial_submit (fields[0], incoming, outgoing);
+               serial_submit (fields[0], rx, tx);
        }
 
        fclose (fh);
+       return (0);
 #endif /* KERNEL_LINUX */
-}
-#else
-# define serial_read NULL
+} /* int serial_read */
 #endif /* SERIAL_HAVE_READ */
 
 void module_register (void)
 {
-   plugin_register (MODULE_NAME, serial_init, serial_read, serial_write);
-}
-
-#undef MODULE_NAME
+#if SERIAL_HAVE_READ
+       plugin_register_read ("serial", serial_read);
+#endif /* SERIAL_HAVE_READ */
+} /* void module_register */
index 43275fc..3018cb9 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/swap.c
- * Copyright (C) 2005,2006  Florian octo Forster
+ * Copyright (C) 2005-2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -37,8 +36,6 @@
 #  include <kvm.h>
 #endif
 
-#define MODULE_NAME "swap"
-
 #if KERNEL_LINUX || HAVE_LIBKSTAT || defined(VM_SWAPUSAGE) || HAVE_LIBKVM || HAVE_LIBSTATGRAB
 # define SWAP_HAVE_READ 1
 #else
 #undef  MAX
 #define MAX(x,y) ((x) > (y) ? (x) : (y))
 
-static char *swap_file = "swap.rrd";
-
-/* 1099511627776 == 1TB ought to be enough for anyone ;) */
-static char *ds_def[] =
-{
-       "DS:used:GAUGE:"COLLECTD_HEARTBEAT":0:1099511627776",
-       "DS:free:GAUGE:"COLLECTD_HEARTBEAT":0:1099511627776",
-       "DS:cached:GAUGE:"COLLECTD_HEARTBEAT":0:1099511627776",
-       "DS:resv:GAUGE:"COLLECTD_HEARTBEAT":0:1099511627776",
-       NULL
-};
-static int ds_num = 4;
-
+#if SWAP_HAVE_READ
 #if KERNEL_LINUX
 /* No global variables */
 /* #endif KERNEL_LINUX */
@@ -83,7 +68,7 @@ int kvm_pagesize;
 /* No global variables */
 #endif /* HAVE_LIBSTATGRAB */
 
-static void swap_init (void)
+static int swap_init (void)
 {
 #if KERNEL_LINUX
        /* No init stuff */
@@ -116,8 +101,8 @@ static void swap_init (void)
                                        NULL)) /* errstr */
                        == NULL)
        {
-               syslog (LOG_ERR, "swap plugin: kvm_open failed.");
-               return;
+               ERROR ("swap plugin: kvm_open failed.");
+               return (-1);
        }
 /* #endif HAVE_LIBKVM */
 
@@ -125,30 +110,27 @@ static void swap_init (void)
        /* No init stuff */
 #endif /* HAVE_LIBSTATGRAB */
 
-       return;
+       return (0);
 }
 
-static void swap_write (char *host, char *inst, char *val)
+static void swap_submit (const char *type_instance, double value)
 {
-       rrd_update_file (host, swap_file, val, ds_def, ds_num);
-}
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-#if SWAP_HAVE_READ
-static void swap_submit (unsigned long long swap_used,
-               unsigned long long swap_free,
-               unsigned long long swap_cached,
-               unsigned long long swap_resv)
-{
-       char buffer[512];
+       values[0].gauge = value;
 
-       if (snprintf (buffer, 512, "%u:%llu:%llu:%llu:%llu", (unsigned int) curtime,
-                               swap_used, swap_free, swap_cached, swap_resv) >= 512)
-               return;
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "swap");
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-       plugin_submit (MODULE_NAME, "-", buffer);
-}
+       plugin_dispatch_values ("swap", &vl);
+} /* void swap_submit */
 
-static void swap_read (void)
+static int swap_read (void)
 {
 #if KERNEL_LINUX
        FILE *fh;
@@ -164,8 +146,10 @@ static void swap_read (void)
 
        if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
        {
-               syslog (LOG_WARNING, "memory: fopen: %s", strerror (errno));
-               return;
+               char errbuf[1024];
+               WARNING ("memory: fopen: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
        while (fgets (buffer, 1024, fh) != NULL)
@@ -190,14 +174,20 @@ static void swap_read (void)
        }
 
        if (fclose (fh))
-               syslog (LOG_WARNING, "memory: fclose: %s", strerror (errno));
+       {
+               char errbuf[1024];
+               WARNING ("memory: fclose: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
 
        if ((swap_total == 0LL) || ((swap_free + swap_cached) > swap_total))
-               return;
+               return (-1);
 
        swap_used = swap_total - (swap_free + swap_cached);
 
-       swap_submit (swap_used, swap_free, swap_cached, -1LL);
+       swap_submit ("used", swap_used);
+       swap_submit ("free", swap_free);
+       swap_submit ("cached", swap_cached);
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_LIBKSTAT
@@ -209,9 +199,10 @@ static void swap_read (void)
 
        if (swapctl (SC_AINFO, &ai) == -1)
        {
-               syslog (LOG_ERR, "swap plugin: swapctl failed: %s",
-                               strerror (errno));
-               return;
+               char errbuf[1024];
+               ERROR ("swap plugin: swapctl failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
        /*
@@ -242,7 +233,9 @@ static void swap_read (void)
        swap_avail  = ai.ani_max - ai.ani_resv;
        swap_avail *= pagesize;
 
-       swap_submit (swap_alloc, swap_avail, -1LL, swap_resv - swap_alloc);
+       swap_submit ("used", swap_alloc);
+       swap_submit ("free", swap_avail);
+       swap_submit ("reserved", swap_resv - swap_alloc);
 /* #endif HAVE_LIBKSTAT */
 
 #elif defined(VM_SWAPUSAGE)
@@ -258,10 +251,11 @@ static void swap_read (void)
        sw_usage_len = sizeof (struct xsw_usage);
 
        if (sysctl (mib, mib_len, &sw_usage, &sw_usage_len, NULL, 0) != 0)
-               return;
+               return (-1);
 
        /* The returned values are bytes. */
-       swap_submit (sw_usage.xsu_used, sw_usage.xsu_avail, -1LL, -1LL);
+       swap_submit ("used", sw_usage.xsu_used);
+       swap_submit ("free", sw_usage.xsu_avail);
 /* #endif VM_SWAPUSAGE */
 
 #elif HAVE_LIBKVM
@@ -273,12 +267,12 @@ static void swap_read (void)
        unsigned long long total;
 
        if (kvm_obj == NULL)
-               return;
+               return (-1);
 
        /* only one structure => only get the grand total, no details */
        status = kvm_getswapinfo (kvm_obj, &data_s, 1, 0);
        if (status == -1)
-               return;
+               return (-1);
 
        total = (unsigned long long) data_s.ksw_total;
        used  = (unsigned long long) data_s.ksw_used;
@@ -288,23 +282,30 @@ static void swap_read (void)
 
        free = total - used;
 
-       swap_submit (used, free, -1LL, -1LL);
+       swap_submit ("used", used);
+       swap_submit ("free", free);
 /* #endif HAVE_LIBKVM */
 
 #elif HAVE_LIBSTATGRAB
        sg_swap_stats *swap;
 
-       if ((swap = sg_get_swap_stats ()) != NULL)
-               swap_submit (swap->used, swap->free, -1LL, -1LL);
+       swap = sg_get_swap_stats ();
+
+       if (swap == NULL)
+               return (-1);
+
+       swap_submit ("used", swap->used);
+       swap_submit ("free", swap->free);
 #endif /* HAVE_LIBSTATGRAB */
-}
-#else
-# define swap_read NULL
+
+       return (0);
+} /* int swap_read */
 #endif /* SWAP_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, swap_init, swap_read, swap_write);
-}
-
-#undef MODULE_NAME
+#if SWAP_HAVE_READ
+       plugin_register_init ("swap", swap_init);
+       plugin_register_read ("swap", swap_read);
+#endif /* SWAP_HAVE_READ */
+} /* void module_register */
diff --git a/src/syslog.c b/src/syslog.c
new file mode 100644 (file)
index 0000000..08dadcb
--- /dev/null
@@ -0,0 +1,95 @@
+/**
+ * collectd - src/syslog.c
+ * Copyright (C) 2007  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; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if HAVE_SYSLOG_H
+# include <syslog.h>
+#endif
+
+static int log_level = LOG_DEBUG;
+
+static const char *config_keys[] =
+{
+       "LogLevel"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
+
+static int sl_config (const char *key, const char *value)
+{
+       if (strcasecmp (key, "LogLevel") == 0)
+       {
+               if ((strcasecmp (value, "emerg") == 0)
+                               || (strcasecmp (value, "alert") == 0)
+                               || (strcasecmp (value, "crit") == 0)
+                               || (strcasecmp (value, "err") == 0))
+                       log_level = LOG_ERR;
+               else if (strcasecmp (value, "warning") == 0)
+                       log_level = LOG_WARNING;
+               else if (strcasecmp (value, "notice") == 0)
+                       log_level = LOG_NOTICE;
+               else if (strcasecmp (value, "info") == 0)
+                       log_level = LOG_INFO;
+#if COLLECT_DEBUG
+               else if (strcasecmp (value, "debug") == 0)
+                       log_level = LOG_DEBUG;
+#endif
+               else
+                       return (1);
+       }
+       else
+               return (-1);
+
+       return (0);
+} /* int sl_config */
+
+static int sl_init (void)
+{
+       openlog ("collectd", LOG_CONS | LOG_PID, LOG_DAEMON);
+
+       return (0);
+}
+
+static void sl_log (int severity, const char *msg)
+{
+       if (severity > log_level)
+               return;
+
+       syslog (severity, "%s", msg);
+} /* void sl_log */
+
+static int sl_shutdown (void)
+{
+       closelog ();
+
+       return (0);
+}
+
+void module_register (void)
+{
+       plugin_register_config ("syslog", sl_config, config_keys, config_keys_num);
+       plugin_register_init ("syslog", sl_init);
+       plugin_register_log ("syslog", sl_log);
+       plugin_register_shutdown ("syslog", sl_shutdown);
+} /* void module_register(void) */
index b13cdbd..5e01f0d 100644 (file)
 #include "common.h"
 #include "plugin.h"
 
-#define MODULE_NAME "tape"
-
 #if defined(HAVE_LIBKSTAT)
 # define TAPE_HAVE_READ 1
 #else
 # define TAPE_HAVE_READ 0
 #endif
 
-static char *tape_filename_template = "tape-%s.rrd";
-
-/* 104857600 == 100 MB */
-static char *tape_ds_def[] =
-{
-       "DS:rcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rmerged:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rbytes:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:rtime:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wcount:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wmerged:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wbytes:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:wtime:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int tape_ds_num = 8;
-
+#if TAPE_HAVE_READ
 #if defined(HAVE_LIBKSTAT)
 #define MAX_NUMTAPE 256
 extern kstat_ctl_t *kc;
@@ -56,7 +38,7 @@ static kstat_t *ksp[MAX_NUMTAPE];
 static int numtape = 0;
 #endif /* HAVE_LIBKSTAT */
 
-static void tape_init (void)
+static int tape_init (void)
 {
 #ifdef HAVE_LIBKSTAT
        kstat_t *ksp_chain;
@@ -64,7 +46,7 @@ static void tape_init (void)
        numtape = 0;
 
        if (kc == NULL)
-               return;
+               return (-1);
 
        for (numtape = 0, ksp_chain = kc->kc_chain;
                        (numtape < MAX_NUMTAPE) && (ksp_chain != NULL);
@@ -78,60 +60,59 @@ static void tape_init (void)
        }
 #endif
 
-       return;
-}
-
-static void tape_write (char *host, char *inst, char *val)
-{
-       char file[512];
-       int status;
-
-       status = snprintf (file, 512, tape_filename_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, tape_ds_def, tape_ds_num);
+       return (0);
 }
 
-
-#if TAPE_HAVE_READ
-#define BUFSIZE 512
-static void tape_submit (char *tape_name,
-               unsigned long long read_count,
-               unsigned long long read_merged,
-               unsigned long long read_bytes,
-               unsigned long long read_time,
-               unsigned long long write_count,
-               unsigned long long write_merged,
-               unsigned long long write_bytes,
-               unsigned long long write_time)
-
+static void tape_submit (const char *plugin_instance,
+               const char *type,
+               counter_t read, counter_t write)
 {
-       char buf[BUFSIZE];
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf (buf, BUFSIZE, "%u:%llu:%llu:%llu:%llu:%llu:%llu:%llu:%llu",
-                               (unsigned int) curtime,
-                               read_count, read_merged, read_bytes, read_time,
-                               write_count, write_merged, write_bytes,
-                               write_time) >= BUFSIZE)
-               return;
+       values[0].counter = read;
+       values[1].counter = write;
 
-       plugin_submit (MODULE_NAME, tape_name, buf);
-}
+       vl.values = values;
+       vl.values_len = 2;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "tape");
+       strncpy (vl.plugin_instance, plugin_instance,
+                       sizeof (vl.plugin_instance));
 
-#undef BUFSIZE
+       plugin_dispatch_values (type, &vl);
+} /* void tape_submit */
 
-static void tape_read (void)
+static int tape_read (void)
 {
 
 #if defined(HAVE_LIBKSTAT)
+# if HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_NWRITES && HAVE_KSTAT_IO_T_WTIME
+#  define KIO_ROCTETS reads
+#  define KIO_WOCTETS writes
+#  define KIO_ROPS    nreads
+#  define KIO_WOPS    nwrites
+#  define KIO_RTIME   rtime
+#  define KIO_WTIME   wtime
+# elif HAVE_KSTAT_IO_T_NWRITTEN && HAVE_KSTAT_IO_T_WRITES && HAVE_KSTAT_IO_T_WTIME
+#  define KIO_ROCTETS nread
+#  define KIO_WOCTETS nwritten
+#  define KIO_ROPS    reads
+#  define KIO_WOPS    writes
+#  define KIO_RTIME   rtime
+#  define KIO_WTIME   wtime
+# else
+#  error "kstat_io_t does not have the required members"
+# endif
        static kstat_io_t kio;
        int i;
 
        if (kc == NULL)
-               return;
+               return (-1);
+
+       if (numtape <= 0)
+               return (-1);
 
        for (i = 0; i < numtape; i++)
        {
@@ -139,19 +120,26 @@ static void tape_read (void)
                        continue;
 
                if (strncmp (ksp[i]->ks_class, "tape", 4) == 0)
-                       tape_submit (ksp[i]->ks_name,
-                                       kio.reads,  0LL, kio.nread,    kio.rtime,
-                                       kio.writes, 0LL, kio.nwritten, kio.wtime);
+               {
+                       tape_submit (ksp[i]->ks_name, "tape_octets",
+                                       kio.KIO_ROCTETS, kio.KIO_WOCTETS);
+                       tape_submit (ksp[i]->ks_name, "tape_ops",
+                                       kio.KIO_ROPS, kio.KIO_WOPS);
+                       /* FIXME: Convert this to microseconds if necessary */
+                       tape_submit (ksp[i]->ks_name, "tape_time",
+                                       kio.KIO_RTIME, kio.KIO_WTIME);
+               }
        }
 #endif /* defined(HAVE_LIBKSTAT) */
+
+       return (0);
 }
-#else
-# define tape_read NULL
 #endif /* TAPE_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, tape_init, tape_read, tape_write);
+#if TAPE_HAVE_READ
+       plugin_register_init ("tape", tape_init);
+       plugin_register_read ("tape", tape_read);
+#endif /* TAPE_HAVE_READ */
 }
-
-#undef MODULE_NAME
diff --git a/src/traffic.c b/src/traffic.c
deleted file mode 100644 (file)
index 14b7dc3..0000000
+++ /dev/null
@@ -1,478 +0,0 @@
-/**
- * collectd - src/traffic.c
- * Copyright (C) 2005,2006  Florian octo Forster
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * 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>
- *   Sune Marcher <sm at flork.dk>
- **/
-
-#include "collectd.h"
-#include "common.h"
-#include "plugin.h"
-#include "configfile.h"
-
-#if HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-#endif
-#if HAVE_SYS_SOCKET_H
-#  include <sys/socket.h>
-#endif
-
-/* One cannot include both. This sucks. */
-#if HAVE_LINUX_IF_H
-#  include <linux/if.h>
-#elif HAVE_NET_IF_H
-#  include <net/if.h>
-#endif
-
-#if HAVE_LINUX_NETDEVICE_H
-#  include <linux/netdevice.h>
-#endif
-#if HAVE_IFADDRS_H
-#  include <ifaddrs.h>
-#endif
-
-#define MODULE_NAME "traffic"
-
-/*
- * Various people have reported problems with `getifaddrs' and varying versions
- * of `glibc'. That's why it's disabled by default. Since more statistics are
- * available this way one may enable it using the `--enable-getifaddrs' option
- * of the configure script. -octo
- */
-#if KERNEL_LINUX
-# if !COLLECT_GETIFADDRS
-#  undef HAVE_GETIFADDRS
-# endif /* !COLLECT_GETIFADDRS */
-#endif /* KERNEL_LINUX */
-
-#if HAVE_GETIFADDRS || KERNEL_LINUX || HAVE_LIBKSTAT || HAVE_LIBSTATGRAB
-# define TRAFFIC_HAVE_READ 1
-#else
-# define TRAFFIC_HAVE_READ 0
-#endif
-
-#define BUFSIZE 512
-
-/*
- * (Module-)Global variables
- */
-/* TODO: Move this to `interface-%s/<blah>.rrd' in version 4. */
-static char *bytes_file   = "traffic-%s.rrd";
-static char *packets_file = "interface-%s/if_packets.rrd";
-static char *errors_file  = "interface-%s/if_errors.rrd";
-/* TODO: Maybe implement multicast and broadcast counters */
-
-static char *config_keys[] =
-{
-       "Interface",
-       "IgnoreSelected",
-       NULL
-};
-static int config_keys_num = 2;
-
-static char *bytes_ds_def[] =
-{
-       "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int bytes_ds_num = 2;
-
-static char *packets_ds_def[] =
-{
-       "DS:rx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:tx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int packets_ds_num = 2;
-
-static char *errors_ds_def[] =
-{
-       "DS:rx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       "DS:tx:COUNTER:"COLLECTD_HEARTBEAT":0:U",
-       NULL
-};
-static int errors_ds_num = 2;
-
-static char **if_list = NULL;
-static int    if_list_num = 0;
-/* 
- * if_list_action:
- * 0 => default is to collect selected interface
- * 1 => ignore selcted interfaces
- */
-static int    if_list_action = 0;
-
-#ifdef HAVE_LIBKSTAT
-#define MAX_NUMIF 256
-extern kstat_ctl_t *kc;
-static kstat_t *ksp[MAX_NUMIF];
-static int numif = 0;
-#endif /* HAVE_LIBKSTAT */
-
-static int traffic_config (char *key, char *value)
-{
-       char **temp;
-
-       if (strcasecmp (key, "Interface") == 0)
-       {
-               temp = (char **) realloc (if_list, (if_list_num + 1) * sizeof (char *));
-               if (temp == NULL)
-               {
-                       syslog (LOG_EMERG, "Cannot allocate more memory.");
-                       return (1);
-               }
-               if_list = temp;
-
-               if ((if_list[if_list_num] = strdup (value)) == NULL)
-               {
-                       syslog (LOG_EMERG, "Cannot allocate memory.");
-                       return (1);
-               }
-               if_list_num++;
-       }
-       else if (strcasecmp (key, "IgnoreSelected") == 0)
-       {
-               if ((strcasecmp (value, "True") == 0)
-                               || (strcasecmp (value, "Yes") == 0)
-                               || (strcasecmp (value, "On") == 0))
-                       if_list_action = 1;
-               else
-                       if_list_action = 0;
-       }
-       else
-       {
-               return (-1);
-       }
-
-       return (0);
-}
-
-static void traffic_init (void)
-{
-#if HAVE_GETIFADDRS
-       /* nothing */
-/* #endif HAVE_GETIFADDRS */
-
-#elif KERNEL_LINUX
-       /* nothing */
-/* #endif KERNEL_LINUX */
-
-#elif HAVE_LIBKSTAT
-       kstat_t *ksp_chain;
-       unsigned long long val;
-
-       numif = 0;
-
-       if (kc == NULL)
-               return;
-
-       for (numif = 0, ksp_chain = kc->kc_chain;
-                       (numif < MAX_NUMIF) && (ksp_chain != NULL);
-                       ksp_chain = ksp_chain->ks_next)
-       {
-               if (strncmp (ksp_chain->ks_class, "net", 3))
-                       continue;
-               if (ksp_chain->ks_type != KSTAT_TYPE_NAMED)
-                       continue;
-               if (kstat_read (kc, ksp_chain, NULL) == -1)
-                       continue;
-               if ((val = get_kstat_value (ksp_chain, "obytes")) == -1LL)
-                       continue;
-               ksp[numif++] = ksp_chain;
-       }
-/* #endif HAVE_LIBKSTAT */
-
-#elif HAVE_LIBSTATG
-       /* nothing */
-#endif /* HAVE_LIBSTATG */
-
-       return;
-}
-
-/*
- * Check if this interface/instance should be ignored. This is called from
- * both, `submit' and `write' to give client and server the ability to
- * ignore certain stuff..
- */
-static int check_ignore_if (const char *interface)
-{
-       int i;
-
-       /* If no interfaces are given collect all interfaces. Mostly to be
-        * backwards compatible, but also because this is much easier. */
-       if (if_list_num < 1)
-               return (0);
-
-       for (i = 0; i < if_list_num; i++)
-               if (strcasecmp (interface, if_list[i]) == 0)
-                       return (if_list_action);
-       return (1 - if_list_action);
-}
-
-static void generic_write (char *host, char *inst, char *val,
-               char *file_template,
-               char **ds_def, int ds_num)
-{
-       char file[512];
-       int status;
-
-       if (check_ignore_if (inst))
-               return;
-
-       status = snprintf (file, BUFSIZE, file_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= 512)
-               return;
-
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
-
-static void bytes_write (char *host, char *inst, char *val)
-{
-       generic_write (host, inst, val, bytes_file, bytes_ds_def, bytes_ds_num);
-}
-
-static void packets_write (char *host, char *inst, char *val)
-{
-       generic_write (host, inst, val, packets_file, packets_ds_def, packets_ds_num);
-}
-
-static void errors_write (char *host, char *inst, char *val)
-{
-       generic_write (host, inst, val, errors_file, errors_ds_def, errors_ds_num);
-}
-
-#if TRAFFIC_HAVE_READ
-static void bytes_submit (char *dev,
-               unsigned long long rx,
-               unsigned long long tx)
-{
-       char buf[512];
-       int  status;
-
-       if (check_ignore_if (dev))
-               return;
-
-       status = snprintf (buf, 512, "%u:%lld:%lld",
-                               (unsigned int) curtime,
-                               rx, tx);
-       if ((status >= 512) || (status < 1))
-               return;
-
-       plugin_submit (MODULE_NAME, dev, buf);
-}
-
-#if HAVE_GETIFADDRS || KERNEL_LINUX || HAVE_LIBKSTAT
-static void packets_submit (char *dev,
-               unsigned long long rx,
-               unsigned long long tx)
-{
-       char buf[512];
-       int  status;
-
-       if (check_ignore_if (dev))
-               return;
-
-       status = snprintf (buf, 512, "%u:%lld:%lld",
-                       (unsigned int) curtime,
-                       rx, tx);
-       if ((status >= 512) || (status < 1))
-               return;
-       plugin_submit ("if_packets", dev, buf);
-}
-
-static void errors_submit (char *dev,
-               unsigned long long rx,
-               unsigned long long tx)
-{
-       char buf[512];
-       int  status;
-
-       if (check_ignore_if (dev))
-               return;
-
-       status = snprintf (buf, 512, "%u:%lld:%lld",
-                       (unsigned int) curtime,
-                       rx, tx);
-       if ((status >= 512) || (status < 1))
-               return;
-       plugin_submit ("if_errors", dev, buf);
-}
-#endif /* HAVE_GETIFADDRS || KERNEL_LINUX || HAVE_LIBKSTAT */
-
-static void traffic_read (void)
-{
-#if HAVE_GETIFADDRS
-       struct ifaddrs *if_list;
-       struct ifaddrs *if_ptr;
-
-/* Darin/Mac OS X and possible other *BSDs */
-#if HAVE_STRUCT_IF_DATA
-#  define IFA_DATA if_data
-#  define IFA_RX_BYTES ifi_ibytes
-#  define IFA_TX_BYTES ifi_obytes
-#  define IFA_RX_PACKT ifi_ipackets
-#  define IFA_TX_PACKT ifi_opackets
-#  define IFA_RX_ERROR ifi_ierrors
-#  define IFA_TX_ERROR ifi_oerrors
-/* #endif HAVE_STRUCT_IF_DATA */
-
-#elif HAVE_STRUCT_NET_DEVICE_STATS
-#  define IFA_DATA net_device_stats
-#  define IFA_RX_BYTES rx_bytes
-#  define IFA_TX_BYTES tx_bytes
-#  define IFA_RX_PACKT rx_packets
-#  define IFA_TX_PACKT tx_packets
-#  define IFA_RX_ERROR rx_errors
-#  define IFA_TX_ERROR tx_errors
-#else
-#  error "No suitable type for `struct ifaddrs->ifa_data' found."
-#endif
-
-       struct IFA_DATA *if_data;
-
-       if (getifaddrs (&if_list) != 0)
-               return;
-
-       for (if_ptr = if_list; if_ptr != NULL; if_ptr = if_ptr->ifa_next)
-       {
-               if ((if_data = (struct IFA_DATA *) if_ptr->ifa_data) == NULL)
-                       continue;
-
-               bytes_submit (if_ptr->ifa_name,
-                               if_data->IFA_RX_BYTES,
-                               if_data->IFA_TX_BYTES);
-               packets_submit (if_ptr->ifa_name,
-                               if_data->IFA_RX_PACKT,
-                               if_data->IFA_TX_PACKT);
-               errors_submit (if_ptr->ifa_name,
-                               if_data->IFA_RX_ERROR,
-                               if_data->IFA_TX_ERROR);
-       }
-
-       freeifaddrs (if_list);
-/* #endif HAVE_GETIFADDRS */
-
-#elif KERNEL_LINUX
-       FILE *fh;
-       char buffer[1024];
-       unsigned long long incoming, outgoing;
-       char *device;
-       
-       char *dummy;
-       char *fields[16];
-       int numfields;
-
-       if ((fh = fopen ("/proc/net/dev", "r")) == NULL)
-       {
-               syslog (LOG_WARNING, "traffic: fopen: %s", strerror (errno));
-               return;
-       }
-
-       while (fgets (buffer, 1024, fh) != NULL)
-       {
-               if (!(dummy = strchr(buffer, ':')))
-                       continue;
-               dummy[0] = '\0';
-               dummy++;
-
-               device = buffer;
-               while (device[0] == ' ')
-                       device++;
-
-               if (device[0] == '\0')
-                       continue;
-               
-               numfields = strsplit (dummy, fields, 16);
-
-               if (numfields < 11)
-                       continue;
-
-               incoming = atoll (fields[0]);
-               outgoing = atoll (fields[8]);
-               bytes_submit (device, incoming, outgoing);
-
-               incoming = atoll (fields[1]);
-               outgoing = atoll (fields[9]);
-               packets_submit (device, incoming, outgoing);
-
-               incoming = atoll (fields[2]);
-               outgoing = atoll (fields[10]);
-               errors_submit (device, incoming, outgoing);
-       }
-
-       fclose (fh);
-/* #endif KERNEL_LINUX */
-
-#elif HAVE_LIBKSTAT
-       int i;
-       unsigned long long rx;
-       unsigned long long tx;
-
-       if (kc == NULL)
-               return;
-
-       for (i = 0; i < numif; i++)
-       {
-               if (kstat_read (kc, ksp[i], NULL) == -1)
-                       continue;
-
-               rx = get_kstat_value (ksp[i], "rbytes");
-               tx = get_kstat_value (ksp[i], "obytes");
-               if ((rx != -1LL) || (tx != -1LL))
-                       bytes_submit (ksp[i]->ks_name, rx, tx);
-
-               rx = get_kstat_value (ksp[i], "ipackets");
-               tx = get_kstat_value (ksp[i], "opackets");
-               if ((rx != -1LL) || (tx != -1LL))
-                       packets_submit (ksp[i]->ks_name, rx, tx);
-
-               rx = get_kstat_value (ksp[i], "ierrors");
-               tx = get_kstat_value (ksp[i], "oerrors");
-               if ((rx != -1LL) || (tx != -1LL))
-                       errors_submit (ksp[i]->ks_name, rx, tx);
-       }
-/* #endif HAVE_LIBKSTAT */
-
-#elif defined(HAVE_LIBSTATGRAB)
-       sg_network_io_stats *ios;
-       int i, num;
-
-       ios = sg_get_network_io_stats (&num);
-
-       for (i = 0; i < num; i++)
-               bytes_submit (ios[i].interface_name, ios[i].rx, ios[i].tx);
-#endif /* HAVE_LIBSTATGRAB */
-}
-#else
-#define traffic_read NULL
-#endif /* TRAFFIC_HAVE_READ */
-
-void module_register (void)
-{
-       plugin_register (MODULE_NAME, traffic_init, traffic_read, bytes_write);
-       plugin_register ("if_packets", NULL, NULL, packets_write);
-       plugin_register ("if_errors",  NULL, NULL, errors_write);
-       cf_register (MODULE_NAME, traffic_config, config_keys, config_keys_num);
-}
-
-#undef BUFSIZE
-#undef MODULE_NAME
diff --git a/src/types.db b/src/types.db
new file mode 100644 (file)
index 0000000..68b966b
--- /dev/null
@@ -0,0 +1,66 @@
+apache_bytes           count:COUNTER:0:134217728
+apache_connections     count:GAUGE:0:65535
+apache_requests                count:COUNTER:0:134217728
+apache_scoreboard      count:GAUGE:0:65535
+charge                 value:GAUGE:0:U
+counter                        value:COUNTER:U:U
+cpu                    value:COUNTER:0:4294967295
+cpufreq                        value:GAUGE:0:U
+current                        value:GAUGE:U:U
+delay                  seconds:GAUGE:-1000000:1000000
+df                     used:GAUGE:0:1125899906842623, free:GAUGE:0:1125899906842623
+disk_merged            read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
+disk_octets            read:COUNTER:0:17179869183, write:COUNTER:0:17179869183
+disk_ops               read:COUNTER:0:4294967295, write:COUNTER:0:4294967295
+disk_time              read:COUNTER:0:1000000, write:COUNTER:0:1000000
+dns_octets             queries:COUNTER:0:125000000, responses:COUNTER:0:125000000
+dns_opcode             value:COUNTER:0:65535
+dns_qtype              value:COUNTER:0:65535
+dns_rcode              value:COUNTER:0:65535
+email_check            value:GAUGE:0:U
+email_count            value:GAUGE:0:U
+email_size             value:GAUGE:0:U
+entropy                        entropy:GAUGE:0:4294967295
+fanspeed               value:GAUGE:0:U
+frequency              frequency:GAUGE:0:U
+frequency_offset       ppm:GAUGE:-1000000:1000000
+gauge                  value:GAUGE:U:U
+humitidy               value:GAUGE:0:100
+if_errors              rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
+if_octets              rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
+if_packets             rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
+ipt_bytes              value:COUNTER:0:134217728
+ipt_packets            value:COUNTER:0:134217728
+irq                    value:COUNTER:U:65535
+load                   shortterm:GAUGE:0:100, midterm:GAUGE:0:100, longterm:GAUGE:0:100
+memory                 value:GAUGE:0:281474976710656
+multimeter             value:GAUGE:U:U
+mysql_commands         value:COUNTER:0:U
+mysql_handler          value:COUNTER:0:U
+mysql_octets           rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
+mysql_qcache           hits:COUNTER:0:U, inserts:COUNTER:0:U, not_cached:COUNTER:0:U, lowmem_prunes:COUNTER:0:U, queries_in_cache:GAUGE:0:U
+mysql_threads          running:GAUGE:0:U, connected:GAUGE:0:U, cached:GAUGE:0:U, created:COUNTER:0:U
+nfs_procedure          value:COUNTER:0:4294967295
+percent                        percent:GAUGE:0:100.1
+ping                   ping:GAUGE:0:65535
+power                  value:GAUGE:0:U
+ps_count               processes:GAUGE:0:1000000, threads:GAUGE:0:1000000
+ps_cputime             user:COUNTER:0:1000000, syst:COUNTER:0:1000000
+ps_pagefaults          minflt:COUNTER:0:9223372036854775807, majflt:COUNTER:0:9223372036854775807
+ps_rss                 value:GAUGE:0:9223372036854775807
+ps_state               value:GAUGE:0:65535
+spam_score             value:GAUGE:U:U
+temperature            value:GAUGE:-273.15:U
+time_dispersion                seconds:GAUGE:-1000000:1000000
+time_offset            seconds:GAUGE:-1000000:1000000
+timeleft               timeleft:GAUGE:0:3600
+voltage                        value:GAUGE:U:U
+serial_octets          rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
+swap                   value:GAUGE:0:1099511627776
+users                  users:GAUGE:0:65535
+vs_threads             value:GAUGE:0:65535
+vs_processes           value:GAUGE:0:65535
+vs_memory              value:GAUGE:0:9223372036854775807
+signal_quality         value:GAUGE:0:U
+signal_power           value:GAUGE:U:0
+signal_noise           value:GAUGE:U:0
diff --git a/src/types_list.c b/src/types_list.c
new file mode 100644 (file)
index 0000000..002761c
--- /dev/null
@@ -0,0 +1,205 @@
+/**
+ * collectd - src/types_list.c
+ * Copyright (C) 2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+
+#include "plugin.h"
+#include "configfile.h"
+
+static int parse_ds (data_source_t *dsrc, char *buf, size_t buf_len)
+{
+  char *dummy;
+  char *saveptr;
+  char *fields[8];
+  int   fields_num;
+
+  if (buf_len < 11)
+  {
+    ERROR ("parse_ds: (buf_len = %u) < 11", buf_len);
+    return (-1);
+  }
+
+  if (buf[buf_len - 1] == ',')
+  {
+    buf_len--;
+    buf[buf_len] = '\0';
+  }
+
+  dummy = buf;
+  saveptr = NULL;
+
+  fields_num = 0;
+  while (fields_num < 8)
+  {
+    if ((fields[fields_num] = strtok_r (dummy, ":", &saveptr)) == NULL)
+      break;
+    dummy = NULL;
+    fields_num++;
+  }
+
+  if (fields_num != 4)
+  {
+    ERROR ("parse_ds: (fields_num = %i) != 4", fields_num);
+    return (-1);
+  }
+
+  strncpy (dsrc->name, fields[0], sizeof (dsrc->name));
+  dsrc->name[sizeof (dsrc->name) - 1] = '\0';
+
+  if (strcasecmp (fields[1], "GAUGE") == 0)
+    dsrc->type = DS_TYPE_GAUGE;
+  else if (strcasecmp (fields[1], "COUNTER") == 0)
+    dsrc->type = DS_TYPE_COUNTER;
+  else
+  {
+    ERROR ("(fields[1] = %s) != (GAUGE || COUNTER)", fields[1]);
+    return (-1);
+  }
+
+  if (strcasecmp (fields[2], "U") == 0)
+    dsrc->min = NAN;
+  else
+    dsrc->min = atof (fields[2]);
+
+  if (strcasecmp (fields[3], "U") == 0)
+    dsrc->max = NAN;
+  else
+    dsrc->max = atof (fields[3]);
+
+  DEBUG ("parse_ds: dsrc = {%s, %i, %lf, %lf};",
+      dsrc->name, dsrc->type, dsrc->min, dsrc->max);
+
+  return (0);
+} /* int parse_ds */
+
+static void parse_line (char *buf, size_t buf_len)
+{
+  char  *fields[64];
+  size_t fields_num;
+  data_set_t *ds;
+  int i;
+
+  fields_num = strsplit (buf, fields, 64);
+  if (fields_num < 2)
+    return;
+
+  ds = (data_set_t *) malloc (sizeof (data_set_t));
+  if (ds == NULL)
+    return;
+
+  memset (ds, '\0', sizeof (data_set_t));
+
+  strncpy (ds->type, fields[0], sizeof (ds->type));
+  ds->type[sizeof (ds->type) - 1] = '\0';
+
+  ds->ds_num = fields_num - 1;
+  ds->ds = (data_source_t *) calloc (ds->ds_num, sizeof (data_source_t));
+  if (ds->ds == NULL)
+    return;
+
+  for (i = 0; i < ds->ds_num; i++)
+    if (parse_ds (ds->ds + i, fields[i + 1], strlen (fields[i + 1])) != 0)
+    {
+      sfree (ds->ds);
+      ERROR ("types_list: parse_line: Cannot parse data source #%i "
+         "of data set %s", i, ds->type);
+      return;
+    }
+
+  DEBUG ("parse_line: ds = {%s, %i, %p};",
+      ds->type, ds->ds_num, (void *) ds->ds);
+
+  plugin_register_data_set (ds);
+
+  sfree (ds->ds);
+  sfree (ds);
+} /* void parse_line */
+
+static void parse_file (FILE *fh)
+{
+  char buf[4096];
+  size_t buf_len;
+
+  while (fgets (buf, sizeof (buf), fh) != NULL)
+  {
+    buf_len = strlen (buf);
+
+    if (buf_len >= 4095)
+    {
+      NOTICE ("Skipping line with more than 4095 characters.");
+      do
+      {
+       if (fgets (buf, sizeof (buf), fh) == NULL)
+         break;
+       buf_len = strlen (buf);
+      } while (buf_len >= 4095);
+      continue;
+    } /* if (buf_len >= 4095) */
+
+    if ((buf_len == 0) || (buf[0] == '#'))
+      continue;
+
+    while ((buf_len > 0) && ((buf[buf_len - 1] == '\n')
+         || (buf[buf_len - 1] == '\n')))
+      buf[--buf_len] = '\0';
+
+    if (buf_len == 0)
+      continue;
+
+    parse_line (buf, buf_len);
+  } /* while (fgets) */
+} /* void parse_file */
+
+int read_types_list (void)
+{
+  const char *file;
+  FILE *fh;
+
+  file = global_option_get ("TypesDB");
+  if (file == NULL)
+  {
+    ERROR ("global_option_get (\"TypesDB\") returned NULL.");
+    return (-1);
+  }
+
+  fh = fopen (file, "r");
+  if (fh == NULL)
+  {
+    char errbuf[1024];
+    ERROR ("open (%s) failed: %s", 
+       file, sstrerror (errno, errbuf, sizeof (errbuf)));
+    return (-1);
+  }
+
+  parse_file (fh);
+
+  fclose (fh);
+  fh = NULL;
+
+  DEBUG ("Done parsing `%s'", file);
+
+  return (0);
+} /* int read_types_list */
+
+/*
+ * vim: shiftwidth=2:softtabstop=2:tabstop=8
+ */
diff --git a/src/types_list.h b/src/types_list.h
new file mode 100644 (file)
index 0000000..c7e6aa0
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef TYPES_LIST_H
+#define TYPES_LIST_H 1
+
+/**
+ * collectd - src/types_list.h
+ * Copyright (C) 2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+int read_types_list (void);
+
+#endif /* TYPES_LIST_H */
diff --git a/src/unixsock.c b/src/unixsock.c
new file mode 100644 (file)
index 0000000..9793659
--- /dev/null
@@ -0,0 +1,897 @@
+/**
+ * collectd - src/unixsock.c
+ * Copyright (C) 2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Author:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+/* Folks without pthread will need to disable this plugin. */
+#include <pthread.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <grp.h>
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
+#endif
+
+#define US_DEFAULT_PATH PREFIX"/var/run/"PACKAGE_NAME"-unixsock"
+
+/*
+ * Private data structures
+ */
+/* linked list of cached values */
+typedef struct value_cache_s
+{
+       char       name[4*DATA_MAX_NAME_LEN];
+       int        values_num;
+       gauge_t   *gauge;
+       counter_t *counter;
+       const data_set_t *ds;
+       time_t     time;
+       struct value_cache_s *next;
+} value_cache_t;
+
+/*
+ * Private variables
+ */
+/* valid configuration file keys */
+static const char *config_keys[] =
+{
+       "SocketFile",
+       "SocketGroup",
+       "SocketPerms",
+       NULL
+};
+static int config_keys_num = 3;
+
+static int loop = 0;
+
+/* socket configuration */
+static int   sock_fd    = -1;
+static char *sock_file  = NULL;
+static char *sock_group = NULL;
+static int   sock_perms = S_IRWXU | S_IRWXG;
+
+static pthread_t listen_thread = (pthread_t) 0;
+
+/* Linked list and auxilliary variables for saving values */
+static value_cache_t   *cache_head = NULL;
+static pthread_mutex_t  cache_lock = PTHREAD_MUTEX_INITIALIZER;
+static unsigned int     cache_oldest = UINT_MAX;
+
+/*
+ * Functions
+ */
+static int parse_identifier (char *str, char **ret_host,
+               char **ret_plugin, char **ret_plugin_instance,
+               char **ret_type, char **ret_type_instance)
+{
+       char *hostname = NULL;
+       char *plugin = NULL;
+       char *plugin_instance = NULL;
+       char *type = NULL;
+       char *type_instance = NULL;
+
+       hostname = str;
+       if (hostname == NULL)
+               return (-1);
+
+       plugin = strchr (hostname, '/');
+       if (plugin == NULL)
+               return (-1);
+       *plugin = '\0'; plugin++;
+
+       type = strchr (plugin, '/');
+       if (type == NULL)
+               return (-1);
+       *type = '\0'; type++;
+
+       plugin_instance = strchr (plugin, '-');
+       if (plugin_instance != NULL)
+       {
+               *plugin_instance = '\0';
+               plugin_instance++;
+       }
+
+       type_instance = strchr (type, '-');
+       if (type_instance != NULL)
+       {
+               *type_instance = '\0';
+               type_instance++;
+       }
+
+       *ret_host = hostname;
+       *ret_plugin = plugin;
+       *ret_plugin_instance = plugin_instance;
+       *ret_type = type;
+       *ret_type_instance = type_instance;
+       return (0);
+} /* int parse_identifier */
+
+static value_cache_t *cache_search (const char *name)
+{
+       value_cache_t *vc;
+
+       for (vc = cache_head; vc != NULL; vc = vc->next)
+       {
+               if (strcmp (vc->name, name) == 0)
+                       break;
+       } /* for vc = cache_head .. NULL */
+
+       return (vc);
+} /* value_cache_t *cache_search */
+
+static int cache_insert (const data_set_t *ds, const value_list_t *vl)
+{
+       /* We're called from `cache_update' so we don't need to lock the mutex */
+       value_cache_t *vc;
+       int i;
+
+       DEBUG ("unixsock plugin: cache_insert: ds->type = %s; ds->ds_num = %i;"
+                       " vl->values_len = %i;",
+                       ds->type, ds->ds_num, vl->values_len);
+#if COLLECT_DEBUG
+       assert (ds->ds_num == vl->values_len);
+#else
+       if (ds->ds_num != vl->values_len)
+       {
+               ERROR ("unixsock plugin: ds->type = %s: (ds->ds_num = %i) != "
+                               "(vl->values_len = %i)",
+                               ds->type, ds->ds_num, vl->values_len);
+               return (-1);
+       }
+#endif
+
+       vc = (value_cache_t *) malloc (sizeof (value_cache_t));
+       if (vc == NULL)
+       {
+               char errbuf[1024];
+               pthread_mutex_unlock (&cache_lock);
+               ERROR ("unixsock plugin: malloc failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       vc->gauge = (gauge_t *) malloc (sizeof (gauge_t) * vl->values_len);
+       if (vc->gauge == NULL)
+       {
+               char errbuf[1024];
+               pthread_mutex_unlock (&cache_lock);
+               ERROR ("unixsock plugin: malloc failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               free (vc);
+               return (-1);
+       }
+
+       vc->counter = (counter_t *) malloc (sizeof (counter_t) * vl->values_len);
+       if (vc->counter == NULL)
+       {
+               char errbuf[1024];
+               pthread_mutex_unlock (&cache_lock);
+               ERROR ("unixsock plugin: malloc failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               free (vc->gauge);
+               free (vc);
+               return (-1);
+       }
+
+       if (FORMAT_VL (vc->name, sizeof (vc->name), vl, ds))
+       {
+               pthread_mutex_unlock (&cache_lock);
+               ERROR ("unixsock plugin: FORMAT_VL failed.");
+               free (vc->counter);
+               free (vc->gauge);
+               free (vc);
+               return (-1);
+       }
+
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+               {
+                       vc->gauge[i] = 0.0;
+                       vc->counter[i] = vl->values[i].counter;
+               }
+               else if (ds->ds[i].type == DS_TYPE_GAUGE)
+               {
+                       vc->gauge[i] = vl->values[i].gauge;
+                       vc->counter[i] = 0;
+               }
+               else
+               {
+                       vc->gauge[i] = 0.0;
+                       vc->counter[i] = 0;
+               }
+       }
+       vc->values_num = ds->ds_num;
+       vc->ds = ds;
+
+       vc->next = cache_head;
+       cache_head = vc;
+
+       vc->time = vl->time;
+       if (vc->time < cache_oldest)
+               cache_oldest = vc->time;
+
+       pthread_mutex_unlock (&cache_lock);
+       return (0);
+} /* int cache_insert */
+
+static int cache_update (const data_set_t *ds, const value_list_t *vl)
+{
+       char name[4*DATA_MAX_NAME_LEN];;
+       value_cache_t *vc;
+       int i;
+
+       if (FORMAT_VL (name, sizeof (name), vl, ds) != 0)
+               return (-1);
+
+       pthread_mutex_lock (&cache_lock);
+
+       vc = cache_search (name);
+
+       /* pthread_mutex_lock is called by cache_insert. */
+       if (vc == NULL)
+               return (cache_insert (ds, vl));
+
+       assert (vc->values_num == ds->ds_num);
+       assert (vc->values_num == vl->values_len);
+
+       /* Avoid floating-point exceptions due to division by zero. */
+       if (vc->time >= vl->time)
+       {
+               pthread_mutex_unlock (&cache_lock);
+               ERROR ("unixsock plugin: vc->time >= vl->time. vc->time = %u; "
+                               "vl->time = %u; vl = %s;",
+                               (unsigned int) vc->time, (unsigned int) vl->time,
+                               name);
+               return (-1);
+       } /* if (vc->time >= vl->time) */
+
+       /*
+        * Update the values. This is possibly a lot more that you'd expect
+        * because we honor min and max values and handle counter overflows here.
+        */
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if (ds->ds[i].type == DS_TYPE_COUNTER)
+               {
+                       if (vl->values[i].counter < vc->counter[i])
+                       {
+                               if (vl->values[i].counter <= 4294967295U)
+                               {
+                                       vc->gauge[i] = ((4294967295U - vl->values[i].counter)
+                                                       + vc->counter[i]) / (vl->time - vc->time);
+                               }
+                               else
+                               {
+                                       vc->gauge[i] = ((18446744073709551615ULL - vl->values[i].counter)
+                                               + vc->counter[i]) / (vl->time - vc->time);
+                               }
+                       }
+                       else
+                       {
+                               vc->gauge[i] = (vl->values[i].counter - vc->counter[i])
+                                       / (vl->time - vc->time);
+                       }
+
+                       vc->counter[i] = vl->values[i].counter;
+               }
+               else if (ds->ds[i].type == DS_TYPE_GAUGE)
+               {
+                       vc->gauge[i] = vl->values[i].gauge;
+                       vc->counter[i] = 0;
+               }
+               else
+               {
+                       vc->gauge[i] = NAN;
+                       vc->counter[i] = 0;
+               }
+
+               if (isnan (vc->gauge[i])
+                               || (!isnan (ds->ds[i].min) && (vc->gauge[i] < ds->ds[i].min))
+                               || (!isnan (ds->ds[i].max) && (vc->gauge[i] > ds->ds[i].max)))
+                       vc->gauge[i] = NAN;
+       } /* for i = 0 .. ds->ds_num */
+
+       vc->ds = ds;
+       vc->time = vl->time;
+
+       if (vc->time < cache_oldest)
+               cache_oldest = vc->time;
+
+       pthread_mutex_unlock (&cache_lock);
+       return (0);
+} /* int cache_update */
+
+static void cache_flush (int max_age)
+{
+       value_cache_t *this;
+       value_cache_t *prev;
+       time_t now;
+
+       pthread_mutex_lock (&cache_lock);
+
+       now = time (NULL);
+
+       if ((now - cache_oldest) <= max_age)
+       {
+               pthread_mutex_unlock (&cache_lock);
+               return;
+       }
+       
+       cache_oldest = now;
+
+       prev = NULL;
+       this = cache_head;
+
+       while (this != NULL)
+       {
+               if ((now - this->time) <= max_age)
+               {
+                       if (this->time < cache_oldest)
+                               cache_oldest = this->time;
+
+                       prev = this;
+                       this = this->next;
+                       continue;
+               }
+
+               if (prev == NULL)
+                       cache_head = this->next;
+               else
+                       prev->next = this->next;
+
+               free (this->gauge);
+               free (this->counter);
+               free (this);
+
+               if (prev == NULL)
+                       this = cache_head;
+               else
+                       this = prev->next;
+       } /* while (this != NULL) */
+
+       pthread_mutex_unlock (&cache_lock);
+} /* int cache_flush */
+
+static int us_open_socket (void)
+{
+       struct sockaddr_un sa;
+       int status;
+
+       sock_fd = socket (PF_UNIX, SOCK_STREAM, 0);
+       if (sock_fd < 0)
+       {
+               char errbuf[1024];
+               ERROR ("unixsock plugin: socket failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       memset (&sa, '\0', sizeof (sa));
+       sa.sun_family = AF_UNIX;
+       strncpy (sa.sun_path, (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
+                       sizeof (sa.sun_path) - 1);
+       /* unlink (sa.sun_path); */
+
+       status = bind (sock_fd, (struct sockaddr *) &sa, sizeof (sa));
+       if (status != 0)
+       {
+               char errbuf[1024];
+               sstrerror (errno, errbuf, sizeof (errbuf));
+               DEBUG ("bind failed: %s; sa.sun_path = %s", errbuf, sa.sun_path);
+               ERROR ("unixsock plugin: bind failed: %s", errbuf);
+               close (sock_fd);
+               sock_fd = -1;
+               return (-1);
+       }
+
+       status = listen (sock_fd, 8);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("unixsock plugin: listen failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               close (sock_fd);
+               sock_fd = -1;
+               return (-1);
+       }
+
+       do
+       {
+               char *grpname;
+               struct group *g;
+               struct group sg;
+               char grbuf[2048];
+
+               grpname = (sock_group != NULL) ? sock_group : COLLECTD_GRP_NAME;
+               g = NULL;
+
+               status = getgrnam_r (grpname, &sg, grbuf, sizeof (grbuf), &g);
+               if (status != 0)
+               {
+                       char errbuf[1024];
+                       WARNING ("unixsock plugin: getgrnam_r (%s) failed: %s", grpname,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       break;
+               }
+               if (g == NULL)
+               {
+                       WARNING ("unixsock plugin: No such group: `%s'",
+                                       grpname);
+                       break;
+               }
+
+               if (chown ((sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
+                                       (uid_t) -1, g->gr_gid) != 0)
+               {
+                       char errbuf[1024];
+                       WARNING ("unixsock plugin: chown (%s, -1, %i) failed: %s",
+                                       (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
+                                       (int) g->gr_gid,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+               }
+       } while (0);
+
+       return (0);
+} /* int us_open_socket */
+
+static int us_handle_getval (FILE *fh, char **fields, int fields_num)
+{
+       char *hostname;
+       char *plugin;
+       char *plugin_instance;
+       char *type;
+       char *type_instance;
+       char  name[4*DATA_MAX_NAME_LEN];
+       value_cache_t *vc;
+       int   status;
+       int   i;
+
+       if (fields_num != 2)
+       {
+               DEBUG ("unixsock plugin: Wrong number of fields: %i", fields_num);
+               fprintf (fh, "-1 Wrong number of fields: Got %i, expected 2.\n",
+                               fields_num);
+               fflush (fh);
+               return (-1);
+       }
+       DEBUG ("unixsock plugin: Got query for `%s'", fields[1]);
+
+       status = parse_identifier (fields[1], &hostname,
+                       &plugin, &plugin_instance,
+                       &type, &type_instance);
+       if (status != 0)
+       {
+               DEBUG ("unixsock plugin: Cannot parse `%s'", fields[1]);
+               fprintf (fh, "-1 Cannot parse identifier.\n");
+               fflush (fh);
+               return (-1);
+       }
+
+       status = format_name (name, sizeof (name),
+                       hostname, plugin, plugin_instance, type, type_instance);
+       if (status != 0)
+       {
+               fprintf (fh, "-1 format_name failed.\n");
+               return (-1);
+       }
+
+       pthread_mutex_lock (&cache_lock);
+
+       DEBUG ("vc = cache_search (%s)", name);
+       vc = cache_search (name);
+
+       if (vc == NULL)
+       {
+               DEBUG ("Did not find cache entry.");
+               fprintf (fh, "-1 No such value");
+       }
+       else
+       {
+               DEBUG ("Found cache entry.");
+               fprintf (fh, "%i", vc->values_num);
+               for (i = 0; i < vc->values_num; i++)
+               {
+                       fprintf (fh, " %s=", vc->ds->ds[i].name);
+                       if (isnan (vc->gauge[i]))
+                               fprintf (fh, "NaN");
+                       else
+                               fprintf (fh, "%12e", vc->gauge[i]);
+               }
+       }
+
+       /* Free the mutex as soon as possible and definitely before flushing */
+       pthread_mutex_unlock (&cache_lock);
+
+       fprintf (fh, "\n");
+       fflush (fh);
+
+       return (0);
+} /* int us_handle_getval */
+
+static int us_handle_putval (FILE *fh, char **fields, int fields_num)
+{
+       char *hostname;
+       char *plugin;
+       char *plugin_instance;
+       char *type;
+       char *type_instance;
+       int   status;
+       int   i;
+
+       const data_set_t *ds;
+       value_list_t vl = VALUE_LIST_INIT;
+
+       char **value_ptr;
+
+       if (fields_num != 3)
+       {
+               DEBUG ("unixsock plugin: Wrong number of fields: %i", fields_num);
+               fprintf (fh, "-1 Wrong number of fields: Got %i, expected 3.\n",
+                               fields_num);
+               fflush (fh);
+               return (-1);
+       }
+
+       status = parse_identifier (fields[1], &hostname,
+                       &plugin, &plugin_instance,
+                       &type, &type_instance);
+       if (status != 0)
+       {
+               DEBUG ("unixsock plugin: Cannot parse `%s'", fields[1]);
+               fprintf (fh, "-1 Cannot parse identifier.\n");
+               fflush (fh);
+               return (-1);
+       }
+
+       if ((strlen (hostname) > sizeof (vl.host))
+                       || (strlen (plugin) > sizeof (vl.plugin))
+                       || ((plugin_instance != NULL)
+                               && (strlen (plugin_instance) > sizeof (vl.plugin_instance)))
+                       || ((type_instance != NULL)
+                               && (strlen (type_instance) > sizeof (vl.type_instance))))
+       {
+               fprintf (fh, "-1 Identifier too long.");
+               return (-1);
+       }
+
+       strcpy (vl.host, hostname);
+       strcpy (vl.plugin, plugin);
+       if (plugin_instance != NULL)
+               strcpy (vl.plugin_instance, plugin_instance);
+       if (type_instance != NULL)
+               strcpy (vl.type_instance, type_instance);
+
+       { /* parse the time */
+               char *t = fields[2];
+               char *v = strchr (t, ':');
+               if (v == NULL)
+               {
+                       fprintf (fh, "-1 No time found.");
+                       return (-1);
+               }
+               *v = '\0'; v++;
+
+               vl.time = (time_t) atoi (t);
+               if (vl.time == 0)
+                       vl.time = time (NULL);
+
+               fields[2] = v;
+       }
+
+       ds = plugin_get_ds (type);
+       if (ds == NULL)
+               return (-1);
+
+       value_ptr = (char **) calloc (ds->ds_num, sizeof (char *));
+       if (value_ptr == NULL)
+       {
+               fprintf (fh, "-1 calloc failed.");
+               return (-1);
+       }
+
+       { /* parse the value-list. It's colon-separated. */
+               char *dummy;
+               char *ptr;
+               char *saveptr;
+
+               i = 0;
+               dummy = fields[2];
+               saveptr = NULL;
+               while ((ptr = strtok_r (dummy, ":", &saveptr)) != NULL)
+               {
+                       dummy = NULL;
+                       if (i >= ds->ds_num)
+                       {
+                               i = ds->ds_num + 1;
+                               break;
+                       }
+                       value_ptr[i] = ptr;
+                       i++;
+               }
+
+               if (i != ds->ds_num)
+               {
+                       sfree (value_ptr);
+                       fprintf (fh, "-1 Number of values incorrect: Got %i, "
+                                       "expected %i.", i, ds->ds_num);
+                       return (-1);
+               }
+       } /* done parsing the value-list */
+
+       vl.values_len = ds->ds_num;
+       vl.values = (value_t *) malloc (vl.values_len * sizeof (value_t));
+       if (vl.values == NULL)
+       {
+               sfree (value_ptr);
+               fprintf (fh, "-1 malloc failed.");
+               return (-1);
+       }
+       DEBUG ("value_ptr = 0x%p; vl.values = 0x%p;", (void *) value_ptr, (void *) vl.values);
+
+       for (i = 0; i < ds->ds_num; i++)
+       {
+               if (strcmp (value_ptr[i], "U") == 0)
+                       vl.values[i].gauge = NAN;
+               else if (ds->ds[i].type == DS_TYPE_COUNTER)
+                       vl.values[i].counter = atoll (value_ptr[i]);
+               else if (ds->ds[i].type == DS_TYPE_GAUGE)
+                       vl.values[i].gauge = atof (value_ptr[i]);
+       } /* for (i = 2 .. fields_num) */
+
+       plugin_dispatch_values (type, &vl);
+
+       DEBUG ("value_ptr = 0x%p; vl.values = 0x%p;", (void *) value_ptr, (void *) vl.values);
+
+       sfree (value_ptr);
+       sfree (vl.values); 
+
+       fprintf (fh, "0 Success\n");
+       fflush (fh);
+
+       return (0);
+} /* int us_handle_putval */
+
+static void *us_handle_client (void *arg)
+{
+       int fd;
+       FILE *fh;
+       char buffer[1024];
+       char *fields[128];
+       int   fields_num;
+
+       fd = *((int *) arg);
+       free (arg);
+       arg = NULL;
+
+       DEBUG ("Reading from fd #%i", fd);
+
+       fh = fdopen (fd, "r+");
+       if (fh == NULL)
+       {
+               char errbuf[1024];
+               ERROR ("unixsock plugin: fdopen failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               close (fd);
+               pthread_exit ((void *) 1);
+       }
+
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               int len;
+
+               len = strlen (buffer);
+               while ((len > 0)
+                               && ((buffer[len - 1] == '\n') || (buffer[len - 1] == '\r')))
+                       buffer[--len] = '\0';
+
+               if (len == 0)
+                       continue;
+
+               DEBUG ("fgets -> buffer = %s; len = %i;", buffer, len);
+
+               fields_num = strsplit (buffer, fields,
+                               sizeof (fields) / sizeof (fields[0]));
+
+               if (fields_num < 1)
+               {
+                       close (fd);
+                       break;
+               }
+
+               if (strcasecmp (fields[0], "getval") == 0)
+               {
+                       us_handle_getval (fh, fields, fields_num);
+               }
+               else if (strcasecmp (fields[0], "putval") == 0)
+               {
+                       us_handle_putval (fh, fields, fields_num);
+               }
+               else
+               {
+                       fprintf (fh, "-1 Unknown command: %s\n", fields[0]);
+                       fflush (fh);
+               }
+       } /* while (fgets) */
+
+       DEBUG ("Exiting..");
+       close (fd);
+
+       pthread_exit ((void *) 0);
+} /* void *us_handle_client */
+
+static void *us_server_thread (void *arg)
+{
+       int  status;
+       int *remote_fd;
+       pthread_t th;
+       pthread_attr_t th_attr;
+
+       if (us_open_socket () != 0)
+               pthread_exit ((void *) 1);
+
+       while (loop != 0)
+       {
+               DEBUG ("Calling accept..");
+               status = accept (sock_fd, NULL, NULL);
+               if (status < 0)
+               {
+                       char errbuf[1024];
+
+                       if (errno == EINTR)
+                               continue;
+
+                       ERROR ("unixsock plugin: accept failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       close (sock_fd);
+                       sock_fd = -1;
+                       pthread_exit ((void *) 1);
+               }
+
+               remote_fd = (int *) malloc (sizeof (int));
+               if (remote_fd == NULL)
+               {
+                       char errbuf[1024];
+                       WARNING ("unixsock plugin: malloc failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       close (status);
+                       continue;
+               }
+               *remote_fd = status;
+
+               DEBUG ("Spawning child to handle connection on fd #%i", *remote_fd);
+
+               pthread_attr_init (&th_attr);
+               pthread_attr_setdetachstate (&th_attr, PTHREAD_CREATE_DETACHED);
+
+               status = pthread_create (&th, &th_attr, us_handle_client, (void *) remote_fd);
+               if (status != 0)
+               {
+                       char errbuf[1024];
+                       WARNING ("unixsock plugin: pthread_create failed: %s",
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
+                       close (*remote_fd);
+                       free (remote_fd);
+                       continue;
+               }
+       } /* while (loop) */
+
+       close (sock_fd);
+       sock_fd = -1;
+
+       status = unlink ((sock_file != NULL) ? sock_file : US_DEFAULT_PATH);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               NOTICE ("unixsock plugin: unlink (%s) failed: %s",
+                               (sock_file != NULL) ? sock_file : US_DEFAULT_PATH,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+       }
+
+       return ((void *) 0);
+} /* void *us_server_thread */
+
+static int us_config (const char *key, const char *val)
+{
+       if (strcasecmp (key, "SocketFile") == 0)
+       {
+               sfree (sock_file);
+               sock_file = strdup (val);
+       }
+       else if (strcasecmp (key, "SocketGroup") == 0)
+       {
+               sfree (sock_group);
+               sock_group = strdup (val);
+       }
+       else if (strcasecmp (key, "SocketPerms") == 0)
+       {
+               sock_perms = (int) strtol (val, NULL, 8);
+       }
+       else
+       {
+               return (-1);
+       }
+
+       return (0);
+} /* int us_config */
+
+static int us_init (void)
+{
+       int status;
+
+       loop = 1;
+
+       status = pthread_create (&listen_thread, NULL, us_server_thread, NULL);
+       if (status != 0)
+       {
+               char errbuf[1024];
+               ERROR ("unixsock plugin: pthread_create failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       return (0);
+} /* int us_init */
+
+static int us_shutdown (void)
+{
+       void *ret;
+
+       loop = 0;
+
+       if (listen_thread != (pthread_t) 0)
+       {
+               pthread_kill (listen_thread, SIGTERM);
+               pthread_join (listen_thread, &ret);
+               listen_thread = (pthread_t) 0;
+       }
+
+       plugin_unregister_init ("unixsock");
+       plugin_unregister_write ("unixsock");
+       plugin_unregister_shutdown ("unixsock");
+
+       return (0);
+} /* int us_shutdown */
+
+static int us_write (const data_set_t *ds, const value_list_t *vl)
+{
+       cache_update (ds, vl);
+       cache_flush (2 * interval_g);
+
+       return (0);
+}
+
+void module_register (void)
+{
+       plugin_register_config ("unixsock", us_config,
+                       config_keys, config_keys_num);
+       plugin_register_init ("unixsock", us_init);
+       plugin_register_write ("unixsock", us_write);
+       plugin_register_shutdown ("unixsock", us_shutdown);
+} /* void module_register (void) */
+
+/* vim: set sw=4 ts=4 sts=4 tw=78 : */
index 973fda0..7df06c2 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/users.c
- * Copyright (C) 2005,2006  Sebastian Harl
+ * Copyright (C) 2005-2007  Sebastian Harl
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 # define USERS_HAVE_READ 0
 #endif
 
-static char *rrd_file = "users.rrd";
-static char *ds_def[] =
-{
-       "DS:users:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       NULL
-};
-static int ds_num = 1;
-
-static void users_init (void)
-{
-       /* we have nothing to do here :-) */
-       return;
-} /* static void users_init(void) */
-
-static void users_write (char *host, char *inst, char *val)
-{
-       rrd_update_file(host, rrd_file, val, ds_def, ds_num);
-       return;
-} /* static void users_write(char *host, char *inst, char *val) */
-
 #if USERS_HAVE_READ
-/* I don't like this temporary macro definition - well it's used everywhere
-   else in the collectd-sources, so I will just stick with it...  */
-#define BUFSIZE 256
-static void users_submit (unsigned int users)
+static void users_submit (gauge_t value)
 {
-       char buf[BUFSIZE] = "";
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       if (snprintf(buf, BUFSIZE, "%u:%u", 
-               (unsigned int)curtime, users) >= BUFSIZE)
-       {
-               return;
-       }
+       values[0].gauge = value;
+
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "users");
 
-       plugin_submit(MODULE_NAME, "-", buf);
-       return;
-} /* static void users_submit(unsigned int users) */
-#undef BUFSIZE
+       plugin_dispatch_values ("users", &vl);
+} /* void users_submit */
 
-static void users_read (void)
+static int users_read (void)
 {
 #if HAVE_GETUTXENT
        unsigned int users = 0;
@@ -114,17 +91,16 @@ static void users_read (void)
        }
        endutent();
 
-       users_submit(users);
+       users_submit (users);
 #endif /* HAVE_GETUTENT */
 
-       return;
-} /* static void users_read(void) */
-#else
-# define users_read NULL
+       return (0);
+} /* int users_read */
 #endif /* USERS_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, users_init, users_read, users_write);
-       return;
+#if USERS_HAVE_READ
+       plugin_register_read ("users", users_read);
+#endif
 } /* void module_register(void) */
diff --git a/src/utils_avltree.c b/src/utils_avltree.c
new file mode 100644 (file)
index 0000000..09cf2e6
--- /dev/null
@@ -0,0 +1,711 @@
+/**
+ * collectd - src/utils_avltree.c
+ * Copyright (C) 2006,2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "utils_avltree.h"
+
+#define BALANCE(n) ((((n)->left == NULL) ? 0 : (n)->left->height) \
+               - (((n)->right == NULL) ? 0 : (n)->right->height))
+
+/*
+ * private data types
+ */
+struct avl_node_s
+{
+       void *key;
+       void *value;
+
+       int height;
+       struct avl_node_s *left;
+       struct avl_node_s *right;
+       struct avl_node_s *parent;
+};
+typedef struct avl_node_s avl_node_t;
+
+struct avl_tree_s
+{
+       avl_node_t *root;
+       int (*compare) (const void *, const void *);
+};
+
+struct avl_iterator_s
+{
+       avl_tree_t *tree;
+       avl_node_t *node;
+};
+
+/*
+ * private functions
+ */
+#if 0
+static void verify_tree (avl_node_t *n)
+{
+       if (n == NULL)
+               return;
+
+       verify_tree (n->left);
+       verify_tree (n->right);
+
+       assert ((BALANCE (n) >= -1) && (BALANCE (n) <= 1));
+       assert ((n->parent == NULL) || (n->parent->right == n) || (n->parent->left == n));
+} /* void verify_tree */
+#else
+# define verify_tree(n) /**/
+#endif
+
+static void free_node (avl_node_t *n)
+{
+       if (n == NULL)
+               return;
+
+       if (n->left != NULL)
+               free_node (n->left);
+       if (n->right != NULL)
+               free_node (n->right);
+
+       free (n);
+}
+
+static int calc_height (avl_node_t *n)
+{
+       int height_left;
+       int height_right;
+
+       if (n == NULL)
+               return (0);
+
+       height_left  = (n->left == NULL)  ? 0 : n->left->height;
+       height_right = (n->right == NULL) ? 0 : n->right->height;
+
+       return (((height_left > height_right)
+                               ? height_left
+                               : height_right) + 1);
+} /* int calc_height */
+
+static avl_node_t *search (avl_tree_t *t, const void *key)
+{
+       avl_node_t *n;
+       int cmp;
+
+       n = t->root;
+       while (n != NULL)
+       {
+               cmp = t->compare (key, n->key);
+               if (cmp == 0)
+                       return (n);
+               else if (cmp < 0)
+                       n = n->left;
+               else
+                       n = n->right;
+       }
+
+       return (NULL);
+}
+
+/*         (x)             (y)
+ *        /   \           /   \
+ *     (y)    /\         /\    (x)
+ *    /   \  /_c\  ==>  / a\  /   \
+ *   /\   /\           /____\/\   /\
+ *  / a\ /_b\               /_b\ /_c\
+ * /____\
+ */
+static avl_node_t *rotate_right (avl_tree_t *t, avl_node_t *x)
+{
+       avl_node_t *p;
+       avl_node_t *y;
+       avl_node_t *b;
+
+       p = x->parent;
+       y = x->left;
+       b = y->right;
+
+       x->left = b;
+       if (b != NULL)
+               b->parent = x;
+
+       x->parent = y;
+       y->right = x;
+
+       y->parent = p;
+       assert ((p == NULL) || (p->left == x) || (p->right == x));
+       if (p == NULL)
+               t->root = y;
+       else if (p->left == x)
+               p->left = y;
+       else
+               p->right = y;
+
+       x->height = calc_height (x);
+       y->height = calc_height (y);
+
+       return (y);
+} /* void rotate_left */
+
+/*
+ *    (x)                   (y)
+ *   /   \                 /   \
+ *  /\    (y)           (x)    /\
+ * /_a\  /   \   ==>   /   \  / c\
+ *      /\   /\       /\   /\/____\
+ *     /_b\ / c\     /_a\ /_b\
+ *         /____\
+ */
+static avl_node_t *rotate_left (avl_tree_t *t, avl_node_t *x)
+{
+       avl_node_t *p;
+       avl_node_t *y;
+       avl_node_t *b;
+
+       p = x->parent;
+       y = x->right;
+       b = y->left;
+
+       x->right = b;
+       if (b != NULL)
+               b->parent = x;
+
+       x->parent = y;
+       y->left = x;
+
+       y->parent = p;
+       assert ((p == NULL) || (p->left == x) || (p->right == x));
+       if (p == NULL)
+               t->root = y;
+       else if (p->left == x)
+               p->left = y;
+       else
+               p->right = y;
+
+       x->height = calc_height (x);
+       y->height = calc_height (y);
+
+       return (y);
+} /* void rotate_left */
+
+static avl_node_t *rotate_left_right (avl_tree_t *t, avl_node_t *x)
+{
+       rotate_left (t, x->left);
+       return (rotate_right (t, x));
+} /* void rotate_left_right */
+
+static avl_node_t *rotate_right_left (avl_tree_t *t, avl_node_t *x)
+{
+       rotate_right (t, x->right);
+       return (rotate_left (t, x));
+} /* void rotate_right_left */
+
+static void rebalance (avl_tree_t *t, avl_node_t *n)
+{
+       int b_top;
+       int b_bottom;
+
+       while (n != NULL)
+       {
+               b_top = BALANCE (n);
+               assert ((b_top >= -2) && (b_top <= 2));
+
+               if (b_top == -2)
+               {
+                       assert (n->right != NULL);
+                       b_bottom = BALANCE (n->right);
+                       assert ((b_bottom >= -1) || (b_bottom <= 1));
+                       if (b_bottom == 1)
+                               n = rotate_right_left (t, n);
+                       else
+                               n = rotate_left (t, n);
+               }
+               else if (b_top == 2)
+               {
+                       assert (n->left != NULL);
+                       b_bottom = BALANCE (n->left);
+                       assert ((b_bottom >= -1) || (b_bottom <= 1));
+                       if (b_bottom == -1)
+                               n = rotate_left_right (t, n);
+                       else
+                               n = rotate_right (t, n);
+               }
+               else
+               {
+                       int height = calc_height (n);
+                       if (height == n->height)
+                               break;
+                       n->height = height;
+               }
+
+               assert (n->height == calc_height (n));
+
+               n = n->parent;
+       } /* while (n != NULL) */
+} /* void rebalance */
+
+static avl_node_t *avl_node_next (avl_tree_t *t, avl_node_t *n)
+{
+       avl_node_t *r; /* return node */
+
+       if (n == NULL)
+       {
+               return (NULL);
+       }
+
+       /* If we can't descent any further, we have to backtrack to the first
+        * parent that's bigger than we, i. e. who's _left_ child we are. */
+       if (n->right == NULL)
+       {
+               r = n->parent;
+               while ((r != NULL) && (r->parent != NULL))
+               {
+                       if (r->left == n)
+                               break;
+                       n = r;
+                       r = n->parent;
+               }
+
+               /* n->right == NULL && r == NULL => t is root and has no next
+                * r->left != n => r->right = n => r->parent == NULL */
+               if ((r == NULL) || (r->left != n))
+               {
+                       assert ((r == NULL) || (r->parent == NULL));
+                       return (NULL);
+               }
+               else
+               {
+                       assert (r->left == n);
+                       return (r);
+               }
+       }
+       else
+       {
+               r = n->right;
+               while (r->left != NULL)
+                       r = r->left;
+       }
+
+       return (r);
+} /* avl_node_t *avl_node_next */
+
+static avl_node_t *avl_node_prev (avl_tree_t *t, avl_node_t *n)
+{
+       avl_node_t *r; /* return node */
+
+       if (n == NULL)
+       {
+               return (NULL);
+       }
+
+       /* If we can't descent any further, we have to backtrack to the first
+        * parent that's smaller than we, i. e. who's _right_ child we are. */
+       if (n->left == NULL)
+       {
+               r = n->parent;
+               while ((r != NULL) && (r->parent != NULL))
+               {
+                       if (r->right == n)
+                               break;
+                       n = r;
+                       r = n->parent;
+               }
+
+               /* n->left == NULL && r == NULL => t is root and has no next
+                * r->right != n => r->left = n => r->parent == NULL */
+               if ((r == NULL) || (r->right != n))
+               {
+                       assert ((r == NULL) || (r->parent == NULL));
+                       return (NULL);
+               }
+               else
+               {
+                       assert (r->right == n);
+                       return (r);
+               }
+       }
+       else
+       {
+               r = n->left;
+               while (r->right != NULL)
+                       r = r->right;
+       }
+
+       return (r);
+} /* avl_node_t *avl_node_prev */
+
+static int _remove (avl_tree_t *t, avl_node_t *n)
+{
+       assert ((t != NULL) && (n != NULL));
+
+       if ((n->left != NULL) && (n->right != NULL))
+       {
+               avl_node_t *r; /* replacement node */
+               if (BALANCE (n) > 0) /* left subtree is higher */
+               {
+                       assert (n->left != NULL);
+                       r = avl_node_prev (t, n);
+                       
+               }
+               else /* right subtree is higher */
+               {
+                       assert (n->right != NULL);
+                       r = avl_node_next (t, n);
+               }
+
+               assert ((r->left == NULL) || (r->right == NULL));
+
+               /* copy content */
+               n->key   = r->key;
+               n->value = r->value;
+
+               n = r;
+       }
+
+       assert ((n->left == NULL) || (n->right == NULL));
+
+       if ((n->left == NULL) && (n->right == NULL))
+       {
+               /* Deleting a leave is easy */
+               if (n->parent == NULL)
+               {
+                       assert (t->root == n);
+                       t->root = NULL;
+               }
+               else
+               {
+                       assert ((n->parent->left == n)
+                                       || (n->parent->right == n));
+                       if (n->parent->left == n)
+                               n->parent->left = NULL;
+                       else
+                               n->parent->right = NULL;
+
+                       rebalance (t, n->parent);
+               }
+
+               free_node (n);
+       }
+       else if (n->left == NULL)
+       {
+               assert (BALANCE (n) == -1);
+               assert ((n->parent == NULL) || (n->parent->left == n) || (n->parent->right == n));
+               if (n->parent == NULL)
+               {
+                       assert (t->root == n);
+                       t->root = n->right;
+               }
+               else if (n->parent->left == n)
+               {
+                       n->parent->left = n->right;
+               }
+               else
+               {
+                       n->parent->right = n->right;
+               }
+               n->right->parent = n->parent;
+
+               if (n->parent != NULL)
+                       rebalance (t, n->parent);
+
+               n->right = NULL;
+               free_node (n);
+       }
+       else if (n->right == NULL)
+       {
+               assert (BALANCE (n) == 1);
+               assert ((n->parent == NULL) || (n->parent->left == n) || (n->parent->right == n));
+               if (n->parent == NULL)
+               {
+                       assert (t->root == n);
+                       t->root = n->left;
+               }
+               else if (n->parent->left == n)
+               {
+                       n->parent->left = n->left;
+               }
+               else
+               {
+                       n->parent->right = n->left;
+               }
+               n->left->parent = n->parent;
+
+               if (n->parent != NULL)
+                       rebalance (t, n->parent);
+
+               n->left = NULL;
+               free_node (n);
+       }
+       else
+       {
+               assert (0);
+       }
+
+       return (0);
+} /* void *_remove */
+
+/*
+ * public functions
+ */
+avl_tree_t *avl_create (int (*compare) (const void *, const void *))
+{
+       avl_tree_t *t;
+
+       if (compare == NULL)
+               return (NULL);
+
+       if ((t = (avl_tree_t *) malloc (sizeof (avl_tree_t))) == NULL)
+               return (NULL);
+
+       t->root = NULL;
+       t->compare = compare;
+
+       return (t);
+}
+
+void avl_destroy (avl_tree_t *t)
+{
+       free_node (t->root);
+       free (t);
+}
+
+int avl_insert (avl_tree_t *t, void *key, void *value)
+{
+       avl_node_t *new;
+       avl_node_t *nptr;
+       int cmp;
+
+       if ((new = (avl_node_t *) malloc (sizeof (avl_node_t))) == NULL)
+               return (-1);
+
+       new->key = key;
+       new->value = value;
+       new->height = 1;
+       new->left = NULL;
+       new->right = NULL;
+
+       if (t->root == NULL)
+       {
+               new->parent = NULL;
+               t->root = new;
+               return (0);
+       }
+
+       nptr = t->root;
+       while (42)
+       {
+               cmp = t->compare (nptr->key, new->key);
+               if (cmp == 0)
+               {
+                       free_node (new);
+                       return (-1);
+               }
+               else if (cmp < 0)
+               {
+                       /* nptr < new */
+                       if (nptr->right == NULL)
+                       {
+                               nptr->right = new;
+                               new->parent = nptr;
+                               rebalance (t, nptr);
+                               break;
+                       }
+                       else
+                       {
+                               nptr = nptr->right;
+                       }
+               }
+               else /* if (cmp > 0) */
+               {
+                       /* nptr > new */
+                       if (nptr->left == NULL)
+                       {
+                               nptr->left = new;
+                               new->parent = nptr;
+                               rebalance (t, nptr);
+                               break;
+                       }
+                       else
+                       {
+                               nptr = nptr->left;
+                       }
+               }
+       } /* while (42) */
+
+       verify_tree (t->root);
+       return (0);
+} /* int avl_insert */
+
+int avl_remove (avl_tree_t *t, const void *key, void **rkey, void **rvalue)
+{
+       avl_node_t *n;
+       int status;
+
+       assert (t != NULL);
+
+       n = search (t, key);
+       if (n == NULL)
+               return (-1);
+
+       if (rkey != NULL)
+               *rkey = n->key;
+       if (rvalue != NULL)
+               *rvalue = n->value;
+
+       status = _remove (t, n);
+       verify_tree (t->root);
+       return (status);
+} /* void *avl_remove */
+
+int avl_get (avl_tree_t *t, const void *key, void **value)
+{
+       avl_node_t *n;
+
+       assert (value != NULL);
+
+       n = search (t, key);
+       if (n == NULL)
+               return (-1);
+
+       *value = n->value;
+
+       return (0);
+}
+
+int avl_pick (avl_tree_t *t, void **key, void **value)
+{
+       avl_node_t *n;
+       avl_node_t *p;
+
+       if ((key == NULL) || (value == NULL))
+               return (-1);
+       if (t->root == NULL)
+               return (-1);
+
+       n = t->root;
+       while ((n->left != NULL) || (n->right != NULL))
+       {
+               int height_left  = (n->left  == NULL) ? 0 : n->left->height;
+               int height_right = (n->right == NULL) ? 0 : n->right->height;
+
+               if (height_left > height_right)
+                       n = n->left;
+               else
+                       n = n->right;
+       }
+
+       p = n->parent;
+       if (p == NULL)
+               t->root = NULL;
+       else if (p->left == n)
+               p->left = NULL;
+       else
+               p->right = NULL;
+
+       *key   = n->key;
+       *value = n->value;
+
+       free_node (n);
+       rebalance (t, p);
+
+       return (0);
+} /* int avl_pick */
+
+avl_iterator_t *avl_get_iterator (avl_tree_t *t)
+{
+       avl_iterator_t *iter;
+
+       if (t == NULL)
+               return (NULL);
+
+       iter = (avl_iterator_t *) malloc (sizeof (avl_iterator_t));
+       if (iter == NULL)
+               return (NULL);
+       memset (iter, '\0', sizeof (avl_iterator_t));
+       iter->tree = t;
+
+       return (iter);
+} /* avl_iterator_t *avl_get_iterator */
+
+int avl_iterator_next (avl_iterator_t *iter, void **key, void **value)
+{
+       avl_node_t *n;
+
+       if ((iter == NULL) || (key == NULL) || (value == NULL))
+               return (-1);
+
+       if (iter->node == NULL)
+       {
+               for (n = iter->tree->root; n != NULL; n = n->left)
+                       if (n->left == NULL)
+                               break;
+               iter->node = n;
+       }
+       else
+       {
+               n = avl_node_next (iter->tree, iter->node);
+       }
+
+       if (n == NULL)
+               return (-1);
+
+       iter->node = n;
+       *key = n->key;
+       *value = n->value;
+
+       return (0);
+} /* int avl_iterator_next */
+
+int avl_iterator_prev (avl_iterator_t *iter, void **key, void **value)
+{
+       avl_node_t *n;
+
+       if ((iter == NULL) || (key == NULL) || (value == NULL))
+               return (-1);
+
+       if (iter->node == NULL)
+       {
+               for (n = iter->tree->root; n != NULL; n = n->left)
+                       if (n->right == NULL)
+                               break;
+               iter->node = n;
+       }
+       else
+       {
+               n = avl_node_prev (iter->tree, iter->node);
+       }
+
+       if (n == NULL)
+               return (-1);
+
+       iter->node = n;
+       *key = n->key;
+       *value = n->value;
+
+       return (0);
+} /* int avl_iterator_prev */
+
+void avl_iterator_destroy (avl_iterator_t *iter)
+{
+       free (iter);
+}
diff --git a/src/utils_avltree.h b/src/utils_avltree.h
new file mode 100644 (file)
index 0000000..0ac6087
--- /dev/null
@@ -0,0 +1,144 @@
+/**
+ * collectd - src/utils_avltree.h
+ * Copyright (C) 2006,2007  Florian octo Forster
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#ifndef UTILS_AVLTREE_H
+#define UTILS_AVLTREE_H 1
+
+struct avl_tree_s;
+typedef struct avl_tree_s avl_tree_t;
+
+struct avl_iterator_s;
+typedef struct avl_iterator_s avl_iterator_t;
+
+/*
+ * NAME
+ *   avl_create
+ *
+ * DESCRIPTION
+ *   Allocates a new AVL-tree.
+ *
+ * PARAMETERS
+ *   `compare'  The function-pointer `compare' is used to compare two keys. It
+ *              has to return less than zero if it's first argument is smaller
+ *              then the second argument, more than zero if the first argument
+ *              is bigger than the second argument and zero if they are equal.
+ *              If your keys are char-pointers, you can use the `strcmp'
+ *              function from the libc here.
+ *
+ * RETURN VALUE
+ *   A avl_tree_t-pointer upon success or NULL upon failure.
+ */
+avl_tree_t *avl_create (int (*compare) (const void *, const void *));
+
+
+/*
+ * NAME
+ *   avl_destroy
+ *
+ * DESCRIPTION
+ *   Deallocates an AVL-tree. Stored value- and key-pointer are lost, but of
+ *   course not freed.
+ */
+void avl_destroy (avl_tree_t *t);
+
+/*
+ * NAME
+ *   avl_insert
+ *
+ * DESCRIPTION
+ *   Stores the key-value-pair in the AVL-tree pointed to by `t'.
+ *
+ * PARAMETERS
+ *   `t'        AVL-tree to store the data in.
+ *   `key'      Key used to store the value under. This is used to get back to
+ *              the value again.
+ *   `value'    Value to be stored.
+ *
+ * RETURN VALUE
+ *   Zero upon success and non-zero upon failure and if the key is already
+ *   stored in the tree.
+ */
+int avl_insert (avl_tree_t *t, void *key, void *value);
+
+/*
+ * NAME
+ *   avl_remove
+ *
+ * DESCRIPTION
+ *   Removes a key-value-pair from the tree t. The stored key and value may be
+ *   returned in `rkey' and `rvalue'.
+ *
+ * PARAMETERS
+ *   `t'       AVL-tree to remove key-value-pair from.
+ *   `key'      Key to identify the entry.
+ *   `rkey'     Pointer to a pointer in which to store the key. May be NULL.
+ *   `rvalue'   Pointer to a pointer in which to store the value. May be NULL.
+ *
+ * RETURN VALUE
+ *   Zero upon success or non-zero if the key isn't found in the tree.
+ */
+int avl_remove (avl_tree_t *t, const void *key, void **rkey, void **rvalue);
+
+/*
+ * NAME
+ *   avl_get
+ *
+ * DESCRIPTION
+ *   Retrieve the `value' belonging to `key'.
+ *
+ * PARAMETERS
+ *   `t'       AVL-tree to get the value from.
+ *   `key'      Key to identify the entry.
+ *   `value'    Pointer to a pointer in which to store the value. May be NULL.
+ *
+ * RETURN VALUE
+ *   Zero upon success or non-zero if the key isn't found in the tree.
+ */
+int avl_get (avl_tree_t *t, const void *key, void **value);
+
+/*
+ * NAME
+ *   avl_pick
+ *
+ * DESCRIPTION
+ *   Remove a (pseudo-)random element from the tree and return it's `key' and
+ *   `value'. Entries are not returned in any particular order. This function
+ *   is intended for cache-flushes that don't care about the order but simply
+ *   want to remove all elements, one at a time.
+ *
+ * PARAMETERS
+ *   `t'       AVL-tree to get the value from.
+ *   `key'      Pointer to a pointer in which to store the key.
+ *   `value'    Pointer to a pointer in which to store the value.
+ *
+ * RETURN VALUE
+ *   Zero upon success or non-zero if the tree is empty or key or value is
+ *   NULL.
+ */
+int avl_pick (avl_tree_t *t, void **key, void **value);
+
+avl_iterator_t *avl_get_iterator (avl_tree_t *t);
+int avl_iterator_next (avl_iterator_t *iter, void **key, void **value);
+int avl_iterator_prev (avl_iterator_t *iter, void **key, void **value);
+void avl_iterator_destroy (avl_iterator_t *iter);
+
+#endif /* UTILS_AVLTREE_H */
diff --git a/src/utils_debug.c b/src/utils_debug.c
deleted file mode 100644 (file)
index 8bfe10b..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/**
- * collectd - src/utils_debug.c
- * Copyright (C) 2005,2006  Niki W. Waibel
- *
- * 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; either version 2 of the License, or (at your
- * option) any later version.
- *
- * 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:
- *   Niki W. Waibel <niki.waibel at gmx.net>
- **/
-
-#if HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include "common.h"
-#include "utils_debug.h"
-
-#if COLLECT_DEBUG
-
-static void cu_vdebug(const char *file, int line, const char *func,
-       const char *format, va_list ap);
-
-/* if preemptive threads are used, these vars need some sort of locking! */
-/* pth is non-preemptive, so no locking is necessary */
-static FILE *cu_debug_file = NULL;
-static char *cu_debug_filename = NULL;
-
-static void
-cu_vdebug(const char *file, int line, const char *func,
-       const char *format, va_list ap)
-{ 
-       FILE *f;
-       if(cu_debug_file != NULL) {
-               f = cu_debug_file;
-       } else {
-               /* stderr might be redirected to /dev/null. in that case */
-               /* you'll not see anything... */
-               f = stderr;
-       }
-
-       fprintf(f, "%s:%d:%s(): ",
-               file, line, func);
-       vfprintf(f, format, ap);
-       fprintf(f, "\n");
-       fflush(f);
-} /* static void cu_vdebug(const char *file, int line, const char *func,
-       const char *format, va_list ap) */
-
-void
-cu_debug(const char *file, int line, const char *func,
-       const char *format, ...)
-{ 
-       va_list ap;
-
-       va_start(ap, format);
-       cu_vdebug(file, line, func, format, ap);
-       va_end(ap);
-} /* void cu_debug(const char *file, int line, const char *func,
-       const char *format, ...) */
-
-int
-cu_debug_startfile(const char *file, int line, const char *func,
-       const char *filename, const char *format, ...)
-{
-       va_list ap;
-
-       if(cu_debug_file != NULL) {
-               DBG("Don't call this function more then once without"
-                       " calling cu_debug_stopfile().");
-               return EXIT_FAILURE;
-       }
-
-       if(cu_debug_filename == NULL) {
-               cu_debug_filename = sstrdup(filename);
-       }
-
-       cu_debug_file = fopen(cu_debug_filename, "a");
-       if(cu_debug_file == NULL) {
-               DBG("Cannot open debug file %s: %s.\n",
-                       cu_debug_filename, strerror(errno));
-               return EXIT_FAILURE;
-       }
-
-       va_start(ap, format);
-       cu_vdebug(file, line, func, format, ap);
-       va_end(ap);
-
-       return EXIT_SUCCESS;
-} /* int cu_debug_start(const char *file, int line, const char *func,
-       const char *format, ...) */
-
-int
-cu_debug_stopfile(const char *file, int line, const char *func,
-       const char *format, ...)
-{
-       va_list ap;
-
-       va_start(ap, format);
-       cu_vdebug(file, line, func, format, ap);
-       va_end(ap);
-
-       if(cu_debug_file == NULL) {
-               DBG("Don't call this function more then once or without"
-                       " calling cu_debug_startfile().");
-               return EXIT_FAILURE;
-       }
-
-       if(fclose(cu_debug_file) != 0) {
-               DBG("Cannot close debug file %s: %s.\n",
-                       cu_debug_filename, strerror(errno));
-               return EXIT_FAILURE;
-       }
-       cu_debug_file = NULL;
-
-       sfree(cu_debug_filename);
-
-       return EXIT_SUCCESS;
-} /* int cu_debug_stop(const char *file, int line, const char *func,
-       const char *format, ...) */
-
-int
-cu_debug_resetfile(const char *file, int line, const char *func,
-       const char *filename)
-{
-       if(filename == NULL) {
-               DBG("You have to set filename when calling this function!\n");
-               return EXIT_FAILURE;
-       }
-       if(cu_debug_file != NULL) {
-               char *save_filename = NULL;
-
-               /* DBG_STARTFILE was called already */
-               /* reopen file */
-
-               DBG_STOPFILE("Closing %s and reopening %s.",
-                       cu_debug_filename, filename);
-               save_filename = smalloc(strlen(cu_debug_filename)+1);
-               sstrncpy(save_filename, cu_debug_filename,
-                       strlen(cu_debug_filename)+1);
-               cu_debug_filename = smalloc(strlen(filename)+1);
-               sstrncpy(cu_debug_filename, filename, strlen(filename)+1);
-               DBG_STARTFILE("Reopening %s after closing %s.",
-                       filename, save_filename);
-               sfree(save_filename);
-               return EXIT_SUCCESS;
-       }
-
-       /* DBG_STARTFILE was NOT called already */
-       /* setting filename only */
-
-       if(cu_debug_filename != NULL) {
-               sfree(cu_debug_filename);
-       }
-       cu_debug_filename = smalloc(strlen(filename)+1);
-       sstrncpy(cu_debug_filename, filename, strlen(filename)+1);
-
-       return EXIT_SUCCESS;
-} /* int cu_debug_resetfile(const char *file, int line, const char *func,
-        const char *filename) */
-
-#endif /* COLLECT_DEBUG */
-
diff --git a/src/utils_debug.h b/src/utils_debug.h
deleted file mode 100644 (file)
index 63567d3..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * collectd - src/utils_debug.h
- * Copyright (C) 2005,2006  Niki W. Waibel
- *
- * This program is free software; you can redistribute it and/
- * or modify it under the terms of the GNU General Public Li-
- * cence as published by the Free Software Foundation; either
- * version 2 of the Licence, or any later version.
- *
- * This program is distributed in the hope that it will be use-
- * ful, but WITHOUT ANY WARRANTY; without even the implied war-
- * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public Licence for more details.
- *
- * You should have received a copy of the GNU General Public
- * Licence along with this program; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
- * USA.
- *
- * Author:
- *   Niki W. Waibel <niki.waibel@gmx.net>
-**/
-
-/* BUG: at the moment you can use DBG_STARTFILE only ONCE in
-   your program. more then one logfile is not supported. */
-
-#if !COLLECTD_UTILS_DEBUG_H
-#define COLLECTD_UTILS_DEBUG_H 1
-
-#if COLLECT_DEBUG
-
-#define DBG(...) cu_debug(__FILE__, __LINE__, __func__, \
-       __VA_ARGS__)
-
-#define DBG_STARTFILE(file, ...) cu_debug_startfile(__FILE__, __LINE__, \
-       __func__, file, __VA_ARGS__)
-#define DBG_STOPFILE(...) cu_debug_stopfile(__FILE__, __LINE__, \
-       __func__, __VA_ARGS__)
-
-#define DBG_RESETFILE(file) cu_debug_resetfile(__FILE__, __LINE__, __func__, \
-       filename)
-
-void cu_debug(const char *file, int line, const char *func,
-       const char *format, ...);
-
-int cu_debug_startfile(const char *file, int line, const char *func,
-       const char *filename, const char *format, ...);
-int cu_debug_stopfile(const char *file, int line, const char *func,
-       const char *format, ...);
-
-int cu_debug_resetfile(const char *file, int line, const char *func,
-       const char *filename);
-
-#else /* !COLLECT_DEBUG */
-
-#define DBG(...)                   /* DBG(...) */
-#define DBG_STARTFILE(file, ...)   /* DBG_STARTFILE(file, ...) */
-#define DBG_STOPFILE(...)          /* DBG_STOPFILE(...) */
-#define DBG_RESETFILE(file)        /* DBG_RESETFILE(file) */
-
-#endif /* COLLECT_DEBUG */
-
-#endif /* !COLLECTD_UTILS_DEBUG_H */
-
index d002c7a..d2546aa 100644 (file)
@@ -52,7 +52,7 @@
 #endif
 
 #include "common.h"
-#include "utils_debug.h"
+#include "plugin.h"
 #include "utils_ignorelist.h"
 
 /*
@@ -98,7 +98,7 @@ static int ignorelist_append_regex(ignorelist_t *il, const char *entry)
        /* create buffer */
        if ((regtemp = malloc(sizeof(regex_t))) == NULL)
        {
-               syslog (LOG_ERR, "cannot allocate new config entry");
+               ERROR ("cannot allocate new config entry");
                return (1);
        }
        memset (regtemp, '\0', sizeof(regex_t));
@@ -115,14 +115,14 @@ static int ignorelist_append_regex(ignorelist_t *il, const char *entry)
                {
                        fprintf (stderr, "Cannot compile regex %s: %i/%s",
                                        entry, rcompile, regerr);
-                       syslog (LOG_ERR, "Cannot compile regex %s: %i/%s",
+                       ERROR ("Cannot compile regex %s: %i/%s",
                                        entry, rcompile, regerr);
                }
                else
                {
                        fprintf (stderr, "Cannot compile regex %s: %i",
                                        entry, rcompile);
-                       syslog (LOG_ERR, "Cannot compile regex %s: %i",
+                       ERROR ("Cannot compile regex %s: %i",
                                        entry, rcompile);
                }
 
@@ -131,12 +131,12 @@ static int ignorelist_append_regex(ignorelist_t *il, const char *entry)
                regfree (regtemp);
                return (1);
        }
-       DBG("regex compiled: %s - %i", entry, rcompile);
+       DEBUG("regex compiled: %s - %i", entry, rcompile);
 
        /* create new entry */
        if ((new = malloc(sizeof(ignorelist_item_t))) == NULL)
        {
-               syslog (LOG_ERR, "cannot allocate new config entry");
+               ERROR ("cannot allocate new config entry");
                regfree (regtemp);
                return (1);
        }
@@ -157,7 +157,7 @@ static int ignorelist_append_string(ignorelist_t *il, const char *entry)
        /* create new entry */
        if ((new = malloc(sizeof(ignorelist_item_t))) == NULL )
        {
-               syslog (LOG_ERR, "cannot allocate new entry");
+               ERROR ("cannot allocate new entry");
                return (1);
        }
        memset (new, '\0', sizeof(ignorelist_item_t));
@@ -217,7 +217,7 @@ ignorelist_t *ignorelist_create (int invert)
 
        /* smalloc exits if it failes */
        il = (ignorelist_t *) smalloc (sizeof (ignorelist_t));
-       DBG("Ignorelist created 0x%p, default is %s",
+       DEBUG("Ignorelist created 0x%p, default is %s",
                        (void *) il,
                        invert ? "collect" : "ignore");
 
@@ -240,7 +240,7 @@ void ignorelist_free (ignorelist_t *il)
        ignorelist_item_t *this;
        ignorelist_item_t *next;
 
-       DBG ("(il = 0x%p)", (void *) il);
+       DEBUG ("(il = 0x%p)", (void *) il);
 
        if (il == NULL)
                return;
@@ -274,7 +274,7 @@ void ignorelist_set_invert (ignorelist_t *il, int invert)
 {
        if (il == NULL)
        {
-               DBG("ignore call with ignorelist_t == NULL");
+               DEBUG("ignore call with ignorelist_t == NULL");
                return;
        }
 
@@ -292,7 +292,7 @@ int ignorelist_add (ignorelist_t *il, const char *entry)
 
        if (il == NULL)
        {
-               DBG ("add called with ignorelist_t == NULL");
+               DEBUG ("add called with ignorelist_t == NULL");
                return (1);
        }
 
@@ -301,7 +301,7 @@ int ignorelist_add (ignorelist_t *il, const char *entry)
        /* append nothing */
        if (entry_len == 0)
        {
-               DBG("not appending: empty entry");
+               DEBUG("not appending: empty entry");
                return (1);
        }
 
@@ -316,14 +316,14 @@ int ignorelist_add (ignorelist_t *il, const char *entry)
                memset (entry_copy, '\0', entry_len);
                strncpy (entry_copy, entry + 1, entry_len - 2);
 
-               DBG("I'm about to add regex entry: %s", entry_copy);
+               DEBUG("I'm about to add regex entry: %s", entry_copy);
                ret = ignorelist_append_regex(il, entry_copy);
                sfree (entry_copy);
        }
        else
 #endif
        {
-               DBG("to add entry: %s", entry);
+               DEBUG("to add entry: %s", entry);
                ret = ignorelist_append_string(il, entry);
        }
 
diff --git a/src/utils_llist.c b/src/utils_llist.c
new file mode 100644 (file)
index 0000000..d8694e3
--- /dev/null
@@ -0,0 +1,150 @@
+/**
+ * collectd - src/utils_llist.c
+ * Copyright (C) 2006 Florian Forster <octo at verplant.org>
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; only
+ * version 2 of the Licence is applicable.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * Licence along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ * Authors:
+ *   Florian Forster <octo at verplant.org>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils_llist.h"
+
+/*
+ * Private data types
+ */
+struct llist_s
+{
+       llentry_t *head;
+       llentry_t *tail;
+};
+
+/*
+ * Public functions
+ */
+llist_t *llist_create (void)
+{
+       llist_t *ret;
+
+       ret = (llist_t *) malloc (sizeof (llist_t));
+       if (ret == NULL)
+               return (NULL);
+
+       memset (ret, '\0', sizeof (llist_t));
+
+       return (ret);
+}
+
+void llist_destroy (llist_t *l)
+{
+       llentry_t *e_this;
+       llentry_t *e_next;
+
+       for (e_this = l->head; e_this != NULL; e_this = e_next)
+       {
+               e_next = e_this->next;
+               llentry_destroy (e_this);
+       }
+
+       free (l);
+}
+
+llentry_t *llentry_create (const char *key, void *value)
+{
+       llentry_t *e;
+
+       e = (llentry_t *) malloc (sizeof (llentry_t));
+       if (e == NULL)
+               return (NULL);
+
+       e->key   = strdup (key);
+       e->value = value;
+       e->next  = NULL;
+
+       if (e->key == NULL)
+       {
+               free (e);
+               return (NULL);
+       }
+
+       return (e);
+}
+
+void llentry_destroy (llentry_t *e)
+{
+       free (e->key);
+       free (e);
+}
+
+void llist_append (llist_t *l, llentry_t *e)
+{
+       e->next = NULL;
+
+       if (l->tail == NULL)
+               l->head = e;
+       else
+               l->tail->next = e;
+
+       l->tail = e;
+}
+
+void llist_prepend (llist_t *l, llentry_t *e)
+{
+       e->next = l->head;
+       l->head = e;
+}
+
+void llist_remove (llist_t *l, llentry_t *e)
+{
+       llentry_t *prev;
+
+       prev = l->head;
+       while ((prev != NULL) && (prev->next != e))
+               prev = prev->next;
+
+       if (prev != NULL)
+               prev->next = e->next;
+       if (l->head == e)
+               l->head = e->next;
+       if (l->tail == e)
+               l->tail = prev;
+}
+
+llentry_t *llist_search (llist_t *l, const char *key)
+{
+       llentry_t *e;
+
+       for (e = l->head; e != NULL; e = e->next)
+               if (strcmp (key, e->key) == 0)
+                       break;
+
+       return (e);
+}
+
+llentry_t *llist_head (llist_t *l)
+{
+       return (l->head);
+}
+
+llentry_t *llist_tail (llist_t *l)
+{
+       return (l->tail);
+}
diff --git a/src/utils_llist.h b/src/utils_llist.h
new file mode 100644 (file)
index 0000000..603fc87
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * collectd - src/utils_llist.h
+ * Copyright (C) 2006 Florian Forster <octo at verplant.org>
+ *
+ * This program is free software; you can redistribute it and/
+ * or modify it under the terms of the GNU General Public Li-
+ * cence as published by the Free Software Foundation; only
+ * version 2 of the Licence is applicable.
+ *
+ * This program is distributed in the hope that it will be use-
+ * ful, but WITHOUT ANY WARRANTY; without even the implied war-
+ * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public Licence for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * Licence along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ * Authors:
+ *   Florian Forster <octo at verplant.org>
+ */
+
+#ifndef UTILS_LLIST_H
+#define UTILS_LLIST_H 1
+
+/*
+ * Data types
+ */
+struct llentry_s
+{
+       char *key;
+       void *value;
+       struct llentry_s *next;
+};
+typedef struct llentry_s llentry_t;
+
+struct llist_s;
+typedef struct llist_s llist_t;
+
+/*
+ * Functions
+ */
+llist_t *llist_create (void);
+void llist_destroy (llist_t *l);
+
+llentry_t *llentry_create (const char *key, void *value);
+void llentry_destroy (llentry_t *e);
+
+void llist_append (llist_t *l, llentry_t *e);
+void llist_prepend (llist_t *l, llentry_t *e);
+void llist_remove (llist_t *l, llentry_t *e);
+
+llentry_t *llist_search (llist_t *l, const char *key);
+
+llentry_t *llist_head (llist_t *l);
+llentry_t *llist_tail (llist_t *l);
+
+#endif /* UTILS_LLIST_H */
index 163bd63..67d2e33 100644 (file)
@@ -32,7 +32,7 @@
 #define XFS_SUPER_MAGIC2_STR "BSFX"
 #endif
 
-#include "utils_debug.h"
+#include "plugin.h"
 #include "utils_mount.h"
 
 #if HAVE_GETVFSSTAT
@@ -334,7 +334,7 @@ get_spec_by_uuid(const char *s)
        return get_spec_by_x(UUID, uuid);
 
        bad_uuid:
-               DBG("Found an invalid UUID: %s", s);
+               DEBUG("Found an invalid UUID: %s", s);
        return NULL;
 }
 
@@ -353,12 +353,12 @@ static char *get_device_name(const char *optstr)
        }
        else if (strncmp (optstr, "UUID=", 5) == 0)
        {
-               DBG ("TODO: check UUID= code!");
+               DEBUG ("TODO: check UUID= code!");
                rc = get_spec_by_uuid (optstr + 5);
        }
        else if (strncmp (optstr, "LABEL=", 6) == 0)
        {
-               DBG ("TODO: check LABEL= code!");
+               DEBUG ("TODO: check LABEL= code!");
                rc = get_spec_by_volume_label (optstr + 6);
        }
        else
@@ -368,7 +368,7 @@ static char *get_device_name(const char *optstr)
 
        if(!rc)
        {
-               DBG ("Error checking device name: optstr = %s", optstr);
+               DEBUG ("Error checking device name: optstr = %s", optstr);
        }
        return rc;
 }
@@ -383,7 +383,9 @@ static cu_mount_t *cu_mount_listmntent (void)
 
        struct tabmntent *mntlist;
        if(listmntent(&mntlist, COLLECTD_MNTTAB, NULL, NULL) < 0) {
-               DBG("calling listmntent() failed: %s", strerror(errno));
+               char errbuf[1024];
+               DEBUG("calling listmntent() failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
        }
 
        for(p = mntlist; p; p = p->next) {
@@ -394,7 +396,7 @@ static cu_mount_t *cu_mount_listmntent (void)
                if(loop == NULL) {   /* no loop= mount */
                        device = get_device_name(mnt->mnt_fsname);
                        if(device == NULL) {
-                               DBG("can't get devicename for fs (%s) %s (%s)"
+                               DEBUG("can't get devicename for fs (%s) %s (%s)"
                                        ": ignored", mnt->mnt_type,
                                        mnt->mnt_dir, mnt->mnt_fsname);
                                continue;
@@ -452,7 +454,9 @@ static cu_mount_t *cu_mount_getfsstat (void)
        /* Get the number of mounted file systems */
        if ((bufsize = CMD_STATFS (NULL, 0, FLAGS_STATFS)) < 1)
        {
-               DBG ("getv?fsstat failed: %s", strerror (errno));
+               char errbuf[1024];
+               DEBUG ("getv?fsstat failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                return (NULL);
        }
 
@@ -465,7 +469,9 @@ static cu_mount_t *cu_mount_getfsstat (void)
         * manpage.. -octo */
        if ((num = CMD_STATFS (buf, bufsize * sizeof (STRUCT_STATFS), FLAGS_STATFS)) < 1)
        {
-               DBG ("getv?fsstat failed: %s", strerror (errno));
+               char errbuf[1024];
+               DEBUG ("getv?fsstat failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                free (buf);
                return (NULL);
        }
@@ -514,11 +520,13 @@ static cu_mount_t *cu_mount_gen_getmntent (void)
        cu_mount_t *last  = NULL;
        cu_mount_t *new   = NULL;
 
-       DBG ("(void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
+       DEBUG ("(void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
 
        if ((fp = fopen (COLLECTD_MNTTAB, "r")) == NULL)
        {
-               syslog (LOG_ERR, "fopen (%s): %s", COLLECTD_MNTTAB, strerror (errno));
+               char errbuf[1024];
+               ERROR ("fopen (%s): %s", COLLECTD_MNTTAB,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                return (NULL);
        }
 
@@ -569,11 +577,13 @@ static cu_mount_t *cu_mount_getmntent (void)
        cu_mount_t *last  = NULL;
        cu_mount_t *new   = NULL;
 
-       DBG ("(void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
+       DEBUG ("(void); COLLECTD_MNTTAB = %s", COLLECTD_MNTTAB);
 
        if ((fp = setmntent (COLLECTD_MNTTAB, "r")) == NULL)
        {
-               syslog (LOG_ERR, "setmntent (%s): %s", COLLECTD_MNTTAB, strerror (errno));
+               char errbuf[1024];
+               ERROR ("setmntent (%s): %s", COLLECTD_MNTTAB,
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
                return (NULL);
        }
 
@@ -591,7 +601,7 @@ static cu_mount_t *cu_mount_getmntent (void)
                new->device      = get_device_name (new->options);
                new->next        = NULL;
 
-               DBG ("new = {dir = %s, spec_device = %s, type = %s, options = %s, device = %s}",
+               DEBUG ("new = {dir = %s, spec_device = %s, type = %s, options = %s, device = %s}",
                                new->dir, new->spec_device, new->type, new->options, new->device);
 
                /* Append to list */
@@ -609,7 +619,7 @@ static cu_mount_t *cu_mount_getmntent (void)
 
        endmntent (fp);
 
-       DBG ("return (0x%p)", (void *) first);
+       DEBUG ("return (0x%p)", (void *) first);
 
        return (first);
 }
@@ -672,7 +682,7 @@ void cu_mount_freelist (cu_mount_t *list)
        cu_mount_t *this;
        cu_mount_t *next;
 
-       DBG ("(list = 0x%p)", (void *) list);
+       DEBUG ("(list = 0x%p)", (void *) list);
 
        for (this = list; this != NULL; this = next)
        {
index 3a77961..8d524e7 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/vserver.c
- * Copyright (C) 2006  Sebastian Harl
+ * Copyright (C) 2006,2007  Sebastian Harl
  *
  * 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; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
 #include "plugin.h"
 
 #include <dirent.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <syslog.h>
 #include <sys/types.h>
-#include <unistd.h>
 
 #define BUFSIZE 512
 
 # define VSERVER_HAVE_READ 0
 #endif /* defined(KERNEL_LINUX) */
 
-static char *rrd_unix  = "vserver-%s/traffic-unix.rrd";
-static char *rrd_inet  = "vserver-%s/traffic-inet.rrd";
-static char *rrd_inet6 = "vserver-%s/traffic-inet6.rrd";
-static char *rrd_other = "vserver-%s/traffic-other.rrd";
-static char *rrd_unspec        = "vserver-%s/traffic-unspec.rrd";
-
-static char *rrd_thread        = "vserver-%s/vs_threads.rrd";
-
-static char *rrd_load  = "vserver-%s/load.rrd";
-
-static char *rrd_procs = "vserver-%s/vs_processes.rrd";
-static char *rrd_memory        = "vserver-%s/vs_memory.rrd";
-
-/* 9223372036854775807 == LLONG_MAX */
-/* bytes transferred */
-static char *ds_def_unix[] =
-{
-       "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:failed:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       NULL
-};
-static int ds_num_unix = 3;
-
-static char *ds_def_inet[] =
-{
-       "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:failed:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       NULL
-};
-static int ds_num_inet = 3;
-
-static char *ds_def_inet6[] =
-{
-       "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:failed:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       NULL
-};
-static int ds_num_inet6 = 3;
-
-static char *ds_def_other[] =
-{
-       "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:failed:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       NULL
-};
-static int ds_num_other = 3;
-
-static char *ds_def_unspec[] =
-{
-       "DS:incoming:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:outgoing:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:failed:COUNTER:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       NULL
-};
-static int ds_num_unspec = 3;
-
-static char *ds_def_threads[] =
-{
-       "DS:total:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       "DS:running:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       "DS:uninterruptible:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       "DS:onhold:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       NULL
-};
-static int ds_num_threads = 4;
-
-static char *ds_def_load[] =
-{
-       "DS:shortterm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-       "DS:midterm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-       "DS:longterm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
-       NULL
-};
-static int ds_num_load = 3;
-
-static char *ds_def_procs[] =
-{
-       "DS:total:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
-       NULL
-};
-static int ds_num_procs = 1;
-
-/* 9223372036854775807 == LLONG_MAX */
-/* bytes */
-static char *ds_def_memory[] =
-{
-       "DS:vm:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:vml:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:rss:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       "DS:anon:GAUGE:"COLLECTD_HEARTBEAT":0:9223372036854775807",
-       NULL
-};
-static int ds_num_memory = 4;
-
+#if VSERVER_HAVE_READ
 static int pagesize = 0;
 
-static void vserver_init (void)
+static int vserver_init (void)
 {
        /* XXX Should we check for getpagesize () in configure?
         * What's the right thing to do, if there is no getpagesize ()? */
        pagesize = getpagesize ();
-       return;
-} /* static void vserver_init(void) */
-
-static void vserver_unix_write (char *host, char *inst, char *val)
-{
-       int  len;
-       char filename[BUFSIZE];
-
-       len = snprintf (filename, BUFSIZE, rrd_unix, inst);
-       if ((len > 0) && (len < BUFSIZE))
-               rrd_update_file (host, filename, val, ds_def_unix, ds_num_unix);
-       return;
-} /* static void vserver_unix_write(char *host, char *inst, char *val) */
-
-static void vserver_inet_write (char *host, char *inst, char *val)
-{
-       int  len;
-       char filename[BUFSIZE];
-
-       len = snprintf (filename, BUFSIZE, rrd_inet, inst);
-       if ((len > 0) && (len < BUFSIZE))
-               rrd_update_file (host, filename, val, ds_def_inet, ds_num_inet);
-       return;
-} /* static void vserver_inet_write(char *host, char *inst, char *val) */
-
-static void vserver_inet6_write (char *host, char *inst, char *val)
-{
-       int  len;
-       char filename[BUFSIZE];
-
-       len = snprintf (filename, BUFSIZE, rrd_inet6, inst);
-       if ((len > 0) && (len < BUFSIZE))
-               rrd_update_file (host, filename, val, ds_def_inet6, ds_num_inet6);
-       return;
-} /* static void vserver_inet6_write(char *host, char *inst, char *val) */
-
-static void vserver_other_write (char *host, char *inst, char *val)
-{
-       int  len;
-       char filename[BUFSIZE];
 
-       len = snprintf (filename, BUFSIZE, rrd_other, inst);
-       if ((len > 0) && (len < BUFSIZE))
-               rrd_update_file (host, filename, val, ds_def_other, ds_num_other);
-       return;
-} /* static void vserver_other_write(char *host, char *inst, char *val) */
+       return (0);
+} /* static void vserver_init(void) */
 
-static void vserver_unspec_write (char *host, char *inst, char *val)
+static void traffic_submit (const char *plugin_instance,
+               const char *type_instance, counter_t rx, counter_t tx)
 {
-       int  len;
-       char filename[BUFSIZE];
+       value_t values[2];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       len = snprintf (filename, BUFSIZE, rrd_unspec, inst);
-       if ((len > 0) && (len < BUFSIZE))
-               rrd_update_file (host, filename, val, ds_def_unspec, ds_num_unspec);
-       return;
-} /* static void vserver_unspec_write(char *host, char *inst, char *val) */
+       values[0].counter = rx;
+       values[1].counter = tx;
 
-static void vserver_threads_write (char *host, char *inst, char *val)
-{
-       int  len;
-       char filename[BUFSIZE];
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "vserver");
+       strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-       len = snprintf (filename, BUFSIZE, rrd_thread, inst);
-       if ((len > 0) && (len < BUFSIZE))
-               rrd_update_file (host, filename, val, ds_def_threads, ds_num_threads);
-       return;
-} /* static void vserver_threads_write(char *host, char *inst, char *val) */
+       plugin_dispatch_values ("if_octets", &vl);
+} /* void traffic_submit */
 
-static void vserver_load_write (char *host, char *inst, char *val)
+static void load_submit (const char *plugin_instance,
+               gauge_t snum, gauge_t mnum, gauge_t lnum)
 {
-       int  len;
-       char filename[BUFSIZE];
+       value_t values[3];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       len = snprintf (filename, BUFSIZE, rrd_load, inst);
-       if ((len > 0) && (len < BUFSIZE))
-               rrd_update_file (host, filename, val, ds_def_load, ds_num_load);
-       return;
-} /* static void vserver_load_write(char *host, char *inst, char *val) */
+       values[0].gauge = snum;
+       values[1].gauge = mnum;
+       values[2].gauge = lnum;
 
-static void vserver_procs_write (char *host, char *inst, char *val)
-{
-       int  len;
-       char filename[BUFSIZE];
-
-       len = snprintf (filename, BUFSIZE, rrd_procs, inst);
-       if ((len > 0) && (len < BUFSIZE))
-               rrd_update_file (host, filename, val, ds_def_procs, ds_num_procs);
-       return;
-} /* static void vserver_procs_write(char *host, char *inst, char *val) */
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "vserver");
+       strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
 
-static void vserver_memory_write (char *host, char *inst, char *val)
-{
-       int  len;
-       char filename[BUFSIZE];
+       plugin_dispatch_values ("load", &vl);
+}
 
-       len = snprintf (filename, BUFSIZE, rrd_memory, inst);
-       if ((len > 0) && (len < BUFSIZE))
-               rrd_update_file (host, filename, val, ds_def_memory, ds_num_memory);
-       return;
-} /* static void vserver_memory_write(char *host, char *inst, char *val) */
+static void submit_gauge (const char *plugin_instance, const char *type,
+               const char *type_instance, gauge_t value)
 
-#if VSERVER_HAVE_READ
-static void vserver_submit (char *inst, long long unix_in, long long unix_out, 
-               long long unix_failed, long long inet_in, long long inet_out, 
-               long long inet_failed, long long inet6_in, long long inet6_out, 
-               long long inet6_failed, long long other_in, long long other_out,
-               long long other_failed, long long unspec_in, long long unspec_out, 
-               long long unspec_failed, int t_total, int t_running, 
-               int t_uninterruptible, int t_onhold, double avg1, double avg5, 
-               double avg15, int p_total, long long vm, long long vml, long long rss, 
-               long long anon)
 {
-       int  len;
-       char buffer[BUFSIZE];
-
-       len = snprintf (buffer, BUFSIZE, 
-                       "N:%lld:%lld:%lld", unix_in, unix_out, unix_failed);
-
-       if ((len > 0) && (len < BUFSIZE))
-               plugin_submit ("vserver_unix", inst, buffer);
-
-
-       len = snprintf (buffer, BUFSIZE, 
-                       "N:%lld:%lld:%lld", inet_in, inet_out, inet_failed);
-
-       if ((len > 0) && (len < BUFSIZE))
-               plugin_submit ("vserver_inet", inst, buffer);
-
-
-       len = snprintf (buffer, BUFSIZE, 
-                       "N:%lld:%lld:%lld", inet6_in, inet6_out, inet6_failed);
-
-       if ((len > 0) && (len < BUFSIZE))
-               plugin_submit ("vserver_inet6", inst, buffer);
-
-
-       len = snprintf (buffer, BUFSIZE, 
-                       "N:%lld:%lld:%lld", other_in, other_out, other_failed);
-
-       if ((len > 0) && (len < BUFSIZE))
-               plugin_submit ("vserver_other", inst, buffer);
-
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       len = snprintf (buffer, BUFSIZE, 
-                       "N:%lld:%lld:%lld", unspec_in, unspec_out, unspec_failed);
+       values[0].gauge = value;
 
-       if ((len > 0) && (len < BUFSIZE))
-               plugin_submit ("vserver_unspec", inst, buffer);
+       vl.values = values;
+       vl.values_len = STATIC_ARRAY_SIZE (values);
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "vserver");
+       strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
+       strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
-
-       len = snprintf (buffer, BUFSIZE, "N:%d:%d:%d:%d",
-                       t_total, t_running, t_uninterruptible, t_onhold);
-
-       if ((len > 0) && (len < BUFSIZE))
-               plugin_submit ("vserver_threads", inst, buffer);
-
-
-       len = snprintf (buffer, BUFSIZE, "N:%.2f:%.2f:%.2f",
-                       avg1, avg5, avg15);
-
-       if ((len > 0) && (len < BUFSIZE))
-               plugin_submit ("vserver_load", inst, buffer);
-
-
-       len = snprintf (buffer, BUFSIZE, "N:%d",
-                       p_total);
-
-       if ((len > 0) && (len < BUFSIZE))
-               plugin_submit ("vserver_procs", inst, buffer);
-
-
-       len = snprintf (buffer, BUFSIZE, "N:%lld:%lld:%lld:%lld",
-                       vm, vml, rss, anon);
-
-       if ((len > 0) && (len < BUFSIZE))
-               plugin_submit ("vserver_memory", inst, buffer);
-       return;
-} /* static void vserver_submit() */
+       plugin_dispatch_values (type, &vl);
+} /* void submit_gauge */
 
 static inline long long __get_sock_bytes(const char *s)
 {
@@ -338,24 +119,22 @@ static inline long long __get_sock_bytes(const char *s)
        return atoll(s);
 }
 
-static void vserver_read (void)
+static int vserver_read (void)
 {
        DIR                     *proc;
        struct dirent   *dent; /* 42 */
 
-       static complain_t complain_obj;
-
        errno = 0;
        if (NULL == (proc = opendir (PROCDIR)))
        {
-               plugin_complain (LOG_ERR, &complain_obj, "vserver plugin: "
-                               "fopen (%s) failed: %s", PROCDIR, strerror (errno));
-               return;
+               char errbuf[1024];
+               ERROR ("vserver plugin: fopen (%s): %s", PROCDIR, 
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
-       plugin_relief (LOG_NOTICE, &complain_obj, "vserver plugin: "
-                       "fopen (%s) succeeded.", PROCDIR);
 
-       while (NULL != (dent = readdir (proc))) {
+       while (NULL != (dent = readdir (proc)))
+       {
                int  len;
                char file[BUFSIZE];
 
@@ -364,24 +143,11 @@ static void vserver_read (void)
 
                char *cols[4];
 
-               long long       unix_s[3]       = {-1, -1, -1};
-               long long       inet[3]         = {-1, -1, -1};
-               long long       inet6[3]        = {-1, -1, -1};
-               long long       other[3]        = {-1, -1, -1};
-               long long       unspec[3]       = {-1, -1, -1};
-               int                     threads[4]      = {-1, -1, -1, -1};
-               double          load[3]         = {-1, -1, -1};
-               /* Just to be consistent ;-) */
-               int                     procs[1]        = {-1};
-               long long       memory[4]       = {-1, -1, -1, -1};
-
                if (dent->d_name[0] == '.')
                        continue;
 
-               /* XXX This check is just the result of a trial-and-error test.
-                * I did not find any documentation describing the d_type field. */
-               if (!(dent->d_type & 0x4))
-                       /* This is not a directory */
+               /* This is not a directory */
+               if (dent->d_type != DT_DIR)
                        continue;
 
                /* socket message accounting */
@@ -389,146 +155,165 @@ static void vserver_read (void)
                if ((len < 0) || (len >= BUFSIZE))
                        continue;
 
-               if (NULL == (fh = fopen (file, "r"))) {
-                       syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
-                       continue;
+               if (NULL == (fh = fopen (file, "r")))
+               {
+                       char errbuf[1024];
+                       ERROR ("Cannot open '%s': %s", file,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                }
 
-               while (NULL != fgets (buffer, BUFSIZE, fh)) {
+               while ((fh != NULL) && (NULL != fgets (buffer, BUFSIZE, fh)))
+               {
+                       counter_t rx;
+                       counter_t tx;
+                       char *type_instance;
+
                        if (strsplit (buffer, cols, 4) < 4)
                                continue;
 
-                       if (0 == strcmp (cols[0], "UNIX:")) {
-                               unix_s[0] = __get_sock_bytes (cols[1]);
-                               unix_s[1] = __get_sock_bytes (cols[2]);
-                               unix_s[2] = __get_sock_bytes (cols[3]);
-                       }
-                       else if (0 == strcmp (cols[0], "INET:")) {
-                               inet[0] = __get_sock_bytes (cols[1]);
-                               inet[1] = __get_sock_bytes (cols[2]);
-                               inet[2] = __get_sock_bytes (cols[3]);
-                       }
-                       else if (0 == strcmp (cols[0], "INET6:")) {
-                               inet6[0] = __get_sock_bytes (cols[1]);
-                               inet6[1] = __get_sock_bytes (cols[2]);
-                               inet6[2] = __get_sock_bytes (cols[3]);
-                       }
-                       else if (0 == strcmp (cols[0], "OTHER:")) {
-                               other[0] = __get_sock_bytes (cols[1]);
-                               other[1] = __get_sock_bytes (cols[2]);
-                               other[2] = __get_sock_bytes (cols[3]);
-                       }
-                       else if (0 == strcmp (cols[0], "UNSPEC:")) {
-                               unspec[0] = __get_sock_bytes (cols[1]);
-                               unspec[1] = __get_sock_bytes (cols[2]);
-                               unspec[2] = __get_sock_bytes (cols[3]);
-                       }
-               }
+                       if (0 == strcmp (cols[0], "UNIX:"))
+                               type_instance = "unix";
+                       else if (0 == strcmp (cols[0], "INET:"))
+                               type_instance = "inet";
+                       else if (0 == strcmp (cols[0], "INET6:"))
+                               type_instance = "inet6";
+                       else if (0 == strcmp (cols[0], "OTHER:"))
+                               type_instance = "other";
+                       else if (0 == strcmp (cols[0], "UNSPEC:"))
+                               type_instance = "unspec";
+                       else
+                               continue;
 
-               fclose (fh);
+                       rx = __get_sock_bytes (cols[1]);
+                       tx = __get_sock_bytes (cols[2]);
+                       /* cols[3] == errors */
+
+                       traffic_submit (dent->d_name, type_instance, rx, tx);
+               } /* while (fgets) */
+
+               if (fh != NULL)
+               {
+                       fclose (fh);
+                       fh = NULL;
+               }
 
                /* thread information and load */
                len = snprintf (file, BUFSIZE, PROCDIR "/%s/cvirt", dent->d_name);
                if ((len < 0) || (len >= BUFSIZE))
                        continue;
 
-               if (NULL == (fh = fopen (file, "r"))) {
-                       syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
-                       continue;
+               if (NULL == (fh = fopen (file, "r")))
+               {
+                       char errbuf[1024];
+                       ERROR ("Cannot open '%s': %s", file,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                }
 
-               while (NULL != fgets (buffer, BUFSIZE, fh)) {
+               while ((fh != NULL) && (NULL != fgets (buffer, BUFSIZE, fh)))
+               {
                        int n = strsplit (buffer, cols, 4);
 
-                       if (2 == n) {
-                               if (0 == strcmp (cols[0], "nr_threads:")) {
-                                       threads[0] = atoi (cols[1]);
-                               }
-                               else if (0 == strcmp (cols[0], "nr_running:")) {
-                                       threads[1] = atoi (cols[1]);
-                               }
-                               else if (0 == strcmp (cols[0], "nr_unintr:")) {
-                                       threads[2] = atoi (cols[1]);
-                               }
-                               else if (0 == strcmp (cols[0], "nr_onhold:")) {
-                                       threads[3] = atoi (cols[1]);
-                               }
+                       if (2 == n)
+                       {
+                               char   *type_instance;
+                               gauge_t value;
+
+                               if (0 == strcmp (cols[0], "nr_threads:"))
+                                       type_instance = "total";
+                               else if (0 == strcmp (cols[0], "nr_running:"))
+                                       type_instance = "running";
+                               else if (0 == strcmp (cols[0], "nr_unintr:"))
+                                       type_instance = "uninterruptable";
+                               else if (0 == strcmp (cols[0], "nr_onhold:"))
+                                       type_instance = "onhold";
+                               else
+                                       continue;
+
+                               value = atof (cols[1]);
+                               submit_gauge (dent->d_name, "vs_threads", type_instance, value);
                        }
                        else if (4 == n) {
-                               if (0 == strcmp (cols[0], "loadavg:")) {
-                                       load[0] = atof (cols[1]);
-                                       load[1] = atof (cols[2]);
-                                       load[2] = atof (cols[3]);
+                               if (0 == strcmp (cols[0], "loadavg:"))
+                               {
+                                       gauge_t snum = atof (cols[1]);
+                                       gauge_t mnum = atof (cols[2]);
+                                       gauge_t lnum = atof (cols[3]);
+                                       load_submit (dent->d_name, snum, mnum, lnum);
                                }
                        }
-               }
+               } /* while (fgets) */
 
-               fclose (fh);
+               if (fh != NULL)
+               {
+                       fclose (fh);
+                       fh = NULL;
+               }
 
                /* processes and memory usage */
                len = snprintf (file, BUFSIZE, PROCDIR "/%s/limit", dent->d_name);
                if ((len < 0) || (len >= BUFSIZE))
                        continue;
 
-               if (NULL == (fh = fopen (file, "r"))) {
-                       syslog (LOG_ERR, "Cannot open '%s': %s", file, strerror (errno));
-                       continue;
+               if (NULL == (fh = fopen (file, "r")))
+               {
+                       char errbuf[1024];
+                       ERROR ("Cannot open '%s': %s", file,
+                                       sstrerror (errno, errbuf, sizeof (errbuf)));
                }
 
-               while (NULL != fgets (buffer, BUFSIZE, fh)) {
+               while ((fh != NULL) && (NULL != fgets (buffer, BUFSIZE, fh)))
+               {
+                       char *type = "vs_memory";
+                       char *type_instance;
+                       gauge_t value;
+
                        if (strsplit (buffer, cols, 2) < 2)
                                continue;
 
-                       if (0 == strcmp (cols[0], "PROC:")) {
-                               procs[0] = atoi (cols[1]);
+                       if (0 == strcmp (cols[0], "PROC:"))
+                       {
+                               type = "vs_processes";
+                               type_instance = "";
+                               value = atof (cols[1]);
                        }
-                       else if (0 == strcmp (cols[0], "VM:")) {
-                               memory[0] = atoll (cols[1]) * pagesize;
+                       else
+                       {
+                               if (0 == strcmp (cols[0], "VM:"))
+                                       type_instance = "vm";
+                               else if (0 == strcmp (cols[0], "VML:"))
+                                       type_instance = "vml";
+                               else if (0 == strcmp (cols[0], "RSS:"))
+                                       type_instance = "rss";
+                               else if (0 == strcmp (cols[0], "ANON:"))
+                                       type_instance = "anon";
+                               else
+                                       continue;
+
+                               value = atof (cols[1]) * pagesize;
                        }
-                       else if (0 == strcmp (cols[0], "VML:")) {
-                               memory[1] = atoll (cols[1]) * pagesize;
-                       }
-                       else if (0 == strcmp (cols[0], "RSS:")) {
-                               memory[2] = atoll (cols[1]) * pagesize;
-                       }
-                       else if (0 == strcmp (cols[0], "ANON:")) {
-                               memory[3] = atoll (cols[1]) * pagesize;
-                       }
-               }
-
-               fclose (fh);
 
-               /* XXX What to do in case of an error (i.e. some value is
-                * still -1)? */
+                       submit_gauge (dent->d_name, type, type_instance, value);
+               } /* while (fgets) */
 
-               vserver_submit (dent->d_name, unix_s[0], unix_s[1], unix_s[2], 
-                               inet[0], inet[1], inet[2], inet6[0], inet6[1], inet6[2], 
-                               other[0], other[1], other[2], unspec[0], unspec[1], unspec[2],
-                               threads[0], threads[1], threads[2], threads[3], load[0], 
-                               load[1], load[2], procs[0], memory[0], memory[1], memory[2], 
-                               memory[3]);
-       }
+               if (fh != NULL)
+               {
+                       fclose (fh);
+                       fh = NULL;
+               }
+       } /* while (readdir) */
 
        closedir (proc);
-       return;
-} /* static void vserver_read(void) */
-#else
-# define vserver_read NULL
+
+       return (0);
+} /* int vserver_read */
 #endif /* VSERVER_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, vserver_init, vserver_read, NULL);
-       plugin_register ("vserver_unix", NULL, NULL, vserver_unix_write);
-       plugin_register ("vserver_inet", NULL, NULL, vserver_inet_write);
-       plugin_register ("vserver_inet6", NULL, NULL, vserver_inet6_write);
-       plugin_register ("vserver_other", NULL, NULL, vserver_other_write);
-       plugin_register ("vserver_unspec", NULL, NULL, vserver_unspec_write);
-       plugin_register ("vserver_threads", NULL, NULL, vserver_threads_write);
-       plugin_register ("vserver_load", NULL, NULL, vserver_load_write);
-       plugin_register ("vserver_procs", NULL, NULL, vserver_procs_write);
-       plugin_register ("vserver_memory", NULL, NULL, vserver_memory_write);
-       return;
+#if VSERVER_HAVE_READ
+       plugin_register_init ("vserver", vserver_init);
+       plugin_register_read ("vserver", vserver_read);
+#endif /* VSERVER_HAVE_READ */
 } /* void module_register(void) */
 
 /* vim: set ts=4 sw=4 noexpandtab : */
index f0b9b01..81c968e 100644 (file)
@@ -1,11 +1,10 @@
 /**
  * collectd - src/wireless.c
- * Copyright (C) 2006  Florian octo Forster
+ * Copyright (C) 2006,2007  Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * 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
@@ -24,9 +23,6 @@
 #include "common.h"
 #include "plugin.h"
 
-#define MODULE_NAME "wireless"
-#define BUFSIZE 1024
-
 #if defined(KERNEL_LINUX)
 # define WIRELESS_HAVE_READ 1
 #else
 
 #define WIRELESS_PROC_FILE "/proc/net/wireless"
 
-static char *filename_template = "wireless-%s.rrd";
-
-static char *ds_def[] =
-{
-       "DS:quality:GAUGE:"COLLECTD_HEARTBEAT":0:U",
-       "DS:power:GAUGE:"COLLECTD_HEARTBEAT":U:0",
-       "DS:noise:GAUGE:"COLLECTD_HEARTBEAT":U:0",
-       NULL
-};
-static int ds_num = 3;
-
-#if WIRELESS_HAVE_READ
-static int proc_file_found = 0;
-#endif
-
-static void wireless_init (void)
-{
-#if WIRELESS_HAVE_READ
-       if (access (WIRELESS_PROC_FILE, R_OK) == 0)
-               proc_file_found = 1;
-       else
-               proc_file_found = 0;
-#endif
-
-       return;
-}
-
-static void wireless_write (char *host, char *inst, char *val)
-{
-       char file[BUFSIZE];
-       int status;
-
-       status = snprintf (file, BUFSIZE, filename_template, inst);
-       if (status < 1)
-               return;
-       else if (status >= BUFSIZE)
-               return;
-
-       rrd_update_file (host, file, val, ds_def, ds_num);
-}
-
 #if WIRELESS_HAVE_READ
 #if 0
 static double wireless_dbm_to_watt (double dbm)
@@ -93,27 +48,30 @@ static double wireless_dbm_to_watt (double dbm)
 }
 #endif
 
-static void wireless_submit (char *device,
-               double quality, double power, double noise)
+static void wireless_submit (const char *plugin_instance, const char *type,
+               double value)
 {
-       char buf[BUFSIZE];
-       int  status;
+       value_t values[1];
+       value_list_t vl = VALUE_LIST_INIT;
 
-       status = snprintf (buf, BUFSIZE, "%u:%f:%f:%f",
-                       (unsigned int) curtime,
-                       quality, power, noise);
-       if ((status < 1) || (status >= BUFSIZE))
-               return;
+       values[0].gauge = value;
 
-       plugin_submit (MODULE_NAME, device, buf);
-}
+       vl.values = values;
+       vl.values_len = 1;
+       vl.time = time (NULL);
+       strcpy (vl.host, hostname_g);
+       strcpy (vl.plugin, "wireless");
+       strncpy (vl.plugin_instance, plugin_instance,
+                       sizeof (vl.plugin_instance));
+
+       plugin_dispatch_values (type, &vl);
+} /* void wireless_submit */
 
-static void wireless_read (void)
+static int wireless_read (void)
 {
 #ifdef KERNEL_LINUX
-
        FILE *fh;
-       char buffer[BUFSIZE];
+       char buffer[1024];
 
        char   *device;
        double  quality;
@@ -123,19 +81,20 @@ static void wireless_read (void)
        char *fields[8];
        int   numfields;
 
+       int devices_found;
        int len;
 
-       if (!proc_file_found)
-               return;
-
        /* there are a variety of names for the wireless device */
        if ((fh = fopen (WIRELESS_PROC_FILE, "r")) == NULL)
        {
-               syslog (LOG_WARNING, "wireless: fopen: %s", strerror (errno));
-               return;
+               char errbuf[1024];
+               WARNING ("wireless: fopen: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
        }
 
-       while (fgets (buffer, BUFSIZE, fh) != NULL)
+       devices_found = 0;
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
                numfields = strsplit (buffer, fields, 8);
 
@@ -164,20 +123,28 @@ static void wireless_read (void)
                if (noise == 0.0)
                        noise = 1.0; /* noise <= 0 */
 
-               wireless_submit (device, quality, power, noise);
+               wireless_submit (device, "signal_quality", quality);
+               wireless_submit (device, "signal_power", power);
+               wireless_submit (device, "signal_noise", noise);
+
+               devices_found++;
        }
 
        fclose (fh);
+
+       /* If no wireless devices are present return an error, so the plugin
+        * code delays our read function. */
+       if (devices_found == 0)
+               return (-1);
 #endif /* KERNEL_LINUX */
-}
-#else
-# define wireless_read NULL
+
+       return (0);
+} /* int wireless_read */
 #endif /* WIRELESS_HAVE_READ */
 
 void module_register (void)
 {
-       plugin_register (MODULE_NAME, wireless_init, wireless_read, wireless_write);
-}
-
-#undef BUFSIZE
-#undef MODULE_NAME
+#if WIRELESS_HAVE_READ
+       plugin_register_read ("wireless", wireless_read);
+#endif /* WIRELESS_HAVE_READ */
+} /* void module_register */