package runner import ( "bytes" "io" "fmt" "log" "os" "strings" exec "os/exec" cgroups "github.com/containerd/cgroups" rand "math/rand" "time" ) const runIdLength = 20 type Limits struct { Processes int32 Core int32 Memory int32 Time int32 } const Mib = 1024*1024; const BuildMemory = 1024*Mib; const BuildTime = 5000; type RunnerService struct { CoresIsolated []int32 CoreFreed chan int32 } func genRunId() (runId string) { for i := 0; i < runIdLength; i++ { runId += string('a' + byte(rand.Int31n(26))) } return } func int32ToString(n int32) string { return fmt.Sprintf("%d", n) } func copyFile(src string,dst string) error { log.Println(src,dst) srcFile,err := os.Open(src) if(err!=nil) {return err} defer srcFile.Close() dstFile,err := os.Create(dst) if(err!=nil) {return err} defer dstFile.Close() _,err = io.Copy(dstFile,srcFile) if(err!=nil) {return err} err = dstFile.Sync() if(err!=nil) {return err} return nil } func extractNumbers(s string) (result []int32) { lastNumber, isNumber := false,false var curNumber int32 = 0 for _, char := range s { isNumber = (char >= '0' && char <= '9') if(isNumber) { curNumber *= 10 curNumber += int32(char - '0') } if(!isNumber && lastNumber) { result = append(result, curNumber) curNumber = 0 } lastNumber = isNumber } if(lastNumber) { result = append(result, curNumber) } return } func NewRunnerService() (*RunnerService, error) { runnerService := RunnerService{make([]int32,0),make(chan int32)} rand.Seed(time.Now().UnixNano()) //croup initialisation: if cgroups.Mode() == cgroups.Unified { log.Println("cgroups v2 usage approved") } else { return nil,fmt.Errorf("cgroups v2 are not enabled")//TODO: trouble description } //isolated cores initialisation: cmdlineBytes := make([]byte, 400) cmdlineFile, _ := os.Open("/proc/cmdline"); countCmdlineBytes, _ := cmdlineFile.Read(cmdlineBytes); cmdline := string(cmdlineBytes[:countCmdlineBytes]) kernelParams := strings.Split(cmdline," ") for _, param := range kernelParams{ if(len(param) >= 9 && param[:9] == "isolcpus=") { runnerService.CoresIsolated = append(runnerService.CoresIsolated, extractNumbers(param[9:])...) } } if(len(runnerService.CoresIsolated) == 0) { return nil,fmt.Errorf("no free cores available")//TODO: trouble description } go func() { for _,core := range runnerService.CoresIsolated { runnerService.CoreFreed <- core } }() log.Println("running on cores:", runnerService.CoresIsolated) //runs directory os.Mkdir("runs",0777) os.Chmod("runs",0777) //complete log.Println("Runner initialisation successful!") return &runnerService,nil } func (runnerService RunnerService) Build(buildCmd []string, filename string) { core := <-runnerService.CoreFreed runId := genRunId() err := os.Mkdir("runs/" + runId, 0777) if(err!=nil) { log.Fatal(err) } os.Chmod("runs/" + runId, 0777) copyFile("starter/shared/main.c","runs/"+runId+"/main.c")//FIXME add source and file extension runnerService.IsolatedRun([]string{"/usr/bin/gcc","/shared/main.c","-o","/shared/executable","-save-temps=obj"},"../runs/"+runId,Limits{Processes: 10,Core:core,Memory:BuildMemory,Time:BuildTime}); copyFile("runs/"+runId+"/executable","destination")//FIXME add destination os.RemoveAll("runs/" + runId) go func() { runnerService.CoreFreed <- core }() } func (runnerService RunnerService) IsolatedRun(command []string, sharedFolder string, limits Limits) { args := []string{int32ToString(limits.Processes),int32ToString(limits.Core), int32ToString(limits.Memory), int32ToString(limits.Time), sharedFolder} args = append(args, command...) log.Println(limits.Processes) log.Println(string(limits.Processes)) log.Println(args) cmd := exec.Command("starter/starter", args...) var stdBuffer bytes.Buffer mw := io.MultiWriter(os.Stdout, &stdBuffer) cmd.Stdout = mw err := cmd.Run(); if err != nil { log.Println(123) log.Println(err) } //cmd.Wait() }