diff options
-rw-r--r-- | cmd/podman/cliconfig/config.go | 1 | ||||
-rw-r--r-- | cmd/podman/libpodruntime/runtime.go | 3 | ||||
-rw-r--r-- | cmd/podman/main.go | 30 | ||||
-rw-r--r-- | cmd/podman/pod.go | 47 | ||||
-rw-r--r-- | cmd/podman/pod_rm.go | 14 | ||||
-rw-r--r-- | cmd/podman/pod_stats.go | 5 | ||||
-rw-r--r-- | cmd/podman/pod_stop.go | 14 | ||||
-rw-r--r-- | cmd/podman/rm.go | 79 | ||||
-rw-r--r-- | docs/libpod.conf.5.md | 4 | ||||
-rw-r--r-- | docs/podman.1.md | 3 | ||||
-rw-r--r-- | libpod/container_internal.go | 2 | ||||
-rw-r--r-- | libpod/networking_linux.go | 13 | ||||
-rw-r--r-- | libpod/options.go | 14 | ||||
-rw-r--r-- | libpod/runtime.go | 2 | ||||
-rw-r--r-- | libpod/runtime_img.go | 29 | ||||
-rw-r--r-- | pkg/adapter/runtime.go | 40 | ||||
-rw-r--r-- | pkg/adapter/runtime_remote.go | 7 | ||||
-rw-r--r-- | pkg/rootless/rootless.go | 9 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 37 | ||||
-rw-r--r-- | pkg/rootless/rootless_unsupported.go | 33 | ||||
-rw-r--r-- | pkg/varlinkapi/system.go | 1 |
21 files changed, 362 insertions, 25 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 7945cb6cb..d58964489 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -25,6 +25,7 @@ type MainFlags struct { StorageOpts []string Syslog bool Trace bool + NetworkCmdPath string Config string CpuProfile string diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index 2b96f0c20..3faea493c 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -86,6 +86,9 @@ func getRuntime(c *cliconfig.PodmanCommand, renumber bool) (*libpod.Runtime, err if c.Flags().Changed("tmpdir") { options = append(options, libpod.WithTmpDir(c.GlobalFlags.TmpDir)) } + if c.Flags().Changed("network-cmd-path") { + options = append(options, libpod.WithNetworkCmdPath(c.GlobalFlags.NetworkCmdPath)) + } if c.Flags().Changed("cgroup-manager") { options = append(options, libpod.WithCgroupManager(c.GlobalFlags.CGroupManager)) diff --git a/cmd/podman/main.go b/cmd/podman/main.go index 7d4b650a9..bbeb72397 100644 --- a/cmd/podman/main.go +++ b/cmd/podman/main.go @@ -65,18 +65,23 @@ var cmdsNotRequiringRootless = map[*cobra.Command]bool{ _exportCommand: true, //// `info` must be executed in an user namespace. //// If this change, please also update libpod.refreshRootless() - _loginCommand: true, - _logoutCommand: true, - _mountCommand: true, - _killCommand: true, - _pauseCommand: true, - _restartCommand: true, - _runCommand: true, - _unpauseCommand: true, - _searchCommand: true, - _statsCommand: true, - _stopCommand: true, - _topCommand: true, + _loginCommand: true, + _logoutCommand: true, + _mountCommand: true, + _killCommand: true, + _pauseCommand: true, + _podRmCommand: true, + _podKillCommand: true, + _podStatsCommand: true, + _podStopCommand: true, + _restartCommand: true, + _rmCommand: true, + _runCommand: true, + _unpauseCommand: true, + _searchCommand: true, + _statsCommand: true, + _stopCommand: true, + _topCommand: true, } var rootCmd = &cobra.Command{ @@ -104,6 +109,7 @@ func init() { rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Config, "config", "", "Path of a libpod config file detailing container server configuration options") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.ConmonPath, "conmon", "", "Path of the conmon binary") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.NetworkCmdPath, "network-cmd-path", "", "Path to the command for configuring the network") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.CniConfigDir, "cni-config-dir", "", "Path of the configuration directory for CNI networks") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.DefaultMountsFile, "default-mounts-file", "", "Path to default mounts file") rootCmd.PersistentFlags().MarkHidden("defaults-mount-file") diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go index 2d9bca21d..9a9c7a702 100644 --- a/cmd/podman/pod.go +++ b/cmd/podman/pod.go @@ -1,7 +1,12 @@ package main import ( + "os" + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -34,6 +39,48 @@ var podSubCommands = []*cobra.Command{ _podUnpauseCommand, } +func joinPodNS(runtime *adapter.LocalRuntime, all, latest bool, inputArgs []string) ([]string, bool, bool, error) { + if rootless.IsRootless() { + if os.Geteuid() == 0 { + return []string{rootless.Argument()}, false, false, nil + } else { + var err error + var pods []*adapter.Pod + if all { + pods, err = runtime.GetAllPods() + if err != nil { + return nil, false, false, errors.Wrapf(err, "unable to get pods") + } + } else if latest { + pod, err := runtime.GetLatestPod() + if err != nil { + return nil, false, false, errors.Wrapf(err, "unable to get latest pod") + } + pods = append(pods, pod) + } else { + for _, i := range inputArgs { + pod, err := runtime.LookupPod(i) + if err != nil { + return nil, false, false, errors.Wrapf(err, "unable to lookup pod %s", i) + } + pods = append(pods, pod) + } + } + for _, p := range pods { + _, ret, err := runtime.JoinOrCreateRootlessPod(p) + if err != nil { + return nil, false, false, err + } + if ret != 0 { + os.Exit(ret) + } + } + os.Exit(0) + } + } + return inputArgs, all, latest, nil +} + func init() { podCommand.AddCommand(podSubCommands...) podCommand.SetHelpTemplate(HelpTemplate()) diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go index a40992818..735676f8a 100644 --- a/cmd/podman/pod_rm.go +++ b/cmd/podman/pod_rm.go @@ -2,9 +2,11 @@ package main import ( "fmt" + "os" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -46,11 +48,23 @@ func init() { // podRmCmd deletes pods func podRmCmd(c *cliconfig.PodRmValues) error { + if os.Geteuid() != 0 { + rootless.SetSkipStorageSetup(true) + } runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) + + if rootless.IsRootless() { + var err error + c.InputArgs, c.All, c.Latest, err = joinPodNS(runtime, c.All, c.Latest, c.InputArgs) + if err != nil { + return err + } + } + podRmIds, podRmErrors := runtime.RemovePods(getContext(), c) for _, p := range podRmIds { fmt.Println(p) diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go index 7dbd84525..5c30e0595 100644 --- a/cmd/podman/pod_stats.go +++ b/cmd/podman/pod_stats.go @@ -53,6 +53,11 @@ func init() { } func podStatsCmd(c *cliconfig.PodStatsValues) error { + + if os.Geteuid() != 0 { + return errors.New("stats is not supported in rootless mode") + } + format := c.Format all := c.All latest := c.Latest diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go index f1b0ac51f..754a3a7db 100644 --- a/cmd/podman/pod_stop.go +++ b/cmd/podman/pod_stop.go @@ -2,9 +2,11 @@ package main import ( "fmt" + "os" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -46,12 +48,24 @@ func init() { } func podStopCmd(c *cliconfig.PodStopValues) error { + if os.Geteuid() != 0 { + rootless.SetSkipStorageSetup(true) + } + runtime, err := adapter.GetRuntime(&c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } defer runtime.Shutdown(false) + if rootless.IsRootless() { + var err error + c.InputArgs, c.All, c.Latest, err = joinPodNS(runtime, c.All, c.Latest, c.InputArgs) + if err != nil { + return err + } + } + podStopIds, podStopErrors := runtime.StopPods(getContext(), c) for _, p := range podStopIds { fmt.Println(p) diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 4230bb396..56aaae9eb 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -2,12 +2,16 @@ package main import ( "fmt" + "io/ioutil" + "os" + "strconv" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -48,11 +52,39 @@ func init() { markFlagHiddenForRemoteClient("latest", flags) } +func joinContainerOrCreateRootlessUserNS(runtime *libpod.Runtime, ctr *libpod.Container) (bool, int, error) { + if os.Geteuid() == 0 { + return false, 0, nil + } + s, err := ctr.State() + if err != nil { + return false, -1, err + } + opts := rootless.Opts{ + Argument: ctr.ID(), + } + if s == libpod.ContainerStateRunning || s == libpod.ContainerStatePaused { + data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) + if err != nil { + return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile) + } + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + return false, -1, errors.Wrapf(err, "cannot parse PID %q", data) + } + return rootless.JoinDirectUserAndMountNSWithOpts(uint(conmonPid), &opts) + } + return rootless.BecomeRootInUserNSWithOpts(&opts) +} + // saveCmd saves the image to either docker-archive or oci func rmCmd(c *cliconfig.RmValues) error { var ( deleteFuncs []shared.ParallelWorkerInput ) + if os.Geteuid() != 0 { + rootless.SetSkipStorageSetup(true) + } ctx := getContext() runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand) @@ -61,6 +93,53 @@ func rmCmd(c *cliconfig.RmValues) error { } defer runtime.Shutdown(false) + if rootless.IsRootless() { + // When running in rootless mode we cannot manage different containers and + // user namespaces from the same context, so be sure to re-exec once for each + // container we are dealing with. + // What we do is to first collect all the containers we want to delete, then + // we re-exec in each of the container namespaces and from there remove the single + // container. + var container *libpod.Container + if os.Geteuid() == 0 { + // We are in the namespace, override InputArgs with the single + // argument that was passed down to us. + c.All = false + c.Latest = false + c.InputArgs = []string{rootless.Argument()} + } else { + var containers []*libpod.Container + if c.All { + containers, err = runtime.GetContainers() + } else if c.Latest { + container, err = runtime.GetLatestContainer() + if err != nil { + return errors.Wrapf(err, "unable to get latest pod") + } + containers = append(containers, container) + } else { + for _, c := range c.InputArgs { + container, err = runtime.LookupContainer(c) + if err != nil { + return err + } + containers = append(containers, container) + } + } + // Now we really delete the containers. + for _, c := range containers { + _, ret, err := joinContainerOrCreateRootlessUserNS(runtime, c) + if err != nil { + return err + } + if ret != 0 { + os.Exit(ret) + } + } + os.Exit(0) + } + } + failureCnt := 0 delContainers, err := getAllOrLatestContainers(&c.PodmanCommand, runtime, -1, "all") if err != nil { diff --git a/docs/libpod.conf.5.md b/docs/libpod.conf.5.md index 9a19e1224..777edeacb 100644 --- a/docs/libpod.conf.5.md +++ b/docs/libpod.conf.5.md @@ -91,6 +91,10 @@ libpod to manage containers. Directory where named volumes will be created in using the default volume driver. By default this will be configured relative to where containers/storage stores containers. +**network_cmd_path**="" + Path to the command binary to use for setting up a network. It is currently only used for setting up + a slirp4netns network. If "" is used then the binary is looked up using the $PATH environment variable. + ## FILES `/usr/share/containers/libpod.conf`, default libpod configuration path diff --git a/docs/podman.1.md b/docs/podman.1.md index 5c930995c..0182690d0 100644 --- a/docs/podman.1.md +++ b/docs/podman.1.md @@ -72,6 +72,9 @@ Default state dir is configured in /etc/containers/storage.conf. Name of the OCI runtime as specified in libpod.conf or absolute path to the OCI compatible binary used to run containers. +**--network-cmd-path**=**path** +Path to the command binary to use for setting up a network. It is currently only used for setting up a slirp4netns network. If "" is used then the binary is looked up using the $PATH environment variable. + **--storage-driver**=**value** Storage driver. The default storage driver for UID 0 is configured in /etc/containers/storage.conf (`$HOME/.config/containers/storage.conf` in rootless mode), and is *vfs* for non-root users when *fuse-overlayfs* is not available. The `STORAGE_DRIVER` environment variable overrides the default. The --storage-driver specified driver overrides all. diff --git a/libpod/container_internal.go b/libpod/container_internal.go index d86062e38..00e6786f9 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1086,7 +1086,7 @@ func (c *Container) cleanupStorage() error { // error // We still want to be able to kick the container out of the // state - if err == storage.ErrNotAContainer || err == storage.ErrContainerUnknown { + if errors.Cause(err) == storage.ErrNotAContainer || errors.Cause(err) == storage.ErrContainerUnknown { logrus.Errorf("Storage for container %s has been removed", c.ID()) return nil } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index f9caf26d1..80d7d8213 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -139,10 +139,15 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) { defer ctr.rootlessSlirpSyncR.Close() defer ctr.rootlessSlirpSyncW.Close() - path, err := exec.LookPath("slirp4netns") - if err != nil { - logrus.Errorf("could not find slirp4netns, the network namespace won't be configured: %v", err) - return nil + path := r.config.NetworkCmdPath + + if path == "" { + var err error + path, err = exec.LookPath("slirp4netns") + if err != nil { + logrus.Errorf("could not find slirp4netns, the network namespace won't be configured: %v", err) + return nil + } } syncR, syncW, err := os.Pipe() diff --git a/libpod/options.go b/libpod/options.go index 5ad2824d9..64b425c57 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -193,6 +193,20 @@ func WithConmonEnv(environment []string) RuntimeOption { } } +// WithNetworkCmdPath specifies the path to the slirp4netns binary which manages the +// runtime. +func WithNetworkCmdPath(path string) RuntimeOption { + return func(rt *Runtime) error { + if rt.valid { + return ErrRuntimeFinalized + } + + rt.config.NetworkCmdPath = path + + return nil + } +} + // WithCgroupManager specifies the manager implementation name which is used to // handle cgroups for containers. // Current valid values are "cgroupfs" and "systemd". diff --git a/libpod/runtime.go b/libpod/runtime.go index 9667abfe6..535b6f41b 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -217,6 +217,8 @@ type RuntimeConfig struct { EnablePortReservation bool `toml:"enable_port_reservation"` // EnableLabeling indicates wether libpod will support container labeling EnableLabeling bool `toml:"label"` + // NetworkCmdPath is the path to the slirp4netns binary + NetworkCmdPath string `toml:"network_cmd_path"` // NumLocks is the number of locks to make available for containers and // pods. diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go index 451c2ebe7..02f925fc6 100644 --- a/libpod/runtime_img.go +++ b/libpod/runtime_img.go @@ -14,6 +14,7 @@ import ( "github.com/containers/libpod/pkg/util" "github.com/containers/storage" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/containers/image/directory" dockerarchive "github.com/containers/image/docker/archive" @@ -183,6 +184,15 @@ func (r *Runtime) Import(ctx context.Context, source string, reference string, c defer os.Remove(file) source = file } + // if it's stdin, buffer it, too + if source == "-" { + file, err := downloadFromFile(os.Stdin) + if err != nil { + return "", err + } + defer os.Remove(file) + source = file + } newImage, err := r.imageRuntime.Import(ctx, source, reference, writer, image.SigningOptions{}, config) if err != nil { @@ -216,6 +226,25 @@ func downloadFromURL(source string) (string, error) { return outFile.Name(), nil } +// donwloadFromFile reads all of the content from the reader and temporarily +// saves in it /var/tmp/importxyz, which is deleted after the image is imported +func downloadFromFile(reader *os.File) (string, error) { + outFile, err := ioutil.TempFile("/var/tmp", "import") + if err != nil { + return "", errors.Wrap(err, "error creating file") + } + defer outFile.Close() + + logrus.Debugf("saving %s to %s", reader.Name(), outFile.Name()) + + _, err = io.Copy(outFile, reader) + if err != nil { + return "", errors.Wrapf(err, "error saving %s to %s", reader.Name(), outFile.Name()) + } + + return outFile.Name(), nil +} + // LoadImage loads a container image into local storage func (r *Runtime) LoadImage(ctx context.Context, name, inputFile string, writer io.Writer, signaturePolicy string) (string, error) { var newImages []*image.Image diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 732b89530..482b6119a 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -337,3 +337,43 @@ func IsImageNotFound(err error) bool { func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.HealthCheckStatus, error) { return r.Runtime.HealthCheck(c.InputArgs[0]) } + +// JoinOrCreateRootlessPod joins the specified pod if it is running or it creates a new user namespace +// if the pod is stopped +func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) { + if os.Geteuid() == 0 { + return false, 0, nil + } + opts := rootless.Opts{ + Argument: pod.ID(), + } + + inspect, err := pod.Inspect() + if err != nil { + return false, 0, err + } + for _, ctr := range inspect.Containers { + prevCtr, err := r.LookupContainer(ctr.ID) + if err != nil { + return false, -1, err + } + s, err := prevCtr.State() + if err != nil { + return false, -1, err + } + if s != libpod.ContainerStateRunning && s != libpod.ContainerStatePaused { + continue + } + data, err := ioutil.ReadFile(prevCtr.Config().ConmonPidFile) + if err != nil { + return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", prevCtr.Config().ConmonPidFile) + } + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + return false, -1, errors.Wrapf(err, "cannot parse PID %q", data) + } + return rootless.JoinDirectUserAndMountNSWithOpts(uint(conmonPid), &opts) + } + + return rootless.BecomeRootInUserNSWithOpts(&opts) +} diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 10c25c3f3..9ca4e245f 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -751,3 +751,10 @@ func IsImageNotFound(err error) bool { func (r *LocalRuntime) HealthCheck(c *cliconfig.HealthCheckValues) (libpod.HealthCheckStatus, error) { return -1, libpod.ErrNotImplemented } + +// JoinOrCreateRootlessPod joins the specified pod if it is running or it creates a new user namespace +// if the pod is stopped +func (r *LocalRuntime) JoinOrCreateRootlessPod(pod *Pod) (bool, int, error) { + // Nothing to do in the remote case + return true, 0, nil +} diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go new file mode 100644 index 000000000..a531e43ce --- /dev/null +++ b/pkg/rootless/rootless.go @@ -0,0 +1,9 @@ +package rootless + +// Opts allows to customize how re-execing to a rootless process is done +type Opts struct { + // Argument overrides the arguments on the command line + // for the re-execed process. The process in the namespace + // must use rootless.Argument() to read its value. + Argument string +} diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index d43151e8e..b2677f7d9 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -60,6 +60,11 @@ func SkipStorageSetup() bool { return skipStorageSetup } +// Argument returns the argument that was set for the rootless session. +func Argument() string { + return os.Getenv("_LIBPOD_ROOTLESS_ARG") +} + // GetRootlessUID returns the UID of the user in the parent userNS func GetRootlessUID() int { uidEnv := os.Getenv("_LIBPOD_ROOTLESS_UID") @@ -134,8 +139,16 @@ func JoinNS(pid uint, preserveFDs int) (bool, int, error) { // JoinDirectUserAndMountNS 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 -// the conmon process. +// the conmon process. It is a convenience function for JoinDirectUserAndMountNSWithOpts +// with a default configuration. func JoinDirectUserAndMountNS(pid uint) (bool, int, error) { + return JoinDirectUserAndMountNSWithOpts(pid, nil) +} + +// JoinDirectUserAndMountNSWithOpts 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 the conmon process. +func JoinDirectUserAndMountNSWithOpts(pid uint, opts *Opts) (bool, int, error) { if os.Geteuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" { return false, -1, nil } @@ -152,6 +165,12 @@ func JoinDirectUserAndMountNS(pid uint) (bool, int, error) { } defer userNS.Close() + if opts != nil && opts.Argument != "" { + if err := os.Setenv("_LIBPOD_ROOTLESS_ARG", opts.Argument); err != nil { + return false, -1, err + } + } + pidC := C.reexec_userns_join(C.int(userNS.Fd()), C.int(mountNS.Fd())) if int(pidC) < 0 { return false, -1, errors.Errorf("cannot re-exec process") @@ -194,8 +213,16 @@ func JoinNSPath(path string) (bool, int, error) { // BecomeRootInUserNS re-exec podman in a new userNS. It returns whether podman was re-executed // into a new user namespace and the return code from the re-executed podman process. // If podman was re-executed the caller needs to propagate the error code returned by the child -// process. +// process. It is a convenience function for BecomeRootInUserNSWithOpts with a default configuration. func BecomeRootInUserNS() (bool, int, error) { + return BecomeRootInUserNSWithOpts(nil) +} + +// BecomeRootInUserNSWithOpts re-exec podman in a new userNS. It returns whether podman was +// re-execute into a new user namespace and the return code from the re-executed podman process. +// If podman was re-executed the caller needs to propagate the error code returned by the child +// process. +func BecomeRootInUserNSWithOpts(opts *Opts) (bool, int, error) { if os.Geteuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" { if os.Getenv("_LIBPOD_USERNS_CONFIGURED") == "init" { return false, 0, runInUser() @@ -214,6 +241,12 @@ func BecomeRootInUserNS() (bool, int, error) { defer w.Close() defer w.Write([]byte("0")) + if opts != nil && opts.Argument != "" { + if err := os.Setenv("_LIBPOD_ROOTLESS_ARG", opts.Argument); err != nil { + return false, -1, err + } + } + pidC := C.reexec_in_user_namespace(C.int(r.Fd())) pid := int(pidC) if pid < 0 { diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go index 1823c023e..54e70961b 100644 --- a/pkg/rootless/rootless_unsupported.go +++ b/pkg/rootless/rootless_unsupported.go @@ -11,10 +11,18 @@ func IsRootless() bool { return false } +// BecomeRootInUserNS re-exec podman in a new userNS. It returns whether podman was re-executed +// into a new user namespace and the return code from the re-executed podman process. +// If podman was re-executed the caller needs to propagate the error code returned by the child +// process. It is a convenience function for BecomeRootInUserNSWithOpts with a default configuration. +func BecomeRootInUserNS() (bool, int, error) { + return false, -1, errors.New("this function is not supported on this os") +} + // BecomeRootInUserNS is a stub function that always returns false and an // error on unsupported OS's -func BecomeRootInUserNS() (bool, int, error) { - return false, -1, errors.New("this function is not supported on this os1") +func BecomeRootInUserNSWithOpts(opts *Opts) (bool, int, error) { + return false, -1, errors.New("this function is not supported on this os") } // GetRootlessUID returns the UID of the user in the parent userNS @@ -34,18 +42,31 @@ func SkipStorageSetup() bool { // JoinNS re-exec podman in a new userNS and join the user namespace of the specified // PID. func JoinNS(pid uint) (bool, int, error) { - return false, -1, errors.New("this function is not supported on this os2") + return false, -1, errors.New("this function is not supported on this os") } // JoinNSPath re-exec podman in a new userNS and join the owner user namespace of the // specified path. func JoinNSPath(path string) (bool, int, error) { - return false, -1, errors.New("this function is not supported on this os3") + return false, -1, errors.New("this function is not supported on this os") +} + +// JoinDirectUserAndMountNSWithOpts 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 the conmon process. +func JoinDirectUserAndMountNSWithOpts(pid uint, opts *Opts) (bool, int, error) { + return false, -1, errors.New("this function is not supported on this os") } // JoinDirectUserAndMountNS 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 -// the conmon process. +// the conmon process. It is a convenience function for JoinDirectUserAndMountNSWithOpts +// with a default configuration. func JoinDirectUserAndMountNS(pid uint) (bool, int, error) { - return false, -1, errors.New("this function is not supported on this os4") + return false, -1, errors.New("this function is not supported on this os") +} + +// Argument returns the argument that was set for the rootless session. +func Argument() string { + return "" } diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index 3f32615ec..816143e9f 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -52,6 +52,7 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { Mem_free: host["MemFree"].(int64), Mem_total: host["MemTotal"].(int64), Swap_free: host["SwapFree"].(int64), + Swap_total: host["SwapTotal"].(int64), Arch: host["arch"].(string), Cpus: int64(host["cpus"].(int)), Hostname: host["hostname"].(string), |