diff --git a/internal/pspy/pspy.go b/internal/pspy/pspy.go index cd854d9..bdf37ad 100644 --- a/internal/pspy/pspy.go +++ b/internal/pspy/pspy.go @@ -6,6 +6,7 @@ import ( "github.com/dominicbreuker/pspy/internal/config" "github.com/dominicbreuker/pspy/internal/logging" + "github.com/dominicbreuker/pspy/internal/psscanner" ) type Bindings struct { @@ -26,13 +27,13 @@ type FSWatcher interface { } type PSScanner interface { - Run(triggerCh chan struct{}) (chan string, chan error) + Run(triggerCh chan struct{}) (chan psscanner.PSEvent, chan error) } type chans struct { sigCh chan os.Signal fsEventCh chan string - psEventCh chan string + psEventCh chan psscanner.PSEvent } 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 } -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) go logErrors(errCh, logger) return psEventCh diff --git a/internal/pspy/pspy_test.go b/internal/pspy/pspy_test.go index 5d3c9a1..c5cd88d 100644 --- a/internal/pspy/pspy_test.go +++ b/internal/pspy/pspy_test.go @@ -9,6 +9,7 @@ import ( "github.com/dominicbreuker/pspy/internal/config" "github.com/dominicbreuker/pspy/internal/logging" + "github.com/dominicbreuker/pspy/internal/psscanner" ) func TestInitFSW(t *testing.T) { @@ -112,7 +113,7 @@ func TestStart(t *testing.T) { close(fsw.initDoneCh) <-time.After(2 * drainFor) fsw.runTriggerCh <- struct{}{} - pss.runEventCh <- "pss event" + pss.runEventCh <- psscanner.PSEvent{UID: 1000, PID: 12345, CMD: "pss event"} pss.runErrCh <- errors.New("pss error") fsw.runEventCh <- "fsw event" fsw.runErrCh <- errors.New("fsw error") @@ -125,7 +126,7 @@ func TestStart(t *testing.T) { <-time.After(2 * drainFor) expectMessage(t, l.Info, "done") 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.Event, fmt.Sprintf("%d FS: fsw event", logging.ColorGreen)) expectMessage(t, l.Error, "ERROR: fsw error") @@ -255,7 +256,7 @@ func (fsw *mockFSWatcher) Run() (chan struct{}, chan string, chan error) { type mockPSScanner struct { runTriggerCh chan struct{} - runEventCh chan string + runEventCh chan psscanner.PSEvent runErrCh chan error numRefreshes int } @@ -264,9 +265,9 @@ func newMockPSScanner() *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.runEventCh = make(chan string) + pss.runEventCh = make(chan psscanner.PSEvent) pss.runErrCh = make(chan error) go func() { diff --git a/internal/psscanner/proclist.go b/internal/psscanner/proclist.go index 7c8fe31..0de01fa 100644 --- a/internal/psscanner/proclist.go +++ b/internal/psscanner/proclist.go @@ -25,7 +25,7 @@ var cmdLineReader = func(pid int) ([]byte, error) { type procList map[int]string -func (pl procList) refresh(eventCh chan string) error { +func (pl procList) refresh(eventCh chan PSEvent) error { pids, err := getPIDs() if err != nil { return err @@ -74,16 +74,16 @@ func file2Pid(f os.FileInfo) (int, error) { 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) if err != nil { cmd = "???" // process probably terminated } uid, err := getUID(pid) 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 } @@ -100,18 +100,26 @@ func getCmd(pid int) (string, error) { return string(cmd), nil } -func getUID(pid int) (string, error) { +func getUID(pid int) (int, error) { stat, err := procStatusReader(pid) if err != nil { - return "", err + return -1, err } + lines := strings.Split(string(stat), "\n") if len(lines) < 9 { - return "", fmt.Errorf("no uid information") + return -1, fmt.Errorf("no uid information") } + uidL := strings.Split(lines[8], "\t") 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 } diff --git a/internal/psscanner/proclist_test.go b/internal/psscanner/proclist_test.go index 4628268..6761244 100644 --- a/internal/psscanner/proclist_test.go +++ b/internal/psscanner/proclist_test.go @@ -136,14 +136,14 @@ func TestGetUID(t *testing.T) { pid int stat []byte statErr error - uid string + uid int err string }{ - {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: notEnoughLines, statErr: nil, uid: "", 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: errors.New("file-system-error"), uid: "", err: "file-system-error"}, // returns file system errors + {pid: 7, stat: completeStatus, statErr: nil, uid: 0, err: ""}, // can read normal stat + {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: -1, 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: -1, err: "file-system-error"}, // returns file system errors } for _, tt := range tests { @@ -151,7 +151,7 @@ func TestGetUID(t *testing.T) { uid, err := getUID(tt.pid) if uid != tt.uid { 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 { 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) { tests := []struct { - eventCh chan string + eventCh chan PSEvent pl procList newPids []int pidsAfter []int 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=2 | 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=2 | the-command", }}, // no events emitted for PIDs already known @@ -200,7 +200,7 @@ func TestRefresh(t *testing.T) { done := make(chan struct{}) go func() { for e := range tt.eventCh { - events = append(events, e) + events = append(events, e.String()) } done <- struct{}{} }() diff --git a/internal/psscanner/psscanner.go b/internal/psscanner/psscanner.go index 21ad0ed..4fb9dda 100644 --- a/internal/psscanner/psscanner.go +++ b/internal/psscanner/psscanner.go @@ -1,13 +1,33 @@ package psscanner +import ( + "fmt" + "strconv" +) + 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 { return &PSScanner{} } -func (p *PSScanner) Run(triggerCh chan struct{}) (chan string, chan error) { - eventCh := make(chan string, 100) +func (p *PSScanner) Run(triggerCh chan struct{}) (chan PSEvent, chan error) { + eventCh := make(chan PSEvent, 100) errCh := make(chan error) pl := make(procList) diff --git a/internal/psscanner/psscanner_test.go b/internal/psscanner/psscanner_test.go index 7f2c44e..773cff8 100644 --- a/internal/psscanner/psscanner_test.go +++ b/internal/psscanner/psscanner_test.go @@ -48,7 +48,7 @@ func TestRun(t *testing.T) { case <-time.After(timeout): t.Errorf("did not receive event in time") 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]) } case err := <-errCh: