utils.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /*
  2. Copyright The containerd Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cgroups
  14. import (
  15. "bufio"
  16. "fmt"
  17. "io"
  18. "os"
  19. "path/filepath"
  20. "strings"
  21. "sync"
  22. "golang.org/x/sys/unix"
  23. )
  24. var (
  25. nsOnce sync.Once
  26. inUserNS bool
  27. checkMode sync.Once
  28. cgMode CGMode
  29. )
  30. const unifiedMountpoint = "/sys/fs/cgroup"
  31. // CGMode is the cgroups mode of the host system
  32. type CGMode int
  33. const (
  34. // Unavailable cgroup mountpoint
  35. Unavailable CGMode = iota
  36. // Legacy cgroups v1
  37. Legacy
  38. // Hybrid with cgroups v1 and v2 controllers mounted
  39. Hybrid
  40. // Unified with only cgroups v2 mounted
  41. Unified
  42. )
  43. // Mode returns the cgroups mode running on the host
  44. func Mode() CGMode {
  45. checkMode.Do(func() {
  46. var st unix.Statfs_t
  47. if err := unix.Statfs(unifiedMountpoint, &st); err != nil {
  48. cgMode = Unavailable
  49. return
  50. }
  51. switch st.Type {
  52. case unix.CGROUP2_SUPER_MAGIC:
  53. cgMode = Unified
  54. default:
  55. cgMode = Legacy
  56. if err := unix.Statfs(filepath.Join(unifiedMountpoint, "unified"), &st); err != nil {
  57. return
  58. }
  59. if st.Type == unix.CGROUP2_SUPER_MAGIC {
  60. cgMode = Hybrid
  61. }
  62. }
  63. })
  64. return cgMode
  65. }
  66. // RunningInUserNS detects whether we are currently running in a user namespace.
  67. // Copied from github.com/lxc/lxd/shared/util.go
  68. func RunningInUserNS() bool {
  69. nsOnce.Do(func() {
  70. file, err := os.Open("/proc/self/uid_map")
  71. if err != nil {
  72. // This kernel-provided file only exists if user namespaces are supported
  73. return
  74. }
  75. defer file.Close()
  76. buf := bufio.NewReader(file)
  77. l, _, err := buf.ReadLine()
  78. if err != nil {
  79. return
  80. }
  81. line := string(l)
  82. var a, b, c int64
  83. fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
  84. /*
  85. * We assume we are in the initial user namespace if we have a full
  86. * range - 4294967295 uids starting at uid 0.
  87. */
  88. if a == 0 && b == 0 && c == 4294967295 {
  89. return
  90. }
  91. inUserNS = true
  92. })
  93. return inUserNS
  94. }
  95. // ParseCgroupFileUnified returns legacy subsystem paths as the first value,
  96. // and returns the unified path as the second value.
  97. func ParseCgroupFileUnified(path string) (map[string]string, string, error) {
  98. f, err := os.Open(path)
  99. if err != nil {
  100. return nil, "", err
  101. }
  102. defer f.Close()
  103. return ParseCgroupFromReaderUnified(f)
  104. }
  105. // ParseCgroupFromReaderUnified returns legacy subsystem paths as the first value,
  106. // and returns the unified path as the second value.
  107. func ParseCgroupFromReaderUnified(r io.Reader) (map[string]string, string, error) {
  108. var (
  109. cgroups = make(map[string]string)
  110. unified = ""
  111. s = bufio.NewScanner(r)
  112. )
  113. for s.Scan() {
  114. var (
  115. text = s.Text()
  116. parts = strings.SplitN(text, ":", 3)
  117. )
  118. if len(parts) < 3 {
  119. return nil, unified, fmt.Errorf("invalid cgroup entry: %q", text)
  120. }
  121. for _, subs := range strings.Split(parts[1], ",") {
  122. if subs == "" {
  123. unified = parts[2]
  124. } else {
  125. cgroups[subs] = parts[2]
  126. }
  127. }
  128. }
  129. if err := s.Err(); err != nil {
  130. return nil, unified, err
  131. }
  132. return cgroups, unified, nil
  133. }