From 3f63b8780765df735192ea0299e86e8cb7dbcb88 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 4 Apr 2013 12:54:53 -0700 Subject: [PATCH 1/7] Disable signal catching and enable real posix raw mode --- docker/docker.go | 9 +++++++++ term/termios_linux.go | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docker/docker.go b/docker/docker.go index 1b1c21990..7e1dfd00e 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -56,6 +56,15 @@ func daemon() error { } func runCommand(args []string) error { + var oldState *term.State + var err error + if term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" { + oldState, err = term.MakeRaw(int(os.Stdin.Fd())) + if err != nil { + return err + } + defer term.Restore(int(os.Stdin.Fd()), oldState) + } // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose // CloseWrite(), which we need to cleanly signal that stdin is closed without // closing the connection. diff --git a/term/termios_linux.go b/term/termios_linux.go index 5275ba87f..92f21edde 100644 --- a/term/termios_linux.go +++ b/term/termios_linux.go @@ -15,7 +15,8 @@ void MakeRaw(int fd) { ioctl(fd, TCGETS, &t); t.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - t.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); + t.c_oflag &= ~OPOST; + t.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); t.c_cflag &= ~(CSIZE | PARENB); t.c_cflag |= CS8; From 1f70b1e15d0dea5f36395d325cbac2892e4f2e8a Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 4 Apr 2013 12:55:24 -0700 Subject: [PATCH 2/7] Implement an escape sequence in order to be able to detach from a container --- container.go | 2 +- utils.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/container.go b/container.go index a7a4de556..5c4f8aa5f 100644 --- a/container.go +++ b/container.go @@ -255,7 +255,7 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s if container.Config.StdinOnce && !container.Config.Tty { defer cStdin.Close() } - _, err := io.Copy(cStdin, stdin) + _, err := CopyEscapable(cStdin, stdin) if err != nil { Debugf("[error] attach stdin: %s\n", err) } diff --git a/utils.go b/utils.go index 5ee84239b..398d6570b 100644 --- a/utils.go +++ b/utils.go @@ -341,3 +341,53 @@ func TruncateId(id string) string { } return id[:shortLen] } + +// Code c/c from io.Copy() modified to handle escape sequence +func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) { + // If the writer has a ReadFrom method, use it to do the copy. + // Avoids an allocation and a copy. + if rt, ok := dst.(io.ReaderFrom); ok { + return rt.ReadFrom(src) + } + // Similarly, if the reader has a WriteTo method, use it to do the copy. + if wt, ok := src.(io.WriterTo); ok { + return wt.WriteTo(dst) + } + buf := make([]byte, 32*1024) + for { + nr, er := src.Read(buf) + if nr > 0 { + // ---- Docker addition + if nr == 1 && buf[0] == '' { + nr, er = src.Read(buf) + if nr == 1 && buf[0] == '' { + if err := src.Close(); err != nil { + return 0, err + } + return 0, io.EOF + } + } + // ---- End of docker + nw, ew := dst.Write(buf[0:nr]) + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + if nr != nw { + err = io.ErrShortWrite + break + } + } + if er == io.EOF { + break + } + if er != nil { + err = er + break + } + } + return written, err +} From faa88436504e4a4a63ddb4f3736b11e721b760cd Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 4 Apr 2013 12:57:45 -0700 Subject: [PATCH 3/7] Look for the escape sequence only in tty mode --- container.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/container.go b/container.go index 5c4f8aa5f..bc5e0ab87 100644 --- a/container.go +++ b/container.go @@ -255,7 +255,11 @@ func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, s if container.Config.StdinOnce && !container.Config.Tty { defer cStdin.Close() } - _, err := CopyEscapable(cStdin, stdin) + if container.Config.Tty { + _, err = CopyEscapable(cStdin, stdin) + } else { + _, err = io.Copy(cStdin, stdin) + } if err != nil { Debugf("[error] attach stdin: %s\n", err) } From 8f41f1fa60587d77ad3ce2109fc03180488f49cc Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 4 Apr 2013 18:12:54 -0700 Subject: [PATCH 4/7] Remove unused optimization that could lead in loosing the escape sequence --- utils.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/utils.go b/utils.go index 398d6570b..4d87bffc1 100644 --- a/utils.go +++ b/utils.go @@ -344,15 +344,6 @@ func TruncateId(id string) string { // Code c/c from io.Copy() modified to handle escape sequence func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) { - // If the writer has a ReadFrom method, use it to do the copy. - // Avoids an allocation and a copy. - if rt, ok := dst.(io.ReaderFrom); ok { - return rt.ReadFrom(src) - } - // Similarly, if the reader has a WriteTo method, use it to do the copy. - if wt, ok := src.(io.WriterTo); ok { - return wt.WriteTo(dst) - } buf := make([]byte, 32*1024) for { nr, er := src.Read(buf) From 626bfd87a7eadd64b16fb02d547f75d1a8f94aa7 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Thu, 4 Apr 2013 18:13:43 -0700 Subject: [PATCH 5/7] Use integers instead of non-printable chars in the escape sequence detection --- utils.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/utils.go b/utils.go index 4d87bffc1..68e12b20b 100644 --- a/utils.go +++ b/utils.go @@ -349,9 +349,11 @@ func CopyEscapable(dst io.Writer, src io.ReadCloser) (written int64, err error) nr, er := src.Read(buf) if nr > 0 { // ---- Docker addition - if nr == 1 && buf[0] == '' { + // char 16 is C-p + if nr == 1 && buf[0] == 16 { nr, er = src.Read(buf) - if nr == 1 && buf[0] == '' { + // char 17 is C-q + if nr == 1 && buf[0] == 17 { if err := src.Close(); err != nil { return 0, err } From 72cef46e5e504355266b9c83bd3693d07a35d0ee Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Tue, 9 Apr 2013 07:44:44 -0700 Subject: [PATCH 6/7] Fix merge issue --- docker/docker.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docker/docker.go b/docker/docker.go index 7e1dfd00e..1b1c21990 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -56,15 +56,6 @@ func daemon() error { } func runCommand(args []string) error { - var oldState *term.State - var err error - if term.IsTerminal(int(os.Stdin.Fd())) && os.Getenv("NORAW") == "" { - oldState, err = term.MakeRaw(int(os.Stdin.Fd())) - if err != nil { - return err - } - defer term.Restore(int(os.Stdin.Fd()), oldState) - } // FIXME: we want to use unix sockets here, but net.UnixConn doesn't expose // CloseWrite(), which we need to cleanly signal that stdin is closed without // closing the connection. From 2e6a5bc7ee932b3d723ca4b0a319477310b12c34 Mon Sep 17 00:00:00 2001 From: "Guillaume J. Charmes" Date: Tue, 9 Apr 2013 10:14:18 -0700 Subject: [PATCH 7/7] Update README with escape sequence --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index ff86de882..c186d9a06 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,12 @@ docker pull base docker run -i -t base /bin/bash ``` +Detaching from the interactive shell +------------------------------------ +``` +# In order to detach without killing the shell, you can use the escape sequence Ctrl-p + Ctrl-q +# Note: this works only in tty mode (run with -t option). +``` Starting a long-running worker process --------------------------------------