-
Notifications
You must be signed in to change notification settings - Fork 5
/
run.go
121 lines (90 loc) · 1.95 KB
/
run.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
package cmdr
import (
"bytes"
"fmt"
"os"
"os/exec"
"regexp"
"strings"
"time"
)
func getPATH() []string {
return strings.Split(os.Getenv("PATH"), ":")
}
// this regex tests if the cmd starts with: ./, ../, ~/ or /
var partialPathRegex = regexp.MustCompile(`^((\~|\.{1,})?\/)`)
func findInPath(cmd string) (found bool) {
// stops validation when a full or
// partial path was inputed
if partialPathRegex.Match([]byte(cmd)) {
found = true
return
}
for _, dir := range getPATH() {
fullPath := fmt.Sprintf("%s/%s", dir, cmd)
if fileExist(fullPath) {
found = true
break
}
}
return
}
func fileExist(name string) bool {
_, err := os.Stat(name)
return !os.IsNotExist(err)
}
// RunCmd runs a command in the operating system
func RunCmd(c Command) ([]byte, error) {
return runCmd(c)
}
func makeCmd(c Command) (cmd *exec.Cmd) {
if c.Options.UseShell {
cmd = exec.Command("bash", "-c", fmt.Sprintf("%s %s", c.Command, strings.Join(c.Args, " ")))
} else {
cmd = exec.Command(c.Command, c.Args...)
}
return
}
func runCmd(c Command) (output []byte, err error) {
if c.Options.CheckPath {
if err := validateCmd(c); err != nil {
return nil, fmt.Errorf("Check PATH failed: %v", err)
}
}
cmd := makeCmd(c)
var b bytes.Buffer
cmd.Stdout = &b
cmd.Stderr = &b
err = cmd.Start()
if err != nil {
err = fmt.Errorf("Error starting a command: %v", err)
return
}
var timer *time.Timer
if c.Options.Timeout > 0 {
execLimit := time.Duration(c.Options.Timeout) * time.Second
timer = time.AfterFunc(execLimit, func() {
cmd.Process.Kill()
})
}
err = cmd.Wait()
if err != nil {
err = fmt.Errorf("Error running a command: %v", err)
}
output = b.Bytes()
if c.Options.Timeout > 0 {
timer.Stop()
}
return
}
func validateCmd(c Command) (err error) {
if c.Command == "" {
err = fmt.Errorf("Missing command name")
return
}
if !findInPath(c.Command) {
err = fmt.Errorf("Command not found in PATH")
return
}
return
}