File check_zypper.pl of Package nagios-plugins-zypper

#!/usr/bin/perl -w
#
# check_zypper - nagios plugin
#
# Copyright (C) 2008, Novell, Inc.
# Author: Lars Vogdt
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the Novell nor the names of its contributors may be
#   used to endorse or promote products derived from this software without
#   specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# $Id$
#

use strict;
use Getopt::Long;
use vars qw($PROGNAME);
use lib "/usr/lib/nagios/plugins"; # Path to util.pm 
use utils qw ($TIMEOUT %ERRORS &print_revision &support &usage);

sub print_help ();
sub print_usage ();

# cleanup the environment
$ENV{'PATH'}='/bin:/usr/bin:/sbin:/usr/sbin:';
$ENV{'BASH_ENV'}='';
$ENV{'ENV'}='';

# constants
$PROGNAME="check_zypper";
my $VERSION="0.9";
my $DEBUG = 0;

# variables
my $zypper="/usr/bin/zypper";
my $zypperopt="--non-interactive --no-gpg-checks xml-updates";
my $sudo="/usr/bin/sudo";
my $use_sudo="unset LANG; ";
my $releasefile="/etc/SuSE-release";
my $release="11.0";
my $dist="openSUSE";
my $patchlevel=0;
my ($opt_V, $opt_h, $opt_H, $opt_w, $opt_c, $opt_r, $opt_s, $opt_t, $opt_v);

$opt_H="";
$opt_w="recommended,optional";
$opt_c="security";
$opt_r="$releasefile";
$opt_t="120";
$opt_v=0;
$opt_s=0;

#######################################################################
# Functions
#######################################################################

sub get_distribution($){
	my $file=shift || "$releasefile";
	open(RELEASE,"<$file") || warn ("Could not open $file\n");
	while (<RELEASE>){
    	if (/^SUSE Linux Enterprise/){
	        $dist="SLE";
	    } 
	    if (/^VERSION/){
	        ( $release ) = $_ =~ m/VERSION = (.*)/;
	    }
	    if (/^PATCHLEVEL/){
	        ( $patchlevel ) = $_ =~ m/PATCHLEVEL = (.*)/;
	    }
	}
	close(RELEASE);
	print STDERR "INFO: $dist,$release,$patchlevel\n" if ($DEBUG);
	return($dist,$release,$patchlevel);
}

sub print_usage () {
    print "This plugin checks for software updates on systems that use package\n";
    print "management systems based on the zypper command found in openSUSE.\n\n";
    print "It checks for security, recommended and optional patches and also for\n";
    print "optional package updates.\n\n";
    print "You can define the status by patch category. Use a commata to list more\n";
    print "than one category to a state.\n\n";
    print "If you like to know the names of available patches and packages, use\n";
    print "the \"-v\" option.\n\n";
    print "Usage:\n";
    print "  $PROGNAME [-w <category>] [-c <category>] [-t <timeout>] [-v]\n";
    print "  $PROGNAME [-h | --help]\n";
    print "  $PROGNAME [-V | --version]\n";
    print "\n\nOptions:\n";
    print "  -w, --warning\n";
    print "      A patch with this category result in warning status.\n"; 
    print "      Default: $opt_w\n";
    print "  -c, --critical\n";
    print "      A patch with this category result in critical status.\n";
    print "      Default: $opt_c\n";
    print "  -r, --releasefile\n";
    print "      Use the given file to get informations about the distribution.\n";
    print "      Default: $releasefile\n";
    print "  -s, --use_sudo\n";
    print "      Zypper needs root privileges on some distributions (known: 10.1 and SLE10).\n";
    print "      You can enable the script to use $sudo to start zypper.\n";
    print "      But don't forget to enable nopasswd sudo for the user starting $PROGNAME\n";
    print "      Via lines like the two below on in /etc/sudoers:\n";
    print "          nagios ALL = NOPASSWD: /usr/bin/zypper sl, \\ \n";
    print "                       /usr/bin/zypper $zypperopt\n";
    print "  -t, --timeout\n";
    print "      Just in case of problems, let's not hang Nagios and define a timeout.\n";
    print "      Default value is: $opt_t seconds\n";
    print "  -v, --verbose_output\n";
    print "      Print more information (useful only with Nagios v3.x).\n";
    print "\n";
    print "  -h, --help\n";
    print "      Print detailed help screen\n";
    print "  -V, --version\n";
    print "      Print version information\n";
}

sub print_help () {
    print "Copyright (c) 2008, Novell, Inc.\n\n";
    print_usage();
    print "\n";
    print "  <help>  This output\n";
    print "\n";
    support();
}

sub check_zypper(){
	if ( -x "$zypper" ){
		print STDERR "Trying: $use_sudo $zypper sl 2>/dev/null 1>&2\n" if ( $DEBUG );
		return (system("$use_sudo $zypper sl 2>/dev/null 1>&2"));
	} else {
		return 1;
	}
}

sub check_errorcode($){
    my $status=shift || "";
    return "CRITICAL" if ("$opt_c" =~ "$status");
    return "WARNING"  if ("$opt_w" =~ "$status");
    return "OK";
}

sub check(){
    my ($status,$ret_str,$error);
    my $secstr="";
    my $recstr="";
    my $optstr="";
    my $pacstr="";
    my $seccount=0;
    my $reccount=0;
    my $optcount=0;
    my $paccount=0;
    my $update_avail=0;
    my %packagelist;
    $error="UNKNOWN";
    $ret_str="$error - Unknown state\n";
    print STDERR "Trying: $use_sudo $zypper $zypperopt\n" if ($DEBUG);
    if (open (FH, "$use_sudo $zypper $zypperopt 2>&1 |")) {
        while(<FH>){
            chomp; 
            my $category="unknown";
            print STDERR "LINE: $_\n" if ($DEBUG);
            # error handling
            return("UNKNOWN: $_","UNKNOWN") if (/not found on medium/);
            return("UNKNOWN: $_","UNKNOWN") if (/I\/O error: Can't provide/);
            return("UNKNOWN: $_","UNKNOWN") if (/Error message:/);
            return("UNKNOWN: $_","UNKNOWN") if (/A ZYpp transaction is already in progress./);
            if (( "$release" eq "10.2" ) || ("$dist" eq "SLE")){
                if (/\|\s*optional\s*\|\s*Needed/) {
                    $category="optional";
                    $optcount++;
                }
                if (/\|\s*recommended\s*\|\s*Needed/){
                    $category="recommended";
                    $reccount++;
                }
                if (/\|\s*security\s*\|\s*Needed/){
                    $category="security";
                    $seccount++;
                }
                my ($url,$name,$version,$category,$status)=split('\s*\|\s*',$_,5); # just for reference - perhaps we need the variables later
                $packagelist{"$name"}="$category" if defined($category);
            } else {
                if (/<update /){ 
                    if (/kind="patch"/){	# line contains patch
                        if (/category="security"/){
                            $seccount++;
                            $category="security";
                        }
                        if (/category="recommended"/){
                            $reccount++;
                            $category="recommended";
                        }
                        if (/category="optional"/){
                            $optcount++;
                            $category="optional";
                        }
                        my ($name)=$_=~/name="(.*?)"/;
                        $packagelist{"$name"}="$category";
                    } elsif (/kind="package"/) {
                        $paccount++;
                        my ($name)=$_=~/name="(.*?)"/;
                        $packagelist{'package'}.="$name ";
                        $category="package";
                    }	
                }
            }
        }
        if ($DEBUG){
            print STDERR "Packages    (paccount): $paccount\n";
            print STDERR "Optional    (optcount): $optcount\n";
            print STDERR "Recommended (reccount): $reccount\n";
            print STDERR "Security    (seccount): $seccount\n";
            use Data::Dumper;
            print STDERR Data::Dumper->Dump([\%packagelist]);
        }
        if ("$paccount" ne "0"){
            $update_avail=1;
            $error="OK";
            $pacstr="$paccount package update(s);";
        }
        if ("$optcount" ne "0"){
            $update_avail=1;
            $error=check_errorcode("optional");
            $optstr="$optcount optinal update(s);";
        }
        if ("$reccount" ne "0"){
            $update_avail=1;
            $error=check_errorcode("recommended");
            $recstr="$reccount recommended update(s);";
        }
        if ("$seccount" ne "0"){
            $update_avail=1;
            $error=check_errorcode("security");
            $secstr="$seccount security update(s);";
        }
        if ( $update_avail ){
            $ret_str="$error : $secstr $recstr $optstr $pacstr\n";
            if ( $opt_v ){
                $ret_str.="Patches:\n" if ($error ne "OK");
                foreach my $cat ('security','recommended','optional'){
                    foreach my $key (sort(keys %packagelist)){
                       if ( $packagelist{$key} eq "$cat"){
                           $ret_str.="$cat: ".$key."\n";
                       }
                    }
                }
                if ( "paccount" ne "0" ){
                    $ret_str.="Packages:\n".$packagelist{'package'}."\n" if $packagelist{'package'};
                }
            }
        } else {
            $error="OK";
            $ret_str="OK: no updates available\n";
        }
    }
    close(FH);
	return("$ret_str","$error");
}

#######################################################################
# Main
#######################################################################

Getopt::Long::Configure('bundling');
GetOptions(
           "V"   => \$opt_V, "version"        => \$opt_V,
           "h"   => \$opt_h, "help"           => \$opt_h,
           "H=s" => \$opt_H, "hostname"	      => \$opt_H,
           "w=s" => \$opt_w, "warning=s"      => \$opt_w,
           "c=s" => \$opt_c, "critical=s"     => \$opt_c,
           "r=s" => \$opt_r, "releasefile=s"  => \$opt_r,
           "t=i" => \$opt_t, "timeout=i"      => \$opt_t,
           "v"   => \$opt_v, "verbose_output" => \$opt_v,
           "s"   => \$opt_s, "use_sudo"       => \$opt_s);

if ($opt_t) {
    $TIMEOUT=$opt_t;
}

# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
    print "UNKNOWN - Plugin timed out\n";
    exit $ERRORS{"UNKNOWN"};
};

alarm($TIMEOUT);

if ($opt_V) {
    print_revision($PROGNAME,"$VERSION");
    exit $ERRORS{'OK'};
}

if (! $opt_H) {
    $opt_H=$ENV{'HOSTNAME'};
}

($dist,$release,$patchlevel)=get_distribution("$opt_r");

if ("$release" eq "10.2"){
    $zypperopt="--non-interactive --no-gpg-checks list-updates";
}

if ("$dist" eq "SLE"){
    if (("$release" eq "10") && ($patchlevel gt 0)){
        $zypperopt="--non-interactive --no-gpg-checks --terse list-updates";
    } else {
        print "UNKNOWN - SLE $release is currently not supported\n";
        exit $ERRORS{"UNKNOWN"};
    }
}

$use_sudo.="$sudo" if ($opt_s);

if ($opt_h) {
    print_help();
    exit $ERRORS{'OK'};
}

if ( check_zypper() ){
	print "UNKNOWN - system doesn't allow to execute zypper\n";
    exit $ERRORS{"UNKNOWN"};
} else {
	my ($ret_str,$error)=check();
    print "Updates $ret_str";
    exit $ERRORS{"$error"};
}

openSUSE Build Service is sponsored by