package main import ( "context" "encoding/json" "flag" "fmt" "io" "io/ioutil" "net" "net/http" "os" "strings" "time" "git.lattuga.net/boyska/circolog" ) var globalOpts struct { ctlSock string verbose bool debug bool } var ctl http.Client type commandFunc func([]string) error var cmdMap map[string]commandFunc func init() { cmdMap = map[string]commandFunc{ // TODO: implement set and get of config at runtime //"set": setCmd, //"get": getCmd, "status": statusCmd, "pause": pauseCmd, "filter": filterCmd, "reload": reloadCmd, "restart": restartCmd, "help": helpCmd, } } //func setCmd(ctlSock string, args []string) error {} //func getCmd(ctlSock string, args []string) error {} func statusCmd(args []string) error { flagset := flag.NewFlagSet(args[0], flag.ExitOnError) outFormat := flagset.String("format", "plain", "Which format to use as output for this command (json, pretty, plain)") flagset.Parse(args[1:]) resp, err := ctl.Get("http://unix/status") if err != nil { return err } defer resp.Body.Close() respBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return err } respJSON := make(map[string]circolog.StatusResponse) err = json.Unmarshal(respBytes, &respJSON) if err != nil { return err } switch *outFormat { case "json": fmt.Printf("%s", string(respBytes)) case "pretty": prettyJSON, err := json.MarshalIndent(respJSON, "", " ") if err != nil { return err } fmt.Printf("%s\n", prettyJSON) case "plain": fmt.Printf("Buffer Size: %d\n", respJSON["status"].Size) fmt.Printf("Server Status: %s\n", respJSON["status"].Status()) } return nil } func pauseCmd(args []string) error { var dontChangeAgain time.Duration flagset := flag.NewFlagSet(args[0], flag.ExitOnError) waitTime := flagset.Duration("wait-time", dontChangeAgain, "How long to wait before untoggling the state, defaults to never") flagset.Parse(args[1:]) postBody := make(map[string][]string) if *waitTime != dontChangeAgain { postBody["waitTime"] = []string{fmt.Sprintf("%s", *waitTime)} } if globalOpts.debug { fmt.Println("[DEBUG] postBody:", postBody) } resp, err := ctl.PostForm("http://unix/pause/toggle", postBody) if globalOpts.verbose { defer resp.Body.Close() bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return err } fmt.Println(string(bodyBytes)) } return err } func filterCmd(args []string) error { filter := strings.Join(args[1:], " ") postBody := make(map[string][]string) postBody["where"] = []string{filter} if globalOpts.debug { fmt.Println("[DEBUG] postBody:", postBody) } resp, err := ctl.PostForm("http://unix/filter", postBody) if resp.StatusCode != 200 || globalOpts.verbose { defer resp.Body.Close() bodyBytes, err := ioutil.ReadAll(resp.Body) if err != nil { return err } fmt.Println(string(bodyBytes)) } return err } func reloadCmd(args []string) error { return nil } func restartCmd(args []string) error { return nil } func helpCmd(args []string) error { usage(os.Stdout) os.Exit(0) return nil } func usage(w io.Writer) { fmt.Fprintf(w, "USAGE: %s [globalOpts] [SUBCOMMAND] [opts]\n", os.Args[0]) fmt.Fprintf(w, "\nSUBCOMMANDS:\n\n") for command := range cmdMap { fmt.Fprintf(w, "\t%s\n", command) } } func parseAndRun(args []string) { cmdName := args[0] cmdToRun, ok := cmdMap[cmdName] if !ok { fmt.Fprintf(os.Stderr, "Unknown subcommand: %s\n", cmdName) usage(os.Stderr) os.Exit(2) } // from here: https://gist.github.com/teknoraver/5ffacb8757330715bcbcc90e6d46ac74 ctl = http.Client{ Transport: &http.Transport{ DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { return net.Dial("unix", globalOpts.ctlSock) }, }, } err := cmdToRun(args) if err != nil { fmt.Fprintf(os.Stderr, "Error:\n%s\n", err) os.Exit(1) } } func main() { flag.StringVar(&globalOpts.ctlSock, "ctl-socket", "/tmp/circologd-ctl.sock", "Path to a unix domain socket for the control server; leave empty to disable") flag.BoolVar(&globalOpts.verbose, "verbose", false, "Print more output") flag.BoolVar(&globalOpts.debug, "debug", false, "Print debugging info") flag.Parse() args := flag.Args() if len(args) == 0 { usage(os.Stderr) os.Exit(-1) } parseAndRun(args) }