diff options
Diffstat (limited to 'cmd/puppeth/wizard.go')
-rw-r--r-- | cmd/puppeth/wizard.go | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/cmd/puppeth/wizard.go b/cmd/puppeth/wizard.go new file mode 100644 index 000000000..92d7962a0 --- /dev/null +++ b/cmd/puppeth/wizard.go @@ -0,0 +1,229 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "bufio" + "encoding/json" + "fmt" + "io/ioutil" + "math/big" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "syscall" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/log" + "golang.org/x/crypto/ssh/terminal" +) + +// config contains all the configurations needed by puppeth that should be saved +// between sessions. +type config struct { + path string // File containing the configuration values + genesis *core.Genesis // Genesis block to cache for node deploys + bootFull []string // Bootnodes to always connect to by full nodes + bootLight []string // Bootnodes to always connect to by light nodes + ethstats string // Ethstats settings to cache for node deploys + + Servers []string `json:"servers,omitempty"` +} + +// flush dumps the contents of config to disk. +func (c config) flush() { + os.MkdirAll(filepath.Dir(c.path), 0755) + + sort.Strings(c.Servers) + out, _ := json.MarshalIndent(c, "", " ") + if err := ioutil.WriteFile(c.path, out, 0644); err != nil { + log.Warn("Failed to save puppeth configs", "file", c.path, "err", err) + } +} + +type wizard struct { + network string // Network name to manage + conf config // Configurations from previous runs + + servers map[string]*sshClient // SSH connections to servers to administer + services map[string][]string // Ethereum services known to be running on servers + + in *bufio.Reader // Wrapper around stdin to allow reading user input +} + +// read reads a single line from stdin, trimming if from spaces. +func (w *wizard) read() string { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + return strings.TrimSpace(text) +} + +// readString reads a single line from stdin, trimming if from spaces, enforcing +// non-emptyness. +func (w *wizard) readString() string { + for { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text != "" { + return text + } + } +} + +// readDefaultString reads a single line from stdin, trimming if from spaces. If +// an empty line is entered, the default value is returned. +func (w *wizard) readDefaultString(def string) string { + for { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text != "" { + return text + } + return def + } +} + +// readInt reads a single line from stdin, trimming if from spaces, enforcing it +// to parse into an integer. +func (w *wizard) readInt() int { + for { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text == "" { + continue + } + val, err := strconv.Atoi(strings.TrimSpace(text)) + if err != nil { + log.Error("Invalid input, expected integer", "err", err) + continue + } + return val + } +} + +// readDefaultInt reads a single line from stdin, trimming if from spaces, enforcing +// it to parse into an integer. If an empty line is entered, the default value is +// returned. +func (w *wizard) readDefaultInt(def int) int { + for { + fmt.Printf("> ") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text == "" { + return def + } + val, err := strconv.Atoi(strings.TrimSpace(text)) + if err != nil { + log.Error("Invalid input, expected integer", "err", err) + continue + } + return val + } +} + +// readPassword reads a single line from stdin, trimming it from the trailing new +// line and returns it. The input will not be echoed. +func (w *wizard) readPassword() string { + for { + fmt.Printf("> ") + text, err := terminal.ReadPassword(int(syscall.Stdin)) + if err != nil { + log.Crit("Failed to read password", "err", err) + } + fmt.Println() + return string(text) + } +} + +// readAddress reads a single line from stdin, trimming if from spaces and converts +// it to an Ethereum address. +func (w *wizard) readAddress() *common.Address { + for { + // Read the address from the user + fmt.Printf("> 0x") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text == "" { + return nil + } + // Make sure it looks ok and return it if so + if len(text) != 40 { + log.Error("Invalid address length, please retry") + continue + } + bigaddr, _ := new(big.Int).SetString(text, 16) + address := common.BigToAddress(bigaddr) + return &address + } +} + +// readDefaultAddress reads a single line from stdin, trimming if from spaces and +// converts it to an Ethereum address. If an empty line is entered, the default +// value is returned. +func (w *wizard) readDefaultAddress(def common.Address) common.Address { + for { + // Read the address from the user + fmt.Printf("> 0x") + text, err := w.in.ReadString('\n') + if err != nil { + log.Crit("Failed to read user input", "err", err) + } + if text = strings.TrimSpace(text); text == "" { + return def + } + // Make sure it looks ok and return it if so + if len(text) != 40 { + log.Error("Invalid address length, please retry") + continue + } + bigaddr, _ := new(big.Int).SetString(text, 16) + return common.BigToAddress(bigaddr) + } +} + +// readJSON reads a raw JSON message and returns it. +func (w *wizard) readJSON() string { + var blob json.RawMessage + + for { + fmt.Printf("> ") + if err := json.NewDecoder(w.in).Decode(&blob); err != nil { + log.Error("Invalid JSON, please try again", "err", err) + continue + } + return string(blob) + } +} |