-
Notifications
You must be signed in to change notification settings - Fork 51
/
launch.go
97 lines (80 loc) · 3.04 KB
/
launch.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
// Copyright (c) Edgeless Systems GmbH.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
package launch
import (
"io"
"os"
"os/exec"
"syscall"
)
// run launches an application with a CappedBuffer and also translates potential Edgeless RT / Open Enclave errors into more user-friendly ones.
func run(runner Runner, cmd *exec.Cmd) (int, error) {
// force line buffering for stdout
// otherwise it will be fully buffered because our stdout is not a tty
path, err := exec.LookPath("stdbuf")
if err != nil {
return 1, err
}
cmd.Path = path
cmd.Args = append([]string{"stdbuf", "-oL"}, cmd.Args...)
cmd.Stdin = os.Stdin
// capture stdout and stderr
var stdout, stderr cappedBuffer
cmd.Stdout = io.MultiWriter(os.Stdout, &stdout)
cmd.Stderr = io.MultiWriter(os.Stderr, &stderr)
if err := runner.Run(cmd); err != nil {
if exitErr, ok := err.(*exec.ExitError); !ok {
return 1, err
} else if !exitErr.Exited() {
// Terminated due to signal. ExitCode will be -1, so return WaitStatus instead (e.g., 139
// for segfault) to mimic the behavior as if the process would have been invoked directly.
return int(exitErr.Sys().(syscall.WaitStatus)), err
}
}
return runner.ExitCode(cmd), findCommonError(string(stdout) + string(stderr))
}
// RunEnclave launches an user EGo enclave.
func RunEnclave(filename string, args []string, egoHostFilename string, egoEnclaveFilename string, runner Runner) (int, error) {
enclaves := egoEnclaveFilename + ":" + filename
args = append([]string{enclaves}, args...)
cmd := exec.Command(egoHostFilename, args...)
return run(runner, cmd)
}
// RunEnclaveMarblerun launches an user EGo enclave in MarbleRun mode, calling into MarbleRun's premain before launching user code.
func RunEnclaveMarblerun(filename string, egoHostFilename string, egoEnclaveFilename string, runner Runner) (int, error) {
enclaves := egoEnclaveFilename + ":" + filename
cmd := exec.Command(egoHostFilename, enclaves)
// Enable the MarbleRun premain.
if err := os.Setenv("EDG_EGO_PREMAIN", "1"); err != nil {
return 1, err
}
return run(runner, cmd)
}
// Runner runs Cmd objects.
type Runner interface {
Run(cmd *exec.Cmd) error
Output(cmd *exec.Cmd) ([]byte, error)
CombinedOutput(cmd *exec.Cmd) ([]byte, error)
ExitCode(cmd *exec.Cmd) int
}
// OsRunner wraps Cmd objects from the real host system, not an unit test environment.
type OsRunner struct{}
// Run for OsRunner redirects to cmd.Run()
func (OsRunner) Run(cmd *exec.Cmd) error {
return cmd.Run()
}
// Output for OsRunner redirects to cmd.Output()
func (OsRunner) Output(cmd *exec.Cmd) ([]byte, error) {
return cmd.Output()
}
// CombinedOutput for OsRunner redirects to cmd.CombinedOutput()
func (OsRunner) CombinedOutput(cmd *exec.Cmd) ([]byte, error) {
return cmd.CombinedOutput()
}
// ExitCode for OsRunner redirects to cmd.ProcessState.ExitCode()
func (OsRunner) ExitCode(cmd *exec.Cmd) int {
return cmd.ProcessState.ExitCode()
}