[project @ peter@palfrader.org-20080623071927-p39zxuq2sk0enkbv]
[mirror/dsa-nagios.git] / dsa-nagios-nrpe-config / apt-status-check
1 #!/usr/bin/perl -Tw
2
3 # $Id: nagios-check-apt-updates 352 2008-05-20 21:36:54Z weasel $
4
5 # nagios check for debian (security) updates,
6 # based on net-snmp glue to security updates via apt-get.
7 #  Copyright (C) 2004 SILVER SERVER Gmbh
8 #  Copyright (C) 2004, 2005, 2006, 2007, 2008 Peter Palfrader
9 #
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful, but
16 # WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 # General Public License for more details.
19 #
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software
22 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 # USA
24
25 use strict;
26 use English;
27 use Getopt::Long;
28 use IO::Handle;
29 use IPC::Open2;
30
31 $ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin';
32 delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
33
34 my $APT = '/usr/bin/apt-get';
35 my $VERBOSE;
36
37 sub do_check($$$$$$) {
38         my ($pre_command, $timeout, $noupdate, $name, $updates_security, $updates_other) = @_;
39         my $fh;
40         my $pid;
41         my @command;
42
43         unless ($noupdate) {
44                 print STDERR "Running $APT update in $name\n" if $VERBOSE;
45                 @command = ($APT, 'update');
46                 unshift @command, @$pre_command;
47                 $fh = new IO::Handle;
48                 $pid = open2($fh, \*STDIN, @command) or die ("Cannot run $APT update in $name: $!\n");
49                 local $SIG{ALRM} = sub { die "Timeout for apt-get update.\n" };
50                 alarm $timeout;
51                 my @ignore=<$fh>;
52                 alarm 0;
53                 close $fh;
54                 waitpid $pid, 0;
55                 if ($CHILD_ERROR) { # program failed
56                         die("$APT update returned with non-zero exit code in $name: ".($CHILD_ERROR / 256)."\n");
57                 };
58         };
59
60         print STDERR "Running $APT --simulate upgrade in $name\n" if $VERBOSE;
61         @command = ($APT, qw{--simulate upgrade});
62         unshift @command, @$pre_command;
63         $fh = new IO::Handle;
64         $pid = open2($fh, \*STDIN, @command) or die ("Cannot run $APT --simulate upgrade | sort -u in $name: $!\n");
65         local $SIG{ALRM} = sub { die "Timeout for apt-get --simulate upgrade.\n" };
66         alarm $timeout;
67         my @lines=<$fh>;
68         close $fh;
69         alarm 0;
70         waitpid $pid, 0;
71         if ($CHILD_ERROR) { # program failed
72                 die("$APT --simulate upgrade | sort -u returned with non-zero exit code in $name: ".($CHILD_ERROR / 256)."\n");
73         };
74
75         @lines = sort {$a cmp $b} @lines;
76         my %uniq;
77         @lines = grep {!$uniq{$_}++} @lines;
78
79         print STDERR "Processing information for $name\n" if $VERBOSE;
80         for my $line (@lines)  {
81                 if ($line =~ m/^Inst\s+(\S+)\s+/) {
82                         my $package = $1;
83                         if ($line =~ m/^Inst\s+\S+\s+.*security/i) {
84                                 push @$updates_security, $package.($name ne '/' ? "($name)" : '');
85                         } else {
86                                 push @$updates_other, $package.($name ne '/' ? "($name)" : '');
87                         };
88                 }
89         }
90 }
91
92
93
94 my $VERSION = '0.0.3 - $Rev: 352 $';
95 my $use_sudo = 1;
96 my $params;
97
98 # nagios exit codes
99 my $OK = 0;
100 my $WARNING = 1;
101 my $CRITICAL = 2;
102 my $UNKNOWN = 3;
103
104 $params->{'chroots'} = [];
105 $params->{'vservers'} = [];
106 $params->{'timeout'} = 20;
107 Getopt::Long::config('bundling');
108 if (!GetOptions (
109         '--help'                => \$params->{'help'},
110         '--version'             => \$params->{'version'},
111         '--sudo'                => \$params->{'sudo'},
112         '--noupdate'            => \$params->{'noupdate'},
113         '--nosudo'              => \$params->{'nosudo'},
114         '--verbose'             => \$params->{'verbose'},
115         '--warnifupdates'       => \$params->{'warnifupdates'},
116         '--timeout=i'           => \$params->{'timeout'},
117         '--chroot=s'            => $params->{'chroots'},
118         '--vserver=s'           => $params->{'vservers'}
119         )) {
120         die ("Usage: $PROGRAM_NAME [--help|--version] [--sudo|--nosudo] [--timeout=<timeout>] [--verbose]\n");
121 };
122 if ($params->{'help'}) {
123         print "nagios-check-apt-updates $VERSION\n";
124         print "Usage: $PROGRAM_NAME [--help|--version] [--sudo|--nosudo] [--verbose]\n";
125         print "Reports packages to upgrade, updating the list if necessary.\n";
126         print "\n";
127         print "  --help              Print this short help.\n";
128         print "  --version           Report version number.\n";
129         print "  --sudo              Use sudo to call apt-get (default).\n";
130         print "  --noupdate          Do not run apt-get update first.\n";
131         print "  --nosudo            Do not use sudo to call apt-get.\n";
132         print "  --warnifupdates     Exit with a WARNING status if any updates are available.\n";
133         print "  --timeout=<timeout> Timeout in seconds for each of the two apt-get runs.\n";
134         print "  --verbose           Be a little verbose.\n";
135         print "  --chroot=<path>     Run check in path.\n";
136         print "  --vserver=<vserver> Run check in vserver.\n";
137         print "\n";
138         print "Note that for --sudo (default) you will need entries in /etc/sudoers like these:\n";
139         print "nagios  ALL=(ALL) NOPASSWD: /usr/bin/apt-get update\n";
140         print "nagios  ALL=(ALL) NOPASSWD: /usr/bin/apt-get --simulate upgrade\n";
141         print "nagios  ALL=(ALL) NOPASSWD: /usr/sbin/chroot /chroot-ia32 /usr/bin/apt-get update\n";
142         print "nagios  ALL=(ALL) NOPASSWD: /usr/sbin/chroot /chroot-ia32 /usr/bin/apt-get --simulate upgrade\n";
143         print "nagios  ALL=(ALL) NOPASSWD: /usr/sbin/vserver phpserver exec /usr/bin/apt-get update\n";
144         print "nagios  ALL=(ALL) NOPASSWD: /usr/sbin/vserver phpserver exec /usr/bin/apt-get --simulate upgrade\n";
145         print "\n";
146         exit (0);
147 };
148 if ($params->{'version'}) {
149         print "nagios-check-apt-updates $VERSION\n";
150         print "nagios check for availability of debian (security) updates\n";
151         print "Copyright (c) 2004 SILVER SERVER Gmbh\n";
152         print "Copyright (c) 2004,2005 Peter Palfrader <peter\@palfrader.org>\n";
153         exit (0);
154 };
155 if ($params->{'sudo'} && $params->{'nosudo'}) {
156         die ("$PROGRAM_NAME: --sudo and --nosudo are mutually exclusive.\n");
157 };
158 if ($params->{'sudo'}) {
159         $use_sudo = 1;
160 };
161 if ($params->{'nosudo'}) {
162         $use_sudo = 0;
163 };
164 if (scalar @{$params->{'chroots'}} == 0 && scalar @{$params->{'vservers'}} == 0) {
165         $params->{'chroots'} = ['/'];
166 };
167 $VERBOSE = $params->{'verbose'};
168
169
170 $SIG{'__DIE__'} = sub {
171         print STDERR @_;
172         exit $UNKNOWN;
173 };
174
175
176 my @updates_security;
177 my @updates_other;
178
179
180 # Make sure chroot paths are nice;
181 my @chroots = ();
182 for my $root (@{$params->{'chroots'}}) {
183         if ($root =~ m#^(/[a-zA-Z0-9/.-]*)$#) {
184                 push @chroots, $1;
185         } else {
186                 die ("Chroot path $root is not nice.\n");
187         };
188 };
189 for my $root (@chroots) {
190         my @pre_command = ();
191         unshift @pre_command, 'chroot', $root if ($root ne '/');
192         unshift @pre_command, 'sudo' if $use_sudo;
193         do_check(\@pre_command, $params->{'timeout'}, $params->{'noupdate'}, $root, \@updates_security, \@updates_other);
194 }
195
196 # Make sure vserver names are nice;
197 my @vservers = ();
198 for my $vserver (@{$params->{'vservers'}}) {
199         if ($vserver =~ m#^([a-zA-Z0-9.-]+)$#) {
200                 push @vservers, $1;
201         } else {
202                 die ("Vserver name $vserver is not nice.\n");
203         };
204 };
205 for my $vserver (@vservers) {
206         my @pre_command = ();
207         unshift @pre_command, '/usr/sbin/vserver', $vserver, 'exec';
208         unshift @pre_command, 'sudo' if $use_sudo;
209         do_check(\@pre_command, $params->{'timeout'}, $params->{'noupdate'}, $vserver, \@updates_security, \@updates_other);
210 }
211
212
213
214
215 my $exit = $OK;
216
217 my $updateinfo;
218 if (@updates_security) {
219         $updateinfo .= 'Security updates ('.(scalar @updates_security).'): '.join(', ', @updates_security)."; ";
220         $exit = $CRITICAL;
221 }
222 if (@updates_other) {
223         $updateinfo .= 'Other Updates ('.(scalar @updates_other).'): '.join(', ', @updates_other)."; ";
224         $exit = $WARNING if ($params->{'warnifupdates'} and $exit == $OK);
225 };
226 $updateinfo = 'No updates available' unless defined $updateinfo;
227
228
229 print $updateinfo,"\n";
230 exit $exit;