diff --git a/daemon/daemon_windows.go b/daemon/daemon_windows.go index 91a26151ea..4050e2880b 100644 --- a/daemon/daemon_windows.go +++ b/daemon/daemon_windows.go @@ -3,13 +3,13 @@ package daemon import ( "fmt" "os" - "syscall" "github.com/Sirupsen/logrus" "github.com/docker/docker/daemon/graphdriver" // register the windows graph driver _ "github.com/docker/docker/daemon/graphdriver/windows" "github.com/docker/docker/pkg/parsers" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/runconfig" "github.com/docker/libnetwork" ) @@ -62,21 +62,15 @@ func checkConfigOptions(config *Config) error { // checkSystem validates platform-specific requirements func checkSystem() error { - var dwVersion uint32 - - // TODO Windows. May need at some point to ensure have elevation and - // possibly LocalSystem. - // Validate the OS version. Note that docker.exe must be manifested for this // call to return the correct version. - dwVersion, err := syscall.GetVersion() + osv, err := system.GetOSVersion() if err != nil { - return fmt.Errorf("Failed to call GetVersion()") + return err } - if int(dwVersion&0xFF) < 10 { + if osv.MajorVersion < 10 { return fmt.Errorf("This version of Windows does not support the docker daemon") } - return nil } diff --git a/pkg/system/syscall_windows.go b/pkg/system/syscall_windows.go index b3b94cb5eb..273aa234bb 100644 --- a/pkg/system/syscall_windows.go +++ b/pkg/system/syscall_windows.go @@ -1,5 +1,34 @@ package system +import ( + "fmt" + "syscall" +) + +// OSVersion is a wrapper for Windows version information +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx +type OSVersion struct { + Version uint32 + MajorVersion uint8 + MinorVersion uint8 + Build uint16 +} + +// GetOSVersion gets the operating system version on Windows. Note that +// docker.exe must be manifested to get the correct version information. +func GetOSVersion() (OSVersion, error) { + var err error + osv := OSVersion{} + osv.Version, err = syscall.GetVersion() + if err != nil { + return osv, fmt.Errorf("Failed to call GetVersion()") + } + osv.MajorVersion = uint8(osv.Version & 0xFF) + osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) + osv.Build = uint16(osv.Version >> 16) + return osv, nil +} + // Unmount is a platform-specific helper function to call // the unmount syscall. Not supported on Windows func Unmount(dest string) error { diff --git a/pkg/term/term_windows.go b/pkg/term/term_windows.go index da9295eec6..04870d1bf7 100644 --- a/pkg/term/term_windows.go +++ b/pkg/term/term_windows.go @@ -7,9 +7,11 @@ import ( "io" "os" "os/signal" + "syscall" "github.com/Azure/go-ansiterm/winterm" "github.com/Sirupsen/logrus" + "github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/term/windows" ) @@ -36,10 +38,66 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) { // MSYS (mingw) does not emulate ANSI well. return windows.ConsoleStreams() default: + if useNativeConsole() { + return os.Stdin, os.Stdout, os.Stderr + } return windows.ConsoleStreams() } } +// useNativeConsole determines if the docker client should use the built-in +// console which supports ANSI emulation, or fall-back to the golang emulator +// (github.com/azure/go-ansiterm). +func useNativeConsole() bool { + osv, err := system.GetOSVersion() + if err != nil { + return false + } + + // Native console is not available major version 10 + if osv.MajorVersion < 10 { + return false + } + + // Must have a late pre-release TP4 build of Windows Server 2016/Windows 10 TH2 or later + if osv.Build < 10578 { + return false + } + + // Environment variable override + if e := os.Getenv("USE_NATIVE_CONSOLE"); e != "" { + if e == "1" { + return true + } + return false + } + + // Get the handle to stdout + stdOutHandle, err := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE) + if err != nil { + return false + } + + // Get the console mode from the consoles stdout handle + var mode uint32 + if err := syscall.GetConsoleMode(stdOutHandle, &mode); err != nil { + return false + } + + // Legacy mode does not have native ANSI emulation. + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx + const enableVirtualTerminalProcessing = 0x0004 + if mode&enableVirtualTerminalProcessing == 0 { + return false + } + + // TODO Windows (Post TP4). The native emulator still has issues which + // mean it shouldn't be enabled for everyone. Change this next line to true + // to change the default to "enable if available". In the meantime, users + // can still try it out by using USE_NATIVE_CONSOLE env variable. + return false +} + // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal. func GetFdInfo(in interface{}) (uintptr, bool) { return windows.GetHandleInfo(in)