package Gruntmaster::Daemon::Format::CPP;

use 5.014000;
use strict;
use warnings;

use BSD::Resource qw/setrlimit RLIMIT_AS RLIMIT_FSIZE/;
use POSIX qw//;
use File::Basename qw/fileparse/;
use Gruntmaster::Daemon::Constants qw/TLE OLE DIED NZX/;
use Log::Log4perl qw/get_logger/;
use Time::HiRes qw/alarm/;
use List::MoreUtils qw/natatime/;
use IPC::Signal qw/sig_name sig_num/;
use IPC::Open3 qw/open3/;
use File::Spec::Functions qw/devnull/;
use Fcntl qw/F_GETFD F_SETFD FD_CLOEXEC/;

our $VERSION = '0.001';

##################################################

sub prepare{
  my $name = $_[0];
  my $basename = fileparse $name, qr/\.[^.]*/;
  get_logger->trace("Preparing file $name...");

  open my $devnull, devnull;
  open my $errors, '>compile-error';
  my $ret = open3 $devnull, $errors, $errors, 'g++', '-o', $basename, $name;
  local $SIG{ALRM} = sub {kill KILL => $ret};
  alarm 5;
  wait;
  close $devnull;
  close $errors;
  die 'Compile error' if $?
}

sub run{
  my ($name, %args) = @_;
  my $basename = fileparse $name, qr/\.[^.]*/;
  my $ret = fork // die 'Cannot fork';
  if ($ret) {
	my $tle;
	local $SIG{ALRM} = sub { kill KILL => $ret; $tle = 1};
	alarm $args{timeout} if exists $args{timeout};
	wait;
	alarm 0;
	my $sig = $? & 127;
	my $signame = sig_name $sig;
	die [TLE, "Time Limit Exceeded"] if $tle;
	die [OLE, 'Output Limit Exceeded'] if $sig && $signame eq 'XFSZ';
	die [DIED, "Crash (SIG$signame)"] if $sig;
	die [NZX, "Non-zero exit status: " . ($? >> 8)] if $?;
  } else {
	$^F = 50;
	POSIX::close $_ for 0, 1, 3 .. $^F;
	my @fds = exists $args{fds} ? @{$args{fds}} : ();
	get_logger->trace("Running $basename with fds ". join ' ', @fds);
	my $it = natatime 2, @fds;
	while (my ($fd, $file) = $it->()) {
	  open my $fh, $file or die $!;
	  my $oldfd = fileno $fh;
	  if ($oldfd != $fd) {
		POSIX::dup2 $oldfd, $fd or die $!;
		POSIX::close $oldfd or die $!;
	  }
	}
#	POSIX::close 2;
	setrlimit RLIMIT_AS, $args{mlimit}, $args{mlimit} if exists $args{mlimit};
	setrlimit RLIMIT_FSIZE, $args{olimit}, $args{olimit} if exists $args{olimit};
	exec "./$basename", exists $args{args} ? @{$args{args}} : ();
  }
}

1
