wizard.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. // Copyright 2017 The go-ethereum Authors
  2. // This file is part of go-ethereum.
  3. //
  4. // go-ethereum is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // go-ethereum is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
  16. package main
  17. import (
  18. "bufio"
  19. "encoding/json"
  20. "fmt"
  21. "io/ioutil"
  22. "math/big"
  23. "os"
  24. "path/filepath"
  25. "sort"
  26. "strconv"
  27. "strings"
  28. "syscall"
  29. "github.com/ethereum/go-ethereum/common"
  30. "github.com/ethereum/go-ethereum/core"
  31. "github.com/ethereum/go-ethereum/log"
  32. "golang.org/x/crypto/ssh/terminal"
  33. )
  34. // config contains all the configurations needed by puppeth that should be saved
  35. // between sessions.
  36. type config struct {
  37. path string // File containing the configuration values
  38. genesis *core.Genesis // Genesis block to cache for node deploys
  39. bootFull []string // Bootnodes to always connect to by full nodes
  40. bootLight []string // Bootnodes to always connect to by light nodes
  41. ethstats string // Ethstats settings to cache for node deploys
  42. Servers []string `json:"servers,omitempty"`
  43. }
  44. // flush dumps the contents of config to disk.
  45. func (c config) flush() {
  46. os.MkdirAll(filepath.Dir(c.path), 0755)
  47. sort.Strings(c.Servers)
  48. out, _ := json.MarshalIndent(c, "", " ")
  49. if err := ioutil.WriteFile(c.path, out, 0644); err != nil {
  50. log.Warn("Failed to save puppeth configs", "file", c.path, "err", err)
  51. }
  52. }
  53. type wizard struct {
  54. network string // Network name to manage
  55. conf config // Configurations from previous runs
  56. servers map[string]*sshClient // SSH connections to servers to administer
  57. services map[string][]string // Ethereum services known to be running on servers
  58. in *bufio.Reader // Wrapper around stdin to allow reading user input
  59. }
  60. // read reads a single line from stdin, trimming if from spaces.
  61. func (w *wizard) read() string {
  62. fmt.Printf("> ")
  63. text, err := w.in.ReadString('\n')
  64. if err != nil {
  65. log.Crit("Failed to read user input", "err", err)
  66. }
  67. return strings.TrimSpace(text)
  68. }
  69. // readString reads a single line from stdin, trimming if from spaces, enforcing
  70. // non-emptyness.
  71. func (w *wizard) readString() string {
  72. for {
  73. fmt.Printf("> ")
  74. text, err := w.in.ReadString('\n')
  75. if err != nil {
  76. log.Crit("Failed to read user input", "err", err)
  77. }
  78. if text = strings.TrimSpace(text); text != "" {
  79. return text
  80. }
  81. }
  82. }
  83. // readDefaultString reads a single line from stdin, trimming if from spaces. If
  84. // an empty line is entered, the default value is returned.
  85. func (w *wizard) readDefaultString(def string) string {
  86. for {
  87. fmt.Printf("> ")
  88. text, err := w.in.ReadString('\n')
  89. if err != nil {
  90. log.Crit("Failed to read user input", "err", err)
  91. }
  92. if text = strings.TrimSpace(text); text != "" {
  93. return text
  94. }
  95. return def
  96. }
  97. }
  98. // readInt reads a single line from stdin, trimming if from spaces, enforcing it
  99. // to parse into an integer.
  100. func (w *wizard) readInt() int {
  101. for {
  102. fmt.Printf("> ")
  103. text, err := w.in.ReadString('\n')
  104. if err != nil {
  105. log.Crit("Failed to read user input", "err", err)
  106. }
  107. if text = strings.TrimSpace(text); text == "" {
  108. continue
  109. }
  110. val, err := strconv.Atoi(strings.TrimSpace(text))
  111. if err != nil {
  112. log.Error("Invalid input, expected integer", "err", err)
  113. continue
  114. }
  115. return val
  116. }
  117. }
  118. // readDefaultInt reads a single line from stdin, trimming if from spaces, enforcing
  119. // it to parse into an integer. If an empty line is entered, the default value is
  120. // returned.
  121. func (w *wizard) readDefaultInt(def int) int {
  122. for {
  123. fmt.Printf("> ")
  124. text, err := w.in.ReadString('\n')
  125. if err != nil {
  126. log.Crit("Failed to read user input", "err", err)
  127. }
  128. if text = strings.TrimSpace(text); text == "" {
  129. return def
  130. }
  131. val, err := strconv.Atoi(strings.TrimSpace(text))
  132. if err != nil {
  133. log.Error("Invalid input, expected integer", "err", err)
  134. continue
  135. }
  136. return val
  137. }
  138. }
  139. // readPassword reads a single line from stdin, trimming it from the trailing new
  140. // line and returns it. The input will not be echoed.
  141. func (w *wizard) readPassword() string {
  142. for {
  143. fmt.Printf("> ")
  144. text, err := terminal.ReadPassword(int(syscall.Stdin))
  145. if err != nil {
  146. log.Crit("Failed to read password", "err", err)
  147. }
  148. fmt.Println()
  149. return string(text)
  150. }
  151. }
  152. // readAddress reads a single line from stdin, trimming if from spaces and converts
  153. // it to an Ethereum address.
  154. func (w *wizard) readAddress() *common.Address {
  155. for {
  156. // Read the address from the user
  157. fmt.Printf("> 0x")
  158. text, err := w.in.ReadString('\n')
  159. if err != nil {
  160. log.Crit("Failed to read user input", "err", err)
  161. }
  162. if text = strings.TrimSpace(text); text == "" {
  163. return nil
  164. }
  165. // Make sure it looks ok and return it if so
  166. if len(text) != 40 {
  167. log.Error("Invalid address length, please retry")
  168. continue
  169. }
  170. bigaddr, _ := new(big.Int).SetString(text, 16)
  171. address := common.BigToAddress(bigaddr)
  172. return &address
  173. }
  174. }
  175. // readDefaultAddress reads a single line from stdin, trimming if from spaces and
  176. // converts it to an Ethereum address. If an empty line is entered, the default
  177. // value is returned.
  178. func (w *wizard) readDefaultAddress(def common.Address) common.Address {
  179. for {
  180. // Read the address from the user
  181. fmt.Printf("> 0x")
  182. text, err := w.in.ReadString('\n')
  183. if err != nil {
  184. log.Crit("Failed to read user input", "err", err)
  185. }
  186. if text = strings.TrimSpace(text); text == "" {
  187. return def
  188. }
  189. // Make sure it looks ok and return it if so
  190. if len(text) != 40 {
  191. log.Error("Invalid address length, please retry")
  192. continue
  193. }
  194. bigaddr, _ := new(big.Int).SetString(text, 16)
  195. return common.BigToAddress(bigaddr)
  196. }
  197. }
  198. // readJSON reads a raw JSON message and returns it.
  199. func (w *wizard) readJSON() string {
  200. var blob json.RawMessage
  201. for {
  202. fmt.Printf("> ")
  203. if err := json.NewDecoder(w.in).Decode(&blob); err != nil {
  204. log.Error("Invalid JSON, please try again", "err", err)
  205. continue
  206. }
  207. return string(blob)
  208. }
  209. }