turn process monitoring events into structured objects

This commit is contained in:
Dominic Breuker
2019-04-24 22:08:59 +02:00
parent 8a1838faee
commit d1b6518db5
6 changed files with 61 additions and 31 deletions

View File

@@ -6,6 +6,7 @@ import (
"github.com/dominicbreuker/pspy/internal/config" "github.com/dominicbreuker/pspy/internal/config"
"github.com/dominicbreuker/pspy/internal/logging" "github.com/dominicbreuker/pspy/internal/logging"
"github.com/dominicbreuker/pspy/internal/psscanner"
) )
type Bindings struct { type Bindings struct {
@@ -26,13 +27,13 @@ type FSWatcher interface {
} }
type PSScanner interface { type PSScanner interface {
Run(triggerCh chan struct{}) (chan string, chan error) Run(triggerCh chan struct{}) (chan psscanner.PSEvent, chan error)
} }
type chans struct { type chans struct {
sigCh chan os.Signal sigCh chan os.Signal
fsEventCh chan string fsEventCh chan string
psEventCh chan string psEventCh chan psscanner.PSEvent
} }
func Start(cfg *config.Config, b *Bindings, sigCh chan os.Signal) chan struct{} { func Start(cfg *config.Config, b *Bindings, sigCh chan os.Signal) chan struct{} {
@@ -112,7 +113,7 @@ func startFSW(fsw FSWatcher, logger Logger, drainFor time.Duration) (triggerCh c
return triggerCh, fsEventCh return triggerCh, fsEventCh
} }
func startPSS(pss PSScanner, logger Logger, triggerCh chan struct{}) (psEventCh chan string) { func startPSS(pss PSScanner, logger Logger, triggerCh chan struct{}) (psEventCh chan psscanner.PSEvent) {
psEventCh, errCh := pss.Run(triggerCh) psEventCh, errCh := pss.Run(triggerCh)
go logErrors(errCh, logger) go logErrors(errCh, logger)
return psEventCh return psEventCh

View File

@@ -9,6 +9,7 @@ import (
"github.com/dominicbreuker/pspy/internal/config" "github.com/dominicbreuker/pspy/internal/config"
"github.com/dominicbreuker/pspy/internal/logging" "github.com/dominicbreuker/pspy/internal/logging"
"github.com/dominicbreuker/pspy/internal/psscanner"
) )
func TestInitFSW(t *testing.T) { func TestInitFSW(t *testing.T) {
@@ -112,7 +113,7 @@ func TestStart(t *testing.T) {
close(fsw.initDoneCh) close(fsw.initDoneCh)
<-time.After(2 * drainFor) <-time.After(2 * drainFor)
fsw.runTriggerCh <- struct{}{} fsw.runTriggerCh <- struct{}{}
pss.runEventCh <- "pss event" pss.runEventCh <- psscanner.PSEvent{UID: 1000, PID: 12345, CMD: "pss event"}
pss.runErrCh <- errors.New("pss error") pss.runErrCh <- errors.New("pss error")
fsw.runEventCh <- "fsw event" fsw.runEventCh <- "fsw event"
fsw.runErrCh <- errors.New("fsw error") fsw.runErrCh <- errors.New("fsw error")
@@ -125,7 +126,7 @@ func TestStart(t *testing.T) {
<-time.After(2 * drainFor) <-time.After(2 * drainFor)
expectMessage(t, l.Info, "done") expectMessage(t, l.Info, "done")
expectTrigger(t, pss.runTriggerCh) // pss receives triggers from fsw expectTrigger(t, pss.runTriggerCh) // pss receives triggers from fsw
expectMessage(t, l.Event, fmt.Sprintf("%d CMD: pss event", logging.ColorRed)) expectMessage(t, l.Event, fmt.Sprintf("%d CMD: UID=1000 PID=12345 | pss event", logging.ColorRed))
expectMessage(t, l.Error, "ERROR: pss error") expectMessage(t, l.Error, "ERROR: pss error")
expectMessage(t, l.Event, fmt.Sprintf("%d FS: fsw event", logging.ColorGreen)) expectMessage(t, l.Event, fmt.Sprintf("%d FS: fsw event", logging.ColorGreen))
expectMessage(t, l.Error, "ERROR: fsw error") expectMessage(t, l.Error, "ERROR: fsw error")
@@ -255,7 +256,7 @@ func (fsw *mockFSWatcher) Run() (chan struct{}, chan string, chan error) {
type mockPSScanner struct { type mockPSScanner struct {
runTriggerCh chan struct{} runTriggerCh chan struct{}
runEventCh chan string runEventCh chan psscanner.PSEvent
runErrCh chan error runErrCh chan error
numRefreshes int numRefreshes int
} }
@@ -264,9 +265,9 @@ func newMockPSScanner() *mockPSScanner {
return &mockPSScanner{} return &mockPSScanner{}
} }
func (pss *mockPSScanner) Run(triggerCh chan struct{}) (chan string, chan error) { func (pss *mockPSScanner) Run(triggerCh chan struct{}) (chan psscanner.PSEvent, chan error) {
pss.runTriggerCh = triggerCh pss.runTriggerCh = triggerCh
pss.runEventCh = make(chan string) pss.runEventCh = make(chan psscanner.PSEvent)
pss.runErrCh = make(chan error) pss.runErrCh = make(chan error)
go func() { go func() {

View File

@@ -25,7 +25,7 @@ var cmdLineReader = func(pid int) ([]byte, error) {
type procList map[int]string type procList map[int]string
func (pl procList) refresh(eventCh chan string) error { func (pl procList) refresh(eventCh chan PSEvent) error {
pids, err := getPIDs() pids, err := getPIDs()
if err != nil { if err != nil {
return err return err
@@ -74,16 +74,16 @@ func file2Pid(f os.FileInfo) (int, error) {
return pid, nil return pid, nil
} }
func (pl procList) addPid(pid int, eventCh chan string) { func (pl procList) addPid(pid int, eventCh chan PSEvent) {
cmd, err := getCmd(pid) cmd, err := getCmd(pid)
if err != nil { if err != nil {
cmd = "???" // process probably terminated cmd = "???" // process probably terminated
} }
uid, err := getUID(pid) uid, err := getUID(pid)
if err != nil { if err != nil {
uid = "???" uid = -1
} }
eventCh <- fmt.Sprintf("UID=%-4s PID=%-6d | %s", uid, pid, cmd) eventCh <- PSEvent{UID: uid, PID: pid, CMD: cmd}
pl[pid] = cmd pl[pid] = cmd
} }
@@ -100,18 +100,26 @@ func getCmd(pid int) (string, error) {
return string(cmd), nil return string(cmd), nil
} }
func getUID(pid int) (string, error) { func getUID(pid int) (int, error) {
stat, err := procStatusReader(pid) stat, err := procStatusReader(pid)
if err != nil { if err != nil {
return "", err return -1, err
} }
lines := strings.Split(string(stat), "\n") lines := strings.Split(string(stat), "\n")
if len(lines) < 9 { if len(lines) < 9 {
return "", fmt.Errorf("no uid information") return -1, fmt.Errorf("no uid information")
} }
uidL := strings.Split(lines[8], "\t") uidL := strings.Split(lines[8], "\t")
if len(uidL) < 2 { if len(uidL) < 2 {
return "", fmt.Errorf("uid line read incomplete") return -1, fmt.Errorf("uid line read incomplete")
} }
return uidL[1], nil
uid, err := strconv.Atoi(uidL[1])
if err != nil {
return -1, fmt.Errorf("converting %s to int: %v", uidL[1], err)
}
return uid, nil
} }

View File

@@ -136,14 +136,14 @@ func TestGetUID(t *testing.T) {
pid int pid int
stat []byte stat []byte
statErr error statErr error
uid string uid int
err string err string
}{ }{
{pid: 7, stat: completeStatus, statErr: nil, uid: "0", err: ""}, // can read normal stat {pid: 7, stat: completeStatus, statErr: nil, uid: 0, err: ""}, // can read normal stat
{pid: 7, stat: uidLineBroken, statErr: nil, uid: "", err: "uid line read incomplete"}, // errors on incomplete Uid line {pid: 7, stat: uidLineBroken, statErr: nil, uid: -1, err: "uid line read incomplete"}, // errors on incomplete Uid line
{pid: 7, stat: notEnoughLines, statErr: nil, uid: "", err: "no uid information"}, // errors for insufficient lines {pid: 7, stat: notEnoughLines, statErr: nil, uid: -1, err: "no uid information"}, // errors for insufficient lines
{pid: 7, stat: []byte(""), statErr: nil, uid: "", err: "no uid information"}, // errors for insufficient lines {pid: 7, stat: []byte(""), statErr: nil, uid: -1, err: "no uid information"}, // errors for insufficient lines
{pid: 7, stat: []byte(""), statErr: errors.New("file-system-error"), uid: "", err: "file-system-error"}, // returns file system errors {pid: 7, stat: []byte(""), statErr: errors.New("file-system-error"), uid: -1, err: "file-system-error"}, // returns file system errors
} }
for _, tt := range tests { for _, tt := range tests {
@@ -151,7 +151,7 @@ func TestGetUID(t *testing.T) {
uid, err := getUID(tt.pid) uid, err := getUID(tt.pid)
if uid != tt.uid { if uid != tt.uid {
fmt.Printf("STAT: %s", tt.stat) fmt.Printf("STAT: %s", tt.stat)
t.Errorf("Wrong uid returned: got %s but want %s", uid, tt.uid) t.Errorf("Wrong uid returned: got %d but want %d", uid, tt.uid)
} }
if (err != nil || tt.err != "") && fmt.Sprintf("%v", err) != tt.err { if (err != nil || tt.err != "") && fmt.Sprintf("%v", err) != tt.err {
t.Errorf("Wrong error returned: got %v but want %s", err, tt.err) t.Errorf("Wrong error returned: got %v but want %s", err, tt.err)
@@ -174,18 +174,18 @@ func mockProcStatusReader(stat []byte, err error) (restore func()) {
func TestRefresh(t *testing.T) { func TestRefresh(t *testing.T) {
tests := []struct { tests := []struct {
eventCh chan string eventCh chan PSEvent
pl procList pl procList
newPids []int newPids []int
pidsAfter []int pidsAfter []int
events []string events []string
}{ }{
{eventCh: make(chan string), pl: procList{}, newPids: []int{1, 2, 3}, pidsAfter: []int{3, 2, 1}, events: []string{ {eventCh: make(chan PSEvent), pl: procList{}, newPids: []int{1, 2, 3}, pidsAfter: []int{3, 2, 1}, events: []string{
"UID=??? PID=3 | the-command", "UID=??? PID=3 | the-command",
"UID=??? PID=2 | the-command", "UID=??? PID=2 | the-command",
"UID=??? PID=1 | the-command", "UID=??? PID=1 | the-command",
}}, }},
{eventCh: make(chan string), pl: procList{1: "pid-found-before"}, newPids: []int{1, 2, 3}, pidsAfter: []int{1, 3, 2}, events: []string{ {eventCh: make(chan PSEvent), pl: procList{1: "pid-found-before"}, newPids: []int{1, 2, 3}, pidsAfter: []int{1, 3, 2}, events: []string{
"UID=??? PID=3 | the-command", "UID=??? PID=3 | the-command",
"UID=??? PID=2 | the-command", "UID=??? PID=2 | the-command",
}}, // no events emitted for PIDs already known }}, // no events emitted for PIDs already known
@@ -200,7 +200,7 @@ func TestRefresh(t *testing.T) {
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
for e := range tt.eventCh { for e := range tt.eventCh {
events = append(events, e) events = append(events, e.String())
} }
done <- struct{}{} done <- struct{}{}
}() }()

View File

@@ -1,13 +1,33 @@
package psscanner package psscanner
import (
"fmt"
"strconv"
)
type PSScanner struct{} type PSScanner struct{}
type PSEvent struct {
UID int
PID int
CMD string
}
func (evt PSEvent) String() string {
uid := strconv.Itoa(evt.UID)
if evt.UID == -1 {
uid = "???"
}
return fmt.Sprintf("UID=%-4s PID=%-6d | %s", uid, evt.PID, evt.CMD)
}
func NewPSScanner() *PSScanner { func NewPSScanner() *PSScanner {
return &PSScanner{} return &PSScanner{}
} }
func (p *PSScanner) Run(triggerCh chan struct{}) (chan string, chan error) { func (p *PSScanner) Run(triggerCh chan struct{}) (chan PSEvent, chan error) {
eventCh := make(chan string, 100) eventCh := make(chan PSEvent, 100)
errCh := make(chan error) errCh := make(chan error)
pl := make(procList) pl := make(procList)

View File

@@ -48,7 +48,7 @@ func TestRun(t *testing.T) {
case <-time.After(timeout): case <-time.After(timeout):
t.Errorf("did not receive event in time") t.Errorf("did not receive event in time")
case e := <-eventCh: case e := <-eventCh:
if e != tt.events[i] { if e.String() != tt.events[i] {
t.Errorf("Wrong event received: got '%s' but wanted '%s'", e, tt.events[i]) t.Errorf("Wrong event received: got '%s' but wanted '%s'", e, tt.events[i])
} }
case err := <-errCh: case err := <-errCh: