go-sandbox/cmd/runprog/main_darwin.go
2020-05-13 16:37:59 -04:00

145 lines
3.4 KiB
Go

// Command runprog executes program defined restricted environment including seccomp-ptraced, namespaced and containerized.
package main
import (
"flag"
"log"
"os"
"syscall"
"time"
"github.com/criyle/go-sandbox/pkg/forkexec"
"github.com/criyle/go-sandbox/pkg/rlimit"
"github.com/criyle/go-sandbox/runner"
)
var (
timeLimit, realTimeLimit, memoryLimit, outputLimit, stackLimit uint64
inputFileName, outputFileName, errorFileName, workPath string
args []string
)
func main() {
flag.Usage = printUsage
flag.Uint64Var(&timeLimit, "tl", 1, "Set time limit (in second)")
flag.Uint64Var(&realTimeLimit, "rtl", 0, "Set real time limit (in second)")
flag.Uint64Var(&memoryLimit, "ml", 256, "Set memory limit (in mb)")
flag.Uint64Var(&outputLimit, "ol", 64, "Set output limit (in mb)")
flag.Uint64Var(&stackLimit, "sl", 32, "Set stack limit (in mb)")
flag.StringVar(&inputFileName, "in", "", "Set input file name")
flag.StringVar(&outputFileName, "out", "", "Set output file name")
flag.StringVar(&errorFileName, "err", "", "Set error file name")
flag.StringVar(&workPath, "work-path", "", "Set the work path of the program")
flag.Parse()
args = flag.Args()
if len(args) == 0 {
printUsage()
}
if realTimeLimit < timeLimit {
realTimeLimit = timeLimit + 2
}
if stackLimit > memoryLimit {
stackLimit = memoryLimit
}
if workPath == "" {
workPath, _ = os.Getwd()
}
ret, err := start()
log.Println(ret, err)
}
func start() (*runner.Result, error) {
var sTime, mTime, fTime time.Time
sTime = time.Now()
files, err := prepareFiles(inputFileName, outputFileName, errorFileName)
if err != nil {
return nil, err
}
defer closeFiles(files)
// if not defined, then use the original value
fds := make([]uintptr, len(files))
for i, f := range files {
if f != nil {
fds[i] = f.Fd()
} else {
fds[i] = uintptr(i)
}
}
rlims := rlimit.RLimits{
CPU: timeLimit,
CPUHard: realTimeLimit,
FileSize: outputLimit << 20,
Data: memoryLimit << 20,
AddressSpace: memoryLimit << 20,
Stack: stackLimit << 20,
}
log.Println(rlims)
log.Println(args)
r := forkexec.Runner{
Args: args,
Env: []string{pathEnv},
RLimits: rlims.PrepareRLimit(),
Files: fds,
WorkDir: workPath,
SandboxProfile: "",
SyncFunc: func(pid int) error {
mTime = time.Now()
return nil
},
}
pid, err := r.Start()
if err != nil {
return nil, err
}
var (
wstatus syscall.WaitStatus
rusage syscall.Rusage
)
_, err = syscall.Wait4(pid, &wstatus, 0, &rusage)
for err == syscall.EINTR {
_, err = syscall.Wait4(pid, &wstatus, 0, &rusage)
}
fTime = time.Now()
if err != nil {
return nil, err
}
result := runner.Result{
Status: runner.StatusNormal,
Time: time.Duration(rusage.Utime.Nano()),
Memory: runner.Size(rusage.Maxrss),
SetUpTime: mTime.Sub(sTime),
RunningTime: fTime.Sub(mTime),
}
switch {
case wstatus.Exited():
if status := wstatus.ExitStatus(); status != 0 {
result.Status = runner.StatusNonzeroExitStatus
return &result, nil
}
case wstatus.Signaled():
result.Status = runner.StatusSignalled
result.ExitStatus = int(wstatus.Signal())
return &result, nil
default:
}
if uint64(result.Time) > timeLimit*1e9 {
result.Status = runner.StatusTimeLimitExceeded
}
if uint64(result.Memory) > memoryLimit<<20 {
result.Status = runner.StatusMemoryLimitExceeded
}
return &result, nil
}