package main import ( "bufio" "flag" "fmt" "os" "regexp" "sort" "strconv" ) var max = flag.Int64("max", 5, "limit output, if negative print all") func main() { flag.Parse() // Failed password for root from 43.229.53.57 port 62954 ssh2 // message repeated 2 times: [ Failed password for root from 43.229.53.57 port 32871 ssh2] p, err := regexp.Compile(`(message repeated (\d+) times: \[)? Failed password for (invalid user )?(.*?) from (.*) port`) if err != nil { fail("failed to compile regex", err) } unames := counter{} ips := counter{} s := bufio.NewScanner(os.Stdin) for s.Scan() { if m := p.FindSubmatch(s.Bytes()); m == nil { continue } else { if len(m) != 6 { fail("failure to capture correct number of tokens", nil) } multstr := string(m[2]) uname, ip := m[4], m[5] multiplier := 1 if multstr != "" { i, err := strconv.Atoi(multstr) if err != nil { fail("problem parsing int", err) } multiplier = i } unames[string(uname)] += multiplier ips[string(ip)] += multiplier } } if err := s.Err(); err != nil { fail("failure reading from stdin", err) } display(unames, ips) } // fail constructs an error message, sends to stderr, and exists 1 func fail(msg string, err error) { fmt.Fprintf(os.Stderr, "%s; %v\n", msg, err) os.Exit(1) } type counter map[string]int // top sorts the map by value and returns max pairs, unless max is negative in // which case it returns all pairs func top(m counter, max int64) pairList { p := make(pairList, len(m)) i := 0 for k, v := range m { p[i] = pair{k, v} i++ } sort.Sort(p) if max < 0 { return p } if int64(len(p)) > max { p = p[:max] } return p } // How I sort a counter by value type pair struct { key string value int } type pairList []pair func (p pairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p pairList) Len() int { return len(p) } // Less sorts in descending order func (p pairList) Less(i, j int) bool { return p[i].value > p[j].value } func display(unames, ips map[string]int) { var title string title = fmt.Sprintf("%-20s %s", "unames", "count") fmt.Printf("%s\n", title) for _ = range title { fmt.Print("-") } fmt.Println() for _, v := range top(unames, *max) { fmt.Printf("%-20s %d\n", v.key, v.value) } title = fmt.Sprintf("%-20s %s", "ips", "count") fmt.Printf("\n%s\n", title) for _ = range title { fmt.Print("-") } fmt.Println() for _, v := range top(ips, *max) { fmt.Printf("%-20s %d\n", v.key, v.value) } }