judge: pass envirent vars to checker

This commit is contained in:
undefined committed 2022-02-08 19:10:18 +08:00
1 parent 9158550139
commit abec5fd3ec
11 files changed
+54 -47

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@hydrooj/hydrojudge", "name": "@hydrooj/hydrojudge",
"bin": "bin/hydrojudge.js", "bin": "bin/hydrojudge.js",
"version": "2.12.17", "version": "2.13.0",
"main": "package.json", "main": "package.json",
"author": "undefined <i@undefined.moe>", "author": "undefined <i@undefined.moe>",
"repository": "https://github.com/hydro-dev/Hydro.git", "repository": "https://github.com/hydro-dev/Hydro.git",
+2 -22
View File
@@ -3,36 +3,16 @@ import { findFileSync } from '@hydrooj/utils/lib/utils';
import checkers from './checkers'; import checkers from './checkers';
import compile from './compile'; import compile from './compile';
import { SystemError } from './error'; import { SystemError } from './error';
import { Execute } from './interface'; import { CheckConfig, Execute } from './interface';
import { CopyInFile } from './sandbox/interface';
import { parseFilename } from './utils'; import { parseFilename } from './utils';
const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h'); const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h');
interface CheckConfig {
checker_type: string;
stdin: CopyInFile,
stdout: CopyInFile,
user_stdout: CopyInFile,
user_stderr: CopyInFile,
copyIn?: Record<string, CopyInFile>,
score: number,
detail: boolean,
}
export async function check(config: CheckConfig): Promise<[number, number, string]> { export async function check(config: CheckConfig): Promise<[number, number, string]> {
if (!checkers[config.checker_type]) throw new SystemError('Unknown checker type {0}', [config.checker_type]); if (!checkers[config.checker_type]) throw new SystemError('Unknown checker type {0}', [config.checker_type]);
const { const {
code, status, score, message, code, status, score, message,
} = await checkers[config.checker_type]({ } = await checkers[config.checker_type](config);
input: config.stdin,
output: config.stdout,
user_stdout: config.user_stdout,
user_stderr: config.user_stderr,
score: config.score,
copyIn: config.copyIn || {},
detail: config.detail,
});
if (code) throw new SystemError('Checker returned {0}.', [code]); if (code) throw new SystemError('Checker returned {0}.', [code]);
return [status, score, message]; return [status, score, message];
} }
+3 -18
View File
@@ -1,27 +1,10 @@
/* eslint-disable no-template-curly-in-string */ /* eslint-disable no-template-curly-in-string */
import { STATUS } from '@hydrooj/utils/lib/status'; import { STATUS } from '@hydrooj/utils/lib/status';
import { SystemError } from './error'; import { SystemError } from './error';
import { CheckConfig, CheckResult } from './interface';
import { run } from './sandbox'; import { run } from './sandbox';
import { CopyInFile } from './sandbox/interface';
import { parse } from './testlib'; import { parse } from './testlib';
interface CheckConfig {
input: CopyInFile,
output: CopyInFile,
user_stdout: CopyInFile,
user_stderr: CopyInFile,
copyIn: Record<string, CopyInFile>,
score: number,
detail: boolean,
}
interface CheckResult {
status: number,
score: number,
message: string,
code?: number,
}
type Checker = (config: CheckConfig) => Promise<CheckResult>; type Checker = (config: CheckConfig) => Promise<CheckResult>;
const checkers: Record<string, Checker> = { const checkers: Record<string, Checker> = {
@@ -129,6 +112,7 @@ const checkers: Record<string, Checker> = {
...config.copyIn, ...config.copyIn,
}, },
copyOut: ['score', 'message'], copyOut: ['score', 'message'],
env: config.env,
}); });
const { message } = files; const { message } = files;
const score = parseInt(files.score, 10); const score = parseInt(files.score, 10);
@@ -196,6 +180,7 @@ const checkers: Record<string, Checker> = {
answer: config.output, answer: config.output,
...config.copyIn, ...config.copyIn,
}, },
env: config.env,
}); });
if (status === STATUS.STATUS_SYSTEM_ERROR) { if (status === STATUS.STATUS_SYSTEM_ERROR) {
return { return {
+8
View File
@@ -38,6 +38,7 @@ class JudgeTask {
data: any[]; data: any[];
folder: string; folder: string;
config: any; config: any;
env: Record<string, string>;
getLang: (name: string) => LangConfig; getLang: (name: string) => LangConfig;
constructor(session: Hydro, request, ws: WebSocket) { constructor(session: Hydro, request, ws: WebSocket) {
@@ -63,6 +64,13 @@ class JudgeTask {
this.source = this.request.source; this.source = this.request.source;
this.tmpdir = path.resolve(getConfig('tmp_dir'), this.host, this.rid); this.tmpdir = path.resolve(getConfig('tmp_dir'), this.host, this.rid);
this.clean = []; this.clean = [];
this.env = {
HYDRO_DOMAIN: this.request.domainId.toString(),
HYDRO_RECORD: this.rid,
HYDRO_LANG: this.lang,
HYDRO_USER: this.request.uid.toString(),
HYDRO_CONTEST: this.request.tid?.toString() || '',
};
await Lock.acquire(`${this.host}/${this.source}/${this.rid}`); await Lock.acquire(`${this.host}/${this.source}/${this.rid}`);
fs.ensureDirSync(this.tmpdir); fs.ensureDirSync(this.tmpdir);
tmpfs.mount(this.tmpdir, getConfig('tmpfs_size')); tmpfs.mount(this.tmpdir, getConfig('tmpfs_size'));
+21
View File
@@ -1,12 +1,33 @@
import { CopyInFile } from './sandbox/interface'; import { CopyInFile } from './sandbox/interface';
export interface Execute { export interface Execute {
execute: string, execute: string,
clean: Function, clean: Function,
copyIn: Record<string, CopyInFile>, copyIn: Record<string, CopyInFile>,
time?: number, time?: number,
} }
export interface CompileErrorInfo { export interface CompileErrorInfo {
stdout?: string, stdout?: string,
stderr?: string, stderr?: string,
status?: number status?: number
} }
export interface CheckConfig {
checker_type: string;
input: CopyInFile;
output: CopyInFile;
user_stdout: CopyInFile;
user_stderr: CopyInFile;
copyIn: Record<string, CopyInFile>;
score: number;
detail: boolean;
env?: Record<string, string>;
}
export interface CheckResult {
status: number,
score: number,
message: string,
code?: number,
}
+4 -3
View File
@@ -67,14 +67,15 @@ function judgeCase(c: Case, sid: string) {
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED; status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
} else { } else {
[status, score, message] = await check({ [status, score, message] = await check({
copyIn: ctx.checker.copyIn, copyIn: ctx.checker.copyIn || {},
stdin: { src: c.input }, input: { src: c.input },
stdout: { src: c.output }, output: { src: c.output },
user_stdout: stdout, user_stdout: stdout,
user_stderr: stderr, user_stderr: stderr,
checker_type: ctx.config.checker_type, checker_type: ctx.config.checker_type,
score: ctxSubtask.subtask.score, score: ctxSubtask.subtask.score,
detail: ctx.config.detail ?? true, detail: ctx.config.detail ?? true,
env: ctx.env,
}); });
} }
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code) { } else if (status === STATUS.STATUS_RUNTIME_ERROR && code) {
+2 -2
View File
@@ -97,8 +97,8 @@ export const judge = async (ctx: Context) => {
} else { } else {
[status, , message] = await check({ [status, , message] = await check({
copyIn: copyInDir(path.resolve(ctx.tmpdir, 'checker')), copyIn: copyInDir(path.resolve(ctx.tmpdir, 'checker')),
stdin: { src: input }, input: { src: input },
stdout: { src: stdout }, output: { src: stdout },
user_stdout: { src: stdout }, user_stdout: { src: stdout },
user_stderr: { src: stderr }, user_stderr: { src: stderr },
checker_type: ctx.config.checker_type, checker_type: ctx.config.checker_type,
@@ -35,6 +35,7 @@ function judgeCase(c: Case) {
time: ctxSubtask.subtask.time * 2 * ctx.executeInteractor.time, time: ctxSubtask.subtask.time * 2 * ctx.executeInteractor.time,
memory: ctxSubtask.subtask.memory * 2, memory: ctxSubtask.subtask.memory * 2,
copyOut: ['/w/tout?'], copyOut: ['/w/tout?'],
env: ctx.env,
}, },
); );
// TODO handle tout (maybe pass to checker?) // TODO handle tout (maybe pass to checker?)
@@ -32,6 +32,7 @@ export interface JudgeTaskInterface {
input?: string; input?: string;
clean: Function[]; clean: Function[];
config: Config; config: Config;
env?: Record<string, string>;
} }
export interface RuntimeContext { export interface RuntimeContext {
+3 -1
View File
@@ -39,6 +39,7 @@ interface Parameter {
copyOut?: string[]; copyOut?: string[];
copyOutCached?: string[]; copyOutCached?: string[];
cacheStdoutAndStderr?: boolean; cacheStdoutAndStderr?: boolean;
env?: Record<string, string>;
} }
interface SandboxAdaptedResult { interface SandboxAdaptedResult {
@@ -72,6 +73,7 @@ function proc({
processLimit = getConfig('processLimit'), processLimit = getConfig('processLimit'),
stdin = '', copyIn = {}, copyOut = [], copyOutCached = [], stdin = '', copyIn = {}, copyOut = [], copyOutCached = [],
cacheStdoutAndStderr = false, cacheStdoutAndStderr = false,
env = {},
}: Parameter = {}): Cmd { }: Parameter = {}): Cmd {
if (!supportOptional) { if (!supportOptional) {
copyOut = (copyOut as string[]).map((i) => (i.endsWith('?') ? i.substr(0, i.length - 1) : i)); copyOut = (copyOut as string[]).map((i) => (i.endsWith('?') ? i.substr(0, i.length - 1) : i));
@@ -84,7 +86,7 @@ function proc({
} }
return { return {
args: parseArgs(execute), args: parseArgs(execute),
env: getConfig('env').split('\n'), env: [...getConfig('env').split('\n'), ...Object.keys(env).map((i) => `${i}=${env[i].replace(/=/g, '\\=')}`)],
files: [ files: [
stdin ? { src: stdin } : { content: '' }, stdin ? { src: stdin } : { content: '' },
{ name: 'stdout', max: Math.floor(1024 * 1024 * size) }, { name: 'stdout', max: Math.floor(1024 * 1024 * size) },
+8
View File
@@ -150,6 +150,7 @@ async function postInit() {
code: string; code: string;
data: any[]; data: any[];
config: any; config: any;
env: any;
input?: string; input?: string;
next: (data: any, id?: number) => void; next: (data: any, id?: number) => void;
end: (data: any) => void; end: (data: any) => void;
@@ -176,6 +177,13 @@ async function postInit() {
this.source = this.request.source; this.source = this.request.source;
this.config = this.request.config; this.config = this.request.config;
this.input = this.request.input; this.input = this.request.input;
this.env = {
HYDRO_DOMAIN: this.request.domainId.toString(),
HYDRO_RECORD: this.rid,
HYDRO_LANG: this.lang,
HYDRO_USER: this.request.uid.toString(),
HYDRO_CONTEST: this.request.tid?.toString() || '',
};
this.next = getNext(this); this.next = getNext(this);
this.end = getEnd(this.rid); this.end = getEnd(this.rid);
this.tmpdir = path.resolve(getConfig('tmp_dir'), this.rid); this.tmpdir = path.resolve(getConfig('tmp_dir'), this.rid);