DOMJudge 9.0.1 常用 CLI 命令与工具详解

盘清 DOMjudge 9.0.1 全部随包 CLI 工具:dj_setup_database 子命令族与 -s 参数、dj_console(domjudge:*/api:call/compare:*)、export/import-contest 三坑与导出实测、force-passwords 无确认改密事故复盘、judgedaemon 与 systemd 托管红线、沙箱三件套、用户与 ACL 增删实测、mysqldump 1G 备份恢复回环,全部命令与回显实测(2026-10-28)。

DOMJudgeDOMjudge代码评测PHP 编程Ubuntu22.042026/10/28

DOMJudge 9.0.1 常用 CLI 命令与工具详解

目标:把 DOMjudge 9.0.1 随包交付的命令行工具一次盘清——每个工具干什么、怎么调、踩什么坑。全部命令与回显均在 Ubuntu 22.04 实测环境验证(2026-10-28),含四类「--help 陷阱」实测与一次 force-passwords 误改 admin 密码的完整事故复盘。安装与环境来源见 《Ubuntu 22.04 安装 DOMJudge 9.0.1 指南》,配置参数体系见 《DOMJudge 9.0.1 配置参数详解》。

一、环境信息

本文所有命令与回显均在以下真实环境实测(2026-10-28,与安装篇/配置篇同一套环境):

  • 操作系统:Ubuntu 22.04 LTS(GNU/Linux 5.15,x86_64)
  • DOMjudge:9.0.1(官方 tarball 源码安装;官方最新稳定版以 domjudge.org 首页 Download 徽章为准,2026-10-28 实查为 9.0.1)
  • 安装前缀:/opt/domjudge,工具分居两个 bin 目录:
    • /opt/domjudge/domserver/bin/:dj_setup_database、dj_console、export-contest、import-contest、force-passwords、fix_permissions、configure-domjudge
    • /opt/domjudge/judgehost/bin/:judgedaemon、dj_make_chroot、dj_run_chroot、dj_judgehost_cleanup、create_cgroups、runguard、runpipe
  • 数据库:MariaDB 10.6.23(root 走 unix_socket 认证)
  • 服务形态:nginx 1.18 + PHP-FPM 8.3 承载网站端;judgedaemon 以 systemd 模板单元 domjudge-judgedaemon@.service 托管
  • 示例账号:admin / q1w2e3r4

二、工具概览

工具所在目录职责
dj_setup_databasedomserver/bin数据库一站式:状态检查、建库建号、凭据生成、备份/恢复、卸载
dj_consoledomserver/binSymfony 控制台:domjudge:* 业务命令、api:call 直调 API、compare:* 结果比对
export-contestdomserver/bin按 CCS API 格式导出整场比赛(JSON + 事件流 + 提交源码)
import-contestdomserver/bin从 CCS 格式数据导入比赛(凭据走 ~/.netrc)
force-passwordsdomserver/bin按 etc 下 secret 文件把 judgehost/admin 密码恢复为初始值
fix_permissionsdomserver/bin修复安装目录属主与权限
configure-domjudgedomserver/bin安装期配置助手
judgedaemonjudgehost/bin评测守护进程,签到领任务、调度沙箱评测
dj_make_chrootjudgehost/bin构建/更新评测 chroot 沙箱
dj_run_chrootjudgehost/bin进入沙箱执行命令(装包、升级、排查)
dj_judgehost_cleanupjudgehost/bin清理评测残留进程/挂载/缓存/日志
create_cgroupsjudgehost/bincgroup 创建辅助(由 create-cgroups.service 调用)
runguard / runpipejudgehost/bin评测底层包装器,由 judgedaemon 自动调用,不手动运行

选型提示:日常运维高频的是 dj_setup_database、dj_console、judgedaemon 三个;沙箱三件套属于部署期与故障期工具。

三、前置条件

  1. 安装就绪:domserver 与 judgehost 已按安装篇完成安装,dj_setup_database -s status 通过(回显见 4.1)。
  2. 数据库访问:MariaDB root 走 unix_socket,本机 mysql 直接可用;dj_setup_database 需带 -s 走 socket(不加会报 ERROR 1698,见 6.6)。
  3. admin 权限:api:call 与导出工具实际使用 admin 凭据(dj_console list 原文注明 Note: this will use admin credentials)。
  4. systemd 可用:judgedaemon 由模板单元托管(安装篇已部署 create-cgroups.service 与 domjudge-judgedaemon@.service)。
  5. 评测运行用户:domjudge 及绑核所需的 domjudge-run-<N> 用户已创建(安装篇 4.6 节实测排障)。

四、各工具详解

4.1 dj_setup_database:数据库一站式工具

直接 --help 是第一个陷阱——它不认长选项,但会顺带打印完整用法:

cd /opt/domjudge/domserver && bin/dj_setup_database --help

实测回显(节选):

Error: unknown option '-'.
Usage: bin/dj_setup_database [option]... <command> [argument]

Commands:
  status           check database installation status
  genpass          generate DB,API,Symfony,admin password files
  create-db-users  create (empty) database and users
  update-password  update DB user database to that in 'etc/dbpasswords.secret'
  install          create database, example contest and users if not existing
  bare-install     create database, setup defaults if not existing
  uninstall        remove database users and database, INCLUDING ALL DATA!
  install-examples install examples only
  install-loadtest configure for load testing. WARNING: CREATES A LOT OF EXTRA ITEMS!
  upgrade          upgrade MySQL database schema to current version
  dump [filename]  backup the current database to file (without .sql.gz suffix)
  load [filename]  load a backup from file (without .sql.gz suffix), REMOVES ALL PREVIOUS DATA!

Options:
  -u <user>  connect to MySQL with DB admin <user>
  -p <pass>  use password <pass> for DB admin user
  -q         be (mostly) quiet
  -r         read DB admin password from prompt

健康检查(安全只读):

bin/dj_setup_database -s status

实测回显:

Database credentials read from '/opt/domjudge/domserver/etc/dbpasswords.secret'.
Trying to connect to the server as DB admin: success.
Trying to connect to the server with user 'domjudge': success.
Searching for database 'domjudge': found.
Searching for data in database: DOMjudge
MySQL server version: 10.6.23-MariaDB-0ubuntu0.22.04.1
Database check successful: database and users present and accessible.

各子命令要点(实测出处均为安装篇与本篇):

  • genpass / install:初始化主线,安装篇 4.5 节全程实测;genpass 会重写 etc 下全部 secret 文件,但不会同步已存在用户的密码——重装后全站 500 的根因即此(安装篇排障实录)。
  • update-password:把数据库用户密码同步为 dbpasswords.secret 当前值,重装后修复链的第一步(实测有效)。
  • uninstall:删除库与用户,含全部数据,仅用于彻底重装。
  • dump / load:官方备份/恢复入口,实测有坑,见 4.8 专题。
  • -s: undocumented 但实测有效,连接走本地 unix socket;本环境 MariaDB root 为 socket 认证,所有子命令都必须带它。

4.2 dj_console:业务命令与 API 直调

先看它提供哪些业务命令(--raw 精简输出后过滤):

bin/dj_console list --raw | grep -E "^(domjudge|api|compare)"

实测回显:

api:call                                    Call the DOMjudge API directly. Note: this will use admin credentials
compare:awards                              Compare awards between two files
compare:results                             Compare results between two files
compare:scoreboard                          Compare scoreboard between two files
domjudge:db-config:check                    Check if the default values of the database configuration are valid
domjudge:load-default-data                  Load the data needed by all DOMjudge installations
domjudge:load-development-data              Load fixture data to verify unit tests.
domjudge:load-example-data                  Load example data to get a sample DOMjudge installation up and running
domjudge:load-gatling-data                  Load the gatling data(for load testing)
domjudge:refresh-cache                      Refreshes the scoreboard caches for all contests
domjudge:reset-user-password                Reset the password of the given user

三个高频命令实测:

改用户密码(支持指定密码,省略则随机生成):

bin/dj_console domjudge:reset-user-password admin q1w2e3r4

实测回显:

 [OK] New password for admin is q1w2e3r4

随后用新密码走登录流程验证成功(login=302)。这是本篇 4.4 事故的标准修复命令。

配置默认值校验(配置变更后的体检命令,配套阅读配置篇):

bin/dj_console domjudge:db-config:check

实测回显:

 [OK] All default values have the correct type

记分板缓存刷新:

bin/dj_console domjudge:refresh-cache

实测回显:

 [OK] Refreshed cache for contest Demo contest.

API 直调(GET 默认形态):

bin/dj_console api:call -m GET contests

实测回显(完整单元素数组):

[{"formal_name":"Demo contest","scoreboard_type":"pass-fail","start_time":"2026-10-28T05:30:30+00:00","end_time":"2026-10-28T10:30:30+00:00","scoreboard_thaw_time":null,"duration":"5:00:00.000","scoreboard_freeze_duration":"1:00:00.000","cid":1,"id":"demo","name":"Demo contest","shortname":"demo","allow_submit":true,"runtime_as_score_tiebreaker":false,"warning_message":null,"penalty_time":20}]

写入类用法为 api:call -m POST -f yaml=contest.yaml contests(官方手册用法;-f 以「格式=文件」传请求体),适合批量建赛。

4.3 export-contest / import-contest:比赛导出与导入

export-contest 三个坑(全部实测):

坑一:contest_id 硬编码。脚本内写死 contest_id = 'wf48_finals',在只有 demo 比赛的站点首轮运行即报该比赛不存在(404)。查看源码片段:

contest_id = 'wf48_finals'

for endpoint in [
        'accounts',
        'awards',
        'balloons',
        'clarifications',
        'groups',
        'judgements',
        'languages',
        'organizations',
        'problems',
#        'runs',
        'scoreboard',
        'submissions',
        'teams',
        ]:
    data = api_to_file(f'contests/{contest_id}/{endpoint}', f'{endpoint}.json')

坑二:首参不是文件/目录而是可选 API URL。bin/export-contest --help 与 bin/export-contest /tmp/d1-export 的实测都把参数当 URL 拼接,报错形态一致(--help 那次的完整报错见 6.3)。用法行只有一句:

Usage: bin/export-contest [<domjudge-api-url>]

坑三:产物落在当前工作目录,脚本不建子目录。正确姿势是 mkdir 后进入目录再运行。

修复与成功路径(实测全程):把比赛 ID 换成本站实际 ID 后在空目录运行:

mkdir -p /tmp/d1-export && cd /tmp/d1-export
sed -i "s/wf48_finals/demo/g" /opt/domjudge/domserver/bin/export-contest
/opt/domjudge/domserver/bin/export-contest

实测回显(尾段,逐个下载提交源码):

Downloading 'contests/demo/submissions/2/files'
Downloading 'contests/demo/submissions/3/files'
Downloading 'contests/demo/submissions/4/files'
Downloading 'contests/demo/submissions/5/files'

实测产物(ls /tmp/d1-export):

accounts.json
awards.json
balloons.json
clarifications.json
event-feed.ndjson
groups.json
judgements.json
languages.json
organizations.json
problems.json
scoreboard.json
submissions
submissions.json
teams.json

即 12 类 CCS JSON + event-feed.ndjson 事件流 + submissions/ 源码目录(按提交号分目录,源码行 submissions[:10] 表明脚本只下载前 10 个提交的源码)。导出使用的凭据来自 etc 下 restapi.secret 对应的 judgehost 用户。

import-contest:与 export 成对,凭据从 ~/.netrc 读取;--help 同样被当 URL(实测报 MissingSchema: Invalid URL '--help/user',见 6.3)。CLI 批量导入比赛数据的替代姿势是 4.2 的 api:call -m POST,或 Web 端 Import / export 页面。

4.4 force-passwords:按 secret 恢复初始密码(高危,无确认)

这个工具没有 --help,传任何参数或不传参数都会直接执行。实测「看一眼帮助」的事故现场:

bin/force-passwords --help

实测回显(原样照录,注意它真的改了密码):

Database credentials read from '/opt/domjudge/domserver/etc/dbpasswords.secret'.
ERROR 1698 (28000): Access denied for user 'root'@'localhost'

 [OK] New password for judgehost is ujN3KSfMCG5aVgsvyWqnsG/ZdXqo0hEa


 [OK] New password for admin is TJKHC33jtTYh0a6M

事故解读:

  • 它读取 etc 下 secret 文件,把 judgehost 与 admin 用户的密码重置为初始 secret 值——admin 密码当场变成 initial_admin_password.secret 里的随机串,原密码失效。
  • 中间的 ERROR 1698 是它尝试用 root 走 TCP 连库失败(本环境 root 为 socket 认证),但不影响其经 API 完成改密——错误信息有误导性,别被「报错了应该没改成」安慰。
  • 它不接受自定义密码,唯一用途是把 judgehost/admin 恢复为 secret 文件记载的初始值;典型场景是重装/凭据重生成后的同步(安装篇排障实录)。

修复:自定义密码请用 domjudge:reset-user-password(4.2 实测回显),本文已当场恢复 admin / q1w2e3r4 并以 login=302 验证。

⚠️ 一句话预防:在弄清工具行为前,不要对生产环境随手敲 --help——本项目至少有三个工具会「认真执行」你的求助请求(另两个见第五节陷阱表)。

4.5 judgedaemon 与 systemd 托管

judgedaemon 由模板单元托管(安装篇部署),单元关键定义:

ExecStart=/opt/domjudge/judgehost/bin/judgedaemon -n %i
User=domjudge
Restart=always

-n %i 即实例号绑定 CPU 核号:systemctl enable --now domjudge-judgedaemon@1 启动绑核 1 的实例。运行痕迹(实时日志):

journalctl -u domjudge-judgedaemon@1 -n 6 --no-pager -o cat

实测回显:

No submissions in queue (for endpoint default), waiting...

API request GET judgehosts

API request POST judgehosts/fetch-work

三条红线,全部实测踩过:

  1. 拒绝 root:以 root 运行直接退出,回显仅一行 This program should not be run as root.
  2. 绑核实例需要 domjudge-run-<N> 用户:缺用户时启动即崩溃循环,报错原文 error: runuser domjudge-run-1 does not exist.,修复 useradd -d /nonexistent -g domjudge-run -M -s /bin/false domjudge-run-1(安装篇 4.6 节实录)。
  3. 没有 --help:误执行会静默进入守护循环。实测 ./judgedaemon --help 无任何输出直接开始运行,进程表出现双实例:
  55213 php /opt/domjudge/judgehost/bin/judgedaemon -n 1
  55567 php ./judgedaemon --help

处置:kill -9 游离 PID(55567),systemd 托管实例(-n 1)不受影响;清理后进程数回到 1。

4.6 沙箱三件套:dj_make_chroot / dj_run_chroot / dj_judgehost_cleanup

dj_make_chroot 构建/更新评测沙箱(安装篇 4.8 节以 -d Ubuntu-22.04 + -i 附加包实测构建成功,收尾回显 Done building in /chroot/domjudge)。用法:

Usage: /opt/domjudge/judgehost/bin/dj_make_chroot [options]...
Creates a chroot environment with Java JRE support using the
Debian or Ubuntu GNU/Linux distribution.

Options:
  -a <arch>    Machine architecture to use.
  -d <dir>     Directory where to build the chroot environment.
  -D <distro>  Linux distribution to use: 'Debian' or 'Ubuntu'.
  -R <release> Release for the selected Linux distribution.
  -i <debs>    List of extra package names to install (comma separated).
  -r <debs>    List of extra package names to remove (comma separated).
  -l <debs>    List of local package files to install (comma separated).
  -s <lists>   List of apt repository .list files that exist outside the chroot
               to add to the chroot (comma separated). Signing keys for the

(--help 会先报 Illegal option -- 再打印用法,无副作用。)

dj_run_chroot 进入沙箱执行命令,典型用途是给沙箱装包/升级:

Usage: ./dj_run_chroot [options]... [command]
Runs <command> inside the prebuilt chroot environment. If no command is
given, then an interactive shell inside the chroot is started.

Options:
  -d <dir>    Directory of the prebuild chroot (if not default).
  -h          Display this help.

This script must be run as root. It mounts proc, sysfs and devpts
before starting the chroot, and unmounts those afterwards. This script
can be used, for example, to upgrade or install new packages in the
chroot:

  sudo dj_run_chroot "apt update && apt upgrade"

注意两点(用法原文):必须 root 运行;仅 [etc, usr, lib, bin, lib64] 等目录被挂进沙箱,传文件进沙箱要先拷贝进沙箱树再操作。

dj_judgehost_cleanup 清理评测残留,五个子命令:

Usage: dj_judgehost_cleanup <command>
Perform various cleanup tasks on a judgehost.

Make sure that no judgings are in process while running this script.
It is best to temporarily disable the judgehost in the web interface
or stop the service and wait until all judgings are finished.

Commands:
  procs     Kill any remaining processes running as the restricted
              domjudge-run* user. These can remain when the judging
              process aborted in an unexpected way.
  mounts    Unmount stale bind mounts to the chroot. These can appear
              when then judgedaemon crashes in the middle of a judging.
  cache     Delete cached test cases and executables.
  judgings  Delete detailed logs and compiled programs of judgings.
              Note that the most important data is stored in the database.
              The additional data on disk is mostly useful for debugging.
  all       First kill remaining processes, then unmount stale bind mounts,
              and finally delete cache and judging data.

用法原文即最重要的注意事项:运行前确保没有评测进行中(最好先在 Web 端停用评测机或停服务)。

4.7 用户与 ACL:角色体系与账号增删实测

角色是权限的最小单元,用户经映射表挂接角色。角色全表(9 个,实测):

mysql -N -e "select roleid,role from domjudge.role order by roleid"

实测回显:

1	admin
2	jury
3	team
4	balloon
5	judgehost
6	api_reader
7	api_writer
8	api_source_reader
9	clarification_rw

当前用户-角色映射(实测):

admin	admin
judgehost	judgehost
demo	team

即:三类核心身份各就各位——admin(管理员全权)、judgehost(评测机 API 机制账号)、demo(队伍)。API 读取类角色(api_reader 等)供外部系统对接使用。

建用户(API 实测):

curl -s -u admin:q1w2e3r4 -X POST http://localhost/domjudge/api/v4/users \
  -H "Content-Type: application/json" \
  -d '{"username":"d1tester","name":"D1 Tester","password":"d1test123","roles":["team"]}'

实测回显:

{"last_login_time":null,"last_api_login_time":null,"first_login_time":null,"team":null,"team_id":null,"roles":["team"],"type":"team","userid":4,"id":"d1tester","username":"d1tester","name":"D1 Tester","email":null,"last_ip":null,"ip":null,"enabled":true}

用户列表 GET /api/v4/users 可见全部账号(含 roles/type/userid)。批量建号更推荐 accounts.yaml 导入(官方手册 Importing accounts 章节,Web 端 Import / export 页面上传)。

删用户(API 无通道,实测):DELETE /api/v4/users/d1tester 实测返回 {"code":405,"message":"Method Not Allowed"}。删除走 Web 端 Users 页面,或数据库直删(实测有效):

delete from domjudge.userrole where userid=4;
delete from domjudge.user where userid=4;

实测回显(复核计数):0——账号连同角色映射一并移除。

4.8 数据库备份恢复专题:两条路径对照

路径一:官方 dump(实测有坑)。dj_setup_database -s dump <name> 有两个坑:文件名按裸名处理(相对 /opt/domjudge/domserver/db-dumps/ 拼接,传绝对路径会拼出非法路径,实测报错 cannot create /opt/domjudge/domserver/db-dumps//tmp/d1-official.sql.gz: Directory nonexistent);依赖 pv 做进度显示(缺失时报 pv: not found 且管道写坏,实测 mysqldump: Got errno 32 on write 产生 20 字节坏文件)。装上 pv 后重试,仍复现大 BLOB 截断:

mysqldump: Error 2013: Lost connection to server during query when dumping table `testcase_content` at row: 6

产物仅 779KB 且无 Dump completed 尾行(损坏,已弃用)。根因与路径二相同:testcase_content 表存测试数据原文(本环境约 38.5MB 单行),超过服务端/客户端默认包上限。

路径二:mysqldump 手动全量备份(实测回环通过):

mysqldump --max_allowed_packet=1G domjudge > /tmp/d1-dump.sql
tail -1 /tmp/d1-dump.sql
du -h /tmp/d1-dump.sql

实测回显:

-- Dump completed on 2026-10-28  7:22:09
40M	/tmp/d1-dump.sql

恢复回环(导入临时库验证,不碰生产库):

mysql -e "create database dj_restore_test"
mysql dj_restore_test < /tmp/d1-dump.sql

实测回显(行数/表数一致性核对后清理):

restore_exit=0
src=5 dst=5
tables=51
cleanup_exit=0

即:恢复库 judging 表 5 行与源库一致、51 张表完整、Dump completed 收尾、验证后删除临时库。恢复到生产库的对应命令是 bin/dj_setup_database -s load <file>——注意用法原文警告 REMOVES ALL PREVIOUS DATA!(先清空再导入,属危险操作,本文以临时库验证代替)。

五、注意事项

--help 陷阱表(本篇四例全部实测):

工具传 --help 的实际行为风险
dj_setup_databaseError: unknown option '-'. 后打印用法无(最友好)
dj_make_chroot / dj_run_chrootIllegal option -- 后打印用法无
force-passwords直接执行,重置 judgehost/admin 密码高(无确认、改生产凭据)
judgedaemon静默进入守护循环高(游离进程抢任务)
export-contest / import-contest参数被当 API URL,抛 Python traceback低(报错即止)

其余要点:

  • root 边界:judgedaemon 拒绝 root 运行;dj_run_chroot 必须 root;dj_setup_database 用 root + socket 即可。
  • 危险命令:dj_setup_database uninstall、load 与 force-passwords 都会动全量数据/凭据,执行前先 dump(用 4.8 路径二)。
  • -s 习惯:本环境所有 dj_setup_database 子命令实测均需 -s(unix_socket),文档未列出但实测必需。
  • api:call 即 admin:等价于用 admin 凭据调 API,脚本里避免对不可信输入使用。
  • 评测机清理前提:dj_judgehost_cleanup 执行窗口必须避开评测进行中(用法原文警告)。

六、常见问题(排障实录)

6.1 force-passwords 把 admin 密码改了怎么办

现象与全回显见 4.4。修复两步:bin/dj_console domjudge:reset-user-password admin <新密码>,然后登录验证(实测 login=302)。预防:把该工具列入「禁裸敲」清单,需要它时先 cat etc/initial_admin_password.secret 确认将写入的值。

6.2 judgedaemon 起不来或出现双实例

起不来:先看 journalctl -u domjudge-judgedaemon@ 输出,实测两类原因——domjudge-run-<N> 用户缺失(报错原文见 4.5)与 restapi.secret 与网站端不一致(凭据同步见安装篇 4.6)。双实例:ps -u domjudge -o pid,cmd | grep judgedaemon 核对,只保留 systemd 托管的 -n <N> 实例,其余 kill。

6.3 export/import-contest 的三种报错

实测回显(--help 被当 URL,两工具同型):

requests.exceptions.MissingSchema: Invalid URL '--help/user': No schema supplied. Perhaps you meant http://--help/user?
RuntimeError: Invalid URL '--help/user': No schema supplied. Perhaps you meant http://--help/user?

处置:export-contest 不接参数直接跑(API 地址取自 restapi.secret),先 mkdir 并 cd 进产物目录、比赛 ID 用 sed 换成本站实际 ID(见 4.3 实测);import-contest 配好 ~/.netrc 再跑,批量建赛可用 api:call -m POST 替代。

6.4 备份文件损坏或报 Error 2013

官方 dump 与裸 mysqldump 在大 BLOB 表(testcase_content)上都会触发 Error 2013: Lost connection...(实测回显见 4.8)。判定标准:文件末行是否为 Dump completed on ...。修复:mysqldump --max_allowed_packet=1G 重导;恢复验证用临时库计数回环(51 表 / judging 行数一致)。

6.5 API 删不掉用户(405)

DELETE /api/v4/users/<id> 实测 405:9.0.1 的 users 端点不支持删除。走 Web 端 Users 页面,或按 4.7 的两条 SQL(先删 userrole 映射再删 user)。

6.6 dj_setup_database 报 ERROR 1698

实测回显 ERROR 1698 (28000): Access denied for user 'root'@'localhost':脚本默认走 TCP,而 MariaDB root 为 unix_socket 认证。所有子命令加 -s 即可(4.1 全部实测均带 -s)。

七、参考链接