From 4e55278839a44da5057903d69f50dd6ab2862566 Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Tue, 26 Feb 2019 11:55:39 +0100 Subject: [PATCH 1/9] cheggaaa.pb: try to not open tty at init to see if it causes #7299 --- vendor/github.com/cheggaaa/pb/pb_x.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/vendor/github.com/cheggaaa/pb/pb_x.go b/vendor/github.com/cheggaaa/pb/pb_x.go index 398345080..493854af4 100644 --- a/vendor/github.com/cheggaaa/pb/pb_x.go +++ b/vendor/github.com/cheggaaa/pb/pb_x.go @@ -24,16 +24,16 @@ var ( ) func init() { - echoLockMutex.Lock() - defer echoLockMutex.Unlock() - - var err error - tty, err = os.Open("/dev/tty") - istty = true - if err != nil { - tty = os.Stdin - istty = false - } + // echoLockMutex.Lock() + // defer echoLockMutex.Unlock() + + // var err error + // tty, err = os.Open("/dev/tty") + // istty = true + // if err != nil { + // tty = os.Stdin + // istty = false + // } } // terminalWidth returns width of the terminal. From c588a8a24d5f1d0609b536272de3484434919cbd Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Wed, 27 Feb 2019 16:52:55 +0100 Subject: [PATCH 2/9] Revert "cheggaaa.pb: try to not open tty at init to see if it causes #7299" This reverts commit 4e55278839a44da5057903d69f50dd6ab2862566. --- vendor/github.com/cheggaaa/pb/pb_x.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/vendor/github.com/cheggaaa/pb/pb_x.go b/vendor/github.com/cheggaaa/pb/pb_x.go index 493854af4..398345080 100644 --- a/vendor/github.com/cheggaaa/pb/pb_x.go +++ b/vendor/github.com/cheggaaa/pb/pb_x.go @@ -24,16 +24,16 @@ var ( ) func init() { - // echoLockMutex.Lock() - // defer echoLockMutex.Unlock() - - // var err error - // tty, err = os.Open("/dev/tty") - // istty = true - // if err != nil { - // tty = os.Stdin - // istty = false - // } + echoLockMutex.Lock() + defer echoLockMutex.Unlock() + + var err error + tty, err = os.Open("/dev/tty") + istty = true + if err != nil { + tty = os.Stdin + istty = false + } } // terminalWidth returns width of the terminal. From 239a0c633fde8302ef617bf588c97d7f586d10fb Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Wed, 27 Feb 2019 16:56:25 +0100 Subject: [PATCH 3/9] use go-tty in ui.Ask to fix #7299 --- packer/ui.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packer/ui.go b/packer/ui.go index e42e08139..87bd854d7 100644 --- a/packer/ui.go +++ b/packer/ui.go @@ -1,7 +1,6 @@ package packer import ( - "bufio" "bytes" "errors" "fmt" @@ -15,6 +14,8 @@ import ( "syscall" "time" "unicode" + + "github.com/mattn/go-tty" ) type UiColor uint @@ -81,7 +82,7 @@ type BasicUi struct { ErrorWriter io.Writer l sync.Mutex interrupted bool - scanner *bufio.Scanner + tty *tty.TTY StackableProgressBar } @@ -209,8 +210,12 @@ func (rw *BasicUi) Ask(query string) (string, error) { return "", errors.New("interrupted") } - if rw.scanner == nil { - rw.scanner = bufio.NewScanner(rw.Reader) + if rw.tty == nil { + var err error + rw.tty, err = tty.Open() + if err != nil { + return "", fmt.Errorf("tty open: %s", err) + } } sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) @@ -225,11 +230,8 @@ func (rw *BasicUi) Ask(query string) (string, error) { result := make(chan string, 1) go func() { - var line string - if rw.scanner.Scan() { - line = rw.scanner.Text() - } - if err := rw.scanner.Err(); err != nil { + line, err := rw.tty.ReadString() + if err != nil { log.Printf("ui: scan err: %s", err) return } From 7f5c794e5fe20fa57c4a51c4f06dc406af5196ab Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Wed, 27 Feb 2019 17:01:02 +0100 Subject: [PATCH 4/9] add go-tty vendor --- go.mod | 2 + go.sum | 4 + .../github.com/mattn/go-runewidth/.travis.yml | 8 + vendor/github.com/mattn/go-tty/.travis.yml | 4 + vendor/github.com/mattn/go-tty/LICENSE | 21 ++ vendor/github.com/mattn/go-tty/README.md | 49 +++ vendor/github.com/mattn/go-tty/tty.go | 120 +++++++ vendor/github.com/mattn/go-tty/tty_bsd.go | 12 + vendor/github.com/mattn/go-tty/tty_linux.go | 8 + vendor/github.com/mattn/go-tty/tty_plan9.go | 63 ++++ vendor/github.com/mattn/go-tty/tty_unix.go | 130 +++++++ vendor/github.com/mattn/go-tty/tty_windows.go | 324 ++++++++++++++++++ 12 files changed, 745 insertions(+) create mode 100644 vendor/github.com/mattn/go-runewidth/.travis.yml create mode 100644 vendor/github.com/mattn/go-tty/.travis.yml create mode 100644 vendor/github.com/mattn/go-tty/LICENSE create mode 100644 vendor/github.com/mattn/go-tty/README.md create mode 100644 vendor/github.com/mattn/go-tty/tty.go create mode 100644 vendor/github.com/mattn/go-tty/tty_bsd.go create mode 100644 vendor/github.com/mattn/go-tty/tty_linux.go create mode 100644 vendor/github.com/mattn/go-tty/tty_plan9.go create mode 100644 vendor/github.com/mattn/go-tty/tty_unix.go create mode 100644 vendor/github.com/mattn/go-tty/tty_windows.go diff --git a/go.mod b/go.mod index 5abf32ecd..9deb50e28 100644 --- a/go.mod +++ b/go.mod @@ -115,6 +115,7 @@ require ( github.com/mattn/go-colorable v0.0.9 // indirect github.com/mattn/go-isatty v0.0.0-20151211000621-56b76bdf51f7 // indirect github.com/mattn/go-runewidth v0.0.0-20170510074858-97311d9f7767 // indirect + github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f github.com/miekg/dns v1.1.1 // indirect github.com/mitchellh/cli v0.0.0-20170908181043-65fcae5817c8 github.com/mitchellh/copystructure v1.0.0 // indirect @@ -175,6 +176,7 @@ require ( golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect golang.org/x/tools v0.0.0-20190221204921-83362c3779f5 // indirect google.golang.org/api v0.0.0-20180818000503-e21acd801f91 + google.golang.org/appengine v1.4.0 // indirect google.golang.org/grpc v1.17.0 // indirect gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect diff --git a/go.sum b/go.sum index dcee98dcb..5465a3343 100644 --- a/go.sum +++ b/go.sum @@ -256,6 +256,8 @@ github.com/mattn/go-isatty v0.0.0-20151211000621-56b76bdf51f7 h1:owMyzMR4QR+jSdl github.com/mattn/go-isatty v0.0.0-20151211000621-56b76bdf51f7/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.0-20170510074858-97311d9f7767 h1:Nk2R0tWpD2RdkQ+53zE6kWnSGuhQyDlnOs2MPiqVubE= github.com/mattn/go-runewidth v0.0.0-20170510074858-97311d9f7767/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f h1:4P7Ul+TAnk92vTeVkXs6VLjmf1EhrYtDRa03PCYY6VM= +github.com/mattn/go-tty v0.0.0-20181127064339-e4f871175a2f/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.1.1 h1:DVkblRdiScEnEr0LR9nTnEQqHYycjkXW9bOjd+2EL2o= @@ -403,6 +405,7 @@ google.golang.org/api v0.0.0-20180818000503-e21acd801f91 h1:MgYYgjaWMS2qQiDwCznf google.golang.org/api v0.0.0-20180818000503-e21acd801f91/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= @@ -437,5 +440,6 @@ gopkg.in/vmihailenco/msgpack.v2 v2.9.1 h1:kb0VV7NuIojvRfzwslQeP3yArBqJHW9tOl4t38 gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/mattn/go-runewidth/.travis.yml b/vendor/github.com/mattn/go-runewidth/.travis.yml new file mode 100644 index 000000000..5c9c2a30f --- /dev/null +++ b/vendor/github.com/mattn/go-runewidth/.travis.yml @@ -0,0 +1,8 @@ +language: go +go: + - tip +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover +script: + - $HOME/gopath/bin/goveralls -repotoken lAKAWPzcGsD3A8yBX3BGGtRUdJ6CaGERL diff --git a/vendor/github.com/mattn/go-tty/.travis.yml b/vendor/github.com/mattn/go-tty/.travis.yml new file mode 100644 index 000000000..63b9f7084 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/.travis.yml @@ -0,0 +1,4 @@ +language: go +sudo: false +go: + - tip diff --git a/vendor/github.com/mattn/go-tty/LICENSE b/vendor/github.com/mattn/go-tty/LICENSE new file mode 100644 index 000000000..e364750d2 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Yasuhiro Matsumoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mattn/go-tty/README.md b/vendor/github.com/mattn/go-tty/README.md new file mode 100644 index 000000000..6712eb49d --- /dev/null +++ b/vendor/github.com/mattn/go-tty/README.md @@ -0,0 +1,49 @@ +# go-tty + +Simple tty utility + +## Usage + +```go +tty, err := tty.Open() +if err != nil { + log.Fatal(err) +} +defer tty.Close() + +for { + r, err := tty.ReadRune() + if err != nil { + log.Fatal(err) + } + // handle key event +} +``` + +if you are on windows and want to display ANSI colors, use go-colorable. + +```go +tty, err := tty.Open() +if err != nil { + log.Fatal(err) +} +defer tty.Close() + +out := colorable.NewColorable(tty.Output()) + +fmt.Fprintln(out, "\x1b[2J") +``` + +## Installation + +``` +$ go get github.com/mattn/go-tty +``` + +## License + +MIT + +## Author + +Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-tty/tty.go b/vendor/github.com/mattn/go-tty/tty.go new file mode 100644 index 000000000..8747638b3 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty.go @@ -0,0 +1,120 @@ +package tty + +import ( + "os" + "strings" + "unicode" +) + +func Open() (*TTY, error) { + return open() +} + +func (tty *TTY) Raw() (func() error, error) { + return tty.raw() +} + +func (tty *TTY) MustRaw() func() error { + f, err := tty.raw() + if err != nil { + panic(err.Error()) + } + return f +} + +func (tty *TTY) Buffered() bool { + return tty.buffered() +} + +func (tty *TTY) ReadRune() (rune, error) { + return tty.readRune() +} + +func (tty *TTY) Close() error { + return tty.close() +} + +func (tty *TTY) Size() (int, int, error) { + return tty.size() +} + +func (tty *TTY) Input() *os.File { + return tty.input() +} + +func (tty *TTY) Output() *os.File { + return tty.output() +} + +// Display types. +const ( + displayNone = iota + displayRune + displayMask +) + +func (tty *TTY) readString(displayType int) (string, error) { + rs := []rune{} +loop: + for { + r, err := tty.readRune() + if err != nil { + return "", err + } + switch r { + case 13: + break loop + case 8, 127: + if len(rs) > 0 { + rs = rs[:len(rs)-1] + if displayType != displayNone { + tty.Output().WriteString("\b \b") + } + } + default: + if unicode.IsPrint(r) { + rs = append(rs, r) + switch displayType { + case displayRune: + tty.Output().WriteString(string(r)) + case displayMask: + tty.Output().WriteString("*") + } + } + } + } + return string(rs), nil +} + +func (tty *TTY) ReadString() (string, error) { + defer tty.Output().WriteString("\n") + return tty.readString(displayRune) +} + +func (tty *TTY) ReadPassword() (string, error) { + defer tty.Output().WriteString("\n") + return tty.readString(displayMask) +} + +func (tty *TTY) ReadPasswordNoEcho() (string, error) { + defer tty.Output().WriteString("\n") + return tty.readString(displayNone) +} + +func (tty *TTY) ReadPasswordClear() (string, error) { + s, err := tty.readString(displayMask) + tty.Output().WriteString( + strings.Repeat("\b", len(s)) + + strings.Repeat(" ", len(s)) + + strings.Repeat("\b", len(s))) + return s, err +} + +type WINSIZE struct { + W int + H int +} + +func (tty *TTY) SIGWINCH() chan WINSIZE { + return tty.sigwinch() +} diff --git a/vendor/github.com/mattn/go-tty/tty_bsd.go b/vendor/github.com/mattn/go-tty/tty_bsd.go new file mode 100644 index 000000000..e0a51fc0d --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_bsd.go @@ -0,0 +1,12 @@ +// +build darwin dragonfly freebsd netbsd openbsd + +package tty + +import ( + "syscall" +) + +const ( + ioctlReadTermios = syscall.TIOCGETA + ioctlWriteTermios = syscall.TIOCSETA +) diff --git a/vendor/github.com/mattn/go-tty/tty_linux.go b/vendor/github.com/mattn/go-tty/tty_linux.go new file mode 100644 index 000000000..1b9e8cef6 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_linux.go @@ -0,0 +1,8 @@ +// +build linux + +package tty + +const ( + ioctlReadTermios = 0x5401 // syscall.TCGETS + ioctlWriteTermios = 0x5402 // syscall.TCSETS +) diff --git a/vendor/github.com/mattn/go-tty/tty_plan9.go b/vendor/github.com/mattn/go-tty/tty_plan9.go new file mode 100644 index 000000000..e880e5147 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_plan9.go @@ -0,0 +1,63 @@ +package tty + +import ( + "bufio" + "os" + "syscall" +) + +type TTY struct { + in *os.File + bin *bufio.Reader + out *os.File +} + +func open() (*TTY, error) { + tty := new(TTY) + + in, err := os.Open("/dev/cons") + if err != nil { + return nil, err + } + tty.in = in + tty.bin = bufio.NewReader(in) + + out, err := os.OpenFile("/dev/cons", syscall.O_WRONLY, 0) + if err != nil { + return nil, err + } + tty.out = out + + return tty, nil +} + +func (tty *TTY) buffered() bool { + return tty.bin.Buffered() > 0 +} + +func (tty *TTY) readRune() (rune, error) { + r, _, err := tty.bin.ReadRune() + return r, err +} + +func (tty *TTY) close() (err error) { + if err2 := tty.in.Close(); err2 != nil { + err = err2 + } + if err2 := tty.out.Close(); err2 != nil { + err = err2 + } + return +} + +func (tty *TTY) size() (int, int, error) { + return 80, 24, nil +} + +func (tty *TTY) input() *os.File { + return tty.in +} + +func (tty *TTY) output() *os.File { + return tty.out +} diff --git a/vendor/github.com/mattn/go-tty/tty_unix.go b/vendor/github.com/mattn/go-tty/tty_unix.go new file mode 100644 index 000000000..1ccdf5c95 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_unix.go @@ -0,0 +1,130 @@ +// +build !windows +// +build !plan9 + +package tty + +import ( + "bufio" + "os" + "os/signal" + "syscall" + "unsafe" + + "golang.org/x/sys/unix" +) + +type TTY struct { + in *os.File + bin *bufio.Reader + out *os.File + termios syscall.Termios + ws chan WINSIZE + ss chan os.Signal +} + +func open() (*TTY, error) { + tty := new(TTY) + + in, err := os.Open("/dev/tty") + if err != nil { + return nil, err + } + tty.in = in + tty.bin = bufio.NewReader(in) + + out, err := os.OpenFile("/dev/tty", syscall.O_WRONLY, 0) + if err != nil { + return nil, err + } + tty.out = out + + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0); err != 0 { + return nil, err + } + newios := tty.termios + newios.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF + newios.Lflag &^= syscall.ECHO | syscall.ICANON /*| syscall.ISIG*/ + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&newios)), 0, 0, 0); err != 0 { + return nil, err + } + + tty.ws = make(chan WINSIZE) + tty.ss = make(chan os.Signal, 1) + signal.Notify(tty.ss, syscall.SIGWINCH) + go func() { + for sig := range tty.ss { + switch sig { + case syscall.SIGWINCH: + if w, h, err := tty.size(); err == nil { + tty.ws <- WINSIZE{ + W: w, + H: h, + } + } + default: + } + } + }() + return tty, nil +} + +func (tty *TTY) buffered() bool { + return tty.bin.Buffered() > 0 +} + +func (tty *TTY) readRune() (rune, error) { + r, _, err := tty.bin.ReadRune() + return r, err +} + +func (tty *TTY) close() error { + close(tty.ss) + close(tty.ws) + _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.in.Fd()), ioctlWriteTermios, uintptr(unsafe.Pointer(&tty.termios)), 0, 0, 0) + return err +} + +func (tty *TTY) size() (int, int, error) { + var dim [4]uint16 + if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(tty.out.Fd()), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dim)), 0, 0, 0); err != 0 { + return -1, -1, err + } + return int(dim[1]), int(dim[0]), nil +} + +func (tty *TTY) input() *os.File { + return tty.in +} + +func (tty *TTY) output() *os.File { + return tty.out +} + +func (tty *TTY) raw() (func() error, error) { + termios, err := unix.IoctlGetTermios(int(tty.in.Fd()), ioctlReadTermios) + if err != nil { + return nil, err + } + + termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON + termios.Oflag &^= unix.OPOST + termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN + termios.Cflag &^= unix.CSIZE | unix.PARENB + termios.Cflag |= unix.CS8 + termios.Cc[unix.VMIN] = 1 + termios.Cc[unix.VTIME] = 0 + if err := unix.IoctlSetTermios(int(tty.in.Fd()), ioctlWriteTermios, termios); err != nil { + return nil, err + } + + return func() error { + if err := unix.IoctlSetTermios(int(tty.in.Fd()), ioctlWriteTermios, termios); err != nil { + return err + } + return nil + }, nil +} + +func (tty *TTY) sigwinch() chan WINSIZE { + return tty.ws +} diff --git a/vendor/github.com/mattn/go-tty/tty_windows.go b/vendor/github.com/mattn/go-tty/tty_windows.go new file mode 100644 index 000000000..db5371845 --- /dev/null +++ b/vendor/github.com/mattn/go-tty/tty_windows.go @@ -0,0 +1,324 @@ +// +build windows + +package tty + +import ( + "os" + "syscall" + "unsafe" + + "github.com/mattn/go-isatty" +) + +const ( + rightAltPressed = 1 + leftAltPressed = 2 + rightCtrlPressed = 4 + leftCtrlPressed = 8 + shiftPressed = 0x0010 + ctrlPressed = rightCtrlPressed | leftCtrlPressed + altPressed = rightAltPressed | leftAltPressed +) + +const ( + enableProcessedInput = 0x1 + enableLineInput = 0x2 + enableEchoInput = 0x4 + enableWindowInput = 0x8 + enableMouseInput = 0x10 + enableInsertMode = 0x20 + enableQuickEditMode = 0x40 + enableExtendedFlag = 0x80 + + enableProcessedOutput = 1 + enableWrapAtEolOutput = 2 + + keyEvent = 0x1 + mouseEvent = 0x2 + windowBufferSizeEvent = 0x4 +) + +var kernel32 = syscall.NewLazyDLL("kernel32.dll") + +var ( + procAllocConsole = kernel32.NewProc("AllocConsole") + procSetStdHandle = kernel32.NewProc("SetStdHandle") + procGetStdHandle = kernel32.NewProc("GetStdHandle") + procSetConsoleScreenBufferSize = kernel32.NewProc("SetConsoleScreenBufferSize") + procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + procWriteConsoleOutputCharacter = kernel32.NewProc("WriteConsoleOutputCharacterW") + procWriteConsoleOutputAttribute = kernel32.NewProc("WriteConsoleOutputAttribute") + procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") + procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") + procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") + procReadConsoleInput = kernel32.NewProc("ReadConsoleInputW") + procGetConsoleMode = kernel32.NewProc("GetConsoleMode") + procSetConsoleMode = kernel32.NewProc("SetConsoleMode") + procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") + procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") + procScrollConsoleScreenBuffer = kernel32.NewProc("ScrollConsoleScreenBufferW") +) + +type wchar uint16 +type short int16 +type dword uint32 +type word uint16 + +type coord struct { + x short + y short +} + +type smallRect struct { + left short + top short + right short + bottom short +} + +type consoleScreenBufferInfo struct { + size coord + cursorPosition coord + attributes word + window smallRect + maximumWindowSize coord +} + +type consoleCursorInfo struct { + size dword + visible int32 +} + +type inputRecord struct { + eventType word + _ [2]byte + event [16]byte +} + +type keyEventRecord struct { + keyDown int32 + repeatCount word + virtualKeyCode word + virtualScanCode word + unicodeChar wchar + controlKeyState dword +} + +type windowBufferSizeRecord struct { + size coord +} + +type mouseEventRecord struct { + mousePos coord + buttonState dword + controlKeyState dword + eventFlags dword +} + +type charInfo struct { + unicodeChar wchar + attributes word +} + +type TTY struct { + in *os.File + out *os.File + st uint32 + rs []rune + ws chan WINSIZE +} + +func readConsoleInput(fd uintptr, record *inputRecord) (err error) { + var w uint32 + r1, _, err := procReadConsoleInput.Call(fd, uintptr(unsafe.Pointer(record)), 1, uintptr(unsafe.Pointer(&w))) + if r1 == 0 { + return err + } + return nil +} + +func open() (*TTY, error) { + tty := new(TTY) + if false && isatty.IsTerminal(os.Stdin.Fd()) { + tty.in = os.Stdin + } else { + in, err := syscall.Open("CONIN$", syscall.O_RDWR, 0) + if err != nil { + return nil, err + } + + tty.in = os.NewFile(uintptr(in), "/dev/tty") + } + + if isatty.IsTerminal(os.Stdout.Fd()) { + tty.out = os.Stdout + } else { + procAllocConsole.Call() + out, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0) + if err != nil { + return nil, err + } + + tty.out = os.NewFile(uintptr(out), "/dev/tty") + } + + h := tty.in.Fd() + var st uint32 + r1, _, err := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&st))) + if r1 == 0 { + return nil, err + } + tty.st = st + + st &^= enableEchoInput + st &^= enableInsertMode + st &^= enableLineInput + st &^= enableMouseInput + st &^= enableWindowInput + st &^= enableExtendedFlag + st &^= enableQuickEditMode + st &^= enableProcessedInput + + // ignore error + procSetConsoleMode.Call(h, uintptr(st)) + + tty.ws = make(chan WINSIZE) + + return tty, nil +} + +func (tty *TTY) buffered() bool { + return len(tty.rs) > 0 +} + +func (tty *TTY) readRune() (rune, error) { + if len(tty.rs) > 0 { + r := tty.rs[0] + tty.rs = tty.rs[1:] + return r, nil + } + var ir inputRecord + err := readConsoleInput(tty.in.Fd(), &ir) + if err != nil { + return 0, err + } + + switch ir.eventType { + case windowBufferSizeEvent: + wr := (*windowBufferSizeRecord)(unsafe.Pointer(&ir.event)) + tty.ws <- WINSIZE{ + W: int(wr.size.x), + H: int(wr.size.y), + } + case keyEvent: + kr := (*keyEventRecord)(unsafe.Pointer(&ir.event)) + if kr.keyDown != 0 { + if kr.controlKeyState&altPressed != 0 && kr.unicodeChar > 0 { + tty.rs = []rune{rune(kr.unicodeChar)} + return rune(0x1b), nil + } + if kr.unicodeChar > 0 { + if kr.controlKeyState&shiftPressed != 0 { + switch kr.unicodeChar { + case 0x09: + tty.rs = []rune{0x5b, 0x5a} + return rune(0x1b), nil + } + } + return rune(kr.unicodeChar), nil + } + vk := kr.virtualKeyCode + switch vk { + case 0x21: // page-up + tty.rs = []rune{0x5b, 0x35, 0x7e} + return rune(0x1b), nil + case 0x22: // page-down + tty.rs = []rune{0x5b, 0x36, 0x7e} + return rune(0x1b), nil + case 0x23: // end + tty.rs = []rune{0x5b, 0x46} + return rune(0x1b), nil + case 0x24: // home + tty.rs = []rune{0x5b, 0x48} + return rune(0x1b), nil + case 0x25: // left + tty.rs = []rune{0x5b, 0x44} + return rune(0x1b), nil + case 0x26: // up + tty.rs = []rune{0x5b, 0x41} + return rune(0x1b), nil + case 0x27: // right + tty.rs = []rune{0x5b, 0x43} + return rune(0x1b), nil + case 0x28: // down + tty.rs = []rune{0x5b, 0x42} + return rune(0x1b), nil + case 0x2e: // delete + tty.rs = []rune{0x5b, 0x33, 0x7e} + return rune(0x1b), nil + case 0x70, 0x71, 0x72, 0x73: // F1,F2,F3,F4 + tty.rs = []rune{0x5b, 0x4f, rune(vk) - 0x20} + return rune(0x1b), nil + case 0x074, 0x75, 0x76, 0x77: // F5,F6,F7,F8 + tty.rs = []rune{0x5b, 0x31, rune(vk) - 0x3f, 0x7e} + return rune(0x1b), nil + case 0x78, 0x79: // F9,F10 + tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x48, 0x7e} + return rune(0x1b), nil + case 0x7a, 0x7b: // F11,F12 + tty.rs = []rune{0x5b, 0x32, rune(vk) - 0x47, 0x7e} + return rune(0x1b), nil + } + return 0, nil + } + } + return 0, nil +} + +func (tty *TTY) close() error { + close(tty.ws) + procSetConsoleMode.Call(tty.in.Fd(), uintptr(tty.st)) + return nil +} + +func (tty *TTY) size() (int, int, error) { + var csbi consoleScreenBufferInfo + r1, _, err := procGetConsoleScreenBufferInfo.Call(tty.out.Fd(), uintptr(unsafe.Pointer(&csbi))) + if r1 == 0 { + return 0, 0, err + } + return int(csbi.window.right - csbi.window.left + 1), int(csbi.window.bottom - csbi.window.top + 1), nil +} + +func (tty *TTY) input() *os.File { + return tty.in +} + +func (tty *TTY) output() *os.File { + return tty.out +} + +func (tty *TTY) raw() (func() error, error) { + var st uint32 + r1, _, err := procGetConsoleMode.Call(tty.in.Fd(), uintptr(unsafe.Pointer(&st))) + if r1 == 0 { + return nil, err + } + mode := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput) + r1, _, err = procSetConsoleMode.Call(tty.in.Fd(), uintptr(mode)) + if r1 == 0 { + return nil, err + } + return func() error { + r1, _, err := procSetConsoleMode.Call(tty.in.Fd(), uintptr(st)) + if r1 == 0 { + return err + } + return nil + }, nil +} + +func (tty *TTY) sigwinch() chan WINSIZE { + return tty.ws +} From 22df491c67e51f9086dc91a9dc5c57c1d18be00e Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Thu, 28 Feb 2019 14:40:55 +0100 Subject: [PATCH 5/9] mock tty using interfaces to test more easily --- packer/tty.go | 5 +++++ packer/ui.go | 4 ++-- packer/ui_test.go | 11 ++++++++++- 3 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 packer/tty.go diff --git a/packer/tty.go b/packer/tty.go new file mode 100644 index 000000000..533d75c46 --- /dev/null +++ b/packer/tty.go @@ -0,0 +1,5 @@ +package packer + +type TTY interface { + ReadString() (string, error) +} diff --git a/packer/ui.go b/packer/ui.go index 87bd854d7..64dcea48d 100644 --- a/packer/ui.go +++ b/packer/ui.go @@ -82,7 +82,7 @@ type BasicUi struct { ErrorWriter io.Writer l sync.Mutex interrupted bool - tty *tty.TTY + tty TTY StackableProgressBar } @@ -235,7 +235,7 @@ func (rw *BasicUi) Ask(query string) (string, error) { log.Printf("ui: scan err: %s", err) return } - result <- line + result <- strings.TrimSpace(line) }() select { diff --git a/packer/ui_test.go b/packer/ui_test.go index a1bb0193c..a6e026d1f 100644 --- a/packer/ui_test.go +++ b/packer/ui_test.go @@ -34,9 +34,18 @@ func testUi() *BasicUi { Reader: new(bytes.Buffer), Writer: new(bytes.Buffer), ErrorWriter: new(bytes.Buffer), + tty: new(testTTY), } } +type testTTY struct { + say string +} + +func (tttyy *testTTY) ReadString() (string, error) { + return tttyy.say, nil +} + func TestColoredUi(t *testing.T) { bufferUi := testUi() ui := &ColoredUi{UiColorYellow, UiColorRed, bufferUi} @@ -217,7 +226,7 @@ func TestBasicUi_Ask(t *testing.T) { for _, testCase := range testCases { // Because of the internal bufio we can't easily reset the input, so create a new one each time bufferUi := testUi() - writeReader(bufferUi, testCase.Input) + bufferUi.tty = &testTTY{testCase.Input} actual, err = bufferUi.Ask(testCase.Prompt) if err != nil { From 94d6fc10f5a754041889b14a480192ce8c8c748b Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Wed, 6 Mar 2019 15:52:59 +0100 Subject: [PATCH 6/9] init tty in main --- main.go | 7 +++++++ packer/ui.go | 14 ++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/main.go b/main.go index e7f750e2d..e47ea05e1 100644 --- a/main.go +++ b/main.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/packer/packer/plugin" "github.com/hashicorp/packer/packer/tmp" "github.com/hashicorp/packer/version" + "github.com/mattn/go-tty" "github.com/mitchellh/cli" "github.com/mitchellh/panicwrap" "github.com/mitchellh/prefixedio" @@ -185,11 +186,17 @@ func wrappedMain() int { defer plugin.CleanupClients() + tty, err := tty.Open() + if err != nil { + log.Printf("running packer without a tty: %s", err) + } + // Setup the UI if we're being machine-readable var ui packer.Ui = &packer.BasicUi{ Reader: os.Stdin, Writer: os.Stdout, ErrorWriter: os.Stdout, + TTY: tty, } if machineReadable { ui = &packer.MachineReadableUi{ diff --git a/packer/ui.go b/packer/ui.go index 64dcea48d..cc4128a07 100644 --- a/packer/ui.go +++ b/packer/ui.go @@ -14,8 +14,6 @@ import ( "syscall" "time" "unicode" - - "github.com/mattn/go-tty" ) type UiColor uint @@ -82,7 +80,7 @@ type BasicUi struct { ErrorWriter io.Writer l sync.Mutex interrupted bool - tty TTY + TTY TTY StackableProgressBar } @@ -210,12 +208,8 @@ func (rw *BasicUi) Ask(query string) (string, error) { return "", errors.New("interrupted") } - if rw.tty == nil { - var err error - rw.tty, err = tty.Open() - if err != nil { - return "", fmt.Errorf("tty open: %s", err) - } + if rw.TTY == nil { + return "", errors.New("no available tty") } sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) @@ -230,7 +224,7 @@ func (rw *BasicUi) Ask(query string) (string, error) { result := make(chan string, 1) go func() { - line, err := rw.tty.ReadString() + line, err := rw.TTY.ReadString() if err != nil { log.Printf("ui: scan err: %s", err) return From 55261f1bf3101f8008113a7507f4623ce7cd489e Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Wed, 6 Mar 2019 15:54:10 +0100 Subject: [PATCH 7/9] Update ui_test.go fix typo and tty usage --- packer/ui_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packer/ui_test.go b/packer/ui_test.go index a6e026d1f..8a20f0a2e 100644 --- a/packer/ui_test.go +++ b/packer/ui_test.go @@ -34,7 +34,7 @@ func testUi() *BasicUi { Reader: new(bytes.Buffer), Writer: new(bytes.Buffer), ErrorWriter: new(bytes.Buffer), - tty: new(testTTY), + TTY: new(testTTY), } } @@ -42,8 +42,8 @@ type testTTY struct { say string } -func (tttyy *testTTY) ReadString() (string, error) { - return tttyy.say, nil +func (tty *testTTY) ReadString() (string, error) { + return tty.say, nil } func TestColoredUi(t *testing.T) { @@ -226,7 +226,7 @@ func TestBasicUi_Ask(t *testing.T) { for _, testCase := range testCases { // Because of the internal bufio we can't easily reset the input, so create a new one each time bufferUi := testUi() - bufferUi.tty = &testTTY{testCase.Input} + bufferUi.TTY = &testTTY{testCase.Input} actual, err = bufferUi.Ask(testCase.Prompt) if err != nil { From d177a2647abd9f271a700f734b64eb2a873337af Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Wed, 6 Mar 2019 16:47:26 +0100 Subject: [PATCH 8/9] stop piping stdin setupStdin switched out stdin for a pipe so that we could close the writer end of the pipe when we receive an interrupt so plugins blocked on reading from stdin are unblocked. But this is now handled using contexts. --- main.go | 36 +++++++++++++++++------------------- stdin.go | 38 -------------------------------------- 2 files changed, 17 insertions(+), 57 deletions(-) delete mode 100644 stdin.go diff --git a/main.go b/main.go index e47ea05e1..155e57acf 100644 --- a/main.go +++ b/main.go @@ -145,12 +145,6 @@ func wrappedMain() int { inPlugin := os.Getenv(plugin.MagicCookieKey) == plugin.MagicCookieValue - // Prepare stdin for plugin usage by switching it to a pipe - // But do not switch to pipe in plugin - if !inPlugin { - setupStdin() - } - config, err := loadConfig() if err != nil { fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err) @@ -186,18 +180,7 @@ func wrappedMain() int { defer plugin.CleanupClients() - tty, err := tty.Open() - if err != nil { - log.Printf("running packer without a tty: %s", err) - } - - // Setup the UI if we're being machine-readable - var ui packer.Ui = &packer.BasicUi{ - Reader: os.Stdin, - Writer: os.Stdout, - ErrorWriter: os.Stdout, - TTY: tty, - } + var ui packer.Ui if machineReadable { ui = &packer.MachineReadableUi{ Writer: os.Stdout, @@ -209,8 +192,23 @@ func wrappedMain() int { fmt.Fprintf(os.Stderr, "Packer failed to initialize UI: %s\n", err) return 1 } + } else { + var TTY packer.TTY + if !inPlugin { + var err error + TTY, err = tty.Open() + if err != nil { + fmt.Fprintf(os.Stderr, "No tty available: %s\n", err) + } + } + // Setup the UI if we're being machine-readable + ui = &packer.BasicUi{ + Reader: os.Stdin, + Writer: os.Stdout, + ErrorWriter: os.Stdout, + TTY: TTY, + } } - // Create the CLI meta CommandMeta = &command.Meta{ CoreConfig: &packer.CoreConfig{ diff --git a/stdin.go b/stdin.go deleted file mode 100644 index 758cc6864..000000000 --- a/stdin.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -import ( - "io" - "log" - "os" - "os/signal" - "syscall" -) - -// setupStdin switches out stdin for a pipe. We do this so that we can -// close the writer end of the pipe when we receive an interrupt so plugins -// blocked on reading from stdin are unblocked. -func setupStdin() { - // Create the pipe and swap stdin for the reader end - r, w, _ := os.Pipe() - originalStdin := os.Stdin - os.Stdin = r - - // Create a goroutine that copies data from the original stdin - // into the writer end of the pipe forever. - go func() { - defer w.Close() - io.Copy(w, originalStdin) - }() - - // Register a signal handler for interrupt in order to close the - // writer end of our pipe so that readers get EOF downstream. - ch := make(chan os.Signal, 1) - signal.Notify(ch, os.Interrupt, syscall.SIGTERM) - - go func() { - defer signal.Stop(ch) - defer w.Close() - <-ch - log.Println("Closing stdin because interrupt received.") - }() -} From cda87d777f678fd7d4076d3ccd223bd654bac800 Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Thu, 7 Mar 2019 09:44:32 +0100 Subject: [PATCH 9/9] Update main.go move machine readable comment to a more logical place. --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 155e57acf..b8d116d2b 100644 --- a/main.go +++ b/main.go @@ -182,6 +182,7 @@ func wrappedMain() int { var ui packer.Ui if machineReadable { + // Setup the UI as we're being machine-readable ui = &packer.MachineReadableUi{ Writer: os.Stdout, } @@ -201,7 +202,6 @@ func wrappedMain() int { fmt.Fprintf(os.Stderr, "No tty available: %s\n", err) } } - // Setup the UI if we're being machine-readable ui = &packer.BasicUi{ Reader: os.Stdin, Writer: os.Stdout,