fix: uninstall now properly cleans files and PATH, stops on permission error, separate config/log cleanup prompts

This commit is contained in:
2026-03-24 23:02:52 +08:00
parent 850cc49daf
commit b00f95b7fb
2 changed files with 51 additions and 27 deletions

View File

@@ -112,30 +112,39 @@ func main() {
Short: "废除并移除在册的后台服务、扫除环境关联残留",
Run: func(cmd *cobra.Command, args []string) {
service.Control(svc, "stop")
handleServiceControl(svc, "uninstall")
if !handleServiceControl(svc, "uninstall") {
return
}
targetDir, targetExe := getTargetSystemPath()
currentExe, _ := os.Executable()
isSelfExe := strings.EqualFold(filepath.Clean(currentExe), filepath.Clean(targetExe))
if !strings.EqualFold(filepath.Clean(currentExe), filepath.Clean(targetExe)) {
if _, err := os.Stat(targetExe); err == nil {
logger.Debug("查明环境曾记录过全局部署逻辑,现已启动强力移除可执行源地址流: %s", targetExe)
os.Remove(targetExe)
// Clean up PATH (Windows)
if runtime.GOOS == "windows" {
os.Remove(targetDir)
envClearCmd := fmt.Sprintf(`$p=[Environment]::GetEnvironmentVariable("Path","Machine");$np=($p -split ';' | Where-Object {$_ -ne "%s" -and $_ -ne ""}) -join ';';[Environment]::SetEnvironmentVariable("Path",$np,"Machine")`, targetDir)
exec.Command("powershell", "-Command", envClearCmd).Run()
logger.Debug("环境变量系统清理Windows Global Path 中的指向挂载项业已剥除卸载完成。")
}
// Delete executable
if _, err := os.Stat(targetExe); err == nil {
if isSelfExe && runtime.GOOS == "windows" {
// Windows: cannot delete a running exe, use delayed self-deletion
delCmd := fmt.Sprintf(`ping 127.0.0.1 -n 2 > nul & del "%s" & rmdir "%s"`, targetExe, targetDir)
exec.Command("cmd", "/C", "start", "/min", "cmd", "/C", delCmd).Start()
logger.Debug("已调度延迟自删除任务: %s", targetExe)
} else {
os.Remove(targetExe)
if runtime.GOOS == "windows" {
os.Remove(targetDir)
}
logger.Debug("已移除可执行文件: %s", targetExe)
}
}
if config.ConfirmConfigCleanup() {
configDir := config.GetConfigDir()
os.RemoveAll(configDir)
fmt.Println("配置文件与日志已清除。")
}
// Prompt for config and log cleanup
config.ConfirmAndCleanup()
},
},
{
@@ -287,18 +296,19 @@ func main() {
}
}
func handleServiceControl(s service.Service, action string) {
func handleServiceControl(s service.Service, action string) bool {
err := service.Control(s, action)
if err != nil {
errStr := err.Error()
if strings.Contains(errStr, "Access is denied") || strings.Contains(errStr, "permission denied") || strings.Contains(errStr, "拒绝访问") {
logger.Fail("管控流程 [%s] 触发越级防卫!无核准身份特设记录。请开具含有全 Root 及高配权限窗格承接口径。", action)
return
return false
}
logger.Crit("后台执行流程组块阻断报错 [%s] : %v", action, err)
return
return false
}
logger.Succ("指令动作 [%s] 解析下发执行完毕,无阻断警告。", action)
return true
}
func printLastLogLines(n int) {

View File

@@ -63,25 +63,39 @@ func InteractiveSetup() (*Config, error) {
return cfg, nil
}
// ConfirmConfigCleanup prompts the user to confirm deletion of config and log files.
// Returns true if the user confirms. Non-terminal stdin defaults to no.
func ConfirmConfigCleanup() bool {
// ConfirmAndCleanup prompts the user to confirm deletion of config and log files separately.
// Non-terminal stdin skips all prompts.
func ConfirmAndCleanup() {
if !isTerminal() {
return false
return
}
scanner := bufio.NewScanner(os.Stdin)
configDir := GetConfigDir()
configPath := filepath.Join(configDir, "config.json")
if _, err := os.Stat(configPath); err != nil {
return false
logDir := GetLogDir()
if _, err := os.Stat(configPath); err == nil {
fmt.Printf("检测到配置文件: %s\n是否清除配置文件[y/N]: ", configPath)
if scanner.Scan() && strings.EqualFold(strings.TrimSpace(scanner.Text()), "y") {
os.Remove(configPath)
fmt.Println("配置文件已清除。")
}
}
fmt.Printf("检测到配置文件: %s\n是否一并清除配置文件与日志[y/N]: ", configDir)
scanner := bufio.NewScanner(os.Stdin)
if scanner.Scan() {
return strings.EqualFold(strings.TrimSpace(scanner.Text()), "y")
if _, err := os.Stat(logDir); err == nil {
fmt.Printf("检测到日志目录: %s\n是否清除日志文件[y/N]: ", logDir)
if scanner.Scan() && strings.EqualFold(strings.TrimSpace(scanner.Text()), "y") {
os.RemoveAll(logDir)
fmt.Println("日志文件已清除。")
}
}
// If configDir is now empty, remove it too
entries, err := os.ReadDir(configDir)
if err == nil && len(entries) == 0 {
os.Remove(configDir)
}
return false
}
func promptString(scanner *bufio.Scanner, label, defaultVal string, validate func(string) error) string {