mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-10 21:53:57 +08:00
bf15cc99b1
Tested with both Podman (master) and Moby (master), on Ubuntu 19.10 . $ podman --cgroup-manager=systemd run -it --rm --runtime=runc \ --cgroupns=host --memory 42m --cpus 0.42 --pids-limit 42 alpine / # cat /proc/self/cgroup 0::/user.slice/user-1001.slice/user@1001.service/user.slice/libpod-132ff0d72245e6f13a3bbc6cdc5376886897b60ac59eaa8dea1df7ab959cbf1c.scope / # cat /sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service/user.slice/libpod-132ff0d72245e6f13a3bbc6cdc5376886897b60ac59eaa8dea1df7ab959cbf1c.scope/memory.max 44040192 / # cat /sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service/user.slice/libpod-132ff0d72245e6f13a3bbc6cdc5376886897b60ac59eaa8dea1df7ab959cbf1c.scope/cpu.max 42000 100000 / # cat /sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service/user.slice/libpod-132ff0d72245e6f13a3bbc6cdc5376886897b60ac59eaa8dea1df7ab959cbf1c.scope/pids.max 42 Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
147 lines
4.3 KiB
Go
147 lines
4.3 KiB
Go
package systemd
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
|
|
dbus "github.com/godbus/dbus/v5"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var (
|
|
connOnce sync.Once
|
|
connDbus *systemdDbus.Conn
|
|
connErr error
|
|
)
|
|
|
|
// NOTE: This function comes from package github.com/coreos/go-systemd/util
|
|
// It was borrowed here to avoid a dependency on cgo.
|
|
//
|
|
// IsRunningSystemd checks whether the host was booted with systemd as its init
|
|
// system. This functions similarly to systemd's `sd_booted(3)`: internally, it
|
|
// checks whether /run/systemd/system/ exists and is a directory.
|
|
// http://www.freedesktop.org/software/systemd/man/sd_booted.html
|
|
func IsRunningSystemd() bool {
|
|
fi, err := os.Lstat("/run/systemd/system")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return fi.IsDir()
|
|
}
|
|
|
|
// systemd represents slice hierarchy using `-`, so we need to follow suit when
|
|
// generating the path of slice. Essentially, test-a-b.slice becomes
|
|
// /test.slice/test-a.slice/test-a-b.slice.
|
|
func ExpandSlice(slice string) (string, error) {
|
|
suffix := ".slice"
|
|
// Name has to end with ".slice", but can't be just ".slice".
|
|
if len(slice) < len(suffix) || !strings.HasSuffix(slice, suffix) {
|
|
return "", fmt.Errorf("invalid slice name: %s", slice)
|
|
}
|
|
|
|
// Path-separators are not allowed.
|
|
if strings.Contains(slice, "/") {
|
|
return "", fmt.Errorf("invalid slice name: %s", slice)
|
|
}
|
|
|
|
var path, prefix string
|
|
sliceName := strings.TrimSuffix(slice, suffix)
|
|
// if input was -.slice, we should just return root now
|
|
if sliceName == "-" {
|
|
return "/", nil
|
|
}
|
|
for _, component := range strings.Split(sliceName, "-") {
|
|
// test--a.slice isn't permitted, nor is -test.slice.
|
|
if component == "" {
|
|
return "", fmt.Errorf("invalid slice name: %s", slice)
|
|
}
|
|
|
|
// Append the component to the path and to the prefix.
|
|
path += "/" + prefix + component + suffix
|
|
prefix += component + "-"
|
|
}
|
|
return path, nil
|
|
}
|
|
|
|
// getDbusConnection lazy initializes systemd dbus connection
|
|
// and returns it
|
|
func getDbusConnection(rootless bool) (*systemdDbus.Conn, error) {
|
|
connOnce.Do(func() {
|
|
if rootless {
|
|
connDbus, connErr = NewUserSystemdDbus()
|
|
} else {
|
|
connDbus, connErr = systemdDbus.New()
|
|
}
|
|
})
|
|
return connDbus, connErr
|
|
}
|
|
|
|
func newProp(name string, units interface{}) systemdDbus.Property {
|
|
return systemdDbus.Property{
|
|
Name: name,
|
|
Value: dbus.MakeVariant(units),
|
|
}
|
|
}
|
|
|
|
func getUnitName(c *configs.Cgroup) string {
|
|
// by default, we create a scope unless the user explicitly asks for a slice.
|
|
if !strings.HasSuffix(c.Name, ".slice") {
|
|
return fmt.Sprintf("%s-%s.scope", c.ScopePrefix, c.Name)
|
|
}
|
|
return c.Name
|
|
}
|
|
|
|
// isUnitExists returns true if the error is that a systemd unit already exists.
|
|
func isUnitExists(err error) bool {
|
|
if err != nil {
|
|
if dbusError, ok := err.(dbus.Error); ok {
|
|
return strings.Contains(dbusError.Name, "org.freedesktop.systemd1.UnitExists")
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func startUnit(dbusConnection *systemdDbus.Conn, unitName string, properties []systemdDbus.Property) error {
|
|
statusChan := make(chan string, 1)
|
|
if _, err := dbusConnection.StartTransientUnit(unitName, "replace", properties, statusChan); err == nil {
|
|
select {
|
|
case s := <-statusChan:
|
|
close(statusChan)
|
|
// Please refer to https://godoc.org/github.com/coreos/go-systemd/dbus#Conn.StartUnit
|
|
if s != "done" {
|
|
dbusConnection.ResetFailedUnit(unitName)
|
|
return errors.Errorf("error creating systemd unit `%s`: got `%s`", unitName, s)
|
|
}
|
|
case <-time.After(time.Second):
|
|
logrus.Warnf("Timed out while waiting for StartTransientUnit(%s) completion signal from dbus. Continuing...", unitName)
|
|
}
|
|
} else if !isUnitExists(err) {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func stopUnit(dbusConnection *systemdDbus.Conn, unitName string) error {
|
|
statusChan := make(chan string, 1)
|
|
if _, err := dbusConnection.StopUnit(unitName, "replace", statusChan); err == nil {
|
|
select {
|
|
case s := <-statusChan:
|
|
close(statusChan)
|
|
// Please refer to https://godoc.org/github.com/coreos/go-systemd/dbus#Conn.StartUnit
|
|
if s != "done" {
|
|
logrus.Warnf("error removing unit `%s`: got `%s`. Continuing...", unitName, s)
|
|
}
|
|
case <-time.After(time.Second):
|
|
logrus.Warnf("Timed out while waiting for StopUnit(%s) completion signal from dbus. Continuing...", unitName)
|
|
}
|
|
}
|
|
return nil
|
|
}
|