#!/usr/bin/perl -w
################################################################################
# Copyright (c) 2007 Thomas Dohl
#
# ping.pl: Perl ping programm.
#
# This is an easy to use ping-programm with some special features. :-)
# Try it.
#
# bugs to thomas.dohl@gmx.de
# webpage is www.Thomas-Dohl.de
#
# 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
#
################################################################################
my $VERSION="1.00";
my $LASTCHANGE="16.08.2007";
# changelog
#
#	16.08.2007 1.00	First release
#
################################################################################

use strict;	
use Net::Ping; 		# http://perldoc.perl.org/Net/Ping.html
use Term::ANSIColor;
use Getopt::Long;
$Term::ANSIColor::AUTORESET++;
$| = 1;			# don't buffer output

my $NAME="ping.pl";
my $OUTPUTCHAR='#';

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

# default values
my $timeout=1000;	# ms
my $warning=200;	# ms
my $loopinterval=1000;	# ms

# Do NOT edit unless you REALLY know what you're doing!
my $average_trimm=0.95;
my $averageNumber=2;

my $p_ok=0;
my $p_warning=0;
my $p_critical=0;

#my @sigs = keys %SIG;
#for (@sigs) { $SIG{$_} = \&catcher; }
$SIG{INT} = \&catcherINT;
$SIG{ALRM} = \&catcherALRM;
$SIG{USR1} = \&catcherUSR;

map $_=0,  my ($opts_help,$opts_verbose,$opts_version,$opts_timeout,$opts_warning,$opts_loop,$opts_quiet,$opts_summary,$opts_use_all_ips,$opts_no_color);

# param
Getopt::Long::Configure ("bundling");
GetOptions(
    'h|help|?' 		=> \$opts_help, 
    'v|verbose+' 	=> \$opts_verbose, 
    'V|version' 	=> \$opts_version,
    't|timeout=i' 	=> \$opts_timeout, 		# ms
    'w|warning=i'	=> \$opts_warning,		# ms
    'l|loopinterval=i'	=> \$opts_loop,			# ms
    'q|quiet'		=> \$opts_quiet,
    's|summary=i'	=> \$opts_summary,
    'use-all-ips'	=> \$opts_use_all_ips,
    'no-color'		=> \$opts_no_color,
    );												


if($opts_help){help();exit 0;}
if($opts_version){printversion();exit 0;}
if($opts_summary){alarm($opts_summary);}
if($opts_warning){$warning=$opts_warning;}
if($opts_timeout){$timeout=$opts_timeout;}
if($opts_verbose and $opts_quiet){print "The options verbose and quite together makes no sense!\n";exit 1;}
if($opts_loop){$loopinterval=$opts_loop;}

if(!defined $ARGV[0]){printusage();die "Hostname needet\n";}
my $host=$ARGV[0];

if(!$opts_quiet){print "Start (PID $$) ping: $host\n";}	# print start pid

$loopinterval/=1000;	# because select understand only seconds

my $ip=0;
my $ip_alt=0;
my %average;
my $ret=0;
while (1){

    # do ping
    if($opts_use_all_ips == 0 and $ip){($average{0},$ip,$ret)=ping($ip,  $timeout);}
    else			      {($average{0},$ip,$ret)=ping($host,$timeout);}		

    if($ip ne $ip_alt){
	if(!$opts_use_all_ips){
	    print "IP: $ip\n";
	    $ip_alt=$ip;
	}
    }

    # calculate average
    if($ret){
	for (my $i=1;$i<=$averageNumber;$i++){
	    if(defined $average{$i}){ $average{$i}=($average{$i}*$average_trimm)+($average{$i-1}*(1-$average_trimm)); }
	    else{$average{$i}=$average{$i-1};}
	}
    }
    
    # error (timeout)
    if($ret==0){				
	if(!$opts_quiet){
	    pe($OUTPUTCHAR);
#	    print" $host is Timeout\n";
#    	    printf(" $host is Timeout (time: %.3f ms average: %.3f ms)\n", $average{0}, $average{$averageNumber})
	}    
	$p_critical++;
    }
    # error but no timeout
    elsif($average{0}>=$timeout){			
	if(!$opts_quiet){
	    pe($OUTPUTCHAR);
	    pingoutput();
	}    
	$p_critical++;		
    }
    # warning
    elsif($average{0}>=$warning){			
	if(!$opts_quiet){
	    pw($OUTPUTCHAR);
	    pingoutput();
	}    
	$p_warning++;		
    }
    # ok
    else{					
	if(!$opts_quiet){
	    po($OUTPUTCHAR);
	    pingoutput();
	}    
	$p_ok++;
    }

    select(undef, undef, undef, $loopinterval);      # usleep
}


#
# Ping ICMP Modul
# 1=host
# 2=timeout in ms
#
sub ping{
my $host=shift;
my $timeout=shift;
$timeout/=1000;
my $p;
my $ret;
my $duration;
my $ip;

    # High precision syntax (requires Time::HiRes) 
    $p = Net::Ping->new('icmp'); 
    $p->hires(); 
    ($ret, $duration, $ip) = $p->ping($host, $timeout); 

    if(!defined $ip){
	print "Host not found\n";
	exit 1;
    }

    $duration*=1000;	# in ms
    $p->close();
    return ($duration,$ip,$ret);
}



sub po{         # print ok (green)
    if($opts_no_color){print @_;}
    else{print colored(@_,'BOLD GREEN');}
}
sub pw{         # print warning (yellow)
    if($opts_no_color){print @_;}
    else{print colored(@_,'BOLD YELLOW');}
}
sub pe{         # print error (red)
    if($opts_no_color){print @_;}
    else{print colored(@_,'BOLD RED');}
}









sub printusage{
    print "\nusage: $NAME host [-w warning in ms] [-t timeout in ms]\n";

}

sub help{
    printversion();
    print "Copyright (c) 2007 Thomas Dohl\n";
    printusage();

    print <<EOF;

options:

-w, --warning [ms]	Time in milliseconds. Color is Yellow (default: $warning ms)
-l, --loopinterval [ms]	Time between the pings (default: $loopinterval ms)
-t, --timeout [ms]	Time in milliseconds, after a packet is timeoutet (default: $timeout ms)
-q, --quiet		no output (only summary at the end is shown
-s, --summary [s]	Show the summary every x seconds
-v, --verbose		output all data
-h, -?, --help		print this help
-V, --version		print version
    --use-all-ips	use all ips of a dns-name
    --no-color		down use colors

example 1:
    $NAME www.google.de
    
    ping the address www.google.de with default values

example 2:
    $NAME www.google.de -w 60 -t 80
    
    ping the address www.google.de
    the waring time is set to 60 ms
    and the timeout is set to 80 ms
    
example 3:   
    $NAME www.google.de -s 5
    
    ping the address www.google.de
    and shows every 5 seconds the summary

by the way, you can send a kill 10 to the process to get the summery
when ever you want. 

Homepage: www.Thomas-Dohl.de
bugs to : thomas.dohl\@gmx.de

EOF
}

sub printversion{
    print "Version: $VERSION\n";
}

sub outputStatus{
    my $all=$p_ok+$p_warning+$p_critical;
    if(!$all){return;}
    my $r=(100*$p_critical)/$all;
    my $y=(100*$p_warning)/$all;
    my $g=100-$r-$y;
    print "\n";
    my $average_ms=0;
    if (defined $average{$averageNumber}){$average_ms=$average{$averageNumber}*1000;}
    $average_ms=sprintf ("%.0f",$average_ms);

write;
    
format STDOUT =
+-----------------------------+
|          Overview           |
+-----------------------------+
|Green:   @>>>% @>>>>>>>>>>>> |
$g,dot($p_ok)
|Yellow:  @>>>% @>>>>>>>>>>>> |
$y,dot($p_warning)
|Red:     @>>>% @>>>>>>>>>>>> |
$r,dot($p_critical)
|Average: @>>>>>>>>>>>>>>> ms |
dot($average_ms)
+-----------------------------+
.    
}

sub dot{
    my $number=shift;
    1 while $number =~ s/(\d+)(\d{3})/$1.$2/;
    return $number
}

sub catcher{
    my $sig = shift;
    chomp($sig);
    print "Signal empfangen: ($sig)\n";
#    exit 1;
}

sub catcherINT{
    outputStatus();
    exit 0;
}

sub catcherALRM{
    outputStatus();
    if($opts_summary){alarm($opts_summary);}
}

sub catcherUSR{
    outputStatus();
}


sub pingoutput{
    if($opts_verbose){
	printf(" $host [ip: $ip] is alive (packet return time: %.3f ms average: %.3f ms)\n", $average{0},$average{$averageNumber})
    }
}