main.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. package main
  2. import (
  3. "context"
  4. "encoding/json"
  5. "flag"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "net"
  10. "net/http"
  11. "os"
  12. "strings"
  13. "time"
  14. "git.lattuga.net/boyska/circolog"
  15. )
  16. var globalOpts struct {
  17. ctlSock string
  18. verbose bool
  19. debug bool
  20. }
  21. var ctl http.Client
  22. type commandFunc func([]string) error
  23. var cmdMap map[string]commandFunc
  24. func init() {
  25. cmdMap = map[string]commandFunc{
  26. // TODO: implement set and get of config at runtime
  27. //"set": setCmd,
  28. //"get": getCmd,
  29. "status": statusCmd,
  30. "pause": pauseCmd,
  31. "filter": filterCmd,
  32. "reload": reloadCmd,
  33. "restart": restartCmd,
  34. "help": helpCmd,
  35. }
  36. }
  37. //func setCmd(ctlSock string, args []string) error {}
  38. //func getCmd(ctlSock string, args []string) error {}
  39. func statusCmd(args []string) error {
  40. flagset := flag.NewFlagSet(args[0], flag.ExitOnError)
  41. outFormat := flagset.String("format", "plain", "Which format to use as output for this command (json, pretty, plain)")
  42. flagset.Parse(args[1:])
  43. resp, err := ctl.Get("http://unix/status")
  44. if err != nil {
  45. return err
  46. }
  47. defer resp.Body.Close()
  48. respBytes, err := ioutil.ReadAll(resp.Body)
  49. if err != nil {
  50. return err
  51. }
  52. respJSON := make(map[string]circolog.StatusResponse)
  53. err = json.Unmarshal(respBytes, &respJSON)
  54. if err != nil {
  55. return err
  56. }
  57. switch *outFormat {
  58. case "json":
  59. fmt.Printf("%s", string(respBytes))
  60. case "pretty":
  61. prettyJSON, err := json.MarshalIndent(respJSON, "", " ")
  62. if err != nil {
  63. return err
  64. }
  65. fmt.Printf("%s\n", prettyJSON)
  66. case "plain":
  67. fmt.Printf("Buffer Size: %d\n", respJSON["status"].Size)
  68. fmt.Printf("Server Status: %s\n", respJSON["status"].Status())
  69. }
  70. return nil
  71. }
  72. func pauseCmd(args []string) error {
  73. var dontChangeAgain time.Duration
  74. flagset := flag.NewFlagSet(args[0], flag.ExitOnError)
  75. waitTime := flagset.Duration("wait-time", dontChangeAgain, "How long to wait before untoggling the state, defaults to never")
  76. flagset.Parse(args[1:])
  77. postBody := make(map[string][]string)
  78. if *waitTime != dontChangeAgain {
  79. postBody["waitTime"] = []string{fmt.Sprintf("%s", *waitTime)}
  80. }
  81. if globalOpts.debug {
  82. fmt.Println("[DEBUG] postBody:", postBody)
  83. }
  84. resp, err := ctl.PostForm("http://unix/pause/toggle", postBody)
  85. if globalOpts.verbose {
  86. defer resp.Body.Close()
  87. bodyBytes, err := ioutil.ReadAll(resp.Body)
  88. if err != nil {
  89. return err
  90. }
  91. fmt.Println(string(bodyBytes))
  92. }
  93. return err
  94. }
  95. func filterCmd(args []string) error {
  96. filter := strings.Join(args[1:], " ")
  97. postBody := make(map[string][]string)
  98. postBody["where"] = []string{filter}
  99. if globalOpts.debug {
  100. fmt.Println("[DEBUG] postBody:", postBody)
  101. }
  102. resp, err := ctl.PostForm("http://unix/filter", postBody)
  103. if resp.StatusCode != 200 || globalOpts.verbose {
  104. defer resp.Body.Close()
  105. bodyBytes, err := ioutil.ReadAll(resp.Body)
  106. if err != nil {
  107. return err
  108. }
  109. fmt.Println(string(bodyBytes))
  110. }
  111. return err
  112. }
  113. func reloadCmd(args []string) error {
  114. return nil
  115. }
  116. func restartCmd(args []string) error {
  117. return nil
  118. }
  119. func helpCmd(args []string) error {
  120. usage(os.Stdout)
  121. os.Exit(0)
  122. return nil
  123. }
  124. func usage(w io.Writer) {
  125. fmt.Fprintf(w, "USAGE: %s [globalOpts] [SUBCOMMAND] [opts]\n", os.Args[0])
  126. fmt.Fprintf(w, "\nSUBCOMMANDS:\n\n")
  127. for command := range cmdMap {
  128. fmt.Fprintf(w, "\t%s\n", command)
  129. }
  130. }
  131. func parseAndRun(args []string) {
  132. cmdName := args[0]
  133. cmdToRun, ok := cmdMap[cmdName]
  134. if !ok {
  135. fmt.Fprintf(os.Stderr, "Unknown subcommand: %s\n", cmdName)
  136. usage(os.Stderr)
  137. os.Exit(2)
  138. }
  139. // from here: https://gist.github.com/teknoraver/5ffacb8757330715bcbcc90e6d46ac74
  140. ctl = http.Client{
  141. Transport: &http.Transport{
  142. DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
  143. return net.Dial("unix", globalOpts.ctlSock)
  144. },
  145. },
  146. }
  147. err := cmdToRun(args)
  148. if err != nil {
  149. fmt.Fprintf(os.Stderr, "Error:\n%s\n", err)
  150. os.Exit(1)
  151. }
  152. }
  153. func main() {
  154. flag.StringVar(&globalOpts.ctlSock, "ctl-socket", "/tmp/circologd-ctl.sock",
  155. "Path to a unix domain socket for the control server; leave empty to disable")
  156. flag.BoolVar(&globalOpts.verbose, "verbose", false, "Print more output")
  157. flag.BoolVar(&globalOpts.debug, "debug", false, "Print debugging info")
  158. flag.Parse()
  159. args := flag.Args()
  160. if len(args) == 0 {
  161. usage(os.Stderr)
  162. os.Exit(-1)
  163. }
  164. parseAndRun(args)
  165. }