sysmonitord/internal/scanner/watcher.go

120 lines
2.6 KiB
Go

package scanner
import (
"log"
"os"
"time"
"github.com/fsnotify/fsnotify"
"github.com/wuko233/sysmonitord/internal/network"
"github.com/wuko233/sysmonitord/internal/whitelist"
)
type Watcher struct {
wlManager *whitelist.Manager
client *network.WSClient
watcher *fsnotify.Watcher
stopChan chan struct{}
watchPaths []string
}
func NewWatcher(wl *whitelist.Manager, client *network.WSClient) (*Watcher, error) {
fsWatch, err := fsnotify.NewWatcher()
if err != nil {
return nil, err
}
return &Watcher{
wlManager: wl,
client: client,
watcher: fsWatch,
stopChan: make(chan struct{}),
// TODO: 当前仅实现对主目录的监控,后续实现递归监控子目录
watchPaths: []string{
"/bin", "/sbin", "/usr/bin", "/etc/init.d", "/tmp",
},
}, nil
}
func (w *Watcher) Start() {
log.Println("[监听器] 启动实时文件监控...")
for _, path := range w.watchPaths {
if _, err := os.Stat(path); err == nil {
if err := w.watcher.Add(path); err != nil {
log.Printf("[监听器] 无法监控路径 %s: %v", path, err)
} else {
log.Printf("[监听器] 开始监控路径: %s", path)
}
}
}
go w.eventLoop()
}
func (w *Watcher) eventLoop() {
for {
select {
case <-w.stopChan:
return
case event, ok := <-w.watcher.Events:
if !ok {
return
}
if event.Has(fsnotify.Create) || event.Has(fsnotify.Write) {
if w.wlManager.IsPathIgnored(event.Name) {
continue
}
go w.handleFileChange(event.Name, event.Op.String())
}
case err, ok := <-w.watcher.Errors:
if !ok {
return
}
log.Printf("[监听器] 错误: %v", err)
}
}
}
func (w *Watcher) handleFileChange(path string, op string) {
time.Sleep(200 * time.Millisecond) // 等待文件写入完成
if _, err := os.Stat(path); os.IsNotExist(err) {
return
}
isWhitelisted, isHashMatch, err := w.wlManager.CheckFileStatus(path)
if err != nil {
return
}
if !isWhitelisted {
log.Printf("[监听器] 实时拦截:检测到非白名单文件变动 (%s): %s", op, path)
w.reportEvent(path, "REALTIME_FILE_ALERT", op)
} else if !isHashMatch {
log.Printf("[监听器] 实时拦截:检测到白名单文件被篡改 (%s): %s", op, path)
w.reportEvent(path, "REALTIME_HASH_MISMATCH", op)
}
}
func (w *Watcher) reportEvent(path, alertType, op string) {
payload := map[string]interface{}{
"filepath": path,
"operation": op,
"time": time.Now(),
}
packet := network.NewPactet(alertType, payload)
w.client.SendQueue(packet)
}
func (w *Watcher) Stop() {
log.Println("[监听器] 停止实时文件监控...")
close(w.stopChan)
w.watcher.Close()
}