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:
parent
b120ecf74d
commit
708b25e61e
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue