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

Hello, I'm having a bit of a problem with using $SIG{ALRM} to kill a child process.

I wrote this script to take two arguments, alarm time and command to execute. The script is called runtimed:
my $status = 0; my $maxTime = shift @ARGV; my $runCmd = join ' ', @ARGV; $SIG{ALRM} = sub { die "timeout" }; eval { alarm($maxTime); `$runCmd`; alarm(0); }; if ($@) { if ($@ =~ /timeout/) { local $SIG{'HUP'} = 'IGNORE'; kill('HUP', -$$); $status = 1; } } exit $status;
I want be able to call runtimed from different scripts, with different times and commands to execute.

Here's the test script I call it with:
my $FIND = '/usr/bin/find'; &finder($FIND, ' . -name somename -print'); sub finder { my ($cmdName,$cmdOptions) = @_; my $cmdExecute = $cmdName . $cmdOptions; chdir "/"; my $res = `/usr/bin/perl runtimed 3 $cmdExecute`; print "$res\n"; return 0; }
I use the test script to call runtimed with a three-second alarm and a find command to execute.

The result I get is that the test script dies after three seconds, but the find command continues to run. If I hard-code the alarm time and the find command into runtimed and just execute that, it kills the find command.

I'm using Perl version 5.8.0 on Red Hat Linux 3.2.2-5. I've read about Perl "safe" signals and how those sometimes aren't able to break in and kill a child process. I don't think this is my problem, though, since I'm able to kill the find command if I hard-code it into runtimed.

Thanks for any help,

Ryan

Replies are listed 'Best First'.
Re: SIG{ALRM} Question
by Errto (Vicar) on Oct 29, 2005 at 21:31 UTC
    I haven't tested this, but I have a guess as to the nature of your problem. The technique of kill('HUP', -$$) will not kill the child process unless the $$ of your "runtimed" program is the current process group id (which I guess it isn't in this situation). So you'll need to capture the child proc id directly using fork and exec:
    my $status = 0; my $maxTime = shift @ARGV; my $runCmd = join ' ', @ARGV; my $child_pid; $SIG{ALRM} = sub { die "timeout" }; eval { alarm($maxTime); unless ($child_pid = fork) { exec $runCmd; die "could not exec $runCmd: $!"; } wait; alarm(0); }; if ($@) { if ($@ =~ /timeout/) { local $SIG{'HUP'} = 'IGNORE'; kill('HUP', $child_pid); $status = 1; } } exit $status;
    Update: One other thing I forgot to mention - your original code traps the output of the "find" command and then discards it (that's what happens when you use backquotes and don't do anything with the return value) whereas mine allows it to be printed, so adjust accordingly as needed.