Files
podman/vendor/github.com/containers/common/pkg/systemd/systemd_linux.go
Urvashi Mohnani 7c8c945496 Vendor in latest c/common
Pull in updates made to the filters code for
images. Filters now perform an AND operation
except for th reference filter which does an
OR operation for positive case but an AND operation
for negative cases.

Signed-off-by: Urvashi Mohnani <umohnani@redhat.com>
2024-01-25 11:10:41 -05:00

152 lines
4.2 KiB
Go

package systemd
import (
"context"
"crypto/rand"
"fmt"
"os"
"strconv"
"sync"
"github.com/containers/common/pkg/cgroups"
"github.com/containers/storage/pkg/unshare"
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
"github.com/godbus/dbus/v5"
"github.com/sirupsen/logrus"
)
var (
runsOnSystemdOnce sync.Once
runsOnSystemd bool
)
// RunsOnSystemd returns whether the system is using systemd
func RunsOnSystemd() bool {
runsOnSystemdOnce.Do(func() {
// per sd_booted(3), check for this dir
fd, err := os.Stat("/run/systemd/system")
runsOnSystemd = err == nil && fd.IsDir()
})
return runsOnSystemd
}
func moveProcessPIDFileToScope(pidPath, slice, scope string) error {
data, err := os.ReadFile(pidPath)
if err != nil {
// do not raise an error if the file doesn't exist
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("cannot read pid file: %w", err)
}
pid, err := strconv.ParseUint(string(data), 10, 0)
if err != nil {
return fmt.Errorf("cannot parse pid file %s: %w", pidPath, err)
}
return moveProcessToScope(int(pid), slice, scope)
}
func moveProcessToScope(pid int, slice, scope string) error {
err := RunUnderSystemdScope(pid, slice, scope)
// If the PID is not valid anymore, do not return an error.
if dbusErr, ok := err.(dbus.Error); ok {
if dbusErr.Name == "org.freedesktop.DBus.Error.UnixProcessIdUnknown" {
return nil
}
}
return err
}
// MoveRootlessNetnsSlirpProcessToUserSlice moves the slirp4netns process for the rootless netns
// into a different scope so that systemd does not kill it with a container.
func MoveRootlessNetnsSlirpProcessToUserSlice(pid int) error {
randBytes := make([]byte, 4)
_, err := rand.Read(randBytes)
if err != nil {
return err
}
return moveProcessToScope(pid, "user.slice", fmt.Sprintf("rootless-netns-%x.scope", randBytes))
}
// MovePauseProcessToScope moves the pause process used for rootless mode to keep the namespaces alive to
// a separate scope.
func MovePauseProcessToScope(pausePidPath string) {
var err error
for i := 0; i < 10; i++ {
randBytes := make([]byte, 4)
_, err = rand.Read(randBytes)
if err != nil {
logrus.Errorf("failed to read random bytes: %v", err)
continue
}
err = moveProcessPIDFileToScope(pausePidPath, "user.slice", fmt.Sprintf("podman-pause-%x.scope", randBytes))
if err == nil {
return
}
}
if err != nil {
unified, err2 := cgroups.IsCgroup2UnifiedMode()
if err2 != nil {
logrus.Warnf("Failed to detect if running with cgroup unified: %v", err)
}
if RunsOnSystemd() && unified {
logrus.Warnf("Failed to add pause process to systemd sandbox cgroup: %v", err)
} else {
logrus.Debugf("Failed to add pause process to systemd sandbox cgroup: %v", err)
}
}
}
// RunUnderSystemdScope adds the specified pid to a systemd scope
func RunUnderSystemdScope(pid int, slice string, unitName string) error {
var properties []systemdDbus.Property
var conn *systemdDbus.Conn
var err error
if unshare.GetRootlessUID() != 0 {
conn, err = cgroups.UserConnection(unshare.GetRootlessUID())
if err != nil {
return err
}
} else {
conn, err = systemdDbus.NewWithContext(context.Background())
if err != nil {
return err
}
}
defer conn.Close()
properties = append(properties, systemdDbus.PropSlice(slice))
properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
properties = append(properties, newProp("Delegate", true))
properties = append(properties, newProp("DefaultDependencies", false))
ch := make(chan string)
_, err = conn.StartTransientUnitContext(context.Background(), unitName, "replace", properties, ch)
if err != nil {
// On errors check if the cgroup already exists, if it does move the process there
if props, err := conn.GetUnitTypePropertiesContext(context.Background(), unitName, "Scope"); err == nil {
if cgroup, ok := props["ControlGroup"].(string); ok && cgroup != "" {
if err := cgroups.MoveUnderCgroup(cgroup, "", []uint32{uint32(pid)}); err == nil {
return nil
}
// On errors return the original error message we got from StartTransientUnit.
}
}
return err
}
// Block until job is started
<-ch
return nil
}
func newProp(name string, units any) systemdDbus.Property {
return systemdDbus.Property{
Name: name,
Value: dbus.MakeVariant(units),
}
}