3 things:
- send all failures in one email message
- instantiate only one Net::Ping and re-use it
- get rid of the tryagain sub
You have coded tryagain to accept and iterate through a list of hosts, but you always pass it one scalar.
But like i said, it's not really necessary anyway. Here is
a re-write that uses Getopt::Long and
Pod::Usage. However, Pod::Usage and "icmp" pings
don't play together in the sandbox very well. By that i
mean that "Superuser must not run perldoc without
security audit and taint checks." (perldoc error message) and "specifying the 'icmp' protocol requires that the program be run as root or that the program be setuid to root" (from the Net::Ping docs). What this
boils down to is unless you set up those "security audit
and taint checks", there is no reason to use a Pod::Usage
-verbose setting higher than 1 (one), but i did
anyway for the help message.
use strict;
use warnings;
use Data::Dumper;
use Net::Ping;
use MIME::Lite;
use Getopt::Long;
use Pod::Usage;
use vars qw(
$ping $first $delay $second
@host $file $help
$to $from
);
parse_args();
$to ||= 'linebacker@perlmonks.org';
$from ||= 'ICMP Monitor <joeblow@i.com>';
$first ||= 5;
$delay ||= 60;
$second ||= 30;
$ping = Net::Ping->new('icmp');
foreach my $host (@host) {
print "$host is ";
unless ($ping->ping($host, $first)) {
sleep ($delay);
unless ($ping->ping($host, $second)) {
notify($host);
print 'NOT ';
}
}
print "reachable.\n";
}
$ping->close();
sub notify {
my @host = @_;
my $data = "Ping failures:\n\t" . join("\n\t",@host) . "\n" . local
+time;
my $msg=MIME::Lite-> new(
To => $to,
From => $from,
Subject => 'Ping Failure',
Data => $data,
);
$msg->send;
}
sub parse_args {
GetOptions(
'host=s' => \@host,
'file=s' => \$file,
'to=s' => \$to,
'from=s' => \$from,
'first=i' => \$first,
'delay=i' => \$delay,
'second=i' => \$second,
'help|h|?' => \$help,
);
pod2usage(-verbose=>2) if $help;
pod2usage(-verbose=>1) unless @host or $file;
@host = file2list($file) unless @host;
}
sub file2list {
my $file = shift;
open FH, $file or die "can't open $file: $!";
my @list = <FH>;
chomp @list;
return @list;
}
__END__
=head1 NAME
ping_mon.pl - Ping Monitor with Email Notification
=head1 SYNOPSIS
ping_mon.pl -host <host> | -file <file>
Options:
-file read list of hosts from file
-host provide host via command line
-to email recepient
-from email sender
-first no. seconds for first time-out
-delay no. seconds between first and second ping
-second no. seconds for second time-out
-help -h brief help message
=head1 DESCRIPTION
B<This program> will ping the provided hosts and send
a email notifying which hosts (if any) were not reachable.
Each host will first be pinged for an initial amount (set
with the -first option). If that ping fails, the program
will sleep for an amount of time (set with the -delay
option) before a second ping attempt. The second ping's
time-out is set with the -second option. You can also
set the to and from fields for the email on the command
line. Modify the source code to add default values.
=head1 EXAMPLES
1 - ping contents of /etc/hosts
./ping_mon.pl -file=/etc/hosts
2 - ping Google
./ping_mon.pl -host=google.com
2 - ping Google and Perlmonks
./ping_mon.pl -host=google.com -host=perlmonks.org
=cut
jeffa
L-LL-L--L-LL-L--L-LL-L--
-R--R-RR-R--R-RR-R--R-RR
B--B--B--B--B--B--B--B--
H---H---H---H---H---H---
(the triplet paradiddle with high-hat)
| [reply] [d/l] |
I may very well misread your code, but aren't you sending one mail for every failed host too?
I'd expected something like
[...]
my @failed = [];
foreach my $host (@host) {
print "$host is ";
unless ($ping->ping($host, $first)) {
sleep ($delay);
unless ($ping->ping($host, $second)) {
push @failed, $host;
print 'NOT ';
}
}
print "reachable.\n";
}
notify(@failed) if @failed;
[...]
may I humbly ask what I've overlooked?
regards,
tomte
| [reply] [d/l] |
"may I humbly ask what I've overlooked?"
The fact that i was half-asleep when i wrote that? :D
May i humbly say, thanks? I will leave my code untouched,
but you are absolutely correct. I wrote my code by modifying
linebacker's, but i forgot to add that feature. Thanks
for catching it, and better yet, offering a solution.
However, don't initialize an array like this:
my @array = []; # use empty parens instead ()
That declared a new array whose first element is an
anonymouse array reference. I have been bitten by that
error before. ;)
jeffa
L-LL-L--L-LL-L--L-LL-L--
-R--R-RR-R--R-RR-R--R-RR
B--B--B--B--B--B--B--B--
H---H---H---H---H---H---
(the triplet paradiddle with high-hat)
| [reply] [d/l] |
| [reply] |
| [reply] [d/l] [select] |
Updated the script. I was unenlightened about using localtime. Funny, I had been using cut before someone enlightened me to split.
| [reply] |
| [reply] |