go-judge/cmd/ffi/cinterface.go

212 lines
4.3 KiB
Go

package main
import "C"
import (
"bytes"
"context"
"encoding/json"
"log"
"os"
"time"
"github.com/criyle/go-judge/cmd/executorserver/model"
"github.com/criyle/go-judge/env"
"github.com/criyle/go-judge/env/pool"
"github.com/criyle/go-judge/filestore"
"github.com/criyle/go-judge/worker"
)
type initParameter struct {
CInitPath string `json:"cinitPath"`
Parallelism int `json:"parallelism"`
TmpFsParam string `json:"tmpfsParam"`
Dir string `json:"dir"`
NetShare bool `json:"netShare"`
MountConf string `json:"mountConf"`
SrcPrefix string `json:"srcPrefix"`
CgroupPrefix string `json:"cgroupPrefix"`
CPUSet string `json:"cpuset"`
CredStart int `json:"credStart"`
}
var (
fs filestore.FileStore
work worker.Worker
srcPrefix string
)
func newFilsStore(dir string) filestore.FileStore {
var fs filestore.FileStore
if dir == "" {
fs = filestore.NewFileMemoryStore()
} else {
os.MkdirAll(dir, 0755)
fs = filestore.NewFileLocalStore(dir)
}
return fs
}
// Init initialize the sandbox environment
//export Init
func Init(i *C.char) C.int {
is := C.GoString(i)
var ip initParameter
if err := json.NewDecoder(bytes.NewBufferString(is)).Decode(&ip); err != nil {
return -1
}
if ip.Parallelism == 0 {
ip.Parallelism = 4
}
if ip.TmpFsParam == "" {
ip.TmpFsParam = "size=16m,nr_inodes=4k"
}
if ip.MountConf == "" {
ip.MountConf = "mount.yaml"
}
srcPrefix = ip.SrcPrefix
fs = newFilsStore(ip.Dir)
b, err := env.NewBuilder(env.Config{
ContainerInitPath: ip.CInitPath,
MountConf: ip.MountConf,
TmpFsParam: ip.TmpFsParam,
NetShare: ip.NetShare,
CgroupPrefix: ip.CgroupPrefix,
Cpuset: ip.CPUSet,
ContainerCredStart: ip.CredStart,
Logger: nopLogger{},
})
if err != nil {
log.Fatalln("create environment builder failed", err)
}
envPool := pool.NewPool(b)
work = worker.New(worker.Config{
FileStore: fs,
EnvironmentPool: envPool,
Parallelism: ip.Parallelism,
WorkDir: ip.Dir,
TimeLimitTickInterval: 100 * time.Millisecond,
})
work.Start()
return 0
}
// Exec runs command inside container runner
//export Exec
func Exec(e *C.char) *C.char {
es := C.GoString(e)
var req model.Request
if err := json.NewDecoder(bytes.NewBufferString(es)).Decode(&req); err != nil {
return nil
}
r, err := model.ConvertRequest(&req, srcPrefix)
if err != nil {
return nil
}
rt := <-work.Submit(context.TODO(), r)
ret := model.ConvertResponse(rt)
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(ret); err != nil {
return nil
}
return C.CString(buf.String())
}
// FileList get the list of files in the file store
//export FileList
func FileList() *C.char {
ids := fs.List()
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(ids); err != nil {
return nil
}
return C.CString(buf.String())
}
// FileAdd adds file to the file store
//export FileAdd
func FileAdd(e *C.char) *C.char {
type fileAdd struct {
Name string `json:"name"`
Content string `json:"content"`
}
es := C.GoString(e)
var f fileAdd
if err := json.NewDecoder(bytes.NewBufferString(es)).Decode(&f); err != nil {
return nil
}
id, err := fs.Add(f.Name, []byte(f.Content))
if err != nil {
return nil
}
return C.CString(id)
}
// FileGet gets file from file store by id
//export FileGet
func FileGet(e *C.char) *C.char {
type fileGet struct {
ID string `json:"id"`
}
es := C.GoString(e)
var f fileGet
if err := json.NewDecoder(bytes.NewBufferString(es)).Decode(&f); err != nil {
return nil
}
file := fs.Get(f.ID)
if file == nil {
return nil
}
c, err := file.Content()
if err != nil {
return nil
}
return C.CString(string(c))
}
// FileDelete deletes file from file store by id
//export FileDelete
func FileDelete(e *C.char) *C.char {
type fileDelete struct {
ID string `json:"id"`
}
es := C.GoString(e)
var f fileDelete
if err := json.NewDecoder(bytes.NewBufferString(es)).Decode(&f); err != nil {
return nil
}
ok := fs.Remove(f.ID)
if !ok {
return nil
}
return C.CString("")
}
type nopLogger struct {
}
func (nopLogger) Debug(args ...interface{}) {
}
func (nopLogger) Info(args ...interface{}) {
}
func (nopLogger) Warn(args ...interface{}) {
}
func (nopLogger) Error(args ...interface{}) {
}