diff --git a/client/service.go b/client/service.go index 184a87a3..da2d6ce2 100644 --- a/client/service.go +++ b/client/service.go @@ -18,6 +18,9 @@ import ( "context" "crypto/tls" "fmt" + "github.com/fatedier/frp/pkg/config" + "github.com/fatedier/frp/pkg/config/v1/validation" + "github.com/fsnotify/fsnotify" "io" "net" "runtime" @@ -93,9 +96,61 @@ func NewService( ctx: context.Background(), exit: 0, } + if cfg.ReloadOnUpdate { + go svr.WatchClientConfig(cfgFile) + } return } +func (svr *Service) WatchClientConfig(cfgFilePath string) { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Error(err.Error()) + } + defer watcher.Close() + + done := make(chan bool) + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + if event.Op&fsnotify.Write == fsnotify.Write { + log.Info("config file changed: %s", event.Name) + cliCfg, pxyCfgs, visitorCfgs, _, err := config.LoadClientConfig(cfgFilePath) + if err != nil { + log.Warn("reload frpc proxy config error: %s", err.Error()) + return + } + if _, err := validation.ValidateAllClientConfig(cliCfg, pxyCfgs, visitorCfgs); err != nil { + log.Warn("reload frpc proxy config error: %s", err.Error()) + return + } + + if err := svr.ReloadConf(pxyCfgs, visitorCfgs); err != nil { + log.Warn("reload frpc proxy config error: %s", err.Error()) + return + } + log.Info("success reload conf") + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Error(err.Error()) + } + } + }() + + err = watcher.Add(cfgFilePath) + if err != nil { + log.Error(err.Error()) + } + <-done +} + func (svr *Service) GetController() *Control { svr.ctlMu.RLock() defer svr.ctlMu.RUnlock() diff --git a/conf/frpc_full_example.toml b/conf/frpc_full_example.toml index bdfc5643..1c07cc27 100644 --- a/conf/frpc_full_example.toml +++ b/conf/frpc_full_example.toml @@ -9,6 +9,9 @@ user = "your_name" serverAddr = "0.0.0.0" serverPort = 7000 +# If true, the configuration file will be automatically reloaded +reloadOnUpdate = true + # STUN server to help penetrate NAT hole. # natHoleStunServer = "stun.easyvoip.com:3478" diff --git a/go.mod b/go.mod index f7996399..849592e3 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect diff --git a/go.sum b/go.sum index 4cab567e..fe589ee0 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,8 @@ github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1: github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s= github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo= github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= diff --git a/pkg/config/v1/client.go b/pkg/config/v1/client.go index 9029aa73..b7c1d285 100644 --- a/pkg/config/v1/client.go +++ b/pkg/config/v1/client.go @@ -38,6 +38,8 @@ type ClientCommonConfig struct { // changed to "{user}.{proxy_name}". User string `json:"user,omitempty"` + ReloadOnUpdate bool `json:"reloadOnUpdate,omitempty"` + // ServerAddr specifies the address of the server to connect to. By // default, this value is "0.0.0.0". ServerAddr string `json:"serverAddr,omitempty"`