X-Git-Url: http://git.ieval.ro/?p=gruntmaster-daemon.git;a=blobdiff_plain;f=gruntmaster-exec;h=b6577f4649206fee2e1cd108b6ec62cce581d8f1;hp=1bb57583633f53402c9e83cad2ceeb256f2f4c16;hb=5738d1dcc0e4af406b1bc14c23af9c6e53ba259c;hpb=69c2540886d549bcd11b375cdaeb83ec4646f619 diff --git a/gruntmaster-exec b/gruntmaster-exec index 1bb5758..b6577f4 100755 --- a/gruntmaster-exec +++ b/gruntmaster-exec @@ -1,20 +1,102 @@ -#!/usr/bin/perl -w +#!/usr/bin/perl use v5.14; use strict; - -use BSD::Resource qw/setrlimit RLIMIT_AS RLIMIT_FSIZE/; +use warnings; + +use constant +{ + # Accepted + AC => 0, + + # Internal server error + ERR => -1, + + # All other errors + WA => 1, + NZX => 2, + TLE => 3, + OLE => 4, + DIED => 5, + REJ => 10, +}; + +use BSD::Resource qw/setrlimit RLIMIT_AS RLIMIT_FSIZE RLIMIT_NPROC/; +use IPC::Signal qw/sig_name sig_num/; use sigtrap qw/XFSZ/; -################################################## - -my ($mlimit, $olimit, @args) = @ARGV; - -setrlimit RLIMIT_AS, $mlimit, $mlimit or die $! if $mlimit; -setrlimit RLIMIT_FSIZE, $olimit, $olimit or die $! if $olimit; - -%ENV = (ONLINE_JUDGE => 1, PATH => $ENV{PATH}, HOME => $ENV{PATH}); -exec @args; - +use Getopt::Long; +use POSIX qw//; +use Text::ParseWords qw/shellwords/; +use Time::HiRes qw/alarm/; + +my (@fds, $timeout, $mlimit, $olimit, $sudo, $keep_stderr); + +GetOptions( + "fd=s" => \@fds, + "timeout=f" => \$timeout, + "mlimit=i" => \$mlimit, + "olimit=i" => \$olimit, + "keep-stderr!" => \$keep_stderr, + "sudo!" => \$sudo, +); + +my $killuser = $ENV{GRUNTMASTER_KILL_USER}; +my @sudo; +@sudo = (shellwords ($ENV{GRUNTMASTER_SUDO}), '--') if $ENV{GRUNTMASTER_SUDO} && $sudo; +undef $mlimit if @sudo; # sudo wants a lot of address space + +my $ret = fork // die 'Cannot fork'; +if ($ret) { + my $tle; + local $SIG{ALRM} = sub { + if ($killuser) { + system @sudo, 'pkill', '-KILL', '-u', $killuser; + } else { + kill KILL => $ret + } + $tle = 1 + }; + alarm ($timeout || 10); + waitpid $ret, 0; + alarm 0; + if (@sudo) { + $? = $? >> 8; + $? = $? < 128 || $? > 128+32 ? ($? << 8) : $? - 128; + } + my $sig = $? & 127; + my $signame = sig_name $sig; + exit !say TLE, "\nTime Limit Exceeded" if $tle; + exit !say OLE, "\nOutput Limit Exceeded" if $sig && $signame eq 'XFSZ'; + exit !say DIED, "\nCrash (SIG$signame)" if $sig && ($signame ne 'PIPE' || $ARGV[0] !~ /prog/); + exit !say NZX, "\nNon-zero exit status: " . ($? >> 8) if $? >> 8; + exit !say AC, "\nAll OK"; +} else { + $^F = 50; + POSIX::close 2 unless $keep_stderr; + POSIX::close $_ for 0, 1, 3 .. $^F; + for my $fdstring (@fds) { + my ($fd, $file) = split ' ', $fdstring, 2; + open my $fh, $file or die $!; + my $oldfd = fileno $fh; + if ($oldfd != $fd) { + POSIX::dup2 $oldfd, $fd or die $!; + POSIX::close $oldfd or die $!; + } + } + my $nproc = $killuser ? 15 : 1; + my $debug = $ENV{TEST_VERBOSE}; + %ENV = (ONLINE_JUDGE => 1, PATH => $ENV{PATH}, HOME => $ENV{HOME}); + setrlimit RLIMIT_AS, $mlimit, $mlimit or die $! if $mlimit; + setrlimit RLIMIT_FSIZE, $olimit, $olimit or die $! if $olimit; + setrlimit RLIMIT_NPROC, $nproc, $nproc or die $! if $sudo; + open my $adj, '>', '/proc/self/oom_score_adj'; + print $adj 900; + close $adj; + unshift @ARGV, @sudo; + say STDERR "Executing: ", join ' ', map { "'$_'" } @ARGV if $debug; + exec @ARGV; +} + +1; __END__ =encoding utf-8