// +build linux package rootless import ( "fmt" "github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups/fs" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs/validate" ) // TODO: This is copied from libcontainer/cgroups/fs, which duplicates this code // needlessly. We should probably export this list. var subsystems = []subsystem{ &fs.CpusetGroup{}, &fs.DevicesGroup{}, &fs.MemoryGroup{}, &fs.CpuGroup{}, &fs.CpuacctGroup{}, &fs.PidsGroup{}, &fs.BlkioGroup{}, &fs.HugetlbGroup{}, &fs.NetClsGroup{}, &fs.NetPrioGroup{}, &fs.PerfEventGroup{}, &fs.FreezerGroup{}, &fs.NameGroup{GroupName: "name=systemd"}, } type subsystem interface { // Name returns the name of the subsystem. Name() string // Returns the stats, as 'stats', corresponding to the cgroup under 'path'. GetStats(path string, stats *cgroups.Stats) error } // The noop cgroup manager is used for rootless containers, because we currently // cannot manage cgroups if we are in a rootless setup. This manager is chosen // by factory if we are in rootless mode. We error out if any cgroup options are // set in the config -- this may change in the future with upcoming kernel features // like the cgroup namespace. type Manager struct { Cgroups *configs.Cgroup Paths map[string]string } func (m *Manager) Apply(pid int) error { // If there are no cgroup settings, there's nothing to do. if m.Cgroups == nil { return nil } // We can't set paths. // TODO(cyphar): Implement the case where the runner of a rootless container // owns their own cgroup, which would allow us to set up a // cgroup for each path. if m.Cgroups.Paths != nil { return fmt.Errorf("cannot change cgroup path in rootless container") } // We load the paths into the manager. paths := make(map[string]string) for _, sys := range subsystems { name := sys.Name() path, err := cgroups.GetOwnCgroupPath(name) if err != nil { // Ignore paths we couldn't resolve. continue } paths[name] = path } m.Paths = paths return nil } func (m *Manager) GetPaths() map[string]string { return m.Paths } func (m *Manager) Set(container *configs.Config) error { // We have to re-do the validation here, since someone might decide to // update a rootless container. return validate.New().Validate(container) } func (m *Manager) GetPids() ([]int, error) { dir, err := cgroups.GetOwnCgroupPath("devices") if err != nil { return nil, err } return cgroups.GetPids(dir) } func (m *Manager) GetAllPids() ([]int, error) { dir, err := cgroups.GetOwnCgroupPath("devices") if err != nil { return nil, err } return cgroups.GetAllPids(dir) } func (m *Manager) GetStats() (*cgroups.Stats, error) { // TODO(cyphar): We can make this work if we figure out a way to allow usage // of cgroups with a rootless container. While this doesn't // actually require write access to a cgroup directory, the // statistics are not useful if they can be affected by // non-container processes. return nil, fmt.Errorf("cannot get cgroup stats in rootless container") } func (m *Manager) Freeze(state configs.FreezerState) error { // TODO(cyphar): We can make this work if we figure out a way to allow usage // of cgroups with a rootless container. return fmt.Errorf("cannot use freezer cgroup in rootless container") } func (m *Manager) Destroy() error { // We don't have to do anything here because we didn't do any setup. return nil }