Files
frpc-console/internal/frp/legacy.go
T

227 lines
5.6 KiB
Go

// 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
}