mirror of
https://github.com/DominicBreuker/pspy.git
synced 2025-12-21 19:54:53 +00:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c0edc540c | ||
|
|
0e4d11951b | ||
|
|
8264b725de | ||
|
|
0c973089dd | ||
|
|
d2b84b9415 | ||
|
|
af4e8ff857 | ||
|
|
21a0666ff1 | ||
|
|
d1b6518db5 | ||
|
|
8a1838faee | ||
|
|
7d9d32412b | ||
|
|
59960f9f37 | ||
|
|
f8b2492730 | ||
|
|
d2c8362729 | ||
|
|
aba533f86e | ||
|
|
986cafff6f | ||
|
|
ab8dfc2252 | ||
|
|
d4e20c3629 | ||
|
|
84db6dd806 | ||
|
|
093b9ec69c | ||
|
|
8e61b2bd9d | ||
|
|
056c91801d | ||
|
|
1e67dc332b | ||
|
|
72bbfac3e0 | ||
|
|
3eed2c29aa | ||
|
|
fe0300a67c | ||
|
|
f357059f36 | ||
|
|
1f52ae340c | ||
|
|
fb7dff2d13 | ||
|
|
8440e46afc | ||
|
|
723fb79e06 | ||
|
|
53bb0c2566 | ||
|
|
b7c5e0b984 | ||
|
|
d56eca305e | ||
|
|
8fea22e762 | ||
|
|
5a2d99fafb | ||
|
|
96fad366d0 | ||
|
|
87639a4cd2 | ||
|
|
5ac0573e41 | ||
|
|
00e2783e2c | ||
|
|
42c4561c22 | ||
|
|
67590d8216 | ||
|
|
0e5d2ed39e | ||
|
|
f4b2fdf6b6 | ||
|
|
8b7ed9c586 | ||
|
|
56cfc0555b | ||
|
|
2d2b523bb6 | ||
|
|
5cdc39457a | ||
|
|
3c755caea7 | ||
|
|
d2996b5229 | ||
|
|
ad8d51f4db | ||
|
|
eb2fe0c668 | ||
|
|
a6d948819f | ||
|
|
65ec5b1202 | ||
|
|
d3c7681096 | ||
|
|
572ce2ef3e | ||
|
|
644d65be7b | ||
|
|
9670b85f43 | ||
|
|
1deb4838a5 | ||
|
|
94a12cf031 | ||
|
|
2750defb63 | ||
|
|
d1c18d901a | ||
|
|
f5ca2dad75 | ||
|
|
cb48cc1b37 | ||
|
|
95150e5e12 | ||
|
|
628e20cd56 | ||
|
|
01521a19cc | ||
|
|
693bc1ef17 | ||
|
|
773f370c35 | ||
|
|
6b1f75f4d8 | ||
|
|
c469335009 | ||
|
|
c191b4b7f4 | ||
|
|
167782b25f | ||
|
|
f7d0089b1e | ||
|
|
4495ae7c96 | ||
|
|
c92dd9992f | ||
|
|
56f706b7e2 | ||
|
|
9bc66835a6 | ||
|
|
38c5d42bb4 |
11
Makefile
11
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)' | cut -d ':' -f1; 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)' | cut -d ':' -f1; then echo 'building build image'; docker build -f $(BUILD_DOCKERFILE) -t $(BUILD_IMAGE) .; fi"
|
||||
|
||||
mkdir -p $(PROJECT_DIR)/bin
|
||||
docker run -it \
|
||||
--rm \
|
||||
|
||||
20
README.md
20
README.md
@@ -17,17 +17,23 @@ Inotify watchers placed on selected parts of the file system trigger these scans
|
||||
|
||||
## Getting started
|
||||
|
||||
Get the tool onto the Linux machine you want to inspect.
|
||||
First get the binaries.
|
||||
### 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)
|
||||
- 64 bit big, static version: `pspy64` [download](https://github.com/DominicBreuker/pspy/releases/download/v1.0.0/pspy64)
|
||||
- 32 bit small version: `pspy32s` [download](https://github.com/DominicBreuker/pspy/releases/download/v1.0.0/pspy32s)
|
||||
- 64 bit small version: `pspy64s` [download](https://github.com/DominicBreuker/pspy/releases/download/v1.0.0/pspy64s)
|
||||
|
||||
You can build them yourself by running `make build-build-image` to build a docker image used in `make build` to build four binaries:
|
||||
- 32 bit big, static version: `pspy32`
|
||||
- 64 bit big, static version: `pspy64`
|
||||
- 32 bit small version: `pspy32s`
|
||||
- 64 bit small version: `pspy64s`
|
||||
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).
|
||||
|
||||
### 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.
|
||||
|
||||
You can run `pspy --help` to learn about the flags and their meaning.
|
||||
The summary is as follows:
|
||||
- -p: enables printing commands to stdout (enabled by default)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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/*
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ const maximumWatchersFile = "/proc/sys/fs/inotify/max_user_watches"
|
||||
// set to -1 if the number cannot be determined
|
||||
var MaxWatchers int = -1
|
||||
|
||||
const EventSize int = unix.SizeofInotifyEvent
|
||||
// sizeof(struct inotify_event) + NAME_MAX + 1
|
||||
const EventSize int = unix.SizeofInotifyEvent + 255 + 1
|
||||
|
||||
func init() {
|
||||
mw, err := getMaxWatchers()
|
||||
@@ -71,7 +72,7 @@ func (i *Inotify) Watch(dir string) error {
|
||||
|
||||
func (i *Inotify) Read(buf []byte) (int, error) {
|
||||
n, errno := unix.Read(i.FD, buf)
|
||||
if n < 0 {
|
||||
if n < 1 {
|
||||
return n, fmt.Errorf("reading from inotify fd %d: errno: %d", i.FD, errno)
|
||||
}
|
||||
return n, nil
|
||||
|
||||
@@ -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{}) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,9 +189,11 @@ func (l *mockLogger) Infof(format string, v ...interface{}) {
|
||||
l.Info <- fmt.Sprintf(format, v...)
|
||||
}
|
||||
|
||||
func (l *mockLogger) Errorf(format string, v ...interface{}) {
|
||||
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{}) {
|
||||
m := fmt.Sprintf(format, v...)
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{}{}
|
||||
}()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user