mirror of
https://github.com/criyle/go-judge.git
synced 2025-11-04 14:50:02 +08:00
202 lines
4.2 KiB
Go
202 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"sync"
|
|
"sync/atomic"
|
|
"syscall"
|
|
|
|
"github.com/criyle/go-judge/client/syzojclient"
|
|
"github.com/criyle/go-judge/file"
|
|
"github.com/criyle/go-judge/file/memfile"
|
|
"github.com/criyle/go-judge/judger"
|
|
"github.com/criyle/go-judge/language"
|
|
"github.com/criyle/go-judge/runner"
|
|
"github.com/criyle/go-judge/taskqueue/channel"
|
|
"github.com/criyle/go-judge/types"
|
|
"github.com/criyle/go-sandbox/daemon"
|
|
"github.com/criyle/go-sandbox/pkg/cgroup"
|
|
"github.com/criyle/go-sandbox/pkg/mount"
|
|
)
|
|
|
|
func init() {
|
|
daemon.Init()
|
|
}
|
|
|
|
func main() {
|
|
log.Printf("GOARCH=%s GOOS=%s", runtime.GOARCH, runtime.GOOS)
|
|
|
|
var url = os.Getenv("WEB_URL")
|
|
c, errCh, err := syzojclient.NewClient(url, "123")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
log.Printf("Connected to %s", url)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
done := make(chan struct{})
|
|
root, err := ioutil.TempDir("", "dm")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
q := channel.New()
|
|
m, err := mount.NewBuilder().
|
|
// basic exec and lib
|
|
WithBind("/bin", "bin", true).
|
|
WithBind("/lib", "lib", true).
|
|
WithBind("/lib64", "lib64", true).
|
|
WithBind("/usr", "usr", true).
|
|
// java wants /proc/self/exe as it need relative path for lib
|
|
// however, /proc gives interface like /proc/1/fd/3 ..
|
|
// it is fine since open that file will be a EPERM
|
|
// changing the fs uid and gid would be a good idea
|
|
WithMount(mount.Mount{
|
|
Source: "proc",
|
|
Target: "proc",
|
|
FsType: "proc",
|
|
Flags: syscall.MS_NOSUID,
|
|
}).
|
|
// some compiler have multiple version
|
|
WithBind("/etc/alternatives", "etc/alternatives", true).
|
|
// fpc wants /etc/fpc.cfg
|
|
WithBind("/etc/fpc.cfg", "etc/fpc.cfg", true).
|
|
// go wants /dev/null
|
|
WithBind("/dev/null", "dev/null", false).
|
|
// work dir
|
|
WithTmpfs("w", "size=8m,nr_inodes=4k").
|
|
// tmp dir
|
|
WithTmpfs("tmp", "size=8m,nr_inodes=4k").
|
|
// finished
|
|
Build(true)
|
|
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
b := &daemon.Builder{
|
|
Root: root,
|
|
Mounts: m,
|
|
CredGenerator: newCredGen(),
|
|
Stderr: true,
|
|
}
|
|
cgb, err := cgroup.NewBuilder("go-judger").WithCPUAcct().WithMemory().WithPids().FilterByEnv()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
log.Printf("Initialized cgroup: %v", cgb)
|
|
r := &runner.Runner{
|
|
Builder: b,
|
|
Queue: q,
|
|
CgroupBuilder: cgb,
|
|
Language: &dumbLang{},
|
|
}
|
|
const parallism = 4
|
|
for i := 0; i < parallism; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
r.Loop(done)
|
|
}()
|
|
}
|
|
|
|
j := &judger.Judger{
|
|
Client: c,
|
|
Sender: q,
|
|
Builder: &dumbBuilder{},
|
|
}
|
|
go j.Loop(done)
|
|
|
|
go func() {
|
|
panic(<-errCh)
|
|
}()
|
|
<-c.Done
|
|
}
|
|
|
|
type credGen struct {
|
|
cur uint32
|
|
}
|
|
|
|
func newCredGen() *credGen {
|
|
return &credGen{cur: 10000}
|
|
}
|
|
|
|
func (c *credGen) Get() syscall.Credential {
|
|
n := atomic.AddUint32(&c.cur, 1)
|
|
return syscall.Credential{
|
|
Uid: n,
|
|
Gid: n,
|
|
}
|
|
}
|
|
|
|
type dumbBuilder struct {
|
|
}
|
|
|
|
func (b *dumbBuilder) Build([]file.File) (types.ProblemConfig, error) {
|
|
const n = 100
|
|
|
|
c := make([]types.Case, 0, n)
|
|
for i := 0; i < n; i++ {
|
|
inputContent := strconv.Itoa(i) + " " + strconv.Itoa(i)
|
|
outputContent := strconv.Itoa(i + i)
|
|
c = append(c, types.Case{
|
|
Input: memfile.New("input", []byte(inputContent)),
|
|
Answer: memfile.New("output", []byte(outputContent)),
|
|
})
|
|
}
|
|
|
|
return types.ProblemConfig{
|
|
Type: "standard",
|
|
Subtasks: []types.SubTask{
|
|
types.SubTask{
|
|
ScoringType: "sum",
|
|
Score: 100,
|
|
Cases: c,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
type dumbLang struct {
|
|
}
|
|
|
|
func (d *dumbLang) Get(name string, t language.Type) language.ExecParam {
|
|
const pathEnv = "PATH=/usr/local/bin:/usr/bin:/bin"
|
|
|
|
switch t {
|
|
case language.TypeCompile:
|
|
return language.ExecParam{
|
|
Args: []string{"/usr/bin/g++", "-O2", "-o", "a", "a.cc"},
|
|
Env: []string{pathEnv},
|
|
|
|
SourceFileName: "a.cc",
|
|
CompiledFileNames: []string{"a"},
|
|
|
|
TimeLimit: 10 * 1000,
|
|
MemoryLimit: 512 << 10,
|
|
ProcLimit: 100,
|
|
OutputLimit: 64 << 10,
|
|
}
|
|
|
|
case language.TypeExec:
|
|
return language.ExecParam{
|
|
Args: []string{"a"},
|
|
Env: []string{pathEnv},
|
|
|
|
SourceFileName: "a.cc",
|
|
CompiledFileNames: []string{"a"},
|
|
|
|
TimeLimit: 1 * 1000,
|
|
MemoryLimit: 256 << 10,
|
|
ProcLimit: 1,
|
|
OutputLimit: 64 << 10,
|
|
}
|
|
|
|
default:
|
|
return language.ExecParam{}
|
|
}
|
|
}
|