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

I have a sysadmin script which is trying to do a network operations on all ports (1-65k). It uses the system function to call a program which takes about 45 seconds to timeout. To make the script finish in my lifetime, I'd like to shorten the timeout. I have something like this:
for ($i = 0; $i <= 65535; $i++) { print "port $i\n"; system "this takes 45 seconds to timeout"; }
I've noticed that pressing the interrupte sequence (^C) will start a new iteration throught the loop. Is it possibel to send the INT signal to the running program? This doesn't seem to work:
for ($i = 0; $i <= 65535; $i++) { print "port $i\n"; system "this takes 45 seconds to timeout"; sleep 2; kill ('INT', $$); }
I think each instance of system() should result in a new PID. Could this be handled by forking, and sending the INT signal from the parent to the child? I'm just not sure where to go. Any advise is appreciated.

Replies are listed 'Best First'.
Re: Sending signal to running script
by gaal (Parson) on Dec 20, 2004 at 21:34 UTC
    To send a SIGINT, you need to look up its numerical value on your system and send that:

    kill $sig_of{INT}, $pid;     # 2 on my system, type 'kill -l' to check yours

    Are you sure you need to send the signal to yourself, and not the child process? You probably want to fork + exec off the child, take note of the pid, and kill that off.

    Update: Correct bogus reference to %SIG

Re: Sending signal to running script
by BrowserUk (Patriarch) on Dec 20, 2004 at 22:40 UTC

    Unless you cause system to run the external program as an asynchronous (background) task, your script will not regain control until after that external program times out. So your sleep will do nothing except slow you down more.

    Additionally, system doesn't give you access to the pid of the external process, so you won't know where to send your interupts to.


    Examine what is said, not who speaks.        The end of an era!
    "But you should never overestimate the ingenuity of the sceptics to come up with a counter-argument." -Myles Allen
    "Think for yourself!" - Abigail        "Time is a poor substitute for thought"--theorbtwo         "Efficiency is intelligent laziness." -David Dunham
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
Re: Sending signal to running script
by Joost (Canon) on Dec 20, 2004 at 23:05 UTC
    I'm not sure about the best way to handle this but a fork(); exec(); combination seems to work:
    #!/usr/local/bin/perl -w $SIG{INT} = sub { warn "in parent"; exit; }; for ( 0 .. 100) { my $pid = fork; unless (defined $pid) { die "Error forking"; } unless ($pid) { exec ("./test2.pl"); } else { wait; } }
    and...
    # test2.pl #!/usr/local/bin/perl $SIG{INT} = sub { warn "in child\n"; exit; }; sleep 100;
    Hitting ^C:
    in parent at ./test.pl line 4. in child > <--- shell prompt.
Re: Sending signal to running script
by Random_Walk (Prior) on Dec 20, 2004 at 23:18 UTC

    You can wrap your system command in an eval, with a suitable alarm handler set then put an alarm for your desired timeout and optionaly trap the exit from eval to see if it timedout. Dont forget to unset the alarm when you come out of the eval.

    Are you forced to only try one port at a time ? If you use fork to spawn a child thread you can keep a record of the time, its PID and the number of children currently running then spawn more.

    Once the number currently running hits some maximum you find good you can sleep a second or too until one exits or start killing the oldies. If you allow N concurrent children and all children took their full 45 seconds before returning this would take 819.2/N hours (2**16 * 45/(60**2)) to complete (for your script now N=1).

    Cheers,
    R.