2024-01-06 19:21:37 +08:00
|
|
|
package runner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"git.0x7f.app/WOJ/woj-server/internal/e"
|
|
|
|
"git.0x7f.app/WOJ/woj-server/pkg/file"
|
|
|
|
"git.0x7f.app/WOJ/woj-server/pkg/utils"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2024-01-27 17:37:27 +08:00
|
|
|
"strings"
|
2024-01-06 19:21:37 +08:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2024-02-15 12:53:57 +08:00
|
|
|
type ProblemRunResult struct {
|
|
|
|
QueueId uint64
|
|
|
|
Status RuntimeStatus
|
|
|
|
}
|
|
|
|
|
|
|
|
type ProblemRunResults map[int]*ProblemRunResult
|
|
|
|
|
2024-01-29 21:15:39 +08:00
|
|
|
func (s *service) SandboxArgsBuilder(meta *JudgeMeta, id int) string {
|
2024-01-27 17:37:27 +08:00
|
|
|
var args []string
|
|
|
|
|
2024-01-29 21:15:39 +08:00
|
|
|
args = append(args, fmt.Sprintf("--memory_limit=%d", meta.Cfg.Lang.Runtime.Run.MemoryLimit))
|
|
|
|
args = append(args, fmt.Sprintf("--nproc_limit=%d", meta.Cfg.Lang.Runtime.Run.NProcLimit))
|
|
|
|
args = append(args, fmt.Sprintf("--time_limit=%d", meta.Cfg.Lang.Runtime.Run.TimeLimit))
|
|
|
|
args = append(args, fmt.Sprintf("--sandbox_template=%s", meta.Cfg.Lang.Lang))
|
2024-01-30 20:56:07 +08:00
|
|
|
args = append(args, fmt.Sprintf("--sandbox_action=%s", "ret"))
|
|
|
|
args = append(args, fmt.Sprintf("--uid=%d", 1000))
|
|
|
|
args = append(args, fmt.Sprintf("--gid=%d", 1000))
|
2024-01-27 17:37:27 +08:00
|
|
|
args = append(args, fmt.Sprintf("--file_input=/woj/problem/data/input/%d.input", id))
|
|
|
|
args = append(args, fmt.Sprintf("--file_output=/woj/user/%d.out.usr", id))
|
|
|
|
args = append(args, fmt.Sprintf("--file_info=/woj/user/%d.info", id))
|
2024-01-30 20:56:07 +08:00
|
|
|
|
|
|
|
if meta.Cfg.Lang.JudgeInterpreter() != "" {
|
|
|
|
sourceFile := fmt.Sprintf("%s.%s", meta.Run.User, meta.Run.Lang)
|
|
|
|
|
|
|
|
args = append(args, fmt.Sprintf("--program=%s", meta.Cfg.Lang.JudgeInterpreter()))
|
|
|
|
args = append(args, fmt.Sprintf("--program_arg=/woj/user/%s", sourceFile))
|
|
|
|
} else {
|
|
|
|
args = append(args, fmt.Sprintf("--program=/woj/user/%s.out", meta.Run.User))
|
|
|
|
}
|
|
|
|
|
|
|
|
s.log.Debug("[run] sandbox args", zap.Strings("args", args))
|
2024-01-27 17:37:27 +08:00
|
|
|
|
|
|
|
return strings.Join(args, " ")
|
|
|
|
}
|
|
|
|
|
2024-02-15 12:53:57 +08:00
|
|
|
func (s *service) ProblemRun(meta *JudgeMeta) ProblemRunResults {
|
2024-01-29 21:15:39 +08:00
|
|
|
workDir := filepath.Join(UserDir, meta.Run.User)
|
|
|
|
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "data", "input")
|
2024-01-27 23:07:14 +08:00
|
|
|
|
|
|
|
runtimeArgs := RuntimeArgs{
|
2024-03-13 20:03:12 +08:00
|
|
|
Rootfs: RootfsRunDir,
|
2024-01-27 23:07:14 +08:00
|
|
|
// sh, woj_launcher:program, woj_launcher:killer, woj_launcher:stat
|
2024-01-29 21:15:39 +08:00
|
|
|
Pid: int64(meta.Cfg.Lang.Runtime.Run.NProcLimit + 4),
|
|
|
|
Memory: uint64(meta.Cfg.Lang.Runtime.Run.MemoryLimit * 1024 * 1024),
|
2024-01-27 23:07:14 +08:00
|
|
|
// woj-sandbox killer will add 1 more second, here we add 1 also
|
2024-01-29 21:15:39 +08:00
|
|
|
Timeout: time.Duration((meta.Cfg.Lang.Runtime.Run.TimeLimit+1000)/1000+1+1) * time.Second,
|
2024-01-27 23:07:14 +08:00
|
|
|
}
|
2024-01-06 19:21:37 +08:00
|
|
|
|
2024-02-15 12:53:57 +08:00
|
|
|
result := make(ProblemRunResults)
|
2024-01-29 21:15:39 +08:00
|
|
|
for _, task := range meta.Cfg.All.Tasks {
|
2024-02-15 12:53:57 +08:00
|
|
|
f := func(id int) func() (interface{}, error) {
|
|
|
|
return func() (interface{}, error) {
|
2024-01-06 19:21:37 +08:00
|
|
|
testCase := filepath.Join(dataDir, fmt.Sprintf("%d.input", id))
|
|
|
|
ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id))
|
|
|
|
ifoFile := filepath.Join(workDir, fmt.Sprintf("%d.info", id))
|
|
|
|
|
2024-01-30 20:56:07 +08:00
|
|
|
targetFile := fmt.Sprintf("%s.out", meta.Run.User)
|
|
|
|
targetPath := filepath.Join(workDir, targetFile)
|
|
|
|
sourceFile := fmt.Sprintf("%s.%s", meta.Run.User, meta.Run.Lang)
|
|
|
|
sourcePath := filepath.Join(workDir, sourceFile)
|
|
|
|
|
2024-01-27 17:37:27 +08:00
|
|
|
args := &RunArgs{
|
|
|
|
Program: ProgramArgs{
|
|
|
|
Args: []string{
|
|
|
|
"sh", "-c",
|
|
|
|
"cd /woj/user && /woj/framework/scripts/woj_launcher " +
|
2024-01-29 21:15:39 +08:00
|
|
|
s.SandboxArgsBuilder(meta, id),
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
},
|
2024-01-27 23:07:14 +08:00
|
|
|
Runtime: runtimeArgs,
|
2024-01-27 17:37:27 +08:00
|
|
|
}
|
2024-03-13 20:03:12 +08:00
|
|
|
args.Runtime.Mount = []MountInfo{
|
2024-01-27 17:37:27 +08:00
|
|
|
{
|
|
|
|
Source: testCase,
|
|
|
|
Destination: fmt.Sprintf("/woj/problem/data/input/%d.input", id),
|
2024-03-13 20:03:12 +08:00
|
|
|
Readonly: true,
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
{
|
2024-01-30 20:56:07 +08:00
|
|
|
Source: utils.If(meta.Cfg.Lang.JudgeInterpreter() != "", sourcePath, targetPath),
|
|
|
|
Destination: fmt.Sprintf("/woj/user/%s", utils.If(meta.Cfg.Lang.JudgeInterpreter() != "", sourceFile, targetFile)),
|
2024-03-13 20:03:12 +08:00
|
|
|
Readonly: true,
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Source: ansFile,
|
|
|
|
Destination: fmt.Sprintf("/woj/user/%d.out.usr", id),
|
2024-03-13 20:03:12 +08:00
|
|
|
Readonly: false,
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Source: ifoFile,
|
|
|
|
Destination: fmt.Sprintf("/woj/user/%d.info", id),
|
2024-03-13 20:03:12 +08:00
|
|
|
Readonly: false,
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2024-01-06 19:21:37 +08:00
|
|
|
err := utils.NewMust().
|
|
|
|
DoAny(func() error { return os.Remove(ansFile) }).
|
|
|
|
DoAny(func() error { return os.Remove(ifoFile) }).
|
|
|
|
Do(func() error { return file.TouchErr(ansFile) }).
|
|
|
|
Do(func() error { return file.TouchErr(ifoFile) }).
|
|
|
|
Done()
|
|
|
|
|
|
|
|
if err != nil {
|
2024-02-15 12:53:57 +08:00
|
|
|
s.log.Info("[run] prepare failed", zap.Error(err), zap.Any("meta", *meta))
|
|
|
|
return nil, err
|
2024-01-06 19:21:37 +08:00
|
|
|
}
|
2024-02-15 12:53:57 +08:00
|
|
|
|
2024-03-13 20:03:12 +08:00
|
|
|
return s.JailRun(args)
|
2024-01-06 19:21:37 +08:00
|
|
|
}
|
|
|
|
}(task.Id)
|
|
|
|
|
2024-02-15 12:53:57 +08:00
|
|
|
queueId := s.pool.AddTask(f)
|
|
|
|
result[task.Id] = &ProblemRunResult{
|
|
|
|
QueueId: queueId,
|
|
|
|
}
|
2024-01-06 19:21:37 +08:00
|
|
|
}
|
|
|
|
|
2024-02-15 12:53:57 +08:00
|
|
|
for i := range result {
|
|
|
|
waitBuf := s.pool.WaitForTask(result[i].QueueId)
|
|
|
|
if waitBuf.Error != nil {
|
|
|
|
s.log.Error(
|
|
|
|
"[run] wait for problem run failed",
|
|
|
|
zap.Error(waitBuf.Error),
|
|
|
|
zap.Any("meta", *meta))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
val, ok := waitBuf.Value.(RuntimeStatus)
|
|
|
|
if !ok {
|
|
|
|
s.log.Error(
|
|
|
|
"[run] container run is not returning RuntimeStatus",
|
|
|
|
zap.Any("waitBuf", waitBuf),
|
|
|
|
zap.Any("meta", *meta))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
result[i].Status = val
|
2024-01-06 19:21:37 +08:00
|
|
|
}
|
2024-02-15 12:53:57 +08:00
|
|
|
|
|
|
|
return result
|
2024-01-06 19:21:37 +08:00
|
|
|
}
|
|
|
|
|
2024-01-29 21:15:39 +08:00
|
|
|
func (s *service) ProblemJudge(meta *JudgeMeta) {
|
|
|
|
workDir := filepath.Join(UserDir, meta.Run.User)
|
|
|
|
dataDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "data")
|
|
|
|
judgeDir := filepath.Join(ProblemDir, fmt.Sprintf("%d", meta.Run.Version), "judge")
|
|
|
|
script := meta.Cfg.Lang.JudgeScript()
|
2024-01-27 23:07:14 +08:00
|
|
|
|
|
|
|
runtimeArgs := RuntimeArgs{
|
2024-03-13 20:03:12 +08:00
|
|
|
Rootfs: RootfsFullDir,
|
2024-01-29 21:15:39 +08:00
|
|
|
Pid: int64(meta.Cfg.Lang.Runtime.Check.NProcLimit + 2), // bash + make
|
|
|
|
Memory: uint64(meta.Cfg.Lang.Runtime.Check.MemoryLimit * 1024 * 1024),
|
|
|
|
Timeout: time.Duration((meta.Cfg.Lang.Runtime.Check.TimeLimit+1000)/1000) * time.Second,
|
2024-01-27 23:07:14 +08:00
|
|
|
}
|
2024-01-06 19:21:37 +08:00
|
|
|
|
2024-02-15 12:53:57 +08:00
|
|
|
ids := make([]uint64, 0)
|
2024-01-29 21:15:39 +08:00
|
|
|
for _, task := range meta.Cfg.All.Tasks {
|
2024-02-15 12:53:57 +08:00
|
|
|
f := func(id int) func() (interface{}, error) {
|
|
|
|
return func() (interface{}, error) {
|
2024-01-06 19:21:37 +08:00
|
|
|
ansFile := filepath.Join(workDir, fmt.Sprintf("%d.out.usr", id))
|
|
|
|
jdgFile := filepath.Join(workDir, fmt.Sprintf("%d.judge", id))
|
|
|
|
|
2024-01-27 17:37:27 +08:00
|
|
|
args := &RunArgs{
|
|
|
|
Program: ProgramArgs{
|
|
|
|
Args: []string{
|
|
|
|
"sh", "-c",
|
2024-01-27 23:07:14 +08:00
|
|
|
fmt.Sprintf("cd /woj/user && make -f %s judge", script),
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
Env: []string{
|
|
|
|
fmt.Sprintf("TEST_NUM=%d", id),
|
2024-01-29 21:15:39 +08:00
|
|
|
fmt.Sprintf("CMP=%s", meta.Cfg.Lang.Judge.Cmp),
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
},
|
2024-01-27 23:07:14 +08:00
|
|
|
Runtime: runtimeArgs,
|
2024-01-27 17:37:27 +08:00
|
|
|
}
|
2024-03-13 20:03:12 +08:00
|
|
|
args.Runtime.Mount = []MountInfo{
|
2024-01-27 17:37:27 +08:00
|
|
|
{
|
|
|
|
Source: judgeDir,
|
|
|
|
Destination: "/woj/problem/judge",
|
2024-03-13 20:03:12 +08:00
|
|
|
Readonly: true,
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Source: dataDir,
|
|
|
|
Destination: "/woj/problem/data",
|
2024-03-13 20:03:12 +08:00
|
|
|
Readonly: true,
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Source: ansFile,
|
|
|
|
Destination: fmt.Sprintf("/woj/user/%d.out.usr", id),
|
2024-03-13 20:03:12 +08:00
|
|
|
Readonly: true,
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Source: jdgFile,
|
|
|
|
Destination: fmt.Sprintf("/woj/user/%d.judge", id),
|
2024-03-13 20:03:12 +08:00
|
|
|
Readonly: false,
|
2024-01-27 17:37:27 +08:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2024-01-06 19:21:37 +08:00
|
|
|
err := utils.NewMust().
|
|
|
|
DoAny(func() error { return os.Remove(jdgFile) }).
|
|
|
|
Do(func() error { return file.TouchErr(jdgFile) }).
|
|
|
|
Done()
|
|
|
|
|
|
|
|
if err != nil {
|
2024-02-15 12:53:57 +08:00
|
|
|
s.log.Info("[judge] judge prepare failed", zap.Error(err), zap.Any("meta", *meta))
|
|
|
|
return nil, err
|
2024-01-06 19:21:37 +08:00
|
|
|
}
|
2024-02-15 12:53:57 +08:00
|
|
|
|
2024-03-13 20:03:12 +08:00
|
|
|
return s.JailRun(args)
|
2024-01-06 19:21:37 +08:00
|
|
|
}
|
|
|
|
}(task.Id)
|
|
|
|
|
|
|
|
id := s.pool.AddTask(f)
|
|
|
|
ids = append(ids, id)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, id := range ids {
|
2024-01-28 18:16:04 +08:00
|
|
|
_ = s.pool.WaitForTask(id)
|
2024-01-06 19:21:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-27 23:07:14 +08:00
|
|
|
func (s *service) RunAndJudge(meta *JudgeMeta) (*JudgeStatus, int32, e.Status) {
|
2024-01-29 21:15:39 +08:00
|
|
|
// 1. run user program
|
2024-02-15 12:53:57 +08:00
|
|
|
results := s.ProblemRun(meta)
|
2024-01-06 19:21:37 +08:00
|
|
|
|
2024-01-29 21:15:39 +08:00
|
|
|
// 2. run judge
|
|
|
|
s.ProblemJudge(meta)
|
2024-01-06 19:21:37 +08:00
|
|
|
|
2024-02-15 12:53:57 +08:00
|
|
|
// 3. final JudgeStatus
|
|
|
|
status, pts := s.CheckResults(meta, results)
|
2024-01-06 19:21:37 +08:00
|
|
|
|
2024-02-15 12:53:57 +08:00
|
|
|
return status, pts, e.Success
|
2024-01-06 19:21:37 +08:00
|
|
|
}
|