// +build linux package fs import ( "errors" "fmt" "io/ioutil" "path/filepath" "strconv" "strings" ) var ( ErrNotValidFormat = errors.New("line is not a valid key value format") ) // Saturates negative values at zero and returns a uint64. // Due to kernel bugs, some of the memory cgroup stats can be negative. func parseUint(s string, base, bitSize int) (uint64, error) { value, err := strconv.ParseUint(s, base, bitSize) if err != nil { intValue, intErr := strconv.ParseInt(s, base, bitSize) // 1. Handle negative values greater than MinInt64 (and) // 2. Handle negative values lesser than MinInt64 if intErr == nil && intValue < 0 { return 0, nil } else if intErr != nil && intErr.(*strconv.NumError).Err == strconv.ErrRange && intValue < 0 { return 0, nil } return value, err } return value, nil } // Parses a cgroup param and returns as name, value // i.e. "io_service_bytes 1234" will return as io_service_bytes, 1234 func getCgroupParamKeyValue(t string) (string, uint64, error) { parts := strings.Fields(t) switch len(parts) { case 2: value, err := parseUint(parts[1], 10, 64) if err != nil { return "", 0, fmt.Errorf("unable to convert param value (%q) to uint64: %v", parts[1], err) } return parts[0], value, nil default: return "", 0, ErrNotValidFormat } } // Gets a single uint64 value from the specified cgroup file. func getCgroupParamUint(cgroupPath, cgroupFile string) (uint64, error) { fileName := filepath.Join(cgroupPath, cgroupFile) contents, err := ioutil.ReadFile(fileName) if err != nil { return 0, err } res, err := parseUint(strings.TrimSpace(string(contents)), 10, 64) if err != nil { return res, fmt.Errorf("unable to parse %q as a uint from Cgroup file %q", string(contents), fileName) } return res, nil } // Gets a string value from the specified cgroup file func getCgroupParamString(cgroupPath, cgroupFile string) (string, error) { contents, err := ioutil.ReadFile(filepath.Join(cgroupPath, cgroupFile)) if err != nil { return "", err } return strings.TrimSpace(string(contents)), nil }