Fix local rule-set not updated

This commit is contained in:
世界 2025-02-01 19:42:21 +08:00
parent bab8dc0b82
commit 27c31eac5d
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
4 changed files with 62 additions and 49 deletions

View file

@ -39,17 +39,17 @@ type CacheFile interface {
StoreSelected(group string, selected string) error StoreSelected(group string, selected string) error
LoadGroupExpand(group string) (isExpand bool, loaded bool) LoadGroupExpand(group string) (isExpand bool, loaded bool)
StoreGroupExpand(group string, expand bool) error StoreGroupExpand(group string, expand bool) error
LoadRuleSet(tag string) *SavedRuleSet LoadRuleSet(tag string) *SavedBinary
SaveRuleSet(tag string, set *SavedRuleSet) error SaveRuleSet(tag string, set *SavedBinary) error
} }
type SavedRuleSet struct { type SavedBinary struct {
Content []byte Content []byte
LastUpdated time.Time LastUpdated time.Time
LastEtag string LastEtag string
} }
func (s *SavedRuleSet) MarshalBinary() ([]byte, error) { func (s *SavedBinary) MarshalBinary() ([]byte, error) {
var buffer bytes.Buffer var buffer bytes.Buffer
err := binary.Write(&buffer, binary.BigEndian, uint8(1)) err := binary.Write(&buffer, binary.BigEndian, uint8(1))
if err != nil { if err != nil {
@ -70,7 +70,7 @@ func (s *SavedRuleSet) MarshalBinary() ([]byte, error) {
return buffer.Bytes(), nil return buffer.Bytes(), nil
} }
func (s *SavedRuleSet) UnmarshalBinary(data []byte) error { func (s *SavedBinary) UnmarshalBinary(data []byte) error {
reader := bytes.NewReader(data) reader := bytes.NewReader(data)
var version uint8 var version uint8
err := binary.Read(reader, binary.BigEndian, &version) err := binary.Read(reader, binary.BigEndian, &version)

View file

@ -284,8 +284,8 @@ func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
}) })
} }
func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedRuleSet { func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedBinary {
var savedSet adapter.SavedRuleSet var savedSet adapter.SavedBinary
err := c.DB.View(func(t *bbolt.Tx) error { err := c.DB.View(func(t *bbolt.Tx) error {
bucket := c.bucket(t, bucketRuleSet) bucket := c.bucket(t, bucketRuleSet)
if bucket == nil { if bucket == nil {
@ -303,7 +303,7 @@ func (c *CacheFile) LoadRuleSet(tag string) *adapter.SavedRuleSet {
return &savedSet return &savedSet
} }
func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedRuleSet) error { func (c *CacheFile) SaveRuleSet(tag string, set *adapter.SavedBinary) error {
return c.DB.Batch(func(t *bbolt.Tx) error { return c.DB.Batch(func(t *bbolt.Tx) error {
bucket, err := c.createBucket(t, bucketRuleSet) bucket, err := c.createBucket(t, bucketRuleSet)
if err != nil { if err != nil {

View file

@ -5,6 +5,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"sync"
"github.com/sagernet/fswatch" "github.com/sagernet/fswatch"
"github.com/sagernet/sing-box/adapter" "github.com/sagernet/sing-box/adapter"
@ -33,6 +34,8 @@ type LocalRuleSet struct {
metadata adapter.RuleSetMetadata metadata adapter.RuleSetMetadata
fileFormat string fileFormat string
watcher *fswatch.Watcher watcher *fswatch.Watcher
callbackAccess sync.Mutex
callbacks list.List[adapter.RuleSetUpdateCallback]
refs atomic.Int32 refs atomic.Int32
} }
@ -52,13 +55,12 @@ func NewLocalRuleSet(ctx context.Context, logger logger.Logger, options option.R
return nil, err return nil, err
} }
} else { } else {
err := ruleSet.reloadFile(filemanager.BasePath(ctx, options.LocalOptions.Path)) filePath := filemanager.BasePath(ctx, options.LocalOptions.Path)
filePath, _ = filepath.Abs(options.LocalOptions.Path)
err := ruleSet.reloadFile(filePath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
if options.Type == C.RuleSetTypeLocal {
filePath, _ := filepath.Abs(options.LocalOptions.Path)
watcher, err := fswatch.NewWatcher(fswatch.Options{ watcher, err := fswatch.NewWatcher(fswatch.Options{
Path: []string{filePath}, Path: []string{filePath},
Callback: func(path string) { Callback: func(path string) {
@ -141,6 +143,12 @@ func (s *LocalRuleSet) reloadRules(headlessRules []option.HeadlessRule) error {
metadata.ContainsIPCIDRRule = hasHeadlessRule(headlessRules, isIPCIDRHeadlessRule) metadata.ContainsIPCIDRRule = hasHeadlessRule(headlessRules, isIPCIDRHeadlessRule)
s.rules = rules s.rules = rules
s.metadata = metadata s.metadata = metadata
s.callbackAccess.Lock()
callbacks := s.callbacks.Array()
s.callbackAccess.Unlock()
for _, callback := range callbacks {
callback(s)
}
return nil return nil
} }
@ -173,10 +181,15 @@ func (s *LocalRuleSet) Cleanup() {
} }
func (s *LocalRuleSet) RegisterCallback(callback adapter.RuleSetUpdateCallback) *list.Element[adapter.RuleSetUpdateCallback] { func (s *LocalRuleSet) RegisterCallback(callback adapter.RuleSetUpdateCallback) *list.Element[adapter.RuleSetUpdateCallback] {
return nil s.callbackAccess.Lock()
defer s.callbackAccess.Unlock()
return s.callbacks.PushBack(callback)
} }
func (s *LocalRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetUpdateCallback]) { func (s *LocalRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetUpdateCallback]) {
s.callbackAccess.Lock()
defer s.callbackAccess.Unlock()
s.callbacks.Remove(element)
} }
func (s *LocalRuleSet) Close() error { func (s *LocalRuleSet) Close() error {

View file

@ -35,8 +35,8 @@ var _ adapter.RuleSet = (*RemoteRuleSet)(nil)
type RemoteRuleSet struct { type RemoteRuleSet struct {
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
outboundManager adapter.OutboundManager
logger logger.ContextLogger logger logger.ContextLogger
outbound adapter.OutboundManager
options option.RuleSet options option.RuleSet
metadata adapter.RuleSetMetadata metadata adapter.RuleSetMetadata
updateInterval time.Duration updateInterval time.Duration
@ -63,7 +63,7 @@ func NewRemoteRuleSet(ctx context.Context, logger logger.ContextLogger, options
return &RemoteRuleSet{ return &RemoteRuleSet{
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
outboundManager: service.FromContext[adapter.OutboundManager](ctx), outbound: service.FromContext[adapter.OutboundManager](ctx),
logger: logger, logger: logger,
options: options, options: options,
updateInterval: updateInterval, updateInterval: updateInterval,
@ -83,13 +83,13 @@ func (s *RemoteRuleSet) StartContext(ctx context.Context, startContext *adapter.
s.cacheFile = service.FromContext[adapter.CacheFile](s.ctx) s.cacheFile = service.FromContext[adapter.CacheFile](s.ctx)
var dialer N.Dialer var dialer N.Dialer
if s.options.RemoteOptions.DownloadDetour != "" { if s.options.RemoteOptions.DownloadDetour != "" {
outbound, loaded := s.outboundManager.Outbound(s.options.RemoteOptions.DownloadDetour) outbound, loaded := s.outbound.Outbound(s.options.RemoteOptions.DownloadDetour)
if !loaded { if !loaded {
return E.New("download_detour not found: ", s.options.RemoteOptions.DownloadDetour) return E.New("download detour not found: ", s.options.RemoteOptions.DownloadDetour)
} }
dialer = outbound dialer = outbound
} else { } else {
dialer = s.outboundManager.Default() dialer = s.outbound.Default()
} }
s.dialer = dialer s.dialer = dialer
if s.cacheFile != nil { if s.cacheFile != nil {
@ -286,7 +286,7 @@ func (s *RemoteRuleSet) fetchOnce(ctx context.Context, startContext *adapter.HTT
} }
s.lastUpdated = time.Now() s.lastUpdated = time.Now()
if s.cacheFile != nil { if s.cacheFile != nil {
err = s.cacheFile.SaveRuleSet(s.options.Tag, &adapter.SavedRuleSet{ err = s.cacheFile.SaveRuleSet(s.options.Tag, &adapter.SavedBinary{
LastUpdated: s.lastUpdated, LastUpdated: s.lastUpdated,
Content: content, Content: content,
LastEtag: s.lastEtag, LastEtag: s.lastEtag,