github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/ntp/service.go (about) 1 package ntp 2 3 import ( 4 "context" 5 "os" 6 "time" 7 8 "github.com/inazumav/sing-box/adapter" 9 "github.com/inazumav/sing-box/common/dialer" 10 "github.com/inazumav/sing-box/common/settings" 11 C "github.com/inazumav/sing-box/constant" 12 "github.com/inazumav/sing-box/option" 13 "github.com/sagernet/sing/common" 14 E "github.com/sagernet/sing/common/exceptions" 15 "github.com/sagernet/sing/common/logger" 16 M "github.com/sagernet/sing/common/metadata" 17 N "github.com/sagernet/sing/common/network" 18 "github.com/sagernet/sing/common/ntp" 19 ) 20 21 var _ ntp.TimeService = (*Service)(nil) 22 23 type Service struct { 24 ctx context.Context 25 cancel common.ContextCancelCauseFunc 26 server M.Socksaddr 27 writeToSystem bool 28 dialer N.Dialer 29 logger logger.Logger 30 ticker *time.Ticker 31 clockOffset time.Duration 32 } 33 34 func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) (*Service, error) { 35 ctx, cancel := common.ContextWithCancelCause(ctx) 36 server := options.ServerOptions.Build() 37 if server.Port == 0 { 38 server.Port = 123 39 } 40 var interval time.Duration 41 if options.Interval > 0 { 42 interval = time.Duration(options.Interval) 43 } else { 44 interval = 30 * time.Minute 45 } 46 outboundDialer, err := dialer.New(router, options.DialerOptions) 47 if err != nil { 48 return nil, err 49 } 50 return &Service{ 51 ctx: ctx, 52 cancel: cancel, 53 server: server, 54 writeToSystem: options.WriteToSystem, 55 dialer: outboundDialer, 56 logger: logger, 57 ticker: time.NewTicker(interval), 58 }, nil 59 } 60 61 func (s *Service) Start() error { 62 err := s.update() 63 if err != nil { 64 return E.Cause(err, "initialize time") 65 } 66 s.logger.Info("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout)) 67 go s.loopUpdate() 68 return nil 69 } 70 71 func (s *Service) Close() error { 72 s.ticker.Stop() 73 s.cancel(os.ErrClosed) 74 return nil 75 } 76 77 func (s *Service) TimeFunc() func() time.Time { 78 return func() time.Time { 79 return time.Now().Add(s.clockOffset) 80 } 81 } 82 83 func (s *Service) loopUpdate() { 84 for { 85 select { 86 case <-s.ctx.Done(): 87 return 88 case <-s.ticker.C: 89 } 90 err := s.update() 91 if err == nil { 92 s.logger.Debug("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout)) 93 } else { 94 s.logger.Warn("update time: ", err) 95 } 96 } 97 } 98 99 func (s *Service) update() error { 100 response, err := ntp.Exchange(s.ctx, s.dialer, s.server) 101 if err != nil { 102 return err 103 } 104 s.clockOffset = response.ClockOffset 105 if s.writeToSystem { 106 writeErr := settings.SetSystemTime(s.TimeFunc()()) 107 if writeErr != nil { 108 s.logger.Warn("write time to system: ", writeErr) 109 } 110 } 111 return nil 112 }