/** * Internal error reporting (back from judgehost) */ function internal_error_POST($args) { global $DB; checkargs($args, array('description', 'judgehostlog', 'disabled')); global $cdatas, $api; // group together duplicate internal errors // note that it may be good to be able to ignore fields here, e.g. judgingid with compile errors $errorid = $DB->q('MAYBEVALUE SELECT errorid FROM internal_error WHERE description=%s AND disabled=%s AND status=%s' . (isset($args['cid']) ? ' AND cid=%i' : '%_'), $args['description'], $args['disabled'], 'open', $args['cid']); if (isset($errorid)) { // FIXME: in some cases it makes sense to extend the known information, e.g. the judgehostlog return $errorid; } $errorid = $DB->q('RETURNID INSERT INTO internal_error (judgingid, cid, description, judgehostlog, time, disabled) VALUES (%i, %i, %s, %s, %i, %s)', $args['judgingid'], $args['cid'], $args['description'], $args['judgehostlog'], now(), $args['disabled']); $disabled = dj_json_decode($args['disabled']); // disable what needs to be disabled set_internal_error($disabled, $args['cid'], 0); if (in_array($disabled['kind'], array('problem', 'language'))) { // give back judging if we have to $submitid = $DB->q('VALUE SELECT submitid FROM judging WHERE judgingid = %i', $args['judgingid']); give_back_judging($args['judgingid'], $submitid); } return $errorid; }
function judge($row) { global $EXITCODES, $myhost, $options, $workdirpath; // Set configuration variables for called programs putenv('USE_CHROOT=' . (USE_CHROOT ? '1' : '')); putenv('SCRIPTTIMELIMIT=' . dbconfig_get_rest('script_timelimit')); putenv('SCRIPTMEMLIMIT=' . dbconfig_get_rest('script_memory_limit')); putenv('SCRIPTFILELIMIT=' . dbconfig_get_rest('script_filesize_limit')); putenv('MEMLIMIT=' . $row['memlimit']); putenv('FILELIMIT=' . $row['outputlimit']); putenv('PROCLIMIT=' . dbconfig_get_rest('process_limit')); $cpuset_opt = ""; if (isset($options['daemonid'])) { $cpuset_opt = "-n {$options['daemonid']}"; } // create workdir for judging $workdir = "{$workdirpath}/c{$row['cid']}-s{$row['submitid']}-j{$row['judgingid']}"; logmsg(LOG_INFO, "Working directory: {$workdir}"); // If a database gets reset without removing the judging // directories, we might hit an old directory: rename it. if (file_exists($workdir)) { $oldworkdir = $workdir . '-old-' . getmypid() . '-' . strftime('%Y-%m-%d_%H:%M'); if (!rename($workdir, $oldworkdir)) { error("Could not rename stale working directory to '{$oldworkdir}'"); } @chmod($oldworkdir, 0700); warning("Found stale working directory; renamed to '{$oldworkdir}'"); } system("mkdir -p '{$workdir}/compile'", $retval); if ($retval != 0) { error("Could not create '{$workdir}/compile'"); } // Make sure the workdir is accessible for the domjudge-run user. // Will be revoked again after this run finished. chmod($workdir, 0755); if (!chdir($workdir)) { error("Could not chdir to '{$workdir}'"); } // Get the source code from the DB and store in local file(s) $sources = request('submission_files', 'GET', 'id=' . urlencode($row['submitid'])); $sources = dj_json_decode($sources); $files = array(); foreach ($sources as $source) { $srcfile = "{$workdir}/compile/{$source['filename']}"; $files[] = "'{$source['filename']}'"; if (file_put_contents($srcfile, base64_decode($source['content'])) === FALSE) { error("Could not create {$srcfile}"); } } if (empty($row['compile_script'])) { error("No compile script specified for language " . $row['langid'] . "."); } $execrunpath = fetch_executable($workdirpath, $row['compile_script'], $row['compile_script_md5sum']); // Compile the program. system(LIBJUDGEDIR . "/compile.sh {$cpuset_opt} '{$execrunpath}' '{$workdir}' " . implode(' ', $files), $retval); // what does the exitcode mean? if (!isset($EXITCODES[$retval])) { alert('error'); error("Unknown exitcode from compile.sh for s{$row['submitid']}: {$retval}"); } $compile_success = $EXITCODES[$retval] != 'compiler-error'; // pop the compilation result back into the judging table request('judgings/' . urlencode($row['judgingid']), 'PUT', 'judgehost=' . urlencode($myhost) . '&compile_success=' . $compile_success . '&output_compile=' . rest_encode_file($workdir . '/compile.out')); // compile error: our job here is done if (!$compile_success) { // revoke readablity for domjudge-run user to this workdir chmod($workdir, 0700); logmsg(LOG_NOTICE, "Judging s{$row['submitid']}/j{$row['judgingid']}: compile error"); return; } // Optionally create chroot environment if (USE_CHROOT && CHROOT_SCRIPT) { logmsg(LOG_INFO, "executing chroot script: '" . CHROOT_SCRIPT . " start'"); system(LIBJUDGEDIR . '/' . CHROOT_SCRIPT . ' start', $retval); if ($retval != 0) { error("chroot script exited with exitcode {$retval}"); } } $totalcases = 0; while (TRUE) { // get the next testcase $testcase = request('testcases', 'GET', 'judgingid=' . urlencode($row['judgingid'])); $tc = dj_json_decode($testcase); // empty means: no more testcases for this judging. if (empty($tc)) { break; } $totalcases++; logmsg(LOG_DEBUG, "Running testcase {$tc['rank']}..."); $testcasedir = $workdir . "/testcase" . sprintf('%03d', $tc['rank']); // Get both in- and output files, only if we didn't have them already. $tcfile = array(); $fetched = array(); foreach (array('input', 'output') as $inout) { $tcfile[$inout] = "{$workdirpath}/testcase/testcase.{$tc['probid']}.{$tc['rank']}." . $tc['md5sum_' . $inout] . "." . substr($inout, 0, -3); if (!file_exists($tcfile[$inout])) { $content = request('testcase_files', 'GET', 'testcaseid=' . urlencode($tc['testcaseid']) . '&' . $inout); $content = base64_decode(dj_json_decode($content)); if (file_put_contents($tcfile[$inout] . ".new", $content) === FALSE) { error("Could not create {$tcfile[$inout]}.new"); } unset($content); if (md5_file("{$tcfile[$inout]}.new") === $tc['md5sum_' . $inout]) { rename("{$tcfile[$inout]}.new", $tcfile[$inout]); } else { error("File corrupted during download."); } $fetched[] = $inout; } // sanity check (NOTE: performance impact is negligible with 5 // testcases and total 3.3 MB of data) if (md5_file($tcfile[$inout]) !== $tc['md5sum_' . $inout]) { error("File corrupted: md5sum mismatch: " . $tcfile[$inout]); } } // Only log downloading input and/or output testdata once. if (count($fetched) > 0) { logmsg(LOG_INFO, "Fetched new " . implode($fetched, ',') . " testcase {$tc['rank']} for problem p{$tc['probid']}"); } // Copy program with all possible additional files to testcase // dir. Use hardlinks to preserve space with big executables. $programdir = $testcasedir . '/execdir'; system("mkdir -p '{$programdir}'", $retval); if ($retval != 0) { error("Could not create directory '{$programdir}'"); } system("cp -PR '{$workdir}'/compile/* '{$programdir}'", $retval); if ($retval != 0) { error("Could not copy program to '{$programdir}'"); } // do the actual test-run $hardtimelimit = $row['maxruntime'] + overshoot_time($row['maxruntime'], dbconfig_get_rest('timelimit_overshoot')); $compare_runpath = fetch_executable($workdirpath, $row['compare'], $row['compare_md5sum']); $run_runpath = fetch_executable($workdirpath, $row['run'], $row['run_md5sum']); system(LIBJUDGEDIR . "/testcase_run.sh {$cpuset_opt} {$tcfile['input']} {$tcfile['output']} " . "{$row['maxruntime']}:{$hardtimelimit} '{$testcasedir}' " . "'{$run_runpath}' '{$compare_runpath}' '{$row['compare_args']}'", $retval); // what does the exitcode mean? if (!isset($EXITCODES[$retval])) { alert('error'); error("Unknown exitcode from testcase_run.sh for s{$row['submitid']}, " . "testcase {$tc['rank']}: {$retval}"); } $result = $EXITCODES[$retval]; // Try to read metadata from file $runtime = NULL; if (is_readable($testcasedir . '/program.meta')) { $metadata = spyc_load_file($testcasedir . '/program.meta'); if (isset($metadata['time-used'])) { $runtime = @$metadata[$metadata['time-used']]; } } request('judging_runs', 'POST', 'judgingid=' . urlencode($row['judgingid']) . '&testcaseid=' . urlencode($tc['testcaseid']) . '&runresult=' . urlencode($result) . '&runtime=' . urlencode($runtime) . '&judgehost=' . urlencode($myhost) . '&output_run=' . rest_encode_file($testcasedir . '/program.out', FALSE) . '&output_error=' . rest_encode_file($testcasedir . '/program.err') . '&output_system=' . rest_encode_file($testcasedir . '/system.out') . '&output_diff=' . rest_encode_file($testcasedir . '/feedback/judgemessage.txt')); logmsg(LOG_DEBUG, "Testcase {$tc['rank']} done, result: " . $result); } // end: for each testcase // revoke readablity for domjudge-run user to this workdir chmod($workdir, 0700); // Optionally destroy chroot environment if (USE_CHROOT && CHROOT_SCRIPT) { logmsg(LOG_INFO, "executing chroot script: '" . CHROOT_SCRIPT . " stop'"); system(LIBJUDGEDIR . '/' . CHROOT_SCRIPT . ' stop', $retval); if ($retval != 0) { error("chroot script exited with exitcode {$retval}"); } } // Sanity check: need to have had at least one testcase if ($totalcases == 0) { logmsg(LOG_WARNING, "No testcases judged for s{$row['submitid']}/j{$row['judgingid']}!"); } // done! logmsg(LOG_NOTICE, "Judging s{$row['submitid']}/j{$row['judgingid']} finished"); }
* Part of the DOMjudge Programming Contest Jury System and licenced * under the GNU GPL. See README and COPYING for details. */ require 'init.php'; $id = getRequestID(); $refresh = array('after' => 15, 'url' => 'internal_error.php?id=' . urlencode($id)); $title = 'Internal Error e' . @$id; if (!$id) { error("Missing or invalid internal error id"); } $edata = $DB->q('TUPLE SELECT * FROM internal_error WHERE errorid=%i', $id); if (!$edata) { error("Missing internal error data for e" . $id); } $disabled = dj_json_decode($edata['disabled']); if (isset($_REQUEST['ignore']) || isset($_REQUEST['resolve'])) { if (isset($_REQUEST['ignore'])) { $status = "ignored"; } if (isset($_REQUEST['resolve'])) { $status = "resolved"; } $DB->q('UPDATE internal_error SET status=%s WHERE errorid=%i', $status, $id); if ($status == 'resolved') { set_internal_error($disabled, $edata['cid'], 1); } auditlog('internal_error', $id, 'internal error: ' + $status, ''); header('Location: internal_error.php?id=' . urlencode($id)); } require LIBWWWDIR . '/header.php';