Files
firewall_containers/network-go/watcher/watcher.go
gyurix c3de398f35
continuous-integration/drone/push Build encountered an error
added network-go project
2026-06-08 15:34:01 +02:00

83 lines
1.8 KiB
Go

package watcher
import (
"crypto/md5"
"fmt"
"log"
"os"
"time"
)
// FileWatcher periodically checks a file for changes using MD5 hash
type FileWatcher struct {
path string
period time.Duration
lastHash string
onChange func()
stopCh chan struct{}
}
// NewFileWatcher creates a new file watcher that polls the file at the given period
func NewFileWatcher(path string, period time.Duration, onChange func()) *FileWatcher {
return &FileWatcher{
path: path,
period: period,
lastHash: "",
onChange: onChange,
stopCh: make(chan struct{}),
}
}
// hashFile computes an MD5 hash of the file contents
func (fw *FileWatcher) hashFile() (string, error) {
data, err := os.ReadFile(fw.path)
if err != nil {
return "", fmt.Errorf("failed to read file %s: %w", fw.path, err)
}
return fmt.Sprintf("%x", md5.Sum(data)), nil
}
// Start begins polling the file for changes in a goroutine
func (fw *FileWatcher) Start() {
// Compute initial hash
hash, err := fw.hashFile()
if err != nil {
log.Printf("WATCHER: initial hash computation failed for %s: %v", fw.path, err)
} else {
fw.lastHash = hash
}
go func() {
ticker := time.NewTicker(fw.period)
defer ticker.Stop()
log.Printf("WATCHER: started watching %s every %s", fw.path, fw.period)
for {
select {
case <-fw.stopCh:
log.Printf("WATCHER: stopped watching %s", fw.path)
return
case <-ticker.C:
hash, err := fw.hashFile()
if err != nil {
log.Printf("WATCHER: failed to hash %s: %v", fw.path, err)
continue
}
if hash != fw.lastHash {
log.Printf("WATCHER: detected change in %s", fw.path)
fw.lastHash = hash
if fw.onChange != nil {
fw.onChange()
}
}
}
}
}()
}
// Stop signals the watcher goroutine to stop
func (fw *FileWatcher) Stop() {
close(fw.stopCh)
}