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

Greetings Monks,

I have a program that goes through about 400 WINNT computers looking for the service pack number. I use a third party program, pstools, to find this info connecting though the Shell module.

My problem is with unsuccessful results. The psinfo program return sucessful results in about 4 seconds but if it can't find, or can't connect to a computer it can take more than a minute. This problem makes my program run longer than I would like

Is there any way, with the shell module (or backticks, qx(), etc), to kill a shell escape if it take more than, say 10 seconds?

use Shell; use strict; use Spreadsheet::WriteExcel; my @computers = qw(big list of computers); my $workbook = Spreadsheet::WriteExcel->new("ServicePack.xls"); my $worksheet = $workbook->addworksheet(); my $rowcount = 0; for my $computer(@computers){ $user{'computer'} = $computer; $user{'servicepack'} = get_sp($computer); $worksheet->write($rowcount, 0, $user{'computer'}); $worksheet->write($rowcount, 1, $user{'servicepack'}); $rowcount++; } #Function: get_sp #Arguments: Computer Name #Return Value: Service Pack number sub get_sp{ my $cpu = shift @_; my $info= psinfo('\\\\'. $cpu); my @lines = split("\n",$info); @lines = grep {/(Service Pack:)/i} @lines; s/[\w\s]+:\s+(\d)$/$1/ for @lines; my $servicepack = $lines[0]; return ($servicepack || "unknown"); }

Replies are listed 'Best First'.
Re: Dealing with timeouts using the Shell module
by dmmiller2k (Chaplain) on Jan 03, 2002 at 02:23 UTC

    One answer to your question would be relevant if you were running your script on a UNIX (or UNIX-like) box, trying to connect to WinNT machines, rather than on a WinNT machine itself.

    On a UNIX-like machine you could set up a SIGALRM handler in your perl script and use alarm() to set the timer. To paraphrase perlfunc:alarm:

    eval { local $SIG{ALRM} = sub { die "alarm\n"; } alarm 10 # here you call your get_sp() subroutine $servicpack = get_sp($computer); alarm 0; }; if ($@) { die unless $@ eq "alarm"; # propagate uexpected errors # timed out } else { # worked }

    AFAIK, Perl's signal handling is iffy at best on WinNT machines (at least with ActiveState's port), so you may have to come up with another approach if your script is running on one of those. (I'd try it anyway :)

    dmm

    You can give a man a fish and feed him for a day ...
    Or, you can
    teach him to fish and feed him for a lifetime
Re: Dealing with timeouts using the Shell module
by mojotoad (Monsignor) on Jan 03, 2002 at 06:33 UTC
    As tye pointed out, using threads or forks is a way to keep things moving. While some processes hang, others can continue. As the slow processes time out, they are cleared away to make room for new processes.

    And you definitely want to put a throttle on this, especially if you are talking about potentially hundreds of processes!

    You might want to consider merlynīs parallelizing engine which is mentioned in this informative perlmonks discussion, as well as his forking parallel link checker column which he mentioned in response to some similar code submitted by kschwab earlier.

    Matt

    P.S. I was going to dust off my so-called "Hatchery" module from my non-released bag of tricks, but considering Randallīs columns I think it would be pretty redundant. sigh.It is amusing to note that what he called "hungry puppies" I tended to call "screaming kids", although I did refer to them as a litter. Well, perhaps once I finish my trip Iīll polish it up and post a comparitive analysis if there seems to be enough differences to warrant it.

(tye)Re: Dealing with timeouts using the Shell module
by tye (Sage) on Jan 03, 2002 at 03:11 UTC

    I don't know how much work "psinfo" does, but at least for recent service packs, I think simply checking HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion//CSDVersion in the registry is enough. And Win32::TieRegistry makes checking this on a remote computer rather easy.

    I recall Windows often taking a while to decide that the connection to a remote computer was going to fail, but I think that had more to do with my strange setup and some simple testing right now shows it taking only about 4 seconds to fail for me. So, if failures usually happen that fast, then something fairly simple will work:

    use Win32::TieRegistry( Delimiter=>"/" ); sub get_sp { my $cpu= shift @_; my $ver= $Registry->{"//$cpu/LMachine/Software/" . "Microsoft/Windows NT/CurrentVersion//CSDVersion"}; if( ! $ver || $ver !~ /(\d+)/ ) { return "unknown"; } return $1; }

            - tye (but my friends call me "Tye")
      tye,
      Adapting that subroutine and the TieRegistry module greatly speeds up all the successful calls. Unfortunately the failures hang for just as long, and those failures are the real bottleneck. Thanks for an intro to a new module for me to play with regardless. BTW, Pstools,which I originally used for this project, is a pretty useful collection of free command-line Administrative software for those of us who are slumming it with WinNT. I recommend it.

        You can use Net::Ping to more quickly decide that a machine is not available before trying to connect.

        Beyond that, you'll need (because the operating system does not provide an asynchronous method for connecting to these resources) separate threads or separate processes, which, when using Perl, means you need separate processes. And since fork emulation for Win32 Perl still sucks, I think the best choice is currently Win32::Process.

        I'd throw together a quick example of how to do this but I don't have the time at the moment and, unfortunately, I don't think there is a really simple way to do this. The simplest-to-code way is probably to blindly create a new process for each machine and have each append to an output file and then read the results from there after all of the children finish. For a large list of computers, that probably won't work, however.

        I think you could find a module to manage a group of slave processes for you, and that might be a good way to go. Sorry, I can find one quickly at the moment.

                - tye (but my friends call me "Tye")