···
ctx, cancel := context.WithTimeout(ctx, time.Duration(timeoutSec)*time.Second)
34
+
// Check if systemd-run is available (not on macOS/local dev)
35
+
_, err := exec.LookPath("systemd-run")
37
+
// Fallback: run directly without sandbox (development only)
38
+
log.Printf("systemd-run not available, running without sandbox: %v", args)
39
+
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
40
+
output, err := cmd.CombinedOutput()
42
+
if ctx.Err() == context.DeadlineExceeded {
43
+
return output, fmt.Errorf("command timed out after %d seconds", timeoutSec)
// Build systemd-run command with security properties
// Using service unit (not scope) to get access to network/filesystem isolation
···
if ctx.Err() == context.DeadlineExceeded {
return output, fmt.Errorf("command timed out after %d seconds", timeoutSec)
84
+
// Check if process was killed by a signal
86
+
if exitErr, ok := err.(*exec.ExitError); ok {
87
+
if status, ok := exitErr.Sys().(syscall.WaitStatus); ok {
88
+
// Direct execution: check if signaled
89
+
if status.Signaled() {
90
+
sig := status.Signal()
91
+
return output, fmt.Errorf("killed by signal: %s", sig.String())
93
+
// systemd-run execution: exit code 128+N means killed by signal N
94
+
exitCode := status.ExitStatus()
95
+
if exitCode >= 128 && exitCode <= 192 {
96
+
sigNum := exitCode - 128
97
+
sigName := "unknown"
99
+
case 1: sigName = "SIGHUP"
100
+
case 2: sigName = "SIGINT"
101
+
case 3: sigName = "SIGQUIT"
102
+
case 4: sigName = "SIGILL"
103
+
case 5: sigName = "SIGTRAP"
104
+
case 6: sigName = "SIGABRT"
105
+
case 7: sigName = "SIGBUS"
106
+
case 8: sigName = "SIGFPE"
107
+
case 9: sigName = "SIGKILL"
108
+
case 10: sigName = "SIGUSR1"
109
+
case 11: sigName = "SIGSEGV"
110
+
case 12: sigName = "SIGUSR2"
111
+
case 13: sigName = "SIGPIPE"
112
+
case 14: sigName = "SIGALRM"
113
+
case 15: sigName = "SIGTERM"
114
+
default: sigName = fmt.Sprintf("signal %d", sigNum)
116
+
return output, fmt.Errorf("killed by %s (exit code %d)", sigName, exitCode)
···
output, err = runSandboxed(context.Background(), "run-match", runArgs, 300)
log.Printf("Match execution failed: %v\n%s", err, output)
234
-
return 0, 0, 0, fmt.Sprintf("Runtime error: %s (possible crash, timeout, or infinite loop)", strings.TrimSpace(string(output)))
287
+
errMsg := strings.TrimSpace(string(output))
289
+
// If there's output, show it along with the exit status
290
+
return 0, 0, 0, fmt.Sprintf("Runtime error: %s (%s)", errMsg, err.Error())
292
+
// If no output, just show the error
293
+
return 0, 0, 0, fmt.Sprintf("Runtime error: %s", err.Error())
p1, p2, moves := parseMatchOutput(string(output))