#!/usr/bin/env perl
#
# SSH Expect Check Plugin
#
# Copyright (c) 2010 Nagios Enterprises, LLC.  All rights reserved.
#
# Written by Mike Mestnik
#
# LICENSE:
#
# Except where explicitly superseded by other restrictions or licenses, permission
# is hereby granted to the end user of the software to use, modify and create
# derivative works or the software under the terms of the Nagios Software License, 
# which can be found online at:
#
# http://www.nagios.com/legal/licenses/
#
# CONTRIBUTION POLICY:
#
# (The following paragraph is not intended to limit the rights granted
# to you to modify and distribute this software under the terms of
# other licenses that may apply to the software.)
#
# Contributions to this software are subject to your understanding and acceptance of
# the terms and conditions of the Nagios Contributor Agreement, which can be found 
# online at:
#
# http://www.nagios.com/legal/contributoragreement/
#
#
# DISCLAIMER:
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
# HOLDERS BE LIABLE FOR ANY CLAIM FOR 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) OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, STRICT LIABILITY, TORT (INCLUDING 
# NEGLIGENCE OR OTHERWISE) OR OTHER ACTION, ARISING FROM, OUT OF OR IN CONNECTION 
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# $Id: check_ssh_expect.pl 302 2010-06-01 16:47:21Z mmestnik $

use Getopt::Std;
use Net::SSH::Expect;
use Switch;
use integer;
use strict;

our($opt_H, $opt_U, $opt_P, $opt_w, $opt_c, $opt_s, $opt_r
	$opt_e, $opt_d);
getopt('H:U:P:w:c:s:r:e:d:');

if ($opt_s !~ /^([0-9]+)$/ ) { $opt_s = 6; }
if ($opt_r !~ /^([0-9]+)$/ ) { $opt_r = 2; }
defined $opt_e or $opt_e = 'Welcome';
defined $opt_d or $opt_d = 'ls -l /;who';

my $sep=':';
my ($wl,$wh,$cl,$ch);
if ($opt_w =~ /^([0-9]*)$sep?([0-9]*)$/ ) {
	($wl,$wh) = ($1,$2);
	}

if ($opt_c =~ /^([0-9]*)$sep?([0-9]*)$/ ) {
	($cl,$ch) = ($1,$2);
	}

my $optbitfld = bitshift(($wl!=''),0)+bitshift(($cl!=''),1)+
		bitshift(($wh!=''),2)+bitshift(($ch!=''),3);
switch($optbitfld){
	case (0) {
		# None of the values set.
		last;
		}
	case (1) {
		# Just wl.
		last;
		}
	case (2) {
		# Just wh.
		last;
		}
	case (4) {
		# Just cl.
		last;
		}
	case (8) {
		# Just ch.
		last;
		}
	case (3) {
		# Just wl&wh.
		($wl>=$wh) && die 'Warning thresholds equil or backwards.';
		last;
		}
	case (12) {
		# Just cl&ch.
		($cl>=$ch) && die 'Critical thresholds equil or backwards.';
		last;
		}
	case (5) {
		# wl&cl
		($wl<=$cl) && die 'Warning Low must be higher then Critical Low.';
		last;
		}
	case (6) {
		# wh&cl.
		($wh<=$cl) && die 'High not greater then Low.';
		last;
		}
	case (7) {
		# wl&wh&cl.
		($wl<=$cl) && die 'Warning Low must be higher then Critical Low.';
		($wl>=$wh) && die 'Warning thresholds equil or backwards.';
		last;
		}
	case (9) {
		# wl&ch
		($wl>=$ch) && die 'High not greater then Low.';
		last;
		}
	case (10) {
		# wh&ch.
		($wh>=$ch) && die 'Warning High must be less then Critical High.';
		last;
		}
	case (11) {
		# wl&wh&ch.
		($wh>=$ch) && die 'Warning High must be less then Critical High.';
		($wl>=$wh) && die 'Warning thresholds equil or backwards.';
		last;
		}
	case (13) {
		# wl&cl&ch
		($wl<=$cl) && die 'Warning Low must be higher then Critical Low.';
		($wl>=$ch) && die 'High not greater then Low.';
		last;
		}
	case (14) {
		# wh&cl&ch.
		($wh>=$ch) && die 'Warning High must be less then Critical High.';
		($wh<=$cl) && die 'High not greater then Low.';
		last;
		}
	case (15) {
		# wl&wh&cl&ch.
		($wl<=$cl) && die 'Warning Low must be higher then Critical Low.';
		($wl>=$wh) && die 'Warning thresholds equil or backwards.';
		($wh>=$ch) && die 'Warning High must be less then Critical High.';
		last;
		}
	else { die "Can't parse Warning/Critical thresholds." }
	};

my $alrmfault = 0;

sub setnextalm {
	my $tdelta=time() - $main::tstart;
	switch($optbitfld){
		case (0) {
			# None of the values set.
			last;
			}
		case (1) {
			# Just wl.
			if ($wl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wl - $tdelta); $alrmfault=1;
				} else { alarm 0; $alrmfault=0; };
			last;
			}
		case (2) {
			# Just wh.
			if ($wh > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wh - $tdelta); $alrmfault=0;
				} else { alarm 0; $alrmfault=1; };
			last;
			}
		case (4) {
			# Just cl.
			if ($wl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wl - $tdelta); $alrmfault=2;
				} else { alarm 0; $alrmfault=0; };
			last;
			}
		case (8) {
			# Just ch.
			if ($wh > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wh - $tdelta); $alrmfault=0;
				} else { alarm 0; $alrmfault=1; };
			last;
			}
		case (3) {
			# Just wl&wh.
			if ($wl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wl - $tdelta); $alrmfault=1;
				} elsif ($wh > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wh - $tdelta); $alrmfault=0;
				} else { alarm 0; $alrmfault=1; };
			last;
			}
		case (12) {
			# Just cl&ch.
			if ($cl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($cl - $tdelta); $alrmfault=2;
				} elsif ($ch > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($ch - $tdelta); $alrmfault=0;
				} else { alarm 0; $alrmfault=2; };
			last;
			}
		case (5) {
			# wl<=cl
			if ($cl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($cl - $tdelta); $alrmfault=2;
				} elsif ($wl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wl - $tdelta); $alrmfault=1;
				} else { alarm 0; $alrmfault=0; };
			last;
			}
		case (6) {
			# wh<=cl
			if ($cl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($cl - $tdelta); $alrmfault=2;
				} elsif ($wh > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wh - $tdelta); $alrmfault=0;
				} else { alarm 0; $alrmfault=1; };
			last;
			}
		case (7) {
			# wl&wh&cl.
			if ($cl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($cl - $tdelta); $alrmfault=2;
				} elsif ($wl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wl - $tdelta); $alrmfault=1;
				} elsif ($wh > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wh - $tdelta); $alrmfault=0;
				} else { alarm 0; $alrmfault=1; };
			last;
			}
		case (9) {
			# wl&ch
			if ($wl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wl - $tdelta); $alrmfault=1;
				} elsif ($ch > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($ch - $tdelta); $alrmfault=0;
				} else { alarm 0; $alrmfault=2; };
			last;
			}
		case (10) {
			# wh&ch.
			if ($wh > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wh - $tdelta); $alrmfault=0;
				} elsif ($ch > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($ch - $tdelta); $alrmfault=1;
				} else { alarm 0; $alrmfault=2; };
			last;
			}
		case (11) {
			# wl&wh&ch.
			if ($wl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wl - $tdelta); $alrmfault=1;
				} elsif ($wh > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wh - $tdelta); $alrmfault=0;
				} elsif ($ch > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($ch - $tdelta); $alrmfault=1;
				} else { alarm 0; $alrmfault=2; };
			last;
			}
		case (13) {
			# wl&cl&ch
			if ($cl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($cl - $tdelta); $alrmfault=2;
				} elsif ($wl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wl - $tdelta); $alrmfault=1;
				} elsif ($ch > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($ch - $tdelta); $alrmfault=0;
				} else { alarm 0; $alrmfault=2; };
			last;
			}
		case (14) {
			# wh&cl&ch.
			if ($cl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($cl - $tdelta); $alrmfault=2;
				} elsif ($wh > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wh - $tdelta); $alrmfault=0;
				} elsif ($ch > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($ch - $tdelta); $alrmfault=1;
				} else { alarm 0; $alrmfault=2; };
			last;
			}
		case (15) {
			# wl&wh&cl&ch.
			if ($cl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($cl - $tdelta); $alrmfault=2;
				} elsif ($wl > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wl - $tdelta); $alrmfault=1;
				} elsif ($wh > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($wh - $tdelta); $alrmfault=0;
				} elsif ($ch > $tdelta){
				$SIG{ALRM} = setnextalm();
				alarm($ch - $tdelta); $alrmfault=1;
				} else { alarm 0; $alrmfault=2; };
			last;
			}
		else { die "Can't parse Warning/Critical thresholds." }
		}
	}

my $ssh = Net::SSH::Expect->new (
	host => $opt_H, 
	user => $opt_U, 
	password=> $opt_P, 
#	exp_internal => 1,
#	raw_pty => 1
	);
$ssh->timeout($opt_s);

my $tstart=time();
setnextalm();

my $login_output = $ssh->login();
$ssh->timeout($opt_r);
if ($login_output !~ /$opt_e/) {
	my $tdelta = time() - $tstart;
	print "CRITICAL: $tdelta Login has failed. Login output was:\n$login_output\n"; exit(2);
	}
my $fprom = $ssh->read_all();
($fprom =~ /$\s*\z/) or die "Where's the remote prompt?";
# we have completed the timed portion of our test.
alarm(0); my $tdelta = time() - $tstart;
# print($login_output . $fprom);
###$ssh->exec('stty raw -echo');
forech (split(';', $opt_d)) {
	$ssh->exec($_);
}

$ssh->close();
my $grahpable=$tdelta*100;
switch($alrmfault){
	case 0 {
		print "OK: Login completed in $tdelta seconds.|wait=${grahpable}ms;\n";
		last;
		}
	case 1 {
		print "WARNING: Login completed in $tdelta seconds.|wait=${grahpable}ms;\n";
		last;
		}
	case 2 {
		print "CRITICAL: Login completed in $tdelta seconds.|wait=${grahpable}ms;\n";
		last;
		}
	else {
		print "UNKNOWN: Timed portion of test took $tdelta seconds.|wait=${grahpable}ms;\n";
		$alrmfault = 3;
		last;
		}
	}
exit($alrmfault);
sub bitshift { $_[0] << $_[1]; }

