#!/usr/bin/perl

my $path_to_leasefile = "/var/state/dhcp/dhcpd.leases";

use strict;
use Net::Ping;
#To convert the UTC times to seconds since the epoch
use Time::Local;
#To format the output time
use POSIX ("strftime");
use Term::ANSIColor;
use Getopt::Long (":config", "bundling");
use Term::ANSIColor (":constants");
$Term::ANSIColor::AUTORESET = 1;

my ($showmac,$showatm,$showip,$showexpired,$help);
GetOptions('mac|m' => \$showmac,'atm|a' => \$showatm,'ip|i=s' => \$showip,'expired|x' => \$showexpired,'help|h'=>\$help);

if ($help) { die(&usage()); }

my @list;

open (INFILE,$path_to_leasefile); 

my %hash;
my ($count,$expired_lease);
$expired_lease=0;
while (<INFILE>) {
	if ($_ =~ /lease (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/i) {
		
		my $ip = $1;
		my $hostname = undef;
		my $remoteid = undef;
		my $macaddr = undef;
		my $lease_start = undef;
		my $lease_end = undef;
		# Go until you see a } which is the end of record char
		while ($_ !~ /}/) {
			$_ = <INFILE>;
			if ($_ =~ /starts/) {
				$lease_start = &leasegm_to_epoch($_);
			}
			elsif ($_ =~ /ends/) {
				$lease_end = &leasegm_to_epoch($_);
			}
			elsif ($_ =~ /client-hostname \"(.*)\"/ ) {
				$hostname = $1;
			}
			elsif ($_ =~ /option agent\.remote-id (.*);/ ) {
				$remoteid = $1;
			}
			elsif ($_ =~ /hardware ethernet (.*);/ ) {

				$macaddr = $1;
			}
		}

		my $expired = &lease_expired($lease_end);

		#If we're not searching for ONE IP and the lease isn't expired add it to the hash
		if (!$showip && !$expired) {
			# Put it in the hash no matter what, if showip isn't set because it will overwrite
			$hash{$ip}={"hostname"=>$hostname,"remoteid"=>$remoteid,"mac"=>$macaddr,"lease_end"=>$lease_end};	
			}
		elsif ($showip && $ip =~ /$showip/ && !$expired) {
			# Only populate the hash if it matches the passed in request
			$hash{$ip}={"hostname"=>$hostname,"remoteid"=>$remoteid,"mac"=>$macaddr,"lease_end"=>$lease_end};
		}
		elsif ($expired) { 
			#if ($showexpired) {
			#	my $ctime = strftime("%m-%d-%Y %I:%M%p",localtime($lease_end));
			#	print "Expired: $ip\t($ctime)\n"; 
			#}
			$expired_lease++;
		}
	
		$count++;
		}
}

close INFILE;

if ($showip) { 
	print "Showing IPs that match \"$showip\"\n";
	}

@list = sort(keys %hash);
my $total = scalar(@list) + 1;  

my $maxlen;
#get the length of the longest IP
for my $ip(@list) {
	if ($maxlen < length($ip)) { $maxlen = length($ip); }	
}

my $output;
#$output .= "Content-Type: text/html\n\n";
#$output .= "Checking $total ($count dupes) leases for validity\n";
print "Checking $total leases ($expired_lease expired) for validity\n";

my $ping = Net::Ping->new("icmp");
my $count=0;

foreach my $ip (@list) {
	my $result = $ping->ping($ip,1);
	if ($result) { 
		$result = GREEN "Alive";
		$count++;
		}
	else { $result = RED "Dead"; }
	
	# Get the hostname part	
	my $hostname;
	$hostname = $hash{$ip}->{'hostname'} or $hostname = BOLD BLUE "*blank*";
	
	my $lease_end;
	if ($showexpired) {	
		if (strftime("%Y",localtime($hash{$ip}->{lease_end})) > 2020) { 
			$lease_end = "Never"; 
			$lease_end = padtext($lease_end,20);
		}
		else {
			$lease_end = strftime("%m-%d-%Y %I:%M%p",localtime($hash{$ip}->{lease_end}));
			$lease_end = padtext($lease_end,20);
		}
	}
	else { $lease_end = ""; }

	# Get the agentid
	my $remoteid;
	$remoteid = $hash{$ip}->{'remoteid'} or $remoteid = "none";	

	my $mac;
	if ($showmac) { $mac = $hash{$ip}->{'mac'} or $mac = ""; }
	my $atm;
	if ($showatm) { $atm = $hash{$ip}->{'remoteid'} or $atm = ""; }
	if ($showatm) { $atm = &getoption82($atm); }

	$ip = padtext($ip,$maxlen + 2);
	$mac = padtext($mac,19);
	$result = padtext($result,16);
	$atm = padtext($atm,5);
	$hostname = padtext("($hostname)",20);
	
	my $outline = "$ip$result$mac$atm$lease_end$hostname\n";	
	print $outline;
}

my $percent;
if (!$total == 0) {
	$percent = sprintf("%2.f%%", ($count/$total) * 100);
}
else {
	$percent = "100%";
}
print "$count active leases ($percent)\n";
#$output .= "$count active leases ($percent)\n";
print $output;

sub getoption82 () {
	my $data = shift;
	if (!$data) { return -1; }

	my @list = split(":",$data);
	my $vpi = hex($list[9]);
	my $vci = (hex($list[10]) * 16) + hex($list[11]);
	return "$vpi-$vci";
}

sub padtext() {
	my $str = shift;
	my $len = shift;
	if (!$str || !$len) { return $str; } 

	$str = sprintf("%-${len}s",$str);
	return $str;
}

sub leasegm_to_epoch() {
	my ($sec,$min,$hours,$mday,$mon,$year);	

	if (my @list = $_[0] =~ /(\w+)\s+(\d+)\s+(\d{4})\/(\d{1,2})\/(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})/) {
		$sec = $list[7];
   	$min = $list[6];
   	$hours = $list[5];
  		$mday = $list[4];
   	$mon = $list[3] - 1;
   	$year = $list[2] - 1900;
	}
	elsif (my @list = $_[0] =~ /ends never/) {
		$sec = 1;
		$min = 1;
		$hours = 1;
		$mday = 1;
		$mon = 1;
		$year = 132;
	}
	else { die("Whoa that aint good!\n"); } 

	#print "$sec,$min,$hours,$mday,$mon,$year\n";
	my $time_string = timegm($sec,$min,$hours,$mday,$mon,$year);

	return $time_string;
}

sub lease_expired() {
	my $lease_time = shift;
	if (!$lease_time) { return undef; }

	my $time_now = time();

	if ($lease_time < $time_now) {
		return 1;
	}
	else {
		return 0;
	}
}

sub usage() {
	my $output .= "$0 
	-x  --expired		show lease expiration times
	-m  --mac		show lease MAC address
	-a  --atm 		show lease ATM (Option 82) information
	-i  --IP=1.2.3.4	filter for ip 1.2.3.4 (regexp)
";
}


syntax highlighted by Code2HTML, v. 0.9