#!/usr/local/bin/perl
#
# check for available updates by running yum check-update
#
# $Header: /home/doke/work/nagios/RCS/check_yum,v 1.16 2016/05/28 07:20:54 doke Exp $


use strict;
use warnings;
use Getopt::Long;
#use Data::Dumper;

use vars qw( $verbose $help @crits @warns @unknowns @oks @ignores %important %unimportant );

$ENV{PATH} = "/usr/local/bin:/sbin:/usr/sbin:/bin:/usr/bin";


# packages that we care about 
%important = ( 
    'bash' => 1,
    'dracut' => 1,
    'dracut-config-rescue' => 1,
    'dracut-kernel' => 1,
    'dracut-network' => 1,
    'gnutls' => 1,
    'grub2' => 1,
    'grub2-tools' => 1,
    'kernel' => 1,
    'kernel-firmware' => 1,
    'kernel-headers' => 1,
    'kernel-tools' => 1,
    'kernel-tools-libs' => 1,
    'kernel-uek' => 1,
    'kernel-uek-firmware' => 1,
    'kernel-uek-headers' => 1,
    'kernel-uek-tools' => 1,
    'kernel-uek-tools-libs' => 1,
    'openldap' => 1,
    'openldap-clients' => 1,
    'openssh' => 1,
    'openssh-clients' => 1,
    'openssh-server' => 1,
    'openssl' => 1,
    'openssl-libs' => 1,
    'p11-kit' => 1,
    'pam' => 1,
    'perl' => 1,
    'php' => 1,
    'plymouth' => 1,
    'plymouth-core-libs' => 1,
    'plymouth-scripts' => 1,
    'policycoreutils' => 1,
    'procps-ng' => 1,
    'pygobject3-base' => 1,
    'python-backports' => 1,
    'python-urlgrabber' => 1,
    'rpm' => 1,
    'rpm-build-libs' => 1,
    'rpm-libs' => 1,
    'rpm-python' => 1,
    'samba' => 1,
    'shadow-utils' => 1,
    'sssd' => 1,
    'stunnel' => 1,
    'sudo' => 1,
    'systemd' => 1,
    'systemd-libs' => 1,
    'systemd-sysv' => 1,
    'udev' => 1,
    'yum' => 1,
    'yum-plugin-fastestmirror' => 1,
    'yum-rhn-plugin' => 1,
    'yum-updatesd' => 1,
    'yum-utils' => 1,
    );


# between important and unimportant is a large collection of 
# packages we havn't classified yet.  


# packages that we probably don't care about 
%unimportant = ( 
    'NetworkManager' => 1,
    'NetworkManager' => 1,
    'NetworkManager-adsl' => 1,
    'NetworkManager-bluetooth' => 1,
    'NetworkManager-glib' => 1,
    'NetworkManager-team' => 1,
    'NetworkManager-tui' => 1,
    'NetworkManager-wifi' => 1,
    'NetworkManager-wwan' => 1,
    'alsa-firmware' => 1,
    'alsa-lib' => 1,
    'alsa-utils' => 1,
    'audit' => 1,
    'audit-libs' => 1,
    'authconfig' => 1,
    'avahi' => 1,
    'avahi-autoipd' => 1,
    'avahi-libs' => 1,
    'bind-libs' => 1,
    'bind-libs-lite' => 1,
    'bind-license' => 1,
    'bind-utils' => 1,
    'binutils' => 1,
    'biosdevname' => 1,
    'btrfs-progs' => 1,
    'ca-certificates' => 1,
    'centos-logos' => 1,
    'centos-release' => 1,
    'cpp' => 1,
    'crash' => 1,
    'cronie' => 1,
    'cronie-anacron' => 1,
    'cryptsetup-libs' => 1,
    'db4' => 1,
    'db4-cxx' => 1,
    'db4-devel' => 1,
    'db4-utils' => 1,
    'dbus' => 1,
    'dbus-libs' => 1,
    'device-mapper' => 1,
    'device-mapper-libs' => 1,
    'dhclient' => 1,
    'dhcp-common' => 1,
    'dhcp-libs' => 1,
    'dnsmasq' => 1,
    'e2fsprogs' => 1,
    'e2fsprogs-libs' => 1,
    'elfutils-libelf' => 1,
    'elfutils-libs' => 1,
    'ethtool' => 1,
    'finger' => 1,
    'firefox' => 1,
    'firewalld' => 1,
    'freetype' => 1,
    'gcc' => 1,
    'glib-networking' => 1,
    'glib2' => 1,
    'glibc' => 1,
    'glibc' => 1,
    'glibc-common' => 1,
    'glibc-devel' => 1,
    'glibc-headers' => 1,
    'gmp' => 1,
    'google-chrome-stable' => 1,
    'grep' => 1,
    'grubby' => 1,
    'hwdata' => 1,
    'initscripts' => 1,
    'ipmitool' => 1,
    'iproute' => 1,
    'iprutils' => 1,
    'irqbalance' => 1,
    'iwl100-firmware' => 1,
    'iwl1000-firmware' => 1,
    'iwl105-firmware' => 1,
    'iwl135-firmware' => 1,
    'iwl2000-firmware' => 1,
    'iwl2030-firmware' => 1,
    'iwl3160-firmware' => 1,
    'iwl3945-firmware' => 1,
    'iwl4965-firmware' => 1,
    'iwl5000-firmware' => 1,
    'iwl5150-firmware' => 1,
    'iwl6000-firmware' => 1,
    'iwl6000g2a-firmware' => 1,
    'iwl6000g2b-firmware' => 1,
    'iwl6050-firmware' => 1,
    'iwl7260-firmware' => 1,
    'kbd' => 1,
    'kbd-misc' => 1,
    'kexec-tools' => 1,
    'kmod' => 1,
    'kmod-libs' => 1,
    'kpartx' => 1,
    'krb5-devel' => 1,
    'krb5-libs' => 1,
    'ksh' => 1,
    'libblkid' => 1,
    'libdrm' => 1,
    'libgcc' => 1,
    'libgcc' => 1,
    'libgcrypt' => 1,
    'libgomp' => 1,
    'libgudev1' => 1,
    'libmount' => 1,
    'libnl3' => 1,
    'libnl3-cli' => 1,
    'libsoup' => 1,
    'libss' => 1,
    'libtasn1' => 1,
    'libteam' => 1,
    'libuuid' => 1,
    'libxml2' => 1,
    'linux-firmware' => 1,
    'net-snmp' => 1,
    'net-snmp-agent-libs' => 1,
    'net-snmp-libs' => 1,
    'nettle' => 1,
    'nspr' => 1,
    'nss' => 1,
    'nss_ldap' => 1,
    'nss-softokn' => 1,
    'nss-softokn-freebl' => 1,
    'nss-softokn-freebl' => 1,
    'nss-sysinit' => 1,
    'nss-tools' => 1,
    'nss-util' => 1,
    'ntpdate' => 1,
    'numactl-libs' => 1,
    'pam-devel' => 1,
    'parted' => 1,
    'pcre' => 1,
    'pcre-devel' => 1,
    'perf' => 1,
    'perl-ExtUtils-Embed' => 1,
    'perl-ExtUtils-Install' => 1,
    'perl-Pod-Escapes' => 1,
    'perl-devel' => 1,
    'perl-libs' => 1,
    'perl-macros' => 1,
    'policycoreutils' => 1,
    'poppler' => 1,
    'poppler-glib' => 1,
    'poppler-utils' => 1,
    'procps' => 1,
    'procps-ng' => 1,
    'pygobject3-base' => 1,
    'python-backports' => 1,
    'python-chardet' => 1,
    'python-urlgrabber' => 1,
    'rpm-build-libs' => 1,
    'rsync' => 1,
    'scl-utils' => 1,
    'selinux-policy' => 1,
    'selinux-policy-devel' => 1,
    'selinux-policy-targeted' => 1,
    'setroubleshoot' => 1,
    'setroubleshoot-server' => 1,
    'setup' => 1,
    'sssd-client' => 1,
    'sysstat' => 1,
    'system-config-network' => 1,
    'system-config-network-tui' => 1,
    'teamd' => 1,
    'thunderbird' => 1,
    'tuned' => 1,
    'tzdata' => 1,
    'tzdata-java' => 1,
    'unzip' => 1,
    'util-linux' => 1,
    'up2date' => 1,
    'up2date-gnome' => 1,
    'wireshark' => 1,
    'xfsprogs' => 1,
    'xorg-x11-server-Xnest' => 1,
    'xorg-x11-server-Xorg' => 1,
    'xz' => 1,
    'xz-libs' => 1,
    'zsh' => 1,
    );


$verbose = 0;
$help = 0;

sub usage {
    my( $rc ) = @_;
    print "Usage: $0 [-vh]
    -v    verbose
    -h    help
";
    exit $rc;
    }

Getopt::Long::Configure ("bundling");
GetOptions(
    'v+' => \$verbose,
    'h' => \$help,
    );
&usage( 0 ) if ( $help );

&check_yum();

my $rc = 0;
my $sep = '';
if ( $#crits >= 0 ) {
    $rc = 2;
    print "CRITICAL ", join( ", ", @crits );
    $sep = '; ';
    }
if ( $#warns >= 0 ) {
    $rc = 1 if ( $rc == 0 );
    print $sep, "Warning ", join( ", ", @warns );
    $sep = '; ';
    }
if ( $#unknowns >= 0 ) {
    $rc = -1 if ( $rc == 0 );
    print $sep, "Unknown ", join( ", ", @unknowns );
    $sep = '; ';
    }
if ( $rc == 0 ) {
    print "Ok ", join( ", ", @oks );
    $sep = '; ';
    }
if ( $#ignores >= 0 ) {
    print $sep, "Ignoring ", join( ", ", @ignores );
    }


print "\n";
exit $rc;


##################



sub check_yum {
    my( $cmd, $pkg, $npkgs, $rc, $nimportant, $nmaybe, $nunimportant,
	$nsecurity );

    if ( -x "/usr/bin/dnf" ) {   # dandified yum
	$cmd = "/usr/bin/dnf check-update";
	}
    elsif ( -x "/usr/bin/yum" ) { 
	$cmd = "/usr/bin/yum check-update";
	}
    else {
	push @oks, "neither yum nor dnf are installed";
	return;
	}

    if ( -e '/usr/lib/yum-plugins/rhnplugin.py' 
	    || -e '/usr/share/yum-plugins/rhnplugin.py' ) { 
	$cmd = "sudo $cmd";
	}
    $verbose && print "+ $cmd\n";
    if ( ! open( pH, "$cmd 2>&1 |" ) ) { 
	push @unknowns, "can't run '$cmd': $!";
	return;
	}

    $npkgs = $nimportant = $nmaybe = $nunimportant = $nsecurity = 0;
    while ( <pH> ) { 
	chomp;
	$verbose >= 2 && print "<$_\n";
	if ( m/^\s*$/i ) { 
	    next;
	    }
	elsif ( m/^Loaded plugins/i ) { 
	    next;
	    }
	elsif ( m/^Loading mirror/i ) { 
	    next;
	    }
	elsif ( m/^ \* \w/i ) { 
	    next;
	    }
	elsif ( m/^Determining fastest mirrors/i ) { 
	    next;
	    }
	elsif ( m/^(Repodata is over .* old)/i ) { 
	    push @warns, $1;
	    next;
	    }
	elsif ( m/^Security:/i ) { 
	    push @warns, $_;
	    next;
	    }
	elsif ( m/^\s*$/i ) { 
	    next;
	    }
	elsif ( m/^(\w\S*)\.\S+\s/i ) { 
	    $pkg = $1;
	    $npkgs ++;
	    if ( $important{ $pkg } ) { 
		$verbose && print "$pkg   important\n";
		$nimportant++;
		}
	    elsif ( $unimportant{ $pkg } ) { 
		$verbose && print "$pkg   unimportant\n";
		$nunimportant++;
		}
	    else { 
		$verbose && print "$pkg   maybe\n";
		$nmaybe++;
		}
	    next;
	    }
	}
    close pH;
    $rc = $? >> 8;

    if ( $rc == 1 ) { 
	push @warns, "yum check-update had errors";
	}
    elsif ( $rc == 100 && $npkgs == 0 ) { 
	push @warns, "yum check-update exited with $rc, but no packages appear to have updates available";
	}
    elsif ( $npkgs == 0 ) {  
	push @oks, "all packages are up to date";
	}

    if ( $nimportant ) { 
	push @warns, "$nimportant important updates available";
	}
    if ( $nmaybe ) { 
	push @warns, "$nmaybe possibly important updates available";
	}
    if ( $nunimportant ) { 
	push @ignores, "$nunimportant unimportant updates available";
	}


    # check for security ones
    $cmd .= " --security";
    $verbose && print "+ $cmd\n";
    if ( ! open( pH, "$cmd 2>&1 |" ) ) { 
	push @unknowns, "can't run '$cmd': $!";
	return;
	}
    $nsecurity = 0;
    while ( <pH> ) { 
	$verbose >= 2 && print "<$_";
	if ( m/Needed (\d+) of \d+ packages, for security/i ) { 
	    $nsecurity = $1;
	    }
	}
    if ( $nsecurity > 0 ) { 
	push @crits, "$nsecurity security updates available";
	}

    }



