neeraj has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I have written a script to query the status of webserver at port 80. I have used nmap for that. as i have to query all the addresses of subnet, i used Parallel::ForkManager to run processes in parallel. But it si giving me some strange results. I m using a global array to strore the results of each IP address . But in the loop, the array gets re-initialized each time. Below is the code for it. Its running perfectly fine if i use a file to write the results instead of an array. Here is the code for it...
#! /usr/bin/perl use strict; use warnings; use Parallel::ForkManager; my @arr; my $PORT_NO = '80'; # Default port of HTTP my $SERVICE = "http"; my $pm = new Parallel::ForkManager(20); # At any instant maximum 20 pr +ocesses can run simultaneously my $ip; # Stores the IP address suppli +ed by user if (!$ARGV[0]) { print "Program intended to check the machines where webserver is r +unning. \n"; print "USAGE: perl wst.pl [ip(xxx.xxx.xxx)] \nInput only 3 octets +\n"; exit; } if ($ARGV[0] =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}$/) { $ip = $ARGV[0]; } else { print "Enter IP in valid format (xxx.xxx.xxx) \n"; print "USAGE: perl wst.pl [ip(xxx.xxx.xxx)] \n"; exit; } foreach my $i (1..254) { my $ip_add = sprintf("%s.%s",$ip,$i); #print $ip_add; my $pid = $pm->start and next; my $val = `nmap -sT -p $PORT_NO $ip_add | grep $SERVICE`; #print $val; if ($val) { my $test = (split(/\s+/,$val))[1]; if ($test =~ m/open/) { #print "Web Server at $ip_add is running.\n"; push (@arr,$ip_add); #print "@arr\n"; } } $pm->finish; } print "reaping....might take some time\n"; $pm->wait_all_children; print "Web Server is running at : @arr \n"; exit 0;
Can u all suggest any solution to it. Is this some kind of synchronization problem. Any help would be greatly appreciated. Thanks Neeraj

Replies are listed 'Best First'.
Re: Parallel::ForkManager Problem
by broquaint (Abbot) on Jun 11, 2004 at 05:46 UTC
    This is because you are trying to modify the @arr in the parent process from the child process, which won't work as the child process is only modifying its own copy of @arr. You either need to communicate to the parent process the information or store the information somewhere than can be accessed by the parent process. A simple solution would be to have the children write to a file e.g
    use strict; use warnings; use Fcntl ':flock'; use Parallel::ForkManager; my $PORT_NO = '80'; # Default port of HTTP my $SERVICE = "http"; my $pm = new Parallel::ForkManager(20); # At any instant maximum 20 pr +ocesses can run simultaneously my $ip; # Stores the IP address supplied by user if(!$ARGV[0]) { print "Program intended to check the machines where webserver is run +ning. \n"; print "USAGE: perl wst.pl [ip(xxx.xxx.xxx)] \nInput only 3 octets\n" +; exit; } if($ARGV[0] =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}$/) { $ip = $ARGV[0]; } else { print "Enter IP in valid format (xxx.xxx.xxx) \n"; print "USAGE: perl wst.pl [ip(xxx.xxx.xxx)] \n"; exit; } ## file to write to open(my $fh, '+>', "ipaddresses.txt") or die "ack: $!"; foreach my $i (1..254) { my $ip_add = sprintf("%s.%s",$ip,$i); my $pid = $pm->start and next; my $val = `nmap -sT -p $PORT_NO $ip_add | grep $SERVICE`; if ($val) { my $test = (split(/\s+/,$val))[1]; if ($test =~ m/open/) { flock $fh, LOCK_EX; print "$ip_add\n"; flock $fh, LOCK_UN; } } $pm->finish; } print "reaping....might take some time\n"; $pm->wait_all_children; seek $fh, 0, 0; print "Web Server is running at : ", <$fh>, "\n"; exit 0;
    So there I've replaced the push with a simple write to a file and once the children are finished the parent process simply reads the file. See. flock and seek for more info on the functions used and perlipc for info on communication between processes.
    HTH

    _________
    broquaint

Re: Parallel::ForkManager Problem
by Zaxo (Archbishop) on Jun 11, 2004 at 05:37 UTC

    Each child gets its own copy of @arr and they are not shared or communicated to the parent. Each push occurs in isolation in the child and that copy dies with it.

    Take a look at perlipc for some possible solutions.

    After Compline,
    Zaxo

Re: Parallel::ForkManager Problem (threads instead?)
by BrowserUk (Patriarch) on Jun 11, 2004 at 06:44 UTC

    I'd do it with threads this way. (I've used ping cos I don't have nmap but that's a one line change).

    #! perl -slw use strict; use threads qw[ yield ]; use Thread::Queue; if( !@ARGV or $ARGV[ 0 ] !~ m[\d{1,3}\.\d{1,3}\.\d{1,3}] ) { die "Usage: $0 xxx.xxx.xxx\nEnter 3 octets only\n"; } my $dom = $ARGV[ 0 ]; my $Qwork = new Thread::Queue; my $Qresults = new Thread::Queue; sub work{ yield until $Qwork->pending; while( $Qwork->pending ) { my $sub = $Qwork->dequeue; system "ping -n 1 -l 0 $dom.$sub 2>&1 1>nul"; $Qresults->enqueue( sprintf "$dom.$sub %s alive", $? >> 8 ? 'is not' : 'is' ); } return; } threads->new( \&work )->detach for 1 .. 20; $Qwork->enqueue( 1..255 ); yield until $Qresults->pending == 255; print $Qresults->dequeue while $Qresults->pending;

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
      HI Thanks the code works fine. Thanks for givin me threads solution, i wanted to use threads only, but i didnt knew abt this module.