-
Notifications
You must be signed in to change notification settings - Fork 69
/
exec.go
126 lines (114 loc) · 2.75 KB
/
exec.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
package plugin
import (
"crypto/cipher"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"syscall"
"github.com/mattn/go-shellwords"
"github.com/starkandwayne/shield/crypter"
)
const NOPIPE = 0
const STDIN = 1
const STDOUT = 2
type ExecOptions struct {
Stdout io.Writer
Stdin io.Reader
Stderr *os.File
Cmd string
ExpectRC []int
}
func ExecWithOptions(opts ExecOptions) error {
cmdArgs, err := shellwords.Parse(opts.Cmd)
if err != nil {
return ExecFailure{Err: fmt.Sprintf("Could not parse '%s' into exec-able command: %s", opts.Cmd, err.Error())}
}
DEBUG("Executing '%s' with arguments %v", cmdArgs[0], cmdArgs[1:])
//Encryption data is passed from the shield-pipe on fd 3
var encStream, decStream cipher.Stream
var data struct {
EncryptionKey string `json:"enc_key"`
EncryptionIV string `json:"enc_iv"`
EncryptionType string `json:"enc_type"`
}
decoder := json.NewDecoder(os.NewFile(uintptr(3), "encConfig"))
if err := decoder.Decode(&data); err == nil {
keyRaw, err := hex.DecodeString(data.EncryptionKey)
if err != nil {
return err
}
ivRaw, err := hex.DecodeString(data.EncryptionIV)
if err != nil {
return err
}
if data.EncryptionType != "" {
encStream, decStream, err = crypter.Stream(data.EncryptionType, keyRaw, ivRaw)
if err != nil {
return err
}
}
}
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
if opts.Stdout != nil {
cmd.Stdout = opts.Stdout
if encStream != nil {
cmd.Stdout = cipher.StreamWriter{
S: encStream,
W: opts.Stdout,
}
}
}
if opts.Stderr != nil {
cmd.Stderr = opts.Stderr
}
if opts.Stdin != nil {
cmd.Stdin = opts.Stdin
if decStream != nil {
cmd.Stdin = cipher.StreamReader{
S: decStream,
R: opts.Stdin,
}
}
}
if len(opts.ExpectRC) == 0 {
opts.ExpectRC = []int{0}
}
err = cmd.Run()
if err != nil {
// make sure we got an Exit error
if exitErr, ok := err.(*exec.ExitError); ok {
sys := exitErr.ProcessState.Sys()
// os.ProcessState.Sys() may not return syscall.WaitStatus on non-UNIX machines,
// so currently this feature only works on UNIX, but shouldn't crash on other OSes
if rc, ok := sys.(syscall.WaitStatus); ok {
code := rc.ExitStatus()
// -1 indicates signals, stops, or traps, so force an error
if code >= 0 {
for _, expect := range opts.ExpectRC {
if code == expect {
return nil
}
}
}
}
}
return ExecFailure{Err: fmt.Sprintf("Unable to exec '%s': %s", cmdArgs[0], err.Error())}
}
return nil
}
func Exec(cmdString string, flags int) error {
opts := ExecOptions{
Cmd: cmdString,
Stderr: os.Stderr,
}
if flags&STDOUT == STDOUT {
opts.Stdout = os.Stdout
}
if flags&STDIN == STDIN {
opts.Stdin = os.Stdin
}
return ExecWithOptions(opts)
}