utils.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. package cgroups
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "strconv"
  10. "strings"
  11. "time"
  12. units "github.com/docker/go-units"
  13. specs "github.com/opencontainers/runtime-spec/specs-go"
  14. )
  15. var isUserNS = runningInUserNS()
  16. // runningInUserNS detects whether we are currently running in a user namespace.
  17. // Copied from github.com/lxc/lxd/shared/util.go
  18. func runningInUserNS() bool {
  19. file, err := os.Open("/proc/self/uid_map")
  20. if err != nil {
  21. // This kernel-provided file only exists if user namespaces are supported
  22. return false
  23. }
  24. defer file.Close()
  25. buf := bufio.NewReader(file)
  26. l, _, err := buf.ReadLine()
  27. if err != nil {
  28. return false
  29. }
  30. line := string(l)
  31. var a, b, c int64
  32. fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
  33. /*
  34. * We assume we are in the initial user namespace if we have a full
  35. * range - 4294967295 uids starting at uid 0.
  36. */
  37. if a == 0 && b == 0 && c == 4294967295 {
  38. return false
  39. }
  40. return true
  41. }
  42. // defaults returns all known groups
  43. func defaults(root string) ([]Subsystem, error) {
  44. h, err := NewHugetlb(root)
  45. if err != nil && !os.IsNotExist(err) {
  46. return nil, err
  47. }
  48. s := []Subsystem{
  49. NewNamed(root, "systemd"),
  50. NewFreezer(root),
  51. NewPids(root),
  52. NewNetCls(root),
  53. NewNetPrio(root),
  54. NewPerfEvent(root),
  55. NewCputset(root),
  56. NewCpu(root),
  57. NewCpuacct(root),
  58. NewMemory(root),
  59. NewBlkio(root),
  60. }
  61. // only add the devices cgroup if we are not in a user namespace
  62. // because modifications are not allowed
  63. if !isUserNS {
  64. s = append(s, NewDevices(root))
  65. }
  66. // add the hugetlb cgroup if error wasn't due to missing hugetlb
  67. // cgroup support on the host
  68. if err == nil {
  69. s = append(s, h)
  70. }
  71. return s, nil
  72. }
  73. // remove will remove a cgroup path handling EAGAIN and EBUSY errors and
  74. // retrying the remove after a exp timeout
  75. func remove(path string) error {
  76. delay := 10 * time.Millisecond
  77. for i := 0; i < 5; i++ {
  78. if i != 0 {
  79. time.Sleep(delay)
  80. delay *= 2
  81. }
  82. if err := os.RemoveAll(path); err == nil {
  83. return nil
  84. }
  85. }
  86. return fmt.Errorf("cgroups: unable to remove path %q", path)
  87. }
  88. // readPids will read all the pids in a cgroup by the provided path
  89. func readPids(path string, subsystem Name) ([]Process, error) {
  90. f, err := os.Open(filepath.Join(path, cgroupProcs))
  91. if err != nil {
  92. return nil, err
  93. }
  94. defer f.Close()
  95. var (
  96. out []Process
  97. s = bufio.NewScanner(f)
  98. )
  99. for s.Scan() {
  100. if t := s.Text(); t != "" {
  101. pid, err := strconv.Atoi(t)
  102. if err != nil {
  103. return nil, err
  104. }
  105. out = append(out, Process{
  106. Pid: pid,
  107. Subsystem: subsystem,
  108. Path: path,
  109. })
  110. }
  111. }
  112. return out, nil
  113. }
  114. func hugePageSizes() ([]string, error) {
  115. var (
  116. pageSizes []string
  117. sizeList = []string{"B", "kB", "MB", "GB", "TB", "PB"}
  118. )
  119. files, err := ioutil.ReadDir("/sys/kernel/mm/hugepages")
  120. if err != nil {
  121. return nil, err
  122. }
  123. for _, st := range files {
  124. nameArray := strings.Split(st.Name(), "-")
  125. pageSize, err := units.RAMInBytes(nameArray[1])
  126. if err != nil {
  127. return nil, err
  128. }
  129. pageSizes = append(pageSizes, units.CustomSize("%g%s", float64(pageSize), 1024.0, sizeList))
  130. }
  131. return pageSizes, nil
  132. }
  133. func readUint(path string) (uint64, error) {
  134. v, err := ioutil.ReadFile(path)
  135. if err != nil {
  136. return 0, err
  137. }
  138. return parseUint(strings.TrimSpace(string(v)), 10, 64)
  139. }
  140. func parseUint(s string, base, bitSize int) (uint64, error) {
  141. v, err := strconv.ParseUint(s, base, bitSize)
  142. if err != nil {
  143. intValue, intErr := strconv.ParseInt(s, base, bitSize)
  144. // 1. Handle negative values greater than MinInt64 (and)
  145. // 2. Handle negative values lesser than MinInt64
  146. if intErr == nil && intValue < 0 {
  147. return 0, nil
  148. } else if intErr != nil &&
  149. intErr.(*strconv.NumError).Err == strconv.ErrRange &&
  150. intValue < 0 {
  151. return 0, nil
  152. }
  153. return 0, err
  154. }
  155. return v, nil
  156. }
  157. func parseKV(raw string) (string, uint64, error) {
  158. parts := strings.Fields(raw)
  159. switch len(parts) {
  160. case 2:
  161. v, err := parseUint(parts[1], 10, 64)
  162. if err != nil {
  163. return "", 0, err
  164. }
  165. return parts[0], v, nil
  166. default:
  167. return "", 0, ErrInvalidFormat
  168. }
  169. }
  170. func parseCgroupFile(path string) (map[string]string, error) {
  171. f, err := os.Open(path)
  172. if err != nil {
  173. return nil, err
  174. }
  175. defer f.Close()
  176. return parseCgroupFromReader(f)
  177. }
  178. func parseCgroupFromReader(r io.Reader) (map[string]string, error) {
  179. var (
  180. cgroups = make(map[string]string)
  181. s = bufio.NewScanner(r)
  182. )
  183. for s.Scan() {
  184. if err := s.Err(); err != nil {
  185. return nil, err
  186. }
  187. var (
  188. text = s.Text()
  189. parts = strings.SplitN(text, ":", 3)
  190. )
  191. if len(parts) < 3 {
  192. return nil, fmt.Errorf("invalid cgroup entry: %q", text)
  193. }
  194. for _, subs := range strings.Split(parts[1], ",") {
  195. if subs != "" {
  196. cgroups[subs] = parts[2]
  197. }
  198. }
  199. }
  200. return cgroups, nil
  201. }
  202. func getCgroupDestination(subsystem string) (string, error) {
  203. f, err := os.Open("/proc/self/mountinfo")
  204. if err != nil {
  205. return "", err
  206. }
  207. defer f.Close()
  208. s := bufio.NewScanner(f)
  209. for s.Scan() {
  210. if err := s.Err(); err != nil {
  211. return "", err
  212. }
  213. fields := strings.Fields(s.Text())
  214. for _, opt := range strings.Split(fields[len(fields)-1], ",") {
  215. if opt == subsystem {
  216. return fields[3], nil
  217. }
  218. }
  219. }
  220. return "", ErrNoCgroupMountDestination
  221. }
  222. func pathers(subystems []Subsystem) []pather {
  223. var out []pather
  224. for _, s := range subystems {
  225. if p, ok := s.(pather); ok {
  226. out = append(out, p)
  227. }
  228. }
  229. return out
  230. }
  231. func initializeSubsystem(s Subsystem, path Path, resources *specs.LinuxResources) error {
  232. if c, ok := s.(creator); ok {
  233. p, err := path(s.Name())
  234. if err != nil {
  235. return err
  236. }
  237. if err := c.Create(p, resources); err != nil {
  238. return err
  239. }
  240. } else if c, ok := s.(pather); ok {
  241. p, err := path(s.Name())
  242. if err != nil {
  243. return err
  244. }
  245. // do the default create if the group does not have a custom one
  246. if err := os.MkdirAll(c.Path(p), defaultDirPerm); err != nil {
  247. return err
  248. }
  249. }
  250. return nil
  251. }
  252. func cleanPath(path string) string {
  253. if path == "" {
  254. return ""
  255. }
  256. path = filepath.Clean(path)
  257. if !filepath.IsAbs(path) {
  258. path, _ = filepath.Rel(string(os.PathSeparator), filepath.Clean(string(os.PathSeparator)+path))
  259. }
  260. return filepath.Clean(path)
  261. }