diff --git a/console/console.go b/console/console.go index 5355d63d..c0d1fb04 100644 --- a/console/console.go +++ b/console/console.go @@ -90,13 +90,16 @@ func CreateMasterAndConsole() (*os.File, string, error) { if err != nil { return nil, "", err } + console, err := Ptsname(master) if err != nil { return nil, "", err } + if err := Unlockpt(master); err != nil { return nil, "", err } + return master, console, nil } diff --git a/namespaces/exec.go b/namespaces/exec.go index ee87250e..6f3838fd 100644 --- a/namespaces/exec.go +++ b/namespaces/exec.go @@ -3,6 +3,7 @@ package namespaces import ( + "io" "os" "os/exec" "syscall" @@ -11,7 +12,6 @@ import ( "github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/cgroups/fs" "github.com/docker/libcontainer/cgroups/systemd" - consolePkg "github.com/docker/libcontainer/console" "github.com/docker/libcontainer/network" "github.com/docker/libcontainer/syncpipe" "github.com/docker/libcontainer/system" @@ -21,11 +21,9 @@ import ( // Move this to libcontainer package. // Exec performs setup outside of a namespace so that a container can be // executed. Exec is a high level function for working with container namespaces. -func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { +func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Writer, console string, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { var ( - master *os.File - console string - err error + err error ) // create a pipe so that we can syncronize with the namespaced process and @@ -36,20 +34,13 @@ func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string } defer syncPipe.Close() - if container.Tty { - master, console, err = consolePkg.CreateMasterAndConsole() - if err != nil { - return -1, err - } - term.SetMaster(master) - } - command := createCommand(container, console, rootfs, dataPath, os.Args[0], syncPipe.Child(), args) - - if err := term.Attach(command); err != nil { - return -1, err - } - defer term.Close() + // Note: these are only used in non-tty mode + // if there is a tty for the container it will be opened within the namespace and the + // fds will be duped to stdin, stdiout, and stderr + command.Stdin = stdin + command.Stdout = stdout + command.Stderr = stderr if err := command.Start(); err != nil { return -1, err diff --git a/namespaces/std_term.go b/namespaces/std_term.go deleted file mode 100644 index 324336af..00000000 --- a/namespaces/std_term.go +++ /dev/null @@ -1,49 +0,0 @@ -package namespaces - -import ( - "io" - "os" - "os/exec" -) - -type StdTerminal struct { - stdin io.Reader - stdout, stderr io.Writer -} - -func (s *StdTerminal) SetMaster(*os.File) { - // no need to set master on non tty -} - -func (s *StdTerminal) Close() error { - return nil -} - -func (s *StdTerminal) Resize(h, w int) error { - return nil -} - -func (s *StdTerminal) Attach(command *exec.Cmd) error { - inPipe, err := command.StdinPipe() - if err != nil { - return err - } - outPipe, err := command.StdoutPipe() - if err != nil { - return err - } - errPipe, err := command.StderrPipe() - if err != nil { - return err - } - - go func() { - defer inPipe.Close() - io.Copy(inPipe, s.stdin) - }() - - go io.Copy(s.stdout, outPipe) - go io.Copy(s.stderr, errPipe) - - return nil -} diff --git a/namespaces/term.go b/namespaces/term.go deleted file mode 100644 index 2a50bf85..00000000 --- a/namespaces/term.go +++ /dev/null @@ -1,29 +0,0 @@ -package namespaces - -import ( - "io" - "os" - "os/exec" -) - -type Terminal interface { - io.Closer - SetMaster(*os.File) - Attach(*exec.Cmd) error - Resize(h, w int) error -} - -func NewTerminal(stdin io.Reader, stdout, stderr io.Writer, tty bool) Terminal { - if tty { - return &TtyTerminal{ - stdin: stdin, - stdout: stdout, - stderr: stderr, - } - } - return &StdTerminal{ - stdin: stdin, - stdout: stdout, - stderr: stderr, - } -} diff --git a/namespaces/tty_term.go b/namespaces/tty_term.go deleted file mode 100644 index 272cf2cd..00000000 --- a/namespaces/tty_term.go +++ /dev/null @@ -1,56 +0,0 @@ -package namespaces - -import ( - "io" - "os" - "os/exec" - - "github.com/dotcloud/docker/pkg/term" -) - -type TtyTerminal struct { - stdin io.Reader - stdout, stderr io.Writer - master *os.File - state *term.State -} - -func (t *TtyTerminal) Resize(h, w int) error { - return term.SetWinsize(t.master.Fd(), &term.Winsize{Height: uint16(h), Width: uint16(w)}) -} - -func (t *TtyTerminal) SetMaster(master *os.File) { - t.master = master -} - -func (t *TtyTerminal) Attach(command *exec.Cmd) error { - go io.Copy(t.stdout, t.master) - go io.Copy(t.master, t.stdin) - - state, err := t.setupWindow(t.master, os.Stdin) - - if err != nil { - return err - } - - t.state = state - return err -} - -// SetupWindow gets the parent window size and sets the master -// pty to the current size and set the parents mode to RAW -func (t *TtyTerminal) setupWindow(master, parent *os.File) (*term.State, error) { - ws, err := term.GetWinsize(parent.Fd()) - if err != nil { - return nil, err - } - if err := term.SetWinsize(master.Fd(), ws); err != nil { - return nil, err - } - return term.SetRawTerminal(parent.Fd()) -} - -func (t *TtyTerminal) Close() error { - term.RestoreTerminal(os.Stdin.Fd(), t.state) - return t.master.Close() -} diff --git a/nsinit/exec.go b/nsinit/exec.go index eb734545..690af5f5 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -2,14 +2,18 @@ package nsinit import ( "fmt" + "io" "log" "os" "os/exec" "os/signal" + "syscall" "github.com/codegangsta/cli" "github.com/docker/libcontainer" + consolepkg "github.com/docker/libcontainer/console" "github.com/docker/libcontainer/namespaces" + "github.com/dotcloud/docker/pkg/term" ) var execCommand = cli.Command{ @@ -34,8 +38,7 @@ func execAction(context *cli.Context) { if state != nil { err = namespaces.ExecIn(container, state, []string(context.Args())) } else { - term := namespaces.NewTerminal(os.Stdin, os.Stdout, os.Stderr, container.Tty) - exitCode, err = startContainer(container, term, dataPath, []string(context.Args())) + exitCode, err = startContainer(container, dataPath, []string(context.Args())) } if err != nil { @@ -49,7 +52,7 @@ func execAction(context *cli.Context) { // error. // // Signals sent to the current process will be forwarded to container. -func startContainer(container *libcontainer.Config, term namespaces.Terminal, dataPath string, args []string) (int, error) { +func startContainer(container *libcontainer.Config, dataPath string, args []string) (int, error) { var ( cmd *exec.Cmd sigc = make(chan os.Signal, 10) @@ -65,13 +68,66 @@ func startContainer(container *libcontainer.Config, term namespaces.Terminal, da return cmd } + var ( + master *os.File + console string + err error + + stdin = os.Stdin + stdout = os.Stdout + stderr = os.Stderr + ) + + if container.Tty { + stdin = nil + stdout = nil + stderr = nil + + master, console, err = consolepkg.CreateMasterAndConsole() + if err != nil { + return -1, err + } + + go io.Copy(master, os.Stdin) + go io.Copy(os.Stdout, master) + + state, err := term.SetRawTerminal(os.Stdin.Fd()) + if err != nil { + return -1, err + } + + defer term.RestoreTerminal(os.Stdin.Fd(), state) + } + startCallback := func() { go func() { + resizeTty(master) + for sig := range sigc { - cmd.Process.Signal(sig) + switch sig { + case syscall.SIGWINCH: + resizeTty(master) + default: + cmd.Process.Signal(sig) + } } }() } - return namespaces.Exec(container, term, "", dataPath, args, createCommand, startCallback) + return namespaces.Exec(container, stdin, stdout, stderr, console, "", dataPath, args, createCommand, startCallback) +} + +func resizeTty(master *os.File) { + if master == nil { + return + } + + ws, err := term.GetWinsize(os.Stdin.Fd()) + if err != nil { + return + } + + if err := term.SetWinsize(master.Fd(), ws); err != nil { + return + } }