I have a forked ping program (mostly lifted from
www.stonehenge.com, thanks merlyn). Anyway, I have modified his code to use Net::Ping and log the results to a file.
The problem I am having is this, I need the part that loads the array @hosts to only be executed in the parent not the child, but the fork comes below in the block that depends on @hosts being populated. The problem is that the loading of @hosts is being done in every child. Not only is this not efficient, but I am getting multiple reports of bad ip's written to the log file.
How can I, prior to having $pid set in the fork, make the block that loads @hosts execute only in the parent?
#!/usr/bin/perl -w
use strict;
use Net::Ping;
my $source_file = (shift || "/tmp/ping_list.txt");
my $good_out = "/tmp/good_ips.txt";
my $bad_out = "/tmp/bad_ips.txt";
my %pid_to_host;
my %host_result;
my @hosts;
$|++;
open(GOOD,">$good_out")
or die "Cannot open output file $good_out: $!\n";
open(BAD,">$bad_out")
or die "Cannot open output file $bad_out: $!\n";
open(IPS,"$source_file")
or die "Cannot open source file $source_file: $!\n";
for (<IPS>)
{
chomp;
s/ *//;
s/\cM//;
if ($_ =~ m/^\d+\.\d+\.\d+\.\d+$/)
{
push(@hosts,$_);
}
else
{
print BAD "$_ : NOT A VALID IP ADDRESS\n";
}
}
for (@hosts)
{
wait_for_a_kid() if keys %pid_to_host > 50;
if ( my $pid = fork ) {
# parent
$pid_to_host{$pid} = $_;
} else {
# child
exit !ping_a_host($_);
}
}
1 while wait_for_a_kid();
for (sort keys %host_result)
{
print GOOD "$_\n" if ($host_result{$_});
print BAD "$_\n" if (!$host_result{$_});
}
sub ping_a_host
{
my $host = shift;
my $p = Net::Ping->new('tcp',5);
$p->ping($host) ? 1 : 0;
}
sub wait_for_a_kid
{
my $pid = wait;
return 0 if $pid < 0;
my $host = delete $pid_to_host{$pid}
or warn("Why did I see $pid ($?)\n"), next;
$host_result{$host} = $? ? 0 : 1;
1;
}