1
0
Fork 0
misc-linux-tools/sbin/pinger

168 lines
4.3 KiB
Perl
Executable File

#!/usr/bin/env perl
use strict;
use Getopt::Std;
use Net::Ping;
use POSIX;
use Socket;
use Switch;
use Time::HiRes qw/time/;
use Term::ANSIColor;
# force print before buffer is full
$| = 1;
# imcp requires root access
die("pinger must be run as root\n") if($> != 0);
my $alive = 1;
my(%switches, $all, $print, $both, $time, $name, $force, $startTime, $finishTime);
# handle command line options
my $status = getopts('abdfrnpth', \%switches);
help() if(!$status || $switches{'h'});
for (keys %switches) {
switch($_) {
case 'a' { $all = 1; }
case 'b' { $both = 1; $name = 1; }
case 'd' { $alive = 0; }
case 'f' { $force = 1; }
case 'p' { $print = 1; }
case 'r' { $alive = 1; }
case 'n' { $name = 1; }
case 't' { $time = 1; }
else {}
}
}
die("Usage: pinger START_IP [END_IP]\n")if(scalar(@ARGV) < 1 || scalar(@ARGV) > 2);
$startTime = time if($time);
my $p = Net::Ping->new('icmp');
my(@reachable_ips,@unreachable_ips);
# must be IP addresses
if($ARGV[0] !~ /((?:\d{1,3}\.){3}\d{1,3})/ || (defined($ARGV[1]) && $ARGV[1] !~ /((?:\d{1,3}\.){3}\d{1,3})/)) {
die("Both arguments must be IP addresses if two are specified.\n");
}
my @ip1 = split(/\./, $ARGV[0]);
# Set the second IP address to the first if no second argument
my @ip2 = (scalar(@ARGV) == 2) ? split(/\./, $ARGV[1]) : @ip1;
# and only the final octet should differ
if($ip1[0] != $ip2[0] || $ip1[1] != $ip2[1] || $ip1[2] != $ip2[2]) {
die("The first three octets of the IP addresses must match.\n")
}
# reverse IPs if 1 is larger than 2
if($ip1[3] > $ip2[3]) {
my $temp = $ip2[3];
$ip2[3] = $ip1[3];
$ip1[3] = $temp;
}
my $baseIp = $ip1[0] . '.' . $ip1[1] . '.' . $ip1[2];
my $start = $ip1[3];
my $end = $ip2[3];
my $count = $ip2[3] - $ip1[3] + 1;
for(my $i = $start, my $k = 1; $i <= $end; $i++, $k++) {
progressBar($k, $count);
my $ip = $baseIp . '.' . $i;
my $reachable = $p->ping($ip, 1);
if($name && ($reachable || $force)) {
my $hostaddr = gethostbyaddr(inet_aton($ip), AF_INET);
if($hostaddr ne "") {
$ip = ($both) ? "$ip\t$hostaddr" : "$hostaddr";
}
}
if($print) {
$ip = ($reachable) ? "o $ip" : "x $ip";
}
if($reachable) {
push(@reachable_ips, colored($ip, 'green'));
} else {
push(@unreachable_ips, colored($ip, 'red'));
}
}
$p->close();
$finishTime = time if($time);
print "\n\n";
my @ips;
if($all) {
print "All:\n";
@ips = (@reachable_ips, @unreachable_ips);
@ips = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [$_, int sprintf("%03.f%03.f%03.f%03.f", split(/\.+/, $_))] }
@ips;
} elsif($alive) {
print "Reachable:\n";
@ips = @reachable_ips;
} else {
print "Dead:\n";
@ips = @unreachable_ips;
}
my $delimeter = ($both) ? "\n" : ", ";
print join($delimeter, @ips);
print "\n\n";
if($time) {
my $completed = $finishTime - $startTime;
printf("Completed in %.2f seconds.\n", $completed);
}
# details from http://stackoverflow.com/questions/1782107/how-do-i-retrieve-the-terminal-width-in-perl
sub findTerminalWidth {
my($winsize, $row, $col, $xpixel, $ypixel);
my $available = 1;
require 'sys/ioctl.ph';
$available = 0 unless defined &TIOCGWINSZ;
open(TTY, "+</dev/tty") or $available = 0;
$available = 0 unless (ioctl(TTY, &TIOCGWINSZ, $winsize = ''));
if($available == 1) {
($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize);
} else {
return -1;
}
close(TTY);
return $col;
}
sub progressBar {
my($current, $total) = @_;
my $width = findTerminalWidth();
my $barWidth = $width - 11;
my $percent = ($current / $total) * 100;
my $barPercent = int($percent / 100 * $barWidth);
printf(" [%3d\%",$percent);
if($width > 0) {
print "] [";
for(my $i = 1; $i < $barWidth; $i++) {
($i <= $barPercent) ? print '=' : print ' ';
}
}
print "]\r";
}
sub help {
print "usage:\t pinger [-d] start [finish]\n\n";
print "\t-a\t\tList both reachable and unreachable hosts\n";
print "\t-b\t\tList both hostname and ip address\n";
print "\t-d\t\tList dead addresses\n";
print "\t-f\t\tForce hostname lookup even if unreachable\n";
print "\t-n\t\tReturn list as hostnames when possible\n";
print "\t-p\t\tEnable print mode; only noticeable with -a enabled\n";
print "\t-r\t\tList reachable addresses (default)\n";
print "\t-t\t\tTime ping execution\n";
exit(1);
}