package sandbox import ( "log/slog" "os" "path/filepath" "strings" "focs.ji.sjtu.edu.cn/git/JOJ/JOJ3/internal/stage" "github.com/criyle/go-judge/pb" ) // copied from https://github.com/criyle/go-judge/blob/master/cmd/go-judge-shell/grpc.go func convertPBCmd(cmd []stage.Cmd) []*pb.Request_CmdType { var ret []*pb.Request_CmdType for _, c := range cmd { ret = append(ret, &pb.Request_CmdType{ Args: c.Args, Env: c.Env, Tty: c.TTY, Files: convertPBFiles([]*stage.CmdFile{c.Stdin, c.Stdout, c.Stderr}), CpuTimeLimit: c.CPULimit, ClockTimeLimit: c.ClockLimit, MemoryLimit: c.MemoryLimit, StackLimit: c.StackLimit, ProcLimit: c.ProcLimit, CpuRateLimit: c.CPURateLimit, CpuSetLimit: c.CPUSetLimit, DataSegmentLimit: c.DataSegmentLimit, AddressSpaceLimit: c.AddressSpaceLimit, CopyIn: convertPBCopyIn(c.CopyIn, c.CopyInDir), CopyOut: convertPBCopyOut(c.CopyOut), CopyOutCached: convertPBCopyOut(c.CopyOutCached), CopyOutMax: c.CopyOutMax, CopyOutDir: c.CopyOutDir, Symlinks: convertSymlink(c.CopyIn), }) } return ret } func convertPBCopyIn( copyIn map[string]stage.CmdFile, copyInDir string, ) map[string]*pb.Request_File { if copyInDir != "" { _ = filepath.Walk(copyInDir, func(path string, info os.FileInfo, err error) error { if err != nil { return nil } absPath, err := filepath.Abs(path) if err != nil { return nil } if !info.IsDir() { copyIn[path] = stage.CmdFile{Src: &absPath} } return nil }) } rt := make(map[string]*pb.Request_File, len(copyIn)) for k, i := range copyIn { if i.Symlink != nil { continue } rt[k] = convertPBFile(i) } return rt } func convertPBCopyOut(copyOut []string) []*pb.Request_CmdCopyOutFile { rt := make([]*pb.Request_CmdCopyOutFile, 0, len(copyOut)) for _, n := range copyOut { optional := false if strings.HasSuffix(n, "?") { optional = true n = strings.TrimSuffix(n, "?") } rt = append(rt, &pb.Request_CmdCopyOutFile{ Name: n, Optional: optional, }) } return rt } func convertSymlink(copyIn map[string]stage.CmdFile) map[string]string { ret := make(map[string]string) for k, v := range copyIn { if v.Symlink == nil { continue } ret[k] = *v.Symlink } return ret } func convertPBFiles(files []*stage.CmdFile) []*pb.Request_File { var ret []*pb.Request_File for _, f := range files { if f == nil { ret = append(ret, nil) } else { ret = append(ret, convertPBFile(*f)) } } return ret } func convertPBFile(i stage.CmdFile) *pb.Request_File { switch { case i.Src != nil: if !filepath.IsAbs(*i.Src) { absPath, err := filepath.Abs(*i.Src) if err == nil { i.Src = &absPath } } s, err := os.ReadFile(*i.Src) if err != nil { s = []byte{} slog.Error("read file error", "path", *i.Src, "error", err) } return &pb.Request_File{File: &pb.Request_File_Memory{Memory: &pb.Request_MemoryFile{Content: s}}} case i.Content != nil: s := strToBytes(*i.Content) return &pb.Request_File{File: &pb.Request_File_Memory{Memory: &pb.Request_MemoryFile{Content: s}}} case i.FileID != nil: return &pb.Request_File{File: &pb.Request_File_Cached{Cached: &pb.Request_CachedFile{FileID: *i.FileID}}} case i.Name != nil && i.Max != nil: return &pb.Request_File{File: &pb.Request_File_Pipe{Pipe: &pb.Request_PipeCollector{Name: *i.Name, Max: *i.Max, Pipe: i.Pipe}}} case i.StreamIn: return &pb.Request_File{File: &pb.Request_File_StreamIn{}} case i.StreamOut: return &pb.Request_File{File: &pb.Request_File_StreamOut{}} } return nil } func convertPBResult(res []*pb.Response_Result) []stage.ExecutorResult { var ret []stage.ExecutorResult for _, r := range res { ret = append(ret, stage.ExecutorResult{ Status: stage.Status(r.Status), ExitStatus: int(r.ExitStatus), Error: r.Error, Time: r.Time, RunTime: r.RunTime, Memory: r.Memory, Files: convertFiles(r.Files), Buffs: r.Files, FileIDs: r.FileIDs, FileError: convertPBFileError(r.FileError), }) } return ret } func convertFiles(buf map[string][]byte) map[string]string { ret := make(map[string]string, len(buf)) for k, v := range buf { ret[k] = byteArrayToString(v) } return ret } func convertPBFileError(fe []*pb.Response_FileError) []stage.FileError { var ret []stage.FileError for _, v := range fe { ret = append(ret, stage.FileError{ Name: v.Name, Type: stage.FileErrorType(v.Type), Message: v.Message, }) } return ret }