mirror of
https://github.com/containers/podman.git
synced 2025-12-13 18:37:36 +08:00
simplify the rootless implementation to use a single user namespace for all the running containers. This makes the rootless implementation behave more like root Podman, where each container is created in the host environment. There are multiple advantages to it: 1) much simpler implementation as there is only one namespace to join. 2) we can join namespaces owned by different containers. 3) commands like ps won't be limited to what container they can access as previously we either had access to the storage from a new namespace or access to /proc when running from the host. 4) rootless varlink works. 5) there are only two ways to enter in a namespace, either by creating a new one if no containers are running or joining the existing one from any container. Containers created by older Podman versions must be restarted. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
129 lines
3.9 KiB
Go
129 lines
3.9 KiB
Go
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/parse"
|
|
"github.com/containers/libpod/libpod"
|
|
"github.com/pkg/errors"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
execCommand cliconfig.ExecValues
|
|
|
|
execDescription = `Execute the specified command inside a running container.
|
|
`
|
|
_execCommand = &cobra.Command{
|
|
Use: "exec [flags] CONTAINER [COMMAND [ARG...]]",
|
|
Short: "Run a process in a running container",
|
|
Long: execDescription,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
execCommand.InputArgs = args
|
|
execCommand.GlobalFlags = MainGlobalOpts
|
|
return execCmd(&execCommand)
|
|
},
|
|
Example: `podman exec -it ctrID ls
|
|
podman exec -it -w /tmp myCtr pwd
|
|
podman exec --user root ctrID ls`,
|
|
}
|
|
)
|
|
|
|
func init() {
|
|
execCommand.Command = _execCommand
|
|
execCommand.SetHelpTemplate(HelpTemplate())
|
|
execCommand.SetUsageTemplate(UsageTemplate())
|
|
flags := execCommand.Flags()
|
|
flags.SetInterspersed(false)
|
|
flags.StringArrayVarP(&execCommand.Env, "env", "e", []string{}, "Set environment variables")
|
|
flags.BoolVarP(&execCommand.Interfactive, "interactive", "i", false, "Not supported. All exec commands are interactive by default")
|
|
flags.BoolVarP(&execCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
|
|
flags.BoolVar(&execCommand.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false")
|
|
flags.BoolVarP(&execCommand.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false")
|
|
flags.StringVarP(&execCommand.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command")
|
|
|
|
flags.IntVar(&execCommand.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container")
|
|
flags.StringVarP(&execCommand.Workdir, "workdir", "w", "", "Working directory inside the container")
|
|
markFlagHiddenForRemoteClient("latest", flags)
|
|
}
|
|
|
|
func execCmd(c *cliconfig.ExecValues) error {
|
|
args := c.InputArgs
|
|
var ctr *libpod.Container
|
|
var err error
|
|
argStart := 1
|
|
if len(args) < 1 && !c.Latest {
|
|
return errors.Errorf("you must provide one container name or id")
|
|
}
|
|
if len(args) < 2 && !c.Latest {
|
|
return errors.Errorf("you must provide a command to exec")
|
|
}
|
|
if c.Latest {
|
|
argStart = 0
|
|
}
|
|
cmd := args[argStart:]
|
|
runtime, err := libpodruntime.GetRuntime(&c.PodmanCommand)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error creating libpod runtime")
|
|
}
|
|
defer runtime.Shutdown(false)
|
|
|
|
if c.Latest {
|
|
ctr, err = runtime.GetLatestContainer()
|
|
} else {
|
|
ctr, err = runtime.LookupContainer(args[0])
|
|
}
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to exec into %s", args[0])
|
|
}
|
|
|
|
if c.PreserveFDs > 0 {
|
|
entries, err := ioutil.ReadDir("/proc/self/fd")
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to read /proc/self/fd")
|
|
}
|
|
m := make(map[int]bool)
|
|
for _, e := range entries {
|
|
i, err := strconv.Atoi(e.Name())
|
|
if err != nil {
|
|
if err != nil {
|
|
return errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name())
|
|
}
|
|
}
|
|
m[i] = true
|
|
}
|
|
for i := 3; i < 3+c.PreserveFDs; i++ {
|
|
if _, found := m[i]; !found {
|
|
return errors.New("invalid --preserve-fds=N specified. Not enough FDs available")
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// ENVIRONMENT VARIABLES
|
|
env := map[string]string{}
|
|
|
|
if err := parse.ReadKVStrings(env, []string{}, c.Env); err != nil {
|
|
return errors.Wrapf(err, "unable to process environment variables")
|
|
}
|
|
envs := []string{}
|
|
for k, v := range env {
|
|
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
|
|
}
|
|
|
|
streams := new(libpod.AttachStreams)
|
|
streams.OutputStream = os.Stdout
|
|
streams.ErrorStream = os.Stderr
|
|
streams.InputStream = os.Stdin
|
|
streams.AttachOutput = true
|
|
streams.AttachError = true
|
|
streams.AttachInput = true
|
|
|
|
return ctr.Exec(c.Tty, c.Privileged, envs, cmd, c.User, c.Workdir, streams, c.PreserveFDs)
|
|
}
|