diff --git a/components/engine/container.go b/components/engine/container.go index f38876ed24..025479f240 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -55,7 +55,6 @@ type Container struct { Driver string command *execdriver.Command - console execdriver.Console stdout *utils.WriteBroadcaster stderr *utils.WriteBroadcaster stdin io.ReadCloser @@ -531,6 +530,9 @@ func (container *Container) Start() (err error) { } populateCommand(container) + if err := execdriver.NewTerminal(container.command); err != nil { + return err + } // Setup logging of stdout and stderr to disk if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil { @@ -541,16 +543,6 @@ func (container *Container) Start() (err error) { } container.waitLock = make(chan struct{}) - container.console, err = execdriver.NewConsole( - container.stdin, container.stdout, container.stderr, - container.Config.OpenStdin, container.Config.Tty) - if err != nil { - return err - } - if err := container.console.AttachTo(container.command); err != nil { - return err - } - callbackLock := make(chan struct{}) callback := func(command *execdriver.Command) { container.State.SetRunning(command.Pid()) @@ -790,7 +782,8 @@ func (container *Container) monitor(callback execdriver.StartCallback) error { populateCommand(container) err = container.runtime.RestoreCommand(container) } else { - exitCode, err = container.runtime.Run(container, callback) + pipes := execdriver.NewPipes(container.stdin, container.stdout, container.stderr, container.Config.OpenStdin) + exitCode, err = container.runtime.Run(container, pipes, callback) } if err != nil { @@ -845,9 +838,9 @@ func (container *Container) cleanup() { if err := container.stderr.Close(); err != nil { utils.Errorf("%s: Error close stderr: %s", container.ID, err) } - if container.console != nil { - if err := container.console.Close(); err != nil { - utils.Errorf("%s: Error closing console: %s", container.ID, err) + if container.command.Terminal != nil { + if err := container.command.Terminal.Close(); err != nil { + utils.Errorf("%s: Error closing terminal: %s", container.ID, err) } } @@ -939,7 +932,7 @@ func (container *Container) Wait() int { } func (container *Container) Resize(h, w int) error { - return container.console.Resize(h, w) + return container.command.Terminal.Resize(h, w) } func (container *Container) ExportRw() (archive.Archive, error) { @@ -1143,7 +1136,7 @@ func (container *Container) Exposes(p nat.Port) bool { } func (container *Container) GetPtyMaster() (*os.File, error) { - ttyConsole, ok := container.console.(*execdriver.TtyConsole) + ttyConsole, ok := container.command.Terminal.(*execdriver.TtyConsole) if !ok { return nil, ErrNoTTY } diff --git a/components/engine/execdriver/chroot/driver.go b/components/engine/execdriver/chroot/driver.go index 396df87bad..dfec680d84 100644 --- a/components/engine/execdriver/chroot/driver.go +++ b/components/engine/execdriver/chroot/driver.go @@ -37,7 +37,7 @@ func NewDriver() (*driver, error) { return &driver{}, nil } -func (d *driver) Run(c *execdriver.Command, startCallback execdriver.StartCallback) (int, error) { +func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { params := []string{ "chroot", c.Rootfs, diff --git a/components/engine/execdriver/console.go b/components/engine/execdriver/console.go deleted file mode 100644 index 01b45afb60..0000000000 --- a/components/engine/execdriver/console.go +++ /dev/null @@ -1,137 +0,0 @@ -package execdriver - -import ( - "github.com/dotcloud/docker/pkg/term" - "github.com/kr/pty" - "io" - "os" -) - -type Console interface { - io.Closer - Resize(height, width int) error - AttachTo(command *Command) error -} - -type pipes struct { - Stdin io.ReadCloser - Stdout, Stderr io.WriteCloser -} - -func (p *pipes) Close() error { - if p.Stderr != nil { - p.Stdin.Close() - } - if p.Stdout != nil { - p.Stdout.Close() - } - if p.Stderr != nil { - p.Stderr.Close() - } - return nil -} - -func NewConsole(stdin io.ReadCloser, stdout, stderr io.WriteCloser, useStdin, tty bool) (Console, error) { - p := &pipes{ - Stdout: stdout, - Stderr: stderr, - } - if useStdin { - p.Stdin = stdin - } - if tty { - return NewTtyConsole(p) - } - return NewStdConsole(p) -} - -type TtyConsole struct { - Master *os.File - Slave *os.File - pipes *pipes -} - -func NewTtyConsole(p *pipes) (*TtyConsole, error) { - ptyMaster, ptySlave, err := pty.Open() - if err != nil { - return nil, err - } - tty := &TtyConsole{ - Master: ptyMaster, - Slave: ptySlave, - pipes: p, - } - return tty, nil -} - -func (t *TtyConsole) Resize(h, w int) error { - return term.SetWinsize(t.Master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)}) - -} - -func (t *TtyConsole) AttachTo(command *Command) error { - command.Stdout = t.Slave - command.Stderr = t.Slave - - command.Console = t.Slave.Name() - - go func() { - defer t.pipes.Stdout.Close() - io.Copy(t.pipes.Stdout, t.Master) - }() - - if t.pipes.Stdin != nil { - command.Stdin = t.Slave - command.SysProcAttr.Setctty = true - - go func() { - defer t.pipes.Stdin.Close() - io.Copy(t.Master, t.pipes.Stdin) - }() - } - return nil -} - -func (t *TtyConsole) Close() error { - err := t.Slave.Close() - if merr := t.Master.Close(); err == nil { - err = merr - } - return err -} - -type StdConsole struct { - pipes *pipes -} - -func NewStdConsole(p *pipes) (*StdConsole, error) { - return &StdConsole{p}, nil -} - -func (s *StdConsole) AttachTo(command *Command) error { - command.Stdout = s.pipes.Stdout - command.Stderr = s.pipes.Stderr - - if s.pipes.Stdin != nil { - stdin, err := command.StdinPipe() - if err != nil { - return err - } - - go func() { - defer stdin.Close() - io.Copy(stdin, s.pipes.Stdin) - }() - } - return nil -} - -func (s *StdConsole) Resize(h, w int) error { - // we do not need to reside a non tty - return nil -} - -func (s *StdConsole) Close() error { - // nothing to close here - return nil -} diff --git a/components/engine/execdriver/driver.go b/components/engine/execdriver/driver.go index 32b39771b6..c9f62f500e 100644 --- a/components/engine/execdriver/driver.go +++ b/components/engine/execdriver/driver.go @@ -58,7 +58,7 @@ type Info interface { } type Driver interface { - Run(c *Command, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code + Run(c *Command, pipes *Pipes, startCallback StartCallback) (int, error) // Run executes the process and blocks until the process exits and returns the exit code Kill(c *Command, sig int) error Restore(c *Command) error // Wait and try to re-attach on an out of process command Name() string // Driver name @@ -82,7 +82,6 @@ type Resources struct { } // Process wrapps an os/exec.Cmd to add more metadata -// TODO: Rename to Command type Command struct { exec.Cmd `json:"-"` @@ -100,7 +99,8 @@ type Command struct { Config []string `json:"config"` // generic values that specific drivers can consume Resources *Resources `json:"resources"` - Console string `json:"-"` + Terminal Term `json:"-"` + Console string `json:"-"` } // Return the pid of the process diff --git a/components/engine/execdriver/lxc/driver.go b/components/engine/execdriver/lxc/driver.go index ee4d02a6b6..da3bc1ec7c 100644 --- a/components/engine/execdriver/lxc/driver.go +++ b/components/engine/execdriver/lxc/driver.go @@ -76,7 +76,12 @@ func (d *driver) Name() string { return fmt.Sprintf("%s-%s", DriverName, version) } -func (d *driver) Run(c *execdriver.Command, startCallback execdriver.StartCallback) (int, error) { +func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { + if c.Terminal != nil { + if err := c.Terminal.Attach(pipes); err != nil { + return -1, err + } + } configPath, err := d.generateLXCConfig(c) if err != nil { return -1, err diff --git a/components/engine/execdriver/term.go b/components/engine/execdriver/term.go new file mode 100644 index 0000000000..e7ec46653c --- /dev/null +++ b/components/engine/execdriver/term.go @@ -0,0 +1,137 @@ +package execdriver + +import ( + "github.com/dotcloud/docker/pkg/term" + "github.com/kr/pty" + "io" + "os" +) + +type Term interface { + io.Closer + Resize(height, width int) error + Attach(pipes *Pipes) error +} + +type Pipes struct { + Stdin io.ReadCloser + Stdout, Stderr io.WriteCloser +} + +func NewPipes(stdin io.ReadCloser, stdout, stderr io.WriteCloser, useStdin bool) *Pipes { + p := &Pipes{ + Stdout: stdout, + Stderr: stderr, + } + if useStdin { + p.Stdin = stdin + } + return p +} + +func NewTerminal(command *Command) error { + var ( + term Term + err error + ) + if command.Tty { + term, err = NewTtyConsole(command) + } else { + term, err = NewStdConsole(command) + } + if err != nil { + return err + } + command.Terminal = term + return nil +} + +type TtyConsole struct { + command *Command + Master *os.File + Slave *os.File +} + +func NewTtyConsole(command *Command) (*TtyConsole, error) { + ptyMaster, ptySlave, err := pty.Open() + if err != nil { + return nil, err + } + tty := &TtyConsole{ + Master: ptyMaster, + Slave: ptySlave, + command: command, + } + return tty, nil +} + +func (t *TtyConsole) Resize(h, w int) error { + return term.SetWinsize(t.Master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)}) +} + +func (t *TtyConsole) Attach(pipes *Pipes) error { + t.command.Stdout = t.Slave + t.command.Stderr = t.Slave + + t.command.Console = t.Slave.Name() + + go func() { + defer pipes.Stdout.Close() + io.Copy(pipes.Stdout, t.Master) + }() + + if pipes.Stdin != nil { + t.command.Stdin = t.Slave + t.command.SysProcAttr.Setctty = true + + go func() { + defer pipes.Stdin.Close() + io.Copy(t.Master, pipes.Stdin) + }() + } + return nil +} + +func (t *TtyConsole) Close() error { + err := t.Slave.Close() + if merr := t.Master.Close(); err == nil { + err = merr + } + return err +} + +type StdConsole struct { + command *Command +} + +func NewStdConsole(command *Command) (*StdConsole, error) { + return &StdConsole{command}, nil +} + +func (s *StdConsole) Attach(pipes *Pipes) error { + s.command.Stdout = pipes.Stdout + s.command.Stderr = pipes.Stderr + + if pipes.Stdin != nil { + stdin, err := s.command.StdinPipe() + if err != nil { + return err + } + + go func() { + defer stdin.Close() + io.Copy(stdin, pipes.Stdin) + }() + } + return nil +} + +func (s *StdConsole) Resize(h, w int) error { + // we do not need to reside a non tty + return nil +} + +func (s *StdConsole) Close() error { + // nothing to close here + return nil +} diff --git a/components/engine/runtime.go b/components/engine/runtime.go index eed28f92ab..a38109cca0 100644 --- a/components/engine/runtime.go +++ b/components/engine/runtime.go @@ -812,8 +812,8 @@ func (runtime *Runtime) Diff(container *Container) (archive.Archive, error) { }), nil } -func (runtime *Runtime) Run(c *Container, startCallback execdriver.StartCallback) (int, error) { - return runtime.execDriver.Run(c.command, startCallback) +func (runtime *Runtime) Run(c *Container, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { + return runtime.execDriver.Run(c.command, pipes, startCallback) } func (runtime *Runtime) Kill(c *Container, sig int) error {