Files
frpmgr/services/client.go
2025-07-19 18:38:50 +08:00

126 lines
3.2 KiB
Go

package services
import (
"context"
"time"
_ "github.com/fatedier/frp/assets/frpc"
"github.com/fatedier/frp/client"
"github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/util/log"
glog "github.com/fatedier/golib/log"
"github.com/koho/frpmgr/pkg/consts"
)
type FrpClientService struct {
svr *client.Service
file string
cfg *v1.ClientCommonConfig
done chan struct{}
statusExporter client.StatusExporter
logger *glog.RotateFileWriter
}
func NewFrpClientService(cfgFile string) (*FrpClientService, error) {
cfg, pxyCfgs, visitorCfgs, _, err := config.LoadClientConfig(cfgFile, false)
if err != nil {
return nil, err
}
svr, err := client.NewService(client.ServiceOptions{
Common: cfg,
ProxyCfgs: pxyCfgs,
VisitorCfgs: visitorCfgs,
ConfigFilePath: cfgFile,
})
if err != nil {
return nil, err
}
logger := initLogger(cfg.Log.To, cfg.Log.Level, int(cfg.Log.MaxDays))
return &FrpClientService{
svr: svr,
file: cfgFile,
cfg: cfg,
done: make(chan struct{}),
statusExporter: svr.StatusExporter(),
logger: logger,
}, nil
}
// Run starts frp client service in blocking mode.
func (s *FrpClientService) Run() {
defer close(s.done)
if s.file != "" {
log.Infof("start frpc service for config file [%s]", s.file)
defer log.Infof("frpc service for config file [%s] stopped", s.file)
}
// There's no guarantee that this function will return after a close call.
// So we can't wait for the Run function to finish.
if err := s.svr.Run(context.Background()); err != nil {
log.Errorf("run service error: %v", err)
}
}
// Stop closes all frp connections.
func (s *FrpClientService) Stop(wait bool) {
// Close client service.
if wait {
s.svr.GracefulClose(500 * time.Millisecond)
} else {
s.svr.Close()
}
}
// Reload creates or updates or removes proxies of frpc.
func (s *FrpClientService) Reload() error {
_, pxyCfgs, visitorCfgs, _, err := config.LoadClientConfig(s.file, false)
if err != nil {
return err
}
return s.svr.UpdateAllConfigurer(pxyCfgs, visitorCfgs)
}
func (s *FrpClientService) Done() <-chan struct{} {
return s.done
}
func (s *FrpClientService) GetProxyStatus(name string) (status *proxy.WorkingStatus, ok bool) {
proxyName := name
if s.cfg.User != "" {
proxyName = s.cfg.User + "." + name
}
status, ok = s.statusExporter.GetProxyStatus(proxyName)
if ok {
status.Name = name
if status.Err == "" {
if status.Type == consts.ProxyTypeTCP || status.Type == consts.ProxyTypeUDP {
status.RemoteAddr = s.cfg.ServerAddr + status.RemoteAddr
}
} else {
status.RemoteAddr = ""
}
}
return
}
func initLogger(logPath string, levelStr string, maxDays int) *glog.RotateFileWriter {
var options []glog.Option
writer := glog.NewRotateFileWriter(glog.RotateFileConfig{
FileName: logPath,
Mode: glog.RotateFileModeDaily,
MaxDays: maxDays,
})
writer.Init()
options = append(options, glog.WithOutput(writer))
level, err := glog.ParseLevel(levelStr)
if err != nil {
level = glog.InfoLevel
}
options = append(options, glog.WithLevel(level))
log.Logger = log.Logger.WithOptions(options...)
return writer
}