mirror of
https://github.com/containers/podman.git
synced 2025-09-26 00:06:04 +08:00
Merge pull request #3397 from giuseppe/fix-reboot
rootless: enable linger if /run/user/UID not exists
This commit is contained in:
@ -100,7 +100,7 @@ func initConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func before(cmd *cobra.Command, args []string) error {
|
func before(cmd *cobra.Command, args []string) error {
|
||||||
if err := libpod.SetXdgRuntimeDir(""); err != nil {
|
if err := libpod.SetXdgRuntimeDir(); err != nil {
|
||||||
logrus.Errorf(err.Error())
|
logrus.Errorf(err.Error())
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
is "github.com/containers/image/storage"
|
is "github.com/containers/image/storage"
|
||||||
@ -312,18 +313,39 @@ func defaultRuntimeConfig() (RuntimeConfig, error) {
|
|||||||
|
|
||||||
// SetXdgRuntimeDir ensures the XDG_RUNTIME_DIR env variable is set
|
// SetXdgRuntimeDir ensures the XDG_RUNTIME_DIR env variable is set
|
||||||
// containers/image uses XDG_RUNTIME_DIR to locate the auth file.
|
// containers/image uses XDG_RUNTIME_DIR to locate the auth file.
|
||||||
func SetXdgRuntimeDir(val string) error {
|
// It internally calls EnableLinger() so that the user's processes are not
|
||||||
|
// killed once the session is terminated. EnableLinger() also attempts to
|
||||||
|
// get the runtime directory when XDG_RUNTIME_DIR is not specified.
|
||||||
|
func SetXdgRuntimeDir() error {
|
||||||
if !rootless.IsRootless() {
|
if !rootless.IsRootless() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if val == "" {
|
|
||||||
|
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
|
||||||
|
|
||||||
|
runtimeDirLinger, err := rootless.EnableLinger()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error enabling user session")
|
||||||
|
}
|
||||||
|
if runtimeDir == "" && runtimeDirLinger != "" {
|
||||||
|
if _, err := os.Stat(runtimeDirLinger); err != nil && os.IsNotExist(err) {
|
||||||
|
chWait := make(chan error)
|
||||||
|
defer close(chWait)
|
||||||
|
if _, err := WaitForFile(runtimeDirLinger, chWait, time.Second*10); err != nil {
|
||||||
|
return errors.Wrapf(err, "waiting for directory '%s'", runtimeDirLinger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
runtimeDir = runtimeDirLinger
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtimeDir == "" {
|
||||||
var err error
|
var err error
|
||||||
val, err = util.GetRootlessRuntimeDir()
|
runtimeDir, err = util.GetRootlessRuntimeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := os.Setenv("XDG_RUNTIME_DIR", val); err != nil {
|
if err := os.Setenv("XDG_RUNTIME_DIR", runtimeDir); err != nil {
|
||||||
return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
|
return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -479,18 +501,6 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options ..
|
|||||||
runtime.config.SignaturePolicyPath = newPath
|
runtime.config.SignaturePolicyPath = newPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimeDir, err := util.GetRootlessRuntimeDir()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// containers/image uses XDG_RUNTIME_DIR to locate the auth file.
|
|
||||||
// So make sure the env variable is set.
|
|
||||||
if err := SetXdgRuntimeDir(runtimeDir); err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if userConfigPath != "" {
|
if userConfigPath != "" {
|
||||||
|
@ -9,14 +9,17 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
gosignal "os/signal"
|
gosignal "os/signal"
|
||||||
"os/user"
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/containers/storage/pkg/idtools"
|
"github.com/containers/storage/pkg/idtools"
|
||||||
"github.com/docker/docker/pkg/signal"
|
"github.com/docker/docker/pkg/signal"
|
||||||
|
"github.com/godbus/dbus"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -198,24 +201,90 @@ func getUserNSFirstChild(fd uintptr) (*os.File, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func enableLinger(pausePid string) {
|
// EnableLinger configures the system to not kill the user processes once the session
|
||||||
if pausePid == "" {
|
// terminates
|
||||||
return
|
func EnableLinger() (string, error) {
|
||||||
|
uid := fmt.Sprintf("%d", GetRootlessUID())
|
||||||
|
|
||||||
|
conn, err := dbus.SystemBus()
|
||||||
|
if err == nil {
|
||||||
|
defer conn.Close()
|
||||||
}
|
}
|
||||||
// If we are trying to write a pause pid file, make sure we can leave processes
|
|
||||||
// running longer than the user session.
|
lingerEnabled := false
|
||||||
err := exec.Command("loginctl", "enable-linger", fmt.Sprintf("%d", GetRootlessUID())).Run()
|
|
||||||
|
// If we have a D-BUS connection, attempt to read the LINGER property from it.
|
||||||
|
if conn != nil {
|
||||||
|
path := dbus.ObjectPath((fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid)))
|
||||||
|
ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.Linger")
|
||||||
|
if err == nil && ret.Value().(bool) {
|
||||||
|
lingerEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR")
|
||||||
|
lingerFile := ""
|
||||||
|
if xdgRuntimeDir != "" && !lingerEnabled {
|
||||||
|
lingerFile = filepath.Join(xdgRuntimeDir, "libpod/linger")
|
||||||
|
_, err := os.Stat(lingerFile)
|
||||||
|
if err == nil {
|
||||||
|
lingerEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lingerEnabled {
|
||||||
|
// First attempt with D-BUS, if it fails, then attempt with "loginctl enable-linger"
|
||||||
|
if conn != nil {
|
||||||
|
o := conn.Object("org.freedesktop.login1", "/org/freedesktop/login1")
|
||||||
|
ret := o.Call("org.freedesktop.login1.Manager.SetUserLinger", 0, uint32(GetRootlessUID()), true, true)
|
||||||
|
if ret.Err == nil {
|
||||||
|
lingerEnabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !lingerEnabled {
|
||||||
|
err := exec.Command("loginctl", "enable-linger", uid).Run()
|
||||||
|
if err == nil {
|
||||||
|
lingerEnabled = true
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("cannot run `loginctl enable-linger` for the current user: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lingerEnabled && lingerFile != "" {
|
||||||
|
f, err := os.Create(lingerFile)
|
||||||
|
if err == nil {
|
||||||
|
f.Close()
|
||||||
|
} else {
|
||||||
|
logrus.Debugf("could not create linger file: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lingerEnabled {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a D-BUS connection, attempt to read the RUNTIME PATH from it.
|
||||||
|
if conn != nil {
|
||||||
|
path := dbus.ObjectPath((fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid)))
|
||||||
|
ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.RuntimePath")
|
||||||
|
if err == nil {
|
||||||
|
return strings.Trim(ret.String(), "\"\n"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If XDG_RUNTIME_DIR is not set and the D-BUS call didn't work, try to get the runtime path with "loginctl"
|
||||||
|
output, err := exec.Command("loginctl", "-pRuntimePath", "show-user", uid).Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warnf("cannot run `loginctl enable-linger` for the current user: %v", err)
|
logrus.Debugf("could not get RuntimePath using loginctl: %v", err)
|
||||||
|
return "", nil
|
||||||
}
|
}
|
||||||
|
return strings.Trim(strings.Replace(string(output), "RuntimePath=", "", -1), "\"\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// joinUserAndMountNS re-exec podman in a new userNS and join the user and mount
|
// joinUserAndMountNS re-exec podman in a new userNS and join the user and mount
|
||||||
// namespace of the specified PID without looking up its parent. Useful to join directly
|
// namespace of the specified PID without looking up its parent. Useful to join directly
|
||||||
// the conmon process.
|
// the conmon process.
|
||||||
func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) {
|
func joinUserAndMountNS(pid uint, pausePid string) (bool, int, error) {
|
||||||
enableLinger(pausePid)
|
|
||||||
|
|
||||||
if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
|
if os.Geteuid() == 0 || os.Getenv("_CONTAINERS_USERNS_CONFIGURED") != "" {
|
||||||
return false, -1, nil
|
return false, -1, nil
|
||||||
}
|
}
|
||||||
@ -406,7 +475,6 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool,
|
|||||||
// If podman was re-executed the caller needs to propagate the error code returned by the child
|
// If podman was re-executed the caller needs to propagate the error code returned by the child
|
||||||
// process.
|
// process.
|
||||||
func BecomeRootInUserNS(pausePid string) (bool, int, error) {
|
func BecomeRootInUserNS(pausePid string) (bool, int, error) {
|
||||||
enableLinger(pausePid)
|
|
||||||
return becomeRootInUserNS(pausePid, "", nil)
|
return becomeRootInUserNS(pausePid, "", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,12 @@ func GetRootlessGID() int {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableLinger configures the system to not kill the user processes once the session
|
||||||
|
// terminates
|
||||||
|
func EnableLinger() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
// TryJoinFromFilePaths attempts to join the namespaces of the pid files in paths.
|
// TryJoinFromFilePaths attempts to join the namespaces of the pid files in paths.
|
||||||
// This is useful when there are already running containers and we
|
// This is useful when there are already running containers and we
|
||||||
// don't have a pause process yet. We can use the paths to the conmon
|
// don't have a pause process yet. We can use the paths to the conmon
|
||||||
|
Reference in New Issue
Block a user