[merged] from default heiko
authorHeiko Schlittermann (JUMPER) <hs@schlittermann.de>
Sat, 25 Jan 2014 23:47:39 +0100
branchheiko
changeset 22 9579c19f13ac
parent 7 2bda25a39ef4 (current diff)
parent 21 2247be0e2a13 (diff)
[merged] from default
check
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,13 @@
+syntax:glob
+*.sw?
+_build/
+blib/
+Build
+*.bak
+MYMETA.*
+debian/nagios-plugin-amanda-client
+debian/debhelper.log
+debian/files
+debian/nagios-plugin-amanda-client.debhelper.log
+debian/nagios-plugin-amanda-client.substvars
+debian/substvars
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgtags	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,1 @@
+8c9ba80ad7cb1505332604087bcb8129a96ae3fe nagios-plugin-amanda-client_0.0.2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Build.PL	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,42 @@
+#! perl
+
+use 5.010;
+use strict;
+use warnings;
+use Module::Build;
+
+my $builder = Module::Build->new(
+    dist_name         => 'nagios-plugin-amanda-client',
+    dist_version_from => 'plugins/check_amanda-client',
+    dist_abstract     => 'nagios check for amanda clients',
+
+    # new type 'plugins'
+    #
+
+    # where to find the sources -> where to put them under blib
+    checks_files => {
+        'plugins/check_amanda-client' =>
+          'nagios/plugins/ius/check_amanda-client',
+    },
+    license       => 'perl',
+    requires      => { perl => 5.014, },
+    test_requires => { 'Test::Exception' => 0, },
+);
+
+# some magic to install it to the proper location}
+if (not defined $builder->install_path('nagios')) {
+    my $base = do {
+        if ($builder->installdirs eq 'vendor') { '/usr/lib' }
+        elsif (defined $builder->install_base) { $builder->install_base }
+        else                                   { '/usr/local/lib' }
+    };
+    $builder->install_path('nagios' => $base . '/nagios');
+}
+
+$builder->bindoc_dirs([@{ $builder->bindoc_dirs }, 'blib/nagios/plugins/ius']);
+$builder->add_build_element('checks');
+
+$builder->create_build_script;
+
+__END__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MANIFEST	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,5 @@
+.hgignore
+.perltidyrc
+Build.PL
+MANIFEST			This list of files
+plugins/check_amanda-client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MANIFEST.SKIP	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,77 @@
+
+#!start included /home/heiko/perl5/lib/perl5/ExtUtils/MANIFEST.SKIP
+# Avoid version control files.
+\bRCS\b
+\bCVS\b
+\bSCCS\b
+,v$
+\B\.svn\b
+\B\.git\b
+\B\.gitignore\b
+\b_darcs\b
+\B\.cvsignore$
+
+# Avoid VMS specific MakeMaker generated files
+\bDescrip.MMS$
+\bDESCRIP.MMS$
+\bdescrip.mms$
+
+# Avoid Makemaker generated and utility files.
+\bMANIFEST\.bak
+\bMakefile$
+\bblib/
+\bMakeMaker-\d
+\bpm_to_blib\.ts$
+\bpm_to_blib$
+\bblibdirs\.ts$         # 6.18 through 6.25 generated this
+
+# Avoid Module::Build generated and utility files.
+\bBuild$
+\b_build/
+\bBuild.bat$
+\bBuild.COM$
+\bBUILD.COM$
+\bbuild.com$
+
+# Avoid temp and backup files.
+~$
+\.old$
+\#$
+\b\.#
+\.bak$
+\.tmp$
+\.#
+\.rej$
+
+# Avoid OS-specific files/dirs
+# Mac OSX metadata
+\B\.DS_Store
+# Mac OSX SMB mount metadata files
+\B\._
+
+# Avoid Devel::Cover and Devel::CoverX::Covered files.
+\bcover_db\b
+\bcovered\b
+ 
+# Avoid MYMETA files
+^MYMETA\.
+#!end included /home/heiko/perl5/lib/perl5/ExtUtils/MANIFEST.SKIP
+
+# Avoid configuration metadata file
+^MYMETA\.
+
+# Avoid Module::Build generated and utility files.
+\bBuild$
+\bBuild.bat$
+\b_build
+\bBuild.COM$
+\bBUILD.COM$
+\bbuild.com$
+^MANIFEST\.SKIP
+
+# Avoid archives of this distribution
+\bnagios-plugin-amanda-client-[\d\.\_]+
+
+^\.hg\/
+^\..*sw.$
+^debian/
--- a/check	Sat Jan 11 23:23:45 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,329 +0,0 @@
-#! /usr/bin/perl
-# source: https://ssl.schlittermann.de/hg/ius/nagios/nagios-plugin-amanda-client
-
-use 5.010;
-use strict;
-use warnings;
-use Getopt::Long;
-use POSIX;
-use File::Spec::Functions;
-use Data::Dumper;
-use File::Find;
-use Carp;
-use Pod::Usage;
-
-sub su;
-sub find_tool;
-sub check_perms;
-sub config_names;
-sub get_devices;
-
-sub amchecks;
-sub amlists;
-
-sub main;
-
-my $NAME = 'AMANDA-CLIENT';
-
-sub ok;
-sub warning;
-sub critical;
-sub unknown;
-sub verbose;
-sub unique { my %h; @h{@_} = (); keys %h }
-
-$SIG{__DIE__} = sub { unknown @_ unless $^S };
-
-exit main @ARGV;
-
-#----
-
-sub main {
-    my @opt_ignore;
-    my $opt_verbose = 0;
-
-    GetOptions(
-        'i|ignore=s@' => \@opt_ignore,
-        'h|help'      => sub { pod2usage(-verbose => 1, -exit => 0) },
-        'm|man'       => sub { pod2usage(-verbose => 2, -exit => 0) },
-        'v|verbose'   => \$opt_verbose,
-    ) or pod2usage;
-
-    if ($opt_verbose) {
-        *::verbose = sub { say '# ', @_ }
-    }
-    else {
-        *::verbose = sub { }
-    }
-
-    # test needs to be run as root:* or as backup:backup
-    my $USER  = 'backup';
-    my $CFDIR = '/etc/amanda';
-
-    # change to backup if still root
-    su $USER if $> == 0;
-
-    # amservice needs to be suid root, but executable
-    # by the backup user/group
-    verbose q{checking permissions for `amservice'};
-    eval { check_perms find_tool('amservice'), 04750, 'root', $) }
-      or unknown $@;
-
-    # find the backup sets we know about
-    # here we suppose that it's possible to find strings like
-    # 'conf "foo"' in files named 'amanda-client.conf' below /etc/amanda
-
-    verbose qq{find config names from $CFDIR};
-    my @confs = sort +unique eval { config_names $CFDIR }
-      or unknown $@;
-
-    eval { amchecks @confs } or critical $@;
-
-    my @dles = eval { amlists confs => \@confs, ignore => \@opt_ignore } or critical $@;
-    ok 'config: ' . join(', ', @confs), @dles;
-
-    # never reached
-    return 0;
-}
-
-# compare the file systems
-# get a list of file system
-sub get_devices {
-    open(my $fh, '/proc/filesystems');
-    my @types = map { /^\s+(\S+)/ ? $1 : () } <$fh>;
-    my @df = (df => '-P', map { -t => $_ } @types);
-    map { [$_, (stat)[0]] } map { (split ' ', $_)[5] } grep { /^\// } `@df`;
-}
-
-sub su {
-    my $user  = shift;
-    my $group = (getgrnam $user)[0];
-    my $uid   = getpwnam $user;
-    my $gid   = getgrnam $group;
-
-    my @groups;
-
-    setgrent;
-    my @rc;
-    while (my @g = getgrent) {
-        push @groups, $g[2] if $user ~~ [split ' ', $g[3]];
-    }
-    endgrent;
-    $) = "@groups";
-
-    verbose "su to $uid:$gid";
-    setgid $gid;
-    setuid $uid;
-}
-
-sub find_tool {
-    my $name = shift;
-    my @rc = grep { -f -x } map { catfile $_, $name } split /:/, $ENV{PATH}
-      or die "Can't find `$name' in $ENV{PATH}\n";
-    $rc[0];
-}
-
-sub check_perms {
-    my ($file, $mode, $owner, $group) = @_;
-
-    $owner = getpwuid $owner if $owner ~~ /^\d+$/;
-
-    $group = getgrgid +(split ' ', $group)[0]
-      if $group ~~ /^[\d\s]+$/;
-
-    stat $file or croak "Can't stat `$file': $!\n";
-
-    eval {
-        my $f_owner = getpwuid +(stat _)[4] or die $!;
-        my $f_group = getgrgid +(stat _)[5] or die $!;
-        my $f_mode  = (stat _)[2] & 07777   or die $!;
-
-        my $msg =
-          sprintf "need: 0%04o root:$group, got: 0%04o $f_owner:$f_group\n",
-          $mode, $f_mode;
-
-        die $msg unless $f_owner eq $owner;
-        die $msg unless $f_group eq $group;
-        die $msg unless $f_mode == $mode;
-    };
-    die "wrong permissions for `$file', $@" if $@;
-    1;
-}
-
-sub config_names {
-    my $dir     = shift;
-    my @configs = ();
-    find(
-        sub {
-            -f and /^amanda-client\.conf$/ or return;
-            open(my $fh, '<', $_) or die "Can't open  $File::Find::name: $!\n";
-            push @configs, map { /^conf\s+"(.+?)"/ ? $1 : () } <$fh>;
-        },
-        $dir
-    );
-
-    die
-"no configs found below $dir (amanda-client.conf needs need `conf \"foo\"' line)\n"
-      if not @configs;
-    return @configs;
-}
-
-sub _amcheck {
-
-    #config: daily
-    #CHECKING
-    #
-    #Amanda Backup Client Hosts Check
-    #--------------------------------
-    #Client check: 1 host checked in 2.242 seconds.  0 problems found.
-    #
-    #(brought to you by Amanda 3.3.1)
-    #The check is finished
-
-    my $conf = shift;
-    my $_    = qx(amdump_client --config '$conf' check 2>&1);
-    /^config:\s+$conf\n
-		 CHECKING\n
-		 .*\n
-		 Client.check:.1.host.checked.in.\d+\.\d+.seconds\.\s+0.problems.found\.\n
-		 .*\n
-		 The.check.is.finished$
-	/smx or die "unexpected output from check:\n$_";
-}
-
-sub amchecks {
-    my @errors = ();
-    foreach my $conf (@_) {
-        eval { _amcheck $conf } or push @errors, $@;
-    }
-    die @errors if @errors;
-    return 1;
-}
-
-sub _amlist {
-
-    # return a list of [ name, dev ] tupels.
-    # name: the name of the disk/device
-    # dev:  the local device id (stat)[0]
-    # iff the inum of $name != 2, it's not the top directory
-    # and we set the device id to -1, since $name does not stand for a whole
-    # device
-    my $conf = shift;
-    chomp((undef, my @dles) = qx(amdump_client --config '$conf' list));
-    return map { [$_, (stat $_)[1] == 2 ? (stat $_)[0] : -1] } @dles;
-}
-
-sub amlists {
-    my %arg = @_;
-    my @confs = @{$arg{confs}} or croak 'missing list of confs';
-    my @ignore = @{$arg{ignore}};
-
-    my @candidates = get_devices;
-
-    my %missing;
-
-    foreach my $conf (@confs) {
-        my @dles = _amlist $conf;
-        foreach my $candidate (@candidates) {
-
-            # we're satisfied if either the name of the device is in
-            # the disklist, or the device id is found
-            $candidate->[0] ~~ [map { $_->[0] } @dles] and next;
-            $candidate->[1] ~~ [map { $_->[1] } @dles] and next;
-            push @{ $missing{$conf} }, $candidate->[0]
-                if not "$conf:$candidate->[0]" ~~ @ignore;
-        }
-    }
-    die map { "$_ missing: " . join(', ' => @{ $missing{$_} }) . "\n" }
-      keys %missing
-      if %missing;
-    return map { $_->[0] } @candidates;
-}
-
-sub ok { say "$NAME OK\n", join "\n" => @_; exit 0 }
-sub warning  { print "$NAME WARNING\n",  join "\n" => @_; exit 1 }
-sub critical { print "$NAME CRITICAL\n", join "\n" => @_; exit 2 }
-sub unknown  { print "$NAME UNKNOWN\n",  join "\n" => @_; exit 3 }
-
-__END__
-
-=head1 NAME
-
- check_amanda-client - check the amanda backup from the client side
-
-=head1 SYNOPSIS
-
-  check_amanda-client [-h|--help] [-m|--man]
-  check_amanda-client [options]
-
-=head1 DESCRIPTION
-
-This nagios check plugin checks the Amanda setup from the client side.
-
-=head1 OPTIONS
-
-=over
-
-=item B<-i>|B<--ignore> I<config:filesystem>
-
-The name of a filesystem to be ignored. Example:
-
-    check_amanda-client --ignore weekly:/var/spool/squid --ignore daily:/boot
-
-=item B<-h>|B<--help>
-
-Show a short description and help text.
-
-=item B<-m>|B<--man>
-
-Show the man page of this tool.
-
-=item B<-v>|B<--verbose>
-
-Show what's going only. Many for debugging purpose. (default: off)
-
-=back
-
-=head1 PREPARATIONS
-
-In order to make the check working, some preparations needs to be done.
-
-=head1 Client
-
-For each backup set you want to check: Create an
-F</etc/amanda/$set/amanda-client.conf>. 
-
-    config "foo"
-    index-server    "amanda.example.com"    # used by restore
-    tape-server	    "amanda.example.com"    # used by restore
-    amdump-server   "amanda.example.com"    # used by amdump_client
-
-In addition, the F<amservice> binary has to be suid root and executable
-by the backup user. This requirement is checked automatically by the
-plugin.
-
-=head1 Server
-
-The server need to know about the amdumpd service. In the F<inetd.conf> 
-you need to add "amdumpd" to the list of allowed services. And
-additionally in F<.amandahosts> the "backup" user of the client needs
-the permissions to run the "amdumpd" service.
-
-    # inetd.conf
-    amanda stream tcp nowait backup /usr/lib/amanda/amandad amandad -auth=bsdtcp amindexd amidxtaped amdumpd
-
-    # .amandahosts
-    client.example.com backup amdumpd
-    client.example.com root amindexd amidxtaped 
-
-=head1 AUTHOR
-
-Heiko Schlittermann L<hs@schlittermann.de>
-
-=head1 SOURCE
-
-Source can be found at L<https://ssl.schlittermann.de/hg/nagios/nagios-plugin-amanda-client>
-
-=cut
-
-# vim:et ts=4 sw=4 aw ai:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/changelog	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,11 @@
+nagios-plugin-amanda-client (0.0.2) stable; urgency=low
+
+  * fixed install path and dependency on amanda-client 
+
+ -- Heiko Schlittermann <hs@schlittermann.de>  Tue, 14 Jan 2014 16:49:15 +0100
+
+nagios-plugin-amanda-client (0.0.1) stable; urgency=low
+
+  * Initial release.
+
+ -- Heiko Schlittermann <hs@schlittermann.de>  Tue, 14 Jan 2014 16:48:48 +0100
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/compat	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,1 @@
+9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/control	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,10 @@
+Source: nagios-plugin-amanda-client
+Maintainer: Heiko Schlittermann <hs@schlittermann.de>
+
+Package: nagios-plugin-amanda-client
+Section: misc
+Architecture: any
+Depends: amanda-client (>= 3.0), libconst-fast-perl
+Description: nagios plugin do client side checks of the Amanda backup suite
+ This is a nagios plugin. It checks triggers the Amanda client check
+ for the current host and compares the DLE with the currenty mounted file systems.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/debian/rules	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,4 @@
+#! /usr/bin/make -f
+
+%: 
+	@dh $@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/check_amanda-client	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,356 @@
+#! /usr/bin/perl
+# source: https://ssl.schlittermann.de/hg/ius/nagios/nagios-plugin-amanda-client
+
+use 5.014;
+use strict;
+use warnings;
+use Getopt::Long;
+use POSIX;
+use File::Spec::Functions;
+use Data::Dumper;
+use File::Find;
+use Carp;
+use Pod::Usage;
+use Const::Fast;
+
+const my $NAME  => 'AMANDA-CLIENT';
+const my $USER  => 'backup';
+const my $CFDIR => '/etc/amanda';
+
+sub su;
+sub find_tool;
+sub check_perms;
+sub config_names;
+sub get_devices;
+
+sub amchecks;
+sub compare_lists;
+
+sub main;
+
+sub OK;
+sub WARNING;
+sub CRITICAL;
+sub UNKNOWN;
+sub verbose;
+sub unique { my %h; @h{@_} = (); keys %h }
+
+$SIG{__DIE__} = sub { UNKNOWN @_ unless $^S };
+
+exit main @ARGV if not caller;
+
+#----
+
+sub main {
+    my @opt_exclude;
+    my @opt_include;
+    my $opt_verbose = 0;
+
+    GetOptions(
+        'e|x|exclude=s@' => \@opt_exclude,
+        'i|include=s@'   => \@opt_include,
+        'h|help'         => sub { pod2usage(-verbose => 1, -exit => 0) },
+        'm|man'          => sub { pod2usage(-verbose => 2, -exit => 0) },
+        'v|verbose'      => \$opt_verbose,
+    ) or pod2usage;
+
+    *::verbose = $opt_verbose ? sub { say '# ', @_ } : sub { };
+
+    # test needs to be run as root:* or as backup:backup
+    # change to backup if still root
+    su $USER if $> == 0;
+
+    # amservice needs to be suid root, but executable
+    # by the backup user/group
+    verbose q{checking permissions for `amservice'};
+    eval { check_perms find_tool('amservice'), 04750, 'root', $) }
+      or UNKNOWN $@;
+
+    # find the backup sets we know about
+    # here we suppose that it's possible to find strings like
+    # 'conf "foo"' in files named 'amanda-client.conf' below /etc/amanda
+
+    verbose qq{find config names from $CFDIR};
+    my @confs = sort +unique eval { config_names $CFDIR }
+      or UNKNOWN $@;
+
+    eval { amchecks @confs } or CRITICAL $@;
+
+    my @dles = eval {
+        compare_lists
+          confs   => \@confs,
+          exclude => \@opt_exclude,
+          include => \@opt_include;
+    }
+      or CRITICAL $@;
+    OK 'config: ' . join(', ', @confs), @dles;
+
+    # never reached
+    return 0;
+}
+
+# compare the file systems
+# get a list of file system
+sub get_devices {
+    open(my $fh, '/proc/filesystems');
+    my @types = map { /^\s+(\S+)/ ? $1 : () } <$fh>;
+    my @df = (df => '-P', map { -t => $_ } @types);
+    map { [$_, (stat)[0]] } map { (split ' ', $_)[5] } grep { /^\// } `@df`;
+}
+
+sub su {
+    my $user  = shift;
+    my $group = (getgrnam $user)[0];
+    my $uid   = getpwnam $user;
+    my $gid   = getgrnam $group;
+
+    my @groups;
+
+    setgrent;
+    my @rc;
+    while (my @g = getgrent) {
+        push @groups, $g[2] if $user ~~ [split ' ', $g[3]];
+    }
+    endgrent;
+    $) = "@groups";
+
+    verbose "su to $uid:$gid";
+    setgid $gid;
+    setuid $uid;
+}
+
+sub find_tool {
+    my $name = shift;
+    my @rc = grep { -f -x } map { catfile $_, $name } split /:/, $ENV{PATH}
+      or die "Can't find `$name' in $ENV{PATH}\n";
+    $rc[0];
+}
+
+sub check_perms {
+    my ($file, $mode, $owner, $group) = @_;
+
+    $owner = getpwuid $owner if $owner ~~ /^\d+$/;
+
+    $group = getgrgid +(split ' ', $group)[0]
+      if $group ~~ /^[\d\s]+$/;
+
+    stat $file or croak "Can't stat `$file': $!\n";
+
+    eval {
+        my $f_owner = getpwuid +(stat _)[4] or die $!;
+        my $f_group = getgrgid +(stat _)[5] or die $!;
+        my $f_mode  = (stat _)[2] & 07777   or die $!;
+
+        my $msg =
+          sprintf "need: 0%04o root:$group, got: 0%04o $f_owner:$f_group\n",
+          $mode, $f_mode;
+
+        die $msg unless $f_owner eq $owner;
+        die $msg unless $f_group eq $group;
+        die $msg unless $f_mode == $mode;
+    };
+    die "wrong permissions for `$file', $@" if $@;
+    1;
+}
+
+sub config_names {
+    my $dir     = shift;
+    my @configs = ();
+    find(
+        sub {
+            -f and /^amanda-client\.conf$/ or return;
+            open(my $fh, '<', $_)
+              or die "Can't open  $File::Find::name: $!\n";
+            push @configs, map { /^conf\s+"(.+?)"/ ? $1 : () } <$fh>;
+        },
+        $dir
+    );
+
+    die
+"no configs found below $dir (amanda-client.conf needs need `conf \"foo\"' line)\n"
+      if not @configs;
+    return @configs;
+}
+
+sub _amcheck {
+
+    #config: daily
+    #CHECKING
+    #
+    #Amanda Backup Client Hosts Check
+    #--------------------------------
+    #Client check: 1 host checked in 2.242 seconds.  0 problems found.
+    #
+    #(brought to you by Amanda 3.3.1)
+    #The check is finished
+
+    my $conf = shift;
+    my $_    = qx(amdump_client --config '$conf' check 2>&1);
+    /^config:\s+$conf\n
+		 CHECKING\n
+		 .*\n
+		 Client.check:.1.host.checked.in.\d+\.\d+.seconds\.\s+0.problems.found\.\n
+		 .*\n
+		 The.check.is.finished$
+	/smx or die "unexpected output from check:\n$_";
+}
+
+sub amchecks {
+    my @errors = ();
+    foreach my $conf (@_) {
+        eval { _amcheck $conf } or push @errors, $@;
+    }
+    die @errors if @errors;
+    return 1;
+}
+
+sub _amlist {
+
+    # return a list of [ name, dev ] tupels.
+    # name: the name of the disk/device
+    # dev:  the local device id (stat)[0]
+    # iff the inum of $name != 2, it's not the top directory
+    # and we set the device id to -1, since $name does not stand for a whole
+    # device
+    my $conf = shift;
+    chomp((undef, my @dles) = qx(amdump_client --config '$conf' list));
+    return map { [$_, (stat $_)[1] == 2 ? (stat $_)[0] : -1] } @dles;
+}
+
+sub compare_lists {
+    my %arg     = @_;
+    my @confs   = @{ $arg{confs} } or croak 'missing list of confs';
+    my @exclude = @{ $arg{exclude} };
+    my @include = @{ $arg{include} };
+
+    WARNING
+      "excluded filesystem(s) @$_ does not exist, update the config please!\n"
+      if @exclude
+          and @$_ = grep { not -e } unique map { /^.*?:(.*)/ } @exclude;
+
+    my @candidates = (get_devices, @include);
+    my %missing;
+
+    foreach my $conf (@confs) {
+        my @dles = _amlist $conf;
+        foreach my $candidate (@candidates) {
+            next if not $candidate =~ m{^/|\Q$conf\E:};
+
+            # we're satisfied if either the name of the device is in
+            # the disklist, or the device id is found
+            $candidate->[0] ~~ [map { $_->[0] } @dles] and next;
+            $candidate->[1] ~~ [map { $_->[1] } @dles] and next;
+            push @{ $missing{$conf} }, $candidate->[0]
+              if not "$conf:$candidate->[0]" ~~ @exclude;
+        }
+    }
+    die map { "$_ missing: " . join(', ' => @{ $missing{$_} }) . "\n" }
+      keys %missing
+      if %missing;
+
+    return map { $_->[0] } @candidates;
+}
+
+sub OK { say "$NAME OK\n", join "\n" => @_; exit 0 }
+sub WARNING  { print "$NAME WARNING\n",  join "\n" => @_; exit 1 }
+sub CRITICAL { print "$NAME CRITICAL\n", join "\n" => @_; exit 2 }
+sub UNKNOWN  { print "$NAME UNKNOWN\n",  join "\n" => @_; exit 3 }
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+ check_amanda-client - check the amanda backup from the client side
+
+=head1 SYNOPSIS
+
+  check_amanda-client [-h|--help] [-m|--man]
+  check_amanda-client [options]
+
+=head1 DESCRIPTION
+
+This nagios check plugin checks the Amanda setup from the client side.
+
+=head1 OPTIONS
+
+=over
+
+=item B<-x>|B<--exclude> [I<config:>]I<filesystem>
+
+The name of a filesystem to be excluded. 
+No config means all configs.
+
+    check_amanda-client --exclude weekly:/var/spool/squid --exclude /boot
+
+
+=item B<-i>|B<--include> [I<config>:]I<filesystem>
+
+Which filesystems/directories we additionally want to check.
+No config means all configs.
+
+    check_amanda-client --include weekly:/etc
+
+If the directory is contained already in another directory/filesystem,
+the test is satisfied.
+
+=item B<-h>|B<--help>
+
+Show a short description and help text.
+
+=item B<-m>|B<--man>
+
+Show the man page of this tool.
+
+=item B<-v>|B<--verbose>
+
+Show what's going only. Many for debugging purpose. (default: off)
+
+=back
+
+=head1 PREPARATIONS
+
+In order to make the check working, some preparations needs to be done.
+
+=head1 Client
+
+For each backup set you want to check: Create an
+F</etc/amanda/$set/amanda-client.conf>. 
+
+    config "foo"
+    index-server    "amanda.example.com"    # used by restore
+    tape-server	    "amanda.example.com"    # used by restore
+    amdump-server   "amanda.example.com"    # used by amdump_client
+
+In addition, the F<amservice> binary has to be suid root and executable
+by the backup user. This requirement is checked automatically by the
+plugin.
+
+=head1 Server
+
+The server need to know about the amdumpd service. In the F<inetd.conf> 
+you need to add "amdumpd" to the list of allowed services. And
+additionally in F<.amandahosts> the "backup" user of the client needs
+the permissions to run the "amdumpd" service.
+
+    # inetd.conf
+    amanda stream tcp nowait backup /usr/lib/amanda/amandad amandad -auth=bsdtcp amindexd amidxtaped amdumpd
+
+    # .amandahosts
+    client.example.com backup amdumpd
+    client.example.com root amindexd amidxtaped 
+
+=head1 AUTHOR
+
+Heiko Schlittermann L<hs@schlittermann.de>
+
+=head1 SOURCE
+
+Source can be found at L<https://ssl.schlittermann.de/hg/nagios/nagios-plugin-amanda-client>
+
+=cut
+
+# vim:et ts=4 sw=4 aw ai:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/00-basic.t	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,58 @@
+
+use 5.014;
+no strict 'subs';
+use Test::More;
+use Test::Exception;
+use English qw(-no_match_vars);
+
+package FOO;
+::require_ok 'blib/nagios/plugins/ius/check_amanda-client';
+
+package main;
+
+subtest 'find_tool' => sub {
+    is FOO::find_tool('sh'), '/bin/sh' => 'found /bin/sh';
+    dies_ok { FOO: find_tool('abc123xyz-unknown-tool') }
+    'dies for unknown tool';
+};
+
+subtest 'check_perms' => sub {
+    my ($mode, $owner, $group) = (stat $EXECUTABLE_NAME)[2, 4, 5];
+    $mode &= 07777;
+
+    ok FOO::check_perms($EXECUTABLE_NAME, $mode, $owner, $group),
+      "perms of $EXECUTABLE_NAME 1";
+    ok FOO::check_perms(
+        $EXECUTABLE_NAME, $mode,
+        scalar(getpwuid $owner),
+        scalar(getgrgid $group)
+      ),
+      "perms of $EXECUTABLE_NAME 2";
+    dies_ok { FOO::check_perms($EXECUTABLE_NAME, $mode, $owner + 1, $group) }
+    'dies on wrong perms';
+};
+
+subtest 'config_names' => sub {
+    is_deeply [sort +FOO::config_names('t/etc/amanda')],
+      [sort qw(foobar DailySet1 WeeklySet1)] => 'got configs';
+    dies_ok { FOO::config_names('t/etc/no-amanda') } 'dies on missing configs';
+};
+
+subtest 'file systems' => sub {
+    my @fs = do {
+	local @ARGV = ('/proc/filesystems');
+	map { /(\S+)/ } grep { not /^nodev/ } <>;
+    };
+    my @devs =
+      sort { $a->[0] cmp $b->[0] }
+      map { [$_->[0], (stat $_->[0])[0]] }
+      grep { $_->[1] ~~ @fs }
+      map { [(split)[6, 1]] } `df -PT`;
+    ok scalar @fs => 'have file systems for testing';
+    ok scalar @devs => 'have devices for testing';
+    is_deeply [sort { $a->[0] cmp $b->[0] } FOO::get_devices()],
+      \@devs => 'got the proper devices';
+
+};
+
+done_testing;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/etc/amanda/DailySet1/amanda-client.conf	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,1 @@
+conf "DailySet1"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/etc/amanda/WeeklySet1/amanda-client.conf	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,1 @@
+conf "WeeklySet1" # testing only
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/t/etc/amanda/foo/amanda-client.conf	Sat Jan 25 23:47:39 2014 +0100
@@ -0,0 +1,1 @@
+conf "foobar"