mirror of
https://github.com/DominicBreuker/pspy.git
synced 2025-12-21 11:44:51 +00:00
add some hacky experiments for inotify event parsing
This commit is contained in:
@@ -1,9 +1,15 @@
|
|||||||
FROM golang:1.9-stretch
|
FROM golang:1.9-stretch
|
||||||
|
|
||||||
RUN useradd -ms /bin/bash myuser
|
RUN apt-get update && apt-get -y install cron sudo
|
||||||
|
COPY docker/etc/cron.d /etc/cron.d
|
||||||
|
COPY docker/scripts /scripts
|
||||||
|
|
||||||
|
RUN useradd -ms /bin/bash myuser && \
|
||||||
|
adduser myuser sudo && \
|
||||||
|
echo 'myuser ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
|
||||||
USER myuser
|
USER myuser
|
||||||
|
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/dominicbreuker
|
WORKDIR /go/src/github.com/dominicbreuker
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
docker/etc/cron.d/myjob
Normal file
1
docker/etc/cron.d/myjob
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* * * * * root echo 'this is some text' >> /tmp/myjob.log
|
||||||
1
docker/etc/cron.d/print
Normal file
1
docker/etc/cron.d/print
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* * * * * root python /scripts/print_stuff.py >> /tmp/print.log
|
||||||
9
docker/scripts/print_stuff.py
Normal file
9
docker/scripts/print_stuff.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
user = "myusername"
|
||||||
|
password = "thepw"
|
||||||
|
|
||||||
|
for i in range(100):
|
||||||
|
print("a"*i)
|
||||||
|
|
||||||
|
print("done")
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/dominicbreuker/pspy/internal/inotify"
|
"github.com/dominicbreuker/pspy/internal/inotify"
|
||||||
"github.com/dominicbreuker/pspy/internal/process"
|
"github.com/dominicbreuker/pspy/internal/process"
|
||||||
|
"github.com/dominicbreuker/pspy/internal/walker"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Process struct {
|
type Process struct {
|
||||||
@@ -19,32 +20,36 @@ type Process struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Monitor() {
|
func Monitor() {
|
||||||
// procList := make(map[int]string)
|
|
||||||
|
|
||||||
watch()
|
watch()
|
||||||
|
|
||||||
// for {
|
|
||||||
// refresh(procList)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func watch() {
|
func watch() {
|
||||||
|
maxWatchers, err := inotify.WatcherLimit()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Can't get inotify watcher limit...: %v\n", err)
|
||||||
|
}
|
||||||
|
log.Printf("Watcher limit: %d\n", maxWatchers)
|
||||||
|
|
||||||
ping := make(chan struct{})
|
ping := make(chan struct{})
|
||||||
in, err := inotify.NewInotify(ping)
|
in, err := inotify.NewInotify(ping)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Can't init inotify: %v", err)
|
log.Fatalf("Can't init inotify: %v", err)
|
||||||
}
|
}
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
dirs := []string{
|
dirCh, errCh := walker.Walk("/tmp")
|
||||||
"/proc",
|
loop:
|
||||||
"/var/log",
|
for {
|
||||||
"/home",
|
select {
|
||||||
"/tmp",
|
case dir, ok := <-dirCh:
|
||||||
|
if !ok {
|
||||||
|
break loop
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, dir := range dirs {
|
|
||||||
if err := in.Watch(dir); err != nil {
|
if err := in.Watch(dir); err != nil {
|
||||||
log.Fatalf("Can't create watcher: %v", err)
|
log.Printf("Can't create watcher: %v", err)
|
||||||
|
}
|
||||||
|
case err := <-errCh:
|
||||||
|
log.Printf("Error walking filesystem: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,14 +57,13 @@ func watch() {
|
|||||||
|
|
||||||
procList := process.NewProcList()
|
procList := process.NewProcList()
|
||||||
|
|
||||||
ticker := time.NewTicker(50 * time.Millisecond).C
|
ticker := time.NewTicker(100 * time.Millisecond).C
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker:
|
case <-ticker:
|
||||||
refresh(in, procList)
|
refresh(in, procList)
|
||||||
case <-ping:
|
case <-ping:
|
||||||
log.Printf("PING")
|
|
||||||
refresh(in, procList)
|
refresh(in, procList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,6 +74,6 @@ func refresh(in *inotify.Inotify, pl *process.ProcList) {
|
|||||||
if err := pl.Refresh(); err != nil {
|
if err := pl.Refresh(); err != nil {
|
||||||
log.Printf("ERROR refreshing process list: %v", err)
|
log.Printf("ERROR refreshing process list: %v", err)
|
||||||
}
|
}
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
in.UnPause()
|
in.UnPause()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,16 @@ package inotify
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Inotify struct {
|
type Inotify struct {
|
||||||
fd int
|
fd int
|
||||||
watchers []*watcher
|
watchers map[int]*watcher
|
||||||
ping chan struct{}
|
ping chan struct{}
|
||||||
paused bool
|
paused bool
|
||||||
}
|
}
|
||||||
@@ -21,6 +24,7 @@ func NewInotify(ping chan struct{}) (*Inotify, error) {
|
|||||||
|
|
||||||
i := &Inotify{
|
i := &Inotify{
|
||||||
fd: fd,
|
fd: fd,
|
||||||
|
watchers: make(map[int]*watcher),
|
||||||
ping: ping,
|
ping: ping,
|
||||||
paused: false,
|
paused: false,
|
||||||
}
|
}
|
||||||
@@ -34,7 +38,14 @@ func (i *Inotify) Watch(dir string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating watcher: %v", err)
|
return fmt.Errorf("creating watcher: %v", err)
|
||||||
}
|
}
|
||||||
i.watchers = append(i.watchers, w)
|
i.watchers[w.wd] = w
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Inotify) Close() error {
|
||||||
|
if err := unix.Close(i.fd); err != nil {
|
||||||
|
return fmt.Errorf("closing inotify file descriptor: %v", err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,19 +58,104 @@ func (i *Inotify) UnPause() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Inotify) String() string {
|
func (i *Inotify) String() string {
|
||||||
|
if len(i.watchers) < 20 {
|
||||||
dirs := make([]string, 0)
|
dirs := make([]string, 0)
|
||||||
for _, w := range i.watchers {
|
for _, w := range i.watchers {
|
||||||
dirs = append(dirs, w.dir)
|
dirs = append(dirs, w.dir)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Watching: %v", dirs)
|
return fmt.Sprintf("Watching: %v", dirs)
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("Watching %d directories", len(i.watchers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type bufRead struct {
|
||||||
|
n int
|
||||||
|
buf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func watch(i *Inotify) {
|
func watch(i *Inotify) {
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 20*unix.SizeofInotifyEvent)
|
||||||
|
buffers := make(chan bufRead)
|
||||||
|
go verboseWatcher(i, buffers)
|
||||||
for {
|
for {
|
||||||
_, _ = unix.Read(i.fd, buf)
|
n, _ := unix.Read(i.fd, buf)
|
||||||
if !i.paused {
|
if !i.paused {
|
||||||
i.ping <- struct{}{}
|
i.ping <- struct{}{}
|
||||||
}
|
}
|
||||||
|
buffers <- bufRead{
|
||||||
|
n: n,
|
||||||
|
buf: buf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verboseWatcher(i *Inotify, buffers chan bufRead) {
|
||||||
|
for bf := range buffers {
|
||||||
|
n := bf.n
|
||||||
|
buf := bf.buf
|
||||||
|
|
||||||
|
if n < unix.SizeofInotifyEvent {
|
||||||
|
if n == 0 {
|
||||||
|
// If EOF is received. This should really never happen.
|
||||||
|
panic(fmt.Sprintf("No bytes read from fd"))
|
||||||
|
} else if n < 0 {
|
||||||
|
// If an error occurred while reading.
|
||||||
|
log.Printf("ERROR: reading from inotify: %d", n)
|
||||||
|
} else {
|
||||||
|
// Read was too short.
|
||||||
|
log.Printf("ERROR: Short read")
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset uint32
|
||||||
|
for offset <= uint32(n-unix.SizeofInotifyEvent) {
|
||||||
|
raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
|
||||||
|
|
||||||
|
mask := uint32(raw.Mask)
|
||||||
|
nameLen := uint32(raw.Len)
|
||||||
|
|
||||||
|
name := i.watchers[int(raw.Wd)].dir
|
||||||
|
if nameLen > 0 {
|
||||||
|
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))
|
||||||
|
if uint32(len(bytes)) > nameLen {
|
||||||
|
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ev := newEvent(name, mask)
|
||||||
|
log.Printf("### %+v", ev)
|
||||||
|
|
||||||
|
offset += unix.SizeofInotifyEvent + nameLen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
name string
|
||||||
|
op string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Event) String() string {
|
||||||
|
return fmt.Sprintf("%10s | %s", e.op, e.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEvent(name string, mask uint32) Event {
|
||||||
|
e := Event{name: name}
|
||||||
|
if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO {
|
||||||
|
e.op = "CREATE"
|
||||||
|
}
|
||||||
|
if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE {
|
||||||
|
e.op = "REMOVE"
|
||||||
|
}
|
||||||
|
if mask&unix.IN_MODIFY == unix.IN_MODIFY {
|
||||||
|
e.op = "WRITE"
|
||||||
|
}
|
||||||
|
if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM {
|
||||||
|
e.op = "RENAME"
|
||||||
|
}
|
||||||
|
if mask&unix.IN_ATTRIB == unix.IN_ATTRIB {
|
||||||
|
e.op = "CHMOD"
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ package inotify
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const events = unix.IN_ALL_EVENTS
|
const events = unix.IN_ALL_EVENTS
|
||||||
|
const MaximumWatchersFile = "/proc/sys/fs/inotify/max_user_watches"
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
wd int
|
wd int
|
||||||
@@ -23,3 +27,18 @@ func newWatcher(fd int, dir string, ping chan struct{}) (*watcher, error) {
|
|||||||
dir: dir,
|
dir: dir,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WatcherLimit() (int, error) {
|
||||||
|
b, err := ioutil.ReadFile(MaximumWatchersFile)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("reading from %s: %v", MaximumWatchersFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := strings.TrimSpace(string(b))
|
||||||
|
m, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("converting to integer: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|||||||
47
internal/walker/walker.go
Normal file
47
internal/walker/walker.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package walker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Walk(root string) (dirCh chan string, errCh chan error) {
|
||||||
|
dirCh = make(chan string)
|
||||||
|
errCh = make(chan error)
|
||||||
|
dirs := make([]string, 1)
|
||||||
|
dirs[0] = root
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
dirCh <- root
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
if len(dirs) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
dirs = descent(dirs, dirCh, errCh)
|
||||||
|
}
|
||||||
|
close(dirCh)
|
||||||
|
close(errCh)
|
||||||
|
}()
|
||||||
|
return dirCh, errCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func descent(dirs []string, dirCh chan string, errCh chan error) []string {
|
||||||
|
next := make([]string, 0)
|
||||||
|
for _, dir := range dirs {
|
||||||
|
ls, err := ioutil.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
errCh <- fmt.Errorf("opening dir %s: %v", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range ls {
|
||||||
|
if e.IsDir() {
|
||||||
|
newDir := dir + e.Name() + "/"
|
||||||
|
dirCh <- newDir
|
||||||
|
next = append(next, newDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user