-
Notifications
You must be signed in to change notification settings - Fork 70
/
utils.go
112 lines (96 loc) · 2.5 KB
/
utils.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
package main
import (
"bufio"
"bytes"
"fmt"
"log"
"os/exec"
"os/user"
"strconv"
"strings"
"time"
)
var (
defaultShellTimeout = 2 * 60 * time.Second
)
// returns current user gid or 0
func currentGid() int {
gid := 0
current, err := user.Current()
if err != nil {
return 0
}
gid, err = strconv.Atoi(current.Gid)
if err != nil {
return 0
}
return gid
}
// sh is a simple os.exec Command tool, returns trimmed string output
func sh(name string, args ...string) (string, error) {
cmd := exec.Command(name, args...)
if isDebugEnabled() {
log.Printf("DEBUG: sh CMD: %q", cmd)
}
// TODO: capture and output STDERR to logfile?
out, err := cmd.Output()
return strings.Trim(string(out), " \n"), err
}
// ShResult used for channel in timeout
type ShResult struct {
Output string // STDOUT
Err error // go error, not STDERR
}
type ShTimeoutError struct {
timeout time.Duration
}
func (e ShTimeoutError) Error() string {
return fmt.Sprintf("Reached TIMEOUT on shell command")
}
// shWithDefaultTimeout will use the defaultShellTimeout so you dont have to pass one
func shWithDefaultTimeout(name string, args ...string) (string, error) {
return shWithTimeout(defaultShellTimeout, name, args...)
}
// shWithTimeout will run the Cmd and wait for the specified duration
func shWithTimeout(howLong time.Duration, name string, args ...string) (string, error) {
// duration can't be zero
if howLong <= 0 {
return "", fmt.Errorf("Timeout duration needs to be positive")
}
// set up the results channel
resultsChan := make(chan ShResult, 1)
if isDebugEnabled() {
log.Printf("DEBUG: shWithTimeout: %v, %s, %v", howLong, name, args)
}
// fire up the goroutine for the actual shell command
go func() {
out, err := sh(name, args...)
resultsChan <- ShResult{Output: out, Err: err}
}()
select {
case res := <-resultsChan:
return res.Output, res.Err
case <-time.After(howLong):
return "", ShTimeoutError{timeout: howLong}
}
return "", nil
}
// grepLines pulls out lines that match a string (no regex ... yet)
func grepLines(data string, like string) []string {
var result = []string{}
if like == "" {
log.Printf("ERROR: unable to look for empty pattern")
return result
}
like_bytes := []byte(like)
scanner := bufio.NewScanner(strings.NewReader(data))
for scanner.Scan() {
if bytes.Contains(scanner.Bytes(), like_bytes) {
result = append(result, scanner.Text())
}
}
if err := scanner.Err(); err != nil {
log.Printf("WARN: error scanning string for %s: %s", like, err)
}
return result
}