[merged] heiko
authorHeiko Schlittermann (JUMPER) <hs@schlittermann.de>
Sat, 11 Jan 2014 17:25:56 +0100
branchheiko
changeset 6 aafd8332d7a6
parent 3 71a0c4bd43b1 (current diff)
parent 5 52faa36ba7f8 (diff)
child 7 2bda25a39ef4
[merged]
check
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.perltidyrc	Sat Jan 11 17:25:56 2014 +0100
@@ -0,0 +1,3 @@
+--paren-tightness=2
+--square-bracket-tightness=2
+--nospace-for-semicolon
--- 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: