fs2.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // +build linux
  2. package fs2
  3. import (
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. securejoin "github.com/cyphar/filepath-securejoin"
  9. "github.com/opencontainers/runc/libcontainer/cgroups"
  10. "github.com/opencontainers/runc/libcontainer/configs"
  11. "github.com/pkg/errors"
  12. )
  13. // NewManager creates a manager for cgroup v2 unified hierarchy.
  14. // dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope".
  15. // If dirPath is empty, it is automatically set using config.
  16. func NewManager(config *configs.Cgroup, dirPath string, rootless bool) (cgroups.Manager, error) {
  17. if config == nil {
  18. config = &configs.Cgroup{}
  19. }
  20. if dirPath != "" {
  21. if filepath.Clean(dirPath) != dirPath || !filepath.IsAbs(dirPath) {
  22. return nil, errors.Errorf("invalid dir path %q", dirPath)
  23. }
  24. } else {
  25. var err error
  26. dirPath, err = defaultDirPath(config)
  27. if err != nil {
  28. return nil, err
  29. }
  30. }
  31. controllers, err := detectControllers(dirPath)
  32. if err != nil && !rootless {
  33. return nil, err
  34. }
  35. m := &manager{
  36. config: config,
  37. dirPath: dirPath,
  38. controllers: controllers,
  39. rootless: rootless,
  40. }
  41. return m, nil
  42. }
  43. func detectControllers(dirPath string) (map[string]struct{}, error) {
  44. if err := os.MkdirAll(dirPath, 0755); err != nil {
  45. return nil, err
  46. }
  47. controllersPath, err := securejoin.SecureJoin(dirPath, "cgroup.controllers")
  48. if err != nil {
  49. return nil, err
  50. }
  51. controllersData, err := ioutil.ReadFile(controllersPath)
  52. if err != nil {
  53. return nil, err
  54. }
  55. controllersFields := strings.Fields(string(controllersData))
  56. controllers := make(map[string]struct{}, len(controllersFields))
  57. for _, c := range controllersFields {
  58. controllers[c] = struct{}{}
  59. }
  60. return controllers, nil
  61. }
  62. type manager struct {
  63. config *configs.Cgroup
  64. // dirPath is like "/sys/fs/cgroup/user.slice/user-1001.slice/session-1.scope"
  65. dirPath string
  66. // controllers is content of "cgroup.controllers" file.
  67. // excludes pseudo-controllers ("devices" and "freezer").
  68. controllers map[string]struct{}
  69. rootless bool
  70. }
  71. func (m *manager) Apply(pid int) error {
  72. if err := cgroups.WriteCgroupProc(m.dirPath, pid); err != nil && !m.rootless {
  73. return err
  74. }
  75. return nil
  76. }
  77. func (m *manager) GetPids() ([]int, error) {
  78. return cgroups.GetPids(m.dirPath)
  79. }
  80. func (m *manager) GetAllPids() ([]int, error) {
  81. return cgroups.GetAllPids(m.dirPath)
  82. }
  83. func (m *manager) GetStats() (*cgroups.Stats, error) {
  84. var (
  85. st cgroups.Stats
  86. errs []error
  87. )
  88. // pids (since kernel 4.5)
  89. if _, ok := m.controllers["pids"]; ok {
  90. if err := statPids(m.dirPath, &st); err != nil {
  91. errs = append(errs, err)
  92. }
  93. } else {
  94. if err := statPidsWithoutController(m.dirPath, &st); err != nil {
  95. errs = append(errs, err)
  96. }
  97. }
  98. // memory (since kenrel 4.5)
  99. if _, ok := m.controllers["memory"]; ok {
  100. if err := statMemory(m.dirPath, &st); err != nil {
  101. errs = append(errs, err)
  102. }
  103. }
  104. // io (since kernel 4.5)
  105. if _, ok := m.controllers["io"]; ok {
  106. if err := statIo(m.dirPath, &st); err != nil {
  107. errs = append(errs, err)
  108. }
  109. }
  110. // cpu (since kernel 4.15)
  111. if _, ok := m.controllers["cpu"]; ok {
  112. if err := statCpu(m.dirPath, &st); err != nil {
  113. errs = append(errs, err)
  114. }
  115. }
  116. if len(errs) > 0 && !m.rootless {
  117. return &st, errors.Errorf("error while statting cgroup v2: %+v", errs)
  118. }
  119. return &st, nil
  120. }
  121. func (m *manager) Freeze(state configs.FreezerState) error {
  122. if err := setFreezer(m.dirPath, state); err != nil {
  123. return err
  124. }
  125. m.config.Resources.Freezer = state
  126. return nil
  127. }
  128. func (m *manager) Destroy() error {
  129. return os.RemoveAll(m.dirPath)
  130. }
  131. // GetPaths is for compatibility purpose and should be removed in future
  132. func (m *manager) GetPaths() map[string]string {
  133. paths := map[string]string{
  134. // pseudo-controller for compatibility
  135. "devices": m.dirPath,
  136. "freezer": m.dirPath,
  137. }
  138. for c := range m.controllers {
  139. paths[c] = m.dirPath
  140. }
  141. return paths
  142. }
  143. func (m *manager) GetUnifiedPath() (string, error) {
  144. return m.dirPath, nil
  145. }
  146. func (m *manager) Set(container *configs.Config) error {
  147. if container == nil || container.Cgroups == nil {
  148. return nil
  149. }
  150. var errs []error
  151. // pids (since kernel 4.5)
  152. if _, ok := m.controllers["pids"]; ok {
  153. if err := setPids(m.dirPath, container.Cgroups); err != nil {
  154. errs = append(errs, err)
  155. }
  156. }
  157. // memory (since kernel 4.5)
  158. if _, ok := m.controllers["memory"]; ok {
  159. if err := setMemory(m.dirPath, container.Cgroups); err != nil {
  160. errs = append(errs, err)
  161. }
  162. }
  163. // io (since kernel 4.5)
  164. if _, ok := m.controllers["io"]; ok {
  165. if err := setIo(m.dirPath, container.Cgroups); err != nil {
  166. errs = append(errs, err)
  167. }
  168. }
  169. // cpu (since kernel 4.15)
  170. if _, ok := m.controllers["cpu"]; ok {
  171. if err := setCpu(m.dirPath, container.Cgroups); err != nil {
  172. errs = append(errs, err)
  173. }
  174. }
  175. // devices (since kernel 4.15, pseudo-controller)
  176. if err := setDevices(m.dirPath, container.Cgroups); err != nil {
  177. errs = append(errs, err)
  178. }
  179. // cpuset (since kernel 5.0)
  180. if _, ok := m.controllers["cpuset"]; ok {
  181. if err := setCpuset(m.dirPath, container.Cgroups); err != nil {
  182. errs = append(errs, err)
  183. }
  184. }
  185. // freezer (since kernel 5.2, pseudo-controller)
  186. if err := setFreezer(m.dirPath, container.Cgroups.Freezer); err != nil {
  187. errs = append(errs, err)
  188. }
  189. if len(errs) > 0 && !m.rootless {
  190. return errors.Errorf("error while setting cgroup v2: %+v", errs)
  191. }
  192. m.config = container.Cgroups
  193. return nil
  194. }
  195. func (m *manager) GetCgroups() (*configs.Cgroup, error) {
  196. return m.config, nil
  197. }