#!/usr/bin/perl

$timout=60;
if ($ARGV[0] eq "-t") {
        $timout=$ARGV[1];
        shift;
        shift;
}

# set timeout after duration
$SIG{"ALRM"} = \&sigalrm;
alarm($timout);

# set interrupt catching
$prevint=$SIG{"INT"};
$prevterm=$SIG{"TERM"};
$SIG{"INT"} = \&sigint;
$SIG{"TERM"} = \&sigterm;

# fork for sub process
$cpid=fork();
unless (defined $cpid) {
        print STDERR "fork: ", $!, "\n";
        exit 1;
}
# child runs job
if ($cpid == 0) {
        exec(@ARGV);
        print STDERR "exec: ", $!, "\n";
        exit 1;
}
# parent wait to complete
wait;
exit 0;

sub sigalrm() {
	&killpstree();
        exit 0;
}

sub sigint() {
	# delay - as processes need time to process SIGINT first
	sleep 1;
	&killpstree();
	$SIG{"INT"}=$prevint;
	kill 2, $$;
        exit 0;
}

sub sigterm() {
	&killpstree();
	$SIG{"TERM"}=$prevterm;
	kill 15, $$;
        exit 0;
}

sub killpstree() {
        # run ps to work out process tree
        open(PS, "ps -el|");
        while ($l=<PS>) {
                $l =~ s/\s+/ /g;
                ($pflg, $pstat, $puid, $pid, $ppid, @prest)=split(" ", $l);
                # store away each PIDs parent
                $ps{$pid}=$ppid;
        }
        close PS;
        # set list of PIDs to kill
        @pall=($cpid);
        # add PIDs whose parent is cpid
        &padd($cpid);
        # kill them all
        kill 15, @pall;
}

sub padd() {
        (local $p)=@_;
        (local @pl);

        @pl=();
        foreach $k (keys %ps) {
                # find PIDs whose parent is p
                if ($ps{$k} == $p) {
                        # add to global & short list
                        push @pall, $k;
                        push @pl, $k;
                }
        }
        foreach $np (@pl) {
                # recurse on short list
                &padd($np);
        }
}
