blob: 71c2257f07f799780a3cb856e608d97e0d11e0a4 [file] [log] [blame]
Serge Bazanskicc25bdf2018-10-25 14:02:58 +02001package common
2
3//
4// gopsutil is a port of psutil(http://pythonhosted.org/psutil/).
5// This covers these architectures.
6// - linux (amd64, arm)
7// - freebsd (amd64)
8// - windows (amd64)
9import (
10 "bufio"
11 "bytes"
12 "context"
13 "errors"
14 "fmt"
15 "io/ioutil"
16 "net/url"
17 "os"
18 "os/exec"
19 "path"
20 "path/filepath"
21 "reflect"
22 "runtime"
23 "strconv"
24 "strings"
25 "time"
26)
27
28var (
29 Timeout = 3 * time.Second
30 ErrTimeout = errors.New("command timed out")
31)
32
33type Invoker interface {
34 Command(string, ...string) ([]byte, error)
35 CommandWithContext(context.Context, string, ...string) ([]byte, error)
36}
37
38type Invoke struct{}
39
40func (i Invoke) Command(name string, arg ...string) ([]byte, error) {
41 ctx, cancel := context.WithTimeout(context.Background(), Timeout)
42 defer cancel()
43 return i.CommandWithContext(ctx, name, arg...)
44}
45
46func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
47 cmd := exec.CommandContext(ctx, name, arg...)
48
49 var buf bytes.Buffer
50 cmd.Stdout = &buf
51 cmd.Stderr = &buf
52
53 if err := cmd.Start(); err != nil {
54 return buf.Bytes(), err
55 }
56
57 if err := cmd.Wait(); err != nil {
58 return buf.Bytes(), err
59 }
60
61 return buf.Bytes(), nil
62}
63
64type FakeInvoke struct {
65 Suffix string // Suffix species expected file name suffix such as "fail"
66 Error error // If Error specfied, return the error.
67}
68
69// Command in FakeInvoke returns from expected file if exists.
70func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) {
71 if i.Error != nil {
72 return []byte{}, i.Error
73 }
74
75 arch := runtime.GOOS
76
77 commandName := filepath.Base(name)
78
79 fname := strings.Join(append([]string{commandName}, arg...), "")
80 fname = url.QueryEscape(fname)
81 fpath := path.Join("testdata", arch, fname)
82 if i.Suffix != "" {
83 fpath += "_" + i.Suffix
84 }
85 if PathExists(fpath) {
86 return ioutil.ReadFile(fpath)
87 }
88 return []byte{}, fmt.Errorf("could not find testdata: %s", fpath)
89}
90
91func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
92 return i.Command(name, arg...)
93}
94
95var ErrNotImplementedError = errors.New("not implemented yet")
96
97// ReadLines reads contents from a file and splits them by new lines.
98// A convenience wrapper to ReadLinesOffsetN(filename, 0, -1).
99func ReadLines(filename string) ([]string, error) {
100 return ReadLinesOffsetN(filename, 0, -1)
101}
102
103// ReadLines reads contents from file and splits them by new line.
104// The offset tells at which line number to start.
105// The count determines the number of lines to read (starting from offset):
106// n >= 0: at most n lines
107// n < 0: whole file
108func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) {
109 f, err := os.Open(filename)
110 if err != nil {
111 return []string{""}, err
112 }
113 defer f.Close()
114
115 var ret []string
116
117 r := bufio.NewReader(f)
118 for i := 0; i < n+int(offset) || n < 0; i++ {
119 line, err := r.ReadString('\n')
120 if err != nil {
121 break
122 }
123 if i < int(offset) {
124 continue
125 }
126 ret = append(ret, strings.Trim(line, "\n"))
127 }
128
129 return ret, nil
130}
131
132func IntToString(orig []int8) string {
133 ret := make([]byte, len(orig))
134 size := -1
135 for i, o := range orig {
136 if o == 0 {
137 size = i
138 break
139 }
140 ret[i] = byte(o)
141 }
142 if size == -1 {
143 size = len(orig)
144 }
145
146 return string(ret[0:size])
147}
148
149func UintToString(orig []uint8) string {
150 ret := make([]byte, len(orig))
151 size := -1
152 for i, o := range orig {
153 if o == 0 {
154 size = i
155 break
156 }
157 ret[i] = byte(o)
158 }
159 if size == -1 {
160 size = len(orig)
161 }
162
163 return string(ret[0:size])
164}
165
166func ByteToString(orig []byte) string {
167 n := -1
168 l := -1
169 for i, b := range orig {
170 // skip left side null
171 if l == -1 && b == 0 {
172 continue
173 }
174 if l == -1 {
175 l = i
176 }
177
178 if b == 0 {
179 break
180 }
181 n = i + 1
182 }
183 if n == -1 {
184 return string(orig)
185 }
186 return string(orig[l:n])
187}
188
189// ReadInts reads contents from single line file and returns them as []int32.
190func ReadInts(filename string) ([]int64, error) {
191 f, err := os.Open(filename)
192 if err != nil {
193 return []int64{}, err
194 }
195 defer f.Close()
196
197 var ret []int64
198
199 r := bufio.NewReader(f)
200
201 // The int files that this is concerned with should only be one liners.
202 line, err := r.ReadString('\n')
203 if err != nil {
204 return []int64{}, err
205 }
206
207 i, err := strconv.ParseInt(strings.Trim(line, "\n"), 10, 32)
208 if err != nil {
209 return []int64{}, err
210 }
211 ret = append(ret, i)
212
213 return ret, nil
214}
215
216// Parse to int32 without error
217func mustParseInt32(val string) int32 {
218 vv, _ := strconv.ParseInt(val, 10, 32)
219 return int32(vv)
220}
221
222// Parse to uint64 without error
223func mustParseUint64(val string) uint64 {
224 vv, _ := strconv.ParseInt(val, 10, 64)
225 return uint64(vv)
226}
227
228// Parse to Float64 without error
229func mustParseFloat64(val string) float64 {
230 vv, _ := strconv.ParseFloat(val, 64)
231 return vv
232}
233
234// StringsHas checks the target string slice contains src or not
235func StringsHas(target []string, src string) bool {
236 for _, t := range target {
237 if strings.TrimSpace(t) == src {
238 return true
239 }
240 }
241 return false
242}
243
244// StringsContains checks the src in any string of the target string slice
245func StringsContains(target []string, src string) bool {
246 for _, t := range target {
247 if strings.Contains(t, src) {
248 return true
249 }
250 }
251 return false
252}
253
254// IntContains checks the src in any int of the target int slice.
255func IntContains(target []int, src int) bool {
256 for _, t := range target {
257 if src == t {
258 return true
259 }
260 }
261 return false
262}
263
264// get struct attributes.
265// This method is used only for debugging platform dependent code.
266func attributes(m interface{}) map[string]reflect.Type {
267 typ := reflect.TypeOf(m)
268 if typ.Kind() == reflect.Ptr {
269 typ = typ.Elem()
270 }
271
272 attrs := make(map[string]reflect.Type)
273 if typ.Kind() != reflect.Struct {
274 return nil
275 }
276
277 for i := 0; i < typ.NumField(); i++ {
278 p := typ.Field(i)
279 if !p.Anonymous {
280 attrs[p.Name] = p.Type
281 }
282 }
283
284 return attrs
285}
286
287func PathExists(filename string) bool {
288 if _, err := os.Stat(filename); err == nil {
289 return true
290 }
291 return false
292}
293
294//GetEnv retrieves the environment variable key. If it does not exist it returns the default.
295func GetEnv(key string, dfault string, combineWith ...string) string {
296 value := os.Getenv(key)
297 if value == "" {
298 value = dfault
299 }
300
301 switch len(combineWith) {
302 case 0:
303 return value
304 case 1:
305 return filepath.Join(value, combineWith[0])
306 default:
307 all := make([]string, len(combineWith)+1)
308 all[0] = value
309 copy(all[1:], combineWith)
310 return filepath.Join(all...)
311 }
312 panic("invalid switch case")
313}
314
315func HostProc(combineWith ...string) string {
316 return GetEnv("HOST_PROC", "/proc", combineWith...)
317}
318
319func HostSys(combineWith ...string) string {
320 return GetEnv("HOST_SYS", "/sys", combineWith...)
321}
322
323func HostEtc(combineWith ...string) string {
324 return GetEnv("HOST_ETC", "/etc", combineWith...)
325}
326
327func HostVar(combineWith ...string) string {
328 return GetEnv("HOST_VAR", "/var", combineWith...)
329}
330
331func HostRun(combineWith ...string) string {
332 return GetEnv("HOST_RUN", "/run", combineWith...)
333}
334
335// https://gist.github.com/kylelemons/1525278
336func Pipeline(cmds ...*exec.Cmd) ([]byte, []byte, error) {
337 // Require at least one command
338 if len(cmds) < 1 {
339 return nil, nil, nil
340 }
341
342 // Collect the output from the command(s)
343 var output bytes.Buffer
344 var stderr bytes.Buffer
345
346 last := len(cmds) - 1
347 for i, cmd := range cmds[:last] {
348 var err error
349 // Connect each command's stdin to the previous command's stdout
350 if cmds[i+1].Stdin, err = cmd.StdoutPipe(); err != nil {
351 return nil, nil, err
352 }
353 // Connect each command's stderr to a buffer
354 cmd.Stderr = &stderr
355 }
356
357 // Connect the output and error for the last command
358 cmds[last].Stdout, cmds[last].Stderr = &output, &stderr
359
360 // Start each command
361 for _, cmd := range cmds {
362 if err := cmd.Start(); err != nil {
363 return output.Bytes(), stderr.Bytes(), err
364 }
365 }
366
367 // Wait for each command to complete
368 for _, cmd := range cmds {
369 if err := cmd.Wait(); err != nil {
370 return output.Bytes(), stderr.Bytes(), err
371 }
372 }
373
374 // Return the pipeline output and the collected standard error
375 return output.Bytes(), stderr.Bytes(), nil
376}
377
378// getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running
379// sysctl commands (see DoSysctrl).
380func getSysctrlEnv(env []string) []string {
381 foundLC := false
382 for i, line := range env {
383 if strings.HasPrefix(line, "LC_ALL") {
384 env[i] = "LC_ALL=C"
385 foundLC = true
386 }
387 }
388 if !foundLC {
389 env = append(env, "LC_ALL=C")
390 }
391 return env
392}