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.
This commit is contained in:
Mitchell Hashimoto 2016-11-21 10:35:18 -08:00
parent 50c1e875d0
commit fd36b548c5
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
5 changed files with 87 additions and 9 deletions

View File

@ -38,10 +38,15 @@ func Stderr() *os.File {
return stderr return stderr
} }
// These are the wrapped streams. There doesn't appear to be a negative // These are the wrapped standard streams. These are setup by the
// impact of opening these files even if the file descriptor doesn't exist. // platform specific code in initPlatform.
var ( var (
wrappedStdin = os.NewFile(uintptr(3), "stdin") wrappedStdin *os.File
wrappedStdout = os.NewFile(uintptr(4), "stdout") wrappedStdout *os.File
wrappedStderr = os.NewFile(uintptr(5), "stderr") wrappedStderr *os.File
) )
func init() {
// Initialize the platform-specific code
initPlatform()
}

View File

@ -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")
}

View File

@ -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)
}

View File

@ -16,6 +16,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"os/signal" "os/signal"
"runtime"
"sync/atomic" "sync/atomic"
"syscall" "syscall"
"time" "time"
@ -151,7 +152,13 @@ func Wrap(c *WrapConfig) (int, error) {
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = stdout_w cmd.Stdout = stdout_w
cmd.Stderr = stderr_w cmd.Stderr = stderr_w
// 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} cmd.ExtraFiles = []*os.File{os.Stdin, os.Stdout, os.Stderr}
}
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return 1, err return 1, err
} }

6
vendor/vendor.json vendored
View File

@ -1915,10 +1915,10 @@
"revision": "314aad379a39f6ad5bcca278e6757d9abbb3a52e" "revision": "314aad379a39f6ad5bcca278e6757d9abbb3a52e"
}, },
{ {
"checksumSHA1": "wqU8bs9c+xm0f07t6ajQFWYS+rE=", "checksumSHA1": "kTntIB9SdU1NsCqKwDkUr99qaj0=",
"path": "github.com/mitchellh/panicwrap", "path": "github.com/mitchellh/panicwrap",
"revision": "168f3680ad986108df63bae07da2978f51c4ac8e", "revision": "fde185d0dfb5ecac6e6b201e8855da798ebcd76f",
"revisionTime": "2016-11-14T06:37:33Z" "revisionTime": "2016-11-21T18:34:54Z"
}, },
{ {
"path": "github.com/mitchellh/prefixedio", "path": "github.com/mitchellh/prefixedio",