Browse code

Windows: Native ANSI console support

Signed-off-by: John Howard <jhoward@microsoft.com>

John Howard authored on 2015/10/16 03:40:14
Showing 3 changed files
... ...
@@ -3,13 +3,13 @@ package daemon
3 3
 import (
4 4
 	"fmt"
5 5
 	"os"
6
-	"syscall"
7 6
 
8 7
 	"github.com/Sirupsen/logrus"
9 8
 	"github.com/docker/docker/daemon/graphdriver"
10 9
 	// register the windows graph driver
11 10
 	_ "github.com/docker/docker/daemon/graphdriver/windows"
12 11
 	"github.com/docker/docker/pkg/parsers"
12
+	"github.com/docker/docker/pkg/system"
13 13
 	"github.com/docker/docker/runconfig"
14 14
 	"github.com/docker/libnetwork"
15 15
 )
... ...
@@ -62,21 +62,15 @@ func checkConfigOptions(config *Config) error {
62 62
 
63 63
 // checkSystem validates platform-specific requirements
64 64
 func checkSystem() error {
65
-	var dwVersion uint32
66
-
67
-	// TODO Windows. May need at some point to ensure have elevation and
68
-	// possibly LocalSystem.
69
-
70 65
 	// Validate the OS version. Note that docker.exe must be manifested for this
71 66
 	// call to return the correct version.
72
-	dwVersion, err := syscall.GetVersion()
67
+	osv, err := system.GetOSVersion()
73 68
 	if err != nil {
74
-		return fmt.Errorf("Failed to call GetVersion()")
69
+		return err
75 70
 	}
76
-	if int(dwVersion&0xFF) < 10 {
71
+	if osv.MajorVersion < 10 {
77 72
 		return fmt.Errorf("This version of Windows does not support the docker daemon")
78 73
 	}
79
-
80 74
 	return nil
81 75
 }
82 76
 
... ...
@@ -1,5 +1,34 @@
1 1
 package system
2 2
 
3
+import (
4
+	"fmt"
5
+	"syscall"
6
+)
7
+
8
+// OSVersion is a wrapper for Windows version information
9
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx
10
+type OSVersion struct {
11
+	Version      uint32
12
+	MajorVersion uint8
13
+	MinorVersion uint8
14
+	Build        uint16
15
+}
16
+
17
+// GetOSVersion gets the operating system version on Windows. Note that
18
+// docker.exe must be manifested to get the correct version information.
19
+func GetOSVersion() (OSVersion, error) {
20
+	var err error
21
+	osv := OSVersion{}
22
+	osv.Version, err = syscall.GetVersion()
23
+	if err != nil {
24
+		return osv, fmt.Errorf("Failed to call GetVersion()")
25
+	}
26
+	osv.MajorVersion = uint8(osv.Version & 0xFF)
27
+	osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF)
28
+	osv.Build = uint16(osv.Version >> 16)
29
+	return osv, nil
30
+}
31
+
3 32
 // Unmount is a platform-specific helper function to call
4 33
 // the unmount syscall. Not supported on Windows
5 34
 func Unmount(dest string) {
... ...
@@ -7,9 +7,11 @@ import (
7 7
 	"io"
8 8
 	"os"
9 9
 	"os/signal"
10
+	"syscall"
10 11
 
11 12
 	"github.com/Azure/go-ansiterm/winterm"
12 13
 	"github.com/Sirupsen/logrus"
14
+	"github.com/docker/docker/pkg/system"
13 15
 	"github.com/docker/docker/pkg/term/windows"
14 16
 )
15 17
 
... ...
@@ -36,10 +38,66 @@ func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
36 36
 		// MSYS (mingw) does not emulate ANSI well.
37 37
 		return windows.ConsoleStreams()
38 38
 	default:
39
+		if useNativeConsole() {
40
+			return os.Stdin, os.Stdout, os.Stderr
41
+		}
39 42
 		return windows.ConsoleStreams()
40 43
 	}
41 44
 }
42 45
 
46
+// useNativeConsole determines if the docker client should use the built-in
47
+// console which supports ANSI emulation, or fall-back to the golang emulator
48
+// (github.com/azure/go-ansiterm).
49
+func useNativeConsole() bool {
50
+	osv, err := system.GetOSVersion()
51
+	if err != nil {
52
+		return false
53
+	}
54
+
55
+	// Native console is not available major version 10
56
+	if osv.MajorVersion < 10 {
57
+		return false
58
+	}
59
+
60
+	// Must have a late pre-release TP4 build of Windows Server 2016/Windows 10 TH2 or later
61
+	if osv.Build < 10578 {
62
+		return false
63
+	}
64
+
65
+	// Environment variable override
66
+	if e := os.Getenv("USE_NATIVE_CONSOLE"); e != "" {
67
+		if e == "1" {
68
+			return true
69
+		}
70
+		return false
71
+	}
72
+
73
+	// Get the handle to stdout
74
+	stdOutHandle, err := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE)
75
+	if err != nil {
76
+		return false
77
+	}
78
+
79
+	// Get the console mode from the consoles stdout handle
80
+	var mode uint32
81
+	if err := syscall.GetConsoleMode(stdOutHandle, &mode); err != nil {
82
+		return false
83
+	}
84
+
85
+	// Legacy mode does not have native ANSI emulation.
86
+	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683167(v=vs.85).aspx
87
+	const enableVirtualTerminalProcessing = 0x0004
88
+	if mode&enableVirtualTerminalProcessing == 0 {
89
+		return false
90
+	}
91
+
92
+	// TODO Windows (Post TP4). The native emulator still has issues which
93
+	// mean it shouldn't be enabled for everyone. Change this next line to true
94
+	// to change the default to "enable if available". In the meantime, users
95
+	// can still try it out by using USE_NATIVE_CONSOLE env variable.
96
+	return false
97
+}
98
+
43 99
 // GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
44 100
 func GetFdInfo(in interface{}) (uintptr, bool) {
45 101
 	return windows.GetHandleInfo(in)