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

Hi, All,

I have put together a script that cycles through a list of linux hostnames. If I can first successfully ping a machine, I then check to see if my home directory is mounted on that machine.

I am running into a small problem, though: even though I can ping a system and get good statistics on the connection, sometimes a machine is, well, brain dead and can't do much of anything (read: it needs a swift reboot :-). I therefore need to improve the way in which I check to see if my home directory is mounted. Here is the routine:-

foreach $Host (@MasterMachineIndex) { &ping; # my subroutine to ping system...works fine $Cmd = "rsh $Host uptime"; $Status = `$Cmd`; $CheckHome = ("rsh $Host \"cd $Home\""); $HomeStatus = `$CheckHome 2>&1`; # This captures STDERR and STD +OUT outputs that may or may not manifest... if ($HomeStatus =~ /No such/) { # May need to add more keyw +ords here. Not sure all OSs echo same nomenclature... print ("$Home directory not mounted on $Host! Skipping...\n") +; } else { $CPUScan = "rsh $Host \"CPUScan.pl >> ./foo\""; $CHECK = `$CPUScan`; print ("$Host:\t$Status"); } }

The crux of the problem is the second and third lines ("rsh $Host uptime"): sometimes after a successful ping, this rsh operation just doesn't complete and the foreach just grinds to a halt. I have been trying to add in alarm functionality, but I am failing so miserably I am not even going to post my pathetic code.

Any suggestions? I just want to try to rsh into the system, and if nothing happens within, say, 5 seconds, just move on the next host in the @MasterMachineIndex. If I eliminate known problem workstations from my list, the code above rips along just fine.

I would greatly appreciate any suggestions!

Reagrds,

fiddler42

Replies are listed 'Best First'.
Re: Trying to get alarm to work...
by liverpole (Monsignor) on Apr 15, 2007 at 21:39 UTC
    Hi fiddler42,

    Coincidentally, I had to use alarm just the other day, and I was also using uptime to test it.

    Have you looked at the alarm docs?  It gives a good example where, in conjunction with eval, you can branch on failure or success (ie. alarm did or didn't take place).

    Something like this, perhaps (using the code example from alarm with your code):

    use strict; use warnings; my @MasterMachineIndex = qw( # For example ... remote_host_1 remote_host_2 remote_host_3 ); foreach $Host (@MasterMachineIndex) { &ping; # my subroutine to ping system...works fine $Cmd = "rsh $Host uptime"; my $result = ""; eval { local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required alarm $timeout; $result = `$Cmd`; alarm 0; }; if ($@) { ($@ eq "alarm\n") or die "Unexpected error\n"; # Handle timeout here. } else { # Success here. } # ... }

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Trying to get alarm to work...
by betterworld (Curate) on Apr 15, 2007 at 21:45 UTC
    If you don't mind using ssh rather than rsh, you can try this:
    system('ssh', '-o', 'ConnectTimeout 4', '-o', 'SetupTimeout 4', $host, + 'cd');
Re: Trying to get alarm to work...
by swares (Monk) on Apr 16, 2007 at 05:58 UTC
    I had almost the same problem. Here is a example that uses a perl based ping. I riped this out of some other code, but you could add your code to it and make a few adjustments.
    use Net::Ping; use POSIX ":sys_wait_h"; my $doping="true"; my pid; $SIG{ALRM}=\&end_of_time; foreach $host ( @HOSTS ) { # If true ping the host 1st to see if its on the network. if ( $doping eq "true" ) { $pong = Net::Ping->new( $> ? "tcp" : "icmp", 10); (defined $pong ) or die "Can't create new ping object: $!\n"; if ( not $pong->ping($host) ) { $pong->close; print "$host --Dead--\n"; #go to next host next; } $pong->close; } ### Set alarm incase processes hang alarm $end_of_time; ### Do your Checks here $pid=....; } sub end_of_time { alarm 0; kill => $pid; print "----[ EOT Reached Killed shell process [ $shell ] for host + [ $host ]----\n"; }
Re: Trying to get alarm to work...
by fiddler42 (Beadle) on Apr 16, 2007 at 14:58 UTC
    Hi, All,

    Thanks for the suggestions. I got it to work. Final solution is below.

    Thanks!

    -fiddler42

    use Mail::Mailer; use Term::Cap; use POSIX; my $TermIOs = new POSIX::Termios; $TermIOs->getattr; my $OSpeed = $TermIOs->getospeed; my $t = Tgetent Term::Cap { TERM => undef, OSPEED => $OSpeed }; (${norm}, ${bold}, ${under}) = map { $t->Tputs($_,1) } qw/me md us/; $Timeout = 5; $Home = "/path/to/my/home"; my @MasterMachineIndex = qw (host1 host host3); foreach $Host (@MasterMachineIndex) { &ping; $UptimeCheck = "rsh $Host uptime"; my $UptimeResult = ""; eval { local $SIG{ALRM} = sub { die "Alarm!\n" }; alarm $Timeout; $UptimeResult = `$UptimeCheck`; alarm 0; }; if ($@) { ($@ eq "Alarm!\n") or die ("Unexpected error...\n"); print ("${bold}Problems accessing $Host.${norm} Skipping...\n +"); next; } else { $CheckHome = ("rsh $Host \"cd $Home\""); $HomeStatus = `$CheckHome 2>&1`; # This captures STDERR and + STDOUT outputs that may or may not manifest... if ($HomeStatus =~ /No such/) { # May need to add more +keywords here. Not sure all OSs echo same nomenclature... print ("${bold}Home directory not mounted on $Host!${norm} + Skipping...\n"); } else { # e.g. auto-e +mail admin? $CPUScan = "rsh $Host \"~/perl/CPUScan.pl >> ~/cpu.stats\" +"; $CHECK = `$CPUScan`; print ("$Host:\t$UptimeResult"); } } } sub ping { my $os = `uname -a`; if ($os =~ /SunOS/) { # For the line below, bytes transmitted will be 56+8=64 and 1 += # of transactions my $p = `ping -s $Host 56 1 | grep 'packet loss'`; unless ($p =~ /(\d+)% packet loss/) { print ("${bold}Bad Response from $Host.${norm} Skipping.. +.\n"); next; } } elsif ($os =~ /Linux/) { my $p = `ping -c 1 -t 1 $Host | grep 'packet loss'`; unless ($p =~ /(\d+)% packet loss/) { print ("${bold}Bad Response from $Host.${norm} Skipping.. +.\n"); next; } } my $loss = $1; if ($loss > 50) { print ("${bold}High packet loss from $Host.${norm} Skipping.. +.\n"); next; } }