#! /usr/bin/perl -w
#
# check_nt_service - nagios plugin
#
# Copyright (C) 2008-2015 Guenther Mair,
# Derived from check_ifoperstatus by Christoph Kron.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
#
# Report bugs to:  guenther.mair@hoslo.ch
#
# $Id: e30a1fd799eae988e27df352b26d0306464d3695 $
#

my $nagioslibexec;

BEGIN {
  my @lib_folders = qw(/usr/local/nagios/libexec/ /usr/lib/nagios/plugins/ /usr/lib64/nagios/plugins/);
  foreach my $lib (@lib_folders) {
    if (-d $lib) {
      $nagioslibexec = $lib;
      last;
    }
  }
}

use POSIX;
use strict;

use lib $nagioslibexec;
use utils qw($TIMEOUT %ERRORS &print_revision &support);

use Net::SNMP;
use Getopt::Long;
&Getopt::Long::config('bundling');

## function prototypes

sub print_help();
sub usage();
sub process_arguments();

## module-specific variables

my $PROGNAME = "check_nt_service";
my $REVISION = '$Id: e30a1fd799eae988e27df352b26d0306464d3695 $';
my $debug;
my $state  = 'UNKNOWN';
my $answer = "";
my $part;
my @parts;
my $list = undef;
my $snmpvalue;
my $service;
my $current;
my $size;

## variables for argument handling

my $opt_h;
my $opt_V;
my $status;

## snmp specific variables

my $timeout;
my $hostname;
my $session;
my $error;
my $response;
my $key;
my $community    = "public";
my $snmp_version = 1;
my $maxmsgsize   = 1472;       # Net::SNMP default is 1472
my ($seclevel, $authproto, $secname, $authpass, $privpass, $auth_method, $auth_token, $priv_method, $priv_token);
my $context = "";
my $port    = 161;

## snmp information

my %serviceStatus = ('1', 'active', '2', 'continue-pending', '3', 'pause-pending', '4', 'paused');

my $svSvcOperatingState = '1.3.6.1.4.1.77.1.2.3.1.3';

## validate arguments

$status = process_arguments();

## just in case of problems, let's avoid blocking the calling process for too long

$SIG{'ALRM'} = sub {
  printf("ERROR: No snmp response from %s (alarm)\n", $hostname);
  exit $ERRORS{"UNKNOWN"};
};

alarm($timeout);

## main function

$snmpvalue = fetch_services();

if ($list) {
  $state = 'OK';
} else {
  if (!defined $snmpvalue) {
    $state = 'CRITICAL';
    printf("%s: Could not match service '%s' on host '%s'\n", $state, $service, $hostname);
  } else {
    $state = ($snmpvalue == 1) ? 'OK' : 'CRITICAL';
    printf("%s: service is %s\n", $state, $serviceStatus{$snmpvalue});
  }
}

## terminate with exit code
exit $ERRORS{$state};

### subroutines

sub fetch_services {

  # retrieve service state table
  if (!defined($response = $session->get_table($svSvcOperatingState))) {
    $answer = $session->error;
    $state  = 'UNKNOWN';
    printf("%s: SNMP error with snmp version %s (%s)\n", $state, $snmp_version, $answer);
    exit $ERRORS{$state};
  }

  # close session
  $session->close;

  # iterate through table
  foreach $key (keys %{$response}) {

    # build "current" service name out of numeric OID rappresentation (accept only alpha-numerics + spaces; trim afterwards)
    $current = "";
    @parts   = split(/\./, substr($key, length($svSvcOperatingState) + 1));
    $size    = shift @parts;
    foreach $part (@parts) {
      $current .= chr($part);
    }
    $current = trim($current);
    printf("%s has %s length of string (should be %s and is %s): ", $key, ($size == length($current) ? "correct" : "WRONG"), $size, length($current)) if (defined $debug);

    # either print element or compare it with the "service" (command line parameter)
    if ($list) {
      printf("%s\n", $current);
    } else {
      printf("comparing %s with %s (state %s)\n", $current, $service, $response->{$key}) if (defined $debug);
      $snmpvalue = $response->{$key} if ($current eq $service);
    }
  }

  return $snmpvalue;
}

sub ltrim { my $s = shift; $s =~ s/^\s+//;       return $s }
sub rtrim { my $s = shift; $s =~ s/\s+$//;       return $s }
sub trim  { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s }

sub usage() {
  printf("\nMissing arguments!\n");
  printf("\nUse the '-s|--service' parameter to provide a valid service name or use '-l' to get a list of available services on a host:\n\n");
  printf("%s -H <HOSTNAME> -l|-s <SERVICE> [-C <community>]\n", $PROGNAME);
  printf("Copyright (C) 2008-2015 Guenther Mair\n\n");
  exit $ERRORS{"UNKNOWN"};
}

sub print_help() {
  printf("%s plugin for Nagios\n", $PROGNAME);
  printf("\nModule specific parameters:\n");
  printf("   -d (--debug)      debug / verbose mode (print more details)\n");
  printf("   -l (--list)       list available service names\n");
  printf("   -s (--service)    SNMP service name\n");
  printf("\nSNMP parameters:\n");
  printf("   -H (--hostname)   hostname to query - (required)\n");
  printf("   -C (--community)  SNMP read community (defaults to public,\n");
  printf("                     used with SNMP v1 and v2c)\n");
  printf("   -v (--snmp_version)  1 for SNMP v1 (default)\n");
  printf("                        2 for SNMP v2c\n");
  printf("                        SNMP v2c will use get_bulk for less overhead\n");
  printf("   -L (--seclevel)   choice of \"noAuthNoPriv\", \"authNoPriv\", or	\"authPriv\"\n");
  printf("   -U (--secname)    username for SNMPv3 context\n");
  printf("   -A (--authpass)   authentication password (cleartext ascii or localized key\n");
  printf("                     in hex with 0x prefix generated by using	\"snmpkey\" utility\n");
  printf("                     auth password and authEngineID\n");
  printf("   -a (--authproto)  authentication protocol ( MD5 or SHA1)\n");
  printf("   -X (--privpass)   privacy password (cleartext ascii or localized key\n");
  printf("                     in hex with 0x prefix generated by using	\"snmpkey\" utility\n");
  printf("                     privacy password and authEngineID\n");
  printf("   -p (--port)       SNMP port (default 161)\n");
  printf("   -M (--maxmsgsize) max message size - usefull only for v1 or v2c\n");
  printf("   -t (--timeout)    seconds before the plugin times out (default=%s)\n", $TIMEOUT);
  printf("   -V (--version)    plugin version\n");
  printf("   -h (--help)       show this usage screen\n\n");
  printf(" -s or -l must be specified\n\n");
  print_revision($PROGNAME, '$Id: e30a1fd799eae988e27df352b26d0306464d3695 $');
}

sub process_arguments() {
  $status = GetOptions(
    "V"              => \$opt_V,
    "version"        => \$opt_V,
    "h"              => \$opt_h,
    "help"           => \$opt_h,
    "d"              => \$debug,
    "debug"          => \$debug,
    "l"              => \$list,
    "list"           => \$list,
    "s=s"            => \$service,
    "service=s"      => \$service,
    "v=i"            => \$snmp_version,
    "snmp_version=i" => \$snmp_version,
    "C=s"            => \$community,
    "community=s"    => \$community,
    "L=s"            => \$seclevel,
    "seclevel=s"     => \$seclevel,
    "a=s"            => \$authproto,
    "authproto=s"    => \$authproto,
    "U=s"            => \$secname,
    "secname=s"      => \$secname,
    "A=s"            => \$authpass,
    "authpass=s"     => \$authpass,
    "X=s"            => \$privpass,
    "privpass=s"     => \$privpass,
    "p=i"            => \$port,
    "port=i"         => \$port,
    "H=s"            => \$hostname,
    "hostname=s"     => \$hostname,
    "M=i"            => \$maxmsgsize,
    "maxmsgsize=i"   => \$maxmsgsize,
    "t=i"            => \$timeout,
    "timeout=i"      => \$timeout,
  );

  if ($status == 0) {
    print_help();
    exit $ERRORS{'OK'};
  }

  if ($opt_V) {
    print_revision($PROGNAME, '$Id: e30a1fd799eae988e27df352b26d0306464d3695 $ ');
    exit $ERRORS{'OK'};
  }

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

  if (!defined $list && !defined $service) {
    usage();
    exit $ERRORS{"UNKNOWN"};
  }

  if (!utils::is_hostname($hostname)) {
    usage();
    exit $ERRORS{"UNKNOWN"};
  }

  unless (defined $timeout) {
    $timeout = $TIMEOUT;
  }

  if ($snmp_version =~ /3/) {

    # Must define a security level even though default is noAuthNoPriv
    # v3 requires a security username
    if (defined $seclevel && defined $secname) {

      # Must define a security level even though default is noAuthNoPriv
      unless (grep /^$seclevel$/, qw(noAuthNoPriv authNoPriv authPriv)) {
        usage();
        exit $ERRORS{"UNKNOWN"};
      }

      # Authentication wanted
      if ($seclevel eq 'authNoPriv' || $seclevel eq 'authPriv') {
        unless ($authproto eq 'MD5' || $authproto eq 'SHA1') {
          usage();
          exit $ERRORS{"UNKNOWN"};
        }

        if (!defined $authpass) {
          usage();
          exit $ERRORS{"UNKNOWN"};
        } else {
          if ($authpass =~ /^0x/) {
            $auth_method = "-authkey";
            $auth_token  = $authpass;
          } else {
            $auth_method = "-authpassword";
            $auth_token  = $authpass;
          }
        }
      }

      # Privacy (DES encryption) wanted
      if ($seclevel eq 'authPriv') {
        if (!defined $privpass) {
          usage();
          exit $ERRORS{"UNKNOWN"};
        } else {
          if ($authpass =~ /^0x/) {
            $priv_method = "-privkey";
            $priv_token  = $privpass;
          } else {
            $priv_method = "-privpassword";
            $priv_token  = $privpass;
          }
        }
      }
    } else {
      usage();
      exit $ERRORS{'UNKNOWN'};
    }
  }    # end snmpv3

  if ($snmp_version =~ /[12]/) {
    ($session, $error) = Net::SNMP->session(
      -hostname   => $hostname,
      -community  => $community,
      -port       => $port,
      -version    => $snmp_version,
      -maxmsgsize => $maxmsgsize
    );

    if (!defined($session)) {
      $state  = 'UNKNOWN';
      $answer = $error;
      printf("%s: %s", $state, $answer);
      exit $ERRORS{$state};
    }
  } elsif ($snmp_version =~ /3/) {
    if ($seclevel eq 'noAuthNoPriv') {
      ($session, $error) = Net::SNMP->session(
        -hostname => $hostname,
        -port     => $port,
        -domain   => $domain,
        -version  => $snmpversion,
        -username => $secname
      );
    } elsif ($seclevel eq 'authNoPriv') {
      ($session, $error) = Net::SNMP->session(
        -hostname     => $hostname,
        -port         => $port,
        -domain       => $domain,
        -version      => $snmpversion,
        -username     => $secname,
        $auth_method  => $auth_token,
        -authprotocol => $authproto
      );
    } elsif ($seclevel eq 'authPriv') {
      ($session, $error) = Net::SNMP->session(
        -hostname     => $hostname,
        -port         => $port,
        -domain       => $domain,
        -version      => $snmpversion,
        -username     => $secname,
        $auth_method  => $auth_token,
        -authprotocol => $authproto,
        $priv_method  => $priv_token,
      );
    }

    if (!defined($session)) {
      $state  = 'UNKNOWN';
      $answer = $error;
      printf("%s: %s", $state, $answer);
      exit $ERRORS{$state};
    }
  } else {
    $state = 'UNKNOWN';
    printf("%s: No support for SNMP v%s yet\n", $state, $snmp_version);
    exit $ERRORS{$state};
  }
}
