diff -r 7b9bad9c85e6 -r da08b2a30e06 check --- a/check Fri Jan 10 10:53:21 2014 +0100 +++ b/check Fri Jan 10 11:20:30 2014 +0100 @@ -37,130 +37,134 @@ sub main { - GetOptions( - 'h|help' => sub { pod2usage(-verbose => 1, -exit => 0) }, - 'm|man' => sub { pod2usage(-verbose => 2, -exit => 0) }, - ) or pod2usage; - + GetOptions( + 'h|help' => sub { pod2usage(-verbose => 1, -exit => 0) }, + 'm|man' => sub { pod2usage(-verbose => 2, -exit => 0) }, + ) or pod2usage; - # test needs to be run as root:* or as backup:backup - my $USER = 'backup'; - my $CFDIR = '/etc/amanda'; + # 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; + # change to backup if still root + su $USER if $> == 0; - # amservice needs to be suid root, but executable - # by the backup user/group - eval { check_perms find_tool('amservice'), 04750, 'root', $) } - or unknown $@; + # amservice needs to be suid root, but executable + # by the backup user/group + 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 + # 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 - my @confs = eval { config_names $CFDIR } - or unknown $@; + my @confs = eval { config_names $CFDIR } + or unknown $@; - eval { amchecks @confs } or critical $@; + eval { amchecks @confs } or critical $@; - my @dles = eval { amlists @confs } or critical $@; - ok @dles; + my @dles = eval { amlists @confs } or critical $@; + ok @dles; - # never reached - return 0; + # 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"; + 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 @@ -170,51 +174,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__ @@ -244,6 +251,38 @@ =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. + + 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 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 +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