266 lines
5.9 KiB
Go
266 lines
5.9 KiB
Go
package frp
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"frpc-console/internal/process"
|
|
)
|
|
|
|
// ================================================================
|
|
// 兼容层:保持对外接口不变
|
|
// 这些函数供 api 调用,实际委托给 process.Manager
|
|
// ================================================================
|
|
|
|
// IsRunning 检查 frpc 是否在运行
|
|
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()
|
|
}
|
|
|
|
// Start 启动 frpc (幂等)
|
|
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()
|
|
}
|
|
|
|
// Stop 停止 frpc (幂等)
|
|
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()
|
|
}
|
|
|
|
// Restart 重启 frpc (原子操作)
|
|
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()
|
|
}
|
|
|
|
// GetStatus 获取 frpc 详细状态 (供 API 调用)
|
|
func GetStatus() (map[string]interface{}, error) {
|
|
pm := process.GetGlobalManager()
|
|
if pm != nil {
|
|
status, err := pm.Status()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return map[string]interface{}{
|
|
"state": status.Phase,
|
|
"pid": status.PID,
|
|
"port": status.Port,
|
|
}, nil
|
|
}
|
|
|
|
running := isRunningLegacy()
|
|
return map[string]interface{}{
|
|
"state": map[bool]string{true: "running", false: "stopped"}[running],
|
|
"pid": 0,
|
|
"port": 0,
|
|
"legacy": true,
|
|
}, nil
|
|
}
|
|
|
|
// Reload 热加载 frpc 配置
|
|
func Reload() error {
|
|
pm := process.GetGlobalManager()
|
|
if pm == nil {
|
|
return reloadLegacy()
|
|
}
|
|
|
|
status, err := pm.Status()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if status.Phase != "RUNNING" {
|
|
return pm.Start(context.Background())
|
|
}
|
|
|
|
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 err := pm.Restart(context.Background()); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
log.Printf("✅ frpc 热加载成功: %s", string(output))
|
|
return nil
|
|
}
|
|
|
|
// ================================================================
|
|
// 旧版实现 (降级方案)
|
|
// ================================================================
|
|
|
|
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))
|
|
}
|
|
|
|
proc, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return proc.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)))
|
|
proc, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
os.Remove("./data/frpc.pid")
|
|
return nil
|
|
}
|
|
|
|
if err := proc.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
|
|
}
|