123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- package fs
- import (
- "io/ioutil"
- "os"
- "path/filepath"
- "strconv"
- "sync"
- "github.com/docker/libcontainer/cgroups"
- "github.com/docker/libcontainer/configs"
- )
- var (
- subsystems = map[string]subsystem{
- "devices": &DevicesGroup{},
- "memory": &MemoryGroup{},
- "cpu": &CpuGroup{},
- "cpuset": &CpusetGroup{},
- "cpuacct": &CpuacctGroup{},
- "blkio": &BlkioGroup{},
- "perf_event": &PerfEventGroup{},
- "freezer": &FreezerGroup{},
- }
- CgroupProcesses = "cgroup.procs"
- )
- type subsystem interface {
- // Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
- GetStats(path string, stats *cgroups.Stats) error
- // Removes the cgroup represented by 'data'.
- Remove(*data) error
- // Creates and joins the cgroup represented by data.
- Apply(*data) error
- // Set the cgroup represented by cgroup.
- Set(path string, cgroup *configs.Cgroup) error
- }
- type Manager struct {
- Cgroups *configs.Cgroup
- Paths map[string]string
- }
- // The absolute path to the root of the cgroup hierarchies.
- var cgroupRootLock sync.Mutex
- var cgroupRoot string
- // Gets the cgroupRoot.
- func getCgroupRoot() (string, error) {
- cgroupRootLock.Lock()
- defer cgroupRootLock.Unlock()
- if cgroupRoot != "" {
- return cgroupRoot, nil
- }
- root, err := cgroups.FindCgroupMountpointDir()
- if err != nil {
- return "", err
- }
- if _, err := os.Stat(root); err != nil {
- return "", err
- }
- cgroupRoot = root
- return cgroupRoot, nil
- }
- type data struct {
- root string
- cgroup string
- c *configs.Cgroup
- pid int
- }
- func (m *Manager) Apply(pid int) error {
- if m.Cgroups == nil {
- return nil
- }
- d, err := getCgroupData(m.Cgroups, pid)
- if err != nil {
- return err
- }
- paths := make(map[string]string)
- defer func() {
- if err != nil {
- cgroups.RemovePaths(paths)
- }
- }()
- for name, sys := range subsystems {
- if err := sys.Apply(d); err != nil {
- return err
- }
- // TODO: Apply should, ideally, be reentrant or be broken up into a separate
- // create and join phase so that the cgroup hierarchy for a container can be
- // created then join consists of writing the process pids to cgroup.procs
- p, err := d.path(name)
- if err != nil {
- if cgroups.IsNotFound(err) {
- continue
- }
- return err
- }
- paths[name] = p
- }
- m.Paths = paths
- return nil
- }
- func (m *Manager) Destroy() error {
- return cgroups.RemovePaths(m.Paths)
- }
- func (m *Manager) GetPaths() map[string]string {
- return m.Paths
- }
- // Symmetrical public function to update device based cgroups. Also available
- // in the systemd implementation.
- func ApplyDevices(c *configs.Cgroup, pid int) error {
- d, err := getCgroupData(c, pid)
- if err != nil {
- return err
- }
- devices := subsystems["devices"]
- return devices.Apply(d)
- }
- func (m *Manager) GetStats() (*cgroups.Stats, error) {
- stats := cgroups.NewStats()
- for name, path := range m.Paths {
- sys, ok := subsystems[name]
- if !ok || !cgroups.PathExists(path) {
- continue
- }
- if err := sys.GetStats(path, stats); err != nil {
- return nil, err
- }
- }
- return stats, nil
- }
- func (m *Manager) Set(container *configs.Config) error {
- for name, path := range m.Paths {
- sys, ok := subsystems[name]
- if !ok || !cgroups.PathExists(path) {
- continue
- }
- if err := sys.Set(path, container.Cgroups); err != nil {
- return err
- }
- }
- return nil
- }
- // Freeze toggles the container's freezer cgroup depending on the state
- // provided
- func (m *Manager) Freeze(state configs.FreezerState) error {
- d, err := getCgroupData(m.Cgroups, 0)
- if err != nil {
- return err
- }
- dir, err := d.path("freezer")
- if err != nil {
- return err
- }
- prevState := m.Cgroups.Freezer
- m.Cgroups.Freezer = state
- freezer := subsystems["freezer"]
- err = freezer.Set(dir, m.Cgroups)
- if err != nil {
- m.Cgroups.Freezer = prevState
- return err
- }
- return nil
- }
- func (m *Manager) GetPids() ([]int, error) {
- d, err := getCgroupData(m.Cgroups, 0)
- if err != nil {
- return nil, err
- }
- dir, err := d.path("devices")
- if err != nil {
- return nil, err
- }
- return cgroups.ReadProcsFile(dir)
- }
- func getCgroupData(c *configs.Cgroup, pid int) (*data, error) {
- root, err := getCgroupRoot()
- if err != nil {
- return nil, err
- }
- cgroup := c.Name
- if c.Parent != "" {
- cgroup = filepath.Join(c.Parent, cgroup)
- }
- return &data{
- root: root,
- cgroup: cgroup,
- c: c,
- pid: pid,
- }, nil
- }
- func (raw *data) parent(subsystem, mountpoint string) (string, error) {
- initPath, err := cgroups.GetInitCgroupDir(subsystem)
- if err != nil {
- return "", err
- }
- return filepath.Join(mountpoint, initPath), nil
- }
- func (raw *data) path(subsystem string) (string, error) {
- mnt, err := cgroups.FindCgroupMountpoint(subsystem)
- // If we didn't mount the subsystem, there is no point we make the path.
- if err != nil {
- return "", err
- }
- // If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
- if filepath.IsAbs(raw.cgroup) {
- return filepath.Join(raw.root, subsystem, raw.cgroup), nil
- }
- parent, err := raw.parent(subsystem, mnt)
- if err != nil {
- return "", err
- }
- return filepath.Join(parent, raw.cgroup), nil
- }
- func (raw *data) join(subsystem string) (string, error) {
- path, err := raw.path(subsystem)
- if err != nil {
- return "", err
- }
- if err := os.MkdirAll(path, 0755); err != nil && !os.IsExist(err) {
- return "", err
- }
- if err := writeFile(path, CgroupProcesses, strconv.Itoa(raw.pid)); err != nil {
- return "", err
- }
- return path, nil
- }
- func writeFile(dir, file, data string) error {
- return ioutil.WriteFile(filepath.Join(dir, file), []byte(data), 0700)
- }
- func readFile(dir, file string) (string, error) {
- data, err := ioutil.ReadFile(filepath.Join(dir, file))
- return string(data), err
- }
- func removePath(p string, err error) error {
- if err != nil {
- return err
- }
- if p != "" {
- return os.RemoveAll(p)
- }
- return nil
- }
|