apply_raw.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. package fs
  2. import (
  3. "io/ioutil"
  4. "os"
  5. "path/filepath"
  6. "strconv"
  7. "sync"
  8. "github.com/docker/libcontainer/cgroups"
  9. "github.com/docker/libcontainer/configs"
  10. )
  11. var (
  12. subsystems = map[string]subsystem{
  13. "devices": &DevicesGroup{},
  14. "memory": &MemoryGroup{},
  15. "cpu": &CpuGroup{},
  16. "cpuset": &CpusetGroup{},
  17. "cpuacct": &CpuacctGroup{},
  18. "blkio": &BlkioGroup{},
  19. "perf_event": &PerfEventGroup{},
  20. "freezer": &FreezerGroup{},
  21. }
  22. CgroupProcesses = "cgroup.procs"
  23. )
  24. type subsystem interface {
  25. // Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
  26. GetStats(path string, stats *cgroups.Stats) error
  27. // Removes the cgroup represented by 'data'.
  28. Remove(*data) error
  29. // Creates and joins the cgroup represented by data.
  30. Apply(*data) error
  31. // Set the cgroup represented by cgroup.
  32. Set(path string, cgroup *configs.Cgroup) error
  33. }
  34. type Manager struct {
  35. Cgroups *configs.Cgroup
  36. Paths map[string]string
  37. }
  38. // The absolute path to the root of the cgroup hierarchies.
  39. var cgroupRootLock sync.Mutex
  40. var cgroupRoot string
  41. // Gets the cgroupRoot.
  42. func getCgroupRoot() (string, error) {
  43. cgroupRootLock.Lock()
  44. defer cgroupRootLock.Unlock()
  45. if cgroupRoot != "" {
  46. return cgroupRoot, nil
  47. }
  48. root, err := cgroups.FindCgroupMountpointDir()
  49. if err != nil {
  50. return "", err
  51. }
  52. if _, err := os.Stat(root); err != nil {
  53. return "", err
  54. }
  55. cgroupRoot = root
  56. return cgroupRoot, nil
  57. }
  58. type data struct {
  59. root string
  60. cgroup string
  61. c *configs.Cgroup
  62. pid int
  63. }
  64. func (m *Manager) Apply(pid int) error {
  65. if m.Cgroups == nil {
  66. return nil
  67. }
  68. d, err := getCgroupData(m.Cgroups, pid)
  69. if err != nil {
  70. return err
  71. }
  72. paths := make(map[string]string)
  73. defer func() {
  74. if err != nil {
  75. cgroups.RemovePaths(paths)
  76. }
  77. }()
  78. for name, sys := range subsystems {
  79. if err := sys.Apply(d); err != nil {
  80. return err
  81. }
  82. // TODO: Apply should, ideally, be reentrant or be broken up into a separate
  83. // create and join phase so that the cgroup hierarchy for a container can be
  84. // created then join consists of writing the process pids to cgroup.procs
  85. p, err := d.path(name)
  86. if err != nil {
  87. if cgroups.IsNotFound(err) {
  88. continue
  89. }
  90. return err
  91. }
  92. paths[name] = p
  93. }
  94. m.Paths = paths
  95. return nil
  96. }
  97. func (m *Manager) Destroy() error {
  98. return cgroups.RemovePaths(m.Paths)
  99. }
  100. func (m *Manager) GetPaths() map[string]string {
  101. return m.Paths
  102. }
  103. // Symmetrical public function to update device based cgroups. Also available
  104. // in the systemd implementation.
  105. func ApplyDevices(c *configs.Cgroup, pid int) error {
  106. d, err := getCgroupData(c, pid)
  107. if err != nil {
  108. return err
  109. }
  110. devices := subsystems["devices"]
  111. return devices.Apply(d)
  112. }
  113. func (m *Manager) GetStats() (*cgroups.Stats, error) {
  114. stats := cgroups.NewStats()
  115. for name, path := range m.Paths {
  116. sys, ok := subsystems[name]
  117. if !ok || !cgroups.PathExists(path) {
  118. continue
  119. }
  120. if err := sys.GetStats(path, stats); err != nil {
  121. return nil, err
  122. }
  123. }
  124. return stats, nil
  125. }
  126. func (m *Manager) Set(container *configs.Config) error {
  127. for name, path := range m.Paths {
  128. sys, ok := subsystems[name]
  129. if !ok || !cgroups.PathExists(path) {
  130. continue
  131. }
  132. if err := sys.Set(path, container.Cgroups); err != nil {
  133. return err
  134. }
  135. }
  136. return nil
  137. }
  138. // Freeze toggles the container's freezer cgroup depending on the state
  139. // provided
  140. func (m *Manager) Freeze(state configs.FreezerState) error {
  141. d, err := getCgroupData(m.Cgroups, 0)
  142. if err != nil {
  143. return err
  144. }
  145. dir, err := d.path("freezer")
  146. if err != nil {
  147. return err
  148. }
  149. prevState := m.Cgroups.Freezer
  150. m.Cgroups.Freezer = state
  151. freezer := subsystems["freezer"]
  152. err = freezer.Set(dir, m.Cgroups)
  153. if err != nil {
  154. m.Cgroups.Freezer = prevState
  155. return err
  156. }
  157. return nil
  158. }
  159. func (m *Manager) GetPids() ([]int, error) {
  160. d, err := getCgroupData(m.Cgroups, 0)
  161. if err != nil {
  162. return nil, err
  163. }
  164. dir, err := d.path("devices")
  165. if err != nil {
  166. return nil, err
  167. }
  168. return cgroups.ReadProcsFile(dir)
  169. }
  170. func getCgroupData(c *configs.Cgroup, pid int) (*data, error) {
  171. root, err := getCgroupRoot()
  172. if err != nil {
  173. return nil, err
  174. }
  175. cgroup := c.Name
  176. if c.Parent != "" {
  177. cgroup = filepath.Join(c.Parent, cgroup)
  178. }
  179. return &data{
  180. root: root,
  181. cgroup: cgroup,
  182. c: c,
  183. pid: pid,
  184. }, nil
  185. }
  186. func (raw *data) parent(subsystem, mountpoint string) (string, error) {
  187. initPath, err := cgroups.GetInitCgroupDir(subsystem)
  188. if err != nil {
  189. return "", err
  190. }
  191. return filepath.Join(mountpoint, initPath), nil
  192. }
  193. func (raw *data) path(subsystem string) (string, error) {
  194. mnt, err := cgroups.FindCgroupMountpoint(subsystem)
  195. // If we didn't mount the subsystem, there is no point we make the path.
  196. if err != nil {
  197. return "", err
  198. }
  199. // If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
  200. if filepath.IsAbs(raw.cgroup) {
  201. return filepath.Join(raw.root, subsystem, raw.cgroup), nil
  202. }
  203. parent, err := raw.parent(subsystem, mnt)
  204. if err != nil {
  205. return "", err
  206. }
  207. return filepath.Join(parent, raw.cgroup), nil
  208. }
  209. func (raw *data) join(subsystem string) (string, error) {
  210. path, err := raw.path(subsystem)
  211. if err != nil {
  212. return "", err
  213. }
  214. if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
  215. return "", err
  216. }
  217. if err := writeFile(path, CgroupProcesses, strconv.Itoa(raw.pid)); err != nil {
  218. return "", err
  219. }
  220. return path, nil
  221. }
  222. func writeFile(dir, file, data string) error {
  223. return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
  224. }
  225. func readFile(dir, file string) (string, error) {
  226. data, err := ioutil.ReadFile(filepath.Join(dir, file))
  227. return string(data), err
  228. }
  229. func removePath(p string, err error) error {
  230. if err != nil {
  231. return err
  232. }
  233. if p != "" {
  234. return os.RemoveAll(p)
  235. }
  236. return nil
  237. }