[project @ peter@palfrader.org-20090203193249-w30idzor9gljzbif]
authorPeter Palfrader <peter@palfrader.org>
Tue, 3 Feb 2009 19:32:49 +0000 (20:32 +0100)
committerPeter Palfrader <peter@palfrader.org>
Tue, 3 Feb 2009 19:32:49 +0000 (20:32 +0100)
Add dsa-check-packages

dsa-nagios-nrpe-config/debian/changelog
dsa-nagios-nrpe-config/debian/copyright
dsa-nagios-nrpe-config/debian/rules
dsa-nagios-nrpe-config/dsa-check-packages [new file with mode: 0755]

index 0f974a3..b72f56f 100644 (file)
@@ -1,3 +1,9 @@
+dsa-nagios-nrpe-config (66) unstable; urgency=low
+
+  * Add dsa-check-packages
+
+ -- Peter Palfrader <weasel@debian.org>  Tue, 03 Feb 2009 20:31:53 +0100
+
 dsa-nagios-nrpe-config (65) unstable; urgency=low
 
   * Add dsa-check-soas
index 6ec8446..300ca52 100644 (file)
@@ -71,6 +71,11 @@ dsa-check-mirrorsync:
   Copyright: 2008: Peter Palfrader
   License: GPL
 
+########################################################################
+dsa-check-packages:
+  Copyright: 2008,2009 Peter Palfrader
+  License: MIT
+
 ########################################################################
 dsa-check-soas:
   Copyright: 2006 Peter Palfrader
index 952f7fb..a00e7a7 100755 (executable)
@@ -29,6 +29,7 @@ install:
        install -m 755 dsa-check-samhain $(CURDIR)/debian/dsa-nagios-nrpe-config/usr/lib/nagios/plugins
        install -m 755 dsa-check-mirrorsync $(CURDIR)/debian/dsa-nagios-nrpe-config/usr/lib/nagios/plugins
        install -m 755 dsa-check-soas $(CURDIR)/debian/dsa-nagios-nrpe-config/usr/lib/nagios/plugins
+       install -m 755 dsa-check-packages $(CURDIR)/debian/dsa-nagios-nrpe-config/usr/lib/nagios/plugins
 
        install -m 755 apt-status-check $(CURDIR)/debian/dsa-nagios-nrpe-config/usr/share/dsa
        install -m 755 weak-ssh-keys-check $(CURDIR)/debian/dsa-nagios-nrpe-config/usr/share/dsa
diff --git a/dsa-nagios-nrpe-config/dsa-check-packages b/dsa-nagios-nrpe-config/dsa-check-packages
new file mode 100755 (executable)
index 0000000..d3073b1
--- /dev/null
@@ -0,0 +1,266 @@
+#!/usr/bin/perl
+
+# dsa-check-packages
+
+# checks for obsolete/local and upgradeable packages.
+#
+# packages for the obsolete/local check can be ignored, by
+# listing their full name in /etc/nagios/obsolete-packages-ignore
+# or by having a regex (starting a line with "/") that matches
+# the packagename in said file.
+#
+# Takes one optional argument, the location of the ignore file.
+
+
+# Copyright (C) 2008, 2009 Peter Palfrader <peter@palfrader.org>
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+use strict;
+use warnings;
+use English;
+
+my $IGNORE = "/etc/nagios/obsolete-packages-ignore";
+
+my %CODE = (
+       'OK'            => 0,
+       'WARNING'       => 1,
+       'CRITICAL'      => 2,
+       'UNKNOWN'       => 3
+);
+my $EXITCODE = 'OK';
+sub record($) {
+       my ($newexit) = @_;
+       die "code $newexit not defined\n" unless defined $CODE{$newexit};
+
+       if ($CODE{$newexit} > $CODE{$EXITCODE}) {
+               $EXITCODE = $newexit;
+       };
+}
+
+
+
+sub get_packages {
+       $ENV{'COLUMNS'} = 1000;
+       $ENV{'LC_ALL'} = 'C';
+       open(F, "dpkg -l|") or die ("Cannot run dpkg: $!\n");
+       my @lines = <F>;
+       close(F);
+       chomp(@lines);
+
+       shift @lines while ($lines[0] !~ /\+\+\+/);
+       shift @lines;
+
+       my %pkgs;
+       for my $line (@lines) {
+               my ($state, $pkg, $version, undef) = split(/  */, $line);
+               $pkgs{$state}{$pkg} = { 'installed' => $version }
+       }
+
+       my $installed = $pkgs{'ii'};
+       delete $pkgs{'ii'};
+
+       open (F, "apt-cache policy ".(join(" ", keys(%$installed)))." |") or die ("Cannot run apt-cache policy: $!\n");
+       @lines = <F>;
+       close(F);
+       chomp(@lines);
+
+       my $line;
+       my $pkgname = undef;
+       while (defined($line = shift @lines)) {
+               if ($line =~ /^([^ ]*):$/) {
+                       $pkgname = $1;
+               } elsif ($line =~ /^ +Installed: (.*)$/) {
+                       # etch dpkg -l does not print epochs, so use this info, it's better
+                       $installed->{$pkgname}{'installed'} = $1;
+               } elsif ($line =~ /^ +Candidate: (.*)$/) {
+                       $installed->{$pkgname}{'candidate'} = $1;
+               } elsif ($line =~ /^ +\*\*\*/) {
+                       my @l;
+                       @l = split(/ +/, $line);
+                       $line = shift @lines;
+                       @l = split(/ +/, $line);
+                       $installed->{$pkgname}{'origin'} = $l[2];
+               }
+       }
+
+       my (%current, %obsolete, %outofdate);
+       for my $pkgname (keys %$installed) {
+               my $pkg = $installed->{$pkgname};
+
+               if ($pkg->{'candidate'} ne $pkg->{'installed'}) {
+                       $outofdate{$pkgname} = $pkg;
+                       next;
+               };
+               if ($pkg->{'origin'} eq '/var/lib/dpkg/status') {
+                       $obsolete{$pkgname} = $pkg;
+                       next;
+               }
+               $current{$pkgname} = $pkg;
+       }
+
+       $pkgs{'current'} = \%current;
+       $pkgs{'outofdate'} = \%outofdate;
+       $pkgs{'obsolete'} = \%obsolete;
+       return \%pkgs;
+}
+
+sub load_ignores {
+       my ($ignorefile, $require_file) = @_;
+
+       my @ignores;
+       if (!$require_file and ! -e $ignorefile) {
+               return \@ignores;
+       }
+
+       open (F, "< $ignorefile") or die ("Cannot open $ignorefile: $!\n");
+       @ignores = <F>;
+       close F;
+       chomp(@ignores);
+       return \@ignores;
+}
+
+sub check_ignore {
+       my ($pkg, $ignores) = @_;
+
+       my $ignore_this = 0;
+       for my $ig (@$ignores) {
+               return 1 if ($ig eq $pkg);
+               if (substr($ig,0,1) eq '/') {
+                       substr($ig, 0, 1, '');
+                       $ig =~ s,/$,,;
+                       return 1 if ($pkg =~ /$ig/);
+               }
+       }
+       return 0
+}
+
+sub filter_ignored {
+       my ($packages, $ignores) = @_;
+
+       my $obs = $packages->{'obsolete'};
+
+       my (%ignored, %bad);
+       for my $pkg (keys %$obs) {
+               if (check_ignore($pkg, $ignores)) {
+                       $ignored{$pkg} = $obs->{$pkg};
+               } else {
+                       $bad{$pkg} = $obs->{$pkg};
+               };
+       }
+       delete $packages->{'obsolete'};
+       $packages->{'obsolete'} = \%bad;
+       $packages->{'obsolete-ignored'} = \%ignored;
+};
+
+sub usage {
+       my ($fd, $exit) = @_;
+       print $fd "Usage: $PROGRAM_NAME [<ignorefile>]\n";
+       exit $exit;
+}
+
+my $ignorefile = $IGNORE;
+my $ignorefile_userset = 0;
+usage(\*STDERR, 1) if (@ARGV > 1);
+if (@ARGV == 1) {
+       usage(\*STDOUT, 0) if ($ARGV[0] eq "-h");
+       usage(\*STDOUT, 0) if ($ARGV[0] eq "--help");
+       $ignorefile = $ARGV[0];
+       $ignorefile_userset = 1;
+};
+
+my $ignores = load_ignores($ignorefile, $ignorefile_userset);
+my $packages = get_packages();
+
+filter_ignored($packages, $ignores);
+
+
+
+my @reportform = (
+       { 'key' => 'obsolete',
+         'listpackages' => 1,
+         'long' => "%d local or obsolete packages: %s",
+         'short' => "%d obs",
+         'status' => 'WARNING' },
+       { 'key' => 'outofdate',
+         'listpackages' => 1,
+         'long' => "%d out of date packages: %s",
+         'short' => "%d updates",
+         'status' => 'WARNING' },
+       { 'key' => 'obsolete-ignored',
+         'listpackages' => 1,
+         'long' => "%d local or obsolete packages (ignored): %s",
+         'short' => "%d obs(ign)",
+         'status' => 'OK' },
+       { 'key' => 'current',
+         'listpackages' => 0,
+         'long' => "%d packages current.",
+         'short' => "%d ok",
+         'status' => 'OK' },
+       { 'key' => 'rc',
+         'listpackages' => 1,
+         'long' => "%d packages removed but not purged: %s",
+         'short' => "%d rc",
+         'status' => 'OK' },
+       { 'key' => 'hi',
+         'listpackages' => 1,
+         'long' => "%d packages on hold: %s",
+         'short' => "%d hi",
+         'status' => 'OK' },
+       { 'key' => 'pc',
+         'listpackages' => 1,
+         'long' => "%d packages requested to be purged but conffiles still installed: %s",
+         'short' => "%d pc",
+         'status' => 'WARNING' },
+       );
+
+my @longout;
+my @shortout;
+for my $form (@reportform) {
+       my $pkgs = $packages->{$form->{'key'}};
+       delete $packages->{$form->{'key'}};
+       my $num = scalar keys %$pkgs;
+       next unless ($num > 0);
+       if ($form->{'listpackages'}) {
+               my $list = join(", ", keys %$pkgs);
+               push @longout, sprintf($form->{'long'}, $num, $list);
+       } else {
+               push @longout, sprintf($form->{'long'}, $num);
+       };
+       push @shortout, sprintf($form->{'short'}, $num);
+       record($form->{'status'});
+};
+if (scalar keys %$packages) {
+       record('WARNING');
+       unshift @shortout, "unk: ".join(", ", keys %$packages);
+       for my $status (sort {$b cmp $a} keys %$packages) {
+               my $pkgs = $packages->{$status};
+               my $list = join(", ", keys %$pkgs);
+               unshift @longout, "Unknown package status $status: $list";
+       };
+}
+
+my $shortout = $EXITCODE.": ".join(", ", @shortout);
+my $longout = join("\n", @longout);
+
+print $shortout,"\n";
+print $longout,"\n";
+
+exit $CODE{$EXITCODE};