From fd36b548c50595bed1306cde581be94e3bec5a61 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 21 Nov 2016 10:35:18 -0800 Subject: [PATCH] helper/wrappedstreams: get original console input/output on Windows Fixes #10266 panicwrap was using Extrafiles to get the original standard streams for `terraform console`. This doesn't work on Windows. Instead, we must use the Win32 APIs to get the exact handles. --- helper/wrappedstreams/streams.go | 15 ++++-- helper/wrappedstreams/streams_other.go | 14 +++++ helper/wrappedstreams/streams_windows.go | 52 +++++++++++++++++++ .../mitchellh/panicwrap/panicwrap.go | 9 +++- vendor/vendor.json | 6 +-- 5 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 helper/wrappedstreams/streams_other.go create mode 100644 helper/wrappedstreams/streams_windows.go diff --git a/helper/wrappedstreams/streams.go b/helper/wrappedstreams/streams.go index c1d6706e8..d3e5cf1c0 100644 --- a/helper/wrappedstreams/streams.go +++ b/helper/wrappedstreams/streams.go @@ -38,10 +38,15 @@ func Stderr() *os.File { return stderr } -// These are the wrapped streams. There doesn't appear to be a negative -// impact of opening these files even if the file descriptor doesn't exist. +// These are the wrapped standard streams. These are setup by the +// platform specific code in initPlatform. var ( - wrappedStdin = os.NewFile(uintptr(3), "stdin") - wrappedStdout = os.NewFile(uintptr(4), "stdout") - wrappedStderr = os.NewFile(uintptr(5), "stderr") + wrappedStdin *os.File + wrappedStdout *os.File + wrappedStderr *os.File ) + +func init() { + // Initialize the platform-specific code + initPlatform() +} diff --git a/helper/wrappedstreams/streams_other.go b/helper/wrappedstreams/streams_other.go new file mode 100644 index 000000000..5ffa413bc --- /dev/null +++ b/helper/wrappedstreams/streams_other.go @@ -0,0 +1,14 @@ +// +build !windows + +package wrappedstreams + +import ( + "os" +) + +func initPlatform() { + // The standard streams are passed in via extra file descriptors. + wrappedStdin = os.NewFile(uintptr(3), "stdin") + wrappedStdout = os.NewFile(uintptr(4), "stdout") + wrappedStderr = os.NewFile(uintptr(5), "stderr") +} diff --git a/helper/wrappedstreams/streams_windows.go b/helper/wrappedstreams/streams_windows.go new file mode 100644 index 000000000..2709cc0c6 --- /dev/null +++ b/helper/wrappedstreams/streams_windows.go @@ -0,0 +1,52 @@ +// +build windows + +package wrappedstreams + +import ( + "log" + "os" + "syscall" +) + +func initPlatform() { + wrappedStdin = openConsole("CONIN$", os.Stdin) + wrappedStdout = openConsole("CONOUT$", os.Stdout) + wrappedStderr = wrappedStdout +} + +// openConsole opens a console handle, using a backup if it fails. +// This is used to get the exact console handle instead of the redirected +// handles from panicwrap. +func openConsole(name string, backup *os.File) *os.File { + // Convert to UTF16 + path, err := syscall.UTF16PtrFromString(name) + if err != nil { + log.Printf("[ERROR] wrappedstreams: %s", err) + return backup + } + + // Determine the share mode + var shareMode uint32 + switch name { + case "CONIN$": + shareMode = syscall.FILE_SHARE_READ + case "CONOUT$": + shareMode = syscall.FILE_SHARE_WRITE + } + + // Get the file + h, err := syscall.CreateFile( + path, + syscall.GENERIC_READ|syscall.GENERIC_WRITE, + shareMode, + nil, + syscall.OPEN_EXISTING, + 0, 0) + if err != nil { + log.Printf("[ERROR] wrappedstreams: %s", err) + return backup + } + + // Create the Go file + return os.NewFile(uintptr(h), name) +} diff --git a/vendor/github.com/mitchellh/panicwrap/panicwrap.go b/vendor/github.com/mitchellh/panicwrap/panicwrap.go index addb9a08e..f5ce93f0b 100644 --- a/vendor/github.com/mitchellh/panicwrap/panicwrap.go +++ b/vendor/github.com/mitchellh/panicwrap/panicwrap.go @@ -16,6 +16,7 @@ import ( "os" "os/exec" "os/signal" + "runtime" "sync/atomic" "syscall" "time" @@ -151,7 +152,13 @@ func Wrap(c *WrapConfig) (int, error) { cmd.Stdin = os.Stdin cmd.Stdout = stdout_w cmd.Stderr = stderr_w - cmd.ExtraFiles = []*os.File{os.Stdin, os.Stdout, os.Stderr} + + // Windows doesn't support this, but on other platforms pass in + // the original file descriptors so they can be used. + if runtime.GOOS != "windows" { + cmd.ExtraFiles = []*os.File{os.Stdin, os.Stdout, os.Stderr} + } + if err := cmd.Start(); err != nil { return 1, err } diff --git a/vendor/vendor.json b/vendor/vendor.json index 5a0e5b440..b4c05bd78 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1915,10 +1915,10 @@ "revision": "314aad379a39f6ad5bcca278e6757d9abbb3a52e" }, { - "checksumSHA1": "wqU8bs9c+xm0f07t6ajQFWYS+rE=", + "checksumSHA1": "kTntIB9SdU1NsCqKwDkUr99qaj0=", "path": "github.com/mitchellh/panicwrap", - "revision": "168f3680ad986108df63bae07da2978f51c4ac8e", - "revisionTime": "2016-11-14T06:37:33Z" + "revision": "fde185d0dfb5ecac6e6b201e8855da798ebcd76f", + "revisionTime": "2016-11-21T18:34:54Z" }, { "path": "github.com/mitchellh/prefixedio",