From 88ef309a940bcbb6f85a750372b8fdbc6569c3a7 Mon Sep 17 00:00:00 2001
From: "Guillaume J. Charmes" <guillaume@dotcloud.com>
Date: Fri, 24 May 2013 14:44:16 -0700
Subject: [PATCH] Finish resize implementation client and server

---
 commands.go  | 42 +++++++++++++++++++++++++++++-------------
 container.go |  7 ++++++-
 term/term.go | 23 +++++++++++++++++++++--
 3 files changed, 56 insertions(+), 16 deletions(-)

diff --git a/commands.go b/commands.go
index 2b96f64f0e..099c3686cc 100644
--- a/commands.go
+++ b/commands.go
@@ -35,19 +35,6 @@ var (
 func ParseCommands(args ...string) error {
 	cli := NewDockerCli("0.0.0.0", 4243)
 
-	c := make(chan os.Signal, 1)
-	signal.Notify(c, syscall.SIGWINCH)
-	go func() {
-		for sig := range c {
-			if sig == syscall.SIGWINCH {
-				_, _, err := cli.call("GET", "/auth", nil)
-				if err != nil {
-					utils.Debugf("Error resize: %s", err)
-				}
-			}
-		}
-	}()
-
 	if len(args) > 0 {
 		methodName := "Cmd" + strings.ToUpper(args[0][:1]) + strings.ToLower(args[0][1:])
 		method, exists := reflect.TypeOf(cli).MethodByName(methodName)
@@ -975,6 +962,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 	v.Set("stderr", "1")
 	v.Set("stdin", "1")
 
+	cli.monitorTtySize(cmd.Arg(0))
 	if err := cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), container.Config.Tty); err != nil {
 		return err
 	}
@@ -1162,6 +1150,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 	}
 
 	if config.AttachStdin || config.AttachStdout || config.AttachStderr {
+		cli.monitorTtySize(out.Id)
 		if err := cli.hijack("POST", "/containers/"+out.Id+"/attach?"+v.Encode(), config.Tty); err != nil {
 			return err
 		}
@@ -1295,6 +1284,33 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool) error {
 
 }
 
+func (cli *DockerCli) resizeTty(id string) {
+	ws, err := term.GetWinsize(os.Stdin.Fd())
+	if err != nil {
+		utils.Debugf("Error getting size: %s", err)
+	}
+	v := url.Values{}
+	v.Set("h", strconv.Itoa(int(ws.Height)))
+	v.Set("w", strconv.Itoa(int(ws.Width)))
+	if _, _, err := cli.call("POST", "/containers/"+id+"/resize?"+v.Encode(), nil); err != nil {
+		utils.Debugf("Error resize: %s", err)
+	}
+}
+
+func (cli *DockerCli) monitorTtySize(id string) {
+	cli.resizeTty(id)
+
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, syscall.SIGWINCH)
+	go func() {
+		for sig := range c {
+			if sig == syscall.SIGWINCH {
+				cli.resizeTty(id)
+			}
+		}
+	}()
+}
+
 func Subcmd(name, signature, description string) *flag.FlagSet {
 	flags := flag.NewFlagSet(name, flag.ContinueOnError)
 	flags.Usage = func() {
diff --git a/container.go b/container.go
index 8cba8f5985..c6b7c8a51c 100644
--- a/container.go
+++ b/container.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"flag"
 	"fmt"
+	"github.com/dotcloud/docker/term"
 	"github.com/dotcloud/docker/utils"
 	"github.com/kr/pty"
 	"io"
@@ -755,7 +756,11 @@ func (container *Container) Wait() int {
 }
 
 func (container *Container) Resize(h, w int) error {
-	return fmt.Errorf("Resize not yet implemented")
+	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)})
 }
 
 func (container *Container) ExportRw() (Archive, error) {
diff --git a/term/term.go b/term/term.go
index 8c07b93356..d0f303f4e4 100644
--- a/term/term.go
+++ b/term/term.go
@@ -1,6 +1,7 @@
 package term
 
 import (
+	"github.com/dotcloud/docker/utils"
 	"os"
 	"os/signal"
 	"syscall"
@@ -109,17 +110,35 @@ type State struct {
 	termios Termios
 }
 
+type Winsize struct {
+	Width  uint16
+	Height uint16
+	x      uint16
+	y      uint16
+}
+
+func GetWinsize(fd uintptr) (*Winsize, error) {
+	ws := &Winsize{}
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
+	return ws, err
+}
+
+func SetWinsize(fd uintptr, ws *Winsize) error {
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
+	return err
+}
+
 // IsTerminal returns true if the given file descriptor is a terminal.
 func IsTerminal(fd int) bool {
 	var termios Termios
-	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
 	return err == 0
 }
 
 // Restore restores the terminal connected to the given file descriptor to a
 // previous state.
 func Restore(fd int, state *State) error {
-	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0)
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
 	return err
 }