Skip to content

Commit

Permalink
exec: support --preserve-fds
Browse files Browse the repository at this point in the history
Allow to pass additional FDs to the process being executed.

Closes: containers#2372

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
  • Loading branch information
giuseppe committed Mar 2, 2019
1 parent 9adcda7 commit 0b34327
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 8 deletions.
1 change: 1 addition & 0 deletions cmd/podman/cliconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ type ExecValues struct {
User string
Latest bool
Workdir string
PreserveFDs int
}

type ImageExistsValues struct {
Expand Down
2 changes: 1 addition & 1 deletion cmd/podman/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -869,7 +869,7 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l
}
return false, -1, errors.Errorf("dependency container %s is not running", ctr.ID())
}
return rootless.JoinNS(uint(pid))
return rootless.JoinNS(uint(pid), 0)
}
}
return rootless.BecomeRootInUserNS()
Expand Down
30 changes: 28 additions & 2 deletions cmd/podman/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"fmt"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/spf13/cobra"
"io/ioutil"
"os"
"strconv"

"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
Expand Down Expand Up @@ -47,6 +49,7 @@ func init() {
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)
}
Expand Down Expand Up @@ -82,11 +85,34 @@ func execCmd(c *cliconfig.ExecValues) error {
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")
}
}

}

pid, err := ctr.PID()
if err != nil {
return err
}
became, ret, err := rootless.JoinNS(uint(pid))
became, ret, err := rootless.JoinNS(uint(pid), c.PreserveFDs)
if err != nil {
return err
}
Expand All @@ -113,5 +139,5 @@ func execCmd(c *cliconfig.ExecValues) error {
streams.AttachError = true
streams.AttachInput = true

return ctr.Exec(c.Tty, c.Privileged, envs, cmd, c.User, c.Workdir, streams)
return ctr.Exec(c.Tty, c.Privileged, envs, cmd, c.User, c.Workdir, streams, c.PreserveFDs)
}
2 changes: 1 addition & 1 deletion cmd/podman/top.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func topCmd(c *cliconfig.TopValues) error {
if err != nil {
return err
}
became, ret, err := rootless.JoinNS(uint(pid))
became, ret, err := rootless.JoinNS(uint(pid), 0)
if err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions docs/podman-exec.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ to run containers such as CRI-O, the last started container could be from eithe

The latest option is not supported on the remote client.

**--preserve-fds=N**

Pass down to the process N additional file descriptors (in addition to 0, 1, 2). The total FDs will be 3+N.

**--privileged**

Give the process extended Linux capabilities when running the command in container.
Expand Down
4 changes: 2 additions & 2 deletions libpod/container_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ func (c *Container) Kill(signal uint) error {

// Exec starts a new process inside the container
// TODO investigate allowing exec without attaching
func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir string, streams *AttachStreams) error {
func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir string, streams *AttachStreams, preserveFDs int) error {
var capList []string

locked := false
Expand Down Expand Up @@ -266,7 +266,7 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir

logrus.Debugf("Creating new exec session in container %s with session id %s", c.ID(), sessionID)

execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, hostUser, sessionID, streams)
execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, hostUser, sessionID, streams, preserveFDs)
if err != nil {
return errors.Wrapf(err, "error exec %s", c.ID())
}
Expand Down
13 changes: 12 additions & 1 deletion libpod/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ func (r *OCIRuntime) unpauseContainer(ctr *Container) error {
// TODO: Add --detach support
// TODO: Convert to use conmon
// TODO: add --pid-file and use that to generate exec session tracking
func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, cwd, user, sessionID string, streams *AttachStreams) (*exec.Cmd, error) {
func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, cwd, user, sessionID string, streams *AttachStreams, preserveFDs int) (*exec.Cmd, error) {
if len(cmd) == 0 {
return nil, errors.Wrapf(ErrInvalidArg, "must provide a command to execute")
}
Expand Down Expand Up @@ -770,6 +770,9 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty
args = append(args, "--user", user)
}

if preserveFDs > 0 {
args = append(args, fmt.Sprintf("--preserve-fds=%d", preserveFDs))
}
if c.config.Spec.Process.NoNewPrivileges {
args = append(args, "--no-new-privs")
}
Expand Down Expand Up @@ -806,6 +809,14 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty
return nil, errors.Wrapf(err, "cannot start container %s", c.ID())
}

if preserveFDs > 0 {
for fd := 3; fd < 3+preserveFDs; fd++ {
// These fds were passed down to the runtime. Close them
// and not interfere
os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)).Close()
}
}

return execCmd, nil
}

Expand Down
9 changes: 8 additions & 1 deletion pkg/rootless/rootless_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap)

// JoinNS re-exec podman in a new userNS and join the user namespace of the specified
// PID.
func JoinNS(pid uint) (bool, int, error) {
func JoinNS(pid uint, preserveFDs int) (bool, int, error) {
if os.Geteuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" {
return false, -1, nil
}
Expand All @@ -117,6 +117,13 @@ func JoinNS(pid uint) (bool, int, error) {
if int(pidC) < 0 {
return false, -1, errors.Errorf("cannot re-exec process")
}
if preserveFDs > 0 {
for fd := 3; fd < 3+preserveFDs; fd++ {
// These fds were passed down to the runtime. Close them
// and not interfere
os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)).Close()
}
}

ret := C.reexec_in_user_namespace_wait(pidC)
if ret < 0 {
Expand Down

0 comments on commit 0b34327

Please sign in to comment.