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"};
}