// internal/frp/legacy.go // Reload 函数适配 P0 改动 package frp import ( "context" "log" "os" "os/exec" "runtime" "strconv" "strings" "syscall" "frpc-console/internal/process" ) // ================================================================ // 兼容层:保持对外接口不变 // ================================================================ func IsRunning() bool { pm := process.GetGlobalManager() if pm != nil { status, err := pm.Status() if err != nil { log.Printf("[WARN] ProcessManager.Status() 失败: %v,降级到 PID 文件", err) return isRunningLegacy() } return status.Phase == "RUNNING" } return isRunningLegacy() } func Start() error { pm := process.GetGlobalManager() if pm != nil { log.Println("[INFO] 使用 ProcessManager 启动 frpc") return pm.Start(context.Background()) } log.Println("[WARN] ProcessManager 未初始化,使用兼容模式启动 frpc") return startLegacy() } func Stop() error { pm := process.GetGlobalManager() if pm != nil { log.Println("[INFO] 使用 ProcessManager 停止 frpc") return pm.Stop(context.Background()) } log.Println("[WARN] ProcessManager 未初始化,使用兼容模式停止 frpc") return stopLegacy() } func Restart() error { pm := process.GetGlobalManager() if pm != nil { log.Println("[INFO] 使用 ProcessManager 重启 frpc") return pm.Restart(context.Background()) } log.Println("[WARN] ProcessManager 未初始化,使用兼容模式重启 frpc") if err := stopLegacy(); err != nil { return err } return startLegacy() } func GetStatus() (map[string]interface{}, error) { pm := process.GetGlobalManager() if pm != nil { state, err := pm.Status() if err != nil { return nil, err } return map[string]interface{}{ "phase": state.Phase, "pid": state.PID, "port": state.Port, "alive": state.Alive, "frp_ready": state.FRPReady, }, nil } running := isRunningLegacy() return map[string]interface{}{ "phase": map[bool]string{true: "RUNNING", false: "STOPPED"}[running], "pid": 0, "port": 0, "legacy": true, }, nil } // ================================================================ // Reload 热加载 frpc 配置(P0 核心) // ================================================================ func Reload() error { pm := process.GetGlobalManager() if pm == nil { log.Println("[WARN] ProcessManager 未初始化,使用兼容模式 reload") return reloadLegacy() } // 使用 ProcessManager 的 ReloadConfig 方法 // 该方法内部处理了 RELOADING 状态和 PID 归属验证 log.Println("[INFO] 使用 ProcessManager 执行热加载") return pm.ReloadConfig(context.Background()) } // ================================================================ // 旧版实现 (降级方案) // ================================================================ func isRunningLegacy() bool { pidData, err := os.ReadFile("./data/frpc.pid") if err != nil { return false } pid, err := strconv.Atoi(strings.TrimSpace(string(pidData))) if err != nil { return false } if runtime.GOOS == "windows" { cmd := exec.Command("tasklist", "/FI", "PID eq", strconv.Itoa(pid)) output, err := cmd.CombinedOutput() if err != nil { return false } return strings.Contains(string(output), strconv.Itoa(pid)) } process, err := os.FindProcess(pid) if err != nil { return false } return process.Signal(syscall.Signal(0)) == nil } func startLegacy() error { frpcPath, err := GetFrpcPath() if err != nil { return err } if err := os.MkdirAll("./data", 0755); err != nil { return err } if _, err := os.Stat("./data/frpc.toml"); os.IsNotExist(err) { if err := GenerateConfig(); err != nil { return err } } if isRunningLegacy() { return nil } os.Remove("./data/frpc.pid") cmd := exec.Command(frpcPath, "-c", "./data/frpc.toml") setWindowHide(cmd) setSysProcAttr(cmd) logFile, err := os.OpenFile("./data/frpc.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { return err } cmd.Stdout = logFile cmd.Stderr = logFile if err := cmd.Start(); err != nil { return err } go func() { if err := cmd.Wait(); err != nil { log.Printf("frpc 子进程退出: %v", err) } os.Remove("./data/frpc.pid") }() return os.WriteFile("./data/frpc.pid", []byte(strconv.Itoa(cmd.Process.Pid)), 0644) } func stopLegacy() error { if runtime.GOOS == "windows" { cmd := exec.Command("taskkill", "/F", "/IM", "frpc.exe") if err := cmd.Run(); err != nil && !strings.Contains(err.Error(), "not found") { return err } os.Remove("./data/frpc.pid") return nil } pidData, err := os.ReadFile("./data/frpc.pid") if err != nil { cmd := exec.Command("pkill", "-f", "frpc") if err := cmd.Run(); err != nil && !strings.Contains(err.Error(), "no process") { return err } return nil } pid, _ := strconv.Atoi(strings.TrimSpace(string(pidData))) process, err := os.FindProcess(pid) if err != nil { os.Remove("./data/frpc.pid") return nil } if err := process.Kill(); err != nil { return err } os.Remove("./data/frpc.pid") return nil } func reloadLegacy() error { if !isRunningLegacy() { return startLegacy() } frpcPath, err := GetFrpcPath() if err != nil { return err } cmd := exec.Command(frpcPath, "reload", "-c", "./data/frpc.toml") output, err := cmd.CombinedOutput() if err != nil { log.Printf("⚠️ 热加载失败 (%v),降级为重启 frpc", err) log.Printf(" reload 输出: %s", string(output)) if stopErr := stopLegacy(); stopErr != nil { return stopErr } return startLegacy() } log.Printf("✅ frpc 热加载成功 (兼容模式): %s", string(output)) return nil }