mirror of
https://github.com/criyle/go-judge.git
synced 2025-09-26 22:39:12 +08:00
doc: remove mount
This commit is contained in:
parent
567b7702d7
commit
707297a66c
@ -1,32 +0,0 @@
|
|||||||
# Binaries for programs and plugins
|
|
||||||
*.exe
|
|
||||||
*.exe~
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
/judge
|
|
||||||
|
|
||||||
# Test binary, build with `go test -c`
|
|
||||||
*.test
|
|
||||||
|
|
||||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
|
||||||
*.out
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Test Env
|
|
||||||
env*.sh
|
|
||||||
init.sql
|
|
||||||
|
|
||||||
# Documents
|
|
||||||
LICENSE
|
|
||||||
README.md
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
|
|
||||||
# not release yet
|
|
||||||
package*.json
|
|
||||||
|
|
||||||
example.js
|
|
||||||
/go-judge*
|
|
@ -1,24 +0,0 @@
|
|||||||
FROM golang:alpine AS build
|
|
||||||
|
|
||||||
WORKDIR /go/judge
|
|
||||||
|
|
||||||
RUN apk update && apk add git
|
|
||||||
|
|
||||||
COPY go.mod go.sum /go/judge/
|
|
||||||
|
|
||||||
RUN go mod download -x
|
|
||||||
|
|
||||||
COPY ./ /go/judge
|
|
||||||
|
|
||||||
RUN go generate ./cmd/go-judge/version \
|
|
||||||
&& CGO_ENABLE=0 go build -v -tags grpcnotrace,nomsgpack -o go-judge ./cmd/go-judge
|
|
||||||
|
|
||||||
FROM alpine:latest
|
|
||||||
|
|
||||||
WORKDIR /opt
|
|
||||||
|
|
||||||
COPY --from=build /go/judge/go-judge /go/judge/mount.yaml /opt/
|
|
||||||
|
|
||||||
EXPOSE 5050/tcp 5051/tcp
|
|
||||||
|
|
||||||
ENTRYPOINT ["./go-judge"]
|
|
@ -1,22 +0,0 @@
|
|||||||
FROM golang:latest AS build
|
|
||||||
|
|
||||||
WORKDIR /go/judge
|
|
||||||
|
|
||||||
COPY go.mod go.sum /go/judge/
|
|
||||||
|
|
||||||
RUN go mod download -x
|
|
||||||
|
|
||||||
COPY ./ /go/judge
|
|
||||||
|
|
||||||
RUN go generate ./cmd/go-judge/version \
|
|
||||||
&& CGO_ENABLE=0 go build -v -tags grpcnotrace,nomsgpack -o go-judge ./cmd/go-judge
|
|
||||||
|
|
||||||
FROM debian:latest
|
|
||||||
|
|
||||||
WORKDIR /opt
|
|
||||||
|
|
||||||
COPY --from=build /go/judge/go-judge /go/judge/mount.yaml /opt/
|
|
||||||
|
|
||||||
EXPOSE 5050/tcp 5051/tcp
|
|
||||||
|
|
||||||
ENTRYPOINT ["./go-judge"]
|
|
382
README.cn.md
382
README.cn.md
@ -34,385 +34,13 @@ docker run -it --rm --privileged --shm-size=256m -p 5050:5050 --name=go-judge cr
|
|||||||
|
|
||||||
### REST API 接口定义
|
### REST API 接口定义
|
||||||
|
|
||||||
```typescript
|
[接口数据类型定义](https://docs.goj.ac/cn/api#rest-api-接口定义)
|
||||||
interface LocalFile {
|
|
||||||
src: string; // 文件绝对路径
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MemoryFile {
|
|
||||||
content: string | Buffer; // 文件内容
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PreparedFile {
|
|
||||||
fileId: string; // 文件 id
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Collector {
|
|
||||||
name: string; // copyOut 文件名
|
|
||||||
max: number; // 最大大小限制
|
|
||||||
pipe?: boolean; // 通过管道收集(默认值为false文件收集)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Symlink {
|
|
||||||
symlink: string; // 符号连接目标 (v1.6.0+)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StreamIn {
|
|
||||||
streamIn: boolean; // 流式输入 (v1.8.1+)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StreamOut {
|
|
||||||
streamOut: boolean; // 流式输出 (v1.8.1+)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Cmd {
|
|
||||||
args: string[]; // 程序命令行参数
|
|
||||||
env?: string[]; // 程序环境变量
|
|
||||||
|
|
||||||
// 指定 标准输入、标准输出和标准错误的文件 (null 是为了 pipe 的使用情况准备的,而且必须被 pipeMapping 的 in / out 指定)
|
|
||||||
files?: (LocalFile | MemoryFile | PreparedFile | Collector | StreamIn | StreamOut | null)[];
|
|
||||||
tty?: boolean; // 开启 TTY (需要保证标准输出和标准错误为同一文件)同时需要指定 TERM 环境变量 (例如 TERM=xterm)
|
|
||||||
|
|
||||||
// 资源限制
|
|
||||||
cpuLimit?: number; // CPU时间限制,单位纳秒
|
|
||||||
clockLimit?: number; // 等待时间限制,单位纳秒 (通常为 cpuLimit 两倍)
|
|
||||||
memoryLimit?: number; // 内存限制,单位 byte
|
|
||||||
stackLimit?: number; // 栈内存限制,单位 byte
|
|
||||||
procLimit?: number; // 线程数量限制
|
|
||||||
cpuRateLimit?: number; // 仅 Linux,CPU 使用率限制,1000 等于单核 100%
|
|
||||||
cpuSetLimit?: string; // 仅 Linux,限制 CPU 使用,使用方式和 cpuset cgroup 相同 (例如,`0` 表示限制仅使用第一个核)
|
|
||||||
strictMemoryLimit?: boolean; // deprecated: 使用 dataSegmentLimit (这个选项依然有效)
|
|
||||||
dataSegmentLimit?: boolean; // 仅linux,开启 rlimit 堆空间限制(如果不使用cgroup默认开启)
|
|
||||||
addressSpaceLimit?: boolean; // 仅linux,开启 rlimit 虚拟内存空间限制(非常严格,在所以申请时触发限制)
|
|
||||||
|
|
||||||
// 在执行程序之前复制进容器的文件列表
|
|
||||||
copyIn?: {[dst:string]:LocalFile | MemoryFile | PreparedFile | Symlink};
|
|
||||||
|
|
||||||
// 在执行程序后从容器文件系统中复制出来的文件列表
|
|
||||||
// 在文件名之后加入 '?' 来使文件变为可选,可选文件不存在的情况不会触发 FileError
|
|
||||||
copyOut?: string[];
|
|
||||||
// 和 copyOut 相同,不过文件不返回内容,而是返回一个对应文件 ID ,内容可以通过 /file/:fileId 接口下载
|
|
||||||
copyOutCached?: string[];
|
|
||||||
// 指定 copyOut 复制文件大小限制,单位 byte
|
|
||||||
copyOutMax?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
Accepted = 'Accepted', // 正常情况
|
|
||||||
MemoryLimitExceeded = 'Memory Limit Exceeded', // 内存超限
|
|
||||||
TimeLimitExceeded = 'Time Limit Exceeded', // 时间超限
|
|
||||||
OutputLimitExceeded = 'Output Limit Exceeded', // 输出超限
|
|
||||||
FileError = 'File Error', // 文件错误
|
|
||||||
NonzeroExitStatus = 'Nonzero Exit Status', // 非 0 退出值
|
|
||||||
Signalled = 'Signalled', // 进程被信号终止
|
|
||||||
InternalError = 'Internal Error', // 内部错误
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PipeIndex {
|
|
||||||
index: number; // cmd 的下标
|
|
||||||
fd: number; // cmd 的 fd
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PipeMap {
|
|
||||||
in: PipeIndex; // 管道的输入端
|
|
||||||
out: PipeIndex; // 管道的输出端
|
|
||||||
// 开启管道代理,传输内容会从输出端复制到输入端
|
|
||||||
// 输入端内容在输出端关闭以后会丢弃 (防止 SIGPIPE )
|
|
||||||
proxy?: boolean;
|
|
||||||
name?: string; // 如果代理开启,内容会作为 copyOut 放在输入端 (用来 debug )
|
|
||||||
// 限制 copyOut 的最大大小,代理会在超出大小之后正常复制
|
|
||||||
max?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum FileErrorType {
|
|
||||||
CopyInOpenFile = 'CopyInOpenFile',
|
|
||||||
CopyInCreateFile = 'CopyInCreateFile',
|
|
||||||
CopyInCopyContent = 'CopyInCopyContent',
|
|
||||||
CopyOutOpen = 'CopyOutOpen',
|
|
||||||
CopyOutNotRegularFile = 'CopyOutNotRegularFile',
|
|
||||||
CopyOutSizeExceeded = 'CopyOutSizeExceeded',
|
|
||||||
CopyOutCreateFile = 'CopyOutCreateFile',
|
|
||||||
CopyOutCopyContent = 'CopyOutCopyContent',
|
|
||||||
CollectSizeExceeded = 'CollectSizeExceeded',
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FileError {
|
|
||||||
name: string; // 错误文件名称
|
|
||||||
type: FileErrorType; // 错误代码
|
|
||||||
message?: string; // 错误信息
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Request {
|
|
||||||
requestId?: string; // 给 WebSocket 使用来区分返回值的来源请求
|
|
||||||
cmd: Cmd[];
|
|
||||||
pipeMapping: PipeMap[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CancelRequest {
|
|
||||||
cancelRequestId: string; // 取消某个正在进行中的请求
|
|
||||||
};
|
|
||||||
|
|
||||||
// WebSocket 请求
|
|
||||||
type WSRequest = Request | CancelRequest;
|
|
||||||
|
|
||||||
interface Result {
|
|
||||||
status: Status;
|
|
||||||
error?: string; // 详细错误信息
|
|
||||||
exitStatus: number; // 程序返回值
|
|
||||||
time: number; // 程序运行 CPU 时间,单位纳秒
|
|
||||||
memory: number; // 程序运行内存,单位 byte
|
|
||||||
procPeak?: number; // 程序运行最大线程数量(需要内核版本>=6.1,且开启 cgroup v2)
|
|
||||||
runTime: number; // 程序运行现实时间,单位纳秒
|
|
||||||
// copyOut 和 pipeCollector 指定的文件内容
|
|
||||||
files?: {[name:string]:string};
|
|
||||||
// copyFileCached 指定的文件 id
|
|
||||||
fileIds?: {[name:string]:string};
|
|
||||||
// 文件错误详细信息
|
|
||||||
fileError?: FileError[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSocket 结果
|
|
||||||
interface WSResult {
|
|
||||||
requestId: string;
|
|
||||||
results: Result[];
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 流式请求 / 响应
|
|
||||||
interface Resize {
|
|
||||||
index: number;
|
|
||||||
fd: number;
|
|
||||||
rows: number;
|
|
||||||
cols: number;
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Input {
|
|
||||||
index: number;
|
|
||||||
fd: number;
|
|
||||||
content: Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Output {
|
|
||||||
index: number;
|
|
||||||
fd: number;
|
|
||||||
content: Buffer;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 示例
|
### 示例
|
||||||
|
|
||||||
请使用 postman 或其他 REST API 调试工具向 http://localhost:5050/run 发送请求
|
请使用 postman 或其他 REST API 调试工具向 http://localhost:5050/run 发送请求
|
||||||
|
|
||||||
<details><summary>单个c++文件编译运行</summary>
|
[请求实例](https://docs.goj.ac/cn/example)
|
||||||
|
|
||||||
这个例子需要安装 `g++`。如果在 docker 环境中运行,请在容器中(`docker exec -it go-judge /bin/bash`)执行 `apt update && apt install g++`
|
|
||||||
|
|
||||||
需要注意,在生产环境中 `copyOutCached` 产生的文件在使用完之后需要使用(`DELETE /file/:id`)删除避免内存泄露
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["/usr/bin/g++", "a.cc", "-o", "a"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{
|
|
||||||
"content": ""
|
|
||||||
}, {
|
|
||||||
"name": "stdout",
|
|
||||||
"max": 10240
|
|
||||||
}, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 10000000000,
|
|
||||||
"memoryLimit": 104857600,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyIn": {
|
|
||||||
"a.cc": {
|
|
||||||
"content": "#include <iostream>\nusing namespace std;\nint main() {\nint a, b;\ncin >> a >> b;\ncout << a + b << endl;\n}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"copyOut": ["stdout", "stderr"],
|
|
||||||
"copyOutCached": ["a"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"status": "Accepted",
|
|
||||||
"exitStatus": 0,
|
|
||||||
"time": 303225231,
|
|
||||||
"memory": 32243712,
|
|
||||||
"runTime": 524177700,
|
|
||||||
"files": {
|
|
||||||
"stderr": "",
|
|
||||||
"stdout": ""
|
|
||||||
},
|
|
||||||
"fileIds": {
|
|
||||||
"a": "5LWIZAA45JHX4Y4Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["a"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{
|
|
||||||
"content": "1 1"
|
|
||||||
}, {
|
|
||||||
"name": "stdout",
|
|
||||||
"max": 10240
|
|
||||||
}, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 10000000000,
|
|
||||||
"memoryLimit": 104857600,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyIn": {
|
|
||||||
"a": {
|
|
||||||
"fileId": "5LWIZAA45JHX4Y4Z" // 这个缓存文件的 ID 来自上一个请求返回的 fileIds
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"status": "Accepted",
|
|
||||||
"exitStatus": 0,
|
|
||||||
"time": 1173000,
|
|
||||||
"memory": 10637312,
|
|
||||||
"runTime": 1100200,
|
|
||||||
"files": {
|
|
||||||
"stderr": "",
|
|
||||||
"stdout": "2\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details><summary>多个程序(例如交互题)</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["/bin/cat", "1"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{
|
|
||||||
"content": ""
|
|
||||||
}, null, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 1000000000,
|
|
||||||
"memoryLimit": 1048576,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyIn": {
|
|
||||||
"1": { "content": "TEST 1" }
|
|
||||||
},
|
|
||||||
"copyOut": ["stderr"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"args": ["/bin/cat"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [null, {
|
|
||||||
"name": "stdout",
|
|
||||||
"max": 10240
|
|
||||||
}, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 1000000000,
|
|
||||||
"memoryLimit": 1048576,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyOut": ["stdout", "stderr"]
|
|
||||||
}],
|
|
||||||
"pipeMapping": [{
|
|
||||||
"in" : {"index": 0, "fd": 1 },
|
|
||||||
"out" : {"index": 1, "fd" : 0 }
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"status": "Accepted",
|
|
||||||
"exitStatus": 0,
|
|
||||||
"time": 1545123,
|
|
||||||
"memory": 253952,
|
|
||||||
"runTime": 4148800,
|
|
||||||
"files": {
|
|
||||||
"stderr": ""
|
|
||||||
},
|
|
||||||
"fileIds": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"status": "Accepted",
|
|
||||||
"exitStatus": 0,
|
|
||||||
"time": 1501463,
|
|
||||||
"memory": 253952,
|
|
||||||
"runTime": 5897700,
|
|
||||||
"files": {
|
|
||||||
"stderr": "",
|
|
||||||
"stdout": "TEST 1"
|
|
||||||
},
|
|
||||||
"fileIds": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details><summary>开启 CPURate 限制的死循环</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["/usr/bin/python3", "1.py"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{"content": ""}, {"name": "stdout","max": 10240}, {"name": "stderr","max": 10240}],
|
|
||||||
"cpuLimit": 3000000000,
|
|
||||||
"clockLimit": 4000000000,
|
|
||||||
"memoryLimit": 104857600,
|
|
||||||
"procLimit": 50,
|
|
||||||
"cpuRate": 0.1,
|
|
||||||
"copyIn": {
|
|
||||||
"1.py": {
|
|
||||||
"content": "while True:\n pass"
|
|
||||||
}
|
|
||||||
}}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"status": "Time Limit Exceeded",
|
|
||||||
"exitStatus": 9,
|
|
||||||
"time": 414803599,
|
|
||||||
"memory": 3657728,
|
|
||||||
"runTime": 4046054900,
|
|
||||||
"files": {
|
|
||||||
"stderr": "",
|
|
||||||
"stdout": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## 进阶设置
|
## 进阶设置
|
||||||
|
|
||||||
@ -475,12 +103,6 @@ interface Output {
|
|||||||
|
|
||||||
所有命令行参数都可以通过环境变量的形式来指定,(类似 `ES_HTTP_ADDR` 来指定 `-http-addr`)。使用 `go-judge --help` 查看所有环境变量
|
所有命令行参数都可以通过环境变量的形式来指定,(类似 `ES_HTTP_ADDR` 来指定 `-http-addr`)。使用 `go-judge --help` 查看所有环境变量
|
||||||
|
|
||||||
### 编译 docker
|
|
||||||
|
|
||||||
终端中运行 `docker build -t go-judge -f Dockerfile.exec .`
|
|
||||||
|
|
||||||
沙箱服务需要特权级别 docker 来创建子容器和提供 cgroup 资源限制。
|
|
||||||
|
|
||||||
### 编译沙箱终端
|
### 编译沙箱终端
|
||||||
|
|
||||||
编译 `go build ./cmd/go-judge-shell`
|
编译 `go build ./cmd/go-judge-shell`
|
||||||
|
563
README.md
563
README.md
@ -34,566 +34,11 @@ A REST service to run program in restricted environment (Listening on `localhost
|
|||||||
|
|
||||||
### REST API Interface
|
### REST API Interface
|
||||||
|
|
||||||
```typescript
|
[API Interface Structure Definition](https://docs.goj.ac/api#rest-api-interface)
|
||||||
interface LocalFile {
|
|
||||||
src: string; // absolute path for the file
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MemoryFile {
|
|
||||||
content: string | Buffer; // file contents
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PreparedFile {
|
|
||||||
fileId: string; // fileId defines file uploaded by /file
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Collector {
|
|
||||||
name: string; // file name in copyOut
|
|
||||||
max: number; // maximum bytes to collect from pipe
|
|
||||||
pipe?: boolean; // collect over pipe or not (default false)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Symlink {
|
|
||||||
symlink: string; // symlink destination (v1.6.0+)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StreamIn {
|
|
||||||
streamIn: boolean; // stream input (v1.8.1+)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StreamOut {
|
|
||||||
streamOut: boolean; // stream output (v1.8.1+)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Cmd {
|
|
||||||
args: string[]; // command line argument
|
|
||||||
env?: string[]; // environment
|
|
||||||
|
|
||||||
// specifies file input / pipe collector for program file descriptors (null is reserved for pipe mapping and must be filled by in / out)
|
|
||||||
files?: (LocalFile | MemoryFile | PreparedFile | Collector | StreamIn | StreamOut | null)[];
|
|
||||||
tty?: boolean; // enables tty on the input and output pipes (should have just one input & one output)
|
|
||||||
// Notice: must have TERM environment variables (e.g. TERM=xterm)
|
|
||||||
|
|
||||||
// limitations
|
|
||||||
cpuLimit?: number; // ns
|
|
||||||
realCpuLimit?: number; // deprecated: use clock limit instead (still working)
|
|
||||||
clockLimit?: number; // ns
|
|
||||||
memoryLimit?: number; // byte
|
|
||||||
stackLimit?: number; // byte (N/A on windows, macOS cannot set over 32M)
|
|
||||||
procLimit?: number;
|
|
||||||
cpuRateLimit?: number; // limit cpu usage (1000 equals 1 cpu)
|
|
||||||
cpuSetLimit?: string; // Linux only: set the cpuSet for cgroup
|
|
||||||
strictMemoryLimit?: boolean; // deprecated: use dataSegmentLimit instead (still working)
|
|
||||||
dataSegmentLimit?: boolean; // Linux only: use (+ rlimit_data limit) enable by default if cgroup not enabled
|
|
||||||
addressSpaceLimit?: boolean; // Linux only: use (+ rlimit_address_space limit)
|
|
||||||
|
|
||||||
// copy the correspond file to the container dst path
|
|
||||||
copyIn?: {[dst:string]:LocalFile | MemoryFile | PreparedFile | Symlink};
|
|
||||||
|
|
||||||
// copy out specifies files need to be copied out from the container after execution
|
|
||||||
// append '?' after file name will make the file optional and do not cause FileError when missing
|
|
||||||
copyOut?: string[];
|
|
||||||
// similar to copyOut but stores file in go judge and returns fileId, later download through /file/:fileId
|
|
||||||
copyOutCached?: string[];
|
|
||||||
// specifies the directory to dump container /w content
|
|
||||||
copyOutDir: string
|
|
||||||
// specifies the max file size to copy out
|
|
||||||
copyOutMax?: number; // byte
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Status {
|
|
||||||
Accepted = 'Accepted', // normal
|
|
||||||
MemoryLimitExceeded = 'Memory Limit Exceeded', // mle
|
|
||||||
TimeLimitExceeded = 'Time Limit Exceeded', // tle
|
|
||||||
OutputLimitExceeded = 'Output Limit Exceeded', // ole
|
|
||||||
FileError = 'File Error', // fe
|
|
||||||
NonzeroExitStatus = 'Nonzero Exit Status',
|
|
||||||
Signalled = 'Signalled',
|
|
||||||
InternalError = 'Internal Error', // system error
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PipeIndex {
|
|
||||||
index: number; // the index of cmd
|
|
||||||
fd: number; // the fd number of cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PipeMap {
|
|
||||||
in: PipeIndex; // input end of the pipe
|
|
||||||
out: PipeIndex; // output end of the pipe
|
|
||||||
// enable pipe proxy from in to out,
|
|
||||||
// content from in will be discarded if out closes
|
|
||||||
proxy?: boolean;
|
|
||||||
name?: string; // copy out proxy content if proxy enabled
|
|
||||||
// limit the copy out content size,
|
|
||||||
// proxy will still functioning after max
|
|
||||||
max?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum FileErrorType {
|
|
||||||
CopyInOpenFile = 'CopyInOpenFile',
|
|
||||||
CopyInCreateFile = 'CopyInCreateFile',
|
|
||||||
CopyInCopyContent = 'CopyInCopyContent',
|
|
||||||
CopyOutOpen = 'CopyOutOpen',
|
|
||||||
CopyOutNotRegularFile = 'CopyOutNotRegularFile',
|
|
||||||
CopyOutSizeExceeded = 'CopyOutSizeExceeded',
|
|
||||||
CopyOutCreateFile = 'CopyOutCreateFile',
|
|
||||||
CopyOutCopyContent = 'CopyOutCopyContent',
|
|
||||||
CollectSizeExceeded = 'CollectSizeExceeded',
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FileError {
|
|
||||||
name: string; // error file name
|
|
||||||
type: FileErrorType; // type
|
|
||||||
message?: string; // detailed message
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Request {
|
|
||||||
requestId?: string; // for WebSocket requests
|
|
||||||
cmd: Cmd[];
|
|
||||||
pipeMapping?: PipeMap[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CancelRequest {
|
|
||||||
cancelRequestId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// WebSocket request
|
|
||||||
type WSRequest = Request | CancelRequest;
|
|
||||||
|
|
||||||
interface Result {
|
|
||||||
status: Status;
|
|
||||||
error?: string; // potential system error message
|
|
||||||
exitStatus: number;
|
|
||||||
time: number; // ns (cgroup recorded time)
|
|
||||||
memory: number; // byte
|
|
||||||
runTime: number; // ns (wall clock time)
|
|
||||||
procPeak?: number; // peak number of process (cgroup v2, kernel >= 6.1)
|
|
||||||
// copyFile name -> content
|
|
||||||
files?: {[name:string]:string};
|
|
||||||
// copyFileCached name -> fileId
|
|
||||||
fileIds?: {[name:string]:string};
|
|
||||||
// fileError contains detailed file errors
|
|
||||||
fileError?: FileError[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebSocket results
|
|
||||||
interface WSResult {
|
|
||||||
requestId: string;
|
|
||||||
results: Result[];
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream request & responses
|
|
||||||
interface Resize {
|
|
||||||
index: number;
|
|
||||||
fd: number;
|
|
||||||
rows: number;
|
|
||||||
cols: number;
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Input {
|
|
||||||
index: number;
|
|
||||||
fd: number;
|
|
||||||
content: Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Output {
|
|
||||||
index: number;
|
|
||||||
fd: number;
|
|
||||||
content: Buffer;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example Request & Response
|
### Example Request & Response
|
||||||
|
|
||||||
<details><summary>FFI</summary>
|
[Example Request & Response](https://docs.goj.ac/example)
|
||||||
|
|
||||||
```javascript
|
|
||||||
var ffi = require('ffi-napi');
|
|
||||||
|
|
||||||
var go_judge = ffi.Library('./go_judge', {
|
|
||||||
'Init': ['int', ['string']],
|
|
||||||
'Exec': ['string', ['string']],
|
|
||||||
'FileList': ['string', []],
|
|
||||||
'FileAdd': ['string', ['string']],
|
|
||||||
'FileGet': ['string', ['string']],
|
|
||||||
'FileDelete': ['string', ['string']]
|
|
||||||
});
|
|
||||||
|
|
||||||
if (go_judge.Init(JSON.stringify({
|
|
||||||
cinitPath: "/judge/cinit",
|
|
||||||
parallelism: 4,
|
|
||||||
}))) {
|
|
||||||
console.log("Failed to init go judge");
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = JSON.parse(go_judge.Exec(JSON.stringify({
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["/bin/cat", "test.txt"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{
|
|
||||||
"content": ""
|
|
||||||
}, {
|
|
||||||
"name": "stdout",
|
|
||||||
"max": 10240
|
|
||||||
}, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 10000000000,
|
|
||||||
"memoryLimit": 104857600,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyIn": {
|
|
||||||
"test.txt": {
|
|
||||||
"content": "TEST"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
})));
|
|
||||||
console.log(result);
|
|
||||||
|
|
||||||
// Async
|
|
||||||
go_judge.Exec.async(JSON.stringify({
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["/bin/cat", "test.txt"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{
|
|
||||||
"content": ""
|
|
||||||
}, {
|
|
||||||
"name": "stdout",
|
|
||||||
"max": 10240
|
|
||||||
}, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 10000000000,
|
|
||||||
"memoryLimit": 104857600,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyIn": {
|
|
||||||
"test.txt": {
|
|
||||||
"content": "TEST"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}), (err, res) => {
|
|
||||||
if (err) throw err;
|
|
||||||
console.log(JSON.parse(res));
|
|
||||||
});
|
|
||||||
|
|
||||||
const fileAdd = (param) => new Promise((resolve, reject) => {
|
|
||||||
go_judge.FileAdd.async(JSON.stringify(param), (err, res) => {
|
|
||||||
if (err != null) { reject(err); } else { resolve(res); }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const fileList = () => new Promise((resolve, reject) => {
|
|
||||||
go_judge.FileList.async((err, res) => {
|
|
||||||
if (err != null && res == null) { reject(err); } else { resolve(JSON.parse(res)); }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const fileGet = (param) => new Promise((resolve, reject) => {
|
|
||||||
go_judge.FileGet.async(JSON.stringify(param), (err, res) => {
|
|
||||||
if (err != null && res == null) { reject(err); } else { resolve(res); }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const fileDelete = (param) => new Promise((resolve, reject) => {
|
|
||||||
go_judge.FileDelete.async(JSON.stringify(param), (err, res) => {
|
|
||||||
if (err != null && res == null) { reject(err); } else { resolve(res); }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const fileOps = async () => {
|
|
||||||
const fileId = await fileAdd({ name: 'Name', content: 'Content' });
|
|
||||||
console.log(fileId);
|
|
||||||
const list = await fileList();
|
|
||||||
console.log(list);
|
|
||||||
const file = await fileGet({ id: fileId });
|
|
||||||
console.log(file);
|
|
||||||
const d = await fileDelete({ id: fileId });
|
|
||||||
console.log(d);
|
|
||||||
const e = await fileList();
|
|
||||||
console.log(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
fileOps();
|
|
||||||
```
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
requestId: '',
|
|
||||||
results: [
|
|
||||||
{
|
|
||||||
status: 'Accepted',
|
|
||||||
exitStatus: 0,
|
|
||||||
time: 814048,
|
|
||||||
memory: 253952,
|
|
||||||
files: [Object]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
Please use PostMan or similar tools to send request to `http://localhost:5050/run`
|
|
||||||
|
|
||||||
<details><summary>Single (this example require `apt install g++` inside the container)</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["/usr/bin/g++", "a.cc", "-o", "a"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{
|
|
||||||
"content": ""
|
|
||||||
}, {
|
|
||||||
"name": "stdout",
|
|
||||||
"max": 10240
|
|
||||||
}, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 10000000000,
|
|
||||||
"memoryLimit": 104857600,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyIn": {
|
|
||||||
"a.cc": {
|
|
||||||
"content": "#include <iostream>\nusing namespace std;\nint main() {\nint a, b;\ncin >> a >> b;\ncout << a + b << endl;\n}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"copyOut": ["stdout", "stderr"],
|
|
||||||
"copyOutCached": ["a.cc", "a"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"status": "Accepted",
|
|
||||||
"exitStatus": 0,
|
|
||||||
"time": 303225231,
|
|
||||||
"memory": 32243712,
|
|
||||||
"runTime": 524177700,
|
|
||||||
"files": {
|
|
||||||
"stderr": "",
|
|
||||||
"stdout": ""
|
|
||||||
},
|
|
||||||
"fileIds": {
|
|
||||||
"a": "5LWIZAA45JHX4Y4Z",
|
|
||||||
"a.cc": "NOHPGGDTYQUFRSLJ"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["a"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{
|
|
||||||
"content": "1 1"
|
|
||||||
}, {
|
|
||||||
"name": "stdout",
|
|
||||||
"max": 10240
|
|
||||||
}, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 10000000000,
|
|
||||||
"memoryLimit": 104857600,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyIn": {
|
|
||||||
"a": {
|
|
||||||
"fileId": "5LWIZAA45JHX4Y4Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"status": "Accepted",
|
|
||||||
"exitStatus": 0,
|
|
||||||
"time": 1173000,
|
|
||||||
"memory": 10637312,
|
|
||||||
"runTime": 1100200,
|
|
||||||
"files": {
|
|
||||||
"stderr": "",
|
|
||||||
"stdout": "2\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details><summary>Multiple (interaction problem)</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["/bin/cat", "1"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{
|
|
||||||
"content": ""
|
|
||||||
}, null, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 1000000000,
|
|
||||||
"memoryLimit": 1048576,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyIn": {
|
|
||||||
"1": { "content": "TEST 1" }
|
|
||||||
},
|
|
||||||
"copyOut": ["stderr"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"args": ["/bin/cat"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [null, {
|
|
||||||
"name": "stdout",
|
|
||||||
"max": 10240
|
|
||||||
}, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 1000000000,
|
|
||||||
"memoryLimit": 1048576,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyOut": ["stdout", "stderr"]
|
|
||||||
}],
|
|
||||||
"pipeMapping": [{
|
|
||||||
"in" : {"index": 0, "fd": 1 },
|
|
||||||
"out" : {"index": 1, "fd" : 0 }
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"status": "Accepted",
|
|
||||||
"exitStatus": 0,
|
|
||||||
"time": 1545123,
|
|
||||||
"memory": 253952,
|
|
||||||
"runTime": 4148800,
|
|
||||||
"files": {
|
|
||||||
"stderr": ""
|
|
||||||
},
|
|
||||||
"fileIds": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"status": "Accepted",
|
|
||||||
"exitStatus": 0,
|
|
||||||
"time": 1501463,
|
|
||||||
"memory": 253952,
|
|
||||||
"runTime": 5897700,
|
|
||||||
"files": {
|
|
||||||
"stderr": "",
|
|
||||||
"stdout": "TEST 1"
|
|
||||||
},
|
|
||||||
"fileIds": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details><summary>Compile On Windows (cygwin)</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["C:\\Cygwin\\bin\\g++", "a.cc", "-o", "a"],
|
|
||||||
"env": ["PATH=C:\\Cygwin\\bin;"],
|
|
||||||
"files": [{
|
|
||||||
"content": ""
|
|
||||||
}, {
|
|
||||||
"name": "stdout",
|
|
||||||
"max": 10240
|
|
||||||
}, {
|
|
||||||
"name": "stderr",
|
|
||||||
"max": 10240
|
|
||||||
}],
|
|
||||||
"cpuLimit": 10000000000,
|
|
||||||
"memoryLimit": 104857600,
|
|
||||||
"procLimit": 50,
|
|
||||||
"copyIn": {
|
|
||||||
"a.cc": {
|
|
||||||
"content": "#include <iostream>\n#include <signal.h>\n#include <unistd.h>\nusing namespace std;\nint main() {\nint a, b;\ncin >> a >> b;\ncout << a + b << endl;\n}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"copyOutCached": ["a.exe"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"status": "Accepted",
|
|
||||||
"exitStatus": 0,
|
|
||||||
"time": 140625000,
|
|
||||||
"memory": 36286464,
|
|
||||||
"files": {
|
|
||||||
"stderr": "",
|
|
||||||
"stdout": ""
|
|
||||||
},
|
|
||||||
"fileIds": {
|
|
||||||
"a.exe": "HLQH2OF4MXUUJBCB"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<details><summary>Infinite loop with cpu rate control</summary>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cmd": [{
|
|
||||||
"args": ["/usr/bin/python3", "1.py"],
|
|
||||||
"env": ["PATH=/usr/bin:/bin"],
|
|
||||||
"files": [{"content": ""}, {"name": "stdout","max": 10240}, {"name": "stderr","max": 10240}],
|
|
||||||
"cpuLimit": 3000000000,
|
|
||||||
"clockLimit": 4000000000,
|
|
||||||
"memoryLimit": 104857600,
|
|
||||||
"procLimit": 50,
|
|
||||||
"cpuRate": 0.1,
|
|
||||||
"copyIn": {
|
|
||||||
"1.py": {
|
|
||||||
"content": "while True:\n pass"
|
|
||||||
}
|
|
||||||
}}]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"status": "Time Limit Exceeded",
|
|
||||||
"exitStatus": 9,
|
|
||||||
"time": 414803599,
|
|
||||||
"memory": 3657728,
|
|
||||||
"runTime": 4046054900,
|
|
||||||
"files": {
|
|
||||||
"stderr": "",
|
|
||||||
"stdout": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
@ -659,10 +104,6 @@ Sandbox:
|
|||||||
|
|
||||||
Environment variable will be override by command line arguments if they both present and all command line arguments have its correspond environment variable (e.g. `ES_HTTP_ADDR`). Run `go-judge --help` to see all the environment variable configurations.
|
Environment variable will be override by command line arguments if they both present and all command line arguments have its correspond environment variable (e.g. `ES_HTTP_ADDR`). Run `go-judge --help` to see all the environment variable configurations.
|
||||||
|
|
||||||
### Build go judge
|
|
||||||
|
|
||||||
Build by your own `docker build -t go-judge -f Dockerfile.exec .`
|
|
||||||
|
|
||||||
### Build Shared object
|
### Build Shared object
|
||||||
|
|
||||||
Build container init `cinit`:
|
Build container init `cinit`:
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
root:x:0:0::/w:/bin/bash
|
|
||||||
go-judge:x:1536:1536::/w:/bin/bash
|
|
Loading…
Reference in New Issue
Block a user