Skip to content

Commit

Permalink
[RFC] Implement systemd-specific per-cgroup support, add it to "devic…
Browse files Browse the repository at this point in the history
…es" cgroup

This PR is trying to accomplish two things:

1. Define a new interface that will allow subsystems/controllers to
   implement systemd-based configuration, by using systemd directives
   rather than writing directly to the cgroup subtree.

2. Add a systemd-based implementation to the "devices" subsystem, to
   illustrate how it is meant to be used.

The initial point I'd like to make here is towards discussing (1) as an
idea and whether the Go abstractions/interfaces are appropriate here or
whether we should move things around.

Consider part (2) to be really a draft and not really finished (even
though it actually works to a large extent, the D-Bus messages are
correct and that has been tested to do what's expected.)

I tested this with Podman using:

  $ podman --runtime ~/go/src/github.com/opencontainers/runc/runc run -t fedora:29 echo hello

And also bringing up a container and checking the contents of
"device.list" in the cgroup subtree:

  $ podman --runtime ~/go/src/github.com/opencontainers/runc/runc run -t fedora:29 sleep 1h
  $ cat /sys/fs/cgroup/devices/machine.slice/libpod-12fc7bd62fd6*/devices.list
  c 10:200 rwm
  c 5:2 rwm
  c 136:* rwm
  c 5:1 rwm
  c 1:9 rwm
  c 1:5 rwm
  c 5:0 rwm
  c 1:7 rwm
  c 1:8 rwm
  c 1:3 rwm
  b *:* m
  c *:* m

This matches the output of devices.list when using the official "runc"
binary, only difference being the lines are inverted in order (again, we
can fix that on a second step.)

Querying systemd for this unit also works as expected:

  $ systemctl show libpod-12fc7bd62fd66ff62fa1b045c2d717c7b2076c072c20de14f5c1ad86b78865eb.scope -p DevicePolicy -p DeviceAllow
  DevicePolicy=strict
  DeviceAllow=/dev/net/tun rwm
  DeviceAllow=/dev/ptmx rwm
  DeviceAllow=char-136 rwm
  DeviceAllow=/dev/console rwm
  DeviceAllow=/dev/urandom rwm
  DeviceAllow=/dev/zero rwm
  DeviceAllow=/dev/tty rwm
  DeviceAllow=/dev/full rwm
  DeviceAllow=/dev/random rwm
  DeviceAllow=/dev/null rwm
  DeviceAllow=block-* m
  DeviceAllow=char-* m

Signed-off-by: Filipe Brandenburger <filbranden@google.com>
  • Loading branch information
filbranden committed Feb 20, 2019
1 parent 751f18d commit a77076f
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
41 changes: 41 additions & 0 deletions libcontainer/cgroups/fs/devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
package fs

import (
"fmt"

"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/system"

systemdDbus "github.com/coreos/go-systemd/dbus"
"github.com/godbus/dbus"
)

type DevicesGroup struct {
Expand Down Expand Up @@ -71,6 +76,42 @@ func (s *DevicesGroup) Set(path string, cgroup *configs.Cgroup) error {
return nil
}

func (s *DevicesGroup) ToSystemdProperties(cgroup *configs.Cgroup) ([]systemdDbus.Property, error) {
var properties []systemdDbus.Property

devices := cgroup.Resources.Devices
if len(devices) > 0 {
// This setup is not supported by systemd. This should log and bail out.
return properties, fmt.Errorf("Not supported by systemd, please use AllowedDevices instead.")
}
if cgroup.Resources.AllowAllDevices != nil {
if *cgroup.Resources.AllowAllDevices {
properties = append(properties, systemdDbus.Property{
Name: "DevicePolicy",
Value: dbus.MakeVariant("auto"),
}, systemdDbus.Property{
Name: "DeviceAllow",
Value: dbus.MakeVariant(""),
})
} else {
properties = append(properties, systemdDbus.Property{
Name: "DevicePolicy",
Value: dbus.MakeVariant("strict"),
}, systemdDbus.Property{
Name: "DeviceAllow",
Value: dbus.MakeVariant(""),
})
for _, dev := range cgroup.Resources.AllowedDevices {
properties = append(properties, systemdDbus.Property{
Name: "DeviceAllow",
Value: dbus.MakeVariant(dev.SystemdCgroupString()),
})
}
}
}
return properties, nil
}

func (s *DevicesGroup) Remove(d *cgroupData) error {
return removePath(d.path("devices"))
}
Expand Down
21 changes: 21 additions & 0 deletions libcontainer/cgroups/systemd/apply_systemd.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ type subsystem interface {
Set(path string, cgroup *configs.Cgroup) error
}

type systemdSubsystem interface {
// Returns a list of systemd properties to manage the underlying cgroups.
ToSystemdProperties(cgroup *configs.Cgroup) ([]systemdDbus.Property, error)
}

var errSubsystemDoesNotExist = errors.New("cgroup: subsystem does not exist")

type subsystemSet []subsystem
Expand Down Expand Up @@ -282,6 +287,18 @@ func (m *Manager) Apply(pid int) error {
}
}

// Set the systemd properties coming from subsystems.
for _, sys := range subsystems {
if sdSys, ok := sys.(systemdSubsystem); ok {
sdProp, err := sdSys.ToSystemdProperties(c)
if err != nil {
return err
}
logrus.Infof("Setting properties on unit %s from subsystem %s: %q", unitName, sys.Name(), sdProp)
properties = append(properties, sdProp...)
}
}

statusChan := make(chan string, 1)
if _, err := theConn.StartTransientUnit(unitName, "replace", properties, statusChan); err == nil {
select {
Expand Down Expand Up @@ -503,6 +520,10 @@ func (m *Manager) Set(container *configs.Config) error {
return nil
}
for _, sys := range subsystems {
if _, ok := sys.(systemdSubsystem); ok {
// Skip it if it's a systemd subsystem, it's been done already.
continue
}
// Get the subsystem path, but don't error out for not found cgroups.
path, err := getSubsystemPath(container.Cgroups, sys.Name())
if err != nil && !cgroups.IsNotFound(err) {
Expand Down
13 changes: 13 additions & 0 deletions libcontainer/configs/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ func (d *Device) CgroupString() string {
return fmt.Sprintf("%c %s:%s %s", d.Type, deviceNumberString(d.Major), deviceNumberString(d.Minor), d.Permissions)
}

func (d *Device) SystemdCgroupString() string {
if d.Minor == Wildcard {
sdType := "char"
if (d.Type == 'b') {
sdType = "block"
}
return fmt.Sprintf("%s-%s %s", sdType, deviceNumberString(d.Major), d.Permissions)
}
// Systemd doesn't support specifying a device by major:minor, only
// major, so block by device path instead.
return fmt.Sprintf("%s %s", d.Path, d.Permissions)
}

func (d *Device) Mkdev() int {
return int((d.Major << 8) | (d.Minor & 0xff) | ((d.Minor & 0xfff00) << 12))
}
Expand Down

0 comments on commit a77076f

Please sign in to comment.