From 21ff3ce8f58fc32ffec6d7e36a72f773a7a40788 Mon Sep 17 00:00:00 2001 From: panda Date: Sun, 5 Nov 2023 04:10:11 +0800 Subject: [PATCH] hydrojudge: add cgroup v2 check for judge (#642) Co-authored-by: undefined --- packages/hydrojudge/package.json | 1 + packages/hydrojudge/src/daemon.ts | 3 +++ packages/hydrojudge/src/hosts/builtin.ts | 4 +++- packages/hydrojudge/src/sandbox.ts | 24 +++++++++++++++++++++++ packages/hydrojudge/src/sandbox/client.ts | 3 +++ packages/hydrojudge/src/service.ts | 2 +- 6 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/hydrojudge/package.json b/packages/hydrojudge/package.json index 034ea320..f3072ad5 100644 --- a/packages/hydrojudge/package.json +++ b/packages/hydrojudge/package.json @@ -11,6 +11,7 @@ "mongodb": "^5.9.1", "p-queue": "^7.4.1", "schemastery": "^3.14.0", + "semver": "^7.5.4", "shell-quote": "^1.8.1", "superagent": "^8.1.2", "ws": "^8.14.2" diff --git a/packages/hydrojudge/src/daemon.ts b/packages/hydrojudge/src/daemon.ts index e4e369b5..0433324e 100644 --- a/packages/hydrojudge/src/daemon.ts +++ b/packages/hydrojudge/src/daemon.ts @@ -20,6 +20,7 @@ import { fs } from '@hydrooj/utils'; import { getConfig } from './config'; import HydroHost from './hosts/hydro'; import log from './log'; +import { versionCheck } from './sandbox'; const hosts: Record = {}; let exit = false; @@ -44,6 +45,8 @@ process.on('unhandledRejection', (reason, p) => { }); async function daemon() { + const shouldRun = await versionCheck((msg) => log.error(msg)); + if (!shouldRun) process.exit(1); const _hosts = getConfig('hosts'); const queue = new PQueue({ concurrency: Infinity }); await fs.ensureDir(getConfig('tmp_dir')); diff --git a/packages/hydrojudge/src/hosts/builtin.ts b/packages/hydrojudge/src/hosts/builtin.ts index 463fb7db..eec49759 100644 --- a/packages/hydrojudge/src/hosts/builtin.ts +++ b/packages/hydrojudge/src/hosts/builtin.ts @@ -11,6 +11,7 @@ import { getConfig } from '../config'; import { FormatError, SystemError } from '../error'; import { Context } from '../judge/interface'; import logger from '../log'; +import { versionCheck } from '../sandbox'; import { JudgeTask } from '../task'; const session = { @@ -71,8 +72,9 @@ const session = { }, }; -export async function postInit() { +export async function postInit(ctx) { if (SystemModel.get('hydrojudge.disable')) return; + ctx.check.addChecker('Judge', (_ctx, log, warn, error) => versionCheck(warn, error)); await fs.ensureDir(getConfig('tmp_dir')); const handle = async (t) => { const rdoc = await RecordModel.get(t.domainId, t.rid); diff --git a/packages/hydrojudge/src/sandbox.ts b/packages/hydrojudge/src/sandbox.ts index 13adf9f9..956661d1 100644 --- a/packages/hydrojudge/src/sandbox.ts +++ b/packages/hydrojudge/src/sandbox.ts @@ -1,7 +1,9 @@ import cac from 'cac'; import PQueue from 'p-queue'; +import { gte } from 'semver'; import { ParseEntry } from 'shell-quote'; import { STATUS } from '@hydrooj/utils/lib/status'; +import * as sysinfo from '@hydrooj/utils/lib/sysinfo'; import { getConfig } from './config'; import { FormatError, SystemError } from './error'; import { Logger } from './log'; @@ -216,4 +218,26 @@ export function runQueued(execute: string, params?: Parameter, priority = 0) { return queue.add(() => run(execute, params), { priority }) as Promise; } +export async function versionCheck(reportWarn: (str: string) => void, reportError = reportWarn) { + let sandboxVersion: string; + let sandboxCgroup: number; + try { + const version = await client.version(); + sandboxVersion = version.buildVersion.split('v')[1]; + const config = await client.config(); + sandboxCgroup = config.runnerConfig?.cgroupType || 0; + } catch (e) { + reportError('Your sandbox version is tooooooo low! Please upgrade!'); + return false; + } + const { osinfo } = await sysinfo.get(); + if (sandboxCgroup === 2) { + const kernelVersion = osinfo.kernel.split('-')[0]; + if (!(gte(kernelVersion, '5.19.0') && gte(sandboxVersion, '1.6.10'))) { + reportWarn('You are using cgroup v2 without kernel 5.19+. This could result in inaccurate memory usage measurements.'); + } + } + return true; +} + export * from './sandbox/interface'; diff --git a/packages/hydrojudge/src/sandbox/client.ts b/packages/hydrojudge/src/sandbox/client.ts index d7f7b5f0..54b6bbd0 100644 --- a/packages/hydrojudge/src/sandbox/client.ts +++ b/packages/hydrojudge/src/sandbox/client.ts @@ -20,6 +20,9 @@ const client = new Proxy({ version(): Promise { return superagent.get(`${url}/version`).then((res) => res.body); }, + config(): Promise> { + return superagent.get(`${url}/config`).then((res) => res.body); + }, }, { get(self, key) { url = getConfig('sandbox_host'); diff --git a/packages/hydrojudge/src/service.ts b/packages/hydrojudge/src/service.ts index 40b23b27..32f99332 100644 --- a/packages/hydrojudge/src/service.ts +++ b/packages/hydrojudge/src/service.ts @@ -16,5 +16,5 @@ declare module 'hydrooj' { export function apply(ctx: Context) { if (process.env.NODE_APP_INSTANCE !== '0') return; - ctx.once('app/started', () => require('./hosts/builtin').postInit()); + ctx.once('app/started', () => require('./hosts/builtin').postInit(ctx)); }