main.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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. fmt.Printf("Filter String: %s\n", respJSON["status"].Filter)
  70. }
  71. return nil
  72. }
  73. func pauseCmd(args []string) error {
  74. var dontChangeAgain time.Duration
  75. flagset := flag.NewFlagSet(args[0], flag.ExitOnError)
  76. waitTime := flagset.Duration("wait-time", dontChangeAgain, "How long to wait before untoggling the state, defaults to never")
  77. flagset.Parse(args[1:])
  78. postBody := make(map[string][]string)
  79. if *waitTime != dontChangeAgain {
  80. postBody["waitTime"] = []string{fmt.Sprintf("%s", *waitTime)}
  81. }
  82. if globalOpts.debug {
  83. fmt.Println("[DEBUG] postBody:", postBody)
  84. }
  85. resp, err := ctl.PostForm("http://unix/pause/toggle", postBody)
  86. if globalOpts.verbose {
  87. defer resp.Body.Close()
  88. bodyBytes, err := ioutil.ReadAll(resp.Body)
  89. if err != nil {
  90. return err
  91. }
  92. fmt.Println(string(bodyBytes))
  93. }
  94. return err
  95. }
  96. func filterCmd(args []string) error {
  97. filter := strings.Join(args[1:], " ")
  98. postBody := make(map[string][]string)
  99. postBody["where"] = []string{filter}
  100. if globalOpts.debug {
  101. fmt.Println("[DEBUG] postBody:", postBody)
  102. }
  103. resp, err := ctl.PostForm("http://unix/filter", postBody)
  104. if resp.StatusCode != 200 || globalOpts.verbose {
  105. defer resp.Body.Close()
  106. bodyBytes, err := ioutil.ReadAll(resp.Body)
  107. if err != nil {
  108. return err
  109. }
  110. fmt.Println(string(bodyBytes))
  111. }
  112. return err
  113. }
  114. func reloadCmd(args []string) error {
  115. return nil
  116. }
  117. func restartCmd(args []string) error {
  118. return nil
  119. }
  120. func helpCmd(args []string) error {
  121. usage(os.Stdout)
  122. os.Exit(0)
  123. return nil
  124. }
  125. func usage(w io.Writer) {
  126. fmt.Fprintf(w, "USAGE: %s [globalOpts] [SUBCOMMAND] [opts]\n", os.Args[0])
  127. fmt.Fprintf(w, "\nSUBCOMMANDS:\n\n")
  128. for command := range cmdMap {
  129. fmt.Fprintf(w, "\t%s\n", command)
  130. }
  131. }
  132. func parseAndRun(args []string) {
  133. cmdName := args[0]
  134. cmdToRun, ok := cmdMap[cmdName]
  135. if !ok {
  136. fmt.Fprintf(os.Stderr, "Unknown subcommand: %s\n", cmdName)
  137. usage(os.Stderr)
  138. os.Exit(2)
  139. }
  140. // from here: https://gist.github.com/teknoraver/5ffacb8757330715bcbcc90e6d46ac74
  141. ctl = http.Client{
  142. Transport: &http.Transport{
  143. DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
  144. return net.Dial("unix", globalOpts.ctlSock)
  145. },
  146. },
  147. }
  148. err := cmdToRun(args)
  149. if err != nil {
  150. fmt.Fprintf(os.Stderr, "Error:\n%s\n", err)
  151. os.Exit(1)
  152. }
  153. }
  154. func main() {
  155. flag.StringVar(&globalOpts.ctlSock, "ctl-socket", "/tmp/circologd-ctl.sock",
  156. "Path to a unix domain socket for the control server; leave empty to disable")
  157. flag.BoolVar(&globalOpts.verbose, "verbose", false, "Print more output")
  158. flag.BoolVar(&globalOpts.debug, "debug", false, "Print debugging info")
  159. flag.Parse()
  160. args := flag.Args()
  161. if len(args) == 0 {
  162. usage(os.Stderr)
  163. os.Exit(-1)
  164. }
  165. parseAndRun(args)
  166. }