github.com/sagernet/sing-box@v1.2.7/ntp/service.go (about) 1 package ntp 2 3 import ( 4 "context" 5 "os" 6 "time" 7 8 "github.com/sagernet/sing-box/adapter" 9 "github.com/sagernet/sing-box/common/dialer" 10 "github.com/sagernet/sing-box/common/settings" 11 C "github.com/sagernet/sing-box/constant" 12 "github.com/sagernet/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 _ adapter.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 { 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 return &Service{ 47 ctx: ctx, 48 cancel: cancel, 49 server: server, 50 writeToSystem: options.WriteToSystem, 51 dialer: dialer.New(router, options.DialerOptions), 52 logger: logger, 53 ticker: time.NewTicker(interval), 54 } 55 } 56 57 func (s *Service) Start() error { 58 err := s.update() 59 if err != nil { 60 return E.Cause(err, "initialize time") 61 } 62 s.logger.Info("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout)) 63 go s.loopUpdate() 64 return nil 65 } 66 67 func (s *Service) Close() error { 68 s.ticker.Stop() 69 s.cancel(os.ErrClosed) 70 return nil 71 } 72 73 func (s *Service) TimeFunc() func() time.Time { 74 return func() time.Time { 75 return time.Now().Add(s.clockOffset) 76 } 77 } 78 79 func (s *Service) loopUpdate() { 80 for { 81 select { 82 case <-s.ctx.Done(): 83 return 84 case <-s.ticker.C: 85 } 86 err := s.update() 87 if err == nil { 88 s.logger.Debug("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout)) 89 } else { 90 s.logger.Warn("update time: ", err) 91 } 92 } 93 } 94 95 func (s *Service) update() error { 96 response, err := ntp.Exchange(s.ctx, s.dialer, s.server) 97 if err != nil { 98 return err 99 } 100 s.clockOffset = response.ClockOffset 101 if s.writeToSystem { 102 writeErr := settings.SetSystemTime(s.TimeFunc()()) 103 if writeErr != nil { 104 s.logger.Warn("write time to system: ", writeErr) 105 } 106 } 107 return nil 108 }