From 0d67b420b59c953cf331f735e49e7acad742a41f Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Wed, 16 Jul 2014 15:37:10 -0700 Subject: [PATCH] Make tty term exec driver specific lxc is special in that we cannot create the master outside of the container without opening the slave because we have nothing to provide to the cmd. We have to open both then do the crazy setup on command right now instead of passing the console path to lxc and telling it to open up that console. we save a couple of openfiles in the native driver because we can do this. Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- daemon/execdriver/lxc/driver.go | 88 +++++++++++++++++++++++++++++- daemon/execdriver/native/driver.go | 69 ++++++++++++++++++++++- daemon/execdriver/termconsole.go | 80 --------------------------- 3 files changed, 153 insertions(+), 84 deletions(-) diff --git a/daemon/execdriver/lxc/driver.go b/daemon/execdriver/lxc/driver.go index 5a6055fc43..fe3a3ce228 100644 --- a/daemon/execdriver/lxc/driver.go +++ b/daemon/execdriver/lxc/driver.go @@ -3,6 +3,7 @@ package lxc import ( "encoding/json" "fmt" + "io" "io/ioutil" "log" "os" @@ -19,7 +20,9 @@ import ( "github.com/docker/libcontainer/label" "github.com/docker/libcontainer/mount/nodes" "github.com/dotcloud/docker/daemon/execdriver" + "github.com/dotcloud/docker/pkg/term" "github.com/dotcloud/docker/utils" + "github.com/kr/pty" ) const DriverName = "lxc" @@ -78,10 +81,20 @@ func (d *driver) Name() string { } func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { - if err := execdriver.SetTerminal(c, pipes); err != nil { - return -1, err + var ( + term execdriver.Terminal + err error + ) + + if c.Tty { + term, err = NewTtyConsole(c, pipes) + } else { + term, err = execdriver.NewStdConsole(c, pipes) } + c.Terminal = term + c.Mounts = append(c.Mounts, execdriver.Mount{d.initPath, c.InitPath, false, true}) + if err := d.generateEnvConfig(c); err != nil { return -1, err } @@ -462,3 +475,74 @@ func (d *driver) generateEnvConfig(c *execdriver.Command) error { return ioutil.WriteFile(p, data, 0600) } + +type TtyConsole struct { + MasterPty *os.File + SlavePty *os.File +} + +func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) { + // lxc is special in that we cannot create the master outside of the container without + // opening the slave because we have nothing to provide to the cmd. We have to open both then do + // the crazy setup on command right now instead of passing the console path to lxc and telling it + // to open up that console. we save a couple of openfiles in the native driver because we can do + // this. + ptyMaster, ptySlave, err := pty.Open() + if err != nil { + return nil, err + } + + tty := &TtyConsole{ + MasterPty: ptyMaster, + SlavePty: ptySlave, + } + + if err := tty.AttachPipes(&command.Cmd, pipes); err != nil { + tty.Close() + return nil, err + } + + command.Console = tty.SlavePty.Name() + + return tty, nil +} + +func (t *TtyConsole) Master() *os.File { + return t.MasterPty +} + +func (t *TtyConsole) Resize(h, w int) error { + return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)}) +} + +func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error { + command.Stdout = t.SlavePty + command.Stderr = t.SlavePty + + go func() { + if wb, ok := pipes.Stdout.(interface { + CloseWriters() error + }); ok { + defer wb.CloseWriters() + } + + io.Copy(pipes.Stdout, t.MasterPty) + }() + + if pipes.Stdin != nil { + command.Stdin = t.SlavePty + command.SysProcAttr.Setctty = true + + go func() { + io.Copy(t.MasterPty, pipes.Stdin) + + pipes.Stdin.Close() + }() + } + return nil +} + +func (t *TtyConsole) Close() error { + t.SlavePty.Close() + return t.MasterPty.Close() +} diff --git a/daemon/execdriver/native/driver.go b/daemon/execdriver/native/driver.go index 5b1b96bdfe..711f29429a 100644 --- a/daemon/execdriver/native/driver.go +++ b/daemon/execdriver/native/driver.go @@ -5,6 +5,7 @@ package native import ( "encoding/json" "fmt" + "io" "io/ioutil" "os" "os/exec" @@ -21,6 +22,7 @@ import ( "github.com/docker/libcontainer/syncpipe" "github.com/dotcloud/docker/daemon/execdriver" "github.com/dotcloud/docker/pkg/system" + "github.com/dotcloud/docker/pkg/term" ) const ( @@ -96,9 +98,14 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba return -1, err } - if err := execdriver.SetTerminal(c, pipes); err != nil { - return -1, err + var term execdriver.Terminal + + if c.Tty { + term, err = NewTtyConsole(c, pipes) + } else { + term, err = execdriver.NewStdConsole(c, pipes) } + c.Terminal = term d.Lock() d.activeContainers[c.ID] = &activeContainer{ @@ -272,3 +279,61 @@ func getEnv(key string, env []string) string { } return "" } + +type TtyConsole struct { + MasterPty *os.File +} + +func NewTtyConsole(command *execdriver.Command, pipes *execdriver.Pipes) (*TtyConsole, error) { + ptyMaster, console, err := system.CreateMasterAndConsole() + if err != nil { + return nil, err + } + + tty := &TtyConsole{ + MasterPty: ptyMaster, + } + + if err := tty.AttachPipes(&command.Cmd, pipes); err != nil { + tty.Close() + return nil, err + } + + command.Console = console + + return tty, nil +} + +func (t *TtyConsole) Master() *os.File { + return t.MasterPty +} + +func (t *TtyConsole) Resize(h, w int) error { + return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)}) +} + +func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *execdriver.Pipes) error { + go func() { + if wb, ok := pipes.Stdout.(interface { + CloseWriters() error + }); ok { + defer wb.CloseWriters() + } + + io.Copy(pipes.Stdout, t.MasterPty) + }() + + if pipes.Stdin != nil { + go func() { + io.Copy(t.MasterPty, pipes.Stdin) + + pipes.Stdin.Close() + }() + } + + return nil +} + +func (t *TtyConsole) Close() error { + return t.MasterPty.Close() +} diff --git a/daemon/execdriver/termconsole.go b/daemon/execdriver/termconsole.go index ebd849209f..dc0e54ccdb 100644 --- a/daemon/execdriver/termconsole.go +++ b/daemon/execdriver/termconsole.go @@ -2,89 +2,9 @@ package execdriver import ( "io" - "os" "os/exec" - - "github.com/dotcloud/docker/pkg/system" - "github.com/dotcloud/docker/pkg/term" ) -func SetTerminal(command *Command, pipes *Pipes) error { - var ( - term Terminal - err error - ) - - if command.Tty { - term, err = NewTtyConsole(command, pipes) - } else { - term, err = NewStdConsole(command, pipes) - } - - if err != nil { - return err - } - - command.Terminal = term - - return nil -} - -type TtyConsole struct { - MasterPty *os.File -} - -func NewTtyConsole(command *Command, pipes *Pipes) (*TtyConsole, error) { - ptyMaster, console, err := system.CreateMasterAndConsole() - if err != nil { - return nil, err - } - - tty := &TtyConsole{ - MasterPty: ptyMaster, - } - - if err := tty.AttachPipes(&command.Cmd, pipes); err != nil { - tty.Close() - return nil, err - } - command.Console = console - - return tty, nil -} - -func (t *TtyConsole) Master() *os.File { - return t.MasterPty -} - -func (t *TtyConsole) Resize(h, w int) error { - return term.SetWinsize(t.MasterPty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)}) -} - -func (t *TtyConsole) AttachPipes(command *exec.Cmd, pipes *Pipes) error { - go func() { - if wb, ok := pipes.Stdout.(interface { - CloseWriters() error - }); ok { - defer wb.CloseWriters() - } - io.Copy(pipes.Stdout, t.MasterPty) - }() - - if pipes.Stdin != nil { - go func() { - defer pipes.Stdin.Close() - io.Copy(t.MasterPty, pipes.Stdin) - }() - } - - return nil -} - -func (t *TtyConsole) Close() error { - return t.MasterPty.Close() -} - type StdConsole struct { }