From d661720fd75f2f6562bd0a83d2b53bffb1f951c3 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 15 Jul 2014 16:26:28 -0700 Subject: [PATCH 1/4] Remove terminal handling in libcontainer Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- console/console.go | 3 +++ namespaces/exec.go | 20 ++++++--------- namespaces/std_term.go | 49 ------------------------------------ namespaces/term.go | 29 ---------------------- namespaces/tty_term.go | 56 ------------------------------------------ nsinit/exec.go | 33 ++++++++++++++++++++++--- 6 files changed, 39 insertions(+), 151 deletions(-) delete mode 100644 namespaces/std_term.go delete mode 100644 namespaces/term.go delete mode 100644 namespaces/tty_term.go 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..1359dfb8 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,9 +21,8 @@ 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, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { var ( - master *os.File console string err error ) @@ -36,20 +35,15 @@ 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) + if f, ok := stdin.(*os.File); ok { + console = f.Name() } 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() + 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..f97efcae 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -2,13 +2,16 @@ 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" ) @@ -34,8 +37,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 +51,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) @@ -73,5 +75,28 @@ func startContainer(container *libcontainer.Config, term namespaces.Terminal, da }() } - return namespaces.Exec(container, term, "", dataPath, args, createCommand, startCallback) + var ( + stdin = os.Stdin + stdout = os.Stdout + stderr = os.Stderr + ) + + if container.Tty { + master, slavePath, err := consolepkg.CreateMasterAndConsole() + if err != nil { + return -1, err + } + + slave, err := consolepkg.OpenTerminal(slavePath, syscall.O_RDWR) + if err != nil { + return -1, err + } + + stdin, stdout, stderr = slave, slave, slave + + go io.Copy(master, os.Stdin) + go io.Copy(os.Stdout, master) + } + + return namespaces.Exec(container, stdin, stdout, stderr, "", dataPath, args, createCommand, startCallback) } From 43b4258c46f9770744115bc809f406afb2b3a38f Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 15 Jul 2014 17:19:32 -0700 Subject: [PATCH 2/4] Open with NOCTTY and set raw term Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- nsinit/exec.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/nsinit/exec.go b/nsinit/exec.go index f97efcae..d4e37cb3 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -13,6 +13,7 @@ import ( "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{ @@ -87,7 +88,7 @@ func startContainer(container *libcontainer.Config, dataPath string, args []stri return -1, err } - slave, err := consolepkg.OpenTerminal(slavePath, syscall.O_RDWR) + slave, err := consolepkg.OpenTerminal(slavePath, syscall.O_RDWR|syscall.O_NOCTTY) if err != nil { return -1, err } @@ -96,6 +97,19 @@ func startContainer(container *libcontainer.Config, dataPath string, args []stri go io.Copy(master, os.Stdin) go io.Copy(os.Stdout, master) + + ws, err := term.GetWinsize(os.Stdin.Fd()) + if err != nil { + return -1, err + } + + if err := term.SetWinsize(master.Fd(), ws); err != nil { + return -1, err + } + + if _, err := term.SetRawTerminal(os.Stdin.Fd()); err != nil { + return -1, err + } } return namespaces.Exec(container, stdin, stdout, stderr, "", dataPath, args, createCommand, startCallback) From 18e12838d6188f97164ad2d317396e4ac558e834 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 15 Jul 2014 17:44:18 -0700 Subject: [PATCH 3/4] Add resize of term in tty mode Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- nsinit/exec.go | 57 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/nsinit/exec.go b/nsinit/exec.go index d4e37cb3..fac057f6 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -68,22 +68,17 @@ func startContainer(container *libcontainer.Config, dataPath string, args []stri return cmd } - startCallback := func() { - go func() { - for sig := range sigc { - cmd.Process.Signal(sig) - } - }() - } - var ( - stdin = os.Stdin - stdout = os.Stdout - stderr = os.Stderr + master *os.File + slavePath string + err error + stdin = os.Stdin + stdout = os.Stdout + stderr = os.Stderr ) if container.Tty { - master, slavePath, err := consolepkg.CreateMasterAndConsole() + master, slavePath, err = consolepkg.CreateMasterAndConsole() if err != nil { return -1, err } @@ -98,19 +93,43 @@ func startContainer(container *libcontainer.Config, dataPath string, args []stri go io.Copy(master, os.Stdin) go io.Copy(os.Stdout, master) - ws, err := term.GetWinsize(os.Stdin.Fd()) + state, err := term.SetRawTerminal(os.Stdin.Fd()) if err != nil { return -1, err } - if err := term.SetWinsize(master.Fd(), ws); err != nil { - return -1, err - } + defer term.RestoreTerminal(os.Stdin.Fd(), state) + } - if _, err := term.SetRawTerminal(os.Stdin.Fd()); err != nil { - return -1, err - } + startCallback := func() { + go func() { + resizeTty(master) + + for sig := range sigc { + switch sig { + case syscall.SIGWINCH: + resizeTty(master) + default: + cmd.Process.Signal(sig) + } + } + }() } return namespaces.Exec(container, stdin, stdout, stderr, "", 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 + } +} From b56aa0658a308a2cd69861a4872ee61fb3838675 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Tue, 15 Jul 2014 18:24:15 -0700 Subject: [PATCH 4/4] Don't open slave in parent Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- namespaces/exec.go | 13 +++++-------- nsinit/exec.go | 28 +++++++++++++--------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/namespaces/exec.go b/namespaces/exec.go index 1359dfb8..6f3838fd 100644 --- a/namespaces/exec.go +++ b/namespaces/exec.go @@ -21,10 +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, stdin io.Reader, stdout, stderr io.Writer, 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 ( - console string - err error + err error ) // create a pipe so that we can syncronize with the namespaced process and @@ -35,12 +34,10 @@ func Exec(container *libcontainer.Config, stdin io.Reader, stdout, stderr io.Wri } defer syncPipe.Close() - if f, ok := stdin.(*os.File); ok { - console = f.Name() - } - command := createCommand(container, console, rootfs, dataPath, os.Args[0], syncPipe.Child(), args) - + // 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 diff --git a/nsinit/exec.go b/nsinit/exec.go index fac057f6..690af5f5 100644 --- a/nsinit/exec.go +++ b/nsinit/exec.go @@ -69,27 +69,25 @@ func startContainer(container *libcontainer.Config, dataPath string, args []stri } var ( - master *os.File - slavePath string - err error - stdin = os.Stdin - stdout = os.Stdout - stderr = os.Stderr + master *os.File + console string + err error + + stdin = os.Stdin + stdout = os.Stdout + stderr = os.Stderr ) if container.Tty { - master, slavePath, err = consolepkg.CreateMasterAndConsole() + stdin = nil + stdout = nil + stderr = nil + + master, console, err = consolepkg.CreateMasterAndConsole() if err != nil { return -1, err } - slave, err := consolepkg.OpenTerminal(slavePath, syscall.O_RDWR|syscall.O_NOCTTY) - if err != nil { - return -1, err - } - - stdin, stdout, stderr = slave, slave, slave - go io.Copy(master, os.Stdin) go io.Copy(os.Stdout, master) @@ -116,7 +114,7 @@ func startContainer(container *libcontainer.Config, dataPath string, args []stri }() } - return namespaces.Exec(container, stdin, stdout, stderr, "", dataPath, args, createCommand, startCallback) + return namespaces.Exec(container, stdin, stdout, stderr, console, "", dataPath, args, createCommand, startCallback) } func resizeTty(master *os.File) {