forked from hpe-storage/common-host-libs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmd.go
116 lines (105 loc) · 3.8 KB
/
cmd.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
/*
(c) Copyright 2017 Hewlett Packard Enterprise Development LP
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"bytes"
"fmt"
log "github.com/hpe-storage/common-host-libs/logger"
"os/exec"
"regexp"
"strings"
"syscall"
"time"
)
const (
defaultTimeout = 60
)
func execCommandOutputWithTimeout(cmd string, args []string, stdinArgs []string, timeout int) (string, int, error) {
log.Trace("execCommandOutputWithTimeout called with ", cmd, log.Scrubber(args), timeout)
var err error
c := exec.Command(cmd, args...)
var b bytes.Buffer
c.Stdout = &b
c.Stderr = &b
if len(stdinArgs) > 0 {
c.Stdin = strings.NewReader(strings.Join(stdinArgs, "\n"))
}
if err = c.Start(); err != nil {
return "", 999, err
}
// Wait for the process to finish or kill it after a timeout:
done := make(chan error, 1)
go func() {
done <- c.Wait()
}()
select {
case <-time.After(time.Duration(timeout) * time.Second):
if err = c.Process.Kill(); err != nil {
log.Errorf("failed to kill process %v: error = %v", c.Process.Pid, err)
}
err = fmt.Errorf("command %s with pid: %v killed as timeout of %d seconds reached", cmd, c.Process.Pid, timeout)
log.Errorf(err.Error())
case err = <-done:
if err != nil {
log.Errorf("process with pid : %v finished with error = %v", c.Process.Pid, err)
} else {
log.Tracef("process with pid: %v finished successfully", c.Process.Pid)
}
}
out := string(b.Bytes())
log.Trace(out)
if err != nil {
//check the rc of the exec
if badnews, ok := err.(*exec.ExitError); ok {
if status, ok := badnews.Sys().(syscall.WaitStatus); ok {
// send the error code and stderr content to the caller
return out, status.ExitStatus(), fmt.Errorf("command %s failed with rc=%d err=%s", cmd, status.ExitStatus(), out)
}
} else {
return out, 888, fmt.Errorf("error %s", err.Error())
}
}
return out, 0, nil
}
// ExecCommandOutputWithTimeout executes ExecCommandOutput with the specified timeout
func ExecCommandOutputWithTimeout(cmd string, args []string, timeout int) (string, int, error) {
return execCommandOutputWithTimeout(cmd, args, []string{}, defaultTimeout)
}
// ExecCommandOutput returns stdout and stderr in a single string, the return code, and error.
// If the return code is not zero, error will not be nil.
// Stdout and Stderr are dumped to the log at the debug level.
// Return code of 999 indicates an error starting the command.
func ExecCommandOutput(cmd string, args []string) (string, int, error) {
return ExecCommandOutputWithTimeout(cmd, args, defaultTimeout)
}
// ExecCommandOutputWithStdinArgs returns stdout and stderr in a single string, the return code, and error.
// If the return code is not zero, error will not be nil.
// Stdout and Stderr are dumped to the log at the debug level.
// Return code of 999 indicates an error starting the command.
func ExecCommandOutputWithStdinArgs(cmd string, args []string, stdInArgs []string) (string, int, error) {
return execCommandOutputWithTimeout(cmd, args, stdInArgs, defaultTimeout)
}
// FindStringSubmatchMap : find and build the map of named groups
func FindStringSubmatchMap(s string, r *regexp.Regexp) map[string]string {
captures := make(map[string]string)
match := r.FindStringSubmatch(s)
if match == nil {
return captures
}
for i, name := range r.SubexpNames() {
if i != 0 {
captures[name] = match[i]
}
}
return captures
}