1 #! /usr/bin/perl |
1 #! /usr/bin/perl |
2 |
2 |
3 use 5.010; |
3 use 5.010; |
4 use strict; |
4 use strict; |
5 use warnings; |
5 use warnings; |
|
6 use Getopt::Long; |
6 use POSIX; |
7 use POSIX; |
7 use File::Spec::Functions; |
8 use File::Spec::Functions; |
8 use Data::Dumper; |
9 use Data::Dumper; |
9 use File::Find; |
10 use File::Find; |
10 use Carp; |
11 use Carp; |
|
12 use Pod::Usage; |
11 |
13 |
12 sub su; |
14 sub su; |
13 sub find_tool; |
15 sub find_tool; |
14 sub check_perms; |
16 sub check_perms; |
15 sub config_names; |
17 sub config_names; |
16 sub get_devices; |
18 sub get_devices; |
17 sub amcheck; |
19 sub amchecks; |
18 sub amlist; |
20 sub amlists; |
19 |
21 sub main; |
20 # test needs to be run as root:* or as backup:backup |
22 |
21 my $USER = 'backup'; |
23 my $NAME = 'AMANDA-CLIENT'; |
22 my $CFDIR = '/etc/amanda'; |
24 |
23 |
25 sub ok; |
24 # change to backup if still root |
26 sub warning; |
25 su $USER if $> == 0; |
27 sub critical; |
26 |
28 sub unknown; |
27 # amservice needs to be suid root, but executable |
29 |
28 # by the backup user/group |
30 $SIG{__DIE__} = sub { unknown @_ unless $^S }; |
29 check_perms find_tool('amservice'), 04750, 'root', $); |
31 |
30 |
32 exit main @ARGV; |
31 # find the backup sets we know about |
33 |
32 # here we suppose that it's possible to find strings like |
34 #---- |
33 # 'conf "foo"' in files named 'amanda-client.conf' below /etc/amanda |
35 |
34 |
36 sub main { |
35 my @confs = config_names $CFDIR |
37 |
36 or die "no amanda backup sets found (did: find $CFDIR -name amanda-client.conf)\n"; |
38 GetOptions( |
37 |
39 'h|help' => sub { pod2usage(-verbose => 1, -exit => 0) }, |
38 #eval { amcheck $_ } or die $@ |
40 'm|man' => sub { pod2usage(-verbose => 2, -exit => 0) }, |
39 # foreach @confs; |
41 ) or pod2usage; |
40 |
42 |
41 my @devs = get_devices; |
43 |
42 foreach (@confs) { |
44 # test needs to be run as root:* or as backup:backup |
43 my @dles = amlist $_; |
45 my $USER = 'backup'; |
44 warn Dumper \@dles; |
46 my $CFDIR = '/etc/amanda'; |
45 warn Dumper \@devs; |
47 |
46 } |
48 # change to backup if still root |
47 |
49 su $USER if $> == 0; |
48 exit; |
50 |
49 #--- |
51 # amservice needs to be suid root, but executable |
|
52 # by the backup user/group |
|
53 eval { check_perms find_tool('amservice'), 04750, 'root', $) } |
|
54 or unknown $@; |
|
55 |
|
56 # find the backup sets we know about |
|
57 # here we suppose that it's possible to find strings like |
|
58 # 'conf "foo"' in files named 'amanda-client.conf' below /etc/amanda |
|
59 |
|
60 my @confs = eval { config_names $CFDIR } |
|
61 or unknown $@; |
|
62 |
|
63 eval { amchecks @confs } or critical $@; |
|
64 |
|
65 my @dles = eval { amlists @confs } or critical $@; |
|
66 ok @dles; |
|
67 |
|
68 # never reached |
|
69 return 0; |
|
70 } |
50 |
71 |
51 # compare the file systems |
72 # compare the file systems |
52 # get a list of file system |
73 # get a list of file system |
53 sub get_devices { |
74 sub get_devices { |
54 open(my $fh, '/proc/filesystems'); |
75 open(my $fh, '/proc/filesystems'); |
66 my @groups; |
87 my @groups; |
67 |
88 |
68 setgrent; |
89 setgrent; |
69 my @rc; |
90 my @rc; |
70 while (my @g = getgrent) { |
91 while (my @g = getgrent) { |
71 push @groups, $g[2] if $USER ~~ [split ' ', $g[3]]; |
92 push @groups, $g[2] if $user ~~ [split ' ', $g[3]]; |
72 } |
93 } |
73 endgrent; |
94 endgrent; |
74 $) = "@groups"; |
95 $) = "@groups"; |
75 setgid $gid; |
96 setgid $gid; |
76 setuid $uid; |
97 setuid $uid; |
77 } |
98 } |
78 |
99 |
79 sub find_tool { |
100 sub find_tool { |
80 my $name = shift; |
101 my $name = shift; |
81 my @rc = grep { -f -x } map { catfile $_, $name } split /:/, $ENV{PATH} |
102 my @rc = grep { -f -x } map { catfile $_, $name } split /:/, $ENV{PATH} |
82 or croak "Can't find `$name' in $ENV{PATH}\n"; |
103 or die "Can't find `$name' in $ENV{PATH}\n"; |
83 $rc[0]; |
104 $rc[0]; |
84 }; |
105 }; |
85 |
106 |
86 sub check_perms { |
107 sub check_perms { |
87 my ($file, $mode, $owner, $group) = @_; |
108 my ($file, $mode, $owner, $group) = @_; |
115 find(sub { |
137 find(sub { |
116 -f and /^amanda-client\.conf$/ or return; |
138 -f and /^amanda-client\.conf$/ or return; |
117 open(my $fh, '<', $_) or die "Can't open $File::Find::name: $!\n"; |
139 open(my $fh, '<', $_) or die "Can't open $File::Find::name: $!\n"; |
118 push @configs, map { /^conf\s+"(.+?)"/ ? $1 : () } <$fh>; |
140 push @configs, map { /^conf\s+"(.+?)"/ ? $1 : () } <$fh>; |
119 }, $dir); |
141 }, $dir); |
|
142 |
|
143 die "no configs found below $dir (amanda-client.conf needs need `conf \"foo\"' line)\n" |
|
144 if not @configs; |
120 return @configs; |
145 return @configs; |
121 }; |
146 }; |
122 |
147 |
123 sub amcheck { |
148 sub amchecks { |
124 my $conf = shift; |
149 my @errors; |
125 my @errors = map { "$conf: $_" } grep { /^error/i } qx(amdump_client --config '$conf' check 2>&1); |
150 foreach my $conf (@_) { |
126 die @errors if @errors; |
151 push @errors, map { "$conf: $_" } grep { /^error/i } qx(amdump_client --config '$conf' check 2>&1); |
|
152 } |
|
153 die join "\n", @errors if @errors; |
127 return 1; |
154 return 1; |
128 } |
155 } |
129 |
156 |
130 sub amlist { |
157 sub amlist { |
|
158 # return a list of [ name, dev ] tupels. |
|
159 # name: the name of the disk/device |
|
160 # dev: the local device id (stat)[0] |
|
161 # iff the inum of $name != 2, it's not the top directory |
|
162 # and we set the device id to -1, since $name does not stand for a whole |
|
163 # device |
131 my $conf = shift; |
164 my $conf = shift; |
132 chomp((undef, my @dles) = qx(amdump_client --config '$conf' list)); |
165 chomp((undef, my @dles) = qx(amdump_client --config '$conf' list)); |
133 return map { [$_, (stat $_)[0] ] } @dles; |
166 return map { [$_, (stat $_)[1] == 2 ? (stat $_)[0] : -1 ] } @dles; |
134 } |
167 } |
|
168 |
|
169 sub amlists { |
|
170 my @confs = @_; |
|
171 my @candidates = get_devices; |
|
172 |
|
173 my %missing; |
|
174 |
|
175 foreach my $conf (@confs) { |
|
176 my @dles = amlist $conf; |
|
177 foreach my $candidate (@candidates) { |
|
178 # we're satisfied if either the name of the device is in |
|
179 # the disklist, or the device id is found |
|
180 $candidate->[0] ~~ [ map { $_->[0] } @dles ] and next; |
|
181 $candidate->[1] ~~ [ map { $_->[1] } @dles ] and next; |
|
182 push @{$missing{$conf}}, $candidate->[0]; |
|
183 } |
|
184 } |
|
185 die map { "$_ missing: " . join(', ' => @{$missing{$_}}) . "\n" } keys %missing |
|
186 if %missing; |
|
187 return map { $_->[0] } @candidates; |
|
188 } |
|
189 |
|
190 sub ok { say "$NAME OK\n", join "\n" => @_; exit 0 } |
|
191 sub warning { print "$NAME WARNING\n", join "\n" => @_; exit 1 } |
|
192 sub critical { print "$NAME CRITICAL\n", join "\n" => @_; exit 2 } |
|
193 sub unknown { print "$NAME UNKNOWN\n", join "\n" => @_; exit 3 } |
135 |
194 |
136 __END__ |
195 __END__ |
137 system amdump_client => '--config', 'daily', 'list'; |
196 |
|
197 =head1 NAME |
|
198 |
|
199 check_amanda-client - check the amanda backup from the client side |
|
200 |
|
201 =head1 SYNOPSIS |
|
202 |
|
203 check_amanda-client [-h|--help] [-m|--man] |
|
204 |
|
205 =head1 DESCRIPTION |
|
206 |
|
207 This nagios check plugin checks the Amanda setup from the client side. |
|
208 |
|
209 =head1 OPTIONS |
|
210 |
|
211 =over |
|
212 |
|
213 =item B<-h>|B<--help> |
|
214 |
|
215 Show a short description and help text. |
|
216 |
|
217 =item B<-m>|B<--man> |
|
218 |
|
219 Show the man page of this tool. |
|
220 |
|
221 =back |
|
222 |
|
223 =head1 AUTHOR |
|
224 |
|
225 Heiko Schlittermann L<hs@schlittermann.de> |
|
226 |
|
227 =head1 SOURCE |
|
228 |
|
229 Source can be found at L<https://ssl.schlittermann.de/hg/nagios/nagios-plugin-amanda-client> |
|
230 |
|
231 =cut |
|
232 |
|
233 |