Add Julia
[gruntmaster-daemon.git] / lib / Gruntmaster / Daemon.pm
CommitLineData
5c5cd38a
MG
1package Gruntmaster::Daemon;
2
3use 5.014000;
4use strict;
5use warnings;
6
3fa65372 7our $VERSION = '5999.000_005';
5c5cd38a
MG
8
9use Gruntmaster::Daemon::Constants qw/ERR/;
fdb99417 10use Gruntmaster::Daemon::Format qw/prepare_files stopvms/;
5c5cd38a 11
ac964998 12use File::Slurp qw/read_file/;
ab436d78 13use File::Temp qw/tempdir/;
be3e5b32 14use JSON qw/decode_json encode_json/;
ab436d78 15use Sys::Hostname qw/hostname/;
5c5cd38a
MG
16use Time::HiRes qw/time/;
17use Try::Tiny;
5c5cd38a
MG
18use Log::Log4perl qw/get_logger/;
19
6bf57d23
MG
20use constant FORMAT_EXTENSION => {
21 C => 'c',
22 CPP => 'cpp',
5a4392d5
MG
23 GCCGO => 'go',
24 GOLANG => 'go',
f8b954a9 25 GOLFSCRIPT => 'gs',
5a4392d5 26 HASKELL => 'hs',
6bf57d23 27 JAVA => 'java',
6d1f2d94
S
28 JULIA => 'jl',
29 MONO => 'cs',
6bf57d23
MG
30 PASCAL => 'pas',
31 PERL => 'pl',
32 PYTHON => 'py',
c4d90e6f 33 RUBY => 'rb',
27be8560 34 RUST => 'rs',
c88da74d 35 SBCL => 'l',
6bf57d23 36};
adb44605 37
5c5cd38a
MG
38##################################################
39
b4ac22ff 40sub safe_can {
a722431b 41 my ($type, $sub, $name) = @_;
5c5cd38a 42
fcfcfb93 43 get_logger->logdie("Invalid \l$type: '$name'") unless $name =~ /^\w+$/s;
a722431b
MG
44 my $pkg = __PACKAGE__ . "::${type}::${name}";
45 eval "require $pkg" or get_logger->warn("Error while requiring $pkg: $@");
b4ac22ff 46 $pkg->can($sub) or get_logger->logdie("No such \l$type: '$name'");
5c5cd38a
MG
47}
48
ab436d78 49sub process{
867a9dad 50 my ($meta) = @_;
a722431b
MG
51
52 my @results;
53 my @full_results = ();
a722431b
MG
54 our $errors = '';
55 try {
a722431b
MG
56 prepare_files $meta;
57 chomp $errors;
58
99d37110 59 my ($files, $generator, $runner, $judge, $testcnt) = map { $meta->{$_} or die "Required parameter missing: $_\n"} qw/files generator runner judge testcnt/;
a722431b
MG
60
61 $generator = safe_can Generator => generate => $generator;
62 $runner = safe_can Runner => run => $runner;
63 $judge = safe_can Judge => judge => $judge;
64
65 for my $test (1 .. $testcnt) {
66 my $start_time = time;
67 my $result;
ac964998 68 unlink 'result';
a722431b
MG
69 try {
70 $generator->($test, $meta);
71 $result = $runner->($test, $meta);
72 } catch {
73 $result = $_;
74 unless (ref $result) {
75 chomp $result;
76 $result = [ERR, $result];
77 }
78 };
79
80 if (ref $result) {
ac964998
MG
81 my $result_text = $result->[1];
82 $result_text .= ': ' . read_file 'result' if -s 'result';
83 get_logger->trace("Test $test result is " . $result_text);
84 push @full_results, {id => $test, result => $result->[0], result_text => $result_text, time => time - $start_time}
a722431b
MG
85 } else {
86 get_logger->trace("Test $test result is $result");
87 push @full_results, {id => $test, result => 0, result_text => $result, time => time - $start_time}
88 }
89 push @results, $result;
90 last if $meta->{judge} eq 'Absolute' && ref $result
5c5cd38a 91 }
5c5cd38a 92
a722431b
MG
93 my %results = $judge->(@results);
94 $meta->{$_} = $results{$_} for keys %results;
7be5f054 95 $meta->{results} = \@full_results
a722431b 96 } catch {
99d37110 97 s/(.*) at .*/$1/s;
a722431b
MG
98 chomp;
99 $meta->{result} = -1;
100 $meta->{result_text} = $_;
101 };
fdb99417 102 stopvms;
867a9dad 103 $meta->{errors} = $errors;
a722431b 104
99d37110 105 get_logger->info('Job result: ' . $meta->{result_text});
867a9dad
MG
106}
107
108sub process_job {
109 my ($job) = @_;
fb7e1b7c 110 my $pb = db()->select('problems', '*', {id => $job->{problem}})->hash;
867a9dad 111 my $meta = {
6101c7ad 112 problem => $job->{problem},
867a9dad
MG
113 files => {
114 prog => {
6101c7ad
MG
115 name => 'prog.' . $job->{extension},
116 format => $job->{format},
117 content => $job->{source},
867a9dad
MG
118 },
119 },
6101c7ad 120 map { $_ => $pb->{$_} } qw/generator runner judge precnt testcnt timeout olimit/
867a9dad 121 };
6101c7ad
MG
122 my $timeout_override = db()->query('SELECT timeout FROM limits WHERE problem=? AND format=?', $job->{problem}, $job->{format})->list;
123 $meta->{timeout} = $timeout_override if defined $timeout_override;
124 $meta->{tests} = decode_json $pb->{tests} if $meta->{runner} eq 'File';
125 $job->{contest} &&= contest_entry($job->{contest});
7e81b08a 126 delete $meta->{precnt} unless $job->{contest} && $job->{contest}{started} && !$job->{contest}{finished}; ## no critic (ProhibitNegativeExpressionsInUnlessAndUntilConditions)
da822e3c 127 $meta->{testcnt} = $meta->{precnt} if $meta->{precnt};
867a9dad
MG
128
129 $meta->{files}{ver} = {
6101c7ad
MG
130 name => 'ver.' . FORMAT_EXTENSION->{$pb->{verformat}},
131 format => $pb->{verformat},
132 content => $pb->{versource},
133 } if $pb->{verformat};
867a9dad
MG
134
135 process $meta;
136
da822e3c 137 $meta->{result_text} .= ' (pretests)' if $meta->{precnt};
6101c7ad
MG
138 finish_job($job, $job->{private} || $pb->{private},
139 result => $meta->{result},
140 result_text => $meta->{result_text},
141 ($meta->{results} ? (results => encode_json $meta->{results}) : ()),
142 $meta->{errors} ? (errors => $meta->{errors}) : ());
ab436d78
MG
143}
144
145sub got_job{
be3e5b32 146 my $job = $_[0];
6101c7ad
MG
147 my $id = $job->{id};
148 get_logger->debug("Processing job $id...");
149 process_job $job;
150 get_logger->debug("Job $id done");
5c5cd38a
MG
151}
152
6101c7ad
MG
153my $daemon = hostname . ":$$";
154
5c5cd38a 155sub run{
55e61ae8 156 require Gruntmaster::Data;
6101c7ad 157 Gruntmaster::Data->import;
074cbf76 158 dbinit($ENV{GRUNTMASTER_DSN} // 'dbi:Pg:');
a722431b 159 Log::Log4perl->init('/etc/gruntmasterd/gruntmasterd-log.conf');
6101c7ad 160 get_logger->info("gruntmasterd $VERSION started ($daemon)");
01d59515
MG
161 my $dir = tempdir 'gruntmasterd.XXXX', CLEANUP => 1, TMPDIR => 1;
162 chmod 0711, $dir;
163 chdir $dir;
be3e5b32 164 while (1) {
6101c7ad 165 my $job = take_job($daemon);
be3e5b32
MG
166 got_job $job if defined $job;
167 sleep 2 unless defined $job;
168 }
5c5cd38a
MG
169}
170
1711;
172__END__
5c5cd38a
MG
173
174=head1 NAME
175
bc372959 176Gruntmaster::Daemon - Gruntmaster 6000 Online Judge -- daemon
5c5cd38a
MG
177
178=head1 SYNOPSIS
179
180 use Gruntmaster::Daemon;
bc372959 181 Gruntmaster::Daemon->run;
5c5cd38a
MG
182
183=head1 DESCRIPTION
184
bc372959 185Gruntmaster::Daemon is the daemon component of the Gruntmaster 6000 online judge.
5c5cd38a
MG
186
187=head1 AUTHOR
188
bc372959 189Marius Gavrilescu E<lt>marius@ieval.roE<gt>
5c5cd38a
MG
190
191=head1 COPYRIGHT AND LICENSE
192
bc372959 193Copyright (C) 2014 by Marius Gavrilescu
5c5cd38a 194
bc372959
MG
195This library is free software: you can redistribute it and/or modify
196it under the terms of the GNU Affero General Public License as published by
197the Free Software Foundation, either version 3 of the License, or
198(at your option) any later version.
5c5cd38a
MG
199
200
201=cut
This page took 0.034918 seconds and 4 git commands to generate.