package app import ( _ "embed" "flag" "fmt" "os" "regexp" ) func addStringFlag(names []string, defvalue string, f *flag.FlagSet) *string { var value string for _, name := range names { f.StringVar(&value, name, defvalue, "") } return &value } func addBoolFlag(names []string, f *flag.FlagSet) *bool { var value bool for _, name := range names { f.BoolVar(&value, name, false, "") } return &value } var SocketPath *string func addSocketFlag(f *flag.FlagSet) *string { return addStringFlag( []string{"s", "socket"}, "/run/reaction/reaction.sock", f) } func addConfFlag(f *flag.FlagSet) *string { return addStringFlag( []string{"c", "config"}, "", f) } func addFormatFlag(f *flag.FlagSet) *string { return addStringFlag( []string{"f", "format"}, "yaml", f) } func subCommandParse(f *flag.FlagSet, maxRemainingArgs int) { help := addBoolFlag([]string{"h", "help"}, f) f.Parse(os.Args[2:]) if *help { basicUsage() os.Exit(0) } if len(f.Args()) > maxRemainingArgs { fmt.Printf("ERROR unrecognized argument(s): %v\n", f.Args()[maxRemainingArgs:]) basicUsage() os.Exit(1) } } func basicUsage() { const ( bold = "\033[1m" reset = "\033[0m" ) fmt.Print(`usage: ` + bold + `reaction start` + reset + ` # start the daemon # options: -c/--config CONFIG_FILE # configuration file (required) -s/--socket SOCKET # path to the client-daemon communication socket # (default: /run/reaction/reaction.sock) ` + bold + `reaction example-conf` + reset + ` # print a configuration file example ` + bold + `reaction show` + reset + ` [.STREAM[.FILTER]] # show which actions are still to be run # (e.g know what is currenly banned) # optional argument: limit to STREAM and FILTER # options: -f/--format yaml|json # (default: yaml) -s/--socket SOCKET # path to the client-daemon communication socket ` + bold + `reaction flush` + reset + ` TARGET [.STREAM[.FILTER]] # run currently pending actions for the specified TARGET # optional argument: limit to STREAM and FILTER # options: -s/--socket SOCKET # path to the client-daemon communication socket ` + bold + `reaction test-regex` + reset + ` REGEX LINE # test REGEX against LINE cat FILE | ` + bold + `reaction test-regex` + reset + ` REGEX # test REGEX against each line of FILE `) } //go:embed reaction.yml var exampleConf string func Main() { if len(os.Args) <= 1 { fmt.Println("No argument provided") basicUsage() os.Exit(1) } else if os.Args[1] == "-h" || os.Args[1] == "--help" { basicUsage() os.Exit(0) } f := flag.NewFlagSet(os.Args[1], flag.ContinueOnError) switch os.Args[1] { case "help", "-h", "--help": basicUsage() case "example-conf": subCommandParse(f, 0) fmt.Print(exampleConf) case "start": SocketPath = addSocketFlag(f) confFilename := addConfFlag(f) subCommandParse(f, 0) if *confFilename == "" { fmt.Println("no configuration file provided") basicUsage() os.Exit(1) } Daemon(*confFilename) case "show": SocketPath = addSocketFlag(f) queryFormat := addFormatFlag(f) subCommandParse(f, 1) // if *queryFormat != "yaml" && *queryFormat != "json" { // fmt.Println("only `yaml` and `json` formats are supported.") // f.PrintDefaults() // os.Exit(1) // } if *queryFormat != "yaml" { fmt.Println("for now, only `yaml` format is supported.") os.Exit(1) } if f.Arg(0) != "" { fmt.Println("for now, .STREAM.FILTER is not supported") os.Exit(1) } // f.Arg(0) is "" if there is no remaining argument ClientQuery(f.Arg(0)) case "flush": SocketPath = addSocketFlag(f) subCommandParse(f, 2) if f.Arg(0) == "" { fmt.Println("subcommand flush takes at least one TARGET argument") os.Exit(1) } if f.Arg(1) != "" { fmt.Println("for now, the .stream[.filter] argument is not supported") os.Exit(1) } ClientFlush(f.Arg(0), f.Arg(1)) case "test-regex": // socket not needed, no interaction with the daemon subCommandParse(f, 2) if f.Arg(0) == "" { fmt.Println("subcommand test-regex takes at least one REGEX argument") basicUsage() os.Exit(1) } regex, err := regexp.Compile(f.Arg(0)) if err != nil { fmt.Printf("ERROR the specified regex is invalid: %v", err) os.Exit(1) } if f.Arg(1) == "" { fmt.Println("INFO no second argument. reading from stdin.") MatchStdin(regex) } else { Match(regex, f.Arg(1)) } default: fmt.Println("subcommand not recognized") basicUsage() os.Exit(1) } }