This commit is contained in:
ppom 2023-03-24 00:27:51 +01:00
parent 9e702c4cdd
commit 94d023e78c
3 changed files with 127 additions and 105 deletions

47
conf.go
View File

@ -2,24 +2,50 @@ package main
import ( import (
// "flag" // "flag"
"fmt"
"log" "log"
"os" "os"
"regexp"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
type Conf struct { type Conf struct {
// Definitions []string Streams map[string]Stream
Streams []struct { }
Cmd string
Filters []struct { type Stream struct {
Cmd []string
Filters map[string]*Filter
}
type Filter struct {
Regex []string Regex []string
compiledRegex []regexp.Regexp
Retry uint Retry uint
RetryPeriod string `yaml:"retry-period"` RetryPeriod string `yaml:"retry-period"`
Actions []struct { Actions map[string]*Action
Cmd string }
type Action struct {
name, filterName, streamName string
Cmd []string
After string `yaml:",omitempty"` After string `yaml:",omitempty"`
} }
func (c *Conf) setup() {
for streamName, stream := range c.Streams {
for filterName, filter := range stream.Filters {
// Compute Regexes
for _, regex := range filter.Regex {
filter.compiledRegex = append(filter.compiledRegex, *regexp.MustCompile(regex))
}
// Give all relevant infos to Actions
for actionName, action := range filter.Actions {
action.name = actionName
action.filterName = filterName
action.streamName = streamName
}
} }
} }
} }
@ -37,13 +63,10 @@ func parseConf(filename string) *Conf {
if err != nil { if err != nil {
log.Fatalln("Failed to parse configuration file:", err) log.Fatalln("Failed to parse configuration file:", err)
} }
log.Println(conf)
yaml, err := yaml.Marshal(conf) conf.setup()
if err != nil { fmt.Printf("conf.Streams[0].Filters[0].Actions: %s\n", conf.Streams["tailDown"].Filters["lookForProuts"].Actions)
log.Fatalln("Failed to rewrite configuration file:", err)
}
log.Println(string(yaml))
return &conf return &conf
} }

108
main.go
View File

@ -4,37 +4,13 @@ import (
"bufio" "bufio"
"log" "log"
"os/exec" "os/exec"
"regexp"
) )
type Action struct { func cmdStdout(commandline []string) chan string {
regex, cmd []string lines := make(chan string)
}
type compiledAction struct { go func() {
regex []regexp.Regexp cmd := exec.Command(commandline[0], commandline[1:]...)
cmd []string
}
type Stream struct {
cmd []string
actions []Action
}
func compileAction(action Action) compiledAction {
var ca compiledAction
ca.cmd = action.cmd
for _, regex := range action.regex {
ca.regex = append(ca.regex, *regexp.MustCompile(regex))
}
return ca
}
// Handle a log command
// Must be started in a goroutine
func streamHandle(stream Stream, execQueue chan []string) {
log.Printf("streamHandle{%v}: start\n", stream.cmd)
cmd := exec.Command(stream.cmd[0], stream.cmd[1:]...)
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
log.Fatal("couldn't open stdout on command:", err) log.Fatal("couldn't open stdout on command:", err)
@ -44,55 +20,63 @@ func streamHandle(stream Stream, execQueue chan []string) {
} }
defer stdout.Close() defer stdout.Close()
compiledActions := make([]compiledAction, 0, len(stream.actions))
for _, action := range stream.actions {
compiledActions = append(compiledActions, compileAction(action))
}
scanner := bufio.NewScanner(stdout) scanner := bufio.NewScanner(stdout)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() lines <- scanner.Text()
for _, action := range compiledActions { }
for _, regex := range action.regex { close(lines)
}()
return lines
}
func (f *Filter) match(line string) bool {
log.Printf("trying to match line {%s}...\n", line)
for _, regex := range f.compiledRegex {
log.Printf("...on %s\n", regex.String())
if match := regex.FindString(line); match != "" { if match := regex.FindString(line); match != "" {
log.Printf("match `%v` in line: `%v`\n", regex.String(), line) log.Printf("match `%v` in line: `%v`\n", regex.String(), line)
execQueue <- action.cmd return true
} }
} }
return false
} }
func (f *Filter) launch(line *string) {
for _, a := range f.Actions {
go a.launch(line)
} }
} }
func execQueue() chan []string { func (a *Action) launch(line *string) {
queue := make(chan []string) log.Printf("INFO %s.%s.%s: line {%s} → run {%s}\n", a.streamName, a.filterName, a.name, *line, a.Cmd)
go func() {
for { cmd := exec.Command(a.Cmd[0], a.Cmd[1:]...)
command := <-queue
cmd := exec.Command(command[0], command[1:]...)
if ret := cmd.Run(); ret != nil { if ret := cmd.Run(); ret != nil {
log.Printf("Error launching `%v`: code %v\n", cmd, ret) log.Printf("ERR %s.%s.%s: line {%s} → run %s, code {%s}\n", a.streamName, a.filterName, a.name, *line, a.Cmd, ret)
}
}
func (s *Stream) handle() {
log.Printf("streamHandle{%v}: start\n", s.Cmd)
lines := cmdStdout(s.Cmd)
for line := range lines {
for _, filter := range s.Filters {
if filter.match(line) {
filter.launch(&line)
}
} }
} }
}()
return queue
} }
func main() { func main() {
conf := parseConf("./reaction.yml") conf := parseConf("./reaction.yml")
conf = conf
// mockstreams := []Stream{Stream{ for _, stream := range conf.Streams {
// []string{"tail", "-f", "/home/ao/DOWN"}, go stream.handle()
// []Action{Action{ }
// []string{"prout.dev"}, // Infinite wait
// []string{"touch", "/home/ao/DAMN"}, <-make(chan bool)
// }},
// }}
// streams := mockstreams
// log.Println(streams)
// queue := execQueue()
// for _, stream := range streams {
// go streamHandle(stream, queue)
// }
// // Infinite wait
// <-make(chan bool)
} }

View File

@ -7,13 +7,28 @@ definitions:
# ip: '(([0-9]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})' # ip: '(([0-9]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})'
streams: streams:
- cmd: journalctl -fu phpfpm-nextcloud.service tailDown:
cmd: [ "tail", "-f", "/home/ao/DOWN" ]
filters: filters:
- regex: lookForProuts:
- '"message":"Login failed: .\+ (Remote IP: <ip>)"' regex:
retry: 3 - prout
retry-period: 1h retry: 1
retry-period: 1s
actions: actions:
- cmd: *iptablesban damn:
- cmd: *iptablesunban cmd: [ "echo", "DAMN" ]
after: 1h sleepdamn:
cmd: [ "echo", "sleepDAMN" ]
after: 2s
# - cmd: journalctl -fu phpfpm-nextcloud.service
# filters:
# - regex:
# - '"message":"Login failed: .\+ (Remote IP: <ip>)"'
# retry: 3
# retry-period: 1h
# actions:
# - cmd: *iptablesban
# - cmd: *iptablesunban
# after: 1h