123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- package common
- //
- // gopsutil is a port of psutil(http://pythonhosted.org/psutil/).
- // This covers these architectures.
- // - linux (amd64, arm)
- // - freebsd (amd64)
- // - windows (amd64)
- import (
- "bufio"
- "bytes"
- "context"
- "errors"
- "fmt"
- "io/ioutil"
- "net/url"
- "os"
- "os/exec"
- "path"
- "path/filepath"
- "reflect"
- "runtime"
- "strconv"
- "strings"
- "time"
- )
- var (
- Timeout = 3 * time.Second
- ErrTimeout = errors.New("command timed out")
- )
- type Invoker interface {
- Command(string, ...string) ([]byte, error)
- CommandWithContext(context.Context, string, ...string) ([]byte, error)
- }
- type Invoke struct{}
- func (i Invoke) Command(name string, arg ...string) ([]byte, error) {
- ctx, cancel := context.WithTimeout(context.Background(), Timeout)
- defer cancel()
- return i.CommandWithContext(ctx, name, arg...)
- }
- func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
- cmd := exec.CommandContext(ctx, name, arg...)
- var buf bytes.Buffer
- cmd.Stdout = &buf
- cmd.Stderr = &buf
- if err := cmd.Start(); err != nil {
- return buf.Bytes(), err
- }
- if err := cmd.Wait(); err != nil {
- return buf.Bytes(), err
- }
- return buf.Bytes(), nil
- }
- type FakeInvoke struct {
- Suffix string // Suffix species expected file name suffix such as "fail"
- Error error // If Error specfied, return the error.
- }
- // Command in FakeInvoke returns from expected file if exists.
- func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) {
- if i.Error != nil {
- return []byte{}, i.Error
- }
- arch := runtime.GOOS
- commandName := filepath.Base(name)
- fname := strings.Join(append([]string{commandName}, arg...), "")
- fname = url.QueryEscape(fname)
- fpath := path.Join("testdata", arch, fname)
- if i.Suffix != "" {
- fpath += "_" + i.Suffix
- }
- if PathExists(fpath) {
- return ioutil.ReadFile(fpath)
- }
- return []byte{}, fmt.Errorf("could not find testdata: %s", fpath)
- }
- func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) {
- return i.Command(name, arg...)
- }
- var ErrNotImplementedError = errors.New("not implemented yet")
- // ReadLines reads contents from a file and splits them by new lines.
- // A convenience wrapper to ReadLinesOffsetN(filename, 0, -1).
- func ReadLines(filename string) ([]string, error) {
- return ReadLinesOffsetN(filename, 0, -1)
- }
- // ReadLines reads contents from file and splits them by new line.
- // The offset tells at which line number to start.
- // The count determines the number of lines to read (starting from offset):
- // n >= 0: at most n lines
- // n < 0: whole file
- func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) {
- f, err := os.Open(filename)
- if err != nil {
- return []string{""}, err
- }
- defer f.Close()
- var ret []string
- r := bufio.NewReader(f)
- for i := 0; i < n+int(offset) || n < 0; i++ {
- line, err := r.ReadString('\n')
- if err != nil {
- break
- }
- if i < int(offset) {
- continue
- }
- ret = append(ret, strings.Trim(line, "\n"))
- }
- return ret, nil
- }
- func IntToString(orig []int8) string {
- ret := make([]byte, len(orig))
- size := -1
- for i, o := range orig {
- if o == 0 {
- size = i
- break
- }
- ret[i] = byte(o)
- }
- if size == -1 {
- size = len(orig)
- }
- return string(ret[0:size])
- }
- func UintToString(orig []uint8) string {
- ret := make([]byte, len(orig))
- size := -1
- for i, o := range orig {
- if o == 0 {
- size = i
- break
- }
- ret[i] = byte(o)
- }
- if size == -1 {
- size = len(orig)
- }
- return string(ret[0:size])
- }
- func ByteToString(orig []byte) string {
- n := -1
- l := -1
- for i, b := range orig {
- // skip left side null
- if l == -1 && b == 0 {
- continue
- }
- if l == -1 {
- l = i
- }
- if b == 0 {
- break
- }
- n = i + 1
- }
- if n == -1 {
- return string(orig)
- }
- return string(orig[l:n])
- }
- // ReadInts reads contents from single line file and returns them as []int32.
- func ReadInts(filename string) ([]int64, error) {
- f, err := os.Open(filename)
- if err != nil {
- return []int64{}, err
- }
- defer f.Close()
- var ret []int64
- r := bufio.NewReader(f)
- // The int files that this is concerned with should only be one liners.
- line, err := r.ReadString('\n')
- if err != nil {
- return []int64{}, err
- }
- i, err := strconv.ParseInt(strings.Trim(line, "\n"), 10, 32)
- if err != nil {
- return []int64{}, err
- }
- ret = append(ret, i)
- return ret, nil
- }
- // Parse to int32 without error
- func mustParseInt32(val string) int32 {
- vv, _ := strconv.ParseInt(val, 10, 32)
- return int32(vv)
- }
- // Parse to uint64 without error
- func mustParseUint64(val string) uint64 {
- vv, _ := strconv.ParseInt(val, 10, 64)
- return uint64(vv)
- }
- // Parse to Float64 without error
- func mustParseFloat64(val string) float64 {
- vv, _ := strconv.ParseFloat(val, 64)
- return vv
- }
- // StringsHas checks the target string slice contains src or not
- func StringsHas(target []string, src string) bool {
- for _, t := range target {
- if strings.TrimSpace(t) == src {
- return true
- }
- }
- return false
- }
- // StringsContains checks the src in any string of the target string slice
- func StringsContains(target []string, src string) bool {
- for _, t := range target {
- if strings.Contains(t, src) {
- return true
- }
- }
- return false
- }
- // IntContains checks the src in any int of the target int slice.
- func IntContains(target []int, src int) bool {
- for _, t := range target {
- if src == t {
- return true
- }
- }
- return false
- }
- // get struct attributes.
- // This method is used only for debugging platform dependent code.
- func attributes(m interface{}) map[string]reflect.Type {
- typ := reflect.TypeOf(m)
- if typ.Kind() == reflect.Ptr {
- typ = typ.Elem()
- }
- attrs := make(map[string]reflect.Type)
- if typ.Kind() != reflect.Struct {
- return nil
- }
- for i := 0; i < typ.NumField(); i++ {
- p := typ.Field(i)
- if !p.Anonymous {
- attrs[p.Name] = p.Type
- }
- }
- return attrs
- }
- func PathExists(filename string) bool {
- if _, err := os.Stat(filename); err == nil {
- return true
- }
- return false
- }
- //GetEnv retrieves the environment variable key. If it does not exist it returns the default.
- func GetEnv(key string, dfault string, combineWith ...string) string {
- value := os.Getenv(key)
- if value == "" {
- value = dfault
- }
- switch len(combineWith) {
- case 0:
- return value
- case 1:
- return filepath.Join(value, combineWith[0])
- default:
- all := make([]string, len(combineWith)+1)
- all[0] = value
- copy(all[1:], combineWith)
- return filepath.Join(all...)
- }
- panic("invalid switch case")
- }
- func HostProc(combineWith ...string) string {
- return GetEnv("HOST_PROC", "/proc", combineWith...)
- }
- func HostSys(combineWith ...string) string {
- return GetEnv("HOST_SYS", "/sys", combineWith...)
- }
- func HostEtc(combineWith ...string) string {
- return GetEnv("HOST_ETC", "/etc", combineWith...)
- }
- func HostVar(combineWith ...string) string {
- return GetEnv("HOST_VAR", "/var", combineWith...)
- }
- func HostRun(combineWith ...string) string {
- return GetEnv("HOST_RUN", "/run", combineWith...)
- }
- // https://gist.github.com/kylelemons/1525278
- func Pipeline(cmds ...*exec.Cmd) ([]byte, []byte, error) {
- // Require at least one command
- if len(cmds) < 1 {
- return nil, nil, nil
- }
- // Collect the output from the command(s)
- var output bytes.Buffer
- var stderr bytes.Buffer
- last := len(cmds) - 1
- for i, cmd := range cmds[:last] {
- var err error
- // Connect each command's stdin to the previous command's stdout
- if cmds[i+1].Stdin, err = cmd.StdoutPipe(); err != nil {
- return nil, nil, err
- }
- // Connect each command's stderr to a buffer
- cmd.Stderr = &stderr
- }
- // Connect the output and error for the last command
- cmds[last].Stdout, cmds[last].Stderr = &output, &stderr
- // Start each command
- for _, cmd := range cmds {
- if err := cmd.Start(); err != nil {
- return output.Bytes(), stderr.Bytes(), err
- }
- }
- // Wait for each command to complete
- for _, cmd := range cmds {
- if err := cmd.Wait(); err != nil {
- return output.Bytes(), stderr.Bytes(), err
- }
- }
- // Return the pipeline output and the collected standard error
- return output.Bytes(), stderr.Bytes(), nil
- }
- // getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running
- // sysctl commands (see DoSysctrl).
- func getSysctrlEnv(env []string) []string {
- foundLC := false
- for i, line := range env {
- if strings.HasPrefix(line, "LC_ALL") {
- env[i] = "LC_ALL=C"
- foundLC = true
- }
- }
- if !foundLC {
- env = append(env, "LC_ALL=C")
- }
- return env
- }
|