shell_liner.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. package shell
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/chrislusf/seaweedfs/weed/cluster"
  6. "github.com/chrislusf/seaweedfs/weed/pb"
  7. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  8. "github.com/chrislusf/seaweedfs/weed/pb/master_pb"
  9. "github.com/chrislusf/seaweedfs/weed/util/grace"
  10. "io"
  11. "math/rand"
  12. "os"
  13. "path"
  14. "regexp"
  15. "sort"
  16. "strings"
  17. "github.com/peterh/liner"
  18. )
  19. var (
  20. line *liner.State
  21. historyPath = path.Join(os.TempDir(), "weed-shell")
  22. )
  23. func RunShell(options ShellOptions) {
  24. sort.Slice(Commands, func(i, j int) bool {
  25. return strings.Compare(Commands[i].Name(), Commands[j].Name()) < 0
  26. })
  27. line = liner.NewLiner()
  28. defer line.Close()
  29. grace.OnInterrupt(func() {
  30. line.Close()
  31. })
  32. line.SetCtrlCAborts(true)
  33. line.SetTabCompletionStyle(liner.TabPrints)
  34. setCompletionHandler()
  35. loadHistory()
  36. defer saveHistory()
  37. reg, _ := regexp.Compile(`'.*?'|".*?"|\S+`)
  38. commandEnv := NewCommandEnv(&options)
  39. go commandEnv.MasterClient.KeepConnectedToMaster()
  40. commandEnv.MasterClient.WaitUntilConnected()
  41. if commandEnv.option.FilerAddress == "" {
  42. var filers []pb.ServerAddress
  43. commandEnv.MasterClient.WithClient(false, func(client master_pb.SeaweedClient) error {
  44. resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
  45. ClientType: cluster.FilerType,
  46. })
  47. if err != nil {
  48. return err
  49. }
  50. for _, clusterNode := range resp.ClusterNodes {
  51. filers = append(filers, pb.ServerAddress(clusterNode.Address))
  52. }
  53. return nil
  54. })
  55. fmt.Printf("master: %s ", *options.Masters)
  56. if len(filers) > 0 {
  57. fmt.Printf("filers: %v", filers)
  58. commandEnv.option.FilerAddress = filers[rand.Intn(len(filers))]
  59. }
  60. fmt.Println()
  61. }
  62. if commandEnv.option.FilerAddress != "" {
  63. commandEnv.WithFilerClient(false, func(filerClient filer_pb.SeaweedFilerClient) error {
  64. resp, err := filerClient.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
  65. if err != nil {
  66. return err
  67. }
  68. if resp.ClusterId != "" {
  69. fmt.Printf(`
  70. ---
  71. Free Monitoring Data URL:
  72. https://cloud.seaweedfs.com/ui/%s
  73. ---
  74. `, resp.ClusterId)
  75. }
  76. return nil
  77. })
  78. }
  79. for {
  80. cmd, err := line.Prompt("> ")
  81. if err != nil {
  82. if err != io.EOF {
  83. fmt.Printf("%v\n", err)
  84. }
  85. return
  86. }
  87. for _, c := range strings.Split(cmd, ";") {
  88. if processEachCmd(reg, c, commandEnv) {
  89. return
  90. }
  91. }
  92. }
  93. }
  94. func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool {
  95. cmds := reg.FindAllString(cmd, -1)
  96. line.AppendHistory(cmd)
  97. if len(cmds) == 0 {
  98. return false
  99. } else {
  100. args := make([]string, len(cmds[1:]))
  101. for i := range args {
  102. args[i] = strings.Trim(string(cmds[1+i]), "\"'")
  103. }
  104. cmd := cmds[0]
  105. if cmd == "help" || cmd == "?" {
  106. printHelp(cmds)
  107. } else if cmd == "exit" || cmd == "quit" {
  108. return true
  109. } else {
  110. foundCommand := false
  111. for _, c := range Commands {
  112. if c.Name() == cmd || c.Name() == "fs."+cmd {
  113. if err := c.Do(args, commandEnv, os.Stdout); err != nil {
  114. fmt.Fprintf(os.Stderr, "error: %v\n", err)
  115. }
  116. foundCommand = true
  117. }
  118. }
  119. if !foundCommand {
  120. fmt.Fprintf(os.Stderr, "unknown command: %v\n", cmd)
  121. }
  122. }
  123. }
  124. return false
  125. }
  126. func printGenericHelp() {
  127. msg :=
  128. `Type: "help <command>" for help on <command>. Most commands support "<command> -h" also for options.
  129. `
  130. fmt.Print(msg)
  131. for _, c := range Commands {
  132. helpTexts := strings.SplitN(c.Help(), "\n", 2)
  133. fmt.Printf(" %-30s\t# %s \n", c.Name(), helpTexts[0])
  134. }
  135. }
  136. func printHelp(cmds []string) {
  137. args := cmds[1:]
  138. if len(args) == 0 {
  139. printGenericHelp()
  140. } else if len(args) > 1 {
  141. fmt.Println()
  142. } else {
  143. cmd := strings.ToLower(args[0])
  144. for _, c := range Commands {
  145. if c.Name() == cmd {
  146. fmt.Printf(" %s\t# %s\n", c.Name(), c.Help())
  147. }
  148. }
  149. }
  150. }
  151. func setCompletionHandler() {
  152. line.SetCompleter(func(line string) (c []string) {
  153. for _, i := range Commands {
  154. if strings.HasPrefix(i.Name(), strings.ToLower(line)) {
  155. c = append(c, i.Name())
  156. }
  157. }
  158. return
  159. })
  160. }
  161. func loadHistory() {
  162. if f, err := os.Open(historyPath); err == nil {
  163. line.ReadHistory(f)
  164. f.Close()
  165. }
  166. }
  167. func saveHistory() {
  168. if f, err := os.Create(historyPath); err != nil {
  169. fmt.Printf("Error creating history file: %v\n", err)
  170. } else {
  171. if _, err = line.WriteHistory(f); err != nil {
  172. fmt.Printf("Error writing history file: %v\n", err)
  173. }
  174. f.Close()
  175. }
  176. }