go: x/crypto/ssh/terminal: ReadPassword doesn't work on redirected stdin giving inappropriate ioctl for device
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (go version)?
go version go1.8 linux/amd64
What operating system and processor architecture are you using (go env)?
GOARCH=“amd64” GOBIN=“” GOEXE=“” GOHOSTARCH=“amd64” GOHOSTOS=“linux” GOOS=“linux” GOPATH=“/home/ncw/go” GORACE=“” GOROOT=“/opt/go/go1.8” GOTOOLDIR=“/opt/go/go1.8/pkg/tool/linux_amd64” GCCGO=“gccgo” CC=“gcc” GOGCCFLAGS=“-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build011975399=/tmp/go-build -gno-record-gcc-switches” CXX=“g++” CGO_ENABLED=“1” PKG_CONFIG=“pkg-config” CGO_CFLAGS=“-g -O2” CGO_CPPFLAGS=“” CGO_CXXFLAGS=“-g -O2” CGO_FFLAGS=“-g -O2” CGO_LDFLAGS=“-g -O2”
What did you do?
Use ReadPassword with redirected stdin - it gives error “inappropriate ioctl for device”
Save this code as readpass.go
$ go build readpass.go
$ ./readpass
2017/04/10 16:18:22 Read "hello"
$ echo "hello" | ./readpass
2017/04/10 16:18:34 inappropriate ioctl for device
$
What did you expect to see?
2017/04/10 16:18:22 Read "hello"
I would expect ReadPass to figure out that it is not reading from a terminal before issuing ioctls that are terminal specific.
What did you see instead?
2017/04/10 16:18:34 inappropriate ioctl for device
Originally reported in: https://github.com/ncw/rclone/issues/1308
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 9
- Comments: 26 (7 by maintainers)
@ncw the only solution I know is to open the tty and read from it, something like:
I end up to this situation too. My specific case is exactly like
#2. Here is what I came up with:@ncw, at first I didn’t like the idea of changing
terminal.ReadPassword, because users have different needs and I like the standard function to be as simple as possible, but now I realize that the code to handle use case #2 is already here, and the change would be trivial.Ok, I would expect a function called
terminal.ReadPasswordto only work on a terminal, but the bad thing is that currently, use case#2requires users to essentially duplicate the readPasswordLine function (possibly in an unsafe way withbufio).The code could be changed from this:
to this:
Users that only want a terminal could still use the
IsTerminalcheck or open/dev/ttydirectly.Possible problems:
ENOTTYerror and other errors (in the latter case it should fail, to avoid the risk of accidentally displaying the password).@truongnmt The
bufio.Readertries to fill its own buffer but doesn’t wait for it to be full, so if it works for you it is probably because the timing is right (no more than the first password is available at the time buffering takes place).For example:
Here is a playground demonstration: https://play.golang.org/p/uxSMCw7anP4
Notice that
terminal.ReadPassworditself also avoids buffering. In fact its implementation is similar to mine, see here.@truongnmt your solution may be fine for your use case, but be aware that the buffering will generally prevent further use of standard input.
For example suppose that you need to read two passwords. Calling your
readPasswordfunction twice won’t work, because thebufio.Readerof the first call will probably buffer more than the first password, and then such buffer is discarded and never seen by the second call. If it’s not a second password, it may be some other kind of input.Here’s a variation that doesn’t have the buffering problem:
@truongnmt you also forgot to trim the final
\nfrom thereader.ReadStringoutput (notice thatterminal.ReadPassworddoes not include the\nin its output).