diff --git a/Makefile b/Makefile index 95043b8..cc20e2e 100644 --- a/Makefile +++ b/Makefile @@ -14,14 +14,10 @@ test: docker build -f $(TEST_DOCKERFILE) -t $(TEST_IMAGE) . docker run -it --rm $(TEST_IMAGE) -# Build Docker image for development -# pspy has to run on Linux - use this if you develop on another OS -dev-build: - docker build -f $(DEV_DOCKERFILE) -t $(DEV_IMAGE) . - # Drops you into a shell in the development container and mounts the source code # You can edit to source on your host, then run go commans (e.g., `go test ./...`) inside the container dev: + sh -c "if ! docker image ls | grep '$(DEV_IMAGE)'; then echo 'building dev image'; docker build -f $(DEV_DOCKERFILE) -t $(DEV_IMAGE) .; fi" docker run -it \ --rm \ -v $(PROJECT_DIR):/go/src/github.com/dominicbreuker/pspy \ @@ -37,14 +33,13 @@ example: docker run -it --rm $(EXAMPLE_IMAGE) -build-build-image: - docker build -f $(BUILD_DOCKERFILE) -t $(BUILD_IMAGE) . - # Build different binaries # builds binaries for both 32bit and 64bit systems # builds one set of static binaries that should work on any system without dependencies, but are huge # builds another set of binaries that are as small as possible, but may not work build: + sh -c "if ! docker image ls | grep '$(BUILD_IMAGE)'; then echo 'building build image'; docker build -f $(BUILD_DOCKERFILE) -t $(BUILD_IMAGE) .; fi" + docker run -it \ mkdir -p $(PROJECT_DIR)/bin docker run -it \ --rm \ diff --git a/README.md b/README.md index fcd5dc6..0de33c1 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ Inotify watchers placed on selected parts of the file system trigger these scans ## Getting started +### Download + Get the tool onto the Linux machine you want to inspect. First get the binaries. Download the released binaries here: - 32 bit big, static version: `pspy32` [download](https://github.com/DominicBreuker/pspy/releases/download/v1.0.0/pspy32) @@ -27,7 +29,8 @@ First get the binaries. Download the released binaries here: The statically compiled files should work on any Linux system but are quite huge (~4MB). If size is an issue, try the smaller versions which depend on libc and are compressed with UPX (<1MB). -Alternatively, build the binaries yourself. +### Build + Either use Go installed on your system or run the Docker-based build process which ran to create the release. For the latter, ensure Docker is installed, and then run `make build-build-image` to build a Docker image, followed by `make build` to build the binaries with it. diff --git a/cmd/root.go b/cmd/root.go index a868975..752aba0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -58,6 +58,7 @@ var defaultRDirs = []string{ var defaultDirs = []string{} var triggerInterval int var colored bool +var debug bool func init() { rootCmd.PersistentFlags().BoolVarP(&logPS, "procevents", "p", true, "print new processes to stdout") @@ -66,12 +67,13 @@ func init() { rootCmd.PersistentFlags().StringArrayVarP(&dirs, "dirs", "d", defaultDirs, "watch these dirs") rootCmd.PersistentFlags().IntVarP(&triggerInterval, "interval", "i", 100, "scan every 'interval' milliseconds for new processes") rootCmd.PersistentFlags().BoolVarP(&colored, "color", "c", true, "color the printed events") + rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "", false, "print detailed error messages") log.SetOutput(os.Stdout) } func root(cmd *cobra.Command, args []string) { - logger := logging.NewLogger() + logger := logging.NewLogger(debug) cfg := &config.Config{ RDirs: rDirs, diff --git a/docker/Dockerfile.build b/docker/Dockerfile.build index 9917f9f..9ccf650 100644 --- a/docker/Dockerfile.build +++ b/docker/Dockerfile.build @@ -1,3 +1,3 @@ -FROM golang:1.10-stretch +FROM golang:1.12-stretch RUN apt-get update && apt-get install -y upx && rm -rf /var/lib/apt/lists/* diff --git a/docker/Dockerfile.development b/docker/Dockerfile.development index f39d22a..881cdfd 100644 --- a/docker/Dockerfile.development +++ b/docker/Dockerfile.development @@ -1,4 +1,4 @@ -FROM golang:1.10-stretch +FROM golang:1.12-stretch RUN apt-get update && apt-get -y install cron python3 sudo procps diff --git a/docker/Dockerfile.testing b/docker/Dockerfile.testing index 3f233d5..ab7c729 100644 --- a/docker/Dockerfile.testing +++ b/docker/Dockerfile.testing @@ -1,4 +1,4 @@ -FROM golang:1.10-stretch +FROM golang:1.12-stretch RUN apt-get update && apt-get -y install cron python3 sudo procps diff --git a/internal/logging/logging.go b/internal/logging/logging.go index 23ab6f0..05e9534 100644 --- a/internal/logging/logging.go +++ b/internal/logging/logging.go @@ -2,27 +2,35 @@ package logging import ( "fmt" + "hash/fnv" "log" "os" + "strconv" ) const ( ColorNone = iota ColorRed ColorGreen + ColorYellow + ColorBlue + ColorPurple + ColorTeal ) type Logger struct { infoLogger *log.Logger errorLogger *log.Logger eventLogger *log.Logger + debug bool } -func NewLogger() *Logger { +func NewLogger(debug bool) *Logger { return &Logger{ infoLogger: log.New(os.Stdout, "", 0), errorLogger: log.New(os.Stderr, "", 0), eventLogger: log.New(os.Stdout, "", log.Ldate|log.Ltime), + debug: debug, } } @@ -32,21 +40,24 @@ func (l *Logger) Infof(format string, v ...interface{}) { } // Errorf writes an error message to stderr -func (l *Logger) Errorf(format string, v ...interface{}) { - l.errorLogger.Printf(format, v...) +func (l *Logger) Errorf(debug bool, format string, v ...interface{}) { + if l.debug == debug { + l.errorLogger.Printf(format, v...) + } } // Eventf writes an event with timestamp to stdout func (l *Logger) Eventf(color int, format string, v ...interface{}) { msg := fmt.Sprintf(format, v...) - - switch color { - case ColorRed: - msg = fmt.Sprintf("\x1b[31;1m%s\x1b[0m", msg) - case ColorGreen: - msg = fmt.Sprintf("\x1b[32;1m%s\x1b[0m", msg) - default: + if color != ColorNone { + msg = fmt.Sprintf("\x1b[%d;1m%s\x1b[0m", 30+color, msg) } l.eventLogger.Printf("%s", msg) } + +func GetColorByUID(uid int) int { + h := fnv.New32a() + h.Write([]byte(strconv.Itoa(uid))) + return (int(h.Sum32()) % (ColorTeal)) + 1 +} diff --git a/internal/logging/logging_test.go b/internal/logging/logging_test.go index f70230c..2112302 100644 --- a/internal/logging/logging_test.go +++ b/internal/logging/logging_test.go @@ -13,7 +13,7 @@ const ansiPattern = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\ var ansiMatcher = regexp.MustCompile(ansiPattern) -var l = NewLogger() +var l = NewLogger(true) var logTests = []struct { logger *log.Logger @@ -23,8 +23,8 @@ var logTests = []struct { }{ {l.infoLogger, func() { l.Infof("Info message no. %d", 1) }, "Info message no. 1\n", nil}, {l.infoLogger, func() { l.Infof("Info message no. %d with a string %s\n", 2, "appended to it") }, "Info message no. 2 with a string appended to it\n", nil}, - {l.errorLogger, func() { l.Errorf("Error message") }, "Error message\n", nil}, - {l.errorLogger, func() { l.Errorf("Error message\n") }, "Error message\n", nil}, + {l.errorLogger, func() { l.Errorf(true, "Error message") }, "Error message\n", nil}, + {l.errorLogger, func() { l.Errorf(true, "Error message\n") }, "Error message\n", nil}, {l.eventLogger, func() { l.Eventf(ColorNone, "Event message") }, dateFormatPattern + " Event message\n", nil}, {l.eventLogger, func() { l.Eventf(ColorRed, "Event message") }, dateFormatPattern + " Event message\n", [][]byte{[]byte("\x1b[31;1m"), []byte("\x1b[0m")}}, {l.eventLogger, func() { l.Eventf(ColorGreen, "Event message") }, dateFormatPattern + " Event message\n", [][]byte{[]byte("\x1b[32;1m"), []byte("\x1b[0m")}}, @@ -33,6 +33,7 @@ var logTests = []struct { func TestLogging(t *testing.T) { for i, tt := range logTests { actual := captureOutput(tt.logger, tt.test) + log.Printf("OUT: %s", actual) // check colors and remove afterwards colors := ansiMatcher.FindAll(actual, 2) @@ -55,3 +56,46 @@ func captureOutput(logger *log.Logger, f func()) []byte { f() return buf.Bytes() } + +func TestGetColorByUID(t *testing.T) { + tests := []struct { + uid int + color int + }{ + {uid: 0, color: 4}, + {uid: 1, color: 5}, + {uid: 2, color: 2}, + {uid: 3, color: 3}, + {uid: 99999999999, color: 5}, + } + + for _, tt := range tests { + color := GetColorByUID(tt.uid) + if color != tt.color { + t.Errorf("GetColorByUID(%d)=%d but want %d", tt.uid, color, tt.color) + } + } + + minColor := 9999999 + maxColor := -9999999 + for i := 0; i < 1000; i++ { + color := GetColorByUID(i) + if color < 1 || color > ColorTeal { + t.Fatalf("GetColorByUID(%d)=%d but this is out of range [%d, %d]", i, color, ColorRed, ColorTeal) + } + + if color < minColor { + minColor = color + } + if color > maxColor { + maxColor = color + } + } + + if minColor != 1 { + t.Errorf("GetColorByUID returned minimum color %d, not %d, on 1000 trials, which is extremely unlikely", minColor, 1) + } + if maxColor != ColorTeal { + t.Errorf("GetColorByUID returned maximum color %d, not %d, on 1000 trials, which is extremely unlikely", maxColor, ColorTeal) + } +} diff --git a/internal/pspy/pspy.go b/internal/pspy/pspy.go index e90dd38..70cd7c6 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 { @@ -16,7 +17,7 @@ type Bindings struct { type Logger interface { Infof(format string, v ...interface{}) - Errorf(format string, v ...interface{}) + Errorf(debug bool, format string, v ...interface{}) Eventf(color int, format string, v ...interface{}) } @@ -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{} { @@ -56,7 +57,7 @@ func Start(cfg *config.Config, b *Bindings, sigCh chan os.Signal) chan struct{} func printOutput(cfg *config.Config, b *Bindings, chans *chans) chan struct{} { exit := make(chan struct{}) - fsEventColor, psEventColor := getColors(cfg.Colored) + // fsEventColor, psEventColor := getColors(cfg.Colored) go func() { for { @@ -66,11 +67,15 @@ func printOutput(cfg *config.Config, b *Bindings, chans *chans) chan struct{} { exit <- struct{}{} case fe := <-chans.fsEventCh: if cfg.LogFS { - b.Logger.Eventf(fsEventColor, "FS: %+v", fe) + b.Logger.Eventf(logging.ColorNone, "FS: %+v", fe) } case pe := <-chans.psEventCh: if cfg.LogPS { - b.Logger.Eventf(psEventColor, "CMD: %+v", pe) + color := logging.ColorNone + if cfg.Colored { + color = logging.GetColorByUID(pe.UID) + } + b.Logger.Eventf(color, "CMD: %+v", pe) } } } @@ -78,17 +83,6 @@ func printOutput(cfg *config.Config, b *Bindings, chans *chans) chan struct{} { return exit } -func getColors(colored bool) (fsEventColor int, psEventColor int) { - if colored { - fsEventColor = logging.ColorGreen - psEventColor = logging.ColorRed - } else { - fsEventColor = logging.ColorNone - psEventColor = logging.ColorNone - } - return -} - func initFSW(fsw FSWatcher, rdirs, dirs []string, logger Logger) { errCh, doneCh := fsw.Init(rdirs, dirs) for { @@ -96,7 +90,7 @@ func initFSW(fsw FSWatcher, rdirs, dirs []string, logger Logger) { case <-doneCh: return case err := <-errCh: - logger.Errorf("initializing fs watcher: %v", err) + logger.Errorf(true, "initializing fs watcher: %v", err) } } } @@ -112,7 +106,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 @@ -130,7 +124,7 @@ func triggerEvery(d time.Duration, triggerCh chan struct{}) { func logErrors(errCh chan error, logger Logger) { for { err := <-errCh - logger.Errorf("ERROR: %v", err) + logger.Errorf(true, "ERROR: %v", err) } } diff --git a/internal/pspy/pspy_test.go b/internal/pspy/pspy_test.go index a8a7b89..f66aefe 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) { @@ -67,24 +68,6 @@ func TestStartPSS(t *testing.T) { expectMessage(t, l.Error, "ERROR: error during refresh") } -func TestGetColors(t *testing.T) { - tests := []struct { - colored bool - fsEventColor int - psEventColor int - }{ - {colored: true, fsEventColor: logging.ColorGreen, psEventColor: logging.ColorRed}, - {colored: false, fsEventColor: logging.ColorNone, psEventColor: logging.ColorNone}, - } - - for _, tt := range tests { - c1, c2 := getColors(tt.colored) - if c1 != tt.fsEventColor || c2 != tt.psEventColor { - t.Errorf("Got wrong colors when colored=%t: expected %d, %d but got %d, %d", tt.colored, tt.fsEventColor, tt.psEventColor, c1, c2) - } - } -} - func TestStart(t *testing.T) { drainFor := 10 * time.Millisecond triggerEvery := 999 * time.Second @@ -112,7 +95,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,9 +108,9 @@ 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.ColorPurple)) 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.ColorNone)) expectMessage(t, l.Error, "ERROR: fsw error") expectMessage(t, l.Info, "Exiting program... (interrupt)") @@ -190,6 +173,7 @@ type mockLogger struct { Info chan string Error chan string Event chan string + Debug bool } func newMockLogger() *mockLogger { @@ -197,6 +181,7 @@ func newMockLogger() *mockLogger { Info: make(chan string, 10), Error: make(chan string, 10), Event: make(chan string, 10), + Debug: true, } } @@ -204,8 +189,10 @@ func (l *mockLogger) Infof(format string, v ...interface{}) { l.Info <- fmt.Sprintf(format, v...) } -func (l *mockLogger) Errorf(format string, v ...interface{}) { - l.Error <- fmt.Sprintf(format, v...) +func (l *mockLogger) Errorf(debug bool, format string, v ...interface{}) { + if l.Debug == debug { + l.Error <- fmt.Sprintf(format, v...) + } } func (l *mockLogger) Eventf(color int, format string, v ...interface{}) { @@ -251,7 +238,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 } @@ -260,9 +247,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: