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

93 lines
2.1 KiB
Go

package frp
import (
"embed"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"sync"
)
//go:embed bin/*
var embeddedFrpc embed.FS
var (
cachedFrpcPath string
frpcPathMutex sync.Mutex
)
// GetFrpcPath 获取 frpc 二进制路径
// 优先级: 本地缓存 > 内嵌二进制 > 系统 PATH
func GetFrpcPath() (string, error) {
frpcPathMutex.Lock()
defer frpcPathMutex.Unlock()
if cachedFrpcPath != "" {
if _, err := os.Stat(cachedFrpcPath); err == nil {
return cachedFrpcPath, nil
}
cachedFrpcPath = ""
}
var fileName string
switch {
case runtime.GOOS == "windows" && runtime.GOARCH == "amd64":
fileName = "frpc_windows_amd64.exe"
case runtime.GOOS == "linux" && runtime.GOARCH == "amd64":
fileName = "frpc_linux_amd64"
case runtime.GOOS == "linux" && runtime.GOARCH == "arm64":
fileName = "frpc_linux_arm64"
case runtime.GOOS == "linux" && runtime.GOARCH == "arm":
fileName = "frpc_linux_arm_hf"
default:
path, err := exec.LookPath("frpc")
if err == nil {
cachedFrpcPath = path
return path, nil
}
return "", fmt.Errorf("不支持的平台: %s/%s", runtime.GOOS, runtime.GOARCH)
}
// 尝试从本地 bin 目录加载
localPath := filepath.Join(".", "bin", fileName)
if _, err := os.Stat(localPath); err == nil {
cachedFrpcPath = localPath
return localPath, nil
}
// 从 embed 提取到 data 目录(Windows 和 Linux 统一)
dataDir := "./data"
if err := os.MkdirAll(dataDir, 0755); err != nil {
return "", fmt.Errorf("创建 data 目录失败: %w", err)
}
tmpPath := filepath.Join(dataDir, "frpc")
if runtime.GOOS == "windows" {
tmpPath += ".exe"
}
data, err := embeddedFrpc.ReadFile("bin/" + fileName)
if err == nil {
if err := os.WriteFile(tmpPath, data, 0755); err == nil {
cachedFrpcPath = tmpPath
return tmpPath, nil
}
// 如果写入失败,检查文件是否已存在
if _, statErr := os.Stat(tmpPath); statErr == nil {
cachedFrpcPath = tmpPath
return tmpPath, nil
}
}
// 最后尝试从系统 PATH 查找
path, err := exec.LookPath("frpc")
if err == nil {
cachedFrpcPath = path
return path, nil
}
return "", fmt.Errorf("未找到 frpc 文件")
}