#--------------------------------------------------------------------# # PingSweep # Date Written: 27-Nov-2001 11:41:02 AM # Last Modified: 03-Jan-2002 02:20:15 PM # Author: Kurt Kincaid # Copyright (c) 2001, Kurt Kincaid # All Rights Reserved # # This is freesoftware and may be modified and/or redistributed # under the same terms as Perl itself. #--------------------------------------------------------------------# use Tk; use Tk::Dialog; use Tk::DialogBox; use Tk::Text; use Tk::Menu; use Tk::Menubutton; use Tk::widgets qw(Menu); use Net::Ping; use IO::File; use FileHandle; use Time::HiRes qw( gettimeofday tv_interval ); use DBI; use constant; use Math::NumberCruncher; use Getopt::Long; use Mail::Sendmail; use strict; use Math::BigInt::Calc; use DBD::AnyData; use AnyData::Format::XML; use AnyData::Storage::PassThru; use vars qw( $iterations $opt_n $title $mw $t $sth $ref $p $name $button $count @button @label $t0 $resp $elapsed $PingSweep $now $PingSweepLabel $total $info $ip @elapsed $VERSION %mail $address $message %previous $m $update $hostfile $opt_f $opt_r $repeat $seconds $opt_h $opt_a $subject $opt_x $noemail $opt_v $desc ); GetOptions( 'help' => \$opt_h, 'pings=i' => \$opt_n, 'email=s' => \$opt_a, 'file=s' => \$opt_f, 'recur=i' => \$opt_r, 'xml' => \$opt_x, 'noemail' => \$noemail, 'version' => \$opt_v ); if ( $opt_r ) { $opt_r *= 1000; } #--------------------------------------------------------------------# # Change the following addresses to suit your needs. #--------------------------------------------------------------------# my $default_addr = 'someaddress@somedomain.com'; my $from_addr = 'someaddress@somedomain.com'; $iterations = $opt_n || 20; $hostfile = $opt_f || "./hostdata.xml"; $repeat = $opt_r || 90000; $seconds = $repeat / 1000; $update = "03-Jan-2002 02:20:15 PM"; $address = $opt_a || $default_addr; $subject = "SERVER UNAVAILABLE!"; $| = 1; $VERSION = "1.01a"; $title = "PingSweep v$VERSION"; if ( $opt_v ) { print $title; exit; } if ( $opt_h ) { print HelpText(); exit; } if ( $opt_x ) { print HelpDetail(); exit; } my $dbh = DBI->connect( "dbi:AnyData(RaiseError=>1):" ); $dbh->func( 'hosts', 'XML', $hostfile, 'ad_import' ); $mw = Tk::MainWindow->new(); $mw->title( $title ); $m = $mw->Frame( -relief => 'groove', -bd => 2 ) ->pack( -side => 'top', -anchor => 'n', -fill => 'x' ); $m->Menubutton( -text => "File", -tearoff => 0, -menuitems => [ [ "command" => "Exit", -command => sub { exit } ] ] )->pack( -side => 'left' ); $m->Menubutton( -text => "Help", -tearoff => 0, -menuitems => [ [ "command" => "Help...", -command => \&help ], [ "command" => "About...", -command => \&about ] ] )->pack( -side => 'left' ); $t = $mw->Scrolled("Text", -width => 82, -height => 38, -cursor => 'arrow', -wrap => 'word', -background => '#808080', -scrollbars => 'e' )->pack( -expand => 1, -fill => 'both' ); $sth = $dbh->prepare( "SELECT * FROM hosts ORDER BY hostname" ); $sth->execute(); while ( $ref = $sth->fetchrow_hashref() ) { $p = Net::Ping->new( "icmp", 1 ); $name = $ref->{ hostname }; $button = "button" . $count; $count++; $button[ $count ] = $t->Button( -width => 30, -command => [ \&Ping, $count ], -cursor => 'hand2', -text => "$ref->{hostname} ($ref->{ip_address})" )->pack( -side => 'left', -anchor => 'w' ); $t->windowCreate( 'end', -window => $button[ $count ] ); $t0 = [ gettimeofday ]; $resp = $p->ping( $ref->{ ip_address } ); $elapsed = sprintf( "%.2f", tv_interval( $t0 ) ); if ( $resp ) { $button[ $count ]->configure( -foreground => 'black', -background => 'green', -activeforeground => 'white', -activebackground => 'blue' ); } else { $elapsed = "n/a"; $button[ $count ]->configure( -foreground => 'yellow', -background => 'red', -activeforeground => 'white', -activebackground => 'blue' ); } $label[ $count ] = $t->Label( -background => '#808080', -foreground => 'white', -width => 60, -cursor => 'arrow', -text => "Ping: $elapsed" )->pack( -side => 'left', -anchor => 'w' ); $t->windowCreate( 'end', -window => $label[ $count ] ); $t->insert( 'end', "\n" ); } $PingSweep = $t->Button( -command => \&PingSweep, -cursor => 'hand2', -width => 30, -text => "PingSweep" )->pack( -side => 'bottom' ); $PingSweep->configure( -foreground => 'black', -background => 'yellow', -activeforeground => 'yellow', -activebackground => 'black' ); $t->windowCreate( 'end', -window => $PingSweep ); $PingSweepLabel = $t->Label( -background => '#808080', -foreground => 'white', -width => 60, -cursor => 'arrow', -text => "Last PingSweep: Never" )->pack( -side => 'left', -anchor => 'w' ); $t->windowCreate( 'end', -window => $PingSweepLabel ); $total = scalar @button - 1; $sth->finish(); $dbh->disconnect(); $mw->Label( -textvariable => \$info, -relief => 'ridge' )->pack( -side => 'bottom', -fill => 'x' ); if ( $noemail ) { $desc = " -- NOT SENDING EMAIL"; } else { $desc = ""; } $info = $title . " -- Auto PingSweep every $seconds seconds$desc"; $mw->repeat( $repeat, \&PingSweep ); MainLoop(); sub Ping { my $num = shift; my $text = $button[ $num ]->cget( '-text' ); $text =~ m/^(.*) \((.*)\)$/; $name = $1; $ip = $2; $p = Net::Ping->new( "icmp", 1 ); my $success = 0; my $failure = 0; undef @elapsed; my $failed = 0; for ( 1 .. $iterations ) { if ( $failure == 5 && $success == 0 ) { $failed = 1; last; } $t0 = [ gettimeofday ]; $resp = $p->ping( $ip ); if ( $resp ) { $elapsed = tv_interval( $t0 ); push ( @elapsed, $elapsed ); $success++; } else { $failure++; } Time::HiRes::usleep( 10_000 ); } my $percent = sprintf( "%.2f", ( $success / $iterations ) * 100 ); my ( $high, $low ) = Math::NumberCruncher::Range( \@elapsed ); $high = sprintf( "%.2f", $high ); $low = sprintf( "%.2f", $low ); if ( $high eq "" ) { $high = 0 } if ( $low eq "" ) { $low = 0 } my $mean; if ( $failed ) { my $current = time(); if ( $current - $previous{ $ip } > 900 && ! $noemail ) { $now = localtime(); my $subj = $subject . " ($ip)"; $message = "Time: $now\nHostname: $name\nIP Address: $ip\n"; $previous{ $ip } = $current; %mail = ( To => $address, From => $from_addr, Message => $message, Subject => $subj ); sendmail( %mail ) or die $Mail::Sendmail::error; } $mean = "n/a"; } else { $mean = sprintf( "%.3f", Math::NumberCruncher::Mean( \@elapsed ) ); } if ( $resp ) { $button[ $num ]->configure( -foreground => 'black', -background => 'green' ); } else { $button[ $num ]->configure( -foreground => 'yellow', -background => 'red' ); } $label[ $num ]->configure( -text => "Ping Success: $percent\% ($success/$iterations) Avg. Time: $mean (High: $high, Low: $low)" ); } sub PingSweep { $PingSweepLabel->configure( -text => "Starting PingSweep...." ); $info = "Updating Ping Data......"; for ( 1 .. $total ) { if ( $_ eq "" ) { last } Ping( $_ ); } $info = $title . " -- Auto PingSweep every $seconds seconds"; $now = localtime(); $PingSweepLabel->configure( -text => "Last PingSweep: $now" ); } sub about { $mw->Dialog( -title => "About...", -text => <<"END", -popover => $mw, -font => 'ansi' )->Show; $title Last Update: $update Author: Kurt Kincaid Copyright (c) 2001, Kurt Kincaid All Rights Reserved This is free software and may be modified and/or redistributed under the sames terms as Perl itself. END } sub help { my $helptext = HelpText(); $helptext .= "\n" . HelpDetail(); my $help_label = "$title Help"; my $help = MainWindow->new(); $help->title( "Help" ); my $mm = $help->Frame->pack( -side => 'top', -fill => 'x' ); $mm->Button( -text => "Close Window", -command => sub { $help->destroy() } ) ->pack( -side => 'right' ); $help->Label( -textvariable => \$help_label, -relief => 'ridge' ) ->pack( -side => 'bottom', -fill => 'x' ); my $tt = $help->Scrolled("Text", -width => 80, -wrap => 'word', -background => '#ffff88', -font => '{Courier} 8 {normal}', -scrollbars => 'e' )->pack( -side => 'bottom', -fill => 'both', -expand => 1 ); $tt->insert( "end", $helptext ); } sub HelpText { my $where; if ( $opt_h ) { $where = "type \"pingsweep --xml\""; } else { $where = "see below." } my $ht = "$title Last Update: $update Usage: pingsweep [--email address] [--file host_file] [--help] [--noemail] [--pings num_of_pings] [--recur seconds] [--version] --email Specify an email address to which warnings should be emailed. The default address is: $address Multiple addresses should be separated by commas. --file Specify alternate XML file with host information. The default is \"hostdata.xml,\" assumed to be stored in the same directory as pingsweep. When specifying another file, the location must be in UNIX format (i.e. /some/dir/hosts.xml) For XML file specifications, $where --help This help text. --noemail Do not send warning email, regardless of server failures. This supercedes all other email settings. --pings The number of pings per host. The default is 20. --recur Frequency of the auto pingsweep, in seconds. Default: 90 seconds. --version Prints the version number and exits. Example: pingsweep --file somefile.xml --pings 10 --recur 300 NOTE: You may use equal signs (=) when passing command line parameters, if preferred. (ex. pingsweep --file=somefile.xml) "; return $ht; } sub HelpDetail { my $dt = "XML Format Information The host file must follow standard XML rules and must be in the following format: some_hostname 255.255.255.1 some_other_hostname 255.255.255.2 ... PingSweep supports an arbitrary number of hosts. "; return $dt; }