--- a/check Fri Jan 10 11:13:12 2014 +0100
+++ b/check Sat Jan 11 17:25:56 2014 +0100
@@ -1,4 +1,5 @@
#! /usr/bin/perl
+# source: https://ssl.schlittermann.de/hg/ius/nagios/nagios-plugin-amanda-client
use 5.010;
use strict;
@@ -28,6 +29,8 @@
sub warning;
sub critical;
sub unknown;
+sub verbose;
+sub unique { my %h; @h{@_} = (); keys %h }
$SIG{__DIE__} = sub { unknown @_ unless $^S };
@@ -36,132 +39,150 @@
#----
sub main {
- my @opt_ignore;
+ 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;
- GetOptions(
- 'i|ignore=s@' => \@opt_ignore,
- 'h|help' => sub { pod2usage(-verbose => 1, -exit => 0) },
- 'm|man' => sub { pod2usage(-verbose => 2, -exit => 0) },
- ) 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;
+ # test needs to be run as root:* or as backup:backup
+ my $USER = 'backup';
+ my $CFDIR = '/etc/amanda';
- # amservice needs to be suid root, but executable
- # by the backup user/group
- eval { check_perms find_tool('amservice'), 04750, 'root', $) }
- or unknown $@;
+ # change to backup if still root
+ su $USER if $> == 0;
- # 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
+ # 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 $@;
- my @confs = eval { config_names $CFDIR }
- 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
- eval { amchecks @confs } or critical $@;
+ verbose qq{find config names from $CFDIR};
+ my @confs = sort +unique eval { config_names $CFDIR }
+ or unknown $@;
- my @dles = eval { amlists @confs } or critical $@;
- ok @dles;
+ eval { amchecks @confs } or critical $@;
- # never reached
- return 0;
+ my @dles = eval { amlists @confs } 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`;
+ 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 $user = shift;
+ my $group = (getgrnam $user)[0];
+ my $uid = getpwnam $user;
+ my $gid = getgrnam $group;
+
+ my @groups;
- my @groups;
-
- setgrent;
- my @rc;
- while (my @g = getgrent) {
- push @groups, $g[2] if $user ~~ [split ' ', $g[3]];
- }
- endgrent;
- $) = "@groups";
- setgid $gid;
- setuid $uid;
+ 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];
-};
+ 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) = @_;
+ my ($file, $mode, $owner, $group) = @_;
+
+ $owner = getpwuid $owner if $owner ~~ /^\d+$/;
- $owner = getpwuid $owner if $owner ~~ /^\d+$/;
-
- $group = getgrgid +(split ' ', $group)[0]
- if $group ~~ /^[\d\s]+$/;
+ $group = getgrgid +(split ' ', $group)[0]
+ if $group ~~ /^[\d\s]+$/;
- stat $file or croak "Can't stat `$file': $!\n";
+ 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;
+ 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 $!;
- 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;
+ 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);
+ 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;
-};
+ 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
+ #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
@@ -171,51 +192,54 @@
}
sub amchecks {
- my @errors = ();
- foreach my $conf (@_) {
- eval { _amcheck $conf } or push @errors, $@;
- }
- die @errors if @errors;
- return 1;
+ 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;
+
+ # 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 @confs = @_;
- my @candidates = get_devices;
+ my @confs = @_;
+ my @candidates = get_devices;
- my %missing;
+ my %missing;
+
+ foreach my $conf (@confs) {
+ my @dles = _amlist $conf;
+ foreach my $candidate (@candidates) {
- 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];
- }
- }
- die map { "$_ missing: " . join(', ' => @{$missing{$_}}) . "\n" } keys %missing
- if %missing;
- return map { $_->[0] } @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];
+ }
+ }
+ 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 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 }
+sub unknown { print "$NAME UNKNOWN\n", join "\n" => @_; exit 3 }
__END__
@@ -247,8 +271,44 @@
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>
@@ -259,4 +319,4 @@
=cut
-
+# vim:et ts=4 sw=4 aw ai: