Merge branch 'collectd-3.10'
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Thu, 21 Dec 2006 19:40:49 +0000 (20:40 +0100)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Thu, 21 Dec 2006 19:40:49 +0000 (20:40 +0100)
Conflicts:

ChangeLog

35 files changed:
AUTHORS
ChangeLog
NEWS [new file with mode: 0644]
README
collectd.spec
configure.in
contrib/SpamAssassin/Collectd.pm [new file with mode: 0644]
contrib/SpamAssassin/example.cf [new file with mode: 0644]
contrib/collection.cgi
contrib/init.d-rh7
debian/control
src/Makefile.am
src/apache.c
src/apcups.c
src/collectd.c
src/collectd.conf.in
src/collectd.conf.pod
src/collectd.h
src/collectd.pod
src/cpu.c
src/df.c
src/disk.c
src/dns.c [new file with mode: 0644]
src/email.c [new file with mode: 0644]
src/liboping/liboping.c
src/multimeter.c [new file with mode: 0644]
src/plugin.c
src/plugin.h
src/processes.c
src/sensors.c
src/utils_dns.c [new file with mode: 0644]
src/utils_dns.h [new file with mode: 0644]
src/utils_ignorelist.c [new file with mode: 0644]
src/utils_ignorelist.h [new file with mode: 0644]
src/vserver.c

diff --git a/AUTHORS b/AUTHORS
index 178cf5c..cde1d11 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,7 +4,7 @@ This package was written by:
 apcups plugin by:
   Anthony Gialluca <tonyabg at charter.net>
 
-cpufreq module by:
+cpufreq and multimeter module by:
   Peter Holik <peter at holik.at>
 
 hddtemp module by:
index 9238f77..2615eaf 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+pending, Version 3.11.0
+       * collectd: The new command line option `-P' makes it easier for
+         distributors to change the location of PID-files.
+       * collectd: The daemon shuts down faster now which makes it easier to
+         write init.d-scripts for it.
+       * apache plugin: Increase the buffersize to 16k, because the 4k buffer
+         caused problems every now and then.
+       * df plugin: New config options allow to ignore certain mountpoints,
+         filesystem types or devices.
+       * dns plugin: The new dns plugin uses `libpcap' to capture DNS traffic
+         and interprets it. It collects traffic as well as qtype, opcode and
+         rcode counts.
+       * email plugin: Sebastian Harl has contributed this plugin which
+         counts received mails in categories (e. g. ham, spam, virus), spam
+         score (as given by SpamAssassin) and check types.
+       * processes plugin: Collect detailed statistics for configured
+         processes, that's process and thread count, CPU usage, resident
+         segment size and pagefaults.
+       * multimeter plugin: Peter Holik contributed a new plugin which
+         queries multimeters.
+       * sensors plugin: Lubos Stanek has put much effort into improving this
+         plugin, including `extended naming', collection of voltage values
+         and the possibility to ignore certain values.
+
 2006-12-21, Version 3.10.4
        * Max Kellermann has identified a bug in the server routine: When
          opening a socket fails the daemon will (re)try opening the socket in
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
index 511683f..0fd3726 100644 (file)
--- a/README
+++ b/README
@@ -39,6 +39,12 @@ Features
     - Disk utilization
       (Sectors read/written, number of read/write actions, time spent doing IO)
 
+    - DNS traffic
+      (query types, response codes, opcodes and traffic)
+
+    - Email statistics
+      (count, traffic, spam scores and checks)
+
     - Harddisk temperatures
       (Uhm, yeah, temperature of harddisks that is ;)
 
@@ -65,7 +71,7 @@ Features
       (Number of running, sleeping, zombie, ... processes)
 
     - Sensors
-      (System temperatured and fan rotation speeds)
+      (lm_sensors voltages, temperatures and fan rotation speeds)
 
     - Serial
       (RX and TX of serial interfaces)
index e305021..387763c 100644 (file)
@@ -80,6 +80,7 @@ rm -rf $RPM_BUILD_ROOT
 %attr(0444,root,root) %{_libdir}/%{name}/hddtemp.so*
 %attr(0444,root,root) %{_libdir}/%{name}/load.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*
index 8054417..27c2978 100644 (file)
@@ -25,7 +25,7 @@ AC_SUBST(LIBLTDL)
 AC_LIBTOOL_DLOPEN
 AC_PROG_LIBTOOL
 #AC_PROG_RANLIB
-AC_CONFIG_SUBDIRS(libltdl src/libconfig)
+AC_CONFIG_SUBDIRS(libltdl)
 
 #
 # Checks for header files.
@@ -197,6 +197,9 @@ AC_CHECK_HEADERS(IOKit/storage/IOBlockStorageDriver.h)
 # For load module
 AC_CHECK_HEADERS(sys/loadavg.h)
 
+# For the processes plugin
+AC_CHECK_HEADERS(linux/config.h)
+
 # For the swap module
 AC_CHECK_HEADERS(sys/swap.h)
 
@@ -266,9 +269,30 @@ AC_CHECK_HEADERS(sys/vfstab.h)
 # For the swap plugin, FreeBSD
 AC_CHECK_HEADERS(kvm.h)
 
+# For the email plugin
+AC_CHECK_HEADERS(linux/un.h, [], [],
+[
+#if HAVE_SYS_SOCKET_H
+#      include <sys/socket.h>
+#endif
+])
+AC_CHECK_HEADERS(sys/un.h)
+AC_CHECK_HEADERS(grp.h)
+
 # For debugging interface (variable number of arguments)
 AC_CHECK_HEADERS(stdarg.h)
 
+# Regular expressions for the ignorelist.
+AC_CHECK_HEADERS(regex.h)
+
+# For the dns plugin
+AC_CHECK_HEADERS(arpa/nameser.h arpa/nameser_compat.h)
+
+AC_CHECK_HEADERS(net/if_arp.h)
+AC_CHECK_HEADERS(net/if_ppp.h)
+AC_CHECK_HEADERS(netinet/if_ether.h)
+AC_CHECK_HEADERS(netinet/udp.h)
+
 dnl Checking for libraries
 AC_CHECK_LIB(m, ext)
 
@@ -299,6 +323,9 @@ nanosleep_needs_rt="no"
 AC_CHECK_FUNCS(nanosleep, [], AC_CHECK_LIB(rt, nanosleep, [nanosleep_needs_rt="yes"], AC_MSG_ERROR(cannot find nanosleep)))
 AM_CONDITIONAL(BUILD_WITH_LIBRT, test "x$nanosleep_needs_rt" = "xyes")
 
+# Regular expressions for the ignorelist.
+AC_CHECK_FUNCS(regcomp regerror regexec regfree)
+
 # For cpu module
 AC_CHECK_FUNCS(sysctlbyname, [have_sysctlbyname="yes"], [have_sysctlbyname="no"])
 
@@ -483,31 +510,36 @@ AC_DEFINE_UNQUOTED(COLLECT_RRDTOOL, [$collect_rrdtool],
        [Wether or not to use rrdtool library])
 AM_CONDITIONAL(BUILD_WITH_RRDTOOL, test "x$with_rrdtool" = "xyes")
 
-#AC_ARG_WITH(pth, [AS_HELP_STRING([--with-pth=@<:@=PREFIX@:>@], [Path to pth (experimental).]),
-#[     if test "x$withval" != "xno" && test "x$withval" != "xyes"
-#      then
-#              LDFLAGS="$LDFLAGS -L$withval/lib"
-#              CPPFLAGS="$CPPFLAGS -I$withval/include"
-#              with_pth="yes"
-#      fi
-#], [with_pth="no"])
-#if test "x$with_pth" = "xyes"
-#then
-#      AC_CHECK_LIB(pth, pth_init,, [with_pth="no (libpth not found)"], [])
-#fi
-#if test "x$with_pth" = "xyes"
-#then
-#      AC_CHECK_HEADERS(pth.h,, [with_pth="no (pth.h not found)"])
-#fi
-#if test "x$with_pth" = "xyes"
-#then
-#      collect_pth=1
-#else
-#      collect_pth=0
-#fi
-#AC_DEFINE_UNQUOTED(COLLECT_PTH, [$collect_pth],
-#      [Wether or not to use pth (portable threads) library])
-#AM_CONDITIONAL(BUILD_WITH_PTH, test "x$with_pth" = "xyes")
+AC_ARG_WITH(libpthread, [AS_HELP_STRING([--with-libpthread=@<:@=PREFIX@:>@], [Path to libpthread.])],
+[      if test "x$withval" != "xno" -a "x$withval" != "xyes"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libpthread="yes"
+       else
+               if test "x$withval" = "xno"
+               then
+                       with_libpthread="no (disabled)"
+               fi
+       fi
+], [with_libpthread="yes"])
+if test "x$with_libpthread" = "xyes"
+then
+       AC_CHECK_LIB(pthread, pthread_create, [with_libpthread="yes"], [with_libpthread="no (libpthread not found)"], [])
+fi
+if test "x$with_libpthread" = "xyes"
+then
+       AC_CHECK_HEADERS(pthread.h,, [with_libpthread="no (pthread.h not found)"])
+fi
+if test "x$with_libpthread" = "xyes"
+then
+       collect_pthread=1
+else
+       collect_pthread=0
+fi
+AC_DEFINE_UNQUOTED(HAVE_LIBPTHREAD, [$collect_pthread],
+       [Wether or not to use pthread (POSIX threads) library])
+AM_CONDITIONAL(BUILD_WITH_LIBPTHREAD, test "x$with_libpthread" = "xyes")
 
 if test "$ac_system" = "Solaris"
 then
@@ -802,6 +834,42 @@ then
        with_liboping="yes (shipped version)"
 fi
 
+AC_ARG_WITH(libpcap, [AS_HELP_STRING([--with-libpcap@<:@=PREFIX@:>@], [Path to libpcap.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               LDFLAGS="$LDFLAGS -L$withval/lib"
+               CPPFLAGS="$CPPFLAGS -I$withval/include"
+               with_libpcap="yes"
+       fi
+],
+[
+       with_libpcap="yes"
+])
+if test "x$with_libpcap" = "xyes"
+then
+       AC_CHECK_LIB(pcap, pcap_open_live,
+       [
+               AC_DEFINE(HAVE_LIBPCAP, 1, [Define to 1 if you have the pcap library (-lpcap).])
+       ], [with_libpcap="no (libpcap not found)"])
+fi
+if test "x$with_libpcap" = "xyes"
+then
+       AC_CHECK_HEADERS(pcap.h,
+       [
+               AC_DEFINE(HAVE_PCAP_H, 1, [Define to 1 if you have the <pcap.h> header file.])
+       ], [with_libpcap="no (pcap.h not found)"])
+fi
+if test "x$with_libpcap" = "xyes"
+then
+       collect_libpcap=1
+else
+       collect_libpcap=0
+fi
+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
@@ -838,7 +906,6 @@ then
        AC_DEFINE_UNQUOTED(COLLECTD_HEARTBEAT, "$collectd_heartbeat", [Interval in which plugins are queried.])
 fi
 
-#
 # Check for enabled/disabled features
 #
 
@@ -918,10 +985,13 @@ 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([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([hddtemp],   [disable], [module], [hdd temperature statistics])
 AC_COLLECTD([load],      [disable], [module], [system load statistics])
 AC_COLLECTD([memory],    [disable], [module], [memory statistics])
+AC_COLLECTD([multimeter],[disable], [module], [multimeter statistics])
 AC_COLLECTD([mysql],     [disable], [module], [mysql statistics])
 AC_COLLECTD([nfs],       [disable], [module], [nfs statistics])
 AC_COLLECTD([ntpd],      [disable], [module], [nfs statistics])
@@ -950,6 +1020,8 @@ Configuration:
     libstatgrab . . . . $with_libstatgrab
     libkstat  . . . . . $with_kstat
     libmysql  . . . . . $with_libmysql
+    libpcap . . . . . . $with_libpcap
+    libpthread  . . . . $with_libpthread
 
   Features:
     debug . . . . . . . $enable_debug
@@ -966,9 +1038,12 @@ Configuration:
     cpufreq . . . . . . $enable_cpufreq
     df  . . . . . . . . $enable_df
     disk  . . . . . . . $enable_disk
+    dns . . . . . . . . $enable_dns
+    email . . . . . . . $enable_email
     hddtemp . . . . . . $enable_hddtemp
     load  . . . . . . . $enable_load
     memory  . . . . . . $enable_memory
+    multimeter  . . . . $enable_multimeter
     mysql . . . . . . . $enable_mysql
     nfs . . . . . . . . $enable_nfs
     ntpd  . . . . . . . $enable_ntpd
diff --git a/contrib/SpamAssassin/Collectd.pm b/contrib/SpamAssassin/Collectd.pm
new file mode 100644 (file)
index 0000000..c76c44d
--- /dev/null
@@ -0,0 +1,218 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+Collectd - plugin for filling collectd with stats 
+
+=head1 INSTALLATION
+
+Just copy Collectd.pm into your SpamAssassin Plugin path 
+(e.g /usr/share/perl5/Mail/SpamAssassin/Plugin/) and
+add a loadplugin call into your init.pre file. 
+
+=head1 SYNOPSIS
+
+  loadplugin    Mail::SpamAssassin::Plugin::Collectd
+
+=head1 USER SETTINGS
+
+=over 4
+
+=item collectd_socket [ socket path ]      (default: /tmp/.collectd-email)
+
+Where the collectd socket is
+
+=cut 
+
+=item collectd_buffersize [ size ] (default: 256) 
+
+the email plugin uses a fixed buffer, if a line exceeds this size
+it has to be continued in another line. (This is of course handled internally)
+If you have changed this setting please get it in sync with the SA Plugin
+config. 
+
+=cut 
+
+=item collectd_timeout [ sec ] (default: 2) 
+
+if sending data to to collectd takes too long the connection will be aborted. 
+
+=cut
+
+=item collectd_retries [ tries ] (default: 3)
+
+the collectd plugin uses a tread pool, if this is empty the connection fails,
+the SA Plugin then tries to reconnect. With this variable you can indicate how
+often it should try. 
+
+=cut
+
+=head1 DESCRIPTION
+
+This modules uses the email plugin of collectd from Sebastian Harl to
+collect statistical informations in rrd files to create some nice looking
+graphs with rrdtool. They communicate over a unix socket that the collectd
+plugin creates. The generated graphs will be placed in /var/lib/collectd/email
+
+=head1 AUTHOR
+
+Alexander Wirt <formorer@formorer.de>
+
+=head1 COPYRIGHT
+
+ Copyright 2006 Alexander Wirt <formorer@formorer.de> 
+ This program is free software; you can redistribute it and/or modify 
+ it under the the terms of either: 
+
+ a) the Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
+
+ or
+
+ b) the GPL (http://www.gnu.org/copyleft/gpl.html)  
+
+ use whatever you like more. 
+
+=cut
+
+package Mail::SpamAssassin::Plugin::Collectd;
+
+use Mail::SpamAssassin::Plugin;
+use Mail::SpamAssassin::Logger;
+use strict;
+use bytes; 
+use warnings;
+use Time::HiRes qw(usleep);
+use IO::Socket;
+
+use vars qw(@ISA);
+@ISA = qw(Mail::SpamAssassin::Plugin);
+
+sub new {
+    my ($class, $mailsa) = @_;
+
+    # the usual perlobj boilerplate to create a subclass object
+    $class = ref($class) || $class;
+    my $self = $class->SUPER::new($mailsa);
+    bless ($self, $class);
+
+    # register our config options
+    $self->set_config($mailsa->{conf});
+
+    # and return the new plugin object
+    return $self;
+}
+
+sub set_config {
+    my ($self, $conf) = @_;
+    my @cmds = ();
+
+    push (@cmds, {
+           setting => 'collectd_buffersize',
+           default => 256,
+           type =>
+           $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
+       });
+
+    push (@cmds, {
+           setting => 'collectd_socket', 
+           default => '/tmp/.collectd-email',
+           type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
+    });
+
+       push (@cmds, {
+                       setting => 'collectd_timeout',
+                       default => 2,
+                       type =>
+                       $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
+       });
+
+       push (@cmds, {
+                       setting => 'collectd_retries',
+                       default => 3,
+                       type =>
+                       $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC,
+       });
+
+
+    $conf->{parser}->register_commands(\@cmds);
+}
+
+sub check_end {
+    my ($self, $params) = @_;
+    my $message_status = $params->{permsgstatus};
+       #create  new connection to our socket
+       eval {
+               local $SIG{ALRM} = sub { die "Sending to collectd timed out.\n" }; # NB: \n required
+
+               #generate a timeout
+               alarm $self->{main}->{conf}->{collectd_timeout};
+
+               my $sock;
+               #try at least $self->{main}->{conf}->{collectd_retries} to get a
+               #connection
+               for (my $i = 0; $i < $self->{main}->{conf}->{collectd_retries} ; ++$i) {
+                       last if $sock = new IO::Socket::UNIX
+                               ($self->{main}->{conf}->{collectd_socket});
+                       #sleep a random value between 0 and 50 microsecs to try for a new
+                       #thread
+                       usleep(int(rand(50))); 
+               }
+
+               die("could not connect to " .
+                               $self->{main}->{conf}->{collectd_socket} . ": $! - collectd plugin disabled") unless $sock; 
+
+               $sock->autoflush(1);
+
+               my $score = $message_status->{score};
+               #get the size of the message 
+               my $body = $message_status->{msg}->{pristine_body};
+
+               my $len = length($body);
+
+               if ($message_status->{score} >= $self->{main}->{conf}->{required_score} ) {
+                       #hey we have spam
+                       print $sock "e:spam:$len\n";
+               } else {
+                       print $sock "e:ham:$len\n";
+               }
+               print $sock "s:$score\n";
+               my @tmp_array; 
+               my @tests = @{$message_status->{test_names_hit}};
+
+               my $buffersize = $self->{main}->{conf}->{collectd_buffersize}; 
+               dbg("collectd: buffersize: $buffersize"); 
+
+               while  (scalar(@tests) > 0) {
+               push (@tmp_array, pop(@tests)); 
+                       if (length(join(',', @tmp_array) . '\n') > $buffersize) {
+                               push (@tests, pop(@tmp_array)); 
+                                       if (length(join(',', @tmp_array) . '\n') > $buffersize or scalar(@tmp_array) == 0) {
+                                               dbg("collectd: this shouldn't happen. Do you have tests"
+                                                       ." with names that have more than ~ $buffersize Bytes?");
+                                               return 1; 
+                                       } else {
+                                               dbg ( "collectd: c:" . join(',', @tmp_array) . "\n" ); 
+                                               print $sock "c:" . join(',', @tmp_array) . "\n"; 
+                                               #clean the array
+                                               @tmp_array = ();
+                                       } 
+                       } elsif ( scalar(@tests) == 0 ) {
+                               dbg ( "collectd: c:" . join(',', @tmp_array) . '\n' );
+                               print $sock "c:" . join(',', @tmp_array) . "\n";
+                       }
+               }
+               close($sock); 
+               alarm 0; 
+       };
+       if ($@) {
+               my $message = $@; 
+               chomp($message); 
+               info("collectd: $message");
+               return -1; 
+       }
+}
+
+1;
+
+# vim: syntax=perl sw=4 ts=4 noet shiftround
diff --git a/contrib/SpamAssassin/example.cf b/contrib/SpamAssassin/example.cf
new file mode 100644 (file)
index 0000000..0abfb49
--- /dev/null
@@ -0,0 +1,5 @@
+collectd_buffersize 256
+collectd_socket /tmp/.collectd-email
+collectd_timeout 2
+collectd_retries 3 
+
index 098c702..5c8bd4d 100755 (executable)
@@ -247,13 +247,96 @@ our $GraphDefs;
                        '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#$HalfBlue",
+                       "AREA:temp_max#$HalfMagenta",
                        "AREA:temp_min#$Canvas",
-                       "LINE1:temp_avg#$FullBlue:RPM",
+                       "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,',
@@ -275,9 +358,9 @@ our $GraphDefs;
                        'DEF:temp_avg={file}:value:AVERAGE',
                        'DEF:temp_min={file}:value:MIN',
                        'DEF:temp_max={file}:value:MAX',
-                       "AREA:temp_max#$HalfBlue",
+                       "AREA:temp_max#$HalfRed",
                        "AREA:temp_min#$Canvas",
-                       "LINE1:temp_avg#$FullBlue:Temperature",
+                       "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,',
@@ -304,13 +387,13 @@ our $GraphDefs;
                        '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. %.0lf Total)\l',
+                       '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. %.0lf Total)\l'
+                       'GPRINT:rx_avg_sum:LAST:(ca. %4.0lf%s Total)\l'
                ],
                load => ['DEF:s_avg={file}:shortterm:AVERAGE',
                        'DEF:s_min={file}:shortterm:MIN',
@@ -618,6 +701,18 @@ our $GraphDefs;
                        '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",
@@ -712,6 +807,109 @@ our $GraphDefs;
                        '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',
@@ -760,9 +958,9 @@ our $GraphDefs;
                        'DEF:temp_avg={file}:value:AVERAGE',
                        'DEF:temp_min={file}:value:MIN',
                        'DEF:temp_max={file}:value:MAX',
-                       "AREA:temp_max#$HalfBlue",
+                       "AREA:temp_max#$HalfRed",
                        "AREA:temp_min#$Canvas",
-                       "LINE1:temp_avg#$FullBlue:Value",
+                       "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,',
@@ -839,6 +1037,18 @@ our $GraphDefs;
                         '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',
@@ -967,7 +1177,12 @@ our $GraphArgs =
        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'],
-       fanspeed => ['-t', '{host} fanspeed {inst}', '-v', 'rpm'],
+       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'],
@@ -981,9 +1196,16 @@ our $GraphArgs =
        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'],
@@ -992,7 +1214,8 @@ our $GraphArgs =
        time_dispersion => ['-t', 'NTPd time dispersion ({inst})', '-v', 'Seconds'],
        traffic => ['-t', '{host} {inst} traffic', '-v', 'Bit/s'],
        users => ['-t', '{host} users', '-v', 'Users'],
-       voltage => ['-t', '{host} voltage', '-v', 'Volts'],
+       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'],
@@ -1004,6 +1227,10 @@ our $GraphMulti =
        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,
@@ -1011,9 +1238,12 @@ our $GraphMulti =
        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
+       users => 1,
+       multimeter => 1
 };
 
 our @Info;
@@ -1189,6 +1419,114 @@ sub output_graph_ping
        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 = @_;
@@ -1275,6 +1613,93 @@ sub output_graph_mysql_handler
        return (@ret);
 }
 
+sub output_graph_named_qtype
+{
+       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/qtype-$inst.rrd:value:AVERAGE",
+                       "DEF:min_$i=$AbsDir/qtype-$inst.rrd:value:MIN",
+                       "DEF:max_$i=$AbsDir/qtype-$inst.rrd:value:MAX");
+       }
+
+       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++)
+       {
+               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");
+       }
+
+       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) > 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");
+       }
+
+       return (@ret);
+}
 sub output_graph
 {
        die unless (defined ($GraphDefs->{$Type}));
index 01b7213..c7fe270 100755 (executable)
@@ -42,6 +42,7 @@ case "$1" in
        ;;
   restart|reload)
        stop
+       sleep 1
        start
        ;;
   condrestart)
index 87d1f75..f9f8f3c 100644 (file)
@@ -133,4 +133,3 @@ Description: statistics collection daemon (development files)
  .
  This package contains the development files needed to create your own
  plugins.
-
index 46d1a69..6e7e339 100644 (file)
@@ -12,6 +12,7 @@ sbin_PROGRAMS = collectd
 collectd_SOURCES = collectd.c collectd.h \
                   utils_debug.c utils_debug.h \
                   utils_mount.c utils_mount.h \
+                  utils_ignorelist.c utils_ignorelist.h \
                   common.c common.h \
                   network.c network.h \
                   plugin.c plugin.h \
@@ -155,6 +156,31 @@ collectd_LDADD += "-dlopen" disk.la
 collectd_DEPENDENCIES += disk.la
 endif
 
+if BUILD_MODULE_DNS
+pkglib_LTLIBRARIES += dns.la
+dns_la_SOURCES = dns.c utils_dns.c utils_dns.h
+dns_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBPCAP
+dns_la_LDFLAGS += -lpcap
+endif
+if BUILD_WITH_LIBPTHREAD
+dns_la_LDFLAGS += -lpthread
+endif
+collectd_LDADD += "-dlopen" dns.la
+collectd_DEPENDENCIES += dns.la
+endif
+
+if BUILD_MODULE_EMAIL
+pkglib_LTLIBRARIES += email.la
+email_la_SOURCES = email.c
+email_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBPTHREAD
+email_la_LDFLAGS += -lpthread
+endif
+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
@@ -217,6 +243,14 @@ memory_la_LDFLAGS += -lstatgrab
 endif
 endif
 
+if BUILD_MODULE_MULTIMETER
+pkglib_LTLIBRARIES += multimeter.la
+multimeter_la_SOURCES = multimeter.c
+multimeter_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" multimeter.la
+collectd_DEPENDENCIES += multimeter.la
+endif
+
 if BUILD_MODULE_MYSQL
 pkglib_LTLIBRARIES += mysql.la
 mysql_la_SOURCES = mysql.c
@@ -389,7 +423,7 @@ install-exec-hook:
        $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
        if test -e $(DESTDIR)$(sysconfdir)/collectd.conf; \
        then \
-               $(INSTALL_DATA) collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf.pkg-orig; \
+               $(INSTALL) -m 0640 collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf.pkg-orig; \
        else \
-               $(INSTALL_DATA) collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
+               $(INSTALL) -m 0640 collectd.conf $(DESTDIR)$(sysconfdir)/collectd.conf; \
        fi
index 156ad5d..530481b 100644 (file)
@@ -42,7 +42,8 @@ static char *cacert = NULL;
 #if HAVE_LIBCURL
 static CURL *curl = NULL;
 
-static char apache_buffer[4096];
+#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 */
@@ -89,9 +90,9 @@ static size_t apache_curl_callback (void *buf, size_t size, size_t nmemb, void *
 {
        size_t len = size * nmemb;
 
-       if ((apache_buffer_len + len) >= 4096)
+       if ((apache_buffer_len + len) >= ABUFFER_SIZE)
        {
-               len = 4095 - apache_buffer_len;
+               len = (ABUFFER_SIZE - 1) - apache_buffer_len;
        }
 
        if (len <= 0)
index f7486f5..cd64426 100644 (file)
@@ -301,7 +301,7 @@ static int apc_query_server (char *host, int port,
        double  value;
 
        static int sockfd   = -1;
-       static unsigned int complain = 0;
+       static complain_t compl;
 
 #if APCMAIN
 # define PRINT_VALUE(name, val) printf("  Found property: name = %s; value = %f;\n", name, val)
@@ -313,19 +313,14 @@ static int apc_query_server (char *host, int port,
        {
                if ((sockfd = net_open (host, NULL, port)) < 0)
                {
-                       /* Complain once every six hours. */
-                       int complain_step = 21600 / atoi (COLLECTD_STEP);
-
-                       if ((complain % complain_step) == 0)
-                               syslog (LOG_ERR, "apcups plugin: Connecting to the apcupsd failed.");
-                       complain++;
-
+                       plugin_complain (LOG_ERR, &compl, "apcups plugin: "
+                                       "Connecting to the apcupsd failed.");
                        return (-1);
                }
-               else if (complain > 1)
+               else
                {
-                       syslog (LOG_NOTICE, "apcups plugin: Connection re-established to the apcupsd.");
-                       complain = 0;
+                       plugin_relief (LOG_NOTICE, &compl, "apcups plugin: "
+                                       "Connection re-established to the apcupsd.");
                }
        }
 
index c9385d9..a93cc28 100644 (file)
@@ -123,6 +123,8 @@ static void exit_usage (char *name)
                        "  General:\n"
                        "    -C <file>       Configuration file.\n"
                        "                    Default: "CONFIGFILE"\n"
+                       "    -P <file>       PID-file.\n"
+                       "                    Default: "PIDFILE"\n"
 #if COLLECT_DAEMON
                        "    -f              Don't fork to the background.\n"
 #endif
@@ -191,7 +193,7 @@ static int start_client (void)
                curtime = time (NULL);
 
                /* Issue all plugins */
-               plugin_read_all ();
+               plugin_read_all (&loop);
 
                if (gettimeofday (&tv_now, NULL) < 0)
                {
@@ -205,7 +207,7 @@ static int start_client (void)
                        continue;
                }
 
-               while (nanosleep (&ts_wait, &ts_wait) == -1)
+               while ((loop == 0) && (nanosleep (&ts_wait, &ts_wait) == -1))
                {
                        if (errno != EINTR)
                        {
@@ -292,7 +294,7 @@ int main (int argc, char **argv)
        char *configfile = CONFIGFILE;
 #if COLLECT_DAEMON
        struct sigaction sigChldAction;
-       char *pidfile    = PIDFILE;
+       char *pidfile    = NULL;
        pid_t pid;
        int daemonize    = 1;
 #endif
@@ -316,7 +318,7 @@ int main (int argc, char **argv)
 
                c = getopt (argc, argv, "hC:"
 #if COLLECT_DAEMON
-                               "f"
+                               "fP:"
 #endif
                );
 
@@ -329,6 +331,9 @@ int main (int argc, char **argv)
                                configfile = optarg;
                                break;
 #if COLLECT_DAEMON
+                       case 'P':
+                               pidfile = optarg;
+                               break;
                        case 'f':
                                daemonize = 0;
                                break;
@@ -379,7 +384,8 @@ int main (int argc, char **argv)
        sigChldAction.sa_handler = SIG_IGN;
        sigaction (SIGCHLD, &sigChldAction, NULL);
 
-       if ((pidfile = cf_get_option ("PIDFile", PIDFILE)) == NULL)
+       if ((pidfile == NULL)
+                       && ((pidfile = cf_get_option ("PIDFile", PIDFILE)) == NULL))
        {
                fprintf (stderr, "Cannot obtain pidfile. This shoud not happen. Ever.");
                return (1);
@@ -449,6 +455,8 @@ int main (int argc, char **argv)
 #endif
                start_client ();
 
+       plugin_shutdown_all ();
+
 #if COLLECT_DEBUG
        if (logfile != NULL)
                DBG_STOPFILE("debug file closed.");
index d733a6f..b73278f 100644 (file)
 @BUILD_MODULE_CPUFREQ_TRUE@LoadPlugin cpufreq
 @BUILD_MODULE_DF_TRUE@LoadPlugin df
 @BUILD_MODULE_DISK_TRUE@LoadPlugin disk
+@BUILD_MODULE_DNS_TRUE@LoadPlugin dns
 @BUILD_MODULE_HDDTEMP_TRUE@LoadPlugin hddtemp
 @BUILD_MODULE_LOAD_TRUE@LoadPlugin load
 @BUILD_MODULE_MEMORY_TRUE@LoadPlugin memory
+@BUILD_MODULE_MULTIMETER_TRUE@LoadPlugin multimeter
 @BUILD_MODULE_MYSQL_TRUE@LoadPlugin mysql
 @BUILD_MODULE_NFS_TRUE@LoadPlugin nfs
 @BUILD_MODULE_NTPD_TRUE@LoadPlugin ntpd
 #      Port 3551
 #</Plugin>
 
+#<Plugin dns>
+#      Interface eth0
+#      IgnoreSource 192.168.0.1
+#</Plugin>
+
 #<Plugin hddtemp>
 #      Host 127.0.0.1
 #      Port 7634
 #      Host host.foo.bar
 #</Plugin>
 
+#<Plugin processes>
+#      Process name
+#</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
+#
+#      IgnoreSelected false
+#</Plugin>
+
 #<Plugin traffic>
 #      Interface eth0
 #      IgnoreSelected false
index f3e2585..aa4799a 100644 (file)
@@ -49,7 +49,8 @@ will be mostly useless. The names of the plugins are listed in L<collectd(1)>.
 =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. Available in B<all modes>.
+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>
 
@@ -159,6 +160,72 @@ TCP-Port to connect to. Defaults to B<3551>.
 
 =back
 
+=head2 Plugin C<df>
+
+=over 4
+
+=item B<Device> I<Device>
+
+Select partitions based on the devicename.
+
+=item B<MountPoint> I<Directory>
+
+Select partitions based on the mountpoint.
+
+=item B<FSType> I<FSType>
+
+Select partitions based on the filesystem type.
+
+=item B<IgnoreSelected> I<true>|I<false>
+
+Invert the selection: If set to true, all partitions B<except> the ones that
+match any one of the criteria are collected. By default only selected
+partitions are collected if a selection is made. If no selection is conifured
+at all, B<all> partitions are selected.
+
+=back
+
+=head2 Plugin C<dns>
+
+=over 4
+
+=item B<Interface> I<Interface>
+
+The dns plugin uses B<libpcap> to capture dns traffic and analyses it. This
+option sets the interface that should be used. If this option is not set, or
+set to "any", the plugin will try to get packets from B<all> interfaces. This
+may not work on certain platforms, such as MacE<nbsp>OSE<nbsp>X.
+
+=item B<IgnoreSource> I<IP-address>
+
+Ignore packets that originate from this address.
+
+=back
+
+=head2 Plugin C<email>
+
+=over 4
+
+=item B<SocketGroup> I<Group>
+
+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>.
+
+=item B<MaxConns> I<Number>
+
+Sets the maximum number of connections that can be handled in parallel. Since
+this many threads will be started immediately setting this to a very high
+value will waste valuable resources. Defaults to B<5> and will be forced to be
+at most B<16384> to prevent typos and dumb mistakes.
+
+=back
+
 =head2 Plugin C<hddtemp>
 
 =over 4
@@ -225,6 +292,68 @@ Sets the Time-To-Live of generated ICMP packets.
 
 =back
 
+=head2 Plugin C<sensors>
+
+=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<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.
+
+back
+
+=back
+
 =head2 Plugin C<traffic>
 
 =over 4
index 4d75db4..2ce0a15 100644 (file)
 #define MODE_LOCAL  0x04
 #define MODE_LOG    0x08
 
+#ifndef COLLECTD_GRP_NAME
+# define COLLECTD_GRP_NAME "collectd"
+#endif
+
 #ifndef COLLECTD_STEP
 #  define COLLECTD_STEP "10"
 #endif
index 26185ee..f5ed782 100644 (file)
@@ -40,6 +40,14 @@ Disk and partition usage/throughput (I<disk>)
 
 =item
 
+DNS traffic (I<dns>)
+
+=item
+
+Email usage (I<email>)
+
+=item
+
 Harddisk temperatures (I<hddtemp>)
 
 =item
@@ -110,6 +118,13 @@ 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<-P> I<E<lt>pid-fileE<gt>>
+
+Specify an alternative pid file. This overwrites any settings in the config 
+file. This is thought for init-scripts that require the PID-file in a certain
+directory to work correctly. For everyday-usage use the B<PIDFile>
+config-option.
+
 =item B<-f>
 
 Don't fork to the background. I<collectd> will also B<not> close standard file
@@ -173,6 +188,27 @@ 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>
+
+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.
+
 =head2 mysql
 
 Requires B<mysqlclient> to be installed. It connects to the database when
@@ -319,6 +355,14 @@ The DS'es depend on the module creating the RRD files:
   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
@@ -414,15 +458,31 @@ The DS'es depend on the module creating the RRD files:
   DS:paging:GAUGE:HEARTBEAT:0:65535
   DS:blocked:GAUGE:HEARTBEAT:0:65535
 
-=item lm_sensors (F<sensors-I<E<lt>chipE<gt>>-I<E<lt>featureE<gt>>.rrd>)
+=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
index ca78294..b087d66 100644 (file)
--- a/src/cpu.c
+++ b/src/cpu.c
@@ -312,12 +312,19 @@ static void cpu_read (void)
        char *fields[9];
        int numfields;
 
+       static complain_t complain_obj;
+
        if ((fh = fopen ("/proc/stat", "r")) == NULL)
        {
-               syslog (LOG_WARNING, "cpu: fopen: %s", strerror (errno));
+               plugin_complain (LOG_ERR, &complain_obj, "cpu plugin: "
+                               "fopen (/proc/stat) failed: %s",
+                               strerror (errno));
                return;
        }
 
+       plugin_relief (LOG_NOTICE, &complain_obj, "cpu plugin: "
+                       "fopen (/proc/stat) succeeded.");
+
        while (fgets (buf, BUFSIZE, fh) != NULL)
        {
                if (strncmp (buf, "cpu", 3))
@@ -384,14 +391,21 @@ static void cpu_read (void)
        long cpuinfo[CPUSTATES];
        size_t cpuinfo_size;
 
+       static complain_t complain_obj;
+
        cpuinfo_size = sizeof (cpuinfo);
 
        if (sysctlbyname("kern.cp_time", &cpuinfo, &cpuinfo_size, NULL, 0) < 0)
        {
-               syslog (LOG_WARNING, "cpu: sysctlbyname: %s", strerror (errno));
+               plugin_complain (LOG_ERR, &complain_obj, "cpu plugin: "
+                               "sysctlbyname failed: %s.",
+                               strerror (errno));
                return;
        }
 
+       plugin_relief (LOG_NOTICE, &complain_obj, "cpu plugin: "
+                       "sysctlbyname succeeded.");
+
        cpuinfo[CP_SYS] += cpuinfo[CP_INTR];
 
        /* FIXME: Instance is always `0' */
index b88ed7d..d327164 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -23,7 +23,9 @@
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
+#include "configfile.h"
 #include "utils_mount.h"
+#include "utils_ignorelist.h"
 
 #define MODULE_NAME "df"
 
@@ -57,13 +59,78 @@ static char *ds_def[] =
 };
 static int ds_num = 2;
 
+static char *config_keys[] =
+{
+       "Device",
+       "MountPoint",
+       "FSType",
+       "IgnoreSelected",
+       NULL
+};
+static int config_keys_num = 4;
+
+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)
 {
+       if (il_device == NULL)
+               il_device = ignorelist_create (1);
+       if (il_mountpoint == NULL)
+               il_mountpoint = ignorelist_create (1);
+       if (il_fstype == NULL)
+               il_fstype = ignorelist_create (1);
+
        return;
 }
 
+static int df_config (char *key, char *value)
+{
+       df_init ();
+
+       if (strcasecmp (key, "Device") == 0)
+       {
+               if (ignorelist_add (il_device, value))
+                       return (1);
+               return (0);
+       }
+       else if (strcasecmp (key, "MountPoint") == 0)
+       {
+               if (ignorelist_add (il_mountpoint, value))
+                       return (1);
+               return (0);
+       }
+       else if (strcasecmp (key, "FSType") == 0)
+       {
+               if (ignorelist_add (il_fstype, value))
+                       return (1);
+               return (0);
+       }
+       else if (strcasecmp (key, "IgnoreSelected") == 0)
+       {
+               if ((strcasecmp (value, "True") == 0)
+                               || (strcasecmp (value, "Yes") == 0)
+                               || (strcasecmp (value, "On") == 0))
+               {
+                       ignorelist_set_invert (il_device, 0);
+                       ignorelist_set_invert (il_mountpoint, 0);
+                       ignorelist_set_invert (il_fstype, 0);
+               }
+               else
+               {
+                       ignorelist_set_invert (il_device, 1);
+                       ignorelist_set_invert (il_mountpoint, 1);
+                       ignorelist_set_invert (il_fstype, 1);
+               }
+               return (0);
+       }
+
+       return (-1);
+}
+
 static void df_write (char *host, char *inst, char *val)
 {
        char file[BUFSIZE];
@@ -143,6 +210,16 @@ static void df_read (void)
                                        mnt_name[i] = '-';
                }
 
+               if (ignorelist_match (il_device,
+                                       (mnt_ptr->spec_device != NULL)
+                                       ? mnt_ptr->spec_device
+                                       : mnt_ptr->device))
+                       continue;
+               if (ignorelist_match (il_mountpoint, mnt_ptr->dir))
+                       continue;
+               if (ignorelist_match (il_fstype, mnt_ptr->type))
+                       continue;
+
                df_submit (mnt_name, df_used, df_free);
        }
 
@@ -155,6 +232,7 @@ static void df_read (void)
 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
index ca3531e..c809fdb 100644 (file)
@@ -315,14 +315,22 @@ static void disk_read (void)
        int  disk_minor;
        char disk_name[64];
 
+       static complain_t complain_obj;
+
        /* Get the list of all disk objects. */
        if (IOServiceGetMatchingServices (io_master_port,
                                IOServiceMatching (kIOBlockStorageDriverClass),
                                &disk_list) != kIOReturnSuccess)
        {
-               syslog (LOG_ERR, "disk-plugin: IOServiceGetMatchingServices failed.");
+               plugin_complain (LOG_ERR, &complain_obj, "disk plugin: "
+                               "IOServiceGetMatchingServices failed.");
                return;
        }
+       else if (complain_obj.interval != 0)
+       {
+               plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: "
+                               "IOServiceGetMatchingServices succeeded.");
+       }
 
        while ((disk = IOIteratorNext (disk_list)) != 0)
        {
@@ -460,15 +468,22 @@ static void disk_read (void)
 
        diskstats_t *ds, *pre_ds;
 
+       static complain_t complain_obj;
+
        if ((fh = fopen ("/proc/diskstats", "r")) == NULL)
        {
                if ((fh = fopen ("/proc/partitions", "r")) == NULL)
+               {
+                       plugin_complain (LOG_ERR, &complain_obj, "disk plugin: Failed to open /proc/{diskstats,partitions}.");
                        return;
+               }
 
                /* Kernel is 2.4.* */
                fieldshift = 1;
        }
 
+       plugin_relief (LOG_NOTICE, &complain_obj, "disk plugin: Succeeded to open /proc/{diskstats,partitions}.");
+
        while (fgets (buffer, 1024, fh) != NULL)
        {
                numfields = strsplit (buffer, fields, 32);
diff --git a/src/dns.c b/src/dns.c
new file mode 100644 (file)
index 0000000..cd068fb
--- /dev/null
+++ b/src/dns.c
@@ -0,0 +1,530 @@
+/**
+ * collectd - src/dns.c
+ * Copyright (C) 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>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+#include "utils_debug.h"
+#include "utils_dns.h"
+
+#define MODULE_NAME "dns"
+
+#if HAVE_LIBPCAP && HAVE_LIBPTHREAD
+# include <pthread.h>
+# include <pcap.h>
+# include <sys/poll.h>
+# define DNS_HAVE_READ 1
+#else
+# define DNS_HAVE_READ 0
+#endif
+
+/*
+ * Private data types
+ */
+#if DNS_HAVE_READ
+struct counter_list_s
+{
+       unsigned int key;
+       unsigned int value;
+       struct counter_list_s *next;
+};
+typedef struct counter_list_s counter_list_t;
+#endif
+
+/*
+ * 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[] =
+{
+       "Interface",
+       "IgnoreSource",
+       NULL
+};
+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_list_t *qtype_list;
+static counter_list_t *opcode_list;
+static counter_list_t *rcode_list;
+
+static pthread_t       listen_thread;
+static int             listen_thread_init = 0;
+/* The `traffic' mutex if for `tr_queries' and `tr_responses' */
+static pthread_mutex_t traffic_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t qtype_mutex   = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t opcode_mutex  = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t rcode_mutex   = PTHREAD_MUTEX_INITIALIZER;
+#endif /* DNS_HAVE_READ */
+
+/*
+ * Private functions
+ */
+#if DNS_HAVE_READ
+static counter_list_t *counter_list_search (counter_list_t **list, unsigned int key)
+{
+       counter_list_t *entry;
+
+       DBG ("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);
+       return (entry);
+}
+
+static counter_list_t *counter_list_create (counter_list_t **list,
+               unsigned int key, unsigned int value)
+{
+       counter_list_t *entry;
+
+       DBG ("counter_list_create (list = %p, key = %u, value = %u)",
+                       (void *) *list, key, value);
+
+       entry = (counter_list_t *) malloc (sizeof (counter_list_t));
+       if (entry == NULL)
+               return (NULL);
+
+       memset (entry, 0, sizeof (counter_list_t));
+       entry->key = key;
+       entry->value = value;
+
+       if (*list == NULL)
+       {
+               *list = entry;
+       }
+       else
+       {
+               counter_list_t *last;
+
+               last = *list;
+               while (last->next != NULL)
+                       last = last->next;
+
+               last->next = entry;
+       }
+
+       DBG ("return (%p)", (void *) entry);
+       return (entry);
+}
+
+static void counter_list_add (counter_list_t **list,
+               unsigned int key, unsigned int increment)
+{
+       counter_list_t *entry;
+
+       DBG ("counter_list_add (list = %p, key = %u, increment = %u)",
+                       (void *) *list, key, increment);
+
+       entry = counter_list_search (list, key);
+
+       if (entry != NULL)
+       {
+               entry->value += increment;
+       }
+       else
+       {
+               counter_list_create (list, key, increment);
+       }
+       DBG ("return ()");
+}
+
+static int dns_config (char *key, char *value)
+{
+       if (strcasecmp (key, "Interface") == 0)
+       {
+               if (pcap_device != NULL)
+                       free (pcap_device);
+               if ((pcap_device = strdup (value)) == NULL)
+                       return (1);
+       }
+       else if (strcasecmp (key, "IgnoreSource") == 0)
+       {
+               if (value != NULL)
+                       ignore_list_add_name (value);
+       }
+       else
+       {
+               return (-1);
+       }
+
+       return (0);
+}
+
+static void dns_child_callback (const rfc1035_header_t *dns)
+{
+       if (dns->qr == 0)
+       {
+               /* This is a query */
+               pthread_mutex_lock (&traffic_mutex);
+               tr_queries += dns->length;
+               pthread_mutex_unlock (&traffic_mutex);
+
+               pthread_mutex_lock (&qtype_mutex);
+               counter_list_add (&qtype_list,  dns->qtype,  1);
+               pthread_mutex_unlock (&qtype_mutex);
+       }
+       else
+       {
+               /* This is a reply */
+               pthread_mutex_lock (&traffic_mutex);
+               tr_responses += dns->length;
+               pthread_mutex_unlock (&traffic_mutex);
+
+               pthread_mutex_lock (&rcode_mutex);
+               counter_list_add (&rcode_list,  dns->rcode,  1);
+               pthread_mutex_unlock (&rcode_mutex);
+       }
+
+       /* FIXME: Are queries, replies or both interesting? */
+       pthread_mutex_lock (&opcode_mutex);
+       counter_list_add (&opcode_list, dns->opcode, 1);
+       pthread_mutex_unlock (&opcode_mutex);
+}
+
+static void *dns_child_loop (void *dummy)
+{
+       pcap_t *pcap_obj;
+       char    pcap_error[PCAP_ERRBUF_SIZE];
+       struct  bpf_program fp;
+
+       int status;
+
+       /* Don't block any signals */
+       {
+               sigset_t sigmask;
+               sigemptyset (&sigmask);
+               pthread_sigmask (SIG_SETMASK, &sigmask, NULL);
+       }
+
+       /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
+       DBG ("Creating PCAP object..");
+       pcap_obj = pcap_open_live (pcap_device,
+                       PCAP_SNAPLEN,
+                       0 /* Not promiscuous */,
+                       atoi (COLLECTD_STEP),
+                       pcap_error);
+       if (pcap_obj == NULL)
+       {
+               syslog (LOG_ERR, "dns plugin: Opening interface `%s' "
+                               "failed: %s",
+                               (pcap_device != NULL) ? pcap_device : "any",
+                               pcap_error);
+               return (NULL);
+       }
+
+       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");
+               return (NULL);
+       }
+       if (pcap_setfilter (pcap_obj, &fp) < 0)
+       {
+               DBG ("pcap_setfilter failed");
+               syslog (LOG_ERR, "dns plugin: pcap_setfilter failed");
+               return (NULL);
+       }
+
+       DBG ("PCAP object created.");
+
+       dnstop_set_pcap_obj (pcap_obj);
+       dnstop_set_callback (dns_child_callback);
+
+       status = pcap_loop (pcap_obj,
+                       -1 /* loop forever */,
+                       handle_pcap /* callback */,
+                       NULL /* Whatever this means.. */);
+       if (status < 0)
+               syslog (LOG_ERR, "dns plugin: Listener thread is exiting "
+                               "abnormally: %s", pcap_geterr (pcap_obj));
+
+       DBG ("child is exiting");
+
+       pcap_close (pcap_obj);
+       listen_thread_init = 0;
+       pthread_exit (NULL);
+
+       return (NULL);
+} /* static void dns_child_loop (void) */
+#endif /* DNS_HAVE_READ */
+
+static void dns_init (void)
+{
+#if DNS_HAVE_READ
+       /* clean up an old thread */
+       int status;
+
+       pthread_mutex_lock (&traffic_mutex);
+       tr_queries   = 0;
+       tr_responses = 0;
+       pthread_mutex_unlock (&traffic_mutex);
+
+       if (listen_thread_init != 0)
+               return;
+
+       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;
+       }
+
+       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);
+}
+
+static void rcode_submit (int rcode, unsigned int counter)
+{
+       char inst[32];
+       char buffer[32];
+       int  status;
+
+       strncpy (inst, rcode_str (rcode), 32);
+       inst[31] = '\0';
+
+       status = snprintf (buffer, 32, "N:%u", counter);
+       if ((status < 1) || (status >= 32))
+               return;
+
+       plugin_submit ("dns_rcode", inst, buffer);
+}
+
+static void opcode_submit (int opcode, unsigned int counter)
+{
+       char inst[32];
+       char buffer[32];
+       int  status;
+
+       strncpy (inst, opcode_str (opcode), 32);
+       inst[31] = '\0';
+
+       status = snprintf (buffer, 32, "N:%u", counter);
+       if ((status < 1) || (status >= 32))
+               return;
+
+       plugin_submit ("dns_opcode", inst, buffer);
+}
+
+static void dns_read (void)
+{
+       unsigned int keys[T_MAX];
+       unsigned int values[T_MAX];
+       int len;
+       int i;
+
+       counter_list_t *ptr;
+
+       pthread_mutex_lock (&traffic_mutex);
+       values[0] = tr_queries;
+       values[1] = tr_responses;
+       pthread_mutex_unlock (&traffic_mutex);
+       traffic_submit (values[0], values[1]);
+
+       pthread_mutex_lock (&qtype_mutex);
+       for (ptr = qtype_list, len = 0;
+                       (ptr != NULL) && (len < T_MAX);
+                       ptr = ptr->next, len++)
+       {
+               keys[len]   = ptr->key;
+               values[len] = ptr->value;
+       }
+       pthread_mutex_unlock (&qtype_mutex);
+
+       for (i = 0; i < len; i++)
+       {
+               DBG ("qtype = %u; counter = %u;", keys[i], values[i]);
+               qtype_submit (keys[i], values[i]);
+       }
+
+       pthread_mutex_lock (&opcode_mutex);
+       for (ptr = opcode_list, len = 0;
+                       (ptr != NULL) && (len < T_MAX);
+                       ptr = ptr->next, len++)
+       {
+               keys[len]   = ptr->key;
+               values[len] = ptr->value;
+       }
+       pthread_mutex_unlock (&opcode_mutex);
+
+       for (i = 0; i < len; i++)
+       {
+               DBG ("opcode = %u; counter = %u;", keys[i], values[i]);
+               opcode_submit (keys[i], values[i]);
+       }
+
+       pthread_mutex_lock (&rcode_mutex);
+       for (ptr = rcode_list, len = 0;
+                       (ptr != NULL) && (len < T_MAX);
+                       ptr = ptr->next, len++)
+       {
+               keys[len]   = ptr->key;
+               values[len] = ptr->value;
+       }
+       pthread_mutex_unlock (&rcode_mutex);
+
+       for (i = 0; i < len; i++)
+       {
+               DBG ("rcode = %u; counter = %u;", keys[i], values[i]);
+               rcode_submit (keys[i], values[i]);
+       }
+}
+#else /* if !DNS_HAVE_READ */
+# define dns_read NULL
+#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);
+#endif
+}
+
+#undef MODULE_NAME
diff --git a/src/email.c b/src/email.c
new file mode 100644 (file)
index 0000000..9bcd5bf
--- /dev/null
@@ -0,0 +1,842 @@
+/**
+ * collectd - src/email.c
+ * Copyright (C) 2006  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.
+ *
+ * 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 communicates with a spam filter, a virus scanner or similar
+ * software using a UNIX socket and a very simple protocol:
+ *
+ * e-mail type (e.g. ham, spam, virus, ...) and size
+ * e:<type>:<bytes>
+ *
+ * spam score
+ * s:<value>
+ *
+ * successful spam checks
+ * c:<type1>[,<type2>,...]
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#include "configfile.h"
+
+#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 */
+
+/* some systems (e.g. Darwin) seem to not define UNIX_PATH_MAX at all */
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof (((struct sockaddr_un *)0)->sun_path)
+#endif /* UNIX_PATH_MAX */
+
+#if HAVE_GRP_H
+#      include <grp.h>
+#endif /* HAVE_GRP_H */
+
+#define MODULE_NAME "email"
+
+/* 256 bytes ought to be enough for anybody ;-) */
+#define BUFSIZE 256
+
+#ifndef COLLECTD_SOCKET_PREFIX
+# define COLLECTD_SOCKET_PREFIX "/tmp/.collectd-"
+#endif /* COLLECTD_SOCKET_PREFIX */
+
+#define SOCK_PATH COLLECTD_SOCKET_PREFIX"email"
+#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__)
+
+/*
+ * Private data structures
+ */
+#if EMAIL_HAVE_READ
+/* linked list of email and check types */
+typedef struct type {
+       char        *name;
+       int         value;
+       struct type *next;
+} type_t;
+
+typedef struct {
+       type_t *head;
+       type_t *tail;
+} type_list_t;
+
+/* collector thread control information */
+typedef struct collector {
+       pthread_t thread;
+
+       /* socket descriptor of the current/last connection */
+       int socket;
+} collector_t;
+
+/* linked list of pending connections */
+typedef struct conn {
+       /* socket to read data from */
+       int socket;
+
+       /* buffer to read data to */
+       char *buffer;
+       int  idx; /* current write position in buffer */
+       int  length; /* length of the current line, i.e. index of '\0' */
+
+       struct conn *next;
+} conn_t;
+
+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[] =
+{
+       "SocketGroup",
+       "SocketPerms",
+       "MaxConns",
+       NULL
+};
+static int config_keys_num = 3;
+
+/* socket configuration */
+static char *sock_group = COLLECTD_GRP_NAME;
+static int  sock_perms  = S_IRWXU | S_IRWXG;
+static int  max_conns   = MAX_CONNS;
+
+/* state of the plugin */
+static int disabled = 0;
+
+/* thread managing "client" connections */
+static pthread_t connector;
+static int connector_socket;
+
+/* tell the collector threads that a new connection is available */
+static pthread_cond_t conn_available = PTHREAD_COND_INITIALIZER;
+
+/* connections that are waiting to be processed */
+static pthread_mutex_t conns_mutex = PTHREAD_MUTEX_INITIALIZER;
+static conn_list_t conns;
+
+/* tell the connector thread that a collector is available */
+static pthread_cond_t collector_available = PTHREAD_COND_INITIALIZER;
+
+/* collector threads */
+static collector_t **collectors;
+
+static pthread_mutex_t available_mutex = PTHREAD_MUTEX_INITIALIZER;
+static int available_collectors;
+
+static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
+static type_list_t count;
+
+static pthread_mutex_t size_mutex = PTHREAD_MUTEX_INITIALIZER;
+static type_list_t size;
+
+static pthread_mutex_t score_mutex = PTHREAD_MUTEX_INITIALIZER;
+static double score;
+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)
+{
+       if (0 == strcasecmp (key, "SocketGroup")) {
+               sock_group = sstrdup (value);
+       }
+       else if (0 == strcasecmp (key, "SocketPerms")) {
+               /* the user is responsible for providing reasonable values */
+               sock_perms = (int)strtol (value, NULL, 8);
+       }
+       else if (0 == strcasecmp (key, "MaxConns")) {
+               long int tmp = strtol (value, NULL, 0);
+
+               if (tmp < 1) {
+                       fprintf (stderr, "email plugin: `MaxConns' was set to invalid "
+                                       "value %li, will use default %i.\n",
+                                       tmp, MAX_CONNS);
+                       max_conns = MAX_CONNS;
+               }
+               else if (tmp > MAX_CONNS_LIMIT) {
+                       fprintf (stderr, "email plugin: `MaxConns' was set to invalid "
+                                       "value %li, will use hardcoded limit %i.\n",
+                                       tmp, MAX_CONNS_LIMIT);
+                       max_conns = MAX_CONNS_LIMIT;
+               }
+               else {
+                       max_conns = (int)tmp;
+               }
+       }
+       else {
+               return -1;
+       }
+       return 0;
+} /* static int email_config (char *, char *) */
+
+/* Increment the value of the given name in the given list by incr. */
+static void type_list_incr (type_list_t *list, char *name, int incr)
+{
+       if (NULL == list->head) {
+               list->head = (type_t *)smalloc (sizeof (type_t));
+
+               list->head->name  = sstrdup (name);
+               list->head->value = incr;
+               list->head->next  = NULL;
+
+               list->tail = list->head;
+       }
+       else {
+               type_t *ptr;
+
+               for (ptr = list->head; NULL != ptr; ptr = ptr->next) {
+                       if (0 == strcmp (name, ptr->name))
+                               break;
+               }
+
+               if (NULL == ptr) {
+                       list->tail->next = (type_t *)smalloc (sizeof (type_t));
+                       list->tail = list->tail->next;
+
+                       list->tail->name  = sstrdup (name);
+                       list->tail->value = incr;
+                       list->tail->next  = NULL;
+               }
+               else {
+                       ptr->value += incr;
+               }
+       }
+       return;
+} /* static void type_list_incr (type_list_t *, char *) */
+
+/* Read a single character from the socket. If an error occurs or end-of-file
+ * is reached return '\0'. */
+static char read_char (conn_t *src)
+{
+       char ret = '\0';
+
+       fd_set fdset;
+
+       FD_ZERO (&fdset);
+       FD_SET (src->socket, &fdset);
+
+       if (-1 == select (src->socket + 1, &fdset, NULL, NULL, NULL)) {
+               log_err ("select() failed: %s", strerror (errno));
+               return '\0';
+       }
+
+       assert (FD_ISSET (src->socket, &fdset));
+
+       do {
+               ssize_t len = 0;
+
+               errno = 0;
+               if (0 > (len = read (src->socket, (void *)&ret, 1))) {
+                       if (EINTR != errno) {
+                               log_err ("read() failed: %s", strerror (errno));
+                               return '\0';
+                       }
+               }
+
+               if (0 == len)
+                       return '\0';
+       } while (EINTR == errno);
+       return ret;
+} /* static char read_char (conn_t *) */
+
+/* Read a single line (terminated by '\n') from the the socket.
+ *
+ * The return value is zero terminated and does not contain any newline
+ * characters.
+ *
+ * If an error occurs or end-of-file is reached return NULL.
+ *
+ * IMPORTANT NOTE: If there is no newline character found in BUFSIZE
+ * characters of the input stream, the line will will be ignored! By
+ * definition we should not get any longer input lines, thus this is
+ * acceptable in this case ;-) */
+static char *read_line (conn_t *src)
+{
+       int i = 0;
+
+       assert ((BUFSIZE >= src->idx) && (src->idx >= 0));
+       assert ((src->idx > src->length) || (src->length == 0));
+
+       if (src->length > 0) { /* remove old line */
+               src->idx -= (src->length + 1);
+               memmove (src->buffer, src->buffer + src->length + 1, src->idx);
+               src->length = 0;
+       }
+
+       for (i = 0; i < src->idx; ++i) {
+               if ('\n' == src->buffer[i])
+                       break;
+       }
+
+       if (i == src->idx) {
+               fd_set fdset;
+
+               ssize_t len = 0;
+
+               FD_ZERO (&fdset);
+               FD_SET (src->socket, &fdset);
+
+               if (-1 == select (src->socket + 1, &fdset, NULL, NULL, NULL)) {
+                       log_err ("select() failed: %s", strerror (errno));
+                       return NULL;
+               }
+
+               assert (FD_ISSET (src->socket, &fdset));
+
+               do {
+                       errno = 0;
+                       if (0 > (len = read (src->socket,
+                                                       (void *)(src->buffer + src->idx),
+                                                       BUFSIZE - src->idx))) {
+                               if (EINTR != errno) {
+                                       log_err ("read() failed: %s", strerror (errno));
+                                       return NULL;
+                               }
+                       }
+
+                       if (0 == len)
+                               return NULL;
+               } while (EINTR == errno);
+
+               src->idx += len;
+
+               for (i = src->idx - len; i < src->idx; ++i) {
+                       if ('\n' == src->buffer[i])
+                               break;
+               }
+
+               if (i == src->idx) {
+                       src->length = 0;
+
+                       if (BUFSIZE == src->idx) { /* no space left in buffer */
+                               while ('\n' != read_char (src))
+                                       /* ignore complete line */;
+
+                               src->idx = 0;
+                       }
+                       return read_line (src);
+               }
+       }
+
+       src->buffer[i] = '\0';
+       src->length    = i;
+
+       return src->buffer;
+} /* static char *read_line (conn_t *) */
+
+static void *collect (void *arg)
+{
+       collector_t *this = (collector_t *)arg;
+
+       char *buffer = (char *)smalloc (BUFSIZE);
+
+       while (1) {
+               int loop = 1;
+
+               conn_t *connection;
+
+               pthread_mutex_lock (&conns_mutex);
+
+               while (NULL == conns.head) {
+                       pthread_cond_wait (&conn_available, &conns_mutex);
+               }
+
+               connection = conns.head;
+               conns.head = conns.head->next;
+
+               if (NULL == conns.head) {
+                       conns.tail = NULL;
+               }
+
+               this->socket = connection->socket;
+
+               pthread_mutex_unlock (&conns_mutex);
+
+               connection->buffer = buffer;
+               connection->idx    = 0;
+               connection->length = 0;
+
+               { /* put the socket in non-blocking mode */
+                       int flags = 0;
+
+                       errno = 0;
+                       if (-1 == fcntl (connection->socket, F_GETFL, &flags)) {
+                               log_err ("fcntl() failed: %s", strerror (errno));
+                               loop = 0;
+                       }
+
+                       errno = 0;
+                       if (-1 == fcntl (connection->socket, F_SETFL, flags | O_NONBLOCK)) {
+                               log_err ("fcntl() failed: %s", strerror (errno));
+                               loop = 0;
+                       }
+               }
+
+               while (loop) {
+                       char *line = read_line (connection);
+
+                       if (NULL == line) {
+                               loop = 0;
+                               break;
+                       }
+
+                       if (':' != line[1]) {
+                               log_err ("syntax error in line '%s'", line);
+                               continue;
+                       }
+
+                       if ('e' == line[0]) { /* e:<type>:<bytes> */
+                               char *ptr  = NULL;
+                               char *type = strtok_r (line + 2, ":", &ptr);
+                               char *tmp  = strtok_r (NULL, ":", &ptr);
+                               int  bytes = 0;
+
+                               if (NULL == tmp) {
+                                       log_err ("syntax error in line '%s'", line);
+                                       continue;
+                               }
+
+                               bytes = atoi (tmp);
+
+                               pthread_mutex_lock (&count_mutex);
+                               type_list_incr (&count, type, 1);
+                               pthread_mutex_unlock (&count_mutex);
+
+                               pthread_mutex_lock (&size_mutex);
+                               type_list_incr (&size, type, bytes);
+                               pthread_mutex_unlock (&size_mutex);
+                       }
+                       else if ('s' == line[0]) { /* s:<value> */
+                               pthread_mutex_lock (&score_mutex);
+                               score = (score * (double)score_count + atof (line + 2))
+                                               / (double)(score_count + 1);
+                               ++score_count;
+                               pthread_mutex_unlock (&score_mutex);
+                       }
+                       else if ('c' == line[0]) { /* c:<type1>[,<type2>,...] */
+                               char *ptr  = NULL;
+                               char *type = strtok_r (line + 2, ",", &ptr);
+
+                               do {
+                                       pthread_mutex_lock (&check_mutex);
+                                       type_list_incr (&check, type, 1);
+                                       pthread_mutex_unlock (&check_mutex);
+                               } while (NULL != (type = strtok_r (NULL, ",", &ptr)));
+                       }
+                       else {
+                               log_err ("unknown type '%c'", line[0]);
+                       }
+               } /* while (loop) */
+
+               close (connection->socket);
+
+               free (connection);
+
+               pthread_mutex_lock (&available_mutex);
+               ++available_collectors;
+               pthread_mutex_unlock (&available_mutex);
+
+               pthread_cond_signal (&collector_available);
+       } /* while (1) */
+
+       free (buffer);
+       pthread_exit ((void *)0);
+} /* static void *collect (void *) */
+
+static void *open_connection (void *arg)
+{
+       struct sockaddr_un addr;
+
+       /* create UNIX socket */
+       errno = 0;
+       if (-1 == (connector_socket = socket (PF_UNIX, SOCK_STREAM, 0))) {
+               disabled = 1;
+               log_err ("socket() failed: %s", strerror (errno));
+               pthread_exit ((void *)1);
+       }
+
+       addr.sun_family = AF_UNIX;
+
+       strncpy (addr.sun_path, SOCK_PATH, (size_t)(UNIX_PATH_MAX - 1));
+       addr.sun_path[UNIX_PATH_MAX - 1] = '\0';
+       unlink (addr.sun_path);
+
+       errno = 0;
+       if (-1 == bind (connector_socket, (struct sockaddr *)&addr,
+                               offsetof (struct sockaddr_un, sun_path)
+                                       + strlen(addr.sun_path))) {
+               disabled = 1;
+               log_err ("bind() failed: %s", strerror (errno));
+               pthread_exit ((void *)1);
+       }
+
+       errno = 0;
+       if (-1 == listen (connector_socket, 5)) {
+               disabled = 1;
+               log_err ("listen() failed: %s", strerror (errno));
+               pthread_exit ((void *)1);
+       }
+
+       if ((uid_t)0 == geteuid ()) {
+               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));
+                       }
+               }
+               else {
+                       log_warn ("getgrnam() failed: %s", strerror (errno));
+               }
+       }
+       else {
+               log_warn ("not running as root");
+       }
+
+       errno = 0;
+       if (0 != chmod (SOCK_PATH, sock_perms)) {
+               log_warn ("chmod() failed: %s", strerror (errno));
+       }
+
+       { /* initialize collector threads */
+               int i   = 0;
+               int err = 0;
+
+               pthread_attr_t ptattr;
+
+               conns.head = NULL;
+               conns.tail = NULL;
+
+               pthread_attr_init (&ptattr);
+               pthread_attr_setdetachstate (&ptattr, PTHREAD_CREATE_DETACHED);
+
+               available_collectors = max_conns;
+
+               collectors =
+                       (collector_t **)smalloc (max_conns * sizeof (collector_t *));
+
+               for (i = 0; i < max_conns; ++i) {
+                       collectors[i] = (collector_t *)smalloc (sizeof (collector_t));
+                       collectors[i]->socket = 0;
+
+                       if (0 != (err = pthread_create (&collectors[i]->thread, &ptattr,
+                                                       collect, collectors[i]))) {
+                               log_err ("pthread_create() failed: %s", strerror (err));
+                       }
+               }
+
+               pthread_attr_destroy (&ptattr);
+       }
+
+       while (1) {
+               int remote = 0;
+
+               conn_t *connection;
+
+               pthread_mutex_lock (&available_mutex);
+
+               while (0 == available_collectors) {
+                       pthread_cond_wait (&collector_available, &available_mutex);
+               }
+
+               --available_collectors;
+
+               pthread_mutex_unlock (&available_mutex);
+
+               do {
+                       errno = 0;
+                       if (-1 == (remote = accept (connector_socket, NULL, NULL))) {
+                               if (EINTR != errno) {
+                                       disabled = 1;
+                                       log_err ("accept() failed: %s", strerror (errno));
+                                       pthread_exit ((void *)1);
+                               }
+                       }
+               } while (EINTR == errno);
+
+               connection = (conn_t *)smalloc (sizeof (conn_t));
+
+               connection->socket = remote;
+               connection->next   = NULL;
+
+               pthread_mutex_lock (&conns_mutex);
+
+               if (NULL == conns.head) {
+                       conns.head = connection;
+                       conns.tail = connection;
+               }
+               else {
+                       conns.tail->next = connection;
+                       conns.tail = conns.tail->next;
+               }
+
+               pthread_mutex_unlock (&conns_mutex);
+
+               pthread_cond_signal (&conn_available);
+       }
+       pthread_exit ((void *)0);
+} /* static void *open_connection (void *) */
+#endif /* EMAIL_HAVE_READ */
+
+static void email_init (void)
+{
+#if EMAIL_HAVE_READ
+       int err = 0;
+
+       if (0 != (err = pthread_create (&connector, NULL,
+                               open_connection, NULL))) {
+               disabled = 1;
+               log_err ("pthread_create() failed: %s", strerror (err));
+               return;
+       }
+#endif /* EMAIL_HAVE_READ */
+       return;
+} /* static void email_init (void) */
+
+#if EMAIL_HAVE_READ
+static void email_shutdown (void)
+{
+       int i = 0;
+
+       if (disabled)
+               return;
+
+       pthread_kill (connector, SIGTERM);
+       close (connector_socket);
+
+       /* 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;
+
+       rrd_update_file (host, file, val, size_ds_def, size_ds_num);
+       return;
+} /* static void size_write (char *host, char *inst, char *val) */
+
+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) */
+
+static void check_write (char *host, char *inst, char *val)
+{
+       char file[BUFSIZE] = "";
+       int  len           = 0;
+
+       len = snprintf (file, BUFSIZE, CHECK_FILE, inst);
+       if ((len < 0) || (len >= BUFSIZE))
+               return;
+
+       rrd_update_file (host, file, val, check_ds_def, check_ds_num);
+       return;
+} /* static void check_write (char *host, char *inst, char *val) */
+
+#if EMAIL_HAVE_READ
+static void type_submit (char *plugin, char *inst, int value)
+{
+       char buf[BUFSIZE] = "";
+       int  len          = 0;
+
+       len = snprintf (buf, BUFSIZE, "%u:%i", (unsigned int)curtime, value);
+       if ((len < 0) || (len >= BUFSIZE))
+               return;
+
+       plugin_submit (plugin, inst, buf);
+       return;
+} /* static void type_submit (char *, char *, int) */
+
+static void score_submit (double value)
+{
+       char buf[BUFSIZE] = "";
+       int  len          = 0;
+
+       len = snprintf (buf, BUFSIZE, "%u:%.2f", (unsigned int)curtime, value);
+       if ((len < 0) || (len >= BUFSIZE))
+               return;
+
+       plugin_submit ("email_spam_score", NULL, buf);
+       return;
+} /* static void score_submit (double) */
+
+static void email_read (void)
+{
+       type_t *ptr;
+
+       if (disabled)
+               return;
+
+       pthread_mutex_lock (&count_mutex);
+
+       for (ptr = count.head; NULL != ptr; ptr = ptr->next) {
+               type_submit ("email_count", ptr->name, ptr->value);
+               ptr->value = 0;
+       }
+
+       pthread_mutex_unlock (&count_mutex);
+
+       pthread_mutex_lock (&size_mutex);
+
+       for (ptr = size.head; NULL != ptr; ptr = ptr->next) {
+               type_submit ("email_size", ptr->name, ptr->value);
+               ptr->value = 0;
+       }
+
+       pthread_mutex_unlock (&size_mutex);
+
+       pthread_mutex_lock (&score_mutex);
+
+       score_submit (score);
+       score = 0.0;
+       score_count = 0;
+
+       pthread_mutex_unlock (&score_mutex);
+
+       pthread_mutex_lock (&check_mutex);
+
+       for (ptr = check.head; NULL != ptr; ptr = ptr->next) {
+               type_submit ("email_spam_check", ptr->name, ptr->value);
+               ptr->value = 0;
+       }
+
+       pthread_mutex_unlock (&check_mutex);
+       return;
+} /* static void read (void) */
+#else /* if !EMAIL_HAVE_READ */
+# define email_read NULL
+#endif
+
+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) */
+
+/* vim: set sw=4 ts=4 tw=78 noexpandtab : */
+
index debd432..13bcc92 100644 (file)
@@ -537,7 +537,17 @@ static ssize_t ping_sendto (pingobj_t *obj, pinghost_t *ph,
                        (struct sockaddr *) ph->addr, ph->addrlen);
 
        if (ret < 0)
+       {
+#if defined(EHOSTUNREACH)
+               if (errno == EHOSTUNREACH)
+                       return (0);
+#endif
+#if defined(ENETUNREACH)
+               if (errno == ENETUNREACH)
+                       return (0);
+#endif
                ping_set_error (obj, "sendto", strerror (errno));
+       }
 
        return (ret);
 }
diff --git a/src/multimeter.c b/src/multimeter.c
new file mode 100644 (file)
index 0000000..317745b
--- /dev/null
@@ -0,0 +1,234 @@
+/**
+ * collectd - src/multimeter.c
+ * Copyright (C) 2005,2006  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>
+ *
+ * Used multimeter: Metex M-4650CR
+ *
+ **/
+
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include "collectd.h"
+#include "common.h"
+#include "plugin.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;
+
+static int fd = -1;
+
+static int multimeter_timeval_sub (struct timeval *tv1, struct timeval *tv2,
+                struct timeval *res)
+{
+        if ((tv1->tv_sec < tv2->tv_sec) ||
+           ((tv1->tv_sec == tv2->tv_sec) && (tv1->tv_usec < tv2->tv_usec)))
+               return (-1);
+
+        res->tv_sec  = tv1->tv_sec  - tv2->tv_sec;
+        res->tv_usec = tv1->tv_usec - tv2->tv_usec;
+
+        assert ((res->tv_sec > 0) || ((res->tv_sec == 0) && (res->tv_usec > 0)));
+
+        while (res->tv_usec < 0)
+        {
+               res->tv_usec += 1000000;
+                res->tv_sec--;
+        }
+       return (0);
+}
+#define LINE_LENGTH 14
+static int multimeter_read_value(double *value)
+{
+       int retry = 3; /* sometimes we receive garbadge */
+
+       do
+       {
+               struct timeval time_end;
+
+               tcflush(fd, TCIFLUSH);
+
+               if (gettimeofday (&time_end, NULL) < 0)
+               {
+                       syslog (LOG_ERR, MODULE_NAME": gettimeofday failed: %s",
+                                strerror (errno));
+                       return (-1);
+               }
+               time_end.tv_sec++;      
+
+               while (1)
+               {
+                       char buf[LINE_LENGTH];
+                       char *range;
+                       int status;
+                       fd_set rfds;
+                       struct timeval timeout;
+                       struct timeval time_now;
+
+                       write(fd, "D", 1);
+
+                       FD_ZERO(&rfds);
+                       FD_SET(fd, &rfds);
+
+                       if (gettimeofday (&time_now, NULL) < 0)
+                       {
+                               syslog (LOG_ERR, MODULE_NAME": gettimeofday failed: %s",
+                                        strerror (errno));
+                               return (-1);
+                       }
+                       if (multimeter_timeval_sub (&time_end, &time_now, &timeout) == -1)
+                               break;
+
+                       status = select(fd+1, &rfds, NULL, NULL, &timeout);
+
+                       if (status > 0) /* usually we succeed */
+                       {
+                               status = read(fd, buf, LINE_LENGTH);
+
+                               if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
+                                       continue;
+
+                               /* Format: "DC 00.000mV  \r" */
+                               if (status > 0 && status == LINE_LENGTH)
+                               {
+                                       *value = strtod(buf + 2, &range);
+
+                                       if ( range > (buf + 6) )
+                                       {
+                                               range = buf + 9;
+
+                                               switch ( *range )
+                                               {
+                                                       case 'p': *value *= 1.0E-12; break;
+                                                       case 'n': *value *= 1.0E-9; break;
+                                                       case 'u': *value *= 1.0E-6; break;
+                                                       case 'm': *value *= 1.0E-3; break;
+                                                       case 'k': *value *= 1.0E3; break;
+                                                       case 'M': *value *= 1.0E6; break;
+                                                       case 'G': *value *= 1.0E9; break;
+                                               }
+                                       }
+                                       else
+                                               return (-1); /* Overflow */
+
+                                       return (0); /* value received */
+                               }
+                               else break;
+                       }
+                       else if (!status) /* Timeout */
+                       {
+                               break;
+                       }
+                       else if ((status == -1) && ((errno == EAGAIN) || (errno == EINTR)))
+                       {
+                               continue;
+                       }
+                       else /* status == -1 */
+                       {
+                               syslog (LOG_ERR, MODULE_NAME": select failed: %s",
+                                        strerror (errno));
+                               break;
+                       }
+               }
+       } while (--retry);
+
+       return (-2);  /* no value received */
+}
+
+static void multimeter_init (void)
+{
+       int i;
+       char device[] = "/dev/ttyS ";
+
+       for (i = 0; i < 10; i++)
+       {
+               device[strlen(device)-1] = i + '0'; 
+
+               if ((fd = open(device, O_RDWR | O_NOCTTY)) > 0)
+               {
+                       struct termios tios;
+                       int rts = TIOCM_RTS;
+                       double value;
+
+                       tios.c_cflag = B1200 | CS7 | CSTOPB | CREAD | CLOCAL;
+                       tios.c_iflag = IGNBRK | IGNPAR;
+                       tios.c_oflag = 0;
+                       tios.c_lflag = 0;
+                       tios.c_cc[VTIME] = 3;
+                       tios.c_cc[VMIN]  = LINE_LENGTH;
+
+                       tcflush(fd, TCIFLUSH);
+                       tcsetattr(fd, TCSANOW, &tios);
+                       ioctl(fd, TIOCMBIC, &rts);
+                       
+                       if (multimeter_read_value(&value) < -1)
+                       {
+                               close(fd);
+                               fd = -1;
+                       }
+                       else
+                       {
+                               syslog (LOG_INFO, MODULE_NAME" found (%s)", device);
+                               return;
+                       }
+               }
+       }
+       syslog (LOG_ERR, MODULE_NAME" not found");
+}
+#undef LINE_LENGTH
+
+static void multimeter_write (char *host, char *inst, char *val)
+{
+       rrd_update_file (host, multimeter_file, val, ds_def, ds_num);
+}
+#define BUFSIZE 128
+static void multimeter_submit (double *value)
+{
+       char buf[BUFSIZE];
+
+       if (snprintf (buf, BUFSIZE, "%u:%f", (unsigned int) curtime, *value) >= BUFSIZE)
+               return;
+
+       plugin_submit (MODULE_NAME, NULL, buf);
+}
+#undef BUFSIZE
+
+static void multimeter_read (void)
+{
+       double value;
+
+       if (fd > -1 && !(multimeter_read_value(&value)))
+               multimeter_submit (&value);
+}
+
+void module_register (void)
+{
+       plugin_register (MODULE_NAME, multimeter_init, multimeter_read, multimeter_write);
+}
+
+#undef MODULE_NAME
index 2f52157..46a1c61 100644 (file)
@@ -34,6 +34,7 @@ typedef struct plugin
        void (*init) (void);
        void (*read) (void);
        void (*write) (char *host, char *inst, char *val);
+       void (*shutdown) (void);
        struct plugin *next;
 } plugin_t;
 
@@ -276,16 +277,29 @@ void plugin_init_all (void)
 /*
  * Call `read' on all plugins (if given)
  */
-void plugin_read_all (void)
+void plugin_read_all (const int *loop)
 {
        plugin_t *p;
 
-       for (p = first_plugin; p != NULL; p = p->next)
+       for (p = first_plugin; (*loop == 0) && (p != NULL); p = p->next)
                if (p->read != NULL)
                        (*p->read) ();
 }
 
 /*
+ * Call `shutdown' on all plugins (if given)
+ */
+void plugin_shutdown_all (void)
+{
+       plugin_t *p;
+
+       for (p = first_plugin; NULL != p; p = p->next)
+               if (NULL != p->shutdown)
+                       (*p->shutdown) ();
+       return;
+}
+
+/*
  * Add plugin to the linked list of registered plugins.
  */
 void plugin_register (char *type,
@@ -317,11 +331,27 @@ void plugin_register (char *type,
        p->read  = read;
        p->write = write;
 
+       p->shutdown = NULL;
+
        p->next = first_plugin;
        first_plugin = p;
 }
 
 /*
+ * Register the shutdown function (optional).
+ */
+int plugin_register_shutdown (char *type, void (*shutdown) (void))
+{
+       plugin_t *p = plugin_search (type);
+
+       if (NULL == p)
+               return -1;
+
+       p->shutdown = shutdown;
+       return 0;
+}
+
+/*
  * Send received data back to the plugin/module which will append DS
  * definitions and pass it on to ``rrd_update_file''.
  */
@@ -350,3 +380,54 @@ void plugin_submit (char *type, char *inst, char *val)
        else
                plugin_write (NULL, type, inst, val);
 }
+
+void plugin_complain (int level, complain_t *c, const char *format, ...)
+{
+       char message[512];
+       va_list ap;
+       int step;
+
+       if (c->delay > 0)
+       {
+               c->delay--;
+               return;
+       }
+
+       step = atoi (COLLECTD_STEP);
+       assert (step > 0);
+
+       if (c->interval < step)
+               c->interval = step;
+       else
+               c->interval *= 2;
+
+       if (c->interval > 86400)
+               c->interval = 86400;
+
+       c->delay = c->interval / step;
+
+       va_start (ap, format);
+       vsnprintf (message, 512, format, ap);
+       message[511] = '\0';
+       va_end (ap);
+
+       syslog (level, message);
+}
+
+void plugin_relief (int level, complain_t *c, const char *format, ...)
+{
+       char message[512];
+       va_list ap;
+
+       if (c->interval == 0)
+               return;
+
+       c->interval = 0;
+
+       va_start (ap, format);
+       vsnprintf (message, 512, format, ap);
+       message[511] = '\0';
+       va_end (ap);
+
+       syslog (level, message);
+}
index e1303c7..45e9b49 100644 (file)
@@ -1,3 +1,6 @@
+#ifndef PLUGIN_H
+#define PLUGIN_H
+
 /**
  * collectd - src/plugin.h
  * Copyright (C) 2005,2006  Florian octo Forster
  *   Florian octo Forster <octo at verplant.org>
  **/
 
-#ifndef PLUGIN_H
-#define PLUGIN_H
+/*
+ *
+ */
+typedef struct complain_s
+{
+       unsigned int interval; /* how long we wait for reporting this error again */
+       unsigned int delay;    /* how many more iterations we still need to wait */
+} complain_t;
 
 /*
  * NAME
@@ -90,13 +99,17 @@ int  plugin_load (const char *type);
 
 int  plugin_load_all (char *dir);
 void plugin_init_all (void);
-void plugin_read_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 *));
 
+int plugin_register_shutdown (char *, void (*) (void));
+
 /*
  * NAME
  *  plugin_write
@@ -117,4 +130,8 @@ void plugin_write    (char *host, char *type, char *inst, char *val);
 
 void plugin_submit   (char *type, char *inst, char *val);
 
+
+void plugin_complain (int level, complain_t *c, const char *format, ...);
+void plugin_relief (int level, complain_t *c, const char *format, ...);
+
 #endif /* PLUGIN_H */
index dba29ef..c8f3016 100644 (file)
 #include "common.h"
 #include "plugin.h"
 #include "utils_debug.h"
+#include "configfile.h"
 
 /* Include header files for the mach system, if they exist.. */
-#if HAVE_MACH_MACH_INIT_H
-#  include <mach/mach_init.h>
-#endif
-#if HAVE_MACH_HOST_PRIV_H
-#  include <mach/host_priv.h>
-#endif
-#if HAVE_MACH_MACH_ERROR_H
-#  include <mach/mach_error.h>
-#endif
-#if HAVE_MACH_MACH_HOST_H
-#  include <mach/mach_host.h>
-#endif
-#if HAVE_MACH_MACH_PORT_H
-#  include <mach/mach_port.h>
-#endif
-#if HAVE_MACH_MACH_TYPES_H
-#  include <mach/mach_types.h>
-#endif
-#if HAVE_MACH_MESSAGE_H
-#  include <mach/message.h>
-#endif
-#if HAVE_MACH_PROCESSOR_SET_H
-#  include <mach/processor_set.h>
-#endif
-#if HAVE_MACH_TASK_H
-#  include <mach/task.h>
-#endif
-#if HAVE_MACH_THREAD_ACT_H
-#  include <mach/thread_act.h>
-#endif
-#if HAVE_MACH_VM_REGION_H
-#  include <mach/vm_region.h>
-#endif
-#if HAVE_MACH_VM_MAP_H
-#  include <mach/vm_map.h>
-#endif
-#if HAVE_MACH_VM_PROT_H
-#  include <mach/vm_prot.h>
-#endif
+#if HAVE_THREAD_INFO
+#  if HAVE_MACH_MACH_INIT_H
+#    include <mach/mach_init.h>
+#  endif
+#  if HAVE_MACH_HOST_PRIV_H
+#    include <mach/host_priv.h>
+#  endif
+#  if HAVE_MACH_MACH_ERROR_H
+#    include <mach/mach_error.h>
+#  endif
+#  if HAVE_MACH_MACH_HOST_H
+#    include <mach/mach_host.h>
+#  endif
+#  if HAVE_MACH_MACH_PORT_H
+#    include <mach/mach_port.h>
+#  endif
+#  if HAVE_MACH_MACH_TYPES_H
+#    include <mach/mach_types.h>
+#  endif
+#  if HAVE_MACH_MESSAGE_H
+#    include <mach/message.h>
+#  endif
+#  if HAVE_MACH_PROCESSOR_SET_H
+#    include <mach/processor_set.h>
+#  endif
+#  if HAVE_MACH_TASK_H
+#    include <mach/task.h>
+#  endif
+#  if HAVE_MACH_THREAD_ACT_H
+#    include <mach/thread_act.h>
+#  endif
+#  if HAVE_MACH_VM_REGION_H
+#    include <mach/vm_region.h>
+#  endif
+#  if HAVE_MACH_VM_MAP_H
+#    include <mach/vm_map.h>
+#  endif
+#  if HAVE_MACH_VM_PROT_H
+#    include <mach/vm_prot.h>
+#  endif
+#  if HAVE_SYS_SYSCTL_H
+#    include <sys/sysctl.h>
+#  endif
+/* #endif HAVE_THREAD_INFO */
+
+#elif KERNEL_LINUX
+#  if HAVE_LINUX_CONFIG_H
+#    include <linux/config.h>
+#  endif
+#  ifndef CONFIG_HZ
+#    define CONFIG_HZ 100
+#  endif
+#endif /* KERNEL_LINUX */
 
 #define MODULE_NAME "processes"
 
@@ -78,9 +93,8 @@
 
 #define BUFSIZE 256
 
-static char *ps_file = "processes.rrd";
-
-static char *ds_def[] =
+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",
@@ -90,7 +104,95 @@ static char *ds_def[] =
        "DS:blocked:GAUGE:"COLLECTD_HEARTBEAT":0:65535",
        NULL
 };
-static int ds_num = 6;
+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;
+
+static char *config_keys[] =
+{
+       "Process",
+       NULL
+};
+static int config_keys_num = 1;
+
+typedef struct procstat_entry_s
+{
+       unsigned long id;
+       unsigned long age;
+
+       unsigned long num_proc;
+       unsigned long num_lwp;
+       unsigned long vmem_rss;
+
+       unsigned long vmem_minflt;
+       unsigned long vmem_majflt;
+       unsigned long vmem_minflt_counter;
+       unsigned long vmem_majflt_counter;
+
+       unsigned long cpu_user;
+       unsigned long cpu_system;
+       unsigned long cpu_user_counter;
+       unsigned long cpu_system_counter;
+
+       struct procstat_entry_s *next;
+} procstat_entry_t;
+
+#define PROCSTAT_NAME_LEN 256
+typedef struct procstat
+{
+       char          name[PROCSTAT_NAME_LEN];
+
+       unsigned long num_proc;
+       unsigned long num_lwp;
+       unsigned long vmem_rss;
+
+       unsigned long vmem_minflt_counter;
+       unsigned long vmem_majflt_counter;
+
+       unsigned long cpu_user_counter;
+       unsigned long cpu_system_counter;
+
+       struct procstat   *next;
+       struct procstat_entry_s *instances;
+} procstat_t;
+
+static procstat_t *list_head_g = NULL;
 
 #if HAVE_THREAD_INFO
 static mach_port_t port_host_self;
@@ -101,9 +203,222 @@ static mach_msg_type_number_t     pset_list_len;
 /* #endif HAVE_THREAD_INFO */
 
 #elif KERNEL_LINUX
-/* No global variables */
+static long pagesize_g;
 #endif /* KERNEL_LINUX */
 
+#if HAVE_THREAD_INFO | KERNEL_LINUX
+static void ps_list_register (const char *name)
+{
+       procstat_t *new;
+       procstat_t *ptr;
+
+       if ((new = (procstat_t *) malloc (sizeof (procstat_t))) == NULL)
+               return;
+       memset (new, 0, sizeof (procstat_t));
+       strncpy (new->name, name, PROCSTAT_NAME_LEN);
+
+       for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
+       {
+               if (strcmp (ptr->name, name) == 0)
+                       return;
+               if (ptr->next == NULL)
+                       break;
+       }
+
+       if (ptr == NULL)
+               list_head_g = new;
+       else
+               ptr->next = new;
+}
+
+static procstat_t *ps_list_search (const char *name)
+{
+       procstat_t *ptr;
+
+       for (ptr = list_head_g; ptr != NULL; ptr = ptr->next)
+               if (strcmp (ptr->name, name) == 0)
+                       break;
+
+       return (ptr);
+}
+
+static void ps_list_add (const char *name, procstat_entry_t *entry)
+{
+       procstat_t *ps;
+       procstat_entry_t *pse;
+
+       if (entry->id == 0)
+               return;
+
+       if ((ps = ps_list_search (name)) == NULL)
+               return;
+
+       for (pse = ps->instances; pse != NULL; pse = pse->next)
+               if ((pse->id == entry->id) || (pse->next == NULL))
+                       break;
+
+       if ((pse == NULL) || (pse->id != entry->id))
+       {
+               procstat_entry_t *new;
+
+               new = (procstat_entry_t *) malloc (sizeof (procstat_entry_t));
+               if (new == NULL)
+                       return;
+               memset (new, 0, sizeof (procstat_entry_t));
+               new->id = entry->id;
+
+               if (pse == NULL)
+                       ps->instances = new;
+               else
+                       pse->next = new;
+
+               pse = new;
+       }
+
+       pse->age = 0;
+       pse->num_proc = entry->num_proc;
+       pse->num_lwp  = entry->num_lwp;
+       pse->vmem_rss = entry->vmem_rss;
+
+       ps->num_proc += pse->num_proc;
+       ps->num_lwp  += pse->num_lwp;
+       ps->vmem_rss += pse->vmem_rss;
+
+       if ((entry->vmem_minflt_counter == 0)
+                       && (entry->vmem_majflt_counter == 0))
+       {
+               pse->vmem_minflt_counter += entry->vmem_minflt;
+               pse->vmem_minflt = entry->vmem_minflt;
+
+               pse->vmem_majflt_counter += entry->vmem_majflt;
+               pse->vmem_majflt = entry->vmem_majflt;
+       }
+       else
+       {
+               if (entry->vmem_minflt_counter < pse->vmem_minflt_counter)
+               {
+                       pse->vmem_minflt = entry->vmem_minflt_counter
+                               + (ULONG_MAX - pse->vmem_minflt_counter);
+               }
+               else
+               {
+                       pse->vmem_minflt = entry->vmem_minflt_counter - pse->vmem_minflt_counter;
+               }
+               pse->vmem_minflt_counter = entry->vmem_minflt_counter;
+
+               if (entry->vmem_majflt_counter < pse->vmem_majflt_counter)
+               {
+                       pse->vmem_majflt = entry->vmem_majflt_counter
+                               + (ULONG_MAX - pse->vmem_majflt_counter);
+               }
+               else
+               {
+                       pse->vmem_majflt = entry->vmem_majflt_counter - pse->vmem_majflt_counter;
+               }
+               pse->vmem_majflt_counter = entry->vmem_majflt_counter;
+       }
+
+       ps->vmem_minflt_counter += pse->vmem_minflt;
+       ps->vmem_majflt_counter += pse->vmem_majflt;
+
+       if ((entry->cpu_user_counter == 0)
+                       && (entry->cpu_system_counter == 0))
+       {
+               pse->cpu_user_counter += entry->cpu_user;
+               pse->cpu_user = entry->cpu_user;
+
+               pse->cpu_system_counter += entry->cpu_system;
+               pse->cpu_system = entry->cpu_system;
+       }
+       else
+       {
+               if (entry->cpu_user_counter < pse->cpu_user_counter)
+               {
+                       pse->cpu_user = entry->cpu_user_counter
+                               + (ULONG_MAX - pse->cpu_user_counter);
+               }
+               else
+               {
+                       pse->cpu_user = entry->cpu_user_counter - pse->cpu_user_counter;
+               }
+               pse->cpu_user_counter = entry->cpu_user_counter;
+
+               if (entry->cpu_system_counter < pse->cpu_system_counter)
+               {
+                       pse->cpu_system = entry->cpu_system_counter
+                               + (ULONG_MAX - pse->cpu_system_counter);
+               }
+               else
+               {
+                       pse->cpu_system = entry->cpu_system_counter - pse->cpu_system_counter;
+               }
+               pse->cpu_system_counter = entry->cpu_system_counter;
+       }
+
+       ps->cpu_user_counter   += pse->cpu_user;
+       ps->cpu_system_counter += pse->cpu_system;
+}
+
+static void ps_list_reset (void)
+{
+       procstat_t *ps;
+       procstat_entry_t *pse;
+       procstat_entry_t *pse_prev;
+
+       for (ps = list_head_g; ps != NULL; ps = ps->next)
+       {
+               ps->num_proc    = 0;
+               ps->num_lwp     = 0;
+               ps->vmem_rss    = 0;
+
+               pse_prev = NULL;
+               pse = ps->instances;
+               while (pse != NULL)
+               {
+                       if (pse->age > 10)
+                       {
+                               DBG ("Removing this procstat entry cause it's too old: "
+                                               "id = %lu; name = %s;",
+                                               pse->id, ps->name);
+
+                               if (pse_prev == NULL)
+                               {
+                                       ps->instances = pse->next;
+                                       free (pse);
+                                       pse = ps->instances;
+                               }
+                               else
+                               {
+                                       pse_prev->next = pse->next;
+                                       free (pse);
+                                       pse = pse_prev->next;
+                               }
+                       }
+                       else
+                       {
+                               pse->age++;
+                               pse_prev = pse;
+                               pse = pse->next;
+                       }
+               } /* while (pse != NULL) */
+       } /* for (ps = list_head_g; ps != NULL; ps = ps->next) */
+}
+#endif /* HAVE_THREAD_INFO | KERNEL_LINUX */
+
+static int ps_config (char *key, char *value)
+{
+       if (strcasecmp (key, "Process") == 0)
+       {
+               ps_list_register (value);
+       }
+       else
+       {
+               return (-1);
+       }
+
+       return (0);
+}
+
 static void ps_init (void)
 {
 #if HAVE_THREAD_INFO
@@ -134,7 +449,9 @@ static void ps_init (void)
 /* #endif HAVE_THREAD_INFO */
 
 #elif KERNEL_LINUX
-       /* No init */
+       pagesize_g = sysconf(_SC_PAGESIZE);
+       DBG ("pagesize_g = %li; CONFIG_HZ = %i;",
+                       pagesize_g, CONFIG_HZ);
 #endif /* KERNEL_LINUX */
 
        return;
@@ -142,7 +459,65 @@ static void ps_init (void)
 
 static void ps_write (char *host, char *inst, char *val)
 {
-       rrd_update_file (host, ps_file, val, ds_def, ds_num);
+       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);
 }
 
 #if PROCESSES_HAVE_READ
@@ -167,6 +542,254 @@ static void ps_submit (int running,
        plugin_submit (MODULE_NAME, "-", buf);
 }
 
+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; "
+                       "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);
+
+}
+
+#if KERNEL_LINUX
+static int *ps_read_tasks (int pid)
+{
+       int *list = NULL;
+       int  list_size = 1; /* size of allocated space, in elements */
+       int  list_len = 0;  /* number of currently used elements */
+
+       char           dirname[64];
+       DIR           *dh;
+       struct dirent *ent;
+
+       snprintf (dirname, 64, "/proc/%i/task", pid);
+       dirname[63] = '\0';
+
+       if ((dh = opendir (dirname)) == NULL)
+       {
+               DBG ("Failed to open directory `%s'", dirname);
+               return (NULL);
+       }
+
+       while ((ent = readdir (dh)) != NULL)
+       {
+               if (!isdigit (ent->d_name[0]))
+                       continue;
+
+               if ((list_len + 1) >= list_size)
+               {
+                       int *new_ptr;
+                       int  new_size = 2 * list_size;
+                       /* Comes in sizes: 2, 4, 8, 16, ... */
+
+                       new_ptr = (int *) realloc (list, (size_t) (sizeof (int) * new_size));
+                       if (new_ptr == NULL)
+                       {
+                               if (list != NULL)
+                                       free (list);
+                               syslog (LOG_ERR, "processes plugin: "
+                                               "Failed to allocate more memory.");
+                               return (NULL);
+                       }
+
+                       list = new_ptr;
+                       list_size = new_size;
+
+                       memset (list + list_len, 0, sizeof (int) * (list_size - list_len));
+               }
+
+               list[list_len] = atoi (ent->d_name);
+               if (list[list_len] != 0)
+                       list_len++;
+       }
+
+       closedir (dh);
+
+       assert (list_len < list_size);
+       assert (list[list_len] == 0);
+
+       return (list);
+}
+
+int ps_read_process (int pid, procstat_t *ps, char *state)
+{
+       char  filename[64];
+       char  buffer[1024];
+       FILE *fh;
+
+       char *fields[64];
+       char  fields_len;
+
+       int  *tasks;
+       int   i;
+
+       int   ppid;
+       int   name_len;
+
+       long long unsigned cpu_user_counter;
+       long long unsigned cpu_system_counter;
+       long long unsigned vmem_rss;
+
+       memset (ps, 0, sizeof (procstat_t));
+
+       snprintf (filename, 64, "/proc/%i/stat", pid);
+       filename[63] = '\0';
+
+       if ((fh = fopen (filename, "r")) == NULL)
+               return (-1);
+
+       if (fgets (buffer, 1024, fh) == NULL)
+       {
+               fclose (fh);
+               return (-1);
+       }
+
+       fclose (fh);
+
+       fields_len = strsplit (buffer, fields, 64);
+       if (fields_len < 24)
+       {
+               DBG ("`%s' has only %i fields..",
+                               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]);
+               return (-1);
+       }
+       fields[1] = fields[1] + 1;
+       fields[1][name_len] = '\0';
+       strncpy (ps->name, fields[1], PROCSTAT_NAME_LEN);
+
+       ppid = atoi (fields[3]);
+
+       if ((tasks = ps_read_tasks (pid)) == NULL)
+       {
+               /* This happends for zombied, e.g. */
+               DBG ("ps_read_tasks (%i) failed.", pid);
+               *state = 'Z';
+               ps->num_lwp  = 0;
+               ps->num_proc = 0;
+       }
+       else
+       {
+               *state = '\0';
+               ps->num_lwp  = 0;
+               ps->num_proc = 1;
+               for (i = 0; tasks[i] != 0; i++)
+                       ps->num_lwp++;
+
+               free (tasks);
+               tasks = NULL;
+       }
+
+       /* Leave the rest at zero if this is only an LWP */
+       if (ps->num_proc == 0)
+       {
+               DBG ("This is only an LWP: pid = %i; name = %s;",
+                               pid, ps->name);
+               return (0);
+       }
+
+       cpu_user_counter   = atoll (fields[13]);
+       cpu_system_counter = atoll (fields[14]);
+       vmem_rss = atoll (fields[23]);
+       ps->vmem_minflt_counter = atol (fields[9]);
+       ps->vmem_majflt_counter = atol (fields[11]);
+       
+       /* Convert jiffies to useconds */
+       cpu_user_counter   = cpu_user_counter   * 1000000 / CONFIG_HZ;
+       cpu_system_counter = cpu_system_counter * 1000000 / CONFIG_HZ;
+       vmem_rss = vmem_rss * pagesize_g;
+
+       ps->cpu_user_counter = (unsigned long) cpu_user_counter;
+       ps->cpu_system_counter = (unsigned long) cpu_system_counter;
+       ps->vmem_rss = (unsigned long) vmem_rss;
+
+       *state = fields[2][0];
+
+       /* success */
+       return (0);
+} /* int ps_read_process (...) */
+#endif /* KERNEL_LINUX */
+
+#if HAVE_THREAD_INFO
+static int mach_get_task_name (task_t t, int *pid, char *name, size_t name_max_len)
+{
+       int mib[4];
+
+       struct kinfo_proc kp;
+       size_t            kp_size;
+
+       mib[0] = CTL_KERN;
+       mib[1] = KERN_PROC;
+       mib[2] = KERN_PROC_PID;
+
+       if (pid_for_task (t, pid) != KERN_SUCCESS)
+               return (-1);
+       mib[3] = *pid;
+
+       kp_size = sizeof (kp);
+       if (sysctl (mib, 4, &kp, &kp_size, NULL, 0) != 0)
+               return (-1);
+
+       if (name_max_len > (MAXCOMLEN + 1))
+               name_max_len = MAXCOMLEN + 1;
+
+       strncpy (name, kp.kp_proc.p_comm, name_max_len - 1);
+       name[name_max_len - 1] = '\0';
+
+       DBG ("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
+        * debugging. -octo */
+
+       return (0);
+}
+#endif /* HAVE_THREAD_INFO */
+
 static void ps_read (void)
 {
 #if HAVE_THREAD_INFO
@@ -179,6 +802,9 @@ static void ps_read (void)
        task_array_t             task_list;
        mach_msg_type_number_t   task_list_len;
 
+       int                      task_pid;
+       char                     task_name[MAXCOMLEN + 1];
+
        int                      thread;
        thread_act_array_t       thread_list;
        mach_msg_type_number_t   thread_list_len;
@@ -191,6 +817,11 @@ static void ps_read (void)
        int stopped  = 0;
        int blocked  = 0;
 
+       procstat_t *ps;
+       procstat_entry_t pse;
+
+       ps_list_reset ();
+
        /*
         * The Mach-concept is a little different from the traditional UNIX
         * concept: All the work is done in threads. Threads are contained in
@@ -222,6 +853,71 @@ static void ps_read (void)
 
                for (task = 0; task < task_list_len; task++)
                {
+                       ps = NULL;
+                       if (mach_get_task_name (task_list[task],
+                                               &task_pid,
+                                               task_name, PROCSTAT_NAME_LEN) == 0)
+                               ps = ps_list_search (task_name);
+
+                       /* Collect more detailed statistics for this process */
+                       if (ps != NULL)
+                       {
+                               task_basic_info_data_t        task_basic_info;
+                               mach_msg_type_number_t        task_basic_info_len;
+                               task_events_info_data_t       task_events_info;
+                               mach_msg_type_number_t        task_events_info_len;
+                               task_absolutetime_info_data_t task_absolutetime_info;
+                               mach_msg_type_number_t        task_absolutetime_info_len;
+
+                               memset (&pse, '\0', sizeof (pse));
+                               pse.id = task_pid;
+
+                               task_basic_info_len = TASK_BASIC_INFO_COUNT;
+                               status = task_info (task_list[task],
+                                               TASK_BASIC_INFO,
+                                               (task_info_t) &task_basic_info,
+                                               &task_basic_info_len);
+                               if (status != KERN_SUCCESS)
+                               {
+                                       syslog (LOG_ERR, "task_info failed: %s",
+                                                       mach_error_string (status));
+                                       continue; /* with next thread_list */
+                               }
+
+                               task_events_info_len = TASK_EVENTS_INFO_COUNT;
+                               status = task_info (task_list[task],
+                                               TASK_EVENTS_INFO,
+                                               (task_info_t) &task_events_info,
+                                               &task_events_info_len);
+                               if (status != KERN_SUCCESS)
+                               {
+                                       syslog (LOG_ERR, "task_info failed: %s",
+                                                       mach_error_string (status));
+                                       continue; /* with next thread_list */
+                               }
+
+                               task_absolutetime_info_len = TASK_ABSOLUTETIME_INFO_COUNT;
+                               status = task_info (task_list[task],
+                                               TASK_ABSOLUTETIME_INFO,
+                                               (task_info_t) &task_absolutetime_info,
+                                               &task_absolutetime_info_len);
+                               if (status != KERN_SUCCESS)
+                               {
+                                       syslog (LOG_ERR, "task_info failed: %s",
+                                                       mach_error_string (status));
+                                       continue; /* with next thread_list */
+                               }
+
+                               pse.num_proc++;
+                               pse.vmem_rss = task_basic_info.resident_size;
+
+                               pse.vmem_minflt_counter = task_events_info.cow_faults;
+                               pse.vmem_majflt_counter = task_events_info.faults;
+
+                               pse.cpu_user_counter = task_absolutetime_info.total_user;
+                               pse.cpu_system_counter = task_absolutetime_info.total_system;
+                       }
+
                        status = task_threads (task_list[task], &thread_list,
                                        &thread_list_len);
                        if (status != KERN_SUCCESS)
@@ -248,7 +944,7 @@ static void ps_read (void)
                                                &thread_data_len);
                                if (status != KERN_SUCCESS)
                                {
-                                       syslog (LOG_ERR, "thread_info failed: %s\n",
+                                       syslog (LOG_ERR, "thread_info failed: %s",
                                                        mach_error_string (status));
                                        if (task_list[task] != port_task_self)
                                                mach_port_deallocate (port_task_self,
@@ -256,6 +952,9 @@ static void ps_read (void)
                                        continue; /* with next thread_list */
                                }
 
+                               if (ps != NULL)
+                                       pse.num_lwp++;
+
                                switch (thread_data.run_state)
                                {
                                        case TH_STATE_RUNNING:
@@ -315,6 +1014,9 @@ static void ps_read (void)
                                        syslog (LOG_ERR, "mach_port_deallocate failed: %s",
                                                        mach_error_string (status));
                        }
+
+                       if (ps != NULL)
+                               ps_list_add (task_name, &pse);
                } /* for (task_list) */
 
                if ((status = vm_deallocate (port_task_self,
@@ -336,6 +1038,9 @@ static void ps_read (void)
        } /* for (pset_list) */
 
        ps_submit (running, sleeping, zombies, stopped, -1, blocked);
+
+       for (ps = list_head_g; ps != NULL; ps = ps->next)
+               ps_submit_proc_list (ps);
 /* #endif HAVE_THREAD_INFO */
 
 #elif KERNEL_LINUX
@@ -346,15 +1051,19 @@ static void ps_read (void)
        int paging   = 0;
        int blocked  = 0;
 
-       char buf[BUFSIZE];
-       char filename[20]; /* need 17 bytes */
-       char *fields[BUFSIZE];
-
        struct dirent *ent;
-       DIR *proc;
-       FILE *fh;
+       DIR           *proc;
+       int            pid;
+
+       int        status;
+       procstat_t ps;
+       procstat_entry_t pse;
+       char       state;
+
+       procstat_t *ps_ptr;
 
        running = sleeping = zombies = stopped = paging = blocked = 0;
+       ps_list_reset ();
 
        if ((proc = opendir ("/proc")) == NULL)
        {
@@ -367,33 +1076,34 @@ static void ps_read (void)
                if (!isdigit (ent->d_name[0]))
                        continue;
 
-               if (snprintf (filename, 20, "/proc/%s/stat", ent->d_name) >= 20)
+               if ((pid = atoi (ent->d_name)) < 1)
                        continue;
 
-               if ((fh = fopen (filename, "r")) == NULL)
+               status = ps_read_process (pid, &ps, &state);
+               if (status != 0)
                {
-                       syslog (LOG_NOTICE, "Cannot open `%s': %s", filename,
-                                       strerror (errno));
+                       DBG ("ps_read_process failed: %i", status);
                        continue;
                }
 
-               if (fgets (buf, BUFSIZE, fh) == NULL)
-               {
-                       syslog (LOG_NOTICE, "Unable to read from `%s': %s",
-                                       filename, strerror (errno));
-                       fclose (fh);
-                       continue;
-               }
+               pse.id       = pid;
+               pse.age      = 0;
 
-               fclose (fh);
+               pse.num_proc = ps.num_proc;
+               pse.num_lwp  = ps.num_lwp;
+               pse.vmem_rss = ps.vmem_rss;
 
-               if (strsplit (buf, fields, BUFSIZE) < 3)
-               {
-                       DBG ("Line has less than three fields.");
-                       continue;
-               }
+               pse.vmem_minflt = 0;
+               pse.vmem_minflt_counter = ps.vmem_minflt_counter;
+               pse.vmem_majflt = 0;
+               pse.vmem_majflt_counter = ps.vmem_majflt_counter;
+
+               pse.cpu_user = 0;
+               pse.cpu_user_counter = ps.cpu_user_counter;
+               pse.cpu_system = 0;
+               pse.cpu_system_counter = ps.cpu_system_counter;
 
-               switch (fields[2][0])
+               switch (state)
                {
                        case 'R': running++;  break;
                        case 'S': sleeping++; break;
@@ -402,11 +1112,16 @@ static void ps_read (void)
                        case 'T': stopped++;  break;
                        case 'W': paging++;   break;
                }
+
+               ps_list_add (ps.name, &pse);
        }
 
        closedir (proc);
 
        ps_submit (running, sleeping, zombies, stopped, paging, 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
@@ -416,6 +1131,11 @@ static void ps_read (void)
 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);
+       cf_register (MODULE_NAME, ps_config, config_keys, config_keys_num);
 }
 
 #undef BUFSIZE
index 940120c..ff8b580 100644 (file)
  *
  * Authors:
  *   Florian octo Forster <octo at verplant.org>
+ *   
+ *   Lubos Stanek <lubek at users.sourceforge.net> Wed Oct 27, 2006
+ *   - config ExtendedSensorNaming option
+ *   - precise sensor feature selection (chip-bus-address/type-feature)
+ *     with ExtendedSensorNaming
+ *   - more sensor features (finite list)
+ *   - honor sensors.conf's ignored
+ *   - config Sensor option
+ *   - config IgnoreSelected option
  **/
 
 #include "collectd.h"
 #include "common.h"
 #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>
@@ -40,8 +53,7 @@
 
 #define BUFSIZE 512
 
-static char *filename_format = "sensors-%s.rrd";
-
+/* temperature and fan sensors */
 static char *ds_def[] =
 {
        "DS:value:GAUGE:"COLLECTD_HEARTBEAT":U:U",
@@ -49,20 +61,176 @@ static char *ds_def[] =
 };
 static int ds_num = 1;
 
-#ifdef HAVE_LIBSENSORS
+/* 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[] =
+{
+       "unknown",
+       "voltage",
+       "fanspeed",
+       "temperature",
+       NULL
+};
+#endif
+
+typedef struct sensors_labeltypes {
+       char *label;
+       int type;
+} sensors_labeltypes;
+
+/*
+ * finite list of known labels extracted from lm_sensors
+ */
+#if SENSORS_HAVE_READ
+static sensors_labeltypes known_features[] = 
+{
+       { "fan1", SENSOR_TYPE_FANSPEED },
+       { "fan2", SENSOR_TYPE_FANSPEED },
+       { "fan3", SENSOR_TYPE_FANSPEED },
+       { "fan4", SENSOR_TYPE_FANSPEED },
+       { "fan5", SENSOR_TYPE_FANSPEED },
+       { "fan6", SENSOR_TYPE_FANSPEED },
+       { "fan7", SENSOR_TYPE_FANSPEED },
+       { "AIN2", SENSOR_TYPE_VOLTAGE },
+       { "AIN1", SENSOR_TYPE_VOLTAGE },
+       { "in10", SENSOR_TYPE_VOLTAGE },
+       { "in9", SENSOR_TYPE_VOLTAGE },
+       { "in8", SENSOR_TYPE_VOLTAGE },
+       { "in7", SENSOR_TYPE_VOLTAGE },
+       { "in6", SENSOR_TYPE_VOLTAGE },
+       { "in5", SENSOR_TYPE_VOLTAGE },
+       { "in4", SENSOR_TYPE_VOLTAGE },
+       { "in3", SENSOR_TYPE_VOLTAGE },
+       { "in2", SENSOR_TYPE_VOLTAGE },
+       { "in0", SENSOR_TYPE_VOLTAGE },
+       { "CPU_Temp", SENSOR_TYPE_TEMPERATURE },
+       { "remote_temp", SENSOR_TYPE_TEMPERATURE },
+       { "temp1", SENSOR_TYPE_TEMPERATURE },
+       { "temp2", SENSOR_TYPE_TEMPERATURE },
+       { "temp3", SENSOR_TYPE_TEMPERATURE },
+       { "temp4", SENSOR_TYPE_TEMPERATURE },
+       { "temp5", SENSOR_TYPE_TEMPERATURE },
+       { "temp6", SENSOR_TYPE_TEMPERATURE },
+       { "temp7", SENSOR_TYPE_TEMPERATURE },
+       { "temp", SENSOR_TYPE_TEMPERATURE },
+       { "Vccp2", SENSOR_TYPE_VOLTAGE },
+       { "Vccp1", SENSOR_TYPE_VOLTAGE },
+       { "vdd", SENSOR_TYPE_VOLTAGE },
+       { "vid5", SENSOR_TYPE_VOLTAGE },
+       { "vid4", SENSOR_TYPE_VOLTAGE },
+       { "vid3", SENSOR_TYPE_VOLTAGE },
+       { "vid2", SENSOR_TYPE_VOLTAGE },
+       { "vid1", SENSOR_TYPE_VOLTAGE },
+       { "vid", SENSOR_TYPE_VOLTAGE },
+       { "vin4", SENSOR_TYPE_VOLTAGE },
+       { "vin3", SENSOR_TYPE_VOLTAGE },
+       { "vin2", SENSOR_TYPE_VOLTAGE },
+       { "vin1", SENSOR_TYPE_VOLTAGE },
+       { "voltbatt", SENSOR_TYPE_VOLTAGE },
+       { "volt12", SENSOR_TYPE_VOLTAGE },
+       { "volt5", SENSOR_TYPE_VOLTAGE },
+       { "vrm", SENSOR_TYPE_VOLTAGE },
+       { "5.0V", SENSOR_TYPE_VOLTAGE },
+       { "5V", SENSOR_TYPE_VOLTAGE },
+       { "3.3V", SENSOR_TYPE_VOLTAGE },
+       { "2.5V", SENSOR_TYPE_VOLTAGE },
+       { "2.0V", SENSOR_TYPE_VOLTAGE },
+       { "12V", SENSOR_TYPE_VOLTAGE },
+       { 0, -1 }
+};
+#endif
+/* end new naming */
+
+static char *config_keys[] =
+{
+       "Sensor",
+       "IgnoreSelected",
+       "ExtendedSensorNaming",
+       NULL
+};
+static int config_keys_num = 3;
+
+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
 typedef struct featurelist
 {
        const sensors_chip_name    *chip;
        const sensors_feature_data *data;
+       int                         type;
        struct featurelist         *next;
 } featurelist_t;
 
 featurelist_t *first_feature = NULL;
-#endif /* defined (HAVE_LIBSENSORS) */
+#endif /* if SENSORS_HAVE_READ */
+
+static int sensors_config (char *key, char *value)
+{
+       if (sensor_list == NULL)
+               sensor_list = ignorelist_create (1);
+
+       if (strcasecmp (key, "Sensor") == 0)
+       {
+               if (ignorelist_add (sensor_list, value))
+               {
+                       syslog (LOG_EMERG, "Cannot add value to ignorelist.");
+                       return (1);
+               }
+       }
+       else if (strcasecmp (key, "IgnoreSelected") == 0)
+       {
+               ignorelist_set_invert (sensor_list, 1);
+               if ((strcasecmp (value, "True") == 0)
+                               || (strcasecmp (value, "Yes") == 0)
+                               || (strcasecmp (value, "On") == 0))
+                       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);
+       }
+
+       return (0);
+}
 
 static void collectd_sensors_init (void)
 {
-#ifdef HAVE_LIBSENSORS
+#if SENSORS_HAVE_READ
        FILE *fh;
        featurelist_t *last_feature = NULL;
        featurelist_t *new_feature;
@@ -92,7 +260,8 @@ static void collectd_sensors_init (void)
        if (sensors_init (fh))
        {
                fclose (fh);
-               syslog (LOG_ERR, "sensors: Cannot initialize sensors. Data will not be collected.");
+               syslog (LOG_ERR, "sensors: Cannot initialize sensors. "
+                               "Data will not be collected.");
                return;
        }
 
@@ -104,49 +273,106 @@ static void collectd_sensors_init (void)
                data = NULL;
                data_num0 = data_num1 = 0;
 
-               while ((data = sensors_get_all_features (*chip, &data_num0, &data_num1)) != NULL)
+               while ((data = sensors_get_all_features (*chip, &data_num0, &data_num1))
+                               != NULL)
                {
+                       int i;
+
                        /* "master features" only */
                        if (data->mapping != SENSORS_NO_MAPPING)
                                continue;
 
-                       /* Only temperature for now.. */
-                       if (strncmp (data->name, "temp", 4)
-                                       && strncmp (data->name, "fan", 3))
-                               continue;
-
-                       if ((new_feature = (featurelist_t *) malloc (sizeof (featurelist_t))) == NULL)
+                       /* Only known features */
+                       for (i = 0; known_features[i].type >= 0; i++)
                        {
-                               perror ("malloc");
-                               continue;
-                       }
+                               if (strcmp (data->name, known_features[i].label) != 0)
+                                       continue;
+
+                               /* skip ignored in sensors.conf */
+                               if (sensors_get_ignored (*chip, data->number) == 0)
+                                       break;
+
+                               DBG ("Adding feature: %s-%s-%s",
+                                               chip->prefix,
+                                               sensor_type_prefix[known_features[i].type],
+                                               data->name);
+
+                               if ((new_feature = (featurelist_t *) malloc (sizeof (featurelist_t))) == NULL)
+                               {
+                                       DBG ("sensors plugin: malloc: %s",
+                                                       strerror (errno));
+                                       syslog (LOG_ERR, "sensors plugin: malloc: %s",
+                                                       strerror (errno));
+                                       break;
+                               }
+
+                               new_feature->chip = chip;
+                               new_feature->data = data;
+                               new_feature->type = known_features[i].type;
+                               new_feature->next = NULL;
+
+                               if (first_feature == NULL)
+                               {
+                                       first_feature = new_feature;
+                                       last_feature  = new_feature;
+                               }
+                               else
+                               {
+                                       last_feature->next = new_feature;
+                                       last_feature = new_feature;
+                               }
+
+                               /* stop searching known features at first found */
+                               break;
+                       } /* for i */
+               } /* while sensors_get_all_features */
+       } /* while sensors_get_detected_chips */
 
-                       /*
-                       syslog (LOG_INFO, "sensors: Adding feature: %s/%s", chip->prefix, data->name);
-                       */
+       if (first_feature == NULL)
+               sensors_cleanup ();
+#endif /* if SENSORS_HAVE_READ */
 
-                       new_feature->chip = chip;
-                       new_feature->data = data;
-                       new_feature->next = NULL;
+       return;
+}
 
-                       if (first_feature == NULL)
-                       {
-                               first_feature = new_feature;
-                               last_feature  = new_feature;
-                       }
-                       else
-                       {
-                               last_feature->next = new_feature;
-                               last_feature = new_feature;
-                       }
-               }
+static void sensors_shutdown (void)
+{
+#if SENSORS_HAVE_READ
+       featurelist_t *thisft = first_feature;
+       featurelist_t *nextft;
+
+       while (thisft != NULL)
+       {
+               nextft = thisft->next;
+               sfree (thisft);
+               thisft = nextft;
        }
 
-       if (first_feature == NULL)
-               sensors_cleanup ();
-#endif /* defined(HAVE_LIBSENSORS) */
+       sensors_cleanup ();
+#endif /* if SENSORS_HAVE_READ */
 
-       return;
+       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 (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)
@@ -154,41 +380,109 @@ static void sensors_write (char *host, char *inst, char *val)
        char file[BUFSIZE];
        int status;
 
-       status = snprintf (file, BUFSIZE, filename_format, inst);
-       if (status < 1)
+       /* skip ignored in our config */
+       if (ignorelist_match (sensor_list, inst))
                return;
-       else if (status >= BUFSIZE)
+
+       /* 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, ds_def, ds_num);
 }
 
 #if SENSORS_HAVE_READ
-static void sensors_submit (const char *feat_name, const char *chip_prefix, double value)
+static void sensors_submit (const char *feat_name,
+               const char *chip_prefix, double value, int type)
 {
        char buf[BUFSIZE];
        char inst[BUFSIZE];
 
-       if (snprintf (buf, BUFSIZE, "%u:%.3f", (unsigned int) curtime, value) >= BUFSIZE)
+       if (snprintf (inst, BUFSIZE, "%s-%s", chip_prefix, feat_name)
+                       >= BUFSIZE)
                return;
 
-       if (snprintf (inst, BUFSIZE, "%s-%s", chip_prefix, feat_name) >= BUFSIZE)
+       /* skip ignored in our config */
+       if (ignorelist_match (sensor_list, inst))
                return;
 
-       plugin_submit (MODULE_NAME, inst, buf);
+       if (snprintf (buf, BUFSIZE, "%u:%.3f", (unsigned int) curtime,
+                               value) >= BUFSIZE)
+               return;
+
+       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);
+       }
 }
 
 static void sensors_read (void)
 {
        featurelist_t *feature;
        double value;
+       char chip_fullprefix[BUFSIZE];
 
        for (feature = first_feature; feature != NULL; feature = feature->next)
        {
                if (sensors_get_feature (*feature->chip, feature->data->number, &value) < 0)
                        continue;
 
-               sensors_submit (feature->data->name, feature->chip->prefix, value);
+               if (sensor_extended_naming)
+               {
+                       /* 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);
+               }
+               else
+               {
+                       sensors_submit (feature->data->name,
+                                       feature->chip->prefix,
+                                       value, feature->type);
+               }
        }
 }
 #else
@@ -198,6 +492,9 @@ static void sensors_read (void)
 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
diff --git a/src/utils_dns.c b/src/utils_dns.c
new file mode 100644 (file)
index 0000000..e9aec38
--- /dev/null
@@ -0,0 +1,977 @@
+/*
+ * collectd - src/utils_dns.c
+ * Modifications Copyright (C) 2006  Florian octo Forster
+ * Copyright (C) 2002  The Measurement Factory, Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of The Measurement Factory nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors:
+ *   The Measurement Factory, Inc. <http://www.measurement-factory.com/>
+ *   Florian octo Forster <octo at verplant.org>
+ */
+
+#include "collectd.h"
+
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_PCAP_H
+# include <pcap.h>
+#endif
+#if HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+#if HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#elif HAVE_ARPA_NAMESER_COMPAT_H
+# include <arpa/nameser_compat.h>
+#endif
+
+#if HAVE_NET_IF_ARP_H
+# include <net/if_arp.h>
+#endif
+#if HAVE_NET_IF_H
+# include <net/if.h>
+#endif
+#if HAVE_NETINET_IF_ETHER_H
+# include <netinet/if_ether.h>
+#endif
+#if HAVE_NET_IF_PPP_H
+# include <net/if_ppp.h>
+#endif
+
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NETDB_H
+# include <netdb.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
+#ifdef HAVE_NETINET_IP_VAR_H
+# include <netinet/ip_var.h>
+#endif
+#if HAVE_NETINET_IP6_H
+# include <netinet/ip6.h>
+#endif
+#if HAVE_NETINET_UDP_H
+# include <netinet/udp.h>
+#endif
+
+#define PCAP_SNAPLEN 1460
+#ifndef ETHER_HDR_LEN
+#define ETHER_ADDR_LEN 6
+#define ETHER_TYPE_LEN 2
+#define ETHER_HDR_LEN (ETHER_ADDR_LEN * 2 + ETHER_TYPE_LEN)
+#endif
+#ifndef ETHERTYPE_8021Q
+# define ETHERTYPE_8021Q 0x8100
+#endif
+#ifndef ETHERTYPE_IPV6
+# define ETHERTYPE_IPV6 0x86DD
+#endif
+
+#ifndef PPP_ADDRESS_VAL
+# define PPP_ADDRESS_VAL 0xff  /* The address byte value */
+#endif
+#ifndef PPP_CONTROL_VAL
+# define PPP_CONTROL_VAL 0x03  /* The control byte value */
+#endif
+
+#ifdef __linux__
+#define uh_sport source
+#define uh_dport dest
+#endif
+
+#include "utils_dns.h"
+
+/*
+ * Type definitions
+ */
+struct ip_list_s
+{
+    struct in6_addr addr;
+    void *data;
+    struct ip_list_s *next;
+};
+typedef struct ip_list_s ip_list_t;
+
+typedef int (printer)(const char *, ...);
+
+/*
+ * flags/features for non-interactive mode
+ */
+
+#ifndef T_A6
+#define T_A6 38
+#endif
+#ifndef T_SRV
+#define T_SRV 33
+#endif
+
+/*
+ * Global variables
+ */
+int qtype_counts[T_MAX];
+int opcode_counts[OP_MAX];
+int qclass_counts[C_MAX];
+
+#if HAVE_PCAP_H
+static pcap_t *pcap_obj = NULL;
+#endif
+
+static ip_list_t *IgnoreList = NULL;
+
+#if HAVE_PCAP_H
+static void (*Callback) (const rfc1035_header_t *) = NULL;
+
+static int query_count_intvl = 0;
+static int query_count_total = 0;
+# ifdef __OpenBSD__
+static struct bpf_timeval last_ts;
+# else
+static struct timeval last_ts;
+# endif /* __OpenBSD__ */
+#endif /* HAVE_PCAP_H */
+
+static int cmp_in6_addr (const struct in6_addr *a,
+       const struct in6_addr *b)
+{
+    int i;
+
+    assert (sizeof (struct in6_addr) == 16);
+
+    for (i = 0; i < 16; i++)
+       if (a->s6_addr[i] != b->s6_addr[i])
+           break;
+
+    if (i >= 16)
+       return (0);
+
+    return (a->s6_addr[i] > b->s6_addr[i] ? 1 : -1);
+} /* int cmp_addrinfo */
+
+static inline int ignore_list_match (const struct in6_addr *addr)
+{
+    ip_list_t *ptr;
+
+    for (ptr = IgnoreList; ptr != NULL; ptr = ptr->next)
+       if (cmp_in6_addr (addr, &ptr->addr) == 0)
+           return (1);
+    return (0);
+} /* int ignore_list_match */
+
+static void ignore_list_add (const struct in6_addr *addr)
+{
+    ip_list_t *new;
+
+    if (ignore_list_match (addr) != 0)
+       return;
+
+    new = malloc (sizeof (ip_list_t));
+    if (new == NULL)
+    {
+       perror ("malloc");
+       return;
+    }
+
+    memcpy (&new->addr, addr, sizeof (struct in6_addr));
+    new->next = IgnoreList;
+
+    IgnoreList = new;
+} /* void ignore_list_add */
+
+void ignore_list_add_name (const char *name)
+{
+    struct addrinfo *ai_list;
+    struct addrinfo *ai_ptr;
+    struct in6_addr  addr;
+    int status;
+
+    status = getaddrinfo (name, NULL, NULL, &ai_list);
+    if (status != 0)
+       return;
+
+    for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+    {
+       if (ai_ptr->ai_family == AF_INET)
+       {
+           memset (&addr, '\0', sizeof (addr));
+           addr.s6_addr[10] = 0xFF;
+           addr.s6_addr[11] = 0xFF;
+           memcpy (addr.s6_addr + 12, &((struct sockaddr_in *) ai_ptr->ai_addr)->sin_addr, 4);
+
+           ignore_list_add (&addr);
+       }
+       else
+       {
+           ignore_list_add (&((struct sockaddr_in6 *) ai_ptr->ai_addr)->sin6_addr);
+       }
+    } /* for */
+
+    freeaddrinfo (ai_list);
+}
+
+#if HAVE_PCAP_H
+static void in6_addr_from_buffer (struct in6_addr *ia,
+       const void *buf, size_t buf_len,
+       int family)
+{
+    memset (ia, 0, sizeof (struct in6_addr));
+    if ((AF_INET == family) && (sizeof (uint32_t) == buf_len))
+    {
+       ia->s6_addr[10] = 0xFF;
+       ia->s6_addr[11] = 0xFF;
+       memcpy (ia->s6_addr + 12, buf, buf_len);
+    }
+    else if ((AF_INET6 == family) && (sizeof (struct in6_addr) == buf_len))
+    {
+       memcpy (ia, buf, buf_len);
+    }
+} /* void in6_addr_from_buffer */
+
+void dnstop_set_pcap_obj (pcap_t *po)
+{
+       pcap_obj = po;
+}
+
+void dnstop_set_callback (void (*cb) (const rfc1035_header_t *))
+{
+       Callback = cb;
+}
+
+#define RFC1035_MAXLABELSZ 63
+static int
+rfc1035NameUnpack(const char *buf, size_t sz, off_t * off, char *name, size_t ns
+)
+{
+    off_t no = 0;
+    unsigned char c;
+    size_t len;
+    assert(ns > 0);
+    do {
+       if ((*off) >= sz)
+           break;
+       c = *(buf + (*off));
+       if (c > 191) {
+           /* blasted compression */
+           unsigned short s;
+           off_t ptr;
+           memcpy(&s, buf + (*off), sizeof(s));
+           s = ntohs(s);
+           (*off) += sizeof(s);
+           /* Sanity check */
+           if ((*off) >= sz)
+               return 1;
+           ptr = s & 0x3FFF;
+           /* Make sure the pointer is inside this message */
+           if (ptr >= sz)
+               return 2;
+           return rfc1035NameUnpack(buf, sz, &ptr, name + no, ns - no);
+       } else if (c > RFC1035_MAXLABELSZ) {
+           /*
+            * "(The 10 and 01 combinations are reserved for future use.)"
+            */
+           break;
+           return 3;
+       } else {
+           (*off)++;
+           len = (size_t) c;
+           if (len == 0)
+               break;
+           if (len > (ns - 1))
+               len = ns - 1;
+           if ((*off) + len > sz)      /* message is too short */
+               return 4;
+           memcpy(name + no, buf + (*off), len);
+           (*off) += len;
+           no += len;
+           *(name + (no++)) = '.';
+       }
+    } while (c > 0);
+    *(name + no - 1) = '\0';
+    /* make sure we didn't allow someone to overflow the name buffer */
+    assert(no <= ns);
+    return 0;
+}
+
+static int
+handle_dns(const char *buf, int len,
+       const struct in6_addr *s_addr,
+       const struct in6_addr *d_addr)
+{
+    rfc1035_header_t qh;
+    uint16_t us;
+    off_t offset;
+    char *t;
+    int x;
+
+    /* The DNS header is 12 bytes long */
+    if (len < 12)
+       return 0;
+
+    memcpy(&us, buf + 0, 2);
+    qh.id = ntohs(us);
+
+    memcpy(&us, buf + 2, 2);
+    us = ntohs(us);
+    fprintf (stderr, "Bytes 0, 1: 0x%04hx\n", us);
+    qh.qr = (us >> 15) & 0x01;
+    qh.opcode = (us >> 11) & 0x0F;
+    qh.aa = (us >> 10) & 0x01;
+    qh.tc = (us >> 9) & 0x01;
+    qh.rd = (us >> 8) & 0x01;
+    qh.ra = (us >> 7) & 0x01;
+    qh.z  = (us >> 6) & 0x01;
+    qh.ad = (us >> 5) & 0x01;
+    qh.cd = (us >> 4) & 0x01;
+    qh.rcode = us & 0x0F;
+
+    memcpy(&us, buf + 4, 2);
+    qh.qdcount = ntohs(us);
+
+    memcpy(&us, buf + 6, 2);
+    qh.ancount = ntohs(us);
+
+    memcpy(&us, buf + 8, 2);
+    qh.nscount = ntohs(us);
+
+    memcpy(&us, buf + 10, 2);
+    qh.arcount = ntohs(us);
+
+    offset = 12;
+    memset(qh.qname, '\0', MAX_QNAME_SZ);
+    x = rfc1035NameUnpack(buf, len, &offset, qh.qname, MAX_QNAME_SZ);
+    if (0 != x)
+       return 0;
+    if ('\0' == qh.qname[0])
+       strcpy(qh.qname, ".");
+    while ((t = strchr(qh.qname, '\n')))
+       *t = ' ';
+    while ((t = strchr(qh.qname, '\r')))
+       *t = ' ';
+    for (t = qh.qname; *t; t++)
+       *t = tolower(*t);
+
+    memcpy(&us, buf + offset, 2);
+    qh.qtype = ntohs(us);
+    memcpy(&us, buf + offset + 2, 2);
+    qh.qclass = ntohs(us);
+
+    qh.length = (uint16_t) len;
+
+    /* gather stats */
+    qtype_counts[qh.qtype]++;
+    qclass_counts[qh.qclass]++;
+    opcode_counts[qh.opcode]++;
+
+    if (Callback != NULL)
+           Callback (&qh);
+
+    return 1;
+}
+
+static int
+handle_udp(const struct udphdr *udp, int len,
+       const struct in6_addr *s_addr,
+       const struct in6_addr *d_addr)
+{
+    char buf[PCAP_SNAPLEN];
+    if ((ntohs (udp->uh_dport) != 53)
+                   && (ntohs (udp->uh_sport) != 53))
+       return 0;
+    memcpy(buf, udp + 1, len - sizeof(*udp));
+    if (0 == handle_dns(buf, len - sizeof(*udp), s_addr, d_addr))
+       return 0;
+    return 1;
+}
+
+static int
+handle_ipv6 (struct ip6_hdr *ipv6, int len)
+{
+    char buf[PCAP_SNAPLEN];
+    int offset;
+    int nexthdr;
+
+    struct in6_addr s_addr;
+    struct in6_addr d_addr;
+    uint16_t payload_len;
+
+    offset = sizeof (struct ip6_hdr);
+    nexthdr = ipv6->ip6_nxt;
+    s_addr = ipv6->ip6_src;
+    d_addr = ipv6->ip6_dst;
+    payload_len = ntohs (ipv6->ip6_plen);
+
+    if (ignore_list_match (&s_addr))
+           return (0);
+
+    /* Parse extension headers. This only handles the standard headers, as
+     * defined in RFC 2460, correctly. Fragments are discarded. */
+    while ((IPPROTO_ROUTING == nexthdr) /* routing header */
+           || (IPPROTO_HOPOPTS == nexthdr) /* Hop-by-Hop options. */
+           || (IPPROTO_FRAGMENT == nexthdr) /* fragmentation header. */
+           || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
+           || (IPPROTO_DSTOPTS == nexthdr) /* destination options. */
+           || (IPPROTO_AH == nexthdr) /* destination options. */
+           || (IPPROTO_ESP == nexthdr)) /* encapsulating security payload. */
+    {
+       struct ip6_ext ext_hdr;
+       uint16_t ext_hdr_len;
+
+       /* Catch broken packets */
+       if ((offset + sizeof (struct ip6_ext)) > len)
+           return (0);
+
+       /* Cannot handle fragments. */
+       if (IPPROTO_FRAGMENT == nexthdr)
+           return (0);
+
+       memcpy (&ext_hdr, (char *) ipv6 + offset, sizeof (struct ip6_ext));
+       nexthdr = ext_hdr.ip6e_nxt;
+       ext_hdr_len = (8 * (ntohs (ext_hdr.ip6e_len) + 1));
+
+       /* This header is longer than the packets payload.. WTF? */
+       if (ext_hdr_len > payload_len)
+           return (0);
+
+       offset += ext_hdr_len;
+       payload_len -= ext_hdr_len;
+    } /* while */
+
+    /* Catch broken and empty packets */
+    if (((offset + payload_len) > len)
+           || (payload_len == 0)
+           || (payload_len > PCAP_SNAPLEN))
+       return (0);
+
+    if (IPPROTO_UDP != nexthdr)
+       return (0);
+
+    memcpy (buf, (char *) ipv6 + offset, payload_len);
+    if (handle_udp ((struct udphdr *) buf, payload_len, &s_addr, &d_addr) == 0)
+       return (0);
+
+    return (1); /* Success */
+} /* int handle_ipv6 */
+
+static int
+handle_ip(const struct ip *ip, int len)
+{
+    char buf[PCAP_SNAPLEN];
+    int offset = ip->ip_hl << 2;
+    struct in6_addr s_addr;
+    struct in6_addr d_addr;
+
+    if (ip->ip_v == 6)
+       return (handle_ipv6 ((struct ip6_hdr *) ip, len));
+
+    in6_addr_from_buffer (&s_addr, &ip->ip_src.s_addr, sizeof (ip->ip_src.s_addr), AF_INET);
+    in6_addr_from_buffer (&d_addr, &ip->ip_dst.s_addr, sizeof (ip->ip_dst.s_addr), AF_INET);
+    if (ignore_list_match (&s_addr))
+           return (0);
+    if (IPPROTO_UDP != ip->ip_p)
+       return 0;
+    memcpy(buf, (void *) ip + offset, len - offset);
+    if (0 == handle_udp((struct udphdr *) buf, len - offset, &s_addr, &d_addr))
+       return 0;
+    return 1;
+}
+
+#if HAVE_NET_IF_PPP_H
+static int
+handle_ppp(const u_char * pkt, int len)
+{
+    char buf[PCAP_SNAPLEN];
+    unsigned short us;
+    unsigned short proto;
+    if (len < 2)
+       return 0;
+    if (*pkt == PPP_ADDRESS_VAL && *(pkt + 1) == PPP_CONTROL_VAL) {
+       pkt += 2;               /* ACFC not used */
+       len -= 2;
+    }
+    if (len < 2)
+       return 0;
+    if (*pkt % 2) {
+       proto = *pkt;           /* PFC is used */
+       pkt++;
+       len--;
+    } else {
+       memcpy(&us, pkt, sizeof(us));
+       proto = ntohs(us);
+       pkt += 2;
+       len -= 2;
+    }
+    if (ETHERTYPE_IP != proto && PPP_IP != proto)
+       return 0;
+    memcpy(buf, pkt, len);
+    return handle_ip((struct ip *) buf, len);
+}
+#endif /* HAVE_NET_IF_PPP_H */
+
+static int
+handle_null(const u_char * pkt, int len)
+{
+    unsigned int family;
+    memcpy(&family, pkt, sizeof(family));
+    if (AF_INET != family)
+       return 0;
+    return handle_ip((struct ip *) (pkt + 4), len - 4);
+}
+
+#ifdef DLT_LOOP
+static int
+handle_loop(const u_char * pkt, int len)
+{
+    unsigned int family;
+    memcpy(&family, pkt, sizeof(family));
+    if (AF_INET != ntohl(family))
+       return 0;
+    return handle_ip((struct ip *) (pkt + 4), len - 4);
+}
+
+#endif
+
+#ifdef DLT_RAW
+static int
+handle_raw(const u_char * pkt, int len)
+{
+    return handle_ip((struct ip *) pkt, len);
+}
+
+#endif
+
+static int
+handle_ether(const u_char * pkt, int len)
+{
+    char buf[PCAP_SNAPLEN];
+    struct ether_header *e = (void *) pkt;
+    unsigned short etype = ntohs(e->ether_type);
+    if (len < ETHER_HDR_LEN)
+       return 0;
+    pkt += ETHER_HDR_LEN;
+    len -= ETHER_HDR_LEN;
+    if (ETHERTYPE_8021Q == etype) {
+       etype = ntohs(*(unsigned short *) (pkt + 2));
+       pkt += 4;
+       len -= 4;
+    }
+    if ((ETHERTYPE_IP != etype)
+           && (ETHERTYPE_IPV6 != etype))
+       return 0;
+    memcpy(buf, pkt, len);
+    if (ETHERTYPE_IPV6 == etype)
+       return (handle_ipv6 ((struct ip6_hdr *) buf, len));
+    else
+       return handle_ip((struct ip *) buf, len);
+}
+
+#ifdef DLT_LINUX_SLL
+static int
+handle_linux_sll (const u_char *pkt, int len)
+{
+    struct sll_header
+    {
+       uint16_t pkt_type;
+       uint16_t dev_type;
+       uint16_t addr_len;
+       uint8_t  addr[8];
+       uint16_t proto_type;
+    } *hdr;
+    uint16_t etype;
+
+    if (len < sizeof (struct sll_header))
+       return (0);
+
+    hdr  = (struct sll_header *) pkt;
+    pkt  = (u_char *) (hdr + 1);
+    len -= sizeof (struct sll_header);
+
+    etype = ntohs (hdr->proto_type);
+
+    if ((ETHERTYPE_IP != etype)
+           && (ETHERTYPE_IPV6 != etype))
+       return 0;
+
+    if (ETHERTYPE_IPV6 == etype)
+       return (handle_ipv6 ((struct ip6_hdr *) pkt, len));
+    else
+       return handle_ip((struct ip *) pkt, len);
+}
+#endif /* DLT_LINUX_SLL */
+
+/* public function */
+void handle_pcap(u_char *udata, const struct pcap_pkthdr *hdr, const u_char *pkt)
+{
+    int status;
+
+    fprintf (stderr, "handle_pcap (udata = %p, hdr = %p, pkt = %p): hdr->caplen = %i\n",
+                   (void *) udata, (void *) hdr, (void *) pkt,
+                   hdr->caplen);
+
+    if (hdr->caplen < ETHER_HDR_LEN)
+       return;
+
+    switch (pcap_datalink (pcap_obj))
+    {
+       case DLT_EN10MB:
+           status = handle_ether (pkt, hdr->caplen);
+           break;
+#if HAVE_NET_IF_PPP_H
+       case DLT_PPP:
+           status = handle_ppp (pkt, hdr->caplen);
+           break;
+#endif
+#ifdef DLT_LOOP
+       case DLT_LOOP:
+           status = handle_loop (pkt, hdr->caplen);
+           break;
+#endif
+#ifdef DLT_RAW
+       case DLT_RAW:
+           status = handle_raw (pkt, hdr->caplen);
+           break;
+#endif
+#ifdef DLT_LINUX_SLL
+       case DLT_LINUX_SLL:
+           status = handle_linux_sll (pkt, hdr->caplen);
+           break;
+#endif
+       case DLT_NULL:
+           status = handle_null (pkt, hdr->caplen);
+           break;
+
+       default:
+           fprintf (stderr, "unsupported data link type %d\n",
+                   pcap_datalink(pcap_obj));
+           status = 0;
+           break;
+    } /* switch (pcap_datalink(pcap_obj)) */
+
+    if (0 == status)
+       return;
+
+    query_count_intvl++;
+    query_count_total++;
+    last_ts = hdr->ts;
+}
+#endif /* HAVE_PCAP_H */
+
+const char *qtype_str(int t)
+{
+    static char buf[32];
+    switch (t) {
+           case ns_t_a:        return ("A");
+           case ns_t_ns:       return ("NS");
+           case ns_t_md:       return ("MD");
+           case ns_t_mf:       return ("MF");
+           case ns_t_cname:    return ("CNAME");
+           case ns_t_soa:      return ("SOA");
+           case ns_t_mb:       return ("MB");
+           case ns_t_mg:       return ("MG");
+           case ns_t_mr:       return ("MR");
+           case ns_t_null:     return ("NULL");
+           case ns_t_wks:      return ("WKS");
+           case ns_t_ptr:      return ("PTR");
+           case ns_t_hinfo:    return ("HINFO");
+           case ns_t_minfo:    return ("MINFO");
+           case ns_t_mx:       return ("MX");
+           case ns_t_txt:      return ("TXT");
+           case ns_t_rp:       return ("RP");
+           case ns_t_afsdb:    return ("AFSDB");
+           case ns_t_x25:      return ("X25");
+           case ns_t_isdn:     return ("ISDN");
+           case ns_t_rt:       return ("RT");
+           case ns_t_nsap:     return ("NSAP");
+           case ns_t_nsap_ptr: return ("NSAP-PTR");
+           case ns_t_sig:      return ("SIG");
+           case ns_t_key:      return ("KEY");
+           case ns_t_px:       return ("PX");
+           case ns_t_gpos:     return ("GPOS");
+           case ns_t_aaaa:     return ("AAAA");
+           case ns_t_loc:      return ("LOC");
+           case ns_t_nxt:      return ("NXT");
+           case ns_t_eid:      return ("EID");
+           case ns_t_nimloc:   return ("NIMLOC");
+           case ns_t_srv:      return ("SRV");
+           case ns_t_atma:     return ("ATMA");
+           case ns_t_naptr:    return ("NAPTR");
+           case ns_t_kx:       return ("KX");
+           case ns_t_cert:     return ("CERT");
+           case ns_t_a6:       return ("A6");
+           case ns_t_dname:    return ("DNAME");
+           case ns_t_sink:     return ("SINK");
+           case ns_t_opt:      return ("OPT");
+           case ns_t_tsig:     return ("TSIG");
+           case ns_t_ixfr:     return ("IXFR");
+           case ns_t_axfr:     return ("AXFR");
+           case ns_t_mailb:    return ("MAILB");
+           case ns_t_maila:    return ("MAILA");
+           case ns_t_any:      return ("ANY");
+           case ns_t_zxfr:     return ("ZXFR");
+           default:
+                   snprintf (buf, 32, "#%i", t);
+                   buf[31] = '\0';
+                   return (buf);
+    }; /* switch (t) */
+    /* NOTREACHED */
+    return (NULL);
+}
+
+const char *opcode_str (int o)
+{
+    static char buf[30];
+    switch (o) {
+    case 0:
+       return "Query";
+       break;
+    case 1:
+       return "Iquery";
+       break;
+    case 2:
+       return "Status";
+       break;
+    case 4:
+       return "Notify";
+       break;
+    case 5:
+       return "Update";
+       break;
+    default:
+       snprintf(buf, 30, "Opcode%d", o);
+       return buf;
+    }
+    /* NOTREACHED */
+}
+
+const char *rcode_str (int rcode)
+{
+       static char buf[32];
+       switch (rcode)
+       {
+               case ns_r_noerror:  return ("NOERROR");
+               case ns_r_formerr:  return ("FORMERR");
+               case ns_r_servfail: return ("SERVFAIL");
+               case ns_r_nxdomain: return ("NXDOMAIN");
+               case ns_r_notimpl:  return ("NOTIMPL");
+               case ns_r_refused:  return ("REFUSED");
+               case ns_r_yxdomain: return ("YXDOMAIN");
+               case ns_r_yxrrset:  return ("YXRRSET");
+               case ns_r_nxrrset:  return ("NXRRSET");
+               case ns_r_notauth:  return ("NOTAUTH");
+               case ns_r_notzone:  return ("NOTZONE");
+               case ns_r_max:      return ("MAX");
+               case ns_r_badsig:   return ("BADSIG");
+               case ns_r_badkey:   return ("BADKEY");
+               case ns_r_badtime:  return ("BADTIME");
+               default:
+                       snprintf (buf, 32, "RCode%i", rcode);
+                       buf[31] = '\0';
+                       return (buf);
+       }
+       /* Never reached */
+       return (NULL);
+} /* const char *rcode_str (int rcode) */
+
+#if 0
+static int
+main(int argc, char *argv[])
+{
+    char errbuf[PCAP_ERRBUF_SIZE];
+    int x;
+    struct stat sb;
+    int readfile_state = 0;
+    struct bpf_program fp;
+
+    port53 = htons(53);
+    SubReport = Sources_report;
+    ignore_addr.s_addr = 0;
+    progname = strdup(strrchr(argv[0], '/') ? strchr(argv[0], '/') + 1 : argv[0]);
+    srandom(time(NULL));
+    ResetCounters();
+
+    while ((x = getopt(argc, argv, "ab:f:i:pst")) != -1) {
+       switch (x) {
+       case 'a':
+           anon_flag = 1;
+           break;
+       case 's':
+           sld_flag = 1;
+           break;
+       case 't':
+           nld_flag = 1;
+           break;
+       case 'p':
+           promisc_flag = 0;
+           break;
+       case 'b':
+           bpf_program_str = strdup(optarg);
+           break;
+       case 'i':
+           ignore_addr.s_addr = inet_addr(optarg);
+           break;
+       case 'f':
+           set_filter(optarg);
+           break;
+       default:
+           usage();
+           break;
+       }
+    }
+    argc -= optind;
+    argv += optind;
+
+    if (argc < 1)
+       usage();
+    device = strdup(argv[0]);
+
+    if (0 == stat(device, &sb))
+       readfile_state = 1;
+    if (readfile_state) {
+       pcap_obj = pcap_open_offline(device, errbuf);
+    } else {
+       pcap_obj = pcap_open_live(device, PCAP_SNAPLEN, promisc_flag, 1000, errbuf);
+    }
+    if (NULL == pcap_obj) {
+       fprintf(stderr, "pcap_open_*: %s\n", errbuf);
+       exit(1);
+    }
+
+    if (0 == isatty(1)) {
+       if (0 == readfile_state) {
+           fprintf(stderr, "Non-interactive mode requires savefile argument\n");
+           exit(1);
+       }
+       interactive = 0;
+       print_func = printf;
+    }
+
+    memset(&fp, '\0', sizeof(fp));
+    x = pcap_compile(pcap_obj, &fp, bpf_program_str, 1, 0);
+    if (x < 0) {
+       fprintf(stderr, "pcap_compile failed\n");
+       exit(1);
+    }
+    x = pcap_setfilter(pcap_obj, &fp);
+    if (x < 0) {
+       fprintf(stderr, "pcap_setfilter failed\n");
+       exit(1);
+    }
+
+    /*
+     * non-blocking call added for Mac OS X bugfix.  Sent by Max Horn.
+     * ref http://www.tcpdump.org/lists/workers/2002/09/msg00033.html
+     */
+    x = pcap_setnonblock(pcap_obj, 1, errbuf);
+    if (x < 0) {
+       fprintf(stderr, "pcap_setnonblock failed: %s\n", errbuf);
+       exit(1);
+    }
+
+    switch (pcap_datalink(pcap_obj)) {
+    case DLT_EN10MB:
+       handle_datalink = handle_ether;
+       break;
+#if HAVE_NET_IF_PPP_H
+    case DLT_PPP:
+       handle_datalink = handle_ppp;
+       break;
+#endif
+#ifdef DLT_LOOP
+    case DLT_LOOP:
+       handle_datalink = handle_loop;
+       break;
+#endif
+#ifdef DLT_RAW
+    case DLT_RAW:
+       handle_datalink = handle_raw;
+       break;
+#endif
+    case DLT_NULL:
+       handle_datalink = handle_null;
+       break;
+    default:
+       fprintf(stderr, "unsupported data link type %d\n",
+           pcap_datalink(pcap_obj));
+       return 1;
+       break;
+    }
+    if (interactive) {
+       init_curses();
+       while (0 == Quit) {
+           if (readfile_state < 2) {
+               /*
+                * On some OSes select() might return 0 even when
+                * there are packets to process.  Thus, we always
+                * ignore its return value and just call pcap_dispatch()
+                * anyway.
+                */
+               if (0 == readfile_state)        /* interactive */
+                   pcap_select(pcap_obj, 1, 0);
+               x = pcap_dispatch(pcap_obj, 50, handle_pcap, NULL);
+           }
+           if (0 == x && 1 == readfile_state) {
+               /* block on keyboard until user quits */
+               readfile_state++;
+               nodelay(w, 0);
+           }
+           keyboard();
+           cron_pre();
+           report();
+           cron_post();
+       }
+       endwin();               /* klin, Thu Nov 28 08:56:51 2002 */
+    } else {
+       while (pcap_dispatch(pcap_obj, 50, handle_pcap, NULL))
+               (void) 0;
+       cron_pre();
+       Sources_report(); print_func("\n");
+       Destinatioreport(); print_func("\n");
+       Qtypes_report(); print_func("\n");
+       Opcodes_report(); print_func("\n");
+       Tld_report(); print_func("\n");
+       Sld_report(); print_func("\n");
+       Nld_report(); print_func("\n");
+       SldBySource_report();
+    }
+
+    pcap_close(pcap_obj);
+    return 0;
+} /* static int main(int argc, char *argv[]) */
+#endif
+/*
+ * vim:shiftwidth=4:tabstop=8:softtabstop=4
+ */
diff --git a/src/utils_dns.h b/src/utils_dns.h
new file mode 100644 (file)
index 0000000..b3f08ac
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef COLLECTD_UTILS_DNS_H
+#define COLLECTD_UTILS_DNS_H 1
+/*
+ * collectd - src/utils_dns.h
+ * Copyright (C) 2006  Florian octo Forster
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ * 3. Neither the name of The Measurement Factory nor the names of its
+ *    contributors may be used to endorse or promote products derived from this
+ *    software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ */
+
+#include "config.h"
+
+#include <arpa/nameser.h>
+
+#if HAVE_PCAP_H
+# include <pcap.h>
+#endif
+
+#define T_MAX 65536
+#define OP_MAX 16
+#define C_MAX 65536
+#define MAX_QNAME_SZ 512
+
+struct rfc1035_header_s {
+    uint16_t id;
+    unsigned int qr:1;
+    unsigned int opcode:4;
+    unsigned int aa:1;
+    unsigned int tc:1;
+    unsigned int rd:1;
+    unsigned int ra:1;
+    unsigned int z:1;
+    unsigned int ad:1;
+    unsigned int cd:1;
+    unsigned int rcode:4;
+    uint16_t qdcount;
+    uint16_t ancount;
+    uint16_t nscount;
+    uint16_t arcount;
+    uint16_t qtype;
+    uint16_t qclass;
+    char     qname[MAX_QNAME_SZ];
+    uint16_t length;
+};
+typedef struct rfc1035_header_s rfc1035_header_t;
+
+extern int qtype_counts[T_MAX];
+extern int opcode_counts[OP_MAX];
+extern int qclass_counts[C_MAX];
+
+#if HAVE_PCAP_H
+void dnstop_set_pcap_obj (pcap_t *po);
+#endif
+void dnstop_set_callback (void (*cb) (const rfc1035_header_t *));
+
+void ignore_list_add_name (const char *name);
+#if HAVE_PCAP_H
+void handle_pcap (u_char * udata, const struct pcap_pkthdr *hdr, const u_char * pkt);
+#endif
+
+const char *qtype_str(int t);
+const char *opcode_str(int o);
+const char *rcode_str (int r);
+
+#endif /* !COLLECTD_UTILS_DNS_H */
diff --git a/src/utils_ignorelist.c b/src/utils_ignorelist.c
new file mode 100644 (file)
index 0000000..532eb4b
--- /dev/null
@@ -0,0 +1,365 @@
+/**
+ * collectd - src/utils_ignorelist.c
+ * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
+ *
+ * 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.
+ *
+ * Authors:
+ *   Lubos Stanek <lubek at users.sourceforge.net>
+ **/
+/**
+ * ignorelist handles plugin's list of configured collectable
+ * entries with global ignore action
+ **/
+/**
+ * Usage:
+ * 
+ * Define plugin's global pointer variable of type ignorelist_t:
+ *   ignorelist_t *myconfig_ignore;
+ * If you know the state of the global ignore (IgnoreSelected),
+ * allocate the variable with:
+ *   myconfig_ignore = ignorelist_create (YourKnownIgnore);
+ * If you do not know the state of the global ignore,
+ * initialize the global variable and set the ignore flag later:
+ *   myconfig_ignore = ignorelist_init ();
+ * Append single entries in your cf_register'ed callback function:
+ *   ignorelist_add (myconfig_ignore, newentry);
+ * When you hit the IgnoreSelected config option,
+ * offer it to the list:
+ *   ignorelist_ignore (myconfig_ignore, instantly_got_value_of_ignore);
+ * That is all for the ignorelist initialization.
+ * Later during read and write (plugin's registered functions) get
+ * the information whether this entry would be collected or not:
+ *   if (ignorelist_match (myconfig_ignore, thisentry))
+ *     return;
+ **/
+
+#include "common.h"
+#include "utils_debug.h"
+#include "utils_ignorelist.h"
+
+/*
+ * private prototypes
+ */
+struct ignorelist_item_s
+{
+#if HAVE_REGEX_H
+       regex_t *rmatch;        /* regular expression entry identification */
+#endif
+       char *smatch;           /* string entry identification */
+       struct ignorelist_item_s *next;
+};
+typedef struct ignorelist_item_s ignorelist_item_t;
+
+struct ignorelist_s
+{
+       int ignore;             /* ignore entries */
+       ignorelist_item_t *head;        /* pointer to the first entry */
+};
+
+/* *** *** *** ********************************************* *** *** *** */
+/* *** *** *** *** *** ***   private functions   *** *** *** *** *** *** */
+/* *** *** *** ********************************************* *** *** *** */
+
+static inline void ignorelist_append (ignorelist_t *il, ignorelist_item_t *item)
+{
+       assert ((il != NULL) && (item != NULL));
+
+       item->next = il->head;
+       il->head = item;
+}
+
+#if HAVE_REGEX_H
+static int ignorelist_append_regex(ignorelist_t *il, const char *entry)
+{
+       int rcompile;
+       regex_t *regtemp;
+       int errsize;
+       char *regerr = NULL;
+       ignorelist_item_t *new;
+
+       /* create buffer */
+       if ((regtemp = malloc(sizeof(regex_t))) == NULL)
+       {
+               syslog (LOG_ERR, "cannot allocate new config entry");
+               return (1);
+       }
+       memset (regtemp, '\0', sizeof(regex_t));
+
+       /* compile regex */
+       if ((rcompile = regcomp (regtemp, entry, REG_EXTENDED)) != 0)
+       {
+               /* prepare message buffer */
+               errsize = regerror(rcompile, regtemp, NULL, 0);
+               if (errsize)
+                       regerr = smalloc(errsize);
+               /* get error message */
+               if (regerror (rcompile, regtemp, regerr, errsize))
+               {
+                       fprintf (stderr, "Cannot compile regex %s: %i/%s",
+                                       entry, rcompile, regerr);
+                       syslog (LOG_ERR, "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",
+                                       entry, rcompile);
+               }
+
+               if (errsize)
+                       sfree (regerr);
+               regfree (regtemp);
+               return (1);
+       }
+       DBG("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");
+               regfree (regtemp);
+               return (1);
+       }
+       memset (new, '\0', sizeof(ignorelist_item_t));
+       new->rmatch = regtemp;
+
+       /* append new entry */
+       ignorelist_append (il, new);
+
+       return (0);
+} /* int ignorelist_append_regex(ignorelist_t *il, const char *entry) */
+#endif
+
+static int ignorelist_append_string(ignorelist_t *il, const char *entry)
+{
+       ignorelist_item_t *new;
+
+       /* create new entry */
+       if ((new = malloc(sizeof(ignorelist_item_t))) == NULL )
+       {
+               syslog (LOG_ERR, "cannot allocate new entry");
+               return (1);
+       }
+       memset (new, '\0', sizeof(ignorelist_item_t));
+       new->smatch = sstrdup(entry);
+
+       /* append new entry */
+       ignorelist_append (il, new);
+
+       return (0);
+} /* int ignorelist_append_string(ignorelist_t *il, const char *entry) */
+
+#if HAVE_REGEX_H
+/*
+ * check list for entry regex match
+ * return 1 if found
+ */
+static int ignorelist_match_regex (ignorelist_item_t *item, const char *entry)
+{
+       assert ((item != NULL) && (item->rmatch != NULL)
+                       && (entry != NULL) && (strlen (entry) > 0));
+
+       /* match regex */
+       if (regexec (item->rmatch, entry, 0, NULL, 0) == 0)
+               return (1);
+
+       return (0);
+} /* int ignorelist_match_regex (ignorelist_item_t *item, const char *entry) */
+#endif
+
+/*
+ * check list for entry string match
+ * return 1 if found
+ */
+static int ignorelist_match_string (ignorelist_item_t *item, const char *entry)
+{
+       assert ((item != NULL) && (item->smatch != NULL)
+                       && (entry != NULL) && (strlen (entry) > 0));
+
+       if (strcmp (entry, item->smatch) == 0)
+               return (1);
+
+       return (0);
+} /* int ignorelist_match_string (ignorelist_item_t *item, const char *entry) */
+
+
+/* *** *** *** ******************************************** *** *** *** */
+/* *** *** *** *** *** ***   public functions   *** *** *** *** *** *** */
+/* *** *** *** ******************************************** *** *** *** */
+
+/*
+ * create the ignorelist_t with known ignore state
+ * return pointer to ignorelist_t
+ */
+ignorelist_t *ignorelist_create (int invert)
+{
+       ignorelist_t *il;
+
+       /* smalloc exits if it failes */
+       il = (ignorelist_t *) smalloc (sizeof (ignorelist_t));
+       DBG("Ignorelist created 0x%p, default is %s",
+                       (void *) il,
+                       invert ? "collect" : "ignore");
+
+       memset (il, '\0', sizeof (ignorelist_t));
+
+       /*
+        * ->ignore == 0  =>  collect
+        * ->ignore == 1  =>  ignore
+        */
+       il->ignore = invert ? 0 : 1;
+
+       return (il);
+} /* ignorelist_t *ignorelist_create (int ignore) */
+
+/*
+ * free memory used by ignorelist_t
+ */
+void ignorelist_free (ignorelist_t *il)
+{
+       ignorelist_item_t *this;
+       ignorelist_item_t *next;
+
+       DBG ("(il = 0x%p)", (void *) il);
+
+       if (il == NULL)
+               return;
+
+       for (this = il->head; this != NULL; this = next)
+       {
+               next = this->next;
+#if HAVE_REGEX_H
+               if (this->rmatch != NULL)
+               {
+                       regfree (this->rmatch);
+                       this->rmatch = NULL;
+               }
+#endif
+               if (this->smatch != NULL)
+               {
+                       sfree (this->smatch);
+                       this->smatch = NULL;
+               }
+               sfree (this);
+       }
+
+       sfree (il);
+       il = NULL;
+} /* void ignorelist_destroy (ignorelist_t *il) */
+
+/*
+ * set ignore state of the ignorelist_t
+ */
+void ignorelist_set_invert (ignorelist_t *il, int invert)
+{
+       if (il == NULL)
+       {
+               DBG("ignore call with ignorelist_t == NULL");
+               return;
+       }
+
+       il->ignore = invert ? 0 : 1;
+} /* void ignorelist_set_invert (ignorelist_t *il, int ignore) */
+
+/*
+ * append entry into ignorelist_t
+ * return 1 for success
+ */
+int ignorelist_add (ignorelist_t *il, const char *entry)
+{
+       int ret;
+       size_t entry_len;
+
+       if (il == NULL)
+       {
+               DBG ("add called with ignorelist_t == NULL");
+               return (1);
+       }
+
+       entry_len = strlen (entry);
+
+       /* append nothing */
+       if (entry_len == 0)
+       {
+               DBG("not appending: empty entry");
+               return (1);
+       }
+
+#if HAVE_REGEX_H
+       /* regex string is enclosed in "/.../" */
+       if ((entry_len > 2) && (entry[0] == '/') && entry[entry_len - 1] == '/')
+       {
+               char *entry_copy;
+
+               /* We need to copy `entry' since it's const */
+               entry_copy = smalloc (entry_len);
+               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);
+               ret = ignorelist_append_regex(il, entry_copy);
+               sfree (entry_copy);
+       }
+       else
+#endif
+       {
+               DBG("to add entry: %s", entry);
+               ret = ignorelist_append_string(il, entry);
+       }
+
+       return (ret);
+} /* int ignorelist_add (ignorelist_t *il, const char *entry) */
+
+/*
+ * check list for entry
+ * return 1 for ignored entry
+ */
+int ignorelist_match (ignorelist_t *il, const char *entry)
+{
+       ignorelist_item_t *traverse;
+
+       assert (il != NULL);
+
+       /* if no entries, collect all */
+       if (il->head == NULL)
+               return (0);
+
+       if ((entry == NULL) || (strlen (entry) == 0))
+               return (0);
+
+       /* traverse list and check entries */
+       for (traverse = il->head; traverse != NULL; traverse = traverse->next)
+       {
+#if HAVE_REGEX_H
+               if (traverse->rmatch != NULL)
+               {
+                       if (ignorelist_match_regex (traverse, entry))
+                               return (il->ignore);
+               }
+               else
+#endif
+               {
+                       if (ignorelist_match_string (traverse, entry))
+                               return (il->ignore);
+               }
+       } /* for traverse */
+
+       return (1 - il->ignore);
+} /* int ignorelist_match (ignorelist_t *il, const char *entry) */
+
diff --git a/src/utils_ignorelist.h b/src/utils_ignorelist.h
new file mode 100644 (file)
index 0000000..b47b55a
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * collectd - src/utils_ignorelist.h
+ * Copyright (C) 2006 Lubos Stanek <lubek at users.sourceforge.net>
+ *
+ * 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.
+ *
+ * Authors:
+ *   Lubos Stanek <lubek at users.sourceforge.net>
+ **/
+/**
+ * ignorelist handles plugin's list of configured collectable
+ * entries with global ignore action
+ **/
+
+#ifndef UTILS_IGNORELIST_H
+#define UTILS_IGNORELIST_H 1
+
+#include "collectd.h"
+
+#if HAVE_REGEX_H
+# include <regex.h>
+#endif
+
+/* public prototypes */
+
+struct ignorelist_s;
+typedef struct ignorelist_s ignorelist_t;
+
+/*
+ * create the ignorelist_t with known ignore state
+ * return pointer to ignorelist_t
+ */
+ignorelist_t *ignorelist_create (int invert);
+
+/*
+ * free memory used by ignorelist_t
+ */
+void ignorelist_free (ignorelist_t *il);
+
+/*
+ * set ignore state of the ignorelist_t
+ */
+void ignorelist_set_invert (ignorelist_t *il, int invert);
+
+/*
+ * append entry to ignorelist_t
+ * returns zero on success, non-zero upon failure.
+ */
+int ignorelist_add (ignorelist_t *il, const char *entry);
+
+/*
+ * check list for entry
+ * return 1 for ignored entry
+ */
+int ignorelist_match (ignorelist_t *il, const char *entry);
+
+#endif /* UTILS_IGNORELIST_H */
index 694b6d0..3a77961 100644 (file)
@@ -343,11 +343,17 @@ static void vserver_read (void)
        DIR                     *proc;
        struct dirent   *dent; /* 42 */
 
+       static complain_t complain_obj;
+
        errno = 0;
-       if (NULL == (proc = opendir (PROCDIR))) {
-               syslog (LOG_ERR, "Cannot open '%s': %s", PROCDIR, strerror (errno));
+       if (NULL == (proc = opendir (PROCDIR)))
+       {
+               plugin_complain (LOG_ERR, &complain_obj, "vserver plugin: "
+                               "fopen (%s) failed: %s", PROCDIR, strerror (errno));
                return;
        }
+       plugin_relief (LOG_NOTICE, &complain_obj, "vserver plugin: "
+                       "fopen (%s) succeeded.", PROCDIR);
 
        while (NULL != (dent = readdir (proc))) {
                int  len;