diff --git a/container.go b/container.go index 9c7fc8ffd7..f38876ed24 100644 --- a/container.go +++ b/container.go @@ -10,10 +10,8 @@ import ( "github.com/dotcloud/docker/graphdriver" "github.com/dotcloud/docker/links" "github.com/dotcloud/docker/nat" - "github.com/dotcloud/docker/pkg/term" "github.com/dotcloud/docker/runconfig" "github.com/dotcloud/docker/utils" - "github.com/kr/pty" "io" "io/ioutil" "log" @@ -57,11 +55,11 @@ type Container struct { Driver string command *execdriver.Command + console execdriver.Console stdout *utils.WriteBroadcaster stderr *utils.WriteBroadcaster stdin io.ReadCloser stdinPipe io.WriteCloser - ptyMaster io.Closer runtime *Runtime @@ -213,56 +211,6 @@ func (container *Container) generateEnvConfig(env []string) error { return nil } -func (container *Container) setupPty() error { - ptyMaster, ptySlave, err := pty.Open() - if err != nil { - return err - } - container.ptyMaster = ptyMaster - container.command.Stdout = ptySlave - container.command.Stderr = ptySlave - container.command.Console = ptySlave.Name() - - // Copy the PTYs to our broadcasters - go func() { - defer container.stdout.CloseWriters() - utils.Debugf("startPty: begin of stdout pipe") - io.Copy(container.stdout, ptyMaster) - utils.Debugf("startPty: end of stdout pipe") - }() - - // stdin - if container.Config.OpenStdin { - container.command.Stdin = ptySlave - container.command.SysProcAttr.Setctty = true - go func() { - defer container.stdin.Close() - utils.Debugf("startPty: begin of stdin pipe") - io.Copy(ptyMaster, container.stdin) - utils.Debugf("startPty: end of stdin pipe") - }() - } - return nil -} - -func (container *Container) setupStd() error { - container.command.Stdout = container.stdout - container.command.Stderr = container.stderr - if container.Config.OpenStdin { - stdin, err := container.command.StdinPipe() - if err != nil { - return err - } - go func() { - defer stdin.Close() - utils.Debugf("start: begin of stdin pipe") - io.Copy(stdin, container.stdin) - utils.Debugf("start: end of stdin pipe") - }() - } - return nil -} - func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error { var cStdout, cStderr io.ReadCloser @@ -593,14 +541,13 @@ func (container *Container) Start() (err error) { } container.waitLock = make(chan struct{}) - // Setuping pipes and/or Pty - var setup func() error - if container.Config.Tty { - setup = container.setupPty - } else { - setup = container.setupStd + container.console, err = execdriver.NewConsole( + container.stdin, container.stdout, container.stderr, + container.Config.OpenStdin, container.Config.Tty) + if err != nil { + return err } - if err := setup(); err != nil { + if err := container.console.AttachTo(container.command); err != nil { return err } @@ -887,22 +834,20 @@ func (container *Container) cleanup() { link.Disable() } } - if container.Config.OpenStdin { if err := container.stdin.Close(); err != nil { utils.Errorf("%s: Error close stdin: %s", container.ID, err) } } - if err := container.stdout.CloseWriters(); err != nil { + if err := container.stdout.Close(); err != nil { utils.Errorf("%s: Error close stdout: %s", container.ID, err) } - if err := container.stderr.CloseWriters(); err != nil { + if err := container.stderr.Close(); err != nil { utils.Errorf("%s: Error close stderr: %s", container.ID, err) } - - if container.ptyMaster != nil { - if err := container.ptyMaster.Close(); err != nil { - utils.Errorf("%s: Error closing Pty master: %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) } } @@ -994,11 +939,7 @@ func (container *Container) Wait() int { } func (container *Container) Resize(h, w int) error { - pty, ok := container.ptyMaster.(*os.File) - if !ok { - return fmt.Errorf("ptyMaster does not have Fd() method") - } - return term.SetWinsize(pty.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)}) + return container.console.Resize(h, w) } func (container *Container) ExportRw() (archive.Archive, error) { @@ -1202,11 +1143,9 @@ func (container *Container) Exposes(p nat.Port) bool { } func (container *Container) GetPtyMaster() (*os.File, error) { - if container.ptyMaster == nil { + ttyConsole, ok := container.console.(*execdriver.TtyConsole) + if !ok { return nil, ErrNoTTY } - if pty, ok := container.ptyMaster.(*os.File); ok { - return pty, nil - } - return nil, ErrNotATTY + return ttyConsole.Master, nil } diff --git a/execdriver/console.go b/execdriver/console.go new file mode 100644 index 0000000000..01b45afb60 --- /dev/null +++ b/execdriver/console.go @@ -0,0 +1,137 @@ +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/utils/utils.go b/utils/utils.go index 1aba80ff41..3a87f76f5f 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -388,7 +388,7 @@ func (w *WriteBroadcaster) Write(p []byte) (n int, err error) { return len(p), nil } -func (w *WriteBroadcaster) CloseWriters() error { +func (w *WriteBroadcaster) Close() error { w.Lock() defer w.Unlock() for sw := range w.writers { diff --git a/utils/utils_test.go b/utils/utils_test.go index 7e63a45cf7..f8b3920548 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -110,7 +110,7 @@ func TestWriteBroadcaster(t *testing.T) { t.Errorf("Buffer contains %v", bufferC.String()) } - writer.CloseWriters() + writer.Close() } type devNullCloser int