From a189daaa66173e3d41608513963be6df1e909232 Mon Sep 17 00:00:00 2001 From: yaotthaha Date: Tue, 10 Oct 2023 10:40:10 +0800 Subject: [PATCH] Add reload support (cherry picked from commit https://github.com/rnetx/sing-box/commit/e98000c371095c1d5f7fcec4a02ffaaf22a68e47) --- adapter/router.go | 2 ++ box.go | 8 ++++++++ cmd/sing-box/cmd_run.go | 17 ++++++++++++++--- experimental/clashapi/configs.go | 3 ++- experimental/clashapi/reload.go | 19 +++++++++++++++++++ experimental/clashapi/reload_stub.go | 19 +++++++++++++++++++ route/router.go | 12 ++++++++++++ 7 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 experimental/clashapi/reload.go create mode 100644 experimental/clashapi/reload_stub.go diff --git a/adapter/router.go b/adapter/router.go index 1449a1c3..fb72fd33 100644 --- a/adapter/router.go +++ b/adapter/router.go @@ -71,6 +71,8 @@ type Router interface { SetV2RayServer(server V2RayServer) ResetNetwork() error + + Reload() } func ContextWithRouter(ctx context.Context, router Router) context.Context { diff --git a/box.go b/box.go index 894f412b..93b06e9c 100644 --- a/box.go +++ b/box.go @@ -40,6 +40,7 @@ type Box struct { preServices1 map[string]adapter.Service preServices2 map[string]adapter.Service postServices map[string]adapter.Service + reloadChan chan struct{} done chan struct{} } @@ -52,6 +53,7 @@ type Options struct { func New(options Options) (*Box, error) { createdAt := time.Now() + reloadChan := make(chan struct{}, 1) ctx := options.Context if ctx == nil { ctx = context.Background() @@ -95,6 +97,7 @@ func New(options Options) (*Box, error) { common.PtrValueOrDefault(options.NTP), options.Inbounds, options.PlatformInterface, + reloadChan, ) if err != nil { return nil, E.Cause(err, "parse route options") @@ -218,6 +221,7 @@ func New(options Options) (*Box, error) { preServices1: preServices1, preServices2: preServices2, postServices: postServices, + reloadChan: reloadChan, done: make(chan struct{}), }, nil } @@ -462,3 +466,7 @@ func (s *Box) Close() error { func (s *Box) Router() adapter.Router { return s.router } + +func (s *Box) ReloadChan() <-chan struct{} { + return s.reloadChan +} diff --git a/cmd/sing-box/cmd_run.go b/cmd/sing-box/cmd_run.go index 6850cd19..cc4dba10 100644 --- a/cmd/sing-box/cmd_run.go +++ b/cmd/sing-box/cmd_run.go @@ -177,20 +177,31 @@ func run() error { } runtimeDebug.FreeOSMemory() for { - osSignal := <-osSignals - if osSignal == syscall.SIGHUP { + reloadTag := false + select { + case osSignal := <-osSignals: + if osSignal == syscall.SIGHUP { + err = check() + if err != nil { + log.Error(E.Cause(err, "reload service")) + continue + } + reloadTag = true + } + case <-instance.ReloadChan(): err = check() if err != nil { log.Error(E.Cause(err, "reload service")) continue } + reloadTag = true } cancel() closeCtx, closed := context.WithCancel(context.Background()) go closeMonitor(closeCtx) err = instance.Close() closed() - if osSignal != syscall.SIGHUP { + if !reloadTag { if err != nil { log.Error(E.Cause(err, "sing-box did not closed properly")) } diff --git a/experimental/clashapi/configs.go b/experimental/clashapi/configs.go index 19e4708f..cabc56b5 100644 --- a/experimental/clashapi/configs.go +++ b/experimental/clashapi/configs.go @@ -12,7 +12,8 @@ import ( func configRouter(server *Server, logFactory log.Factory) http.Handler { r := chi.NewRouter() r.Get("/", getConfigs(server, logFactory)) - r.Put("/", updateConfigs) + // r.Put("/", updateConfigs) + r.Put("/", reload(server)) r.Patch("/", patchConfigs(server)) return r } diff --git a/experimental/clashapi/reload.go b/experimental/clashapi/reload.go new file mode 100644 index 00000000..10c24b40 --- /dev/null +++ b/experimental/clashapi/reload.go @@ -0,0 +1,19 @@ +//go:build !ios + +package clashapi + +import ( + "net/http" + + "github.com/go-chi/render" +) + +func reload(server *Server) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + defer func() { + server.logger.Warn("sing-box restarting...") + server.router.Reload() + }() + render.NoContent(w, r) + } +} diff --git a/experimental/clashapi/reload_stub.go b/experimental/clashapi/reload_stub.go new file mode 100644 index 00000000..4326f8d2 --- /dev/null +++ b/experimental/clashapi/reload_stub.go @@ -0,0 +1,19 @@ +//go:build ios + +package clashapi + +import ( + "net/http" + + "github.com/go-chi/render" +) + +var ErrOSNotSupported = &HTTPError{ + Message: "OS not supported", +} + +func reload(server *Server) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + render.JSON(w, r, ErrOSNotSupported) + } +} diff --git a/route/router.go b/route/router.go index 18141137..9c71039f 100644 --- a/route/router.go +++ b/route/router.go @@ -100,6 +100,7 @@ type Router struct { needPackageManager bool wifiState adapter.WIFIState started bool + reloadChan chan<- struct{} } func NewRouter( @@ -110,6 +111,7 @@ func NewRouter( ntpOptions option.NTPOptions, inbounds []option.Inbound, platformInterface platform.Interface, + reloadChan chan<- struct{}, ) (*Router, error) { router := &Router{ ctx: ctx, @@ -138,6 +140,7 @@ func NewRouter( needPackageManager: common.Any(inbounds, func(inbound option.Inbound) bool { return len(inbound.TunOptions.IncludePackage) > 0 || len(inbound.TunOptions.ExcludePackage) > 0 }), + reloadChan: reloadChan, } router.dnsClient = dns.NewClient(dns.ClientOptions{ DisableCache: dnsOptions.DNSClientOptions.DisableCache, @@ -1438,3 +1441,12 @@ func (r *Router) notifyWindowsPowerEvent(event int) { _ = r.ResetNetwork() } } + +func (r *Router) Reload() { + if r.platformInterface == nil { + select { + case r.reloadChan <- struct{}{}: + default: + } + } +}