1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/daemon/execdriver/native/exec.go
Phil Estes 995386735c Add synchronization and closure to IO pipes in userns path
The execdriver pipes setup uses OS pipes with fds so that they can be
chown'ed to the remapped root user for proper access. Recent flakiness
in certain short-lived tests (usually via the "exec" path) reveals that
the copy routines are not completing before exit/tear-down.

This fix adds synchronization and proper closure such that these
routines exit successfully.

Docker-DCO-1.1-Signed-off-by: Phil Estes <estesp@linux.vnet.ibm.com> (github: estesp)
2016-02-26 13:47:34 -05:00

96 lines
2.5 KiB
Go

// +build linux
package native
import (
"fmt"
"os"
"os/exec"
"strings"
"sync"
"syscall"
"github.com/docker/docker/daemon/execdriver"
"github.com/opencontainers/runc/libcontainer"
// Blank import 'nsenter' so that init in that package will call c
// function 'nsexec()' to do 'setns' before Go runtime take over,
// it's used for join to exist ns like 'docker exec' command.
_ "github.com/opencontainers/runc/libcontainer/nsenter"
"github.com/opencontainers/runc/libcontainer/utils"
)
// Exec implements the exec driver Driver interface,
// it calls libcontainer APIs to execute a container.
func (d *Driver) Exec(c *execdriver.Command, processConfig *execdriver.ProcessConfig, pipes *execdriver.Pipes, hooks execdriver.Hooks) (int, error) {
active := d.activeContainers[c.ID]
if active == nil {
return -1, fmt.Errorf("No active container exists with ID %s", c.ID)
}
user := processConfig.User
if c.RemappedRoot.UID != 0 && user == "" {
//if user namespaces are enabled, set user explicitly so uid/gid is set to 0
//otherwise we end up with the overflow id and no permissions (65534)
user = "0"
}
p := &libcontainer.Process{
Args: append([]string{processConfig.Entrypoint}, processConfig.Arguments...),
Env: c.ProcessConfig.Env,
Cwd: c.WorkingDir,
User: user,
}
if processConfig.Privileged {
p.Capabilities = execdriver.GetAllCapabilities()
}
// add CAP_ prefix to all caps for new libcontainer update to match
// the spec format.
for i, s := range p.Capabilities {
if !strings.HasPrefix(s, "CAP_") {
p.Capabilities[i] = fmt.Sprintf("CAP_%s", s)
}
}
config := active.Config()
wg := sync.WaitGroup{}
writers, err := setupPipes(&config, processConfig, p, pipes, &wg)
if err != nil {
return -1, err
}
if err := active.Start(p); err != nil {
return -1, err
}
//close the write end of any opened pipes now that they are dup'ed into the container
for _, writer := range writers {
writer.Close()
}
if hooks.Start != nil {
pid, err := p.Pid()
if err != nil {
p.Signal(os.Kill)
p.Wait()
return -1, err
}
// A closed channel for OOM is returned here as it will be
// non-blocking and return the correct result when read.
chOOM := make(chan struct{})
close(chOOM)
hooks.Start(&c.ProcessConfig, pid, chOOM)
}
ps, err := p.Wait()
if err != nil {
exitErr, ok := err.(*exec.ExitError)
if !ok {
return -1, err
}
ps = exitErr.ProcessState
}
// wait for all IO goroutine copiers to finish
wg.Wait()
return utils.ExitStatus(ps.Sys().(syscall.WaitStatus)), nil
}