Compare commits

...

2 Commits

Author SHA1 Message Date
wuko233 99eb6c2aed [monitor] 规范watcher代码 2026-04-03 08:42:05 +08:00
wuko233 c9a5a40645 [monitor] 实现进程检测 2026-04-03 08:32:57 +08:00
6 changed files with 186 additions and 19 deletions

View File

@ -3,7 +3,11 @@ package start
import ( import (
"fmt" "fmt"
"os" "os"
"os/signal"
"syscall"
"sysmonitord/internal/config" "sysmonitord/internal/config"
"sysmonitord/internal/monitor/detector"
"sysmonitord/internal/monitor/timer"
"sysmonitord/internal/monitor/watcher" "sysmonitord/internal/monitor/watcher"
"sysmonitord/internal/scanner/file" "sysmonitord/internal/scanner/file"
"sysmonitord/internal/scanner/process" "sysmonitord/internal/scanner/process"
@ -83,7 +87,7 @@ var StartCmd = &cobra.Command{
// ====== 启动文件监听 ====== // ====== 启动文件监听 ======
logger.Log.Info("正在启动文件监听...") logger.Log.Info("正在启动文件监听...")
mon, err := watcher.NewWatcher(cfg.Scanner.File.IncludePaths, cfg.Scanner.File.ExcludePaths) mon, err := watcher.NewWatcher(cfg)
if err != nil { if err != nil {
logger.Log.Error("启动文件监听失败", zap.Error(err)) logger.Log.Error("启动文件监听失败", zap.Error(err))
os.Exit(1) os.Exit(1)
@ -91,7 +95,15 @@ var StartCmd = &cobra.Command{
mon.Start() mon.Start()
logger.Log.Info("系统监控守护服务已启动,正在监控文件系统变化...") // ====== 启动进程检测定时任务 ======
procDetector := detector.NewProcessDetector(cfg)
procScheduler := timer.NewScheduler(time.Duration(cfg.Scanner.Process.Interval)*time.Second, procDetector)
procScheduler.Start()
logger.Log.Info("系统监控守护服务已启动,正在监控系统变化...")
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
for { for {
select { select {
@ -110,11 +122,12 @@ var StartCmd = &cobra.Command{
case err := <-mon.Errors(): case err := <-mon.Errors():
logger.Log.Error("文件监听错误", zap.Error(err)) logger.Log.Error("文件监听错误", zap.Error(err))
// case <-quit: case <-quit:
// logger.Log.Info("正在停止系统监控守护服务...") logger.Log.Info("正在停止系统监控守护服务...")
// mon.Stop() mon.Stop()
// logger.Log.Info("系统监控守护服务已停止") procScheduler.Stop()
// return logger.Log.Info("系统监控守护服务已停止")
return
} }
} }

View File

@ -3,8 +3,8 @@ server:
port: 8080 port: 8080
log: log:
# level: "info" level: "info"
level: "debug" # level: "debug"
audit: audit:
enabled: true enabled: true
@ -26,6 +26,8 @@ scanner:
fast_hash: true fast_hash: true
fast_hash_size: 100MB fast_hash_size: 100MB
fast_hash_chunk: 2MB fast_hash_chunk: 2MB
process:
interval: 30 # seconds
storage: storage:
data_dir: "./data" data_dir: "./data"

View File

@ -19,14 +19,19 @@ type AuditConfig struct {
} }
type ScannerConfig struct { type ScannerConfig struct {
File FileScannerConfig `yaml:"file"` File FileScannerConfig `yaml:"file"`
Hash hashConfig `yaml:"hash"` Hash hashConfig `yaml:"hash"`
Process ProcessScannerConfig `yaml:"process"`
} }
type hashConfig struct { type hashConfig struct {
Algorithm string `yaml:"algorithm"` Algorithm string `yaml:"algorithm"`
} }
type ProcessScannerConfig struct {
Interval int `yaml:"interval"`
}
type StorageConfig struct { type StorageConfig struct {
DataDir string `yaml:"data_dir"` DataDir string `yaml:"data_dir"`
ProcessSystemFile string `yaml:"process_system_file"` ProcessSystemFile string `yaml:"process_system_file"`

View File

@ -0,0 +1,82 @@
package detector
import (
"bufio"
"os"
"path/filepath"
"strings"
"sysmonitord/internal/config"
"sysmonitord/internal/scanner/process"
"sysmonitord/pkg/logger"
"go.uber.org/zap"
)
type ProcessDetector struct {
cfg *config.Config
whiteList map[string]string
storagePath string
}
func NewProcessDetector(cfg *config.Config) *ProcessDetector {
p := &ProcessDetector{
cfg: cfg,
whiteList: make(map[string]string),
}
p.loadWhiteList()
return p
}
func (p *ProcessDetector) loadWhiteList() {
filepath := filepath.Join(p.cfg.Storage.DataDir, p.cfg.Storage.ProcessSystemFile)
file, err := os.Open(filepath)
if err != nil {
logger.Log.Error("[monitor] 加载进程白名单失败", zap.String("file", filepath), zap.Error(err))
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if line == "" || strings.HasPrefix(line, "#") {
continue
}
parts := strings.Split(line, ":")
if len(parts) >= 3 {
p.whiteList[parts[1]] = parts[2] // name:path:hash
}
}
logger.Log.Info("[monitor] 进程白名单加载完成", zap.Int("count", len(p.whiteList)))
}
func (p *ProcessDetector) Run() error {
logger.Log.Info("[monitor] 进程检测已启动")
currentProcs, err := process.ScanAllProcesses(p.cfg)
if err != nil {
logger.Log.Error("[monitor] 扫描进程失败", zap.Error(err))
return err
}
newCount := 0
for _, proc := range currentProcs {
_, exists := p.whiteList[proc.Path]
if !exists {
logger.Log.Warn("[monitor] 发现新进程", zap.String("name", proc.Name), zap.String("path", proc.Path))
newCount++
// Todo: 处理新进程
}
}
logger.Log.Info("[monitor] 进程检测完成", zap.Int("total", len(currentProcs)), zap.Int("new", newCount))
return nil
}
func (p *ProcessDetector) Name() string {
return "ProcessMonitor"
}

View File

@ -0,0 +1,64 @@
package timer
import (
"sync"
"sysmonitord/pkg/logger"
"time"
"go.uber.org/zap"
)
type Job interface {
Run() error
Name() string
}
type Scheduler struct {
ticker *time.Ticker
stopCh chan struct{}
job Job
wg sync.WaitGroup
interval time.Duration
}
func NewScheduler(interval time.Duration, job Job) *Scheduler {
return &Scheduler{
ticker: time.NewTicker(interval),
stopCh: make(chan struct{}),
job: job,
interval: interval,
}
}
func (s *Scheduler) Start() {
logger.Log.Info("[monitor] 定时任务已启动", zap.String("job", s.job.Name()), zap.Duration("interval", s.interval))
s.wg.Add(1)
go func() {
defer s.wg.Done()
logger.Log.Info("[monitor] 执行定时任务", zap.String("job", s.job.Name()))
if err := s.job.Run(); err != nil {
logger.Log.Error("[monitor] 定时任务执行失败", zap.String("job", s.job.Name()), zap.Error(err))
}
for {
select {
case <-s.ticker.C:
logger.Log.Info("[monitor] 执行定时任务", zap.String("job", s.job.Name()))
if err := s.job.Run(); err != nil {
logger.Log.Error("[monitor] 定时任务执行失败", zap.String("job", s.job.Name()), zap.Error(err))
}
case <-s.stopCh:
logger.Log.Info("[monitor] 定时任务已停止", zap.String("job", s.job.Name()))
return
}
}
}()
}
func (s *Scheduler) Stop() {
close(s.stopCh)
s.ticker.Stop()
s.wg.Wait()
}

View File

@ -5,6 +5,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"sysmonitord/internal/config"
"sysmonitord/pkg/logger" "sysmonitord/pkg/logger"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
@ -13,8 +14,7 @@ import (
type Watcher struct { type Watcher struct {
fsnWatcher *fsnotify.Watcher fsnWatcher *fsnotify.Watcher
paths []string cfg *config.Config
ignore []string
eventChan chan EventMsg eventChan chan EventMsg
} }
@ -24,7 +24,7 @@ type EventMsg struct {
FileInfo os.FileInfo FileInfo os.FileInfo
} }
func NewWatcher(paths []string, ignore []string) (*Watcher, error) { func NewWatcher(cfg *config.Config) (*Watcher, error) {
fsnW, err := fsnotify.NewWatcher() fsnW, err := fsnotify.NewWatcher()
if err != nil { if err != nil {
return nil, fmt.Errorf("[monitor] 创建文件监听失败: %w", err) return nil, fmt.Errorf("[monitor] 创建文件监听失败: %w", err)
@ -32,14 +32,15 @@ func NewWatcher(paths []string, ignore []string) (*Watcher, error) {
return &Watcher{ return &Watcher{
fsnWatcher: fsnW, fsnWatcher: fsnW,
paths: paths, cfg: cfg,
ignore: ignore,
eventChan: make(chan EventMsg, 100), eventChan: make(chan EventMsg, 100),
}, nil }, nil
} }
func (w *Watcher) Start() { func (w *Watcher) Start() {
for _, path := range w.paths { paths := w.cfg.Scanner.File.IncludePaths
for _, path := range paths {
if _, err := os.Stat(path); os.IsNotExist(err) { if _, err := os.Stat(path); os.IsNotExist(err) {
fmt.Printf("[monitor] 路径不存在: %s\n", path) fmt.Printf("[monitor] 路径不存在: %s\n", path)
continue continue
@ -48,7 +49,7 @@ func (w *Watcher) Start() {
w.addPath(path) w.addPath(path)
} }
logger.Log.Info("[monitor] 已启用文件监听", zap.Strings("paths", w.paths)) logger.Log.Info("[monitor] 已启用文件监听", zap.Strings("paths", paths))
go w.eventLoop() go w.eventLoop()
} }
@ -113,7 +114,7 @@ func (w *Watcher) addPath(path string) {
} }
if d.IsDir() { if d.IsDir() {
for _, ignorePath := range w.ignore { for _, ignorePath := range w.cfg.Scanner.File.ExcludePaths {
if strings.HasPrefix(subPath, ignorePath) { if strings.HasPrefix(subPath, ignorePath) {
return filepath.SkipDir return filepath.SkipDir
} }