package main import ( "crypto/sha256" "fmt" "io" "log" "maps" "os" "os/exec" "os/signal" "slices" "time" "github.com/fsnotify/fsnotify" ) type Listen struct { FileMap map[string]bool CksumMap map[string][]byte Condition string Cksum bool Interval string Command []string } func loopFsnotify(l Listen, quit chan bool) { watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal(err) } defer watcher.Close() for key := range l.FileMap { watcher.Add(key) fmt.Println(watcher.WatchList()) } for { var renameAdd []string = []string{} hasher := sha256.New() trigger := false select { case event, ok := <-watcher.Events: if !ok { return } for key := range l.FileMap { if event.Name == key { if l.Cksum { f, err := os.Open(key) if err != nil { log.Fatal(err) } defer f.Close() if _, err := io.Copy(hasher, f); err != nil { log.Fatal(err) } if !slices.Equal(l.CksumMap[key], hasher.Sum(nil)) { if l.Condition == "any" { trigger = true break } l.FileMap[key] = true } } else { if event.Has(fsnotify.Write) { if l.Condition == "any" { trigger = true break } l.FileMap[key] = true } else if event.Has(fsnotify.Rename) { // we need to rewatch file // sleeping small amount to allow CREATE event to propogate time.Sleep(10 * time.Millisecond) // ... then adding to a list to allow additional time renameAdd = append(renameAdd, key) if l.Condition == "any" { trigger = true break } l.FileMap[key] = true } } } } // end for if l.Condition == "all" { trigger = true for value := range maps.Values(l.FileMap) { if !value { trigger = false break } } } // end if condition // end case event case _, ok := <-watcher.Errors: if !ok { return } } // end switch // end case errors if trigger { if len(l.Command) >= 1 { cmd := exec.Command(l.Command[0], l.Command[1:]...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Run() } else { quit <- true } } // end if trigger for _, value := range renameAdd { err := watcher.Add(value) if err != nil { fmt.Println(err) } } } // end for } func loopInterval(l Listen, quit chan bool) { for { fmt.Printf("running") time.Sleep(1 * time.Second) } } func (l Listen) Run() { // catch ^C quit := make(chan bool) c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) // main loop if l.Interval == "" { go loopFsnotify(l, quit) } else { go loopInterval(l, quit) } select { case <-c: case <-quit: } }