package Gruntmaster::Daemon::Format;

use 5.014000;
use strict;
use warnings;
use parent qw/Exporter/;

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';
our @EXPORT_OK = qw/mkrun/;

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

sub command_and_args{
	my ($format, $basename) = @_;
	return "./$basename" if $format eq 'CPP';
	die
}

sub mkrun{
	my $format = shift;
	sub{
		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 {
			my @fds = exists $args{fds} ? @{$args{fds}} : ();
			$^F = 50;
			POSIX::close $_ for 0 .. $^F;
			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 $!;
				}
			}
			exec 'gruntmaster-exec', $args{mlimit} // 0, $args{olimit} // 0, command_and_args($format, $basename), exists $args{args} ? @{$args{args}} : ();
		}
	}
}

1
