#!/usr/local/bin/perl
#
# $Id: rperl.pl,v 0.3 2009/03/20 14:03:28 dankogai Exp $
#
use strict;
use warnings;
use FreeBSD::i386::Ptrace;
use FreeBSD::i386::Ptrace::Syscall;
use File::Temp;
use BSD::Resource;
setrlimit( RLIMIT_CPU,   1, 1 );
setrlimit( RLIMIT_CORE,  0, 0 );

our $DEBUG = 0;
my @banned = qw{
ptrace fork vfork rfork bind listen accept
sleep nanosleep
};
my %banned = map { $_ => 1 } @banned;
my $timeout = 1;

my $src    = slurp();

my $pfh = File::Temp->new() or die $!;
$pfh->print($src);
$pfh->close;

my $coh = File::Temp->new() or die $!;
$coh->autoflush(1);
my $ceh = File::Temp->new() or die $!;
$ceh->autoflush(1);

defined( my $pid = fork() ) or die "fork failed:$!";

if ( $pid == 0 ) {    # son
    no warnings;
    close STDIN;
    open STDOUT, '>&', $coh;
    open STDERR, '>&', $ceh;
    # showtime!
    pt_trace_me;
    exec qw/perl -Tw/, $pfh->filename;
}
else {                # mother
    wait;             # for exec;
    eval {
        local $SIG{ALRM} = sub { die "timed out\n" };    # NB: \n required
        alarm $timeout;
        my $count = 0;    # odd on enter, even on leave
	while ( pt_to_sce($pid) == 0 ) {
            last if wait == -1;
            my $call = pt_getcall($pid);
	    my $name = $SYS{$call} || 'unknown';
            next if !$banned{ $name };
	    #pt_kill($pid);
	    ptrace(PT_CONTINUE, $pid, 0, 9);
	    die "SYS_$SYS{$call} banned.\n";
        }
        alarm 0;
    };
    if ($@) {
        print "# $pid killed: $@";
    }
    my $cout = slurp($coh->filename);
    my $cerr = slurp($ceh->filename);
    print "# stdout\n", $cout, "\n", "# stderr\n", $cerr, "\n";
}

sub slurp {
    my $ret;
    local $/;
    if (@_) {
        my $fn = shift;
        open my $fh, "<", $fn or die "$fn:$!";
        $ret = <$fh>;
        close $fh;
    }
    else {
        $ret = <>;
    }
    $ret;
}
