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

I have a script that pings URLs and fetches files from selected URLs. If fetching or pinging to the respective URL fails for a specific no. of Times it updates a log file and forwards it to specific email addresses.

The Script reads The Urls, the function ( ping or fetch ), the no. of times the function has to be carried out before sending a failure email and the email addresses from a specific text file.

To automate the process i am using cron which runs the script every 30 minutes.

The problem is that a few URLs are not pinged when the program is run through the cron tab command and their failure is reported, but if I run the program manually i can ping those URLs.

Is there any way to find the reason for the failure during execution of the program eg. if the error is Request Time out or something like that then it gets added to the log file as the reason.

I am posting my script below.

#!/usr/bin/perl use warnings; use LWP::Simple; use Email::Send; use Email::Send::Gmail; use Email::MIME::Creator; use Net::Ping::External qw(ping); use File::Fetch; use Tie::File; my $testfolder = "/Users/Appy/Desktop/"; my $to = "test2\@gmail.com"; my $from = "test1\@gmail.com"; my $date = localtime(); my $count = 0; my $logfilelocation = "/Users/Appy/Desktop/"; tie @file, 'Tie::File', $testfolder . "testfile.txt" or die; foreach $URL (@file) { my $string1 = $URL; if ( $string1 =~ m/ # Match ping\s # 'ping ' /ix # i = Ignore case # x = allow the regexp to go over multiple + lines ) { my $URI = substr $URL, 8, 28; print "$URI\n"; &myping($URI,$URL); sub myping { my $newURI = $_[0]; my $newURL = $_[1]; my $alive = ping(host => "$newURI"); if ($alive) { print "$newURI is active.\n"; $count = 0; } else { print "$newURI is inactive\n"; my $c = substr $newURL, 5, 2; if ($count<$c) { $count = $count + 1; print "$count\n"; &myping; } elsif ($count==$c) { my $logfile = $logfilelocation . "log.txt"; my $logmsg = "$date cannot ping $newURI"; open LOGFILE, ">>$logfile" or die "cannot open $logfile for append: $! +"; print LOGFILE $logmsg, "\n"; close LOGFILE; my $emailadd = substr $newURL, 22; @personal = split(/,/, $emailadd); foreach $eadd (@personal) { my $email = Email::MIME->create( header => [ From => $from, To => $eadd, Subject => $logmsg, ], attributes => { filename =>"log.txt", content_type =>"application/text", disposition =>"attachment", Name =>"/Desktop/log.txt", }, body => "$newURI is not working" ); my $sender = Email::Send->new( { mailer => 'Gmail', mailer_args => [ username => 'appy.test1@gmail.com', password => '1234qwerty', ] } ); eval { $sender->send($email) }; die "Error sending email: $@" if $@ } } } } } }
Fetching part is Similar and has no problems. Kindly Help. Thanx in advance

Replies are listed 'Best First'.
Re: Ping through Cron Fails
by graff (Chancellor) on Mar 30, 2010 at 13:28 UTC
    The way that you've written your script is frankly quite deranged. Apart from the purely "stylistic" abuses (random indentation, and declaring a large subroutine block inside an "if" block that is inside a "for" loop), you use some very strange logic:

    The "myping" sub, which is initially called in the main "if" block (where you also perversely declare the sub), calls itself recursively every time a the Net::Ping::External ping function returns false (until you reach some limit specified in your input text list for each url).

    Also, your method of extracting information from the input text list is going to make trouble -- it's too easy to break, and there's no error checking on whether the fields you're expecting are in the exact places where you expect them to be. And why are you using "tie"? Seems like overkill if you're just iterating over the lines of a list file.

    When you say:

    The problem is that a few URLs are not pinged when the program is run through the cron tab command and their failure is reported, but if I run the program manually i can ping those URLs.

    Do you mean that some urls actually do work, and only a few urls (in the same input list file on a given run) don't work? And are you saying that when you run the script manually at the command line, using the exact same input list file, all the urls work? That would be very strange, if that's the real evidence.

    Without trying to test, here is how I would frame your script -- note the inclusion of "use strict":

    #!/usr/bin/perl use strict; use warnings; use Net::Ping::External qw(ping); # add more modules as needed... my $testfolder = "/Users/Appy/Desktop"; # other variables should be declared in the "myping" sub, or wherever +they're needed open( my $infh, "<", "$testfolder/testfile.txt" ) or die "$testfolder/testfile.txt: $!"; my @file = <$infh>; chomp @file; foreach (@file) { my ( $method, $count, $uri ) = split; # I'm GUESSING that each lin +e has 3 fields next unless ( $method eq 'ping' and # I'm GUESSING that "ping" i +s line-initial $count =~ /^\d+$/ and $count > 0 ); myping( $uri, $count); } sub myping { my ( $newURI, $count ) = @_; my $tries = 0; my $alive; while ( ++$tries < $count ) { $alive = ping(host => "$newURI"); last if ( $alive ); # you might want to put a sleep call here } if ( ! $alive ) { # do whatever needs to be done... } }
    UPDATE: replaced unused variables with a comment, added comment about using "sleep". Also, if you were not expressing yourself clearly, and it's actually a problem that none of the urls work when running under cron, then the second reply below tells you what the problem is -- your environment under cron is different from your interactive shell environment, and you have to specify your PATH variable explicitly, if your script is using any executables outside of /bin, /usr/bin and /usr/local/bin. (Note that "ping" is "/sbin/ping" on macosx, so add $ENV{PATH} .= ':/sbin'; near the top of the script.)
Re: Ping through Cron Fails
by Damashii (Scribe) on Mar 30, 2010 at 13:29 UTC
    Greetings Appy16,
    I am kind of new to perl, so I admit I very well could be wrong about this. From reading the Net::Ping::External page (http://search.cpan.org/~colinm/Net-Ping-External-0.10/External.pm) there is a warning at the bottom. It states
    'This module calls whatever "ping" program it first finds in your PATH environment variable.'
    I do know that crontabs typically do not have the PATH environment variable set. Yet when you run it by hand, the PATH environment variable is set. So Net::Ping::External can find ping.
    As I said, I am new to perl, so I could be totally off. Hopefully this will set you in the right direction.