mirror of
https://github.com/DominicBreuker/pspy.git
synced 2025-12-21 03:34:50 +00:00
Added --ppid command line option that includes the parent process identifier.
Also included small refactoring in PSScanner module to allow for parameter to be passed.
This commit is contained in:
@@ -61,6 +61,7 @@ var defaultDirs = []string{}
|
|||||||
var triggerInterval int
|
var triggerInterval int
|
||||||
var colored bool
|
var colored bool
|
||||||
var debug bool
|
var debug bool
|
||||||
|
var ppid bool
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.PersistentFlags().BoolVarP(&logPS, "procevents", "p", true, "print new processes to stdout")
|
rootCmd.PersistentFlags().BoolVarP(&logPS, "procevents", "p", true, "print new processes to stdout")
|
||||||
@@ -70,6 +71,7 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().IntVarP(&triggerInterval, "interval", "i", 100, "scan every 'interval' milliseconds for new processes")
|
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(&colored, "color", "c", true, "color the printed events")
|
||||||
rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "", false, "print detailed error messages")
|
rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "", false, "print detailed error messages")
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&ppid, "ppid", "", false, "record process ppids")
|
||||||
|
|
||||||
log.SetOutput(os.Stdout)
|
log.SetOutput(os.Stdout)
|
||||||
}
|
}
|
||||||
@@ -91,7 +93,7 @@ func root(cmd *cobra.Command, args []string) {
|
|||||||
fsw := fswatcher.NewFSWatcher()
|
fsw := fswatcher.NewFSWatcher()
|
||||||
defer fsw.Close()
|
defer fsw.Close()
|
||||||
|
|
||||||
pss := psscanner.NewPSScanner()
|
pss := psscanner.NewPSScanner(ppid)
|
||||||
|
|
||||||
sigCh := make(chan os.Signal)
|
sigCh := make(chan os.Signal)
|
||||||
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
||||||
|
|||||||
@@ -95,7 +95,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 <- psscanner.PSEvent{UID: 1000, PID: 12345, CMD: "pss event"}
|
pss.runEventCh <- psscanner.PSEvent{UID: 1000, PID: 12345, PPID: 54321, 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")
|
||||||
@@ -108,7 +108,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: UID=1000 PID=12345 | pss event", logging.ColorPurple))
|
expectMessage(t, l.Event, fmt.Sprintf("%d CMD: UID=1000 PID=12345 PPID=54321 | pss event", logging.ColorPurple))
|
||||||
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.ColorNone))
|
expectMessage(t, l.Event, fmt.Sprintf("%d FS: fsw event", logging.ColorNone))
|
||||||
expectMessage(t, l.Error, "ERROR: fsw error")
|
expectMessage(t, l.Error, "ERROR: fsw error")
|
||||||
|
|||||||
@@ -6,26 +6,19 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var procDirReader = func() ([]os.FileInfo, error) {
|
var procDirReader = func() ([]os.FileInfo, error) {
|
||||||
return ioutil.ReadDir("/proc")
|
return ioutil.ReadDir("/proc")
|
||||||
}
|
}
|
||||||
|
|
||||||
var procStatusReader = func(pid int) ([]byte, error) {
|
type procList map[int]struct{}
|
||||||
statPath := fmt.Sprintf("/proc/%d/status", pid)
|
|
||||||
return ioutil.ReadFile(statPath)
|
type pidProcessor interface {
|
||||||
|
processNewPid(pid int)
|
||||||
}
|
}
|
||||||
|
|
||||||
var cmdLineReader = func(pid int) ([]byte, error) {
|
func (pl procList) refresh(p pidProcessor) error {
|
||||||
cmdPath := fmt.Sprintf("/proc/%d/cmdline", pid)
|
|
||||||
return ioutil.ReadFile(cmdPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
type procList map[int]string
|
|
||||||
|
|
||||||
func (pl procList) refresh(eventCh chan PSEvent) error {
|
|
||||||
pids, err := getPIDs()
|
pids, err := getPIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -35,7 +28,8 @@ func (pl procList) refresh(eventCh chan PSEvent) error {
|
|||||||
pid := pids[i]
|
pid := pids[i]
|
||||||
_, ok := pl[pid]
|
_, ok := pl[pid]
|
||||||
if !ok {
|
if !ok {
|
||||||
pl.addPid(pid, eventCh)
|
p.processNewPid(pid)
|
||||||
|
pl[pid] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,53 +67,3 @@ func file2Pid(f os.FileInfo) (int, error) {
|
|||||||
|
|
||||||
return pid, nil
|
return pid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = -1
|
|
||||||
}
|
|
||||||
eventCh <- PSEvent{UID: uid, PID: pid, CMD: cmd}
|
|
||||||
pl[pid] = cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCmd(pid int) (string, error) {
|
|
||||||
cmd, err := cmdLineReader(pid)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
for i := 0; i < len(cmd); i++ {
|
|
||||||
if cmd[i] == 0 {
|
|
||||||
cmd[i] = 32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(cmd), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUID(pid int) (int, error) {
|
|
||||||
stat, err := procStatusReader(pid)
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := strings.Split(string(stat), "\n")
|
|
||||||
if len(lines) < 9 {
|
|
||||||
return -1, fmt.Errorf("no uid information")
|
|
||||||
}
|
|
||||||
|
|
||||||
uidL := strings.Split(lines[8], "\t")
|
|
||||||
if len(uidL) < 2 {
|
|
||||||
return -1, fmt.Errorf("uid line read incomplete")
|
|
||||||
}
|
|
||||||
|
|
||||||
uid, err := strconv.Atoi(uidL[1])
|
|
||||||
if err != nil {
|
|
||||||
return -1, fmt.Errorf("converting %s to int: %v", uidL[1], err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return uid, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,54 +1,15 @@
|
|||||||
package psscanner
|
package psscanner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetCmd
|
|
||||||
|
|
||||||
func TestGetCmd(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
pid int
|
|
||||||
cmdLine []byte
|
|
||||||
cmdErr error
|
|
||||||
cmd string
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
{pid: 1, cmdLine: []byte("abc"), cmdErr: nil, cmd: "abc", err: ""},
|
|
||||||
{pid: 1, cmdLine: []byte(""), cmdErr: nil, cmd: "", err: ""}, // can work with empty result
|
|
||||||
{pid: 1, cmdLine: []byte("abc\x00123"), cmdErr: nil, cmd: "abc 123", err: ""}, // turns null bytes into spaces
|
|
||||||
{pid: 1, cmdLine: []byte("abc"), cmdErr: errors.New("file-system-error"), cmd: "", err: "file-system-error"}, // returns error from file reader
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
restore := mockCmdLineReader(tt.cmdLine, tt.cmdErr)
|
|
||||||
cmd, err := getCmd(tt.pid)
|
|
||||||
if cmd != tt.cmd {
|
|
||||||
t.Errorf("Wrong cmd line returned: got %s but want %s", cmd, tt.cmd)
|
|
||||||
}
|
|
||||||
if (err != nil || tt.err != "") && fmt.Sprintf("%v", err) != tt.err {
|
|
||||||
t.Errorf("Wrong error returned: got %v but want %s", err, tt.err)
|
|
||||||
}
|
|
||||||
restore()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func mockCmdLineReader(cmdLine []byte, err error) (restore func()) {
|
|
||||||
oldFunc := cmdLineReader
|
|
||||||
cmdLineReader = func(pid int) ([]byte, error) {
|
|
||||||
return cmdLine, err
|
|
||||||
}
|
|
||||||
return func() {
|
|
||||||
cmdLineReader = oldFunc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPIDs
|
// GetPIDs
|
||||||
|
|
||||||
func TestGetPIDs(t *testing.T) {
|
func TestGetPIDs(t *testing.T) {
|
||||||
@@ -126,117 +87,82 @@ func (f *mockFileInfo) Sys() interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUID
|
type mockPidProcessor struct {
|
||||||
|
t *testing.T
|
||||||
func TestGetUID(t *testing.T) {
|
pids []int
|
||||||
completeStatus, _ := hex.DecodeString("4e616d653a0963726f6e0a556d61736b3a09303032320a53746174653a09532028736c656570696e67290a546769643a09370a4e6769643a09300a5069643a09370a505069643a09350a5472616365725069643a09300a5569643a09300930093009300a4769643a09300930093009300a464453697a653a0936340a47726f7570733a0930200a4e53746769643a09370a4e537069643a09370a4e53706769643a09310a4e537369643a09310a566d5065616b3a092020203238303132206b420a566d53697a653a092020203237393932206b420a566d4c636b3a092020202020202030206b420a566d50696e3a092020202020202030206b420a566d48574d3a092020202032333532206b420a566d5253533a092020202032333532206b420a527373416e6f6e3a092020202020323430206b420a52737346696c653a092020202032313132206b420a52737353686d656d3a092020202020202030206b420a566d446174613a092020202020333430206b420a566d53746b3a092020202020313332206b420a566d4578653a092020202020203434206b420a566d4c69623a092020202032383536206b420a566d5054453a092020202020203736206b420a566d504d443a092020202020203132206b420a566d537761703a092020202020202030206b420a48756765746c6250616765733a092020202020202030206b420a546872656164733a09310a536967513a09302f34373834320a536967506e643a09303030303030303030303030303030300a536864506e643a09303030303030303030303030303030300a536967426c6b3a09303030303030303030303030303030300a53696749676e3a09303030303030303030303030303030360a5369674367743a09303030303030303138303031303030310a436170496e683a09303030303030303061383034323566620a43617050726d3a09303030303030303061383034323566620a4361704566663a09303030303030303061383034323566620a436170426e643a09303030303030303061383034323566620a436170416d623a09303030303030303030303030303030300a536563636f6d703a09320a437075735f616c6c6f7765643a09330a437075735f616c6c6f7765645f6c6973743a09302d310a4d656d735f616c6c6f7765643a09310a4d656d735f616c6c6f7765645f6c6973743a09300a766f6c756e746172795f637478745f73776974636865733a0932350a6e6f6e766f6c756e746172795f637478745f73776974636865733a09310a")
|
|
||||||
uidLineBroken, _ := hex.DecodeString("4e616d653a0963726f6e0a556d61736b3a09303032320a53746174653a09532028736c656570696e67290a546769643a09370a4e6769643a09300a5069643a09370a505069643a09350a5472616365725069643a09300a5569643a")
|
|
||||||
notEnoughLines, _ := hex.DecodeString("4e616d653a0963726f6e0a556d61736b3a09303032320a537461")
|
|
||||||
tests := []struct {
|
|
||||||
pid int
|
|
||||||
stat []byte
|
|
||||||
statErr error
|
|
||||||
uid int
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
{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 {
|
|
||||||
restore := mockProcStatusReader(tt.stat, tt.statErr)
|
|
||||||
uid, err := getUID(tt.pid)
|
|
||||||
if uid != tt.uid {
|
|
||||||
fmt.Printf("STAT: %s", tt.stat)
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
restore()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func mockProcStatusReader(stat []byte, err error) (restore func()) {
|
func (m *mockPidProcessor) processNewPid(pid int) {
|
||||||
oldFunc := procStatusReader
|
if testing.Verbose() {
|
||||||
procStatusReader = func(pid int) ([]byte, error) {
|
m.t.Logf("proc %d processed", pid)
|
||||||
return stat, err
|
|
||||||
}
|
|
||||||
return func() {
|
|
||||||
procStatusReader = oldFunc
|
|
||||||
}
|
}
|
||||||
|
m.pids = append(m.pids, pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
// refresh
|
var unit = struct{}{}
|
||||||
|
|
||||||
func TestRefresh(t *testing.T) {
|
func TestRefresh(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
eventCh chan PSEvent
|
name string
|
||||||
pl procList
|
pl procList
|
||||||
newPids []int
|
newPids []int
|
||||||
pidsAfter []int
|
plAfter procList
|
||||||
events []string
|
pidsProcessed []int
|
||||||
}{
|
}{
|
||||||
{eventCh: make(chan PSEvent), pl: procList{}, newPids: []int{1, 2, 3}, pidsAfter: []int{3, 2, 1}, events: []string{
|
{
|
||||||
"UID=??? PID=3 | the-command",
|
name: "nominal",
|
||||||
"UID=??? PID=2 | the-command",
|
pl: procList{},
|
||||||
"UID=??? PID=1 | the-command",
|
newPids: []int{1, 2, 3},
|
||||||
}},
|
plAfter: procList{1: unit, 2: unit, 3: unit},
|
||||||
{eventCh: make(chan PSEvent), pl: procList{1: "pid-found-before"}, newPids: []int{1, 2, 3}, pidsAfter: []int{1, 3, 2}, events: []string{
|
pidsProcessed: []int{3, 2, 1},
|
||||||
"UID=??? PID=3 | the-command",
|
},
|
||||||
"UID=??? PID=2 | the-command",
|
{
|
||||||
}}, // no events emitted for PIDs already known
|
name: "merge",
|
||||||
|
pl: procList{1: unit},
|
||||||
|
newPids: []int{1, 2, 3},
|
||||||
|
plAfter: procList{1: unit, 2: unit, 3: unit},
|
||||||
|
pidsProcessed: []int{3, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nothing new",
|
||||||
|
pl: procList{1: unit, 2: unit, 3: unit},
|
||||||
|
newPids: []int{1, 2, 3},
|
||||||
|
plAfter: procList{1: unit, 2: unit, 3: unit},
|
||||||
|
pidsProcessed: []int{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
restoreGetPIDs := mockPidList(tt.newPids)
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
restoreCmdLineReader := mockCmdLineReader([]byte("the-command"), nil)
|
defer mockPidList(tt.newPids)()
|
||||||
restoreProcStatusReader := mockProcStatusReader([]byte(""), nil) // don't mock read value since it's not worth it
|
|
||||||
|
|
||||||
events := make([]string, 0)
|
m := &mockPidProcessor{t, []int{}}
|
||||||
done := make(chan struct{})
|
tt.pl.refresh(m)
|
||||||
go func() {
|
|
||||||
for e := range tt.eventCh {
|
if !reflect.DeepEqual(m.pids, tt.pidsProcessed) {
|
||||||
events = append(events, e.String())
|
t.Errorf("Unexpected pids got processed: got %v but want %v", m.pids, tt.pidsProcessed)
|
||||||
}
|
}
|
||||||
done <- struct{}{}
|
if !reflect.DeepEqual(tt.pl, tt.plAfter) {
|
||||||
}()
|
t.Errorf("Unexpected pids stored in procList: got %v but want %v", tt.pl, tt.plAfter)
|
||||||
tt.pl.refresh(tt.eventCh)
|
|
||||||
close(tt.eventCh)
|
|
||||||
<-done
|
|
||||||
|
|
||||||
restoreProcStatusReader()
|
|
||||||
restoreCmdLineReader()
|
|
||||||
restoreGetPIDs()
|
|
||||||
|
|
||||||
pidsAfter := getPids(&tt.pl)
|
|
||||||
|
|
||||||
for _, pid := range tt.pidsAfter {
|
|
||||||
if !contains(pidsAfter, pid) {
|
|
||||||
t.Errorf("PID %d should be in list %v but was not!", pid, pidsAfter)
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
for _, pid := range pidsAfter {
|
|
||||||
if !contains(tt.pidsAfter, pid) {
|
|
||||||
t.Errorf("PID %d should be in list %v but was not!", pid, pidsAfter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(events, tt.events) {
|
|
||||||
t.Errorf("Wrong events returned: got %v but want %v", events, tt.events)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func contains(list []int, v int) bool {
|
// separate test for failing, only one case where getPids fails
|
||||||
for _, i := range list {
|
func TestRefreshFail(t *testing.T) {
|
||||||
if i == v {
|
e := errors.New("file-system-error")
|
||||||
return true
|
defer mockProcDirReader([]os.FileInfo{}, e)()
|
||||||
|
m := &mockPidProcessor{t, []int{}}
|
||||||
|
pl := procList{1: unit}
|
||||||
|
err := pl.refresh(m)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Expected an error")
|
||||||
|
} else {
|
||||||
|
if strings.Index(err.Error(), e.Error()) == -1 {
|
||||||
|
t.Errorf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func mockPidList(pids []int) func() {
|
func mockPidList(pids []int) func() {
|
||||||
@@ -247,11 +173,3 @@ func mockPidList(pids []int) func() {
|
|||||||
restore := mockProcDirReader(dirs, nil)
|
restore := mockProcDirReader(dirs, nil)
|
||||||
return restore
|
return restore
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPids(pl *procList) []int {
|
|
||||||
pids := make([]int, 0)
|
|
||||||
for pid := range *pl {
|
|
||||||
pids = append(pids, pid)
|
|
||||||
}
|
|
||||||
return pids
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,15 +2,21 @@ package psscanner
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PSScanner struct{}
|
type PSScanner struct {
|
||||||
|
enablePpid bool
|
||||||
|
eventCh chan<- PSEvent
|
||||||
|
}
|
||||||
|
|
||||||
type PSEvent struct {
|
type PSEvent struct {
|
||||||
UID int
|
UID int
|
||||||
PID int
|
PID int
|
||||||
CMD string
|
PPID int
|
||||||
|
CMD string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (evt PSEvent) String() string {
|
func (evt PSEvent) String() string {
|
||||||
@@ -19,23 +25,101 @@ func (evt PSEvent) String() string {
|
|||||||
uid = "???"
|
uid = "???"
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("UID=%-4s PID=%-6d | %s", uid, evt.PID, evt.CMD)
|
if evt.PPID == -1 {
|
||||||
|
return fmt.Sprintf("UID=%-4s PID=%-6d | %s", uid, evt.PID, evt.CMD)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"UID=%-4s PID=%-6d PPID=%-6d | %s", uid, evt.PID, evt.PPID, evt.CMD)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPSScanner() *PSScanner {
|
func NewPSScanner(ppid bool) *PSScanner {
|
||||||
return &PSScanner{}
|
return &PSScanner{
|
||||||
|
enablePpid: ppid,
|
||||||
|
eventCh: nil,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PSScanner) Run(triggerCh chan struct{}) (chan PSEvent, chan error) {
|
func (p *PSScanner) Run(triggerCh chan struct{}) (chan PSEvent, chan error) {
|
||||||
eventCh := make(chan PSEvent, 100)
|
eventCh := make(chan PSEvent, 100)
|
||||||
|
p.eventCh = eventCh
|
||||||
errCh := make(chan error)
|
errCh := make(chan error)
|
||||||
pl := make(procList)
|
pl := make(procList)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
<-triggerCh
|
<-triggerCh
|
||||||
pl.refresh(eventCh)
|
pl.refresh(p)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return eventCh, errCh
|
return eventCh, errCh
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PSScanner) processNewPid(pid int) {
|
||||||
|
// quickly load data into memory before processing it, with preferance for cmd
|
||||||
|
cmdLine, errCmdLine := cmdLineReader(pid)
|
||||||
|
status, errStatus := procStatusReader(pid)
|
||||||
|
|
||||||
|
cmd := "???" // process probably terminated
|
||||||
|
if errCmdLine == nil {
|
||||||
|
for i := 0; i < len(cmdLine); i++ {
|
||||||
|
if cmdLine[i] == 0 {
|
||||||
|
cmdLine[i] = 32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd = string(cmdLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, ppid := -1, -1
|
||||||
|
if errStatus == nil {
|
||||||
|
uid, ppid, errStatus = p.parseProcessStatus(status)
|
||||||
|
if errStatus != nil {
|
||||||
|
uid = -1
|
||||||
|
ppid = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.eventCh <- PSEvent{UID: uid, PID: pid, PPID: ppid, CMD: cmd}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PSScanner) parseProcessStatus(status []byte) (int, int, error) {
|
||||||
|
lines := strings.Split(string(status), "\n")
|
||||||
|
if len(lines) < 9 {
|
||||||
|
return -1, -1, fmt.Errorf("no uid information")
|
||||||
|
}
|
||||||
|
|
||||||
|
uidL := strings.Split(lines[8], "\t")
|
||||||
|
if len(uidL) < 2 {
|
||||||
|
return -1, -1, fmt.Errorf("uid line read incomplete")
|
||||||
|
}
|
||||||
|
|
||||||
|
uid, err := strconv.Atoi(uidL[1])
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("converting %s to int: %v", uidL[1], err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ppid := -1
|
||||||
|
if p.enablePpid {
|
||||||
|
ppidL := strings.Split(lines[6], "\t")
|
||||||
|
if len(ppidL) < 2 {
|
||||||
|
return -1, -1, fmt.Errorf("ppid line read incomplete")
|
||||||
|
}
|
||||||
|
|
||||||
|
ppid, err = strconv.Atoi(ppidL[1])
|
||||||
|
if err != nil {
|
||||||
|
return -1, -1, fmt.Errorf("converting %s to int: %v", ppidL[1], err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return uid, ppid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var procStatusReader = func(pid int) ([]byte, error) {
|
||||||
|
statPath := fmt.Sprintf("/proc/%d/status", pid)
|
||||||
|
return ioutil.ReadFile(statPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdLineReader = func(pid int) ([]byte, error) {
|
||||||
|
cmdPath := fmt.Sprintf("/proc/%d/cmdline", pid)
|
||||||
|
return ioutil.ReadFile(cmdPath)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package psscanner
|
package psscanner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const timeout = 100 * time.Millisecond
|
const timeout = 100 * time.Millisecond
|
||||||
|
|
||||||
// refresh
|
|
||||||
|
|
||||||
func TestRun(t *testing.T) {
|
func TestRun(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
pids []int
|
pids []int
|
||||||
@@ -26,7 +27,7 @@ func TestRun(t *testing.T) {
|
|||||||
restoreCmdLineReader := mockCmdLineReader([]byte("the-command"), nil)
|
restoreCmdLineReader := mockCmdLineReader([]byte("the-command"), nil)
|
||||||
restoreProcStatusReader := mockProcStatusReader([]byte(""), nil) // don't mock read value since it's not worth it
|
restoreProcStatusReader := mockProcStatusReader([]byte(""), nil) // don't mock read value since it's not worth it
|
||||||
|
|
||||||
pss := NewPSScanner()
|
pss := NewPSScanner(false)
|
||||||
triggerCh := make(chan struct{})
|
triggerCh := make(chan struct{})
|
||||||
eventCh, errCh := pss.Run(triggerCh)
|
eventCh, errCh := pss.Run(triggerCh)
|
||||||
|
|
||||||
@@ -61,3 +62,414 @@ func TestRun(t *testing.T) {
|
|||||||
restoreGetPIDs()
|
restoreGetPIDs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var completeStatus, _ = hex.DecodeString("4e616d653a0963726f6e0a556d6" +
|
||||||
|
"1736b3a09303032320a53746174653a09532028736c656570696e67290a5" +
|
||||||
|
"46769643a09370a4e6769643a09300a5069643a09370a505069643a09350" +
|
||||||
|
"a5472616365725069643a09300a5569643a09300930093009300a4769643" +
|
||||||
|
"a09300930093009300a464453697a653a0936340a47726f7570733a09302" +
|
||||||
|
"00a4e53746769643a09370a4e537069643a09370a4e53706769643a09310" +
|
||||||
|
"a4e537369643a09310a566d5065616b3a092020203238303132206b420a5" +
|
||||||
|
"66d53697a653a092020203237393932206b420a566d4c636b3a092020202" +
|
||||||
|
"020202030206b420a566d50696e3a092020202020202030206b420a566d4" +
|
||||||
|
"8574d3a092020202032333532206b420a566d5253533a092020202032333" +
|
||||||
|
"532206b420a527373416e6f6e3a092020202020323430206b420a5273734" +
|
||||||
|
"6696c653a092020202032313132206b420a52737353686d656d3a0920202" +
|
||||||
|
"02020202030206b420a566d446174613a092020202020333430206b420a5" +
|
||||||
|
"66d53746b3a092020202020313332206b420a566d4578653a09202020202" +
|
||||||
|
"0203434206b420a566d4c69623a092020202032383536206b420a566d505" +
|
||||||
|
"4453a092020202020203736206b420a566d504d443a09202020202020313" +
|
||||||
|
"2206b420a566d537761703a092020202020202030206b420a48756765746" +
|
||||||
|
"c6250616765733a092020202020202030206b420a546872656164733a093" +
|
||||||
|
"10a536967513a09302f34373834320a536967506e643a093030303030303" +
|
||||||
|
"03030303030303030300a536864506e643a0930303030303030303030303" +
|
||||||
|
"0303030300a536967426c6b3a09303030303030303030303030303030300" +
|
||||||
|
"a53696749676e3a09303030303030303030303030303030360a536967436" +
|
||||||
|
"7743a09303030303030303138303031303030310a436170496e683a09303" +
|
||||||
|
"030303030303061383034323566620a43617050726d3a093030303030303" +
|
||||||
|
"03061383034323566620a4361704566663a0930303030303030306138303" +
|
||||||
|
"4323566620a436170426e643a09303030303030303061383034323566620" +
|
||||||
|
"a436170416d623a09303030303030303030303030303030300a536563636" +
|
||||||
|
"f6d703a09320a437075735f616c6c6f7765643a09330a437075735f616c6" +
|
||||||
|
"c6f7765645f6c6973743a09302d310a4d656d735f616c6c6f7765643a093" +
|
||||||
|
"10a4d656d735f616c6c6f7765645f6c6973743a09300a766f6c756e74617" +
|
||||||
|
"2795f637478745f73776974636865733a0932350a6e6f6e766f6c756e746" +
|
||||||
|
"172795f637478745f73776974636865733a09310a")
|
||||||
|
|
||||||
|
var uidLineBroken, _ = hex.DecodeString("4e616d653a0963726f6e0a556d61" +
|
||||||
|
"736b3a09303032320a53746174653a09532028736c656570696e67290a54" +
|
||||||
|
"6769643a09370a4e6769643a09300a5069643a09370a505069643a09350a" +
|
||||||
|
"5472616365725069643a09300a5569643a")
|
||||||
|
|
||||||
|
var uidNaN, _ = hex.DecodeString("4e616d653a0963726f6e0a556d61736b3a0" +
|
||||||
|
"9303032320a53746174653a09532028736c656570696e67290a546769643" +
|
||||||
|
"a09370a4e6769643a09300a5069643a09370a505069643a09350a5472616" +
|
||||||
|
"365725069643a09300a5569643a0964")
|
||||||
|
|
||||||
|
var ppidLineShort, _ = hex.DecodeString("4e616d653a0963726f6e0a556d61" +
|
||||||
|
"736b3a09303032320a53746174653a09532028736c656570696e67290a54" +
|
||||||
|
"6769643a09370a4e6769643a09300a5069643a09370a505069643a0a5472" +
|
||||||
|
"616365725069643a09300a5569643a09300a")
|
||||||
|
|
||||||
|
var ppidNaN, _ = hex.DecodeString("4e616d653a0963726f6e0a556d61736b3a" +
|
||||||
|
"09303032320a53746174653a09532028736c656570696e67290a54676964" +
|
||||||
|
"3a09370a4e6769643a09300a5069643a09370a505069643a0955450a5472" +
|
||||||
|
"616365725069643a09300a5569643a09300a")
|
||||||
|
|
||||||
|
var notEnoughLines, _ = hex.DecodeString(
|
||||||
|
"4e616d653a0963726f6e0a556d61736b3a09303032320a537461")
|
||||||
|
|
||||||
|
func TestProcessNewPid(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
enablePpid bool
|
||||||
|
pid int
|
||||||
|
cmdLine []byte
|
||||||
|
cmdLineErr error
|
||||||
|
status []byte
|
||||||
|
statusErr error
|
||||||
|
expected PSEvent
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nominal-no-ppid",
|
||||||
|
enablePpid: false,
|
||||||
|
pid: 1,
|
||||||
|
cmdLine: []byte("abc\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: completeStatus,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: 0,
|
||||||
|
PID: 1,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "abc 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nominal-ppid",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 1,
|
||||||
|
cmdLine: []byte("abc\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: completeStatus,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: 0,
|
||||||
|
PID: 1,
|
||||||
|
PPID: 5,
|
||||||
|
CMD: "abc 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty-cmd-ok",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 1,
|
||||||
|
cmdLine: []byte{},
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: completeStatus,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: 0,
|
||||||
|
PID: 1,
|
||||||
|
PPID: 5,
|
||||||
|
CMD: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cmd-io-error",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 2,
|
||||||
|
cmdLine: nil,
|
||||||
|
cmdLineErr: errors.New("file-system-error"),
|
||||||
|
status: completeStatus,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: 0,
|
||||||
|
PID: 2,
|
||||||
|
PPID: 5,
|
||||||
|
CMD: "???",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "status-io-error",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 2,
|
||||||
|
cmdLine: []byte("some\x00cmd\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: nil,
|
||||||
|
statusErr: errors.New("file-system-error"),
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: -1,
|
||||||
|
PID: 2,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "some cmd 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "status-too-short",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 3,
|
||||||
|
cmdLine: []byte("some\x00cmd\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: notEnoughLines,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: -1,
|
||||||
|
PID: 3,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "some cmd 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "status-empty",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 3,
|
||||||
|
cmdLine: []byte("some\x00cmd\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: []byte{},
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: -1,
|
||||||
|
PID: 3,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "some cmd 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "uid-line-too-short",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 3,
|
||||||
|
cmdLine: []byte("some\x00cmd\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: uidLineBroken,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: -1,
|
||||||
|
PID: 3,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "some cmd 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "uid-parse-error",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 3,
|
||||||
|
cmdLine: []byte("some\x00cmd\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: uidNaN,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: -1,
|
||||||
|
PID: 3,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "some cmd 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ppid-line-too-short",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 3,
|
||||||
|
cmdLine: []byte("some\x00cmd\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: ppidLineShort,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: -1,
|
||||||
|
PID: 3,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "some cmd 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ppid-parse-error",
|
||||||
|
enablePpid: true,
|
||||||
|
pid: 3,
|
||||||
|
cmdLine: []byte("some\x00cmd\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: ppidNaN,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: -1,
|
||||||
|
PID: 3,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "some cmd 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-ppid-line-too-short",
|
||||||
|
enablePpid: false,
|
||||||
|
pid: 3,
|
||||||
|
cmdLine: []byte("some\x00cmd\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: ppidLineShort,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: 0,
|
||||||
|
PID: 3,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "some cmd 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-ppid-parse-error",
|
||||||
|
enablePpid: false,
|
||||||
|
pid: 3,
|
||||||
|
cmdLine: []byte("some\x00cmd\x00123"),
|
||||||
|
cmdLineErr: nil,
|
||||||
|
status: ppidNaN,
|
||||||
|
statusErr: nil,
|
||||||
|
expected: PSEvent{
|
||||||
|
UID: 0,
|
||||||
|
PID: 3,
|
||||||
|
PPID: -1,
|
||||||
|
CMD: "some cmd 123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
defer mockCmdLineReader(tt.cmdLine, tt.cmdLineErr)()
|
||||||
|
defer mockProcStatusReader(tt.status, tt.statusErr)()
|
||||||
|
|
||||||
|
results := make(chan PSEvent, 1)
|
||||||
|
|
||||||
|
scanner := &PSScanner{
|
||||||
|
enablePpid: tt.enablePpid,
|
||||||
|
eventCh: results,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
scanner.processNewPid(tt.pid)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-time.After(timeout):
|
||||||
|
t.Error("Timeout waiting for event")
|
||||||
|
case event := <-results:
|
||||||
|
close(results)
|
||||||
|
if testing.Verbose() {
|
||||||
|
t.Logf("received event: %#v", event)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(event, tt.expected) {
|
||||||
|
t.Errorf("Event received but format is has unexpected values: got %#v but want %#v", event, tt.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockCmdLineReader(cmdLine []byte, err error) (restore func()) {
|
||||||
|
oldFunc := cmdLineReader
|
||||||
|
cmdLineReader = func(pid int) ([]byte, error) {
|
||||||
|
return cmdLine, err
|
||||||
|
}
|
||||||
|
return func() {
|
||||||
|
cmdLineReader = oldFunc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mockProcStatusReader(stat []byte, err error) (restore func()) {
|
||||||
|
oldFunc := procStatusReader
|
||||||
|
procStatusReader = func(pid int) ([]byte, error) {
|
||||||
|
return stat, err
|
||||||
|
}
|
||||||
|
return func() {
|
||||||
|
procStatusReader = oldFunc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewPSScanner(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
ppid bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "without-ppid",
|
||||||
|
ppid: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with-ppid",
|
||||||
|
ppid: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
expected := &PSScanner{
|
||||||
|
enablePpid: tt.ppid,
|
||||||
|
eventCh: nil,
|
||||||
|
}
|
||||||
|
new := NewPSScanner(tt.ppid)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(new, expected) {
|
||||||
|
t.Errorf("Unexpected scanner initialisation state: got %#v but want %#v", new, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPSEvent(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
uid int
|
||||||
|
pid int
|
||||||
|
ppid int
|
||||||
|
cmd string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nominal-with-ppid",
|
||||||
|
uid: 999,
|
||||||
|
pid: 123,
|
||||||
|
ppid: 321,
|
||||||
|
cmd: "some cmd",
|
||||||
|
expected: "UID=999 PID=123 PPID=321 | some cmd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nominal-without-ppid",
|
||||||
|
uid: 999,
|
||||||
|
pid: 123,
|
||||||
|
ppid: -1,
|
||||||
|
cmd: "some cmd",
|
||||||
|
expected: "UID=999 PID=123 | some cmd",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nocmd-without-ppid",
|
||||||
|
uid: 999,
|
||||||
|
pid: 123,
|
||||||
|
ppid: -1,
|
||||||
|
cmd: "",
|
||||||
|
expected: "UID=999 PID=123 | ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nocmd-with-ppid",
|
||||||
|
uid: 999,
|
||||||
|
pid: 123,
|
||||||
|
ppid: 321,
|
||||||
|
cmd: "",
|
||||||
|
expected: "UID=999 PID=123 PPID=321 | ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nouid",
|
||||||
|
uid: -1,
|
||||||
|
pid: 123,
|
||||||
|
ppid: 321,
|
||||||
|
cmd: "some cmd",
|
||||||
|
expected: "UID=??? PID=123 PPID=321 | some cmd",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ps := PSEvent{
|
||||||
|
UID: tt.uid,
|
||||||
|
PID: tt.pid,
|
||||||
|
PPID: tt.ppid,
|
||||||
|
CMD: tt.cmd,
|
||||||
|
}
|
||||||
|
if ps.String() != tt.expected {
|
||||||
|
t.Errorf("Expecting \"%s\", got \"%s\"", tt.expected, ps.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user