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 { }