github.com/kelleygo/clashcore@v1.0.2/ntp/service.go (about) 1 package ntp 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 "github.com/kelleygo/clashcore/component/dialer" 9 "github.com/kelleygo/clashcore/component/proxydialer" 10 "github.com/kelleygo/clashcore/log" 11 12 M "github.com/sagernet/sing/common/metadata" 13 "github.com/sagernet/sing/common/ntp" 14 ) 15 16 var offset time.Duration 17 var service *Service 18 19 type Service struct { 20 server M.Socksaddr 21 dialer proxydialer.SingDialer 22 ticker *time.Ticker 23 ctx context.Context 24 cancel context.CancelFunc 25 mu sync.Mutex 26 syncSystemTime bool 27 running bool 28 } 29 30 func ReCreateNTPService(server string, interval time.Duration, dialerProxy string, syncSystemTime bool) { 31 if service != nil { 32 service.Stop() 33 } 34 ctx, cancel := context.WithCancel(context.Background()) 35 service = &Service{ 36 server: M.ParseSocksaddr(server), 37 dialer: proxydialer.NewByNameSingDialer(dialerProxy, dialer.NewDialer()), 38 ticker: time.NewTicker(interval * time.Minute), 39 ctx: ctx, 40 cancel: cancel, 41 syncSystemTime: syncSystemTime, 42 } 43 service.Start() 44 } 45 46 func (srv *Service) Start() { 47 srv.mu.Lock() 48 defer srv.mu.Unlock() 49 log.Infoln("NTP service start, sync system time is %t", srv.syncSystemTime) 50 err := srv.update() 51 if err != nil { 52 log.Errorln("Initialize NTP time failed: %s", err) 53 return 54 } 55 service.running = true 56 go srv.loopUpdate() 57 } 58 59 func (srv *Service) Stop() { 60 srv.mu.Lock() 61 defer srv.mu.Unlock() 62 if service.running { 63 srv.ticker.Stop() 64 srv.cancel() 65 service.running = false 66 } 67 } 68 69 func (srv *Service) Running() bool { 70 if srv == nil { 71 return false 72 } 73 srv.mu.Lock() 74 defer srv.mu.Unlock() 75 return srv.running 76 } 77 78 func (srv *Service) update() error { 79 var response *ntp.Response 80 var err error 81 for i := 0; i < 3; i++ { 82 if response, err = ntp.Exchange(context.Background(), srv.dialer, srv.server); err == nil { 83 break 84 } 85 if i == 2 { 86 return err 87 } 88 } 89 offset = response.ClockOffset 90 if offset > time.Duration(0) { 91 log.Infoln("System clock is ahead of NTP time by %s", offset) 92 } else if offset < time.Duration(0) { 93 log.Infoln("System clock is behind NTP time by %s", -offset) 94 } 95 if srv.syncSystemTime { 96 timeNow := response.Time 97 syncErr := setSystemTime(timeNow) 98 if syncErr == nil { 99 log.Infoln("Sync system time success: %s", timeNow.Local().Format(ntp.TimeLayout)) 100 } else { 101 log.Errorln("Write time to system: %s", syncErr) 102 srv.syncSystemTime = false 103 } 104 } 105 return nil 106 } 107 108 func (srv *Service) loopUpdate() { 109 for { 110 select { 111 case <-srv.ctx.Done(): 112 return 113 case <-srv.ticker.C: 114 } 115 err := srv.update() 116 if err != nil { 117 log.Warnln("Sync time failed: %s", err) 118 } 119 } 120 } 121 122 func Now() time.Time { 123 now := time.Now() 124 if service.Running() && offset.Abs() > 0 { 125 now = now.Add(offset) 126 } 127 return now 128 }