Skip to content

Commit

Permalink
CWA customer changes #2: Add support to WMI like syntax to regexp pat…
Browse files Browse the repository at this point in the history
…tern for Procstat on Windows
  • Loading branch information
ZhenyuTan-amz committed Mar 24, 2022
1 parent 908f02f commit 30da07b
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 0 deletions.
40 changes: 40 additions & 0 deletions plugins/inputs/procstat/like2regexp/like2regexp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT

package like2regexp

import (
"strings"
)

const special = `\.+*?()|{}^$` // All regexp special chars excetp `[]`

func WMILikeToRegexp(like string) string {
var buf strings.Builder
// Quote special characters
inclass := false
for i := 0; i < len(like); i++ {
c := like[i]

if inclass && c == ']' {
inclass = false
} else if c == '[' {
inclass = true
} else if !inclass {
switch c {
case '_':
c = '.'
case '%':
buf.WriteByte('.')
c = '*'
default:
if strings.IndexByte(special, c) != -1 { // Escape special chars
buf.WriteByte('\\')
}
}
}
buf.WriteByte(c)
}

return `(?i:^` + buf.String() + `$)`
}
76 changes: 76 additions & 0 deletions plugins/inputs/procstat/like2regexp/like2regexp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT

package like2regexp

import (
"regexp"
"testing"
)

var tests = []struct {
input, expected string
matches, excludes []string
}{
{`abc`, `(?i:^abc$)`,
[]string{`abc`, `ABC`, `AbC`},
[]string{`def`, `DEF`},
},
{`a.c`, `(?i:^a\.c$)`,
[]string{`a.c`},
[]string{`abc`, `abbc`, `xcc`, `abb`, `a`, `ac`},
},
{`%watch%`, `(?i:^.*watch.*$)`,
[]string{`amazon-cloudwatch-agent.exe`, `WatchSomething`, `watch`, `amazoncloudwatch`},
[]string{`amazon-ssm-agent.exe`, `wach`, `amazoncloudwatc`},
},
{`[a-z]`, `(?i:^[a-z]$)`,
[]string{`a`, `c`, `x`},
[]string{`1`, `2`, `3`, `ab`},
},
{`[^a-z]`, `(?i:^[^a-z]$)`,
[]string{`1`, `2`},
[]string{`a`, `c`, `x`, `abc`},
},
{`abc^def[^a-z]`, `(?i:^abc\^def[^a-z]$)`,
[]string{`abc^def1`, `abc^def_`},
[]string{`abc^defa`, `abc^defz`, `abcdef`},
},
{`a_c`, `(?i:^a.c$)`,
[]string{`a.c`, `abc`, `acc`},
[]string{`bbc`, `abb`, `ac`},
},
{`%a_c%`, `(?i:^.*a.c.*$)`,
[]string{`aa.cc`, `abc`, `acc`, `xxxabcxxx`},
[]string{`abbc`, `abbb`, `bbac`},
},
{`%[_][_]%`, `(?i:^.*[_][_].*$)`,
[]string{`__`, `a__cc`},
[]string{`_x_`, `acc`},
},
{`he[[]ll]o.exe`, `(?i:^he[[]ll]o\.exe$)`,
[]string{`he[ll]o.exe`},
[]string{`hello.exe`},
},
}

func TestWMILikeToRegexp(t *testing.T) {
for _, test := range tests {
output := WMILikeToRegexp(test.input)
if test.expected != output {
t.Errorf("translated regexp does not match expected result:\n\tExpected: %v\n\tOutput : %v", test.expected, output)
}

re := regexp.MustCompile(output)
for _, m := range test.matches {
if !re.MatchString(m) {
t.Errorf("case '%v': generated regexp %v does not match value '%v'", test.input, output, m)
}
}
for _, m := range test.excludes {
if re.MatchString(m) {
t.Errorf("case '%v': generated regexp %v should not match value '%v'", test.input, output, m)
}
}
}
}
48 changes: 48 additions & 0 deletions plugins/inputs/procstat/native_finder_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,51 @@ func (pg *NativeFinder) Pattern(pattern string) ([]PID, error) {
}
return pids, err
}

var patternCache = make(map[string]*regexp.Regexp)
var pcmut sync.RWMutex

func likeToRegexp(p string) (*regexp.Regexp, error) {
pcmut.RLock()
re, ok := patternCache[p]
pcmut.RUnlock()
if ok {
return re, nil
}

pattern := like2regexp.WMILikeToRegexp(p)
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
pcmut.Lock()
patternCache[p] = re
pcmut.Unlock()
return re, nil
}

//FullPattern matches on the command line when the process was executed
func (pg *NativeFinder) FullPattern(pattern string) ([]PID, error) {
var pids []PID

regxPattern, err := likeToRegexp(pattern)
if err != nil {
return pids, err
}
procs, err := process.Processes()
if err != nil {
return pids, err
}
for _, p := range procs {
cmd, err := p.Cmdline()
if err != nil {
//skip, this can be caused by the pid no longer existing
//or you having no permissions to access it
continue
}
if regxPattern.MatchString(cmd) {
pids = append(pids, PID(p.Pid))
}
}
return pids, err
}

0 comments on commit 30da07b

Please sign in to comment.