Merge branch 'collectd-5.0'
authorFlorian Forster <octo@collectd.org>
Sun, 27 Mar 2011 17:06:47 +0000 (10:06 -0700)
committerFlorian Forster <octo@collectd.org>
Sun, 27 Mar 2011 17:06:47 +0000 (10:06 -0700)
12 files changed:
.gitignore
ChangeLog
contrib/collection3/bin/graph.cgi
contrib/collection3/bin/index.cgi
contrib/collection3/lib/Collectd/Graph/Common.pm
contrib/collection3/lib/Collectd/Graph/TypeLoader.pm
src/collectd.conf.in
src/collectd.conf.pod
src/modbus.c
src/rrdcached.c
src/snmp.c
src/swap.c

index cbdd62f..75cb307 100644 (file)
@@ -33,6 +33,7 @@ src/stamp-h1
 .libs/
 src/collectd
 src/collectd-nagios
+src/collectdctl
 src/collectdmon
 src/*.1
 src/*.5
index fb55560..234a311 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -43,7 +43,7 @@
          technique of POWER CPUs. Thanks to AurĂ©lien Reynaud for his code and
          patience.
        * modbus plugin: Support for libmodbus 2.9.2 has been added and the
-         license has been changes to LGPLv2.1.
+         license has been changed to LGPLv2.1.
        * mysql plugin: Backwards compatibility code has been removed. The
          data sets used have been improved.
        * network plugin: The default buffer size has been increased to
index 8a5bf85..2b3f2fe 100755 (executable)
@@ -1,14 +1,45 @@
 #!/usr/bin/perl
 
+# Copyright (C) 2008-2011  Florian Forster
+# Copyright (C) 2011       noris network AG
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; only version 2 of the License is applicable.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+# Authors:
+#   Florian "octo" Forster <octo at collectd.org>
+
 use strict;
 use warnings;
-use lib ('../lib');
 use utf8;
+use vars (qw($BASE_DIR));
+
+BEGIN
+{
+  if (defined $ENV{'SCRIPT_FILENAME'})
+  {
+    if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+    {
+      $::BASE_DIR = $1;
+      unshift (@::INC, "$::BASE_DIR/lib");
+    }
+  }
+}
 
-use FindBin ('$RealBin');
 use Carp (qw(confess cluck));
 use CGI (':cgi');
 use RRDs ();
+use File::Temp (':POSIX');
 
 use Collectd::Graph::Config (qw(gc_read_config gc_get_scalar));
 use Collectd::Graph::TypeLoader (qw(tl_load_type));
@@ -17,200 +48,287 @@ use Collectd::Graph::Common (qw(sanitize_type get_selected_files
       epoch_to_rfc1123 flush_files));
 use Collectd::Graph::Type ();
 
-our $Debug = param ('debug');
-our $Begin = param ('begin');
-our $End = param ('end');
-our $GraphWidth = param ('width');
-our $GraphHeight = param ('height');
-our $Index = param ('index') || 0;
-our $OutputFormat = 'PNG';
-our $ContentType = 'image/png';
-
-if (param ('format'))
+sub base_dir
 {
-  my $temp = param ('format') || '';
-  $temp = uc ($temp);
+  if (defined $::BASE_DIR)
+  {
+    return ($::BASE_DIR);
+  }
 
-  if ($temp =~ m/^(PNG|SVG|EPS|PDF)$/)
+  if (!defined ($ENV{'SCRIPT_FILENAME'}))
   {
-    $OutputFormat = $temp;
+    return;
+  }
 
-    if ($OutputFormat eq 'SVG') { $ContentType = 'image/svg+xml'; }
-    elsif ($OutputFormat eq 'EPS') { $ContentType = 'image/eps'; }
-    elsif ($OutputFormat eq 'PDF') { $ContentType = 'application/pdf'; }
+  if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+  {
+    $::BASE_DIR = $1;
+    return ($::BASE_DIR);
   }
+
+  return;
 }
 
-if ($Debug)
+sub lib_dir
 {
-  print <<HTTP;
-Content-Type: text/plain
+  my $base = base_dir ();
 
-HTTP
+  if ($base)
+  {
+    return "$base/lib";
+  }
+  else
+  {
+    return "../lib";
+  }
 }
 
-gc_read_config ("$RealBin/../etc/collection.conf");
-
-if ($GraphWidth)
+sub sysconf_dir
 {
-  $GraphWidth =~ s/\D//g;
-}
+  my $base = base_dir ();
 
-if (!$GraphWidth)
-{
-  $GraphWidth = gc_get_scalar ('GraphWidth', 400);
+  if ($base)
+  {
+    return "$base/etc";
+  }
+  else
+  {
+    return "../etc";
+  }
 }
 
-if ($GraphHeight)
+sub init
 {
-  $GraphHeight =~ s/\D//g;
+  my $lib_dir = lib_dir ();
+  my $sysconf_dir = sysconf_dir ();
+
+  if (!grep { $lib_dir eq $_ } (@::INC))
+  {
+    unshift (@::INC, $lib_dir);
+  }
+
+  gc_read_config ("$sysconf_dir/collection.conf");
 }
 
-if (!$GraphHeight)
+sub main
 {
-  $GraphHeight = gc_get_scalar ('GraphHeight', 100);
-}
+  my $Begin = param ('begin');
+  my $End = param ('end');
+  my $GraphWidth = param ('width');
+  my $GraphHeight = param ('height');
+  my $Index = param ('index') || 0;
+  my $OutputFormat = 'PNG';
+  my $ContentType = 'image/png';
 
-{ # Sanitize begin and end times
-  $End ||= 0;
-  $Begin ||= 0;
+  init ();
 
-  if ($End =~ m/\D/)
+  if (param ('format'))
   {
-    $End = 0;
+    my $temp = param ('format') || '';
+    $temp = uc ($temp);
+
+    if ($temp =~ m/^(PNG|SVG|EPS|PDF)$/)
+    {
+      $OutputFormat = $temp;
+
+      if ($OutputFormat eq 'SVG') { $ContentType = 'image/svg+xml'; }
+      elsif ($OutputFormat eq 'EPS') { $ContentType = 'image/eps'; }
+      elsif ($OutputFormat eq 'PDF') { $ContentType = 'application/pdf'; }
+    }
   }
 
-  if (!$Begin || !($Begin =~ m/^-?([1-9][0-9]*)$/))
+  if (param ('debug'))
   {
-    $Begin = -86400;
+    print <<HTTP;
+Content-Type: text/plain
+
+HTTP
+    $ContentType = 'text/plain';
   }
 
-  if ($Begin < 0)
+  if ($GraphWidth)
   {
-    if ($End)
-    {
-      $Begin = $End + $Begin;
-    }
-    else
-    {
-      $Begin = time () + $Begin;
-    }
+    $GraphWidth =~ s/\D//g;
   }
 
-  if ($Begin < 0)
+  if (!$GraphWidth)
   {
-    $Begin = time () - 86400;
+    $GraphWidth = gc_get_scalar ('GraphWidth', 400);
   }
 
-  if (($End > 0) && ($Begin > $End))
+  if ($GraphHeight)
   {
-    my $temp = $End;
-    $End = $Begin;
-    $Begin = $temp;
+    $GraphHeight =~ s/\D//g;
   }
-}
 
-my $type = param ('type') or die;
-my $obj;
+  if (!$GraphHeight)
+  {
+    $GraphHeight = gc_get_scalar ('GraphHeight', 100);
+  }
 
-$obj = tl_load_type ($type);
-if (!$obj)
-{
-  confess ("tl_load_type ($type) failed");
-}
+  { # Sanitize begin and end times
+    $End ||= 0;
+    $Begin ||= 0;
 
-$type = ucfirst (lc ($type));
-$type =~ s/_([A-Za-z])/\U$1\E/g;
-$type = sanitize_type ($type);
+    if ($End =~ m/\D/)
+    {
+      $End = 0;
+    }
 
-my $files = get_selected_files ();
-if ($Debug)
-{
-  require Data::Dumper;
-  print STDOUT Data::Dumper->Dump ([$files], ['files']);
-}
-for (@$files)
-{
-  $obj->addFiles ($_);
-}
+    if (!$Begin || !($Begin =~ m/^-?([1-9][0-9]*)$/))
+    {
+      $Begin = -86400;
+    }
 
-my $expires = time ();
-# IF (End is `now')
-#    OR (Begin is before `now' AND End is after `now')
-if (($End == 0) || (($Begin <= $expires) && ($End >= $expires)))
-{
-  # 400 == width in pixels
-  my $timespan;
+    if ($Begin < 0)
+    {
+      if ($End)
+      {
+        $Begin = $End + $Begin;
+      }
+      else
+      {
+        $Begin = time () + $Begin;
+      }
+    }
+
+    if ($Begin < 0)
+    {
+      $Begin = time () - 86400;
+    }
+
+    if (($End > 0) && ($Begin > $End))
+    {
+      my $temp = $End;
+      $End = $Begin;
+      $Begin = $temp;
+    }
+  }
 
-  if ($End == 0)
+  my $type = param ('type') or die;
+  my $obj;
+
+  $obj = tl_load_type ($type);
+  if (!$obj)
   {
-    $timespan = $expires - $Begin;
+    confess ("tl_load_type ($type) failed");
   }
-  else
+
+  $type = ucfirst (lc ($type));
+  $type =~ s/_([A-Za-z])/\U$1\E/g;
+  $type = sanitize_type ($type);
+
+  my $files = get_selected_files ();
+  if (param ('debug'))
   {
-    $timespan = $End - $Begin;
+    require Data::Dumper;
+    print Data::Dumper->Dump ([$files], ['files']);
+  }
+  for (@$files)
+  {
+    $obj->addFiles ($_);
+  }
+
+  my $expires = time ();
+# IF (End is `now')
+#    OR (Begin is before `now' AND End is after `now')
+  if (($End == 0) || (($Begin <= $expires) && ($End >= $expires)))
+  {
+    # 400 == width in pixels
+    my $timespan;
+
+    if ($End == 0)
+    {
+      $timespan = $expires - $Begin;
+    }
+    else
+    {
+      $timespan = $End - $Begin;
+    }
+    $expires += int ($timespan / 400.0);
   }
-  $expires += int ($timespan / 400.0);
-}
 # IF (End is not `now')
 #    AND (End is before `now')
 # ==> Graph will never change again!
-elsif (($End > 0) && ($End < $expires))
-{
-  $expires += (366 * 86400);
-}
-elsif ($Begin > $expires)
-{
-  $expires = $Begin;
-}
+  elsif (($End > 0) && ($End < $expires))
+  {
+    $expires += (366 * 86400);
+  }
+  elsif ($Begin > $expires)
+  {
+    $expires = $Begin;
+  }
 
 # Send FLUSH command to the daemon if necessary and possible.
-flush_files ($files,
+  flush_files ($files,
     begin => $Begin,
     end => $End,
     addr => gc_get_scalar ('UnixSockAddr', undef),
     interval => gc_get_scalar ('Interval', 10));
 
-print STDOUT header (-Content_type => $ContentType,
-  -Last_Modified => epoch_to_rfc1123 ($obj->getLastModified ()),
-  -Expires => epoch_to_rfc1123 ($expires));
-
-if ($Debug)
-{
-  print "\$expires = $expires;\n";
-}
+  print header (-Content_type => $ContentType,
+    -Last_Modified => epoch_to_rfc1123 ($obj->getLastModified ()),
+    -Expires => epoch_to_rfc1123 ($expires));
 
-my $args = $obj->getRRDArgs (0 + $Index);
-
-if ($Debug)
-{
-  require Data::Dumper;
-  print STDOUT Data::Dumper->Dump ([$obj], ['obj']);
-  print STDOUT join (",\n", @$args) . "\n";
-  print STDOUT "Last-Modified: " . epoch_to_rfc1123 ($obj->getLastModified ()) . "\n";
-}
-else
-{
-  my @timesel = ();
+  if (param ('debug'))
+  {
+    print "\$expires = $expires;\n";
+  }
 
-  if ($End) # $Begin is always true
+  my $args = $obj->getRRDArgs (0 + $Index);
+  if (param ('debug'))
   {
-    @timesel = ('-s', $Begin, '-e', $End);
+    require Data::Dumper;
+    print Data::Dumper->Dump ([$obj], ['obj']);
+    print join (",\n", @$args) . "\n";
+    print "Last-Modified: " . epoch_to_rfc1123 ($obj->getLastModified ()) . "\n";
   }
   else
   {
-    @timesel = ('-s', $Begin); # End is implicitely `now'.
-  }
+    my @timesel = ();
+    my $tmpfile = tmpnam ();
+    my $status;
 
-  $| = 1;
-  RRDs::graph ('-', '-a', $OutputFormat, '--width', $GraphWidth, '--height', $GraphHeight, @timesel, @$args);
-  if (my $err = RRDs::error ())
-  {
-    print STDERR "RRDs::graph failed: $err\n";
-    exit (1);
+    if ($End) # $Begin is always true
+    {
+      @timesel = ('-s', $Begin, '-e', $End);
+    }
+    else
+    {
+      @timesel = ('-s', $Begin); # End is implicitely `now'.
+    }
+
+    if (-S "/var/run/rrdcached.sock" && -w "/var/run/rrdcached.sock")
+    {
+      $ENV{"RRDCACHED_ADDRESS"} = "/var/run/rrdcached.sock";
+    }
+    unlink ($tmpfile);
+    RRDs::graph ($tmpfile, '-a', $OutputFormat, '--width', $GraphWidth, '--height', $GraphHeight, @timesel, @$args);
+    if (my $err = RRDs::error ())
+    {
+      print STDERR "RRDs::graph failed: $err\n";
+      exit (1);
+    }
+
+    $status = open (IMG, '<', $tmpfile) or die ("open ($tmpfile): $!");
+    if (!$status)
+    {
+      print STDERR "graph.cgi: Unable to open temporary file \"$tmpfile\" for reading: $!\n";
+    }
+    else
+    {
+      local $/ = undef;
+      while (my $data = <IMG>)
+      {
+        print STDOUT $data;
+      }
+
+      close (IMG);
+      unlink ($tmpfile);
+    }
   }
-}
+} # sub main
 
-exit (0);
+main ();
 
 # vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
index 85064b8..027961f 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/perl
 
-# Copyright (C) 2008  Florian octo Forster <octo at verplant.org>
+# Copyright (C) 2008-2011  Florian Forster
+# Copyright (C) 2011       noris network AG
 #
 # This program is free software; you can redistribute it and/or modify it under
 # the terms of the GNU General Public License as published by the Free Software
 # You should have received a copy of the GNU General Public License along with
 # this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+# Authors:
+#   Florian "octo" Forster <octo at collectd.org>
 
 use strict;
 use warnings;
-use lib ('../lib');
 use utf8;
+use vars (qw($BASE_DIR));
+
+BEGIN
+{
+  if (defined $ENV{'SCRIPT_FILENAME'})
+  {
+    if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+    {
+      $::BASE_DIR = $1;
+      unshift (@::INC, "$::BASE_DIR/lib");
+    }
+  }
+}
 
 use Carp (qw(cluck confess));
-use FindBin ('$RealBin');
 use CGI (':cgi');
 use CGI::Carp ('fatalsToBrowser');
 use HTML::Entities ('encode_entities');
@@ -35,8 +50,6 @@ use Collectd::Graph::Common (qw(get_files_from_directory get_all_hosts
       get_plugin_selection flush_files));
 use Collectd::Graph::Type ();
 
-our $Debug = param ('debug') ? 1 : 0;
-
 our $TimeSpans =
 {
   Hour  =>        3600,
@@ -46,23 +59,90 @@ our $TimeSpans =
   Year  => 366 * 86400
 };
 
-my $action = param ('action') || 'list_hosts';
-our %Actions =
+my %Actions =
 (
   list_hosts => \&action_list_hosts,
   show_selection => \&action_show_selection
 );
 
-if (!exists ($Actions{$action}))
+sub base_dir
 {
-  print STDERR "No such action: $action\n";
-  exit 1;
+  if (defined $::BASE_DIR)
+  {
+    return ($::BASE_DIR);
+  }
+
+  if (!defined ($ENV{'SCRIPT_FILENAME'}))
+  {
+    return;
+  }
+
+  if ($ENV{'SCRIPT_FILENAME'} =~ m{^(/.+)/bin/[^/]+$})
+  {
+    $::BASE_DIR = $1;
+    return ($::BASE_DIR);
+  }
+
+  return;
 }
 
-gc_read_config ("$RealBin/../etc/collection.conf");
+sub lib_dir
+{
+  my $base = base_dir ();
 
-$Actions{$action}->();
-exit (0);
+  if ($base)
+  {
+    return "$base/lib";
+  }
+  else
+  {
+    return "../lib";
+  }
+}
+
+sub sysconf_dir
+{
+  my $base = base_dir ();
+
+  if ($base)
+  {
+    return "$base/etc";
+  }
+  else
+  {
+    return "../etc";
+  }
+}
+
+sub init
+{
+  my $lib_dir = lib_dir ();
+  my $sysconf_dir = sysconf_dir ();
+
+  if (!grep { $lib_dir eq $_ } (@::INC))
+  {
+    unshift (@::INC, $lib_dir);
+  }
+
+  gc_read_config ("$sysconf_dir/collection.conf");
+}
+
+sub main
+{
+  my $Debug = param ('debug') ? 1 : 0;
+  my $action = param ('action') || 'list_hosts';
+
+  if (!exists ($Actions{$action}))
+  {
+    print STDERR "No such action: $action\n";
+    return (1);
+  }
+
+  init ();
+
+  $Actions{$action}->();
+  return (1);
+} # sub main
 
 sub can_handle_xhtml
 {
@@ -104,7 +184,7 @@ sub can_handle_xhtml
   }
 } # can_handle_xhtml
 
-{my $html_started;
+my $html_started;
 sub start_html
 {
   return if ($html_started);
@@ -119,9 +199,8 @@ sub start_html
 
   if (can_handle_xhtml ())
   {
+    print header (-Content_Type => 'application/xhtml+xml; charset=UTF-8');
     print <<HTML;
-Content-Type: application/xhtml+xml; charset=UTF-8
-
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
@@ -133,9 +212,8 @@ HTML
   }
   else
   {
+    print header (-Content_Type => 'text/html; charset=UTF-8');
     print <<HTML;
-Content-Type: text/html; charset=UTF-8
-
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
     "http://www.w3.org/TR/html4/strict.dtd">
 <html>
@@ -151,7 +229,7 @@ HTML
   <body onload="nav_init ($begin, $end);">
 HTML
   $html_started = 1;
-}}
+}
 
 sub end_html
 {
@@ -159,6 +237,7 @@ sub end_html
   </body>
 </html>
 HTML
+  $html_started = 0;
 }
 
 sub show_selector
@@ -282,7 +361,7 @@ sub action_show_selection
   $all_files = get_selected_files ();
   $timespan = get_timespan_selection ();
 
-  if ($Debug)
+  if (param ('debug'))
   {
     print "<pre>", Data::Dumper->Dump ([$all_files], ['all_files']), "</pre>\n";
   }
@@ -307,7 +386,7 @@ sub action_show_selection
       $types->{$type} = tl_load_type ($file->{'type'});
       if (!$types->{$type})
       {
-        cluck ("tl_load_type (" . $file->{'type'} . ") failed");
+        warn ("tl_load_type (" . $file->{'type'} . ") failed");
         next;
       }
     }
@@ -320,6 +399,12 @@ sub action_show_selection
   for (sort (keys %$types))
   {
     my $type = $_;
+
+    if (!defined ($types->{$type}))
+    {
+      next;
+    }
+
     my $graphs_num = $types->{$type}->getGraphsNum ();
 
     for (my $i = 0; $i < $graphs_num; $i++)
@@ -374,6 +459,8 @@ EOF
   end_html ();
 }
 
+main ();
+
 =head1 SEE ALSO
 
 L<Collectd::Graph::Type>
index c6e2508..cc7e141 100644 (file)
@@ -1,5 +1,24 @@
 package Collectd::Graph::Common;
 
+# Copyright (C) 2008-2011  Florian Forster
+# Copyright (C) 2011       noris network AG
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; only version 2 of the License is applicable.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+# Authors:
+#   Florian "octo" Forster <octo at collectd.org>
+
 use strict;
 use warnings;
 
@@ -11,6 +30,8 @@ use CGI (':cgi');
 use Exporter;
 use Collectd::Graph::Config (qw(gc_get_scalar));
 
+our $Cache = {};
+
 $ColorCanvas   = 'FFFFFF';
 $ColorFullBlue = '0000FF';
 $ColorHalfBlue = 'B7B7F7';
@@ -191,22 +212,48 @@ sub ident_to_filename
   return ($ret);
 } # ident_to_filename
 
+sub _part_to_string
+{
+  my $part = shift;
+
+  if (!defined ($part))
+  {
+    return ("(UNDEF)");
+  }
+  if (ref ($part) eq 'ARRAY')
+  {
+    if (1 == @$part)
+    {
+      return ($part->[0]);
+    }
+    else
+    {
+      return ('(' . join (',', @$part) . ')');
+    }
+  }
+  else
+  {
+    return ($part);
+  }
+} # _part_to_string
+
 sub ident_to_string
 {
   my $ident = shift;
 
   my $ret = '';
 
-  $ret .= $ident->{'hostname'} . '/' . $ident->{'plugin'};
+  $ret .= _part_to_string ($ident->{'hostname'})
+  . '/' . _part_to_string ($ident->{'plugin'});
   if (defined ($ident->{'plugin_instance'}))
   {
-    $ret .= '-' . $ident->{'plugin_instance'};
+    $ret .= '-' . _part_to_string ($ident->{'plugin_instance'});
   }
 
-  $ret .= '/' . $ident->{'type'};
+  $ret .= '/' . _part_to_string ($ident->{'type'});
   if (defined ($ident->{'type_instance'}))
   {
-    $ret .= '-' . $ident->{'type_instance'};
+    $ret .= '-' . _part_to_string ($ident->{'type_instance'});
   }
 
   return ($ret);
@@ -258,27 +305,36 @@ sub get_files_from_directory
 
 sub get_all_hosts
 {
-  my $dh;
-  my @ret = ();
-  my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
+  my $ret = [];
 
-  opendir ($dh, "$data_dir") or confess ("opendir ($data_dir): $!");
-  while (my $entry = readdir ($dh))
+  if (defined ($Cache->{'get_all_hosts'}))
   {
-    next if ($entry =~ m/^\./);
-    next if (!-d "$data_dir/$entry");
-    next if (!-r "$data_dir/$entry" or !-x "$data_dir/$entry");
-    push (@ret, sanitize_hostname ($entry));
+    $ret = $Cache->{'get_all_hosts'};
+  }
+  else
+  {
+    my $dh;
+    my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
+
+    opendir ($dh, "$data_dir") or confess ("opendir ($data_dir): $!");
+    while (my $entry = readdir ($dh))
+    {
+      next if ($entry =~ m/^\./);
+      next if (!-d "$data_dir/$entry");
+      push (@$ret, sanitize_hostname ($entry));
+    }
+    closedir ($dh);
+
+    $Cache->{'get_all_hosts'} = $ret;
   }
-  closedir ($dh);
 
   if (wantarray ())
   {
-    return (@ret);
+    return (@$ret);
   }
-  elsif (@ret)
+  elsif (@$ret)
   {
-    return (\@ret);
+    return ($ret);
   }
   else
   {
@@ -292,12 +348,32 @@ sub get_all_plugins
   my $ret = {};
   my $dh;
   my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
+  my $cache_key;
 
-  if (!@hosts)
+  if (@hosts)
   {
+    $cache_key = join (';', @hosts);
+  }
+  else
+  {
+    $cache_key = "/*/";
     @hosts = get_all_hosts ();
   }
 
+  if (defined ($Cache->{'get_all_plugins'}{$cache_key}))
+  {
+    $ret = $Cache->{'get_all_plugins'}{$cache_key};
+
+    if (wantarray ())
+    {
+      return (sort (keys %$ret));
+    }
+    else
+    {
+      return ($ret);
+    }
+  }
+
   for (@hosts)
   {
     my $host = $_;
@@ -331,6 +407,7 @@ sub get_all_plugins
     closedir ($dh);
   } # for (@hosts)
 
+  $Cache->{'get_all_plugins'}{$cache_key} = $ret;
   if (wantarray ())
   {
     return (sort (keys %$ret));
@@ -386,24 +463,44 @@ sub _filter_ident
   return (0);
 } # _filter_ident
 
+sub _get_all_files
+{
+  my $ret;
+
+  if (defined ($Cache->{'_get_all_files'}))
+  {
+    $ret = $Cache->{'_get_all_files'};
+  }
+  else
+  {
+    my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
+
+    $ret = get_files_from_directory ($data_dir, 3);
+    $Cache->{'_get_all_files'} = $ret;
+  }
+
+  return ($ret);
+} # _get_all_files
+
 sub get_files_by_ident
 {
   my $ident = shift;
   my $all_files;
   my @ret = ();
-  my $data_dir = gc_get_scalar ('DataDir', $DefaultDataDir);
 
-  #if ($ident->{'hostname'})
-  #{
-  #$all_files = get_files_for_host ($ident->{'hostname'});
-  #}
-  #else
-  #{
-    $all_files = get_files_from_directory ($data_dir, 3);
-    #}
+  my $cache_key = ident_to_string ($ident);
+  if (defined ($Cache->{'get_files_by_ident'}{$cache_key}))
+  {
+    my $ret = $Cache->{'get_files_by_ident'}{$cache_key};
+
+    return ($ret)
+  }
+
+  $all_files = _get_all_files ();
 
   @ret = grep { _filter_ident ($ident, $_) == 0 } (@$all_files);
 
+  $Cache->{'get_files_by_ident'}{$cache_key} = \@ret;
   return (\@ret);
 } # get_files_by_ident
 
index 9078110..5a0b522 100644 (file)
@@ -92,7 +92,8 @@ sub _load_module_from_config
     $obj = _create_object ($module);
     if (!$obj)
     {
-      cluck ("Creating an $module object failed");
+      #cluck ("Creating an $module object failed");
+      warn ("Creating an $module object failed");
       return;
     }
   }
index 38b7c90..94cf2a9 100644 (file)
 #   </Host>
 #</Plugin>
 
+#<Plugin "swap">
+#      ReportByDevice false
+#</Plugin>
+
 #<Plugin "table">
 #      <Table "/proc/slabinfo">
 #              Instance "slabinfo"
index de3e516..b0f8dfe 100644 (file)
@@ -1933,11 +1933,11 @@ Configures the base register to read from the device. If the option
 B<RegisterType> has been set to B<Uint32> or B<Float>, this and the next
 register will be read (the register number is increased by one).
 
-=item B<RegisterType> B<Uint16>|B<Uint32>|B<Float>
+=item B<RegisterType> B<Int16>|B<Int32>|B<Uint16>|B<Uint32>|B<Float>
 
-Specifies what kind of data is returned by the device. If the type is B<Uint32>
-or B<Float>, two 16E<nbsp>bit registers will be read and the data is combined
-into one value. Defaults to B<Uint16>.
+Specifies what kind of data is returned by the device. If the type is B<Int32>,
+B<Uint32> or B<Float>, two 16E<nbsp>bit registers will be read and the data is
+combined into one value. Defaults to B<Uint16>.
 
 =item B<Type> I<Type>
 
@@ -4177,19 +4177,19 @@ L<collectd-snmp(5)>. Please see there for details.
 =head2 Plugin C<swap>
 
 The I<Swap plugin> collects information about used and available swap space. On
-I<Solaris>, the following options are available:
+I<Linux> and I<Solaris>, the following options are available:
 
 =over 4
 
 =item B<ReportByDevice> B<false>|B<true>
 
-Configures how to report physical swap devices. If set to B<false> is used (the
+Configures how to report physical swap devices. If set to B<false> (the
 default), the summary over all swap devices is reported only, i.e. the globally
 used and available space over all devices. If B<true> is configured, the used
 and available space of each device will be reported separately.
 
-This option is only available if the I<Swap plugin> can use the L<swapctl(2)>
-mechanism under I<Solaris>.
+This option is only available if the I<Swap plugin> can read C</proc/swaps>
+(under Linux) or use the L<swapctl(2)> mechanism (under I<Solaris>).
 
 =back
 
index cad4b2c..19848b0 100644 (file)
@@ -1,6 +1,6 @@
 /**
  * collectd - src/modbus.c
- * Copyright (C) 2010  noris network AG
+ * Copyright (C) 2010,2011  noris network AG
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
@@ -69,6 +69,8 @@
  */
 enum mb_register_type_e /* {{{ */
 {
+  REG_TYPE_INT16,
+  REG_TYPE_INT32,
   REG_TYPE_UINT16,
   REG_TYPE_UINT32,
   REG_TYPE_FLOAT
@@ -411,6 +413,7 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 
   if ((ds->ds[0].type != DS_TYPE_GAUGE)
+      && (data->register_type != REG_TYPE_INT32)
       && (data->register_type != REG_TYPE_UINT32))
   {
     NOTICE ("Modbus plugin: The data source of type \"%s\" is %s, not gauge. "
@@ -419,7 +422,8 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
   }
 
   memset (values, 0, sizeof (values));
-  if ((data->register_type == REG_TYPE_UINT32)
+  if ((data->register_type == REG_TYPE_INT32)
+      || (data->register_type == REG_TYPE_UINT32)
       || (data->register_type == REG_TYPE_FLOAT))
     values_num = 2;
   else
@@ -502,12 +506,47 @@ static int mb_read_data (mb_host_t *host, mb_slave_t *slave, /* {{{ */
     CAST_TO_VALUE_T (ds, vt, float_value);
     mb_submit (host, slave, data, vt);
   }
+  else if (data->register_type == REG_TYPE_INT32)
+  {
+    union
+    {
+      uint32_t u32;
+      int32_t  i32;
+    } v;
+    value_t vt;
+
+    v.u32 = (((uint32_t) values[0]) << 16)
+      | ((uint32_t) values[1]);
+    DEBUG ("Modbus plugin: mb_read_data: "
+        "Returned int32 value is %"PRIi32, v.i32);
+
+    CAST_TO_VALUE_T (ds, vt, v.i32);
+    mb_submit (host, slave, data, vt);
+  }
+  else if (data->register_type == REG_TYPE_INT16)
+  {
+    union
+    {
+      uint16_t u16;
+      int16_t  i16;
+    } v;
+    value_t vt;
+
+    v.u16 = values[0];
+
+    DEBUG ("Modbus plugin: mb_read_data: "
+        "Returned int16 value is %"PRIi16, v.i16);
+
+    CAST_TO_VALUE_T (ds, vt, v.i16);
+    mb_submit (host, slave, data, vt);
+  }
   else if (data->register_type == REG_TYPE_UINT32)
   {
     uint32_t v32;
     value_t vt;
 
-    v32 = (values[0] << 16) | values[1];
+    v32 = (((uint32_t) values[0]) << 16)
+      | ((uint32_t) values[1]);
     DEBUG ("Modbus plugin: mb_read_data: "
         "Returned uint32 value is %"PRIu32, v32);
 
@@ -663,6 +702,10 @@ static int mb_config_add_data (oconfig_item_t *ci) /* {{{ */
       status = cf_util_get_string_buffer (child, tmp, sizeof (tmp));
       if (status != 0)
         /* do nothing */;
+      else if (strcasecmp ("Int16", tmp) == 0)
+        data.register_type = REG_TYPE_INT16;
+      else if (strcasecmp ("Int32", tmp) == 0)
+        data.register_type = REG_TYPE_INT32;
       else if (strcasecmp ("Uint16", tmp) == 0)
         data.register_type = REG_TYPE_UINT16;
       else if (strcasecmp ("Uint32", tmp) == 0)
index fb7eb79..3099421 100644 (file)
@@ -328,7 +328,7 @@ static int rc_init (void)
 static int rc_write (const data_set_t *ds, const value_list_t *vl,
     user_data_t __attribute__((unused)) *user_data)
 {
-  char filename[512];
+  char filename[PATH_MAX];
   char values[512];
   char *values_array[2];
   int status;
@@ -406,6 +406,41 @@ static int rc_write (const data_set_t *ds, const value_list_t *vl,
   return (0);
 } /* int rc_write */
 
+static int rc_flush (__attribute__((unused)) cdtime_t timeout, /* {{{ */
+    const char *identifier,
+    __attribute__((unused)) user_data_t *ud)
+{
+  char filename[PATH_MAX + 1];
+  int status;
+
+  if (identifier == NULL)
+    return (EINVAL);
+
+  if (datadir != NULL)
+    ssnprintf (filename, sizeof (filename), "%s/%s.rrd", datadir, identifier);
+  else
+    ssnprintf (filename, sizeof (filename), "%s.rrd", identifier);
+
+  status = rrdc_connect (daemon_address);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_connect (%s) failed with status %i.",
+        daemon_address, status);
+    return (-1);
+  }
+
+  status = rrdc_flush (filename);
+  if (status != 0)
+  {
+    ERROR ("rrdcached plugin: rrdc_flush (%s) failed with status %i.",
+        filename, status);
+    return (-1);
+  }
+  DEBUG ("rrdcached plugin: rrdc_flush (%s): Success.", filename);
+
+  return (0);
+} /* }}} int rc_flush */
+
 static int rc_shutdown (void)
 {
   rrdc_disconnect ();
@@ -418,6 +453,7 @@ void module_register (void)
       config_keys, config_keys_num);
   plugin_register_init ("rrdcached", rc_init);
   plugin_register_write ("rrdcached", rc_write, /* user_data = */ NULL);
+  plugin_register_flush ("rrdcached", rc_flush, /* user_data = */ NULL);
   plugin_register_shutdown ("rrdcached", rc_shutdown);
 } /* void module_register */
 
index 54bcf67..7a84851 100644 (file)
@@ -703,7 +703,8 @@ static void csnmp_host_open_session (host_definition_t *host)
 
 /* TODO: Check if negative values wrap around. Problem: negative temperatures. */
 static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
-    double scale, double shift)
+    double scale, double shift,
+    const char *host_name, const char *data_name)
 {
   value_t ret;
   uint64_t tmp_unsigned = 0;
@@ -748,8 +749,11 @@ static value_t csnmp_value_list_to_value (struct variable_list *vl, int type,
          oid_buffer);
     else
 #endif
-      WARNING ("snmp plugin: I don't know the ASN type \"%i\" (OID: %s)",
-         (int) vl->type, oid_buffer);
+      WARNING ("snmp plugin: I don't know the ASN type #%i "
+               "(OID: \"%s\", data block \"%s\", host block \"%s\")",
+          (int) vl->type, oid_buffer,
+          (data_name != NULL) ? data_name : "UNKNOWN",
+          (host_name != NULL) ? host_name : "UNKNOWN");
 
     defined = 0;
   }
@@ -958,7 +962,8 @@ static int csnmp_strvbcopy (char *dst, /* {{{ */
 
 static int csnmp_instance_list_add (csnmp_list_instances_t **head,
     csnmp_list_instances_t **tail,
-    const struct snmp_pdu *res)
+    const struct snmp_pdu *res,
+    const host_definition_t *hd, const data_definition_t *dd)
 {
   csnmp_list_instances_t *il;
   struct variable_list *vb;
@@ -998,7 +1003,8 @@ static int csnmp_instance_list_add (csnmp_list_instances_t **head,
   }
   else
   {
-    value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER, 1.0, 0.0);
+    value_t val = csnmp_value_list_to_value (vb, DS_TYPE_COUNTER,
+        /* scale = */ 1.0, /* shift = */ 0.0, hd->name, dd->name);
     ssnprintf (il->instance, sizeof (il->instance),
        "%llu", val.counter);
   }
@@ -1286,7 +1292,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
       /* Allocate a new `csnmp_list_instances_t', insert the instance name and
        * add it to the list */
       if (csnmp_instance_list_add (&instance_list, &instance_list_ptr,
-           res) != 0)
+           res, host, data) != 0)
       {
        ERROR ("snmp plugin: csnmp_instance_list_add failed.");
        status = -1;
@@ -1342,7 +1348,7 @@ static int csnmp_read_table (host_definition_t *host, data_definition_t *data)
 
       vt->subid = vb->name[vb->name_length - 1];
       vt->value = csnmp_value_list_to_value (vb, ds->ds[i].type,
-         data->scale, data->shift);
+          data->scale, data->shift, host->name, data->name);
       vt->next = NULL;
 
       if (value_table_ptr[i] == NULL)
@@ -1492,8 +1498,8 @@ static int csnmp_read_value (host_definition_t *host, data_definition_t *data)
     for (i = 0; i < data->values_len; i++)
       if (snmp_oid_compare (data->values[i].oid, data->values[i].oid_len,
            vb->name, vb->name_length) == 0)
-       vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type,
-           data->scale, data->shift);
+        vl.values[i] = csnmp_value_list_to_value (vb, ds->ds[i].type,
+            data->scale, data->shift, host->name, data->name);
   } /* for (res->variables) */
 
   if (res != NULL)
index 629d1a1..397969e 100644 (file)
 #define MAX(x,y) ((x) > (y) ? (x) : (y))
 
 #if KERNEL_LINUX
+# define SWAP_HAVE_CONFIG 1
 /* No global variables */
 /* #endif KERNEL_LINUX */
 
 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
+# define SWAP_HAVE_CONFIG 1
 static derive_t pagesize;
-
-static const char *config_keys[] =
-{
-       "ReportByDevice"
-};
-static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
-
-static _Bool report_by_device = 0;
 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
 
 #elif defined(VM_SWAPUSAGE)
@@ -107,7 +101,34 @@ static perfstat_memory_total_t pmemory;
 # error "No applicable input method."
 #endif /* HAVE_LIBSTATGRAB */
 
-static int swap_init (void)
+#if SWAP_HAVE_CONFIG
+static const char *config_keys[] =
+{
+       "ReportByDevice"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static _Bool report_by_device = 0;
+
+static int swap_config (const char *key, const char *value) /* {{{ */
+{
+       if (strcasecmp ("ReportByDevice", key) == 0)
+       {
+               if (IS_TRUE (value))
+                       report_by_device = 1;
+               else
+                       report_by_device = 0;
+       }
+       else
+       {
+               return (-1);
+       }
+
+       return (0);
+} /* }}} int swap_config */
+#endif /* SWAP_HAVE_CONFIG */
+
+static int swap_init (void) /* {{{ */
 {
 #if KERNEL_LINUX
        /* No init stuff */
@@ -152,102 +173,188 @@ static int swap_init (void)
 #endif /* HAVE_PERFSTAT */
 
        return (0);
-}
+} /* }}} int swap_init */
 
-static void swap_submit_inst (const char *plugin_instance, /* {{{ */
-               const char *type_instance, derive_t value, unsigned type)
+static void swap_submit (const char *plugin_instance, /* {{{ */
+               const char *type, const char *type_instance,
+               value_t value)
 {
-       value_t values[1];
        value_list_t vl = VALUE_LIST_INIT;
 
-       switch (type)
-       {
-               case DS_TYPE_GAUGE:
-                       values[0].gauge = (gauge_t) value;
-                       sstrncpy (vl.type, "swap", sizeof (vl.type));
-                       break;
-               case DS_TYPE_DERIVE:
-                       values[0].derive = value;
-                       sstrncpy (vl.type, "swap_io", sizeof (vl.type));
-                       break;
-               default:
-                       ERROR ("swap plugin: swap_submit called with wrong"
-                               " type");
-       }
+       assert (type != NULL);
 
-       vl.values = values;
+       vl.values = &value;
        vl.values_len = 1;
        sstrncpy (vl.host, hostname_g, sizeof (vl.host));
        sstrncpy (vl.plugin, "swap", sizeof (vl.plugin));
        if (plugin_instance != NULL)
                sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
-       sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+       sstrncpy (vl.type, type, sizeof (vl.type));
+       if (type_instance != NULL)
+               sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
 
        plugin_dispatch_values (&vl);
 } /* }}} void swap_submit_inst */
 
-static void swap_submit (const char *type_instance, derive_t value, unsigned type)
+static void swap_submit_gauge (const char *plugin_instance, /* {{{ */
+               const char *type_instance, gauge_t value)
 {
-       swap_submit_inst (/* plugin instance = */ NULL,
-                       type_instance, value, type);
-}
+       value_t v;
+
+       v.gauge = value;
+       swap_submit (plugin_instance, "swap", type_instance, v);
+} /* }}} void swap_submit_gauge */
 
 #if KERNEL_LINUX
-static int swap_read (void) /* {{{ */
+static void swap_submit_derive (const char *plugin_instance, /* {{{ */
+               const char *type_instance, derive_t value)
+{
+       value_t v;
+
+       v.derive = value;
+       swap_submit (plugin_instance, "swap_io", type_instance, v);
+} /* }}} void swap_submit_derive */
+
+static int swap_read_separate (void) /* {{{ */
 {
        FILE *fh;
        char buffer[1024];
 
-       char *fields[8];
-       int numfields;
+       fh = fopen ("/proc/swaps", "r");
+       if (fh == NULL)
+       {
+               char errbuf[1024];
+               WARNING ("swap plugin: fopen (/proc/swaps) failed: %s",
+                               sstrerror (errno, errbuf, sizeof (errbuf)));
+               return (-1);
+       }
+
+       while (fgets (buffer, sizeof (buffer), fh) != NULL)
+       {
+               char *fields[8];
+               int numfields;
+               char *endptr;
+
+               char path[PATH_MAX];
+               gauge_t size;
+               gauge_t used;
+               gauge_t free;
+
+               numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
+               if (numfields != 5)
+                       continue;
+
+               sstrncpy (path, fields[0], sizeof (path));
+               escape_slashes (path, sizeof (path));
+
+               errno = 0;
+               endptr = NULL;
+               size = strtod (fields[2], &endptr);
+               if ((endptr == fields[2]) || (errno != 0))
+                       continue;
+
+               errno = 0;
+               endptr = NULL;
+               used = strtod (fields[3], &endptr);
+               if ((endptr == fields[3]) || (errno != 0))
+                       continue;
+
+               if (size < used)
+                       continue;
+
+               free = size - used;
+
+               swap_submit_gauge (path, "used", used);
+               swap_submit_gauge (path, "free", free);
+       }
+
+       fclose (fh);
 
-       _Bool old_kernel=0;
+       return (0);
+} /* }}} int swap_read_separate */
+
+static int swap_read_combined (void) /* {{{ */
+{
+       FILE *fh;
+       char buffer[1024];
 
-       derive_t swap_used   = 0;
-       derive_t swap_cached = 0;
-       derive_t swap_free   = 0;
-       derive_t swap_total  = 0;
-       derive_t swap_in     = 0;
-       derive_t swap_out    = 0;
+       uint8_t have_data = 0;
+       gauge_t swap_used   = 0.0;
+       gauge_t swap_cached = 0.0;
+       gauge_t swap_free   = 0.0;
+       gauge_t swap_total  = 0.0;
 
-       if ((fh = fopen ("/proc/meminfo", "r")) == NULL)
+       fh = fopen ("/proc/meminfo", "r");
+       if (fh == NULL)
        {
                char errbuf[1024];
-               WARNING ("memory: fopen: %s",
+               WARNING ("swap plugin: fopen (/proc/meminfo) failed: %s",
                                sstrerror (errno, errbuf, sizeof (errbuf)));
                return (-1);
        }
 
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
+               char *fields[8];
+               int numfields;
+
                numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
                if (numfields < 2)
                        continue;
 
                if (strcasecmp (fields[0], "SwapTotal:") == 0)
-                       strtoderive (fields[1], &swap_total);
+               {
+                       swap_total = strtod (fields[1], /* endptr = */ NULL);
+                       have_data |= 0x01;
+               }
                else if (strcasecmp (fields[0], "SwapFree:") == 0)
-                       strtoderive (fields[1], &swap_free);
+               {
+                       swap_free = strtod (fields[1], /* endptr = */ NULL);
+                       have_data |= 0x02;
+               }
                else if (strcasecmp (fields[0], "SwapCached:") == 0)
-                       strtoderive (fields[1], &swap_cached);
+               {
+                       swap_cached = strtod (fields[1], /* endptr = */ NULL);
+                       have_data |= 0x04;
+               }
        }
 
-       if (fclose (fh))
-       {
-               char errbuf[1024];
-               WARNING ("memory: fclose: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-       }
+       fclose (fh);
 
-       if ((swap_total == 0LL) || ((swap_free + swap_cached) > swap_total))
-               return (-1);
+       if (have_data != 0x07)
+               return (ENOENT);
+
+       if (isnan (swap_total)
+                       || (swap_total <= 0.0)
+                       || ((swap_free + swap_cached) > swap_total))
+               return (EINVAL);
 
        swap_used = swap_total - (swap_free + swap_cached);
 
-       if ((fh = fopen ("/proc/vmstat", "r")) == NULL)
+       swap_submit_gauge (NULL, "used",   1024.0 * swap_used);
+       swap_submit_gauge (NULL, "free",   1024.0 * swap_free);
+       swap_submit_gauge (NULL, "cached", 1024.0 * swap_cached);
+
+       return (0);
+} /* }}} int swap_read_combined */
+
+static int swap_read_io (void) /* {{{ */
+{
+       FILE *fh;
+       char buffer[1024];
+
+       _Bool old_kernel = 0;
+
+       uint8_t have_data = 0;
+       derive_t swap_in  = 0;
+       derive_t swap_out = 0;
+
+       fh = fopen ("/proc/vmstat", "r");
+       if (fh == NULL)
        {
-               // /proc/vmstat does not exist in kernels <2.6
-               if ((fh = fopen ("/proc/stat", "r")) == NULL )
+               /* /proc/vmstat does not exist in kernels <2.6 */
+               fh = fopen ("/proc/stat", "r");
+               if (fh == NULL)
                {
                        char errbuf[1024];
                        WARNING ("swap: fopen: %s",
@@ -260,6 +367,9 @@ static int swap_read (void) /* {{{ */
 
        while (fgets (buffer, sizeof (buffer), fh) != NULL)
        {
+               char *fields[8];
+               int numfields;
+
                numfields = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
 
                if (!old_kernel)
@@ -268,9 +378,15 @@ static int swap_read (void) /* {{{ */
                                continue;
 
                        if (strcasecmp ("pswpin", fields[0]) == 0)
+                       {
                                strtoderive (fields[1], &swap_in);
+                               have_data |= 0x01;
+                       }
                        else if (strcasecmp ("pswpout", fields[0]) == 0)
+                       {
                                strtoderive (fields[1], &swap_out);
+                               have_data |= 0x02;
+                       }
                }
                else /* if (old_kernel) */
                {
@@ -285,18 +401,25 @@ static int swap_read (void) /* {{{ */
                }
        } /* while (fgets) */
 
-       if (fclose (fh))
-       {
-               char errbuf[1024];
-               WARNING ("swap: fclose: %s",
-                               sstrerror (errno, errbuf, sizeof (errbuf)));
-       }
+       fclose (fh);
+
+       if (have_data != 0x03)
+               return (ENOENT);
+
+       swap_submit_derive (NULL, "in",  swap_in);
+       swap_submit_derive (NULL, "out", swap_out);
+
+       return (0);
+} /* }}} int swap_read_io */
 
-       swap_submit ("used",   1024 * swap_used,   DS_TYPE_GAUGE);
-       swap_submit ("free",   1024 * swap_free,   DS_TYPE_GAUGE);
-       swap_submit ("cached", 1024 * swap_cached, DS_TYPE_GAUGE);
-       swap_submit ("in",  swap_in,  DS_TYPE_DERIVE);
-       swap_submit ("out", swap_out, DS_TYPE_DERIVE);
+static int swap_read (void) /* {{{ */
+{
+       if (report_by_device)
+               swap_read_separate ();
+       else
+               swap_read_combined ();
+
+       swap_read_io ();
 
        return (0);
 } /* }}} int swap_read */
@@ -355,9 +478,9 @@ static int swap_read_kstat (void) /* {{{ */
                        * pagesize);
        swap_avail  = (derive_t) ((ai.ani_max - ai.ani_resv) * pagesize);
 
-       swap_submit ("used", swap_alloc, DS_TYPE_GAUGE);
-       swap_submit ("free", swap_avail, DS_TYPE_GAUGE);
-       swap_submit ("reserved", swap_resv, DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", swap_alloc);
+       swap_submit_gauge (NULL, "free", swap_avail);
+       swap_submit_gauge (NULL, "reserved", swap_resv);
 
        return (0);
 } /* }}} int swap_read_kstat */
@@ -454,25 +577,11 @@ static int swap_read (void) /* {{{ */
                        continue;
                }
 
-               /* Okay, using "/" as swap device would be super-weird, but
-                * we'll handle it anyway to cover all cases. */
-               if (strcmp ("/", s->swt_ent[i].ste_path) == 0)
-                       sstrncpy (path, "root", sizeof (path));
-               else
-               {
-                       int j;
-
-                       s->swt_ent[i].ste_path[PATH_MAX - 1] = 0;
-                       /* Don't copy the leading slash */
-                       sstrncpy (path, &s->swt_ent[i].ste_path[1], sizeof (path));
-                       /* Convert slashes to dashes, just like the "df" plugin. */
-                       for (j = 0; path[j] != 0; j++)
-                               if (path[j] == '/')
-                                       path[j] = '-';
-               }
+               sstrncpy (path, s->swt_ent[i].ste_path, sizeof (path));
+               escape_slashes (path, sizeof (path));
 
-               swap_submit_inst (path, "used", this_total - this_avail, DS_TYPE_GAUGE);
-               swap_submit_inst (path, "free", this_avail, DS_TYPE_GAUGE);
+               swap_submit_gauge (path, "used", (gauge_t) (this_total - this_avail));
+               swap_submit_gauge (path, "free", (gauge_t) this_avail);
         } /* for (swap_num) */
 
         if (total < avail)
@@ -489,32 +598,14 @@ static int swap_read (void) /* {{{ */
         * values have already been dispatched from within the loop. */
        if (!report_by_device)
        {
-               swap_submit ("used", total - avail, DS_TYPE_GAUGE);
-               swap_submit ("free", avail, DS_TYPE_GAUGE);
+               swap_submit_gauge (NULL, "used", (gauge_t) (total - avail));
+               swap_submit_gauge (NULL, "free", (gauge_t) avail);
        }
 
        sfree (s_paths);
         sfree (s);
        return (0);
 } /* }}} int swap_read */
-
-/* Configuration: Present when swapctl or both methods are available. */
-static int swap_config (const char *key, const char *value) /* {{{ */
-{
-       if (strcasecmp ("ReportByDevice", key) == 0)
-       {
-               if (IS_TRUE (value))
-                       report_by_device = 1;
-               else
-                       report_by_device = 0;
-       }
-       else
-       {
-               return (-1);
-       }
-
-       return (0);
-} /* }}} int swap_config */
 /* #endif HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS */
 
 #elif HAVE_SWAPCTL && HAVE_SWAPCTL_THREE_ARGS
@@ -579,8 +670,8 @@ static int swap_read (void) /* {{{ */
                return (-1);
        }
 
-       swap_submit ("used", used, DS_TYPE_GAUGE);
-       swap_submit ("free", total - used, DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", (gauge_t) used);
+       swap_submit_gauge (NULL, "free", (gauge_t) (total - used));
 
        sfree (swap_entries);
 
@@ -606,8 +697,8 @@ static int swap_read (void) /* {{{ */
                return (-1);
 
        /* The returned values are bytes. */
-       swap_submit ("used", (derive_t) sw_usage.xsu_used, DS_TYPE_GAUGE);
-       swap_submit ("free", (derive_t) sw_usage.xsu_avail, DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", (gauge_t) sw_usage.xsu_used);
+       swap_submit_gauge (NULL, "free", (gauge_t) sw_usage.xsu_avail);
 
        return (0);
 } /* }}} int swap_read */
@@ -639,8 +730,8 @@ static int swap_read (void) /* {{{ */
 
        free = total - used;
 
-       swap_submit ("used", used, DS_TYPE_GAUGE);
-       swap_submit ("free", free, DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", (gauge_t) used);
+       swap_submit_gauge (NULL, "free", (gauge_t) free);
 
        return (0);
 } /* }}} int swap_read */
@@ -656,8 +747,8 @@ static int swap_read (void) /* {{{ */
        if (swap == NULL)
                return (-1);
 
-       swap_submit ("used", (derive_t) swap->used, DS_TYPE_GAUGE);
-       swap_submit ("free", (derive_t) swap->free, DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", (gauge_t) swap->used);
+       swap_submit_gauge (NULL, "free", (gauge_t) swap->free);
 
        return (0);
 } /* }}} int swap_read */
@@ -673,8 +764,8 @@ static int swap_read (void) /* {{{ */
                         sstrerror (errno, errbuf, sizeof (errbuf)));
                 return (-1);
         }
-       swap_submit ("used", (derive_t) (pmemory.pgsp_total - pmemory.pgsp_free) * pagesize, DS_TYPE_GAUGE);
-       swap_submit ("free", (derive_t) pmemory.pgsp_free * pagesize , DS_TYPE_GAUGE);
+       swap_submit_gauge (NULL, "used", (gauge_t) (pmemory.pgsp_total - pmemory.pgsp_free) * pagesize);
+       swap_submit_gauge (NULL, "free", (gauge_t) pmemory.pgsp_free * pagesize );
 
        return (0);
 } /* }}} int swap_read */
@@ -682,7 +773,7 @@ static int swap_read (void) /* {{{ */
 
 void module_register (void)
 {
-#if HAVE_SWAPCTL && HAVE_SWAPCTL_TWO_ARGS
+#if SWAP_HAVE_CONFIG
        plugin_register_config ("swap", swap_config, config_keys, config_keys_num);
 #endif
        plugin_register_init ("swap", swap_init);