Skip to content

Commit

Permalink
go/oasis-node/cmd/common: Add Isatty and use it
Browse files Browse the repository at this point in the history
Having to use a command line flag when not running in an interactive
terminal is somewhat silly, and recursively calling the Fisher Price
Baby's First Interactive Prompt routine till the command exhauses the
heap and crashes when not run in a tty is even sillier.
  • Loading branch information
Yawning committed Jul 30, 2020
1 parent 8162e5f commit 0d720dd
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 18 deletions.
42 changes: 26 additions & 16 deletions go/oasis-node/cmd/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,24 +278,34 @@ func ExportEntity(signerBackend, entityDir string) error {
return err
}

// GetUserConfirmation scans the input for user's confirmation.
// GetUserConfirmation displays the prompt, and scans the input for
// the user's confirmation, until the user either explicitly confirms
// or rejects the prompt.
//
// If the user's response is not recognized, it prompts the user again.
func GetUserConfirmation() bool {
var response string

_, err := fmt.Scanln(&response)
if err != nil && err.Error() != "unexpected newline" {
rootLog.Error("Error reading from line", "err", err)
// Note: If standard input is not a tty, this will omit displaying
// the prompt, and assume the user entered yes.
func GetUserConfirmation(prompt string) bool {
if !Isatty(os.Stdin.Fd()) {
return true
}

switch strings.ToLower(response) {
case "y", "yes":
return true
case "n", "no":
return false
default:
fmt.Printf("Unrecognized response: '%s'. Please, type (y)es or (n)o: ", response)
return GetUserConfirmation()
fmt.Printf("%s", prompt)

var response string
for {
_, err := fmt.Scanln(&response)
if err != nil && err.Error() != "unexpected newline" {
rootLog.Error("Error reading from line", "err", err)
continue
}

switch strings.ToLower(response) {
case "y", "yes":
return true
case "n", "no":
return false
default:
fmt.Printf("Unrecognized response: '%s'. Please, type (y)es or (n)o: ", response)
}
}
}
3 changes: 1 addition & 2 deletions go/oasis-node/cmd/common/consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ func SignAndSaveTx(ctx context.Context, tx *transaction.Transaction) {
tx.PrettyPrint(ctx, " ", os.Stdout)

if !cmdFlags.AssumeYes() && cmdSigner.Backend() == signerFile.SignerName {
fmt.Printf("\nAre you sure you want to continue? (y)es/(n)o ")
if !cmdCommon.GetUserConfirmation() {
if !cmdCommon.GetUserConfirmation("\nAre you sure you want to continue? (y)es/(n)o: ") {
os.Exit(1)
}
}
Expand Down
31 changes: 31 additions & 0 deletions go/oasis-node/cmd/common/isatty.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package common

import (
"syscall"
"unsafe"
)

// Isatty returns true iff the provided file descriptor is a terminal.
func Isatty(fd uintptr) bool {
var attrs syscall.Termios

// This could examine the error more specifically to see if
// something really strange is going on since we expect it
// to return 0 or ENOTTY all the time, but, "the messed up
// thing the user passed in that makes it complain" also
// is not a tty.
//
// And yes, this is the standard way of doing this, see your
// libc implementation of choice.
_, _, errno := syscall.Syscall6(
syscall.SYS_IOCTL,
fd,
syscall.TCGETS,
uintptr(unsafe.Pointer(&attrs)),
0,
0,
0,
)

return errno == 0
}

0 comments on commit 0d720dd

Please sign in to comment.