server-client "show" reimplemented
Bug → expired matches are still present
This commit is contained in:
parent
a30a6644dc
commit
b6d7e5a946
@ -8,10 +8,12 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Query = 0
|
Show = 0
|
||||||
Flush = 1
|
Flush = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,7 +24,7 @@ type Request struct {
|
|||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Err error
|
Err error
|
||||||
Actions ReadableMap
|
ClientStatus ClientStatus
|
||||||
Number int
|
Number int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +33,7 @@ func SendAndRetrieve(data Request) Response {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Error opening connection top daemon:", err)
|
log.Fatalln("Error opening connection top daemon:", err)
|
||||||
}
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
err = gob.NewEncoder(conn).Encode(data)
|
err = gob.NewEncoder(conn).Encode(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -45,19 +48,60 @@ func SendAndRetrieve(data Request) Response {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PatternStatus struct {
|
||||||
|
Matches int `yaml:"matches_since_last_trigger"`
|
||||||
|
Actions map[string][]string `yaml:"pending_actions"`
|
||||||
|
}
|
||||||
|
type MapPatternStatus map[string]*PatternStatus
|
||||||
|
type ClientStatus map[string]map[string]MapPatternStatus
|
||||||
|
|
||||||
|
// This block is made to hide pending_actions when empty
|
||||||
|
// and matches_since_last_trigger when zero
|
||||||
|
type FullPatternStatus PatternStatus
|
||||||
|
type MatchesStatus struct {
|
||||||
|
Matches int `yaml:"matches_since_last_trigger"`
|
||||||
|
}
|
||||||
|
type ActionsStatus struct {
|
||||||
|
Actions map[string][]string `yaml:"pending_actions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mps MapPatternStatus) MarshalYAML() (interface{}, error) {
|
||||||
|
ret := make(map[string]interface{})
|
||||||
|
for k, v := range mps {
|
||||||
|
if v.Matches == 0 {
|
||||||
|
if len(v.Actions) != 0 {
|
||||||
|
ret[k] = ActionsStatus{v.Actions}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(v.Actions) != 0 {
|
||||||
|
ret[k] = v
|
||||||
|
} else {
|
||||||
|
ret[k] = MatchesStatus{v.Matches}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// end block
|
||||||
|
|
||||||
func usage(err string) {
|
func usage(err string) {
|
||||||
fmt.Println("Usage: reactionc")
|
fmt.Println("Usage: reactionc")
|
||||||
fmt.Println("Usage: reactionc flush <PATTERN>")
|
fmt.Println("Usage: reactionc flush <PATTERN>")
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClientQuery(streamfilter string) {
|
func ClientShow(streamfilter string) {
|
||||||
response := SendAndRetrieve(Request{Query, streamfilter})
|
response := SendAndRetrieve(Request{Show, streamfilter})
|
||||||
if response.Err != nil {
|
if response.Err != nil {
|
||||||
log.Fatalln("Received error from daemon:", response.Err)
|
log.Fatalln("Received error from daemon:", response.Err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Println(response.Actions.ToString())
|
text, err := yaml.Marshal(response.ClientStatus)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Failed to convert daemon binary response to text format:", err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(text))
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +90,6 @@ func (a *Action) exec(match string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ActionsManager() {
|
func ActionsManager() {
|
||||||
actions := make(ActionsMap)
|
|
||||||
pendingActionsC := make(chan PAT)
|
pendingActionsC := make(chan PAT)
|
||||||
var (
|
var (
|
||||||
pat PAT
|
pat PAT
|
||||||
@ -111,12 +110,14 @@ func ActionsManager() {
|
|||||||
wgActions.Add(1)
|
wgActions.Add(1)
|
||||||
go action.exec(match)
|
go action.exec(match)
|
||||||
} else {
|
} else {
|
||||||
|
actionsLock.Lock()
|
||||||
// make sure map exists
|
// make sure map exists
|
||||||
if actions[action] == nil {
|
if actions[action] == nil {
|
||||||
actions[action] = make(PatternTimes)
|
actions[action] = make(PatternTimes)
|
||||||
}
|
}
|
||||||
// append() to nil is valid go
|
// append() to nil is valid go
|
||||||
actions[action][match] = append(actions[action][match], then)
|
actions[action][match] = append(actions[action][match], then)
|
||||||
|
actionsLock.Unlock()
|
||||||
go func(pat PAT, now time.Time) {
|
go func(pat PAT, now time.Time) {
|
||||||
time.Sleep(pat.t.Sub(now))
|
time.Sleep(pat.t.Sub(now))
|
||||||
pendingActionsC <- pat
|
pendingActionsC <- pat
|
||||||
@ -125,10 +126,13 @@ func ActionsManager() {
|
|||||||
case pat = <-pendingActionsC:
|
case pat = <-pendingActionsC:
|
||||||
match = pat.p
|
match = pat.p
|
||||||
action = pat.a
|
action = pat.a
|
||||||
|
actionsLock.Lock()
|
||||||
actions[action][match] = actions[action][match][1:]
|
actions[action][match] = actions[action][match][1:]
|
||||||
|
actionsLock.Unlock()
|
||||||
wgActions.Add(1)
|
wgActions.Add(1)
|
||||||
go action.exec(match)
|
go action.exec(match)
|
||||||
case _, _ = <-stopActions:
|
case _, _ = <-stopActions:
|
||||||
|
actionsLock.Lock()
|
||||||
for action := range actions {
|
for action := range actions {
|
||||||
if action.OnExit {
|
if action.OnExit {
|
||||||
for match := range actions[action] {
|
for match := range actions[action] {
|
||||||
@ -137,6 +141,7 @@ func ActionsManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
actionsLock.Unlock()
|
||||||
wgActions.Done()
|
wgActions.Done()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -144,7 +149,6 @@ func ActionsManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func MatchesManager() {
|
func MatchesManager() {
|
||||||
matches := make(MatchesMap)
|
|
||||||
var pf PF
|
var pf PF
|
||||||
var pft PFT
|
var pft PFT
|
||||||
end := false
|
end := false
|
||||||
@ -157,7 +161,7 @@ func MatchesManager() {
|
|||||||
if !ok {
|
if !ok {
|
||||||
end = true
|
end = true
|
||||||
} else {
|
} else {
|
||||||
_ = matchesManagerHandleMatch(matches, pft)
|
_ = matchesManagerHandleMatch(pft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,19 +169,23 @@ func MatchesManager() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case pf = <-cleanMatchesC:
|
case pf = <-cleanMatchesC:
|
||||||
|
matchesLock.Lock()
|
||||||
delete(matches[pf.f], pf.p)
|
delete(matches[pf.f], pf.p)
|
||||||
|
matchesLock.Unlock()
|
||||||
case pft = <-matchesC:
|
case pft = <-matchesC:
|
||||||
|
|
||||||
entry := LogEntry{pft.t, pft.p, pft.f.stream.name, pft.f.name, false}
|
entry := LogEntry{pft.t, pft.p, pft.f.stream.name, pft.f.name, false}
|
||||||
|
|
||||||
entry.Exec = matchesManagerHandleMatch(matches, pft)
|
matchesLock.Lock()
|
||||||
|
entry.Exec = matchesManagerHandleMatch(pft)
|
||||||
|
matchesLock.Unlock()
|
||||||
|
|
||||||
logsC <- entry
|
logsC <- entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchesManagerHandleMatch(matches MatchesMap, pft PFT) bool {
|
func matchesManagerHandleMatch(pft PFT) bool {
|
||||||
filter, match, then := pft.f, pft.p, pft.t
|
filter, match, then := pft.f, pft.p, pft.t
|
||||||
|
|
||||||
if filter.Retry > 1 {
|
if filter.Retry > 1 {
|
||||||
@ -229,9 +237,13 @@ func StreamManager(s *Stream, endedSignal chan *Stream) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var actions ActionsMap
|
||||||
|
var matches MatchesMap
|
||||||
|
var actionsLock sync.Mutex
|
||||||
|
var matchesLock sync.Mutex
|
||||||
|
|
||||||
var stopStreams chan bool
|
var stopStreams chan bool
|
||||||
var stopActions chan bool
|
var stopActions chan bool
|
||||||
var actionStore ActionStore
|
|
||||||
var wgActions sync.WaitGroup
|
var wgActions sync.WaitGroup
|
||||||
var wgStreams sync.WaitGroup
|
var wgStreams sync.WaitGroup
|
||||||
|
|
||||||
@ -254,8 +266,6 @@ var cleanMatchesC chan PF
|
|||||||
var actionsC chan PAT
|
var actionsC chan PAT
|
||||||
|
|
||||||
func Daemon(confFilename string) {
|
func Daemon(confFilename string) {
|
||||||
actionStore.store = make(ActionMap)
|
|
||||||
|
|
||||||
conf := parseConf(confFilename)
|
conf := parseConf(confFilename)
|
||||||
|
|
||||||
logsC = make(chan LogEntry)
|
logsC = make(chan LogEntry)
|
||||||
@ -266,6 +276,8 @@ func Daemon(confFilename string) {
|
|||||||
actionsC = make(chan PAT)
|
actionsC = make(chan PAT)
|
||||||
stopActions = make(chan bool)
|
stopActions = make(chan bool)
|
||||||
stopStreams = make(chan bool)
|
stopStreams = make(chan bool)
|
||||||
|
actions = make(ActionsMap)
|
||||||
|
matches = make(MatchesMap)
|
||||||
|
|
||||||
go DatabaseManager(conf)
|
go DatabaseManager(conf)
|
||||||
go MatchesManager()
|
go MatchesManager()
|
||||||
@ -311,7 +323,6 @@ func quit() {
|
|||||||
// send stop to ActionsManager
|
// send stop to ActionsManager
|
||||||
close(stopActions)
|
close(stopActions)
|
||||||
// stop all actions
|
// stop all actions
|
||||||
actionStore.Quit()
|
|
||||||
log.Println("INFO Waiting for Actions to finish...")
|
log.Println("INFO Waiting for Actions to finish...")
|
||||||
wgActions.Wait()
|
wgActions.Wait()
|
||||||
// delete pipe
|
// delete pipe
|
||||||
|
@ -146,7 +146,7 @@ func Main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
// f.Arg(0) is "" if there is no remaining argument
|
// f.Arg(0) is "" if there is no remaining argument
|
||||||
ClientQuery(*limit)
|
ClientShow(*limit)
|
||||||
|
|
||||||
case "flush":
|
case "flush":
|
||||||
SocketPath = addSocketFlag(f)
|
SocketPath = addSocketFlag(f)
|
||||||
|
159
app/pipe.go
159
app/pipe.go
@ -6,115 +6,58 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ActionMap map[string]map[*Action]map[chan bool]bool
|
func genClientStatus() ClientStatus {
|
||||||
type ReadableMap map[string]map[string]map[string]int
|
cs := make(ClientStatus)
|
||||||
|
matchesLock.Lock()
|
||||||
|
|
||||||
type ActionStore struct {
|
// Painful data manipulation
|
||||||
store ActionMap
|
for filter, filterMatches := range matches {
|
||||||
mutex sync.Mutex
|
if cs[filter.stream.name] == nil {
|
||||||
|
cs[filter.stream.name] = make(map[string]MapPatternStatus)
|
||||||
|
}
|
||||||
|
if cs[filter.stream.name][filter.name] == nil {
|
||||||
|
cs[filter.stream.name][filter.name] = make(MapPatternStatus)
|
||||||
|
}
|
||||||
|
for pattern, patternMatches := range filterMatches {
|
||||||
|
var ps PatternStatus
|
||||||
|
cs[filter.stream.name][filter.name][pattern] = &ps
|
||||||
|
|
||||||
|
ps.Matches = len(patternMatches)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matchesLock.Unlock()
|
||||||
|
actionsLock.Lock()
|
||||||
|
|
||||||
|
// Painful data manipulation
|
||||||
|
for action, pendingActions := range actions {
|
||||||
|
if cs[action.filter.stream.name] == nil {
|
||||||
|
cs[action.filter.stream.name] = make(map[string]MapPatternStatus)
|
||||||
|
}
|
||||||
|
if cs[action.filter.stream.name][action.filter.name] == nil {
|
||||||
|
cs[action.filter.stream.name][action.filter.name] = make(MapPatternStatus)
|
||||||
|
}
|
||||||
|
for pattern, patternPendingActions := range pendingActions {
|
||||||
|
if cs[action.filter.stream.name][action.filter.name][pattern] == nil {
|
||||||
|
var ps PatternStatus
|
||||||
|
cs[action.filter.stream.name][action.filter.name][pattern] = &ps
|
||||||
|
}
|
||||||
|
var ps *PatternStatus
|
||||||
|
ps = cs[action.filter.stream.name][action.filter.name][pattern]
|
||||||
|
ps.Actions = make(map[string][]string)
|
||||||
|
|
||||||
|
for _, t := range patternPendingActions {
|
||||||
|
ps.Actions[action.name] = append(ps.Actions[action.name], t.Format(time.DateTime))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actionsLock.Unlock()
|
||||||
|
return cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by an Action before entering sleep
|
|
||||||
func (a *ActionStore) Register(action *Action, pattern string) chan bool {
|
|
||||||
a.mutex.Lock()
|
|
||||||
defer a.mutex.Unlock()
|
|
||||||
if a.store[pattern] == nil {
|
|
||||||
a.store[pattern] = make(map[*Action]map[chan bool]bool)
|
|
||||||
}
|
|
||||||
if a.store[pattern][action] == nil {
|
|
||||||
a.store[pattern][action] = make(map[chan bool]bool)
|
|
||||||
}
|
|
||||||
sig := make(chan bool)
|
|
||||||
a.store[pattern][action][sig] = true
|
|
||||||
return sig
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by an Action after sleep
|
|
||||||
func (a *ActionStore) Unregister(action *Action, pattern string, sig chan bool) {
|
|
||||||
a.mutex.Lock()
|
|
||||||
defer a.mutex.Unlock()
|
|
||||||
if a.store[pattern] == nil || a.store[pattern][action] == nil || len(a.store[pattern][action]) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(sig)
|
|
||||||
delete(a.store[pattern][action], sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by Main
|
|
||||||
func (a *ActionStore) Quit() {
|
|
||||||
a.mutex.Lock()
|
|
||||||
defer a.mutex.Unlock()
|
|
||||||
for _, actions := range a.store {
|
|
||||||
for action, sigs := range actions {
|
|
||||||
for sig := range sigs {
|
|
||||||
sig <- action.OnExit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a.store = make(ActionMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by a CLI
|
|
||||||
func (a *ActionStore) Flush(pattern string) int {
|
|
||||||
var cpt int
|
|
||||||
a.mutex.Lock()
|
|
||||||
defer a.mutex.Unlock()
|
|
||||||
if a.store[pattern] != nil {
|
|
||||||
for _, action := range a.store[pattern] {
|
|
||||||
for sig := range action {
|
|
||||||
sig <- true
|
|
||||||
}
|
|
||||||
cpt++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete(a.store, pattern)
|
|
||||||
flushesC <- LogEntry{time.Now(), pattern, "", "", false}
|
|
||||||
return cpt
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by a CLI
|
|
||||||
func (a *ActionStore) pendingActions() ReadableMap {
|
|
||||||
a.mutex.Lock()
|
|
||||||
defer a.mutex.Unlock()
|
|
||||||
return a.store.ToReadable()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a ActionMap) ToReadable() ReadableMap {
|
|
||||||
res := make(ReadableMap)
|
|
||||||
|
|
||||||
for pattern, actions := range a {
|
|
||||||
for action := range actions {
|
|
||||||
filter := action.filter.name
|
|
||||||
stream := action.filter.stream.name
|
|
||||||
if res[stream] == nil {
|
|
||||||
res[stream] = make(map[string]map[string]int)
|
|
||||||
}
|
|
||||||
if res[stream][filter] == nil {
|
|
||||||
res[stream][filter] = make(map[string]int)
|
|
||||||
}
|
|
||||||
res[stream][filter][pattern] = res[stream][filter][pattern] + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadableMap) ToString() string {
|
|
||||||
text, err := yaml.Marshal(r)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
return string(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Socket-related, server-related functions
|
|
||||||
|
|
||||||
func createOpenSocket() net.Listener {
|
func createOpenSocket() net.Listener {
|
||||||
err := os.MkdirAll(path.Dir(*SocketPath), 0755)
|
err := os.MkdirAll(path.Dir(*SocketPath), 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -146,6 +89,7 @@ func SocketManager() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
go func(conn net.Conn) {
|
go func(conn net.Conn) {
|
||||||
|
defer conn.Close()
|
||||||
var request Request
|
var request Request
|
||||||
var response Response
|
var response Response
|
||||||
|
|
||||||
@ -156,16 +100,17 @@ func SocketManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch request.Request {
|
switch request.Request {
|
||||||
case Query:
|
case Show:
|
||||||
response.Actions = actionStore.store.ToReadable()
|
response.ClientStatus = genClientStatus()
|
||||||
case Flush:
|
case Flush:
|
||||||
response.Number = actionStore.Flush(request.Pattern)
|
// FIXME reimplement flush
|
||||||
|
response.Number = 0
|
||||||
default:
|
default:
|
||||||
log.Println("ERROR Invalid Message from cli: unrecognised Request type")
|
log.Println("ERROR Invalid Message from cli: unrecognised Request type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gob.NewEncoder(conn).Encode(response)
|
err = gob.NewEncoder(conn).Encode(response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("ERROR Can't respond to cli:", err)
|
log.Println("ERROR Can't respond to cli:", err)
|
||||||
return
|
return
|
||||||
|
@ -2,20 +2,24 @@
|
|||||||
patterns:
|
patterns:
|
||||||
num:
|
num:
|
||||||
regex: '[0-9]+'
|
regex: '[0-9]+'
|
||||||
|
ip:
|
||||||
|
regex: '(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})|(?:[0-9a-fA-F:]{2,90})'
|
||||||
|
ignore:
|
||||||
|
- 1.0.0.1
|
||||||
|
|
||||||
streams:
|
streams:
|
||||||
tailDown:
|
tailDown1:
|
||||||
cmd: [ "sh", "-c", "sleep 0.5; echo found 1; sleep 1; echo found 1; sleep 10" ]
|
cmd: [ "sh", "-c", "echo 1 2 3 4 5 1 2 3 4 5 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 | tr ' ' '\n' | while read i; do sleep 2; echo found $(($i % 10)); done" ]
|
||||||
filters:
|
filters:
|
||||||
findIP:
|
findIP:
|
||||||
regex:
|
regex:
|
||||||
- '^found <num>$'
|
- '^found <num>$'
|
||||||
retry: 2
|
retry: 3
|
||||||
retry-period: 1m
|
retry-period: 30s
|
||||||
actions:
|
actions:
|
||||||
damn:
|
damn:
|
||||||
cmd: [ "echo", "<num>" ]
|
cmd: [ "echo", "<num>" ]
|
||||||
undamn:
|
undamn:
|
||||||
cmd: [ "echo", "undamn", "<num>" ]
|
cmd: [ "echo", "undamn", "<num>" ]
|
||||||
after: 5s
|
after: 30s
|
||||||
onexit: true
|
onexit: true
|
||||||
|
Loading…
Reference in New Issue
Block a user