Merge pull request #516 from crosbymichael/additional-fds
Append childpipe for adding addtional Fds to container
This commit is contained in:
commit
b271fcfec6
|
@ -16,6 +16,8 @@ import (
|
||||||
"github.com/docker/libcontainer/configs"
|
"github.com/docker/libcontainer/configs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const stdioFdCount = 3
|
||||||
|
|
||||||
type linuxContainer struct {
|
type linuxContainer struct {
|
||||||
id string
|
id string
|
||||||
root string
|
root string
|
||||||
|
@ -139,7 +141,8 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
|
||||||
if cmd.SysProcAttr == nil {
|
if cmd.SysProcAttr == nil {
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
}
|
}
|
||||||
cmd.ExtraFiles = []*os.File{childPipe}
|
cmd.ExtraFiles = append(p.ExtraFiles, childPipe)
|
||||||
|
cmd.Env = append(cmd.Env, fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
|
||||||
// NOTE: when running a container with no PID namespace and the parent process spawning the container is
|
// NOTE: when running a container with no PID namespace and the parent process spawning the container is
|
||||||
// PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason
|
// PID1 the pdeathsig is being delivered to the container's init process by the kernel for some reason
|
||||||
// even with the parent still running.
|
// even with the parent still running.
|
||||||
|
@ -178,11 +181,9 @@ func (c *linuxContainer) newSetnsProcess(p *Process, cmd *exec.Cmd, parentPipe,
|
||||||
fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()),
|
fmt.Sprintf("_LIBCONTAINER_INITPID=%d", c.initProcess.pid()),
|
||||||
"_LIBCONTAINER_INITTYPE=setns",
|
"_LIBCONTAINER_INITTYPE=setns",
|
||||||
)
|
)
|
||||||
|
|
||||||
if p.consolePath != "" {
|
if p.consolePath != "" {
|
||||||
cmd.Env = append(cmd.Env, "_LIBCONTAINER_CONSOLE_PATH="+p.consolePath)
|
cmd.Env = append(cmd.Env, "_LIBCONTAINER_CONSOLE_PATH="+p.consolePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: set on container for process management
|
// TODO: set on container for process management
|
||||||
return &setnsProcess{
|
return &setnsProcess{
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
|
@ -202,6 +203,7 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
|
||||||
Cwd: process.Cwd,
|
Cwd: process.Cwd,
|
||||||
Console: process.consolePath,
|
Console: process.consolePath,
|
||||||
Capabilities: process.Capabilities,
|
Capabilities: process.Capabilities,
|
||||||
|
PassedFilesCount: len(process.ExtraFiles),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
factory.go
10
factory.go
|
@ -33,14 +33,12 @@ type Factory interface {
|
||||||
Load(id string) (Container, error)
|
Load(id string) (Container, error)
|
||||||
|
|
||||||
// StartInitialization is an internal API to libcontainer used during the rexec of the
|
// StartInitialization is an internal API to libcontainer used during the rexec of the
|
||||||
// container. pipefd is the fd to the child end of the pipe used to syncronize the
|
// container.
|
||||||
// parent and child process providing state and configuration to the child process and
|
|
||||||
// returning any errors during the init of the container
|
|
||||||
//
|
//
|
||||||
// Errors:
|
// Errors:
|
||||||
// pipe connection error
|
// Pipe connection error
|
||||||
// system error
|
// System error
|
||||||
StartInitialization(pipefd uintptr) error
|
StartInitialization() error
|
||||||
|
|
||||||
// Type returns info string about factory type (e.g. lxc, libcontainer...)
|
// Type returns info string about factory type (e.g. lxc, libcontainer...)
|
||||||
Type() string
|
Type() string
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
|
@ -194,7 +195,11 @@ func (l *LinuxFactory) Type() string {
|
||||||
|
|
||||||
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
|
// StartInitialization loads a container by opening the pipe fd from the parent to read the configuration and state
|
||||||
// This is a low level implementation detail of the reexec and should not be consumed externally
|
// This is a low level implementation detail of the reexec and should not be consumed externally
|
||||||
func (l *LinuxFactory) StartInitialization(pipefd uintptr) (err error) {
|
func (l *LinuxFactory) StartInitialization() (err error) {
|
||||||
|
pipefd, err := strconv.Atoi(os.Getenv("_LIBCONTAINER_INITPIPE"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
pipe = os.NewFile(uintptr(pipefd), "pipe")
|
pipe = os.NewFile(uintptr(pipefd), "pipe")
|
||||||
it = initType(os.Getenv("_LIBCONTAINER_INITTYPE"))
|
it = initType(os.Getenv("_LIBCONTAINER_INITTYPE"))
|
||||||
|
|
|
@ -48,6 +48,7 @@ type initConfig struct {
|
||||||
Config *configs.Config `json:"config"`
|
Config *configs.Config `json:"config"`
|
||||||
Console string `json:"console"`
|
Console string `json:"console"`
|
||||||
Networks []*network `json:"network"`
|
Networks []*network `json:"network"`
|
||||||
|
PassedFilesCount int `json:"passed_files_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type initer interface {
|
type initer interface {
|
||||||
|
@ -95,10 +96,10 @@ func populateProcessEnvironment(env []string) error {
|
||||||
// and working dir, and closes any leaked file descriptors
|
// and working dir, and closes any leaked file descriptors
|
||||||
// before executing the command inside the namespace
|
// before executing the command inside the namespace
|
||||||
func finalizeNamespace(config *initConfig) error {
|
func finalizeNamespace(config *initConfig) error {
|
||||||
// Ensure that all non-standard fds we may have accidentally
|
// Ensure that all unwanted fds we may have accidentally
|
||||||
// inherited are marked close-on-exec so they stay out of the
|
// inherited are marked close-on-exec so they stay out of the
|
||||||
// container
|
// container
|
||||||
if err := utils.CloseExecFrom(3); err != nil {
|
if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -648,3 +648,69 @@ func TestContainerState(t *testing.T) {
|
||||||
stdinW.Close()
|
stdinW.Close()
|
||||||
p.Wait()
|
p.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPassExtraFiles(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rootfs, err := newRootfs()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer remove(rootfs)
|
||||||
|
|
||||||
|
config := newTemplateConfig(rootfs)
|
||||||
|
|
||||||
|
factory, err := libcontainer.New(rootfs, libcontainer.Cgroupfs)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
container, err := factory.Create("test", config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer container.Destroy()
|
||||||
|
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
pipeout1, pipein1, err := os.Pipe()
|
||||||
|
pipeout2, pipein2, err := os.Pipe()
|
||||||
|
process := libcontainer.Process{
|
||||||
|
Args: []string{"sh", "-c", "cd /proc/$$/fd; echo -n *; echo -n 1 >3; echo -n 2 >4"},
|
||||||
|
Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
|
||||||
|
ExtraFiles: []*os.File{pipein1, pipein2},
|
||||||
|
Stdin: nil,
|
||||||
|
Stdout: &stdout,
|
||||||
|
}
|
||||||
|
err = container.Start(&process)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
waitProcess(&process, t)
|
||||||
|
|
||||||
|
out := string(stdout.Bytes())
|
||||||
|
// fd 5 is the directory handle for /proc/$$/fd
|
||||||
|
if out != "0 1 2 3 4 5" {
|
||||||
|
t.Fatalf("expected to have the file descriptors '0 1 2 3 4 5' passed to init, got '%s'", out)
|
||||||
|
}
|
||||||
|
var buf = []byte{0}
|
||||||
|
_, err = pipeout1.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
out1 := string(buf)
|
||||||
|
if out1 != "1" {
|
||||||
|
t.Fatalf("expected first pipe to receive '1', got '%s'", out1)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = pipeout2.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
out2 := string(buf)
|
||||||
|
if out2 != "2" {
|
||||||
|
t.Fatalf("expected second pipe to receive '2', got '%s'", out2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ func init() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("unable to initialize for container: %s", err)
|
log.Fatalf("unable to initialize for container: %s", err)
|
||||||
}
|
}
|
||||||
if err := factory.StartInitialization(3); err != nil {
|
if err := factory.StartInitialization(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ var initCommand = cli.Command{
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
if err := factory.StartInitialization(3); err != nil {
|
if err := factory.StartInitialization(); err != nil {
|
||||||
fatal(err)
|
fatal(err)
|
||||||
}
|
}
|
||||||
panic("This line should never been executed")
|
panic("This line should never been executed")
|
||||||
|
|
|
@ -38,6 +38,9 @@ type Process struct {
|
||||||
// Stderr is a pointer to a writer which receives the standard error stream.
|
// Stderr is a pointer to a writer which receives the standard error stream.
|
||||||
Stderr io.Writer
|
Stderr io.Writer
|
||||||
|
|
||||||
|
// ExtraFiles specifies additional open files to be inherited by the container
|
||||||
|
ExtraFiles []*os.File
|
||||||
|
|
||||||
// consolePath is the path to the console allocated to the container.
|
// consolePath is the path to the console allocated to the container.
|
||||||
consolePath string
|
consolePath string
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue