-
Notifications
You must be signed in to change notification settings - Fork 63
/
run_single.go
130 lines (115 loc) · 3.14 KB
/
run_single.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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, c *Cmd, fds []*os.File, ptc []pipeCollector, newStoreFile NewStoreFile) (result Result, err error) {
m := c.Environment
// copyin
if fe, err := runSingleCopyIn(m, c.CopyIn); err != nil {
result.Status = StatusFileError
result.Error = err.Error()
result.FileError = fe
closeFiles(fds...)
return result, nil
}
// symlink
if fe, err := symlink(m, c.SymLinks); err != nil {
result.Status = StatusFileError
result.Error = err.Error()
result.FileError = []FileError{*fe}
closeFiles(fds...)
return result, nil
}
// run cmd and wait for result
rt := runSingleWait(pc, m, c, fds)
// collect result
files, fe, err := copyOutAndCollect(m, c, ptc, newStoreFile)
result = Result{
Status: convertStatus(rt.Status),
ExitStatus: rt.ExitStatus,
Error: rt.Error,
Time: rt.Time,
RunTime: rt.RunningTime,
Memory: rt.Memory,
Files: files,
FileError: fe,
}
// 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
}
return result, nil
}
func runSingleCopyIn(m Environment, copyInFiles map[string]File) ([]FileError, error) {
if len(copyInFiles) == 0 {
return nil, nil
}
return copyIn(m, copyInFiles)
}
func runSingleWait(pc context.Context, m Environment, c *Cmd, fds []*os.File) RunnerResult {
// start the cmd (they will be canceled in other goroutines)
ctx, cancel := context.WithCancel(pc)
defer cancel()
process, err := runSingleExecve(ctx, m, c, fds)
if err != nil {
return runner.Result{
Status: runner.StatusRunnerError,
Error: err.Error(),
}
}
// starts waiter to periodically check cpu usage
c.Waiter(ctx, process)
// cancel the process as waiter exits
cancel()
return process.Result()
}
func runSingleExecve(ctx context.Context, m Environment, c *Cmd, fds []*os.File) (Process, error) {
defer closeFiles(fds...)
extraMemoryLimit := c.ExtraMemoryLimit
if extraMemoryLimit == 0 {
extraMemoryLimit = defaultExtraMemoryLimit
}
memoryLimit := c.MemoryLimit + extraMemoryLimit
var stackLimit Size
if c.StackLimit > 0 {
stackLimit = c.StackLimit
}
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,
Output: c.OutputLimit,
Rate: c.CPURateLimit,
OpenFile: c.OpenFileLimit,
CPUSet: c.CPUSetLimit,
DataSegment: c.DataSegmentLimit,
AddressSpace: c.AddressSpaceLimit,
},
}
return m.Execve(ctx, execParam)
}