Compare commits

..

6 Commits

5 changed files with 236 additions and 19 deletions

View File

@ -21,9 +21,9 @@ func NewSafeCmd() *cobra.Command {
Short: "交互式安全确认,将可疑对象加入白名单",
Long: "查看当前的可疑文件和进程列表,并选择将其移入白名单。",
Run: func(cmd *cobra.Command, args []string) {
cfg, err := config.LoadConfig("./config.yaml")
if err != nil {
fmt.Printf("加载配置失败: %v\n", err)
cfg, ok := cmd.Context().Value("config").(*config.Config)
if !ok {
fmt.Println("无法获取配置")
os.Exit(1)
}

View File

@ -4,10 +4,15 @@ import (
"bufio"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"sysmonitord/internal/config"
"sysmonitord/pkg/logger"
"time"
"github.com/spf13/cobra"
"go.uber.org/zap"
)
func NewStatusCmd() *cobra.Command {
@ -16,10 +21,9 @@ func NewStatusCmd() *cobra.Command {
Short: "显示系统状态",
Long: "显示Sysmonitod的当前状态",
Run: func(cmd *cobra.Command, args []string) {
cfg, err := config.LoadConfig("./config.yaml")
if err != nil {
fmt.Printf("加载配置失败: %v\n", err)
cfg, ok := cmd.Context().Value("config").(*config.Config)
if !ok {
fmt.Println("无法获取配置")
os.Exit(1)
}
@ -36,7 +40,7 @@ func printStatus(cfg *config.Config) {
fmt.Println("================")
// Todo: 显示运行时长
runtimeInfo := "N/A"
runtimeInfo := getRuntime()
fmt.Printf("Runtime: %s\n", runtimeInfo)
fmt.Printf("Data Directory: %s\n", dataDir)
@ -88,3 +92,69 @@ func countLines(filePath string) (int, error) {
return lineCount, scanner.Err()
}
func getRuntime() string {
cmd := exec.Command("systemctl", "is-active", "sysmonitord")
output, err := cmd.Output()
if err != nil || strings.TrimSpace(string(output)) != "active" {
return "N/A"
}
cmd = exec.Command("systemctl", "show", "sysmonitord", "--property=ActiveEnterTimestamp")
output, err = cmd.Output()
if err != nil {
return "N/A"
}
parts := strings.SplitN(string(output), "=", 2)
if len(parts) != 2 {
return "N/A"
}
timestampStr := strings.TrimSpace(parts[1])
if timestampStr == "" {
return "N/A"
}
layouts := []string{
"Mon 2006-01-02 15:04:05 MST",
"Mon 2006-01-02 15:04:05",
"2006-01-02 15:04:05 MST",
"2006-01-02 15:04:05",
"Mon 2006-01-02 15:04:05 MST 2006",
}
var startTime time.Time
var parseErr error
for _, layout := range layouts {
startTime, parseErr = time.Parse(layout, timestampStr)
if parseErr == nil {
logger.Log.Debug("时间解析成功", zap.String("layout", layout))
break
}
}
if parseErr != nil {
return "N/A"
}
if time.Since(startTime) < 0 {
return "N/A"
}
runtime := time.Since(startTime)
days := int(runtime.Hours()) / 24
hours := int(runtime.Hours()) % 24
minutes := int(runtime.Minutes()) % 60
seconds := int(runtime.Seconds()) % 60
if days > 0 {
return fmt.Sprintf("%d天 %d小时 %d分钟 %d秒", days, hours, minutes, seconds)
} else if hours > 0 {
return fmt.Sprintf("%d小时 %d分钟 %d秒", hours, minutes, seconds)
} else if minutes > 0 {
return fmt.Sprintf("%d分钟 %d秒", minutes, seconds)
} else {
return fmt.Sprintf("%d秒", seconds)
}
}

View File

@ -21,8 +21,65 @@ scanner:
include_paths:
- /
exclude_paths:
# ========== 虚拟/临时文件系统==========
- /proc
- /sys
- /dev
- /tmp
- /var/tmp
- /run
- /mnt
- /media
# ========== 系统高频写入目录==========
- /var/log
- /var/cache
- /var/mail
- /var/spool
- /var/lib/docker
- /var/lib/containerd
- /var/lib/systemd
# ========== 内核模块==========
- /usr/lib/modules
- /lib/modules
- /usr/src
# ========== 应用缓存和构建目录==========
# 通用
- "**/node_modules"
- "**/.git"
- "**/.cache"
- "**/build"
- "**/dist"
- "**/unpackage"
- "**/vendor"
- "**/__pycache__"
- "**/.idea"
- "**/.vscode"
# ========== Web 应用特定==========
- "**/cache"
- "**/logs"
- "**/tmp"
- "**/temp"
- "**/uploads/tmp"
# ========== 用户缓存目录 ==========
- /root/.cache
- /root/.npm
- /root/.local
- /home/*/.cache
- /home/*/.npm
- /home/*/.local
- /home/*/.gradle
- /home/*/.m2
# ========== 其他高频变化目录 ==========
- /var/run
- /var/lock
- /opt/*/cache
- /opt/*/logs
fast_hash: true
fast_hash_size: 100MB
fast_hash_chunk: 2MB

View File

@ -56,7 +56,9 @@ func (s *Scanner) Scan() ([]FileInfo, error) {
var allFiles []FileInfo
hashCfg, _ := s.cfg.GetHashConfig()
bar := progressbar.NewOptions(len(allPaths),
var bar *progressbar.ProgressBar
if isInteractiveTerminal() {
bar = progressbar.NewOptions(len(allPaths),
progressbar.OptionSetDescription("[scan]计算文件哈希"),
progressbar.OptionSetWriter(os.Stderr),
progressbar.OptionShowCount(),
@ -66,9 +68,14 @@ func (s *Scanner) Scan() ([]FileInfo, error) {
logger.Log.Info("[scan]文件哈希计算完成")
}),
)
} else {
logger.Log.Info("[scan]开始计算文件哈希", zap.Int("total_files", len(allPaths)))
}
for _, path := range allPaths {
if bar != nil {
bar.Add(1)
}
info, err := os.Stat(path)
if err != nil {
@ -155,6 +162,17 @@ func (s *Scanner) collectPathsFunc(result *[]string) fs.WalkDirFunc {
return nil
}
info, err := d.Info()
if err == nil {
if info.Mode()&os.ModeSymlink != 0 {
realInfo, err := os.Stat(path)
if err == nil && realInfo.IsDir() {
logger.Log.Debug("[scan]跳过指向目录的符号链接", zap.String("path", path))
return nil
}
}
}
for _, exclude := range s.cfg.Scanner.File.ExcludePaths {
if strings.HasPrefix(path, exclude) {
logger.Log.Debug("[scan]跳过路径", zap.String("path", path), zap.String("reason", "匹配排除路径"))
@ -167,6 +185,15 @@ func (s *Scanner) collectPathsFunc(result *[]string) fs.WalkDirFunc {
}
}
func isInteractiveTerminal() bool {
fileInfo, err := os.Stderr.Stat()
if err != nil {
return false
}
return (fileInfo.Mode() & os.ModeCharDevice) != 0
}
func (f FileInfo) String() string {
return fmt.Sprintf("%s:%s", f.Path, f.Hash)
}

63
uninstall.sh Normal file
View File

@ -0,0 +1,63 @@
#!/bin/bash
# sysmonitord 卸载脚本
set -e
echo "正在卸载 sysmonitord..."
# 检测是否为 root 用户
if [ "$EUID" -ne 0 ]; then
echo "请使用 root 用户运行此卸载脚本。"
exit 1
fi
# 路径设置
BIN_NAME="sysmonitord"
INSTALL_DIR="/usr/local/bin"
CONFIG_DIR="/etc/sysmonitord"
DATA_DIR="/var/lib/sysmonitord"
LOG_DIR="/var/log/sysmonitord"
SERVICE_FILE="/etc/systemd/system/sysmonitord.service"
# 停止并禁用服务
if systemctl is-active --quiet sysmonitord; then
echo "正在停止 sysmonitord 服务..."
systemctl stop sysmonitord
fi
if systemctl is-enabled --quiet sysmonitord 2>/dev/null; then
echo "正在禁用 sysmonitord 服务..."
systemctl disable sysmonitord
fi
# 删除 systemd 服务文件
if [ -f "$SERVICE_FILE" ]; then
echo "正在删除 systemd 服务文件..."
rm -f "$SERVICE_FILE"
systemctl daemon-reload
fi
# 删除可执行文件
if [ -f "$INSTALL_DIR/$BIN_NAME" ]; then
echo "正在删除可执行文件..."
rm -f "$INSTALL_DIR/$BIN_NAME"
fi
# 询问是否删除数据文件
echo ""
read -p "是否删除所有数据文件(包括配置、数据和日志)?[y/N] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo "正在删除数据文件..."
rm -rf "$CONFIG_DIR"
rm -rf "$DATA_DIR"
rm -rf "$LOG_DIR"
echo "数据文件已删除。"
else
echo "保留数据文件。"
echo "配置文件目录: $CONFIG_DIR"
echo "数据目录: $DATA_DIR"
echo "日志目录: $LOG_DIR"
fi
echo ""
echo "sysmonitord 卸载完成!"