utils.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package fscommon
  2. import (
  3. "errors"
  4. "fmt"
  5. "math"
  6. "path"
  7. "strconv"
  8. "strings"
  9. "github.com/opencontainers/runc/libcontainer/cgroups"
  10. )
  11. var (
  12. // Deprecated: use cgroups.OpenFile instead.
  13. OpenFile = cgroups.OpenFile
  14. // Deprecated: use cgroups.ReadFile instead.
  15. ReadFile = cgroups.ReadFile
  16. // Deprecated: use cgroups.WriteFile instead.
  17. WriteFile = cgroups.WriteFile
  18. )
  19. // ParseError records a parse error details, including the file path.
  20. type ParseError struct {
  21. Path string
  22. File string
  23. Err error
  24. }
  25. func (e *ParseError) Error() string {
  26. return "unable to parse " + path.Join(e.Path, e.File) + ": " + e.Err.Error()
  27. }
  28. func (e *ParseError) Unwrap() error { return e.Err }
  29. // ParseUint converts a string to an uint64 integer.
  30. // Negative values are returned at zero as, due to kernel bugs,
  31. // some of the memory cgroup stats can be negative.
  32. func ParseUint(s string, base, bitSize int) (uint64, error) {
  33. value, err := strconv.ParseUint(s, base, bitSize)
  34. if err != nil {
  35. intValue, intErr := strconv.ParseInt(s, base, bitSize)
  36. // 1. Handle negative values greater than MinInt64 (and)
  37. // 2. Handle negative values lesser than MinInt64
  38. if intErr == nil && intValue < 0 {
  39. return 0, nil
  40. } else if errors.Is(intErr, strconv.ErrRange) && intValue < 0 {
  41. return 0, nil
  42. }
  43. return value, err
  44. }
  45. return value, nil
  46. }
  47. // ParseKeyValue parses a space-separated "name value" kind of cgroup
  48. // parameter and returns its key as a string, and its value as uint64
  49. // (ParseUint is used to convert the value). For example,
  50. // "io_service_bytes 1234" will be returned as "io_service_bytes", 1234.
  51. func ParseKeyValue(t string) (string, uint64, error) {
  52. parts := strings.SplitN(t, " ", 3)
  53. if len(parts) != 2 {
  54. return "", 0, fmt.Errorf("line %q is not in key value format", t)
  55. }
  56. value, err := ParseUint(parts[1], 10, 64)
  57. if err != nil {
  58. return "", 0, err
  59. }
  60. return parts[0], value, nil
  61. }
  62. // GetValueByKey reads a key-value pairs from the specified cgroup file,
  63. // and returns a value of the specified key. ParseUint is used for value
  64. // conversion.
  65. func GetValueByKey(path, file, key string) (uint64, error) {
  66. content, err := cgroups.ReadFile(path, file)
  67. if err != nil {
  68. return 0, err
  69. }
  70. lines := strings.Split(content, "\n")
  71. for _, line := range lines {
  72. arr := strings.Split(line, " ")
  73. if len(arr) == 2 && arr[0] == key {
  74. val, err := ParseUint(arr[1], 10, 64)
  75. if err != nil {
  76. err = &ParseError{Path: path, File: file, Err: err}
  77. }
  78. return val, err
  79. }
  80. }
  81. return 0, nil
  82. }
  83. // GetCgroupParamUint reads a single uint64 value from the specified cgroup file.
  84. // If the value read is "max", the math.MaxUint64 is returned.
  85. func GetCgroupParamUint(path, file string) (uint64, error) {
  86. contents, err := GetCgroupParamString(path, file)
  87. if err != nil {
  88. return 0, err
  89. }
  90. contents = strings.TrimSpace(contents)
  91. if contents == "max" {
  92. return math.MaxUint64, nil
  93. }
  94. res, err := ParseUint(contents, 10, 64)
  95. if err != nil {
  96. return res, &ParseError{Path: path, File: file, Err: err}
  97. }
  98. return res, nil
  99. }
  100. // GetCgroupParamInt reads a single int64 value from specified cgroup file.
  101. // If the value read is "max", the math.MaxInt64 is returned.
  102. func GetCgroupParamInt(path, file string) (int64, error) {
  103. contents, err := cgroups.ReadFile(path, file)
  104. if err != nil {
  105. return 0, err
  106. }
  107. contents = strings.TrimSpace(contents)
  108. if contents == "max" {
  109. return math.MaxInt64, nil
  110. }
  111. res, err := strconv.ParseInt(contents, 10, 64)
  112. if err != nil {
  113. return res, &ParseError{Path: path, File: file, Err: err}
  114. }
  115. return res, nil
  116. }
  117. // GetCgroupParamString reads a string from the specified cgroup file.
  118. func GetCgroupParamString(path, file string) (string, error) {
  119. contents, err := cgroups.ReadFile(path, file)
  120. if err != nil {
  121. return "", err
  122. }
  123. return strings.TrimSpace(contents), nil
  124. }