New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ANSI terminal emulation for windows #10864

Closed
wants to merge 4 commits into
base: master
from

Conversation

Projects
None yet
10 participants
@sachin-jayant-joshi
Contributor

sachin-jayant-joshi commented Feb 17, 2015

Background

The Linux console implements a large subset of the VT102 and ECMA-48/ISO 6429/ANSI X3.64 terminal controls, plus certain private-mode sequences for changing the color palette, character-set mapping, and so on.

However similar standard is not natively available in Windows Console and therefore the various escape sequences are not interpreted and the output looks unformatted and garbled.

This change implements the functionality to emulate the Linux console escape sequences on Windows console by intercepting the terminal output and calling appropriate win32 functions to actualize the console control commands.

ANSI functionality implemented

• Translating keystroke events to appropriate ANSI sequences
• Text color and attributes
• Cursor movement
• Erasing parts of the screen

High Level Design

  • Implement io.Writer and io.ReadCloser interfaces in two structs terminalWriter and terminalReader.
    • When docker cli is initialized in NewDockerCli function, on windows we use these wrapper reader/writers. But for the other operating systems we continue to use standard reader/writers as before .
      • Under the hood these wrapper classes parse the incoming/outgoing byte streams, extract that ANSI command and call the appropriate APIs.
      • The rest of the docker code need not to know whether or how the byte streams are intercepted and interpreted.
  • The actual reading from and writing to the windows console is done in WindowsTerminal struct.
    • This functionality is exposed to terminalWriter and terminalReader through an interface terminalEmulator
      • HandleOutputCommand(command []byte) (n int, err error) : Takes appropriate action for the given ansi command
      • WriteChars(w io.Writer, p []byte) (n int, err error): Writes Characters to console output
      • ReadChars(w io.Reader, p []byte) (n int, err error): Reads Characters from keyboard
  • Much of the implementation of HandleOutputCommand involves first parsing the ANSI command, and then translating commands and their parameters to series of Win32 APIs calls.
  • The output is buffered to accommodate when the command sequences are divided between more than one consecutive write calls.
  • Similarly while reading keystokes, all available input events are “consumed” by first translating them to appropriate ANSI sequences , but returning only the requested number of bytes and buffering the rest.

Limitations

The approach depends on re-implementing the entire ANSI and other standards that Linux implements.
This is not practical and therefore just enough functionality is implemented to make it usable for 90% of the cases.

Known Issues

  • Terminal size issues
    • Terminal resize is not supported.
    • The host always thinks that the terminal is 80 x 24. This code does not attempt to configure it to match console size on windows.
  • The RBG values for basic ansi color palette cannot be selected.
  • Simulating Linux signals and Interrupt handling is not implemented.

References

  1. Manual for Linux console : http://man7.org/linux/man-pages/man4/console_codes.4.html
  2. Documentation for windows console functionality : https://msdn.microsoft.com/en-us/library/windows/desktop/ms682087(v=vs.85).aspx

cc: @shykes @icecrime @vieux @tiborvass @ahmetalpbalkan @johngossman

@tiborvass

This comment has been minimized.

Collaborator

tiborvass commented Feb 18, 2015

<3

@SvenDowideit

This comment has been minimized.

Contributor

SvenDowideit commented Feb 18, 2015

💃

@ahmetb

This comment has been minimized.

Contributor

ahmetb commented Feb 18, 2015

this is Dr. Nyan Cat, and this PR is terminal...

@nathanleclaire

This comment has been minimized.

Contributor

nathanleclaire commented Feb 19, 2015

👏 👏 👏

type Terminal struct {
StdOut io.Writer
StdErr io.Writer
StdIn io.ReadCloser

This comment has been minimized.

@tiborvass

tiborvass Feb 19, 2015

Collaborator

Maybe we should rename Terminal to Stdio. This is not a terminal in the TTY sense.
Ping @sachin-jayant-joshi @ahmetalpbalkan

This comment has been minimized.

@tiborvass

tiborvass Feb 20, 2015

Collaborator

@sachin-jayant-joshi I would like to make sure that we don't translate ANSI stuff if stdout is not a tty, and have a test for that.

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Feb 20, 2015

Contributor

I agree that it could be a better name.

VT1002 was an actual terminal and There are many places where VT100 and ANSI are used synonymously.
In fact http://en.wikipedia.org/wiki/VT100
"The VT100 is a video terminal, introduced in August 1978 ....
...This led to rapid uptake of the ANSI standard, becoming the de facto standard for terminal emulators."

I think for the programmers coming from c/c++ background (like me), the name stdio has a specific connotation. Here I want to capture the concept of bundling together of stdin+stdout+stderr so used the name Terminal.

How about "Console" ?

A slight diversion --
(A bit of interesting history .."The Linux console was one of the first features of the kernel and was originally written by Linus Torvalds in 1991[4]"
http://en.wikipedia.org/wiki/Linux_console
http://en.wikipedia.org/wiki/Linux_console#Control_characters

This comment has been minimized.

@ahmetb

ahmetb Feb 20, 2015

Contributor

@sachin-jayant-joshi can we please write an integration-cli test case that verifies if docker run ubuntu grep root /etc/passwd | cat -e is exactly the same as the one we get on linux? @tibor suggested this on IRC.

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Feb 20, 2015

Contributor

@tiborvass I agree, I will double check that when stdout is redirected the escape sequence is not filtered out.
@ahmetalpbalkan Lets figure out a way to implement this test on Windows as windows does not have cat.

This comment has been minimized.

@ahmetb

ahmetb Feb 20, 2015

Contributor

@sachin-jayant-joshi our CI tests are running inside msys bash, it does have cat. We should be able to implement it. +1 on this, it's better to have a test case because someone might modify it by not realizing the possible outcome unless it's not covered with a test.

@jessfraz

This comment has been minimized.

Contributor

jessfraz commented Feb 21, 2015

So for fun we tried to run a container with a nyan cat in cmd.exe

docker run -it --rm supertest2014/nyan

http://pastebin.com/daJEzAK0

we do know that it may not work for the nyan cat ;)

@jessfraz

This comment has been minimized.

Contributor

jessfraz commented Feb 21, 2015

but colors on ls work ;) so yayyyyyyyy!

same with ^A, ^E, ^R, ^W YAYYYYYY LGTM

@sachin-jayant-joshi sachin-jayant-joshi force-pushed the sachin-jayant-joshi:windows-tty branch 2 times, most recently from 399a4bd to 699cc53 Feb 25, 2015

@sachin-jayant-joshi

This comment has been minimized.

Contributor

sachin-jayant-joshi commented Feb 25, 2015

amended the commit with the changes requested.

  • removed the terminal struct,
  • checking if stream is redirected or not
  • fixed some issues with nyan (fixed bufferio panic),
  • In addition using channel for buffering keystrokes.
@tiborvass

This comment has been minimized.

Collaborator

tiborvass commented Feb 25, 2015

What do you guys think of:

// +build windows

package main

func init() {
  os.Stdin = term.Convert(os.Stdin)
  os.Stdout = term.Convert(os.Stdout)
  os.Stderr = term.Convert(os.Stderr)
}

Ping @tianon

@jessfraz

This comment has been minimized.

Contributor

jessfraz commented Feb 25, 2015

ooooo that looks nice

On Wed, Feb 25, 2015 at 1:26 PM, Tibor Vass notifications@github.com
wrote:

What do you guys think of:

// +build windowspackage main
func init() {
os.Stdin = term.Convert(os.Stdin)
os.Stdout = term.Convert(os.Stdout)
os.Stderr = term.Convert(os.Stderr)
}

Ping @tianon https://github.com/tianon


Reply to this email directly or view it on GitHub
#10864 (comment).

@ahmetb

This comment has been minimized.

Contributor

ahmetb commented Feb 25, 2015

@tiborvass +1 for the idea of wrapping but I hate the init() part, it makes things pretty undiscoverable. (something magically executed and had some global side effects in the program at some point in the history) I just prefer passing wrapped streams to the term methods.

@sachin-jayant-joshi awesome thanks! it's no big deal but I'm having some issues which we should probably address in next PRs.

  • CTRL+left/right arr keys on docker run -it ubuntu bash print 5D/5C to screen rather than moving the cursor.
  • Pressing Home key and keep typing behaves like INS but when I press End key, the text gets corrected just like it wasn't in Insert mode.
  • Running docker.exe resizes my terminal to 80x26 without asking (kinda frustrating if we can avoid, I'm even file with showing a 80x26 output in a bigger term window)
VK_INSERT: "\x1B[2%s~",
VK_DELETE: "\x1B[3%s~",
VK_PRIOR: "\x1B[5%s~",
VK_NEXT: "\x1B[6%s~",

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Feb 25, 2015

Contributor

The keys are supposed to be translated to a specific escape sequences.
If any of the keys is not working as expected then perhaps mapping is wrong

As for the specific translation of keystrokes for arrows - the vi specifically asks for sending application mapping and that is what we send.
Can I take this as a specific bug and fix it later in a smaller PR for bug fixes?

handler.screenBufferInfo = screenBufferInfo
}
// Set the window size

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Feb 25, 2015

Contributor

This is a temp solution to the problem where the terminal escape sequence that we send to host does not change the term size on the host. So instead we adjust the client size to match the size host thinks client is.

This is obviously temporary hack.
Can we track this separately as an Issue ?

@tiborvass

This comment has been minimized.

Collaborator

tiborvass commented Mar 4, 2015

@sachin-jayant-joshi

When I use docker -D version with an incorrect DOCKER_HOST, I get a FATA error, but that FATA is supposed to be red. It is red if I do docker version though.

Z:\windows>docker-1.5.0-dev.exe version
Client version: 1.5.0-dev
Client API version: 1.18
Go version (client): go1.4.1
Git commit (client): 699cc53
OS/Arch (client): windows/amd64
FATA[0000] An error occurred trying to connect: Get https:///var/run/docker.sock/v1.18/version: dial unix /var/run/docker.sock: An address incompatible with the requested protocol was used.

Z:\windows>docker-1.5.0-dev.exe -D version
Client version: 1.5.0-dev
Client API version: 1.18
Go version (client): go1.4.1
Git commit (client): 699cc53
OS/Arch (client): windows/amd64
←[31mFATA←[0m[0000] An error occurred trying to connect: Get https:///var/run/docker.sock/v1.18/version: dial unix /var/run/docker.sock: An address incompatible with the requested protocol was used.

The reason for that is because in docker/log.go we reset the log output to os.Stderr. Not sure what would be the best refactoring but some refactoring is needed.

Also: it would be really nice to have the ANSI translations for repositioning the cursor, so that docker pull busybox the first time, shows the progress bar.

When I tried to use vi, it gave me a weird error.

Z:\windows>docker-1.5.0-dev.exe run -it busybox vi
FATA[0000] The parameter is incorrect.

Z:\windows>docker-1.5.0-dev.exe -D run -it busybox vi
←[34mDEBU←[0m[0000] [hijack] End of stdout
←[34mDEBU←[0m[0000] Error receiveStdout: The parameter is incorrect.
←[34mDEBU←[0m[0000] Error hijack: The parameter is incorrect.
←[34mDEBU←[0m[0000] End of CmdRun(), Waiting for hijack to finish.
←[31mFATA←[0m[0000] The parameter is incorrect.

And finally, I managed to get a panic I don't really know how (tried to reproduce it, I manage to do it quite often, but could not find a consistent way): I do something like l, then Tab (couple of times) and then Ctrl+D. Can be a variation of those.

/ # lunexpected fault address 0xffffffffffffffff
fatal error: fault
[signal 0xc0000005 code=0x0 addr=0xffffffffffffffff pc=0x411271]

goroutine 12 [running]:
runtime.gothrow(0x88bf90, 0x5)
        /usr/local/go/src/runtime/panic.go:503 +0x8e fp=0xc0820937d8 sp=0xc0820937c0
runtime.sigpanic()
        /usr/local/go/src/runtime/os_windows.go:45 +0x118 fp=0xc0820937f8 sp=0xc0820937d8
runtime.mallocgc(0x4, 0x0, 0x3, 0xc0820e000c)
        /usr/local/go/src/runtime/malloc.go:152 +0x201 fp=0xc0820938a8 sp=0xc0820937f8
runtime.rawstring(0x4, 0x0, 0x0, 0x0, 0x0, 0x0)
        /usr/local/go/src/runtime/string.go:195 +0x9a fp=0xc0820938d8 sp=0xc0820938a8
runtime.intstring(0x20, 0x0, 0x0)
        /usr/local/go/src/runtime/string.go:149 +0x40 fp=0xc082093938 sp=0xc0820938d8
github.com/docker/docker/pkg/term.clearDisplayRect(0x24, 0x900000007db20, 0x4b007800490077, 0x73005c7b01, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/console_windows.go:432 +0x120 fp=0xc0820939e0 sp=0xc082093938
github.com/docker/docker/pkg/term.clearDisplayRange(0x24, 0x800050007a020, 0x4b0078004a0077, 0x0, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/console_windows.go:477 +0x34f fp=0xc082093a50 sp=0xc0820939e0
github.com/docker/docker/pkg/term.(*WindowsTerminal).HandleOutputCommand(0xc08209a700, 0xc082074000, 0x3, 0x100, 0x0, 0x                                                         0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/console_windows.go:778 +0xd66 fp=0xc082093c30 sp=0xc082093a50
github.com/docker/docker/pkg/term.(*terminalWriter).Write(0xc08209a800, 0xc082087000, 0xdd, 0x1000, 0xdd, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/term_emulator.go:94 +0x3c9 fp=0xc082093d48 sp=0xc082093c30
bufio.(*Reader).writeBuf(0xc08203a780, 0x1d22d8, 0xc08209a800, 0xdd, 0x0, 0x0)
        /usr/local/go/src/bufio/bufio.go:463 +0xac fp=0xc082093db0 sp=0xc082093d48
bufio.(*Reader).WriteTo(0xc08203a780, 0x1d22d8, 0xc08209a800, 0x16f5, 0x0, 0x0)
        /usr/local/go/src/bufio/bufio.go:444 +0x230 fp=0xc082093e28 sp=0xc082093db0
io.Copy(0x1d22d8, 0xc08209a800, 0x1d4730, 0xc08203a780, 0x0, 0x0, 0x0)
        /usr/local/go/src/io/io.go:354 +0xb9 fp=0xc082093ee0 sp=0xc082093e28
github.com/docker/docker/api/client.func┬╖023(0x0, 0x0)
        /go/src/github.com/docker/docker/api/client/hijack.go:210 +0x1e0 fp=0xc082093fa8 sp=0xc082093ee0
github.com/docker/docker/pkg/promise.func┬╖001()
        /go/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x36 fp=0xc082093fe0 sp=0xc082093fa8
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc082093fe8 sp=0xc082093fe0
created by github.com/docker/docker/pkg/promise.Go
        /go/src/github.com/docker/docker/pkg/promise/promise.go:9 +0x102

goroutine 1 [chan receive]:
github.com/docker/docker/api/client.(*DockerCli).CmdRun(0xc0820fc280, 0xc0820061e0, 0x2, 0x2, 0x0, 0x0)
        /go/src/github.com/docker/docker/api/client/commands.go:2359 +0xe56
reflect.callMethod(0xc0820f4ff0, 0xc08203dbe8)
        /usr/local/go/src/reflect/value.go:605 +0x180
reflect.methodValueCall(0xc0820061e0, 0x2, 0x2, 0x1, 0xc0820f4ff0, 0xc0820fc301, 0xc0820e81b0, 0x5, 0xc08209a800, 0x1c5e                                                         e0, ...)
        /usr/local/go/src/reflect/asm_amd64.s:29 +0x3d
github.com/docker/docker/api/client.(*DockerCli).Cmd(0xc0820fc280, 0xc0820061d0, 0x3, 0x3, 0x0, 0x0)
        /go/src/github.com/docker/docker/api/client/cli.go:81 +0x385
main.main()
        /go/src/github.com/docker/docker/docker/docker.go:133 +0xb6d

goroutine 5 [syscall]:
os/signal.loop()
        /usr/local/go/src/os/signal/signal_unix.go:21 +0x26
created by os/signal.init┬╖1
        /usr/local/go/src/os/signal/signal_unix.go:27 +0x3c

goroutine 11 [chan receive]:
github.com/docker/docker/api/client.(*DockerCli).hijack(0xc0820fc280, 0x885c50, 0x4, 0xc08206c800, 0x76, 0x1, 0x1d2330,                                                          0xc08209a880, 0x1d22d8, 0xc08209a800, ...)
        /go/src/github.com/docker/docker/api/client/hijack.go:237 +0xd38
github.com/docker/docker/api/client.func┬╖014(0x0, 0x0)
        /go/src/github.com/docker/docker/api/client/commands.go:2326 +0x1fd
github.com/docker/docker/pkg/promise.func┬╖001()
        /go/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x36
created by github.com/docker/docker/pkg/promise.Go
        /go/src/github.com/docker/docker/pkg/promise/promise.go:9 +0x102
fold               lspci              setconsole         xzcat
goroutine 9 [IO wait]:sb              setkeycodes        yes
net.(*pollDesc).Wait(0xc0820662f0, 0x72, 0x0, 0x0)       zcat
        /usr/local/go/src/net/fd_poll_runtime.go:84 +0x4e
net.(*ioSrv).ExecIO(0xc0820382d0, 0xc0820661e0, 0x8883f0, 0x7, 0x965258, 0x5d, 0x0, 0x0)
        /usr/local/go/src/net/fd_windows.go:188 +0x305
net.(*netFD).Read(0xc082066180, 0xc08202ac00, 0x400, 0x400, 0x0, 0x0, 0x0)
        /usr/local/go/src/net/fd_windows.go:470 +0x180
net.(*conn).Read(0xc0820382e8, 0xc08202ac00, 0x400, 0x400, 0x0, 0x0, 0x0)
        /usr/local/go/src/net/net.go:121 +0xe3
crypto/tls.(*block).readFromUntil(0xc0820f4bd0, 0x1d3db0, 0xc0820382e8, 0x5, 0x0, 0x0)
        /usr/local/go/src/crypto/tls/conn.go:454 +0xed
crypto/tls.(*Conn).readRecord(0xc0820842c0, 0x17, 0x0, 0x0)
        /usr/local/go/src/crypto/tls/conn.go:539 +0x2e1
crypto/tls.(*Conn).Read(0xc0820842c0, 0xc08200f000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
        /usr/local/go/src/crypto/tls/conn.go:904 +0x16d
net/http.noteEOFReader.Read(0x1d44f0, 0xc0820842c0, 0xc0820b81b8, 0xc08200f000, 0x1000, 0x1000, 0xc0820fe4c0, 0x0, 0x0)
        /usr/local/go/src/net/http/transport.go:1270 +0x75
net/http.(*noteEOFReader).Read(0xc082068640, 0xc08200f000, 0x1000, 0x1000, 0xc082012000, 0x0, 0x0)
        <autogenerated>:125 +0xdb
bufio.(*Reader).fill(0xc08203a900)
        /usr/local/go/src/bufio/bufio.go:97 +0x1d5
bufio.(*Reader).Peek(0xc08203a900, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0)
        /usr/local/go/src/bufio/bufio.go:132 +0xf7
net/http.(*persistConn).readLoop(0xc0820b8160)
        /usr/local/go/src/net/http/transport.go:842 +0xab
created by net/http.(*Transport).dialConn
        /usr/local/go/src/net/http/transport.go:660 +0xca6

goroutine 10 [select]:
net/http.(*persistConn).writeLoop(0xc0820b8160)
        /usr/local/go/src/net/http/transport.go:945 +0x424
created by net/http.(*Transport).dialConn
        /usr/local/go/src/net/http/transport.go:661 +0xcc3

goroutine 13 [runnable, locked to thread]:
syscall.(*Proc).Call(0xc0820fa160, 0xc082107c70, 0x4, 0x4, 0x2, 0x759b80, 0x0, 0x0)
        /usr/local/go/src/syscall/dll_windows.go:136 +0x5c2
syscall.(*LazyProc).Call(0xc08206a330, 0xc082107c70, 0x4, 0x4, 0xc0820ba000, 0x965498, 0x0, 0x0)
        /usr/local/go/src/syscall/dll_windows.go:279 +0x74
github.com/docker/docker/pkg/term.readConsoleInputKey(0x20, 0xc0820ba000, 0x400, 0x400, 0x400, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/console_windows.go:543 +0xf4
github.com/docker/docker/pkg/term.getAvailableInputEvents(0x0, 0x0, 0x0, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/console_windows.go:1039 +0xff
github.com/docker/docker/pkg/term.(*WindowsTerminal).ReadChars(0xc08209a700, 0x1d23b0, 0xc082038000, 0xc0820ec000, 0x800                                                         0, 0x8000, 0x0, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/console_windows.go:1080 +0x13f
github.com/docker/docker/pkg/term.(*terminalReader).Read(0xc08209a880, 0xc0820ec000, 0x8000, 0x8000, 0x1, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/term_emulator.go:151 +0x13b
io.Copy(0x1d4ab8, 0xc0820fa320, 0x1d4bd8, 0xc08209a880, 0x16, 0x0, 0x0)
        /usr/local/go/src/io/io.go:362 +0x1fd
github.com/docker/docker/api/client.func┬╖024(0x0, 0x0)
        /go/src/github.com/docker/docker/api/client/hijack.go:221 +0xe3
github.com/docker/docker/pkg/promise.func┬╖001()
        /go/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x36
created by github.com/docker/docker/pkg/promise.Go
        /go/src/github.com/docker/docker/pkg/promise/promise.go:9 +0x102

Also I wonder whether we should emulate the ANSI console if os.Getenv("TERM") == "cygwin" (cc @ahmetalpbalkan @tianon )

@sachin-jayant-joshi

This comment has been minimized.

Contributor

sachin-jayant-joshi commented Mar 4, 2015

@tiborvass Thanks a lot for looking into this and trying it out !!
I will try to repro the rest of the issues and fix them asap. (And do some more thorough testing as well)

[1] I agree that people will always use os.stderr or os.stdout etc directly and that we need global singleton objects that wrap ANSI functionality. I wish I could just go back and fix os package.
But in the mean time I think we can still use something likeglobal vars in term pkg : term.Stderr, term.Stdin, term.Stdout .

[2] parameter is incorrect - I think it is unlikely that this particular error is caused by ANSI code per se, will double check if it still repros without any ANSI processing.
[3] Panic is always bad - will try to repro and fix that. And thanks for finding it !!
[4] Regarding the Cygwin - I am not sure I completely understood what is the expected behavior.

cc @ahmetalpbalkan @tianon

@tianon

This comment has been minimized.

Member

tianon commented Mar 4, 2015

@tiborvass

This comment has been minimized.

Collaborator

tiborvass commented Mar 4, 2015

@sachin-jayant-joshi @tianon

I still think we should reset os.Stdout etc. on windows:

package main

import (
    "bufio"
    "fmt"
    "os"
)

var ch = make(chan struct{})

func init() {
    stdoutR, stdoutW, err := os.Pipe()
    if err != nil {
        panic(err)
    }

    os.Stdout = stdoutW

    go func() {
        scanner := bufio.NewScanner(stdoutR)
        for scanner.Scan() {
            line := scanner.Text()
            fmt.Fprintln(os.Stderr, "modified:", line)
        }
        close(ch)
    }()
}

func main() {
    defer func() {
        os.Stdout.Close()
        <-ch
    }()
    fmt.Println("Hello world")
}

Something like above. We would need to do that for Stdin and Stderr as well. We would also need to make sure IsTerminal(os.Stdout) returns true after modifying os.Stdout.

@tiborvass

This comment has been minimized.

Collaborator

tiborvass commented Mar 4, 2015

@sachin-jayant-joshi @tianon what do you guys think of using this api instead https://gist.github.com/tiborvass/6d8e8662118dec4ca2ba

I think that will be the perfect API.

@sachin-jayant-joshi

This comment has been minimized.

Contributor

sachin-jayant-joshi commented Mar 4, 2015

ok. I'll try that out.

// Save current screen buffer info
handle, _ := syscall.GetStdHandle(STD_OUTPUT_HANDLE)
screenBufferInfo, err := GetConsoleScreenBufferInfo(uintptr(handle))

This comment has been minimized.

@tiborvass

tiborvass Mar 4, 2015

Collaborator

what happens if err != nil ?

This comment has been minimized.

@tiborvass

tiborvass Mar 4, 2015

Collaborator

@sachin-jayant-joshi Also, this seems to be only necessary for HandleOutputCommand so I would put this under the IsTerminal(os.Stdout.Fd()) condition. Let me know if I'm mistaken!

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Mar 4, 2015

Contributor

ok. That call can be probably avoided in case both stdout and stderr are redirected

This is also same - the value is used during clear display functionality. We need to use the console color that we started with to implement it. Eg Yellow foreground on blue background ..when you call "clear" on shell then need to use those colors. I think the problem might be that the method signature uses func (term *WindowsTerminal)

// Calls the graphics functions specified by the following values.
// These specified functions remain active until the next occurrence of this escape sequence.
// Graphics mode changes the colors and attributes of text (such as bold and underline) displayed on the screen.
screenBufferInfo, err := GetConsoleScreenBufferInfo(uintptr(handle))

This comment has been minimized.

@tiborvass

tiborvass Mar 4, 2015

Collaborator

@sachin-jayant-joshi why do you need a new buffer here? Why can't you use term.screenBufferInfo ?

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Mar 4, 2015

Contributor

short answer - term.screenBufferInfo. is used to remember the value so we can implement ANSI_ATTR_RESET functionality, on the other hand because most of the attribute additions are additive which mean we need to OR the new bit flag with the old value and then set the new value

// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683231(v=vs.85).aspx
STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12

This comment has been minimized.

@tiborvass

tiborvass Mar 4, 2015

Collaborator

these are already defined in the syscall package, let's try to reuse as much as possible from the syscall package.

FOREGROUND_RED = 4
FOREGROUND_INTENSITY = 8
FOREGROUND_MASK_SET = 0x000F
FOREGROUND_MASK_UNSET = 0xFFF0

This comment has been minimized.

@tiborvass

tiborvass Mar 4, 2015

Collaborator

@sachin-jayant-joshi I don't want to export all these from pkg/term. Maybe we could do pkg/term/ansi for ansi-specific things, and pkg/term/console for windows-specific things? If @tianon is okay we could import it with import . "github.com/.../pkg/term/console" eliminating the need for a verbose console. prefix.

That way, FOREGROUND_BLUE in the code would be windows-specific (from pkg/term/console), and ansi.FOREGROUND_BLUE would be ansi-specific.

This comment has been minimized.

@ahmetb

ahmetb Mar 4, 2015

Contributor

this whole windows implementation probably should go to a different pkg like pkg/term/winterm or /ansi as you said.

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Mar 6, 2015

Contributor

moved to term/winconsole

// types for calling various windows API
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
type (

This comment has been minimized.

@tiborvass

tiborvass Mar 4, 2015

Collaborator

please rename the structs to CamelCase (exporting them: e.g. SmallRect) and make the other types internal: e.g. s/SHORT/short/.

This comment has been minimized.

@tiborvass

tiborvass Mar 4, 2015

Collaborator

Or maybe this should also go to the pkg/term/console package mentioned above

This comment has been minimized.

@sachin-jayant-joshi
)
// Implements the TerminalEmulator interface
type WindowsTerminal struct {

This comment has been minimized.

@tiborvass

tiborvass Mar 4, 2015

Collaborator

no need to export this for now.

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Mar 4, 2015

Contributor

http://play.golang.org/p/YmtxqGhorH
prog.go:32: cannot use f (type *MyFile) as type *File in argument to Hello

With respect to re-initializing the Stdin etc . The variables are of type File* which is a concrete type and not an interface.

I am having hard time reinitializing them in a polymorphic way.
Here are some of my unsuccessful attempts

http://play.golang.org/p/TDxLBEYtE5

prog.go:22: cannot use wr (type *MyWriter) as type *os.File in assignment

http://play.golang.org/p/LtKvOFkbbi

prog.go:50: cannot use NewDerived1() (type *Derived1) as type *Base in assignment
prog.go:51: cannot use NewDerived2() (type *Derived2) as type *Base in assignment

http://play.golang.org/p/EHf4rtIEch

prog.go:47: cannot convert NewDerived1() (type *Derived1) to type PBASE
prog.go:48: cannot convert NewDerived2() (type *Derived2) to type PBASE
prog.go:53: arr[0].Foo undefined (type PBASE has no field or method Foo)
prog.go:54: arr[1].Foo undefined (type PBASE has no field or method Foo)
prog.go:55: arr[2].Foo undefined (type PBASE has no field or method Foo)

sachin-jayant-joshi added some commits Mar 6, 2015

Move windows console specific implementation in sub package
Signed-off-by: Sachin Joshi <sachin_jayant_joshi@hotmail.com>
Use syscall consts, check for errors,
Also rename func for non-windows specific names.

Signed-off-by: Sachin Joshi <sachin_jayant_joshi@hotmail.com>
// Set terminal emulation based on platform as required.
stdout, stderr, stdin := term.StdStreams()
initLogging(stderr)

This comment has been minimized.

@icecrime

icecrime Mar 18, 2015

Contributor

Just a question, but should be keep reexec.Init() the first thing? Some of the use of reexec being security related, I'm wondering if it wouldn't be safer to call the less possible code between main() invocation and actual reexec.

Any thoughts @tiborvass?

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Mar 18, 2015

Contributor

@icecrime
I am not familiar with how reexec works. But my impression was that in case of the errors some of the initializers might write to the stderr. That was one reason why setting terminal and log output is done as soon as we can.

This comment has been minimized.

@icecrime

icecrime Mar 18, 2015

Contributor

You're right, but I'm not sure it's desirable.

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

I agree with icecrime, I would vote for putting this after the reexec.Init().

Fix panic with vi in busybox
Following bugs are fixed:
1.Handle out of bound cursor movements: vi in busybox sets cursor
to (999,999) expecting it to be set to right, bottom correctly.

2.Correctly determine redirected non-terminal file.

Signed-off-by: Sachin Joshi <sachin_jayant_joshi@hotmail.com>

@sachin-jayant-joshi sachin-jayant-joshi force-pushed the sachin-jayant-joshi:windows-tty branch from cd22a4d to cb7a65c Mar 18, 2015

@icecrime

This comment has been minimized.

Contributor

icecrime commented Mar 18, 2015

I can confirm that the last commit properly fixes vi in busybox 👍

@ahmetb

This comment has been minimized.

Contributor

ahmetb commented Mar 18, 2015

awesome, thanks @sachin-jayant-joshi

@tiborvass

This comment has been minimized.

Collaborator

tiborvass commented Mar 19, 2015

Great job @sachin-jayant-joshi ! vi works indeed.

It seems I had a little harder time reproducing the panic I mentioned earlier, but still doing something similar, I managed to get the following:

/ # s
sed                setsid             sort               swapoff
seq                sh                 ss                 swapon
setarch            sha1sum            start-stop-daemon  switch_root
setconsole         sha256sum          strings            sync
setkeycodes        sha3sum            stty               sysctl
setlogcons         sha512sum          su                 syslogd
setserial          sleep              sulogin
/ # s
sed                setsid             sort               swapoff
seq                sh                 ss                 swapon
setarch            sha1sum            start-stop-daemon  switch_root
setconsole         sha256sum          strings            sync
setkeycodes        sha3sum            stty               sysctl
setlogcons         sha512sum          su                 syslogd
setserial          sleep              sulogin
/ # runtime: garbage collector found invalid heap pointer *(0xc08207ecd0+0x0)=0xc0820
70000 span=0xc082070000-0xc082071f80-0xc082072000 state=2
runtime: found *(0xc0820f0900+0x20) = 0xc08207ecd0+0x0
runtime: found *(0xc08208e480+0x268) = 0xc0820f0900+0x0
runtime: found *(0xc08207ee20+0x0) = 0xc08208e480+0x0
runtime: found *(0xc0820df420+0x0) = 0xc08207ee20+0x0
runtime: found *(0xc0820d0400+0x48) = 0xc0820df420+0x0
runtime: found *(0xc0820dc160+0x48) = 0xc0820d0400+0x0
runtime: found *(0xc0820ae540+0x10) = 0xc0820dc1b8+0xffffffffffffffa8
runtime: found *(0xc082127d18+0x0) = 0xc0820ae540+0x0
fatal error: bad pointer

runtime stack:
runtime.throw(0xabbfb9)
        /usr/local/go/src/runtime/panic.go:491 +0xad fp=0x3df9e8 sp=0x3df9b8
scanblock(0xc082127d18, 0x38, 0x94ca34)
        /usr/local/go/src/runtime/mgc0.c:415 +0x990 fp=0x3dfb28 sp=0x3df9e8
scanframe(0x3dfc30, 0x0, 0x1)
        /usr/local/go/src/runtime/mgc0.c:743 +0x1c9 fp=0x3dfb98 sp=0x3dfb28
runtime.gentraceback(0x418f7c, 0xc082127378, 0x0, 0xc08202c7e0, 0x0, 0x0, 0x7fffffff,
 0x3dfce0, 0x0, 0x0, ...)
        /usr/local/go/src/runtime/traceback.go:311 +0x7af fp=0x3dfc88 sp=0x3dfb98
scanstack(0xc08202c7e0)
        /usr/local/go/src/runtime/mgc0.c:780 +0x22a fp=0x3dfcf8 sp=0x3dfc88
markroot(0xc082010000, 0xc00000000c)
        /usr/local/go/src/runtime/mgc0.c:556 +0xee fp=0x3dfd58 sp=0x3dfcf8
runtime.parfordo(0xc082010000)
        /usr/local/go/src/runtime/parfor.c:76 +0xb9 fp=0x3dfdd8 sp=0x3dfd58
gc(0x3dff10)
        /usr/local/go/src/runtime/mgc0.c:1442 +0x26c fp=0x3dfef0 sp=0x3dfdd8
runtime.gc_m()
        /usr/local/go/src/runtime/mgc0.c:1376 +0x11a fp=0x3dff28 sp=0x3dfef0
runtime.onM(0xc082012000)
        /usr/local/go/src/runtime/asm_amd64.s:257 +0x6d fp=0x3dff30 sp=0x3dff28
runtime.mstart()
        /usr/local/go/src/runtime/proc.c:818 fp=0x3dff38 sp=0x3dff30

goroutine 12 [garbage collection]:
runtime.switchtoM()
        /usr/local/go/src/runtime/asm_amd64.s:198 fp=0xc0821257d0 sp=0xc0821257c8
runtime.gogc(0x0)
        /usr/local/go/src/runtime/malloc.go:469 +0x1d6 fp=0xc082125808 sp=0xc0821257d
0
runtime.mallocgc(0x5000, 0x81d740, 0xc000000001, 0x4b005500000000)
        /usr/local/go/src/runtime/malloc.go:341 +0x398 fp=0xc0821258b8 sp=0xc08212580
8
runtime.newarray(0x81d740, 0x12ed, 0x1c0000)
        /usr/local/go/src/runtime/malloc.go:365 +0xc8 fp=0xc0821258f0 sp=0xc0821258b8

runtime.makeslice(0x7576a0, 0x12ed, 0x12ed, 0x0, 0x0, 0x0)
        /usr/local/go/src/runtime/slice.go:32 +0x163 fp=0xc082125938 sp=0xc0821258f0
github.com/docker/docker/pkg/term/winconsole.clearDisplayRect(0x24, 0x700000020, 0x55
004900540011, 0xc08207004b, 0x5100000001, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/winconsole/console_windows.go:442 +
0xd8 fp=0xc0821259c8 sp=0xc082125938
github.com/docker/docker/pkg/term/winconsole.clearDisplayRange(0x24, 0x4000700000020,
 0x55004a00540010, 0x4b, 0x0, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/winconsole/console_windows.go:489 +
0x362 fp=0xc082125a40 sp=0xc0821259c8
github.com/docker/docker/pkg/term/winconsole.(*WindowsTerminal).HandleOutputCommand(0
xc0820b08c0, 0x24, 0xc08207a300, 0x3, 0x100, 0x0, 0x0, 0x0)rings
        /go/src/github.com/docker/docker/pkg/term/winconsole/console_windows.go:799 +
0xdf3 fp=0xc082125c28 sp=0xc082125a40 mount              su
github.com/docker/docker/pkg/term/winconsole.(*terminalWriter).Write(0xc08200ab40, 0x
c082111000, 0x4, 0x1000, 0x1c5, 0x0, 0x0)                swapoff
        /go/src/github.com/docker/docker/pkg/term/winconsole/term_emulator.go:96 +0x3
cf fp=0xc082125d48 sp=0xc082125c28    nameif             switch_root
bufio.(*Reader).writeBuf(0xc0820ee480, 0x1d22d8, 0xc08200ab40, 0x1c5, 0x0, 0x0)
        /usr/local/go/src/bufio/bufio.go:463 +0xac fp=0xc082125db0 sp=0xc082125d48
bufio.(*Reader).WriteTo(0xc0820ee480, 0x1d22d8, 0xc08200ab40, 0x2422, 0x0, 0x0)
        /usr/local/go/src/bufio/bufio.go:444 +0x230 fp=0xc082125e28 sp=0xc082125db0
io.Copy(0x1d22d8, 0xc08200ab40, 0x1d4680, 0xc0820ee480, 0x0, 0x0, 0x0)
        /usr/local/go/src/io/io.go:354 +0xb9 fp=0xc082125ee0 sp=0xc082125e28
github.com/docker/docker/api/client.func┬╖024(0x0, 0x0)  tee
        /go/src/github.com/docker/docker/api/client/hijack.go:210 +0x1e0 fp=0xc082125
fa8 sp=0xc082125ee0ip                 passwd             test
github.com/docker/docker/pkg/promise.func┬╖001()         tftp
        /go/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x36 fp=0xc082125f
e0 sp=0xc082125fa8 ipcs               ping               top
runtime.goexit()   iplink             pipe_progress      touch
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc082125fe8 sp=0xc082125f
e0t                iprule             poweroff           traceroute
created by github.com/docker/docker/pkg/promise.Go       true
        /go/src/github.com/docker/docker/pkg/promise/promise.go:9 +0x102
dd                 iptables-save      ps                 udhcpc
goroutine 1 [chan receive]:-xml       pwd                umount
runtime.gopark(0x439680, 0xc0820ee7d8, 0x8b8650, 0xc)    uname
        /usr/local/go/src/runtime/proc.go:130 +0x10c fp=0xc0820195d8 sp=0xc0820195a8
runtime.goparkunlock(0xc0820ee7d8, 0x8b8650, 0xc)        unix2dos
        /usr/local/go/src/runtime/proc.go:136 +0x4f fp=0xc082019600 sp=0xc0820195d8
runtime.chanrecv(0x75a520, 0xc0820ee780, 0xc082019988, 0x1, 0x0)
        /usr/local/go/src/runtime/chan.go:467 +0x833 fp=0xc0820196a0 sp=0xc082019600
runtime.chanrecv1(0x75a520, 0xc0820ee780, 0xc082019988)  uptime
        /usr/local/go/src/runtime/chan.go:311 +0x2b fp=0xc0820196d0 sp=0xc0820196a0
github.com/docker/docker/api/client.(*DockerCli).CmdRun(0xc0820aca00, 0xc08200a1b0, 0
x3, 0x3, 0x0, 0x0) linux32            rmdir              uuencode
        /go/src/github.com/docker/docker/api/client/commands.go:2476 +0xfc9 fp=0xc082
019af8 sp=0xc0820196d0                route              vi
runtime.call64(0x88cda8, 0xc0820f13b0, 0x2000000030)     vlock
        /usr/local/go/src/runtime/asm_amd64.s:403 +0x4c fp=0xc082019b40 sp=0xc082019a
f8ect              loadkmap           rtacct             watchdog
reflect.callMethod(0xc0820f1350, 0xc082019be8)           wc
        /usr/local/go/src/reflect/value.go:605 +0x180 fp=0xc082019bd0 sp=0xc082019b40
expr               logname            rtstat             which
reflect.methodValueCall(0xc08200a1b0, 0x3, 0x3, 0x1, 0xc0820f1350, 0xc0820aca01, 0xc0
82073290, 0x5, 0xc08200ab00, 0x1c5ee0, ...)vel           whoami
        /usr/local/go/src/reflect/asm_amd64.s:29 +0x3d fp=0xc082019be8 sp=0xc082019bd
0grep              lsmod              seq                xtables-multi
github.com/docker/docker/api/client.(*DockerCli).Cmd(0xc0820aca00, 0xc08200a1a0, 0x4,
 0x4, 0x0, 0x0)    lspci              setconsole         xzcat
        /go/src/github.com/docker/docker/api/client/cli.go:81 +0x385 fp=0xc082019ca8
sp=0xc082019be8    lzcat              setlogcons         zcat
main.main()
        /go/src/github.com/docker/docker/docker/docker.go:138 +0xb90 fp=0xc082019f98
sp=0xc082019ca8
runtime.main()
        /usr/local/go/src/runtime/proc.go:63 +0xfa fp=0xc082019fe0 sp=0xc082019f98
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc082019fe8 sp=0xc082019f
e0

goroutine 2 [force gc (idle)]:
runtime.gopark(0x439680, 0xaca880, 0x8bb0d0, 0xf)
        /usr/local/go/src/runtime/proc.go:130 +0x10c fp=0xc08201df98 sp=0xc08201df68
runtime.goparkunlock(0xaca880, 0x8bb0d0, 0xf)
        /usr/local/go/src/runtime/proc.go:136 +0x4f fp=0xc08201dfc0 sp=0xc08201df98
runtime.forcegchelper()
        /usr/local/go/src/runtime/proc.go:99 +0xd5 fp=0xc08201dfe0 sp=0xc08201dfc0
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc08201dfe8 sp=0xc08201df
e0
created by runtime.init┬╖4
        /usr/local/go/src/runtime/proc.go:87 +0x2c

goroutine 3 [runnable]:
runtime.gopark(0x439680, 0xad7488, 0x8adf50, 0xd)
        /usr/local/go/src/runtime/proc.go:130 +0x10c fp=0xc08201bf98 sp=0xc08201bf68
runtime.goparkunlock(0xad7488, 0x8adf50, 0xd)
        /usr/local/go/src/runtime/proc.go:136 +0x4f fp=0xc08201bfc0 sp=0xc08201bf98
runtime.bgsweep()
        /usr/local/go/src/runtime/mgc0.go:98 +0xc3 fp=0xc08201bfe0 sp=0xc08201bfc0
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc08201bfe8 sp=0xc08201bf
e0
created by gc
        /usr/local/go/src/runtime/mgc0.c:1386

goroutine 4 [finalizer wait]:
runtime.gopark(0x439680, 0xad7480, 0x8ba9d0, 0xe)
        /usr/local/go/src/runtime/proc.go:130 +0x10c fp=0xc08204bf30 sp=0xc08204bf00
runtime.goparkunlock(0xad7480, 0x8ba9d0, 0xe)
        /usr/local/go/src/runtime/proc.go:136 +0x4f fp=0xc08204bf58 sp=0xc08204bf30
runtime.runfinq()
        /usr/local/go/src/runtime/malloc.go:727 +0xc1 fp=0xc08204bfe0 sp=0xc08204bf58

runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc08204bfe8 sp=0xc08204bf
e0
created by runtime.createfing
        /usr/local/go/src/runtime/malloc.go:707 +0x65

goroutine 5 [syscall]:
runtime.notetsleepg(0xad84c0, 0xffffffffffffffff, 0xacb601)
        /usr/local/go/src/runtime/lock_sema.go:266 +0x93 fp=0xc08201ff68 sp=0xc08201f
f28
runtime.signal_recv(0x0)
        /usr/local/go/src/runtime/sigqueue.go:109 +0x13c fp=0xc08201ffa0 sp=0xc08201f
f68
os/signal.loop()
        /usr/local/go/src/os/signal/signal_unix.go:21 +0x26 fp=0xc08201ffe0 sp=0xc082
01ffa0
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc08201ffe8 sp=0xc08201ff
e0
created by os/signal.init┬╖1
        /usr/local/go/src/os/signal/signal_unix.go:27 +0x3c

goroutine 11 [chan receive]:
runtime.gopark(0x439680, 0xc0820ee538, 0x8b8650, 0xc)
        /usr/local/go/src/runtime/proc.go:130 +0x10c fp=0xc082045bd0 sp=0xc082045ba0
runtime.goparkunlock(0xc0820ee538, 0x8b8650, 0xc)
        /usr/local/go/src/runtime/proc.go:136 +0x4f fp=0xc082045bf8 sp=0xc082045bd0
runtime.chanrecv(0x75a520, 0xc0820ee4e0, 0xc082045e30, 0xc082042001, 0xc082040000)
        /usr/local/go/src/runtime/chan.go:467 +0x833 fp=0xc082045c98 sp=0xc082045bf8
runtime.chanrecv1(0x75a520, 0xc0820ee4e0, 0xc082045e30)
        /usr/local/go/src/runtime/chan.go:311 +0x2b fp=0xc082045cc8 sp=0xc082045c98
github.com/docker/docker/api/client.(*DockerCli).hijack(0xc0820aca00, 0x897c10, 0x4,
0xc0820d0500, 0x76, 0x1, 0x1d2330, 0xc08200abe0, 0x1d22d8, 0xc08200ab40, ...)
        /go/src/github.com/docker/docker/api/client/hijack.go:237 +0xd38 fp=0xc082045
ed0 sp=0xc082045cc8
github.com/docker/docker/api/client.func┬╖015(0x0, 0x0)
        /go/src/github.com/docker/docker/api/client/commands.go:2444 +0x1c9 fp=0xc082
045fa8 sp=0xc082045ed0
github.com/docker/docker/pkg/promise.func┬╖001()
        /go/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x36 fp=0xc082045f
e0 sp=0xc082045fa8
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc082045fe8 sp=0xc082045f
e0
created by github.com/docker/docker/pkg/promise.Go
        /go/src/github.com/docker/docker/pkg/promise/promise.go:9 +0x102

goroutine 7 [syscall]:
runtime.notetsleepg(0xacaa58, 0x7732bae60, 0x5)
        /usr/local/go/src/runtime/lock_sema.go:266 +0x93 fp=0xc082049f68 sp=0xc082049
f28
runtime.timerproc()
        /usr/local/go/src/runtime/time.go:207 +0x101 fp=0xc082049fe0 sp=0xc082049f68
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc082049fe8 sp=0xc082049f
e0
created by runtime.addtimerLocked
        /usr/local/go/src/runtime/time.go:113 +0x1c1

goroutine 9 [IO wait]:
runtime.gopark(0x4157a0, 0x1d3c40, 0x895bb0, 0x7)
        /usr/local/go/src/runtime/proc.go:130 +0x10c fp=0xc0821273a8 sp=0xc082127378
runtime.netpollblock(0x1d3c18, 0x72, 0xc082066001)
        /usr/local/go/src/runtime/netpoll.go:347 +0x177 fp=0xc0821273e8 sp=0xc0821273
a8
runtime.netpollWait(0x1d3c18, 0x72, 0xc0820cf448)
        /usr/local/go/src/runtime/netpoll.go:150 +0x6f fp=0xc082127408 sp=0xc0821273e
8
net.(*pollDesc).Wait(0xc082066170, 0x72, 0x0, 0x0)
        /usr/local/go/src/net/fd_poll_runtime.go:84 +0x4e fp=0xc082127428 sp=0xc08212
7408
net.(*ioSrv).ExecIO(0xc082042220, 0xc082066060, 0x89a470, 0x7, 0x97bec0, 0x5d, 0x0, 0
x0)
        /usr/local/go/src/net/fd_windows.go:188 +0x305 fp=0xc082127510 sp=0xc08212742
8
net.(*netFD).Read(0xc082066000, 0xc082057400, 0x400, 0x400, 0x0, 0x0, 0x0)
        /usr/local/go/src/net/fd_windows.go:470 +0x180 fp=0xc082127598 sp=0xc08212751
0
net.(*conn).Read(0xc082042238, 0xc082057400, 0x400, 0x400, 0x0, 0x0, 0x0)
        /usr/local/go/src/net/net.go:121 +0xe3 fp=0xc0821275e0 sp=0xc082127598
crypto/tls.(*block).readFromUntil(0xc0820f0780, 0x1d3cd8, 0xc082042238, 0x5, 0x0, 0x0
)
        /usr/local/go/src/crypto/tls/conn.go:454 +0xed fp=0xc082127648 sp=0xc0821275e
0
crypto/tls.(*Conn).readRecord(0xc082086580, 0x17, 0x0, 0x0)
        /usr/local/go/src/crypto/tls/conn.go:539 +0x2e1 fp=0xc082127b90 sp=0xc0821276
48
crypto/tls.(*Conn).Read(0xc082086580, 0xc08200f000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
        /usr/local/go/src/crypto/tls/conn.go:904 +0x16d fp=0xc082127c78 sp=0xc082127b
90
net/http.noteEOFReader.Read(0x1d4440, 0xc082086580, 0xc0820dc1b8, 0xc08200f000, 0x100
0, 0x1000, 0xc0820d8140, 0x0, 0x0)
        /usr/local/go/src/net/http/transport.go:1270 +0x75 fp=0xc082127cc8 sp=0xc0821
27c78
net/http.(*noteEOFReader).Read(0xc0820ae540, 0xc08200f000, 0x1000, 0x1000, 0xc0820120
00, 0x0, 0x0)
        <autogenerated>:125 +0xdb fp=0xc082127d18 sp=0xc082127cc8
bufio.(*Reader).fill(0xc0820ee600)
        /usr/local/go/src/bufio/bufio.go:97 +0x1d5 fp=0xc082127dc0 sp=0xc082127d18
bufio.(*Reader).Peek(0xc0820ee600, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0)
        /usr/local/go/src/bufio/bufio.go:132 +0xf7 fp=0xc082127dd0 sp=0xc082127dc0
net/http.(*persistConn).readLoop(0xc0820dc160)
        /usr/local/go/src/net/http/transport.go:842 +0xab fp=0xc082127fd8 sp=0xc08212
7dd0
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc082127fe0 sp=0xc082127f
d8
created by net/http.(*Transport).dialConn
        /usr/local/go/src/net/http/transport.go:660 +0xca6

goroutine 10 [select]:
runtime.gopark(0x41a1e0, 0xc082047f40, 0x8a35b0, 0x6)
        /usr/local/go/src/runtime/proc.go:130 +0x10c fp=0xc082047cb8 sp=0xc082047c88
runtime.selectgoImpl(0xc082047f40, 0x0, 0x18)
        /usr/local/go/src/runtime/select.go:366 +0xb33 fp=0xc082047e50 sp=0xc082047cb
8
runtime.selectgo(0xc082047f40)
        /usr/local/go/src/runtime/select.go:183 +0x12 fp=0xc082047e70 sp=0xc082047e50

net/http.(*persistConn).writeLoop(0xc0820dc160)
        /usr/local/go/src/net/http/transport.go:945 +0x424 fp=0xc082047fd8 sp=0xc0820
47e70
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc082047fe0 sp=0xc082047f
d8
created by net/http.(*Transport).dialConn
        /usr/local/go/src/net/http/transport.go:661 +0xcc3

goroutine 13 [runnable, locked to thread]:
runtime.cgocall_errno(0x443e30, 0xc082129ab8, 0x0)
        /usr/local/go/src/runtime/cgocall.go:130 +0xdb fp=0xc082129aa0 sp=0xc082129a7
8
runtime.syscall_Syscall6(0x7ff8627a6940, 0x4, 0x20, 0xc08216b000, 0x400, 0xc08207ed60
, 0x0, 0x0, 0xc08212afc0, 0x411329, ...)
        /usr/local/go/src/runtime/syscall_windows.go:142 +0x6c fp=0xc082129b00 sp=0xc
082129aa0
syscall.(*Proc).Call(0xc0820de020, 0xc082129c68, 0x4, 0x4, 0x10, 0x768720, 0x0, 0x0)
        /usr/local/go/src/syscall/dll_windows.go:136 +0x5c2 fp=0xc082129be0 sp=0xc082
129b00
syscall.(*LazyProc).Call(0xc08205a2d0, 0xc082129c68, 0x4, 0x4, 0xc08216b000, 0x408018
, 0x0, 0x0)
        /usr/local/go/src/syscall/dll_windows.go:279 +0x74 fp=0xc082129c28 sp=0xc0821
29be0
github.com/docker/docker/pkg/term/winconsole.readConsoleInputKey(0x20, 0xc08216b000,
0x400, 0x400, 0x400, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/winconsole/console_windows.go:554 +
0xf4 fp=0xc082129cb0 sp=0xc082129c28
github.com/docker/docker/pkg/term/winconsole.getAvailableInputEvents(0x20, 0x0, 0x0,
0x0, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/winconsole/console_windows.go:1060
+0xf1 fp=0xc082129d28 sp=0xc082129cb0
github.com/docker/docker/pkg/term/winconsole.(*WindowsTerminal).ReadChars(0xc0820b08c
0, 0x20, 0x1d23b0, 0xc082042000, 0xc08212c000, 0x8000, 0x8000, 0x0, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/winconsole/console_windows.go:1101
+0x14b fp=0xc082129dd0 sp=0xc082129d28
github.com/docker/docker/pkg/term/winconsole.(*terminalReader).Read(0xc08200abe0, 0xc
08212c000, 0x8000, 0x8000, 0x1, 0x0, 0x0)
        /go/src/github.com/docker/docker/pkg/term/winconsole/term_emulator.go:153 +0x
14f fp=0xc082129e38 sp=0xc082129dd0
io.Copy(0x1d4a08, 0xc0820de340, 0x1d4b28, 0xc08200abe0, 0x32, 0x0, 0x0)
        /usr/local/go/src/io/io.go:362 +0x1fd fp=0xc082129ef0 sp=0xc082129e38
github.com/docker/docker/api/client.func┬╖025(0x0, 0x0)
        /go/src/github.com/docker/docker/api/client/hijack.go:221 +0xe3 fp=0xc082129f
a8 sp=0xc082129ef0
github.com/docker/docker/pkg/promise.func┬╖001()
        /go/src/github.com/docker/docker/pkg/promise/promise.go:8 +0x36 fp=0xc082129f
e0 sp=0xc082129fa8
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc082129fe8 sp=0xc082129f
e0
created by github.com/docker/docker/pkg/promise.Go
        /go/src/github.com/docker/docker/pkg/promise/promise.go:9 +0x102
width := toCoord.X - fromCoord.X + 1
height := toCoord.Y - fromCoord.Y + 1
size := width * height
if size > 0 {

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Mar 19, 2015

Contributor

Thanks
the stack trace shows following line is at the top of the stack. The garbage collector+ memory manager in go is throwing exception.
This is the second time I am seeing GC throwing exceptions . I can eliminate the memory alloc all together by using the static var .
But then it defeats the purpose of having garbage collected language.

height := toCoord.Y - fromCoord.Y + 1
size := width * height
if size > 0 {
buffer := make([]CHAR_INFO, size)

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Mar 19, 2015

Contributor

This line is where exception is thrown.
Line 442

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

Okay, my guess is that this buffer is big and constantly free'd/malloc'd. We should reuse it. Could be useful to profile.

}
func clearDisplayRect(fileDesc uintptr, fillChar rune, attributes WORD, fromCoord COORD, toCoord COORD, windowSize COORD) (bool, uint32, error) {
var writeRegion SMALL_RECT

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator
writeRegion := SMALL_RECT{
  Top: fromCoord.Y, 
  Left: fromCoord.X,
  Right: toCoord.X,
  Bottom: toCoord.Y,
}
// allocate and initialize buffer
width := toCoord.X - fromCoord.X + 1
height := toCoord.Y - fromCoord.Y + 1
size := width * height

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

@sachin-jayant-joshi is there a maximum size ?

return true, nil
}
func writeConsoleOutput(fileDesc uintptr, buffer []CHAR_INFO, bufferSize COORD, bufferCoord COORD, writeRegion *SMALL_RECT) (bool, error) {

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

try buffer *[]CHAR_INFO

}
// ReadChars reads the characters from the given reader
func (term *WindowsTerminal) ReadChars(fd uintptr, w io.Reader, p []byte) (n int, err error) {

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

w is a bad name for an io.Reader

// ReadChars reads the characters from the given reader
func (term *WindowsTerminal) ReadChars(fd uintptr, w io.Reader, p []byte) (n int, err error) {
n = 0

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

n is 0 by default, so no need for this line

return tr.wrappedReader.Close()
}
func (tr *terminalReader) readFromWrappedReader(p []byte) (n int, err error) {

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

what's the use of this function? you might as well write tr.wrappedReader.Read(p) at the call site.

if n > 0 {
return n, nil
}
inputEvents, _ := getAvailableInputEvents(fd)

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

this should not ignore the error

}
if 0 < nr {
retValue := make([]INPUT_RECORD, nr)
for i := 0; i < nr; i++ {

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

you should use the copy() builtin instead of manually copying

This comment has been minimized.

@sachin-jayant-joshi

sachin-jayant-joshi Mar 19, 2015

Contributor

Will do that!
This is my first time writing any Go code and I am not yet completely familiar with Go std lib

if len(inputEvents) != 0 {
keyCodes := getTranslatedKeyCodes(inputEvents, term.inputEscapeSequence)
for _, b := range []byte(keyCodes) {
term.inputBuffer <- b

This comment has been minimized.

@tiborvass

tiborvass Mar 19, 2015

Collaborator

i would get rid of the channel and the for loop and simply do n += copy(p[n:], keyCodes)

}
// Write to buffer
r, err := writeConsoleOutput(fileDesc, buffer, windowSize, COORD{X: 0, Y: 0}, &writeRegion)

This comment has been minimized.

@tiborvass

tiborvass Mar 20, 2015

Collaborator

@sachin-jayant-joshi according to MSDN doc, windowSize should be the size of buffer, why not just pass len(buffer) ? I see that the size of buffer is set to size, which is width * height. How is that different from windowSize ?

}
// mapKeystokeToTerminalString maps the given input event record to string
func mapKeystokeToTerminalString(keyEvent *KEY_EVENT_RECORD, escapeSequence []byte) string {

This comment has been minimized.

@tiborvass

tiborvass Mar 20, 2015

Collaborator

you forgot an r in mapKeystokeToTerminalString

@tiborvass

This comment has been minimized.

Collaborator

tiborvass commented Mar 20, 2015

I have some other stacktraces here: https://gist.github.com/tiborvass/d5f9a64be665fa4e9f38

@sachin-jayant-joshi if you could update this branch with your branch named win-tty-no-chan, that'd be fantastic.

Otherwise we'll have to carry it.

@tiborvass

This comment has been minimized.

Collaborator

tiborvass commented Mar 22, 2015

Carried at #11566

@tiborvass tiborvass closed this Mar 22, 2015

@tiborvass tiborvass removed their assignment Nov 3, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment