mirror of
https://github.com/criyle/go-judge.git
synced 2025-09-26 22:39:12 +08:00
114 lines
2.4 KiB
Go
114 lines
2.4 KiB
Go
package envexec
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
|
|
"github.com/criyle/go-sandbox/runner"
|
|
)
|
|
|
|
// runSingle runs Cmd inside the given environment and cgroup
|
|
func runSingle(pc context.Context, m Environment, c *Cmd, fds []*os.File, ptc []pipeCollector) (result Result, err error) {
|
|
fdToClose := fds
|
|
defer func() { closeFiles(fdToClose) }()
|
|
|
|
// copyin
|
|
if len(c.CopyIn) > 0 {
|
|
if err := copyIn(m, c.CopyIn); err != nil {
|
|
result.Status = StatusInternalError
|
|
result.Error = err.Error()
|
|
return result, nil
|
|
}
|
|
}
|
|
|
|
extraMemoryLimit := c.ExtraMemoryLimit
|
|
if extraMemoryLimit == 0 {
|
|
extraMemoryLimit = defaultExtraMemoryLimit
|
|
}
|
|
|
|
memoryLimit := c.MemoryLimit + extraMemoryLimit
|
|
|
|
var stackLimit Size
|
|
if c.StackLimit > 0 {
|
|
stackLimit = c.StackLimit + extraMemoryLimit
|
|
}
|
|
if stackLimit > memoryLimit {
|
|
stackLimit = memoryLimit
|
|
}
|
|
|
|
// set running parameters
|
|
execParam := ExecveParam{
|
|
Args: c.Args,
|
|
Env: c.Env,
|
|
Files: getFdArray(fds),
|
|
TTY: c.TTY,
|
|
Limit: Limit{
|
|
Time: c.TimeLimit,
|
|
Memory: memoryLimit,
|
|
Proc: c.ProcLimit,
|
|
Stack: stackLimit,
|
|
Rate: c.CPURateLimit,
|
|
},
|
|
}
|
|
|
|
// start the cmd (they will be canceled in other goroutines)
|
|
ctx, cancel := context.WithCancel(pc)
|
|
waiterCtx, waiterCancel := context.WithCancel(ctx)
|
|
|
|
process, err := m.Execve(ctx, execParam)
|
|
|
|
// close files
|
|
closeFiles(fds)
|
|
fdToClose = nil
|
|
|
|
// starts waiter to periodically check cpu usage
|
|
go func() {
|
|
c.Waiter(waiterCtx, process)
|
|
cancel()
|
|
}()
|
|
|
|
var rt runner.Result
|
|
if err == nil {
|
|
<-process.Done()
|
|
rt = process.Result()
|
|
} else {
|
|
rt = runner.Result{
|
|
Status: runner.StatusRunnerError,
|
|
Error: err.Error(),
|
|
}
|
|
}
|
|
|
|
waiterCancel()
|
|
|
|
// collect result
|
|
files, err := copyOutAndCollect(m, c, ptc)
|
|
result = Result{
|
|
Status: convertStatus(rt.Status),
|
|
ExitStatus: rt.ExitStatus,
|
|
Error: rt.Error,
|
|
Time: rt.Time,
|
|
RunTime: rt.RunningTime,
|
|
Memory: rt.Memory,
|
|
Files: files,
|
|
}
|
|
// collect error (only if the process exits normally)
|
|
if rt.Status == runner.StatusNormal && err != nil && result.Error == "" {
|
|
switch err := err.(type) {
|
|
case runner.Status:
|
|
result.Status = convertStatus(err)
|
|
default:
|
|
result.Status = StatusFileError
|
|
}
|
|
result.Error = err.Error()
|
|
}
|
|
if result.Time > c.TimeLimit {
|
|
result.Status = StatusTimeLimitExceeded
|
|
}
|
|
if result.Memory > c.MemoryLimit {
|
|
result.Status = StatusMemoryLimitExceeded
|
|
}
|
|
// make sure waiter exit
|
|
<-ctx.Done()
|
|
return result, nil
|
|
}
|