#! /usr/bin/perl -w
#
# check_citrix_activeusers - nagios plugin
#
# Copyright (C) 2009 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: 98fae8d8f24dcb8f67c084170bf9591be02c7896 $
#

use POSIX;
use strict;
use lib "/usr/local/nagios/libexec";
use utils qw($TIMEOUT %ERRORS &print_revision &support);

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

my $PROGNAME = 'check_citrix_activeusers';
my $REVISION = '$Id: 98fae8d8f24dcb8f67c084170bf9591be02c7896 $';
sub print_help ();
sub usage ();
sub process_arguments ();
sub connect_snmp ();

my $DEBUG = 0;

my $timeout;
my $SNMP_TIMEOUT = 5;
my $SNMP_RETRIES = 2;
my $snmp_timeout;
my $snmp_retries;
my @hosts;
our $hostname;
my $session;
my $error;
my $response;
my $sub_response;
my $opt_h;
my $opt_V;
my $key;
my $lastc;
my $name;
my $status;
my $answer       = "";
my $snmpkey      = 0;
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 $port = 161;
my $warning;
my $critical;
my @tmp;
my $count      = 0;
my $sum        = 0;
my $currentOID = '';
my $state      = 'OK';

my $WFActive = '1.3.6.1.4.1.9827.1.1.1.1';

## Validate Arguments

process_arguments();

## Just in case of problems, let's not hang Nagios

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

alarm($timeout);

print $PROGNAME . ":";
print "\n" if $DEBUG;

## Main function

foreach $hostname (@hosts) {

  connect_snmp();

  if (!defined($response = $session->get_table($WFActive))) {
    $answer = $session->error;
    $session->close;
    $state = 'CRITICAL';
    printf("$state: SNMP error with snmp version $snmp_version ($answer)\n");
    $session->close;
    exit $ERRORS{$state};
  }

  print " " if not $DEBUG;
  print $hostname;
  print ":\n" if $DEBUG;

  foreach $key (keys %{$response}) {
    $count++;
    $sum += $response->{$key};

    $key =~ /.*\.(\d+)$/;
    $snmpkey = $1;
    print " " . $response->{$key} . " user(s)";
    print " (" . $key . ": " . $response->{$key} . ")\n" if $DEBUG;
  }

  print ";" if not $DEBUG;

  $session->close;
}

if ($sum > $critical) {
  $state = 'CRITICAL';
} elsif ($sum > $warning) {
  $state = 'WARNING';
} else {
  $state = 'OK';
}
printf(" total %.0f users (%s)|users=%.0f\n", $sum, $state, $sum);
exit $ERRORS{$state};

### subroutines

sub usage() {
  printf "\nMissing arguments!\n";
  printf "\n";
  printf "usage: \n";
  printf "check_citrix_activeusers -w <MAX> -c <MAX> -H <HOSTNAME>[,HOSTNAME,...] [-C <community>]\n";
  printf "Copyright (C) 2009 Guenther Mair\n";
  printf "\n\n";
  exit $ERRORS{"UNKNOWN"};
}

sub print_help() {
  printf "check_citrix_activeusers plugin for Nagios\n";
  printf "\nUsage:\n";
  printf "   -H (--hostnames)  Hostnames to query (at least one 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 "                        if monitoring with -d\n";
  printf "   -L (--seclevel)   choice of \"noAuthNoPriv\", \"authNoPriv\", or	\"authPriv\"\n";
  printf "   -U (--secname)    username for SNMPv3 context\n";
  printf "   -w (--warning)    maximum amount of users\n";
  printf "   -c (--critical)   maximum amount of users\n";
  printf "   -r (--retries)    maximum retries for each snmp connection [default=2]\n";
  printf "   -s (--snmp-timout)   timeout in seconds for each snmp connection attempt [default=5]\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=$TIMEOUT)\n";
  printf "   -V (--version)    Plugin version\n";
  printf "   -h (--help)       usage help \n\n";
  printf " -c and -w must be specified\n\n";
  print_revision($PROGNAME, '$Id: 98fae8d8f24dcb8f67c084170bf9591be02c7896 $');

}

sub process_arguments() {
  $status = GetOptions(
    "V"              => \$opt_V,
    "version"        => \$opt_V,
    "h"              => \$opt_h,
    "help"           => \$opt_h,
    "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,
    "c=s"            => \$critical,
    "critical=s"     => \$critical,
    "w=s"            => \$warning,
    "warning=s"      => \$warning,
    "p=i"            => \$port,
    "port=i"         => \$port,
    "H=s"            => \$hostname,
    "hostnames=s"    => \$hostname,
    "M=i"            => \$maxmsgsize,
    "maxmsgsize=i"   => \$maxmsgsize,
    "t=i"            => \$timeout,
    "timeout=i"      => \$timeout,
    "r=i"            => \$snmp_retries,
    "retries=i"      => \$snmp_retries,
    "s=i"            => \$snmp_timeout,
    "snmp-timeout=i" => \$snmp_timeout,
  );

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

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

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

  @hosts = split(/,/, $hostname);
  foreach $hostname (@hosts) {
    print "lookup: " . $hostname . "\n" if $DEBUG;
    if (!utils::is_hostname($hostname)) {
      usage();
      exit $ERRORS{"UNKNOWN"};
    }
  }

  unless (defined $critical) {
    printf "Maximum allowed Users (-c).\n";
    usage();
    exit $ERRORS{"UNKNOWN"};
  }

  unless (defined $warning) {
    printf "Maximum allowed Users (-w).\n";
    usage();
    exit $ERRORS{"UNKNOWN"};
  }

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

  unless (defined $snmp_timeout) {
    $snmp_timeout = $SNMP_TIMEOUT;
  }

  unless (defined $snmp_retries) {
    $snmp_retries = $SNMP_RETRIES;
  }

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

      # Context name defined or default

      unless (defined $context) {
        $context = "";
      }
    } else {
      usage();
      exit $ERRORS{'UNKNOWN'};
    }
  }    # end snmpv3 parameter verifications
}

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

    if (!defined($session)) {
      $state  = 'UNKNOWN';
      $answer = $error;
      print("$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;
      print("$state: $answer");
      exit $ERRORS{$state};
    }

  } else {
    $state = 'UNKNOWN';
    print("$state: No support for SNMP v$snmp_version yet\n");
    exit $ERRORS{$state};
  }
}
## End validation
