add option to pass additional fds to container process

This can be usefull to implement socket activated containers for example.

Signed-off-by: Jörg Thalheim <joerg@higgsboson.tk>
This commit is contained in:
Jörg Thalheim 2015-03-31 23:40:05 +02:00 committed by Michael Crosby
parent b120ecf74d
commit 708b25e61e
4 changed files with 101 additions and 18 deletions

View File

@ -139,7 +139,7 @@ func (c *linuxContainer) commandTemplate(p *Process, childPipe *os.File) (*exec.
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
cmd.ExtraFiles = []*os.File{childPipe}
cmd.ExtraFiles = append([]*os.File{childPipe}, p.ExtraFiles...)
// 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
// even with the parent still running.
@ -202,6 +202,7 @@ func (c *linuxContainer) newInitConfig(process *Process) *initConfig {
Cwd: process.Cwd,
Console: process.consolePath,
Capabilities: process.Capabilities,
PassedFilesCount: len(process.ExtraFiles),
}
}

View File

@ -48,6 +48,7 @@ type initConfig struct {
Config *configs.Config `json:"config"`
Console string `json:"console"`
Networks []*network `json:"network"`
PassedFilesCount int `json:"passed_files_count"`
}
type initer interface {
@ -95,10 +96,22 @@ func populateProcessEnvironment(env []string) error {
// and working dir, and closes any leaked file descriptors
// before executing the command inside the namespace
func finalizeNamespace(config *initConfig) error {
// Ensure that all non-standard fds we may have accidentally
// FD 3 is the child pipe, which needs to be closed.
// Additional file descriptors starts from 3 to (3 + n)
// To fix the order all additional file descriptors
// are shiftet by one right
for fd := 3; fd < (config.PassedFilesCount + 3); fd++ {
err := syscall.Dup2(fd+1, fd)
if err != nil {
return err
}
}
// Ensure that all unwanted fds we may have accidentally
// inherited are marked close-on-exec so they stay out of the
// container
if err := utils.CloseExecFrom(3); err != nil {
if err := utils.CloseExecFrom(config.PassedFilesCount + 3); err != nil {
return err
}

View File

@ -648,3 +648,69 @@ func TestContainerState(t *testing.T) {
stdinW.Close()
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)
}
}

View File

@ -38,6 +38,9 @@ type Process struct {
// Stderr is a pointer to a writer which receives the standard error stream.
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 string