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

Greetings Fellow Monks!
In the code posted below I am attempting (unsucessfully) to cause a command to timeout with ALARM. Simply, if the command does NOT finish/complete in 90 seconds I want it to abort and throw in alarm. When I run the code shown below it just sits there, well past the timeout limit.
There must be some subtlety that I am missing.
Any help you can provide in this matter as to why this is not working will be greatly appreciated.
Thanks in advance.
#!/usr/bin/perl -w use strict; use FileHandle; my $command="/usr/local/files/command"; my @array; my $timeout=50; eval{ local $SIG{ALRM}=sub{die "alarm\n"}; alarm $timeout; my $pid = open my $sys, "-|", $command or die $!; @array=<$sys>; close $sys; alarm 0; }; if($@){ die unless $@ eq "alarm\n"; }else{} if($@ eq "alarm\n"){print "COMMAND FAILURE!\n";} my $rule=qr/string/; for(my $i=0; $i<@array; $i++){ my $line=$array[$i]; $line =~ s/\s+//g; if($line =~ m/$rule/){ my $VAR=(split $rule, $line)[1]; print "VALUE VAR: $VAR\n"; exit; } }
References to actual command files and directory structures have been changed in accordance with security protocol.

Replies are listed 'Best First'.
Re: command timeout not working
by eserte (Deacon) on May 14, 2004 at 13:37 UTC
    This sounds like a safe signals problem. Look at the "Deferred signals" section in perlipc for a description of the problem and possible solutions.
Re: command timeout not working
by Crackers2 (Parson) on May 14, 2004 at 15:30 UTC

    It might be easier (better?) to just dump the alarm completely, and use select(). That will take care of any safe signals issue, and allows you a bit more control as well

    Something like this:

    use strict; use warnings; use IO::Select; my $timeout = 20; my $read_set = new IO::Select(); my $pid; my $starttime = time(); my $pid = open my $fh, "-|", $command or die $!; $read_set->add($fh); while (time() - starttime < $timeout) { my ($rh_set) = IO::Select->select($read_set, undef, undef, 1); if (defined $rh_set) { # note: use sysread() instead of <> to be really safe if ($_ = <$fh>) { push @array, $_; print "+"; } else { # eof last; } } else { # nothing received for a second print "."; } } kill 9, $pid; close $fh; if (time() - starttime >= $timeout) { print "Timed out\n"; }

    Update:Use time() instead of localtime()
    Update 2:Fixed open() line

Re: command timeout not working
by dave_the_m (Monsignor) on May 14, 2004 at 14:18 UTC
    If I replace
    my $pid = open my $sys, "-|", $command or die $!;
    with
    my $pid = open my $sys, "-|", sleep, 1000 or die $!;
    I get COMMAND FAILURE! after the allotted timeout.
      but what about $command?
      how does that fit into the picture with this solution?
      the goal was that if $command does not complete with in a certain amount of time it times out.
        I wasn't presenting a solution; I was just demonstating that if the command happens to be a simple UNIX process that hangs, your code does in fact correctly timeout (for me at least). You could take this as a starting point; firstly, does your code timeout for you too if you replace the command with a simple sleep? And if so, what is the diffence in behaviour between your command and the UNIX 'sleep' command that causes one to fail and not the other? Investigating that difference may lead to further clues.
Re: command timeout not working
by bluto (Curate) on May 14, 2004 at 15:27 UTC
    You may want to implement this differently. Even if you can get the parent process to continue, you are not guaranteed that the child process will die when using alarm(), in fact it usually doesn't for me. You will probably need to kill it off with a signal from the parent. Note that this question has been asked many times before (see Super Search).

    One simple workaround: I tend to fork the child which sends it's output to a temp file, wait in the parent with waitpid and then kill the child from the parent after the timeout expires. You can timeout the waitpid call with alarm() or just call it non-blocking and sleep a second or two between each call.

Re: command timeout not working
by Anonymous Monk on May 14, 2004 at 13:51 UTC
    Additional INFO:
    PERL VERSION: 5.8.0 for i386-linux-thread-multi
    OS: Linux