bustle/cmd/bustled/watcher.go

126 lines
2.5 KiB
Go

package main
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/fsnotify/fsnotify"
)
type Watcher interface {
AddDirectory(string) error
Watch()
Close()
Root() string
}
type HomeWatcher struct {
watcher *fsnotify.Watcher
root string
raw chan Event
}
func NewHomeWatcher(rawEvents chan Event) (Watcher, error) {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
hw := &HomeWatcher{
watcher: watcher,
root: "/home",
raw: rawEvents,
}
out, err := exec.Command("sh", "-c", "stats | jq .active_users[] | tr -d '\"'").Output()
if err != nil {
return nil, fmt.Errorf("failed to call and process stats: %w", err)
}
scanner := bufio.NewScanner(bytes.NewReader(out))
for scanner.Scan() {
username := strings.TrimSpace(scanner.Text())
home := filepath.Join(hw.Root(), username)
if err := hw.AddDirectory(home); err != nil {
return nil, err
}
}
return hw, nil
}
func (hw *HomeWatcher) AddDirectory(path string) error {
fileCount := 0
filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if info.IsDir() && strings.HasPrefix(info.Name(), ".") {
return filepath.SkipDir
}
fileCount++
if info.Mode()&os.ModeSymlink != 0 {
return nil
}
err = hw.watcher.Add(path)
if err != nil && err.Error() != "permission denied" && err.Error() != "no such file or directory" {
return fmt.Errorf("died at file %d. info: %#v error: %w\n", fileCount, info, err)
}
log.Printf("watching %s\n", path)
return nil
})
return nil
}
func (hw *HomeWatcher) Root() string {
return hw.root
}
func (hw *HomeWatcher) Close() {
hw.watcher.Close()
}
func (hw *HomeWatcher) Watch() {
unre := regexp.MustCompile("^/home/([^/]+)/.*$")
for {
select {
case event, ok := <-hw.watcher.Events:
if !ok {
return
}
matches := unre.FindStringSubmatch(event.Name)
if matches == nil {
log.Printf("could not extract username: %s\n", event)
}
un := matches[1]
hw.raw <- Event{
Username: un,
Type: eventHomeActivity,
Flavor: fmt.Sprintf("%s is knocking about in %s", un, path.Dir(event.Name)),
}
// event.Name is filepath
log.Println("event:", event)
fmt.Printf("DBG %#v\n", event)
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("modified file:", event.Name)
}
case err, ok := <-hw.watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}