views:

237

answers:

2

I created a script in perl to run programs with a timeout. If the program being executed takes longer then the timeout than the script kills this program and returns the message "TIMEOUT".

The script worked quite well until I decided to redirect the output of the executed program.

When the stdout and stderr are being redirected, the program executed by the script is not being killed because it has a pid different than the one I got from fork.

It seems perl executes a shell that executes my program in the case of redirection.

I would like to have the output redirection but still be able to kill the program in the case of a timeout.

Any ideas on how I could do that?

A simplified code of my script is:

#!/usr/bin/perl

use strict;
use warnings;
use POSIX ":sys_wait_h";

my $timeout = 5;
my $cmd = "very_long_program 1>&2 > out.txt";

my $pid = fork();
if( $pid == 0 )
{
   exec($cmd) or print STDERR "Couldn't exec '$cmd': $!";
   exit(2);
}
my $time = 0;
my $kid = waitpid($pid, WNOHANG);
while ( $kid == 0 )
{
   sleep(1);
   $time ++;
   $kid = waitpid($pid, WNOHANG);
   print "Waited $time sec, result $kid\n";
   if ($timeout > 0 && $time > $timeout)
   {
      print "TIMEOUT!\n";
      #Kill process
      kill 9, $pid;
      exit(3);
   }
}

if ( $kid == -1)
{
   print "Process did not exist\n";
   exit(4);
}
print "Process exited with return code $?\n";
exit($?);

Thanks for any help.

+6  A: 

Try changing $cmd from

my $cmd = "very_long_program 1>&2 > out.txt";

to

my $cmd = "exec very_long_program 1>&2 > out.txt";

The exec will tell the shell that gets spawned by perl to replace itself with very_long_program, rather than running very_long_program as a child.

(The reason perl spawns a shell in this case is because $cmd contains the redirect characters - and perl doesn't know how to handle them itself. An alternative way of solving the problem is to do the redirection in perl itself after the fork() but prior to calling exec() - but that's slightly trickier, so try the exec workaround first!)

psmears
Clever, clean and works. Tks.
Edu
+1  A: 

An alternative is to redirect STDOUT and STDERR after the fork and run the command without the redirection:

open(STDOUT, ">", "out.txt") or die "Err: $!";
open(STDERR, ">&STDOUT");
exec("very_long_command");
die "Failed to exec very_long_command: $!";
runrig