1
0
mirror of https://github.com/MetaCubeX/mihomo.git synced 2025-09-20 04:25:59 +08:00
Files
mihomo/ntp/service.go

124 lines
2.8 KiB
Go
Raw Normal View History

2023-09-01 03:11:35 +08:00
package ntp
import (
"context"
"sync"
2025-09-13 10:30:14 +08:00
"sync/atomic"
"time"
2023-11-03 21:01:45 +08:00
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/log"
2025-05-03 15:06:13 +08:00
M "github.com/metacubex/sing/common/metadata"
"github.com/metacubex/sing/common/ntp"
2023-09-01 03:11:35 +08:00
)
2025-09-13 10:30:14 +08:00
var globalSrv atomic.Pointer[Service]
var globalMu sync.Mutex
2023-09-01 03:11:35 +08:00
type Service struct {
server M.Socksaddr
dialer proxydialer.SingDialer
ticker *time.Ticker
ctx context.Context
cancel context.CancelFunc
2025-09-13 10:30:14 +08:00
offset atomic.Int64 // [time.Duration]
syncSystemTime bool
2023-09-01 03:11:35 +08:00
}
func ReCreateNTPService(server string, interval time.Duration, dialerProxy string, syncSystemTime bool) {
2025-09-13 10:30:14 +08:00
globalMu.Lock()
defer globalMu.Unlock()
2025-09-13 11:14:48 +08:00
if service := globalSrv.Swap(nil); service != nil {
2023-09-01 03:11:35 +08:00
service.Stop()
}
2025-09-13 11:14:48 +08:00
if server == "" || interval <= 0 {
2025-09-13 10:30:14 +08:00
return
}
2023-09-01 03:11:35 +08:00
ctx, cancel := context.WithCancel(context.Background())
2025-09-13 11:14:48 +08:00
service := &Service{
server: M.ParseSocksaddr(server),
dialer: proxydialer.NewByNameSingDialer(dialerProxy, dialer.NewDialer()),
ticker: time.NewTicker(interval * time.Minute),
ctx: ctx,
cancel: cancel,
syncSystemTime: syncSystemTime,
}
2023-09-01 03:11:35 +08:00
service.Start()
2025-09-13 10:30:14 +08:00
globalSrv.Store(service)
2023-09-01 03:11:35 +08:00
}
func (srv *Service) Start() {
log.Infoln("NTP service start, sync system time is %t", srv.syncSystemTime)
go srv.loopUpdate()
2023-09-01 03:11:35 +08:00
}
func (srv *Service) Stop() {
2025-09-13 11:14:48 +08:00
log.Infoln("NTP service stop")
2025-09-13 10:30:14 +08:00
srv.cancel()
2023-09-02 12:37:43 +08:00
}
2025-09-13 10:30:14 +08:00
func (srv *Service) Offset() time.Duration {
2025-09-13 11:14:48 +08:00
return time.Duration(srv.offset.Load())
2023-09-01 03:11:35 +08:00
}
func (srv *Service) update() error {
2023-09-04 18:19:22 +08:00
var response *ntp.Response
var err error
for i := 0; i < 3; i++ {
2025-09-13 10:30:14 +08:00
response, err = ntp.Exchange(srv.ctx, srv.dialer, srv.server)
if err != nil {
2025-09-13 11:14:48 +08:00
if srv.ctx.Err() != nil {
return nil
}
2025-09-13 10:30:14 +08:00
continue
}
2025-09-13 10:30:14 +08:00
offset := response.ClockOffset
if offset > time.Duration(0) {
log.Infoln("System clock is ahead of NTP time by %s", offset)
} else if offset < time.Duration(0) {
log.Infoln("System clock is behind NTP time by %s", -offset)
2023-09-04 18:19:22 +08:00
}
2025-09-13 10:30:14 +08:00
srv.offset.Store(int64(offset))
if srv.syncSystemTime {
timeNow := response.Time
syncErr := setSystemTime(timeNow)
if syncErr == nil {
log.Infoln("Sync system time success: %s", timeNow.Local().Format(ntp.TimeLayout))
} else {
log.Errorln("Write time to system: %s", syncErr)
srv.syncSystemTime = false
}
}
2025-09-13 10:30:14 +08:00
return nil
}
2025-09-13 10:30:14 +08:00
return err
}
func (srv *Service) loopUpdate() {
2025-09-13 11:14:48 +08:00
defer srv.offset.Store(0)
2025-09-13 10:30:14 +08:00
defer srv.ticker.Stop()
for {
2025-09-13 10:30:14 +08:00
err := srv.update()
if err != nil {
log.Warnln("Sync time failed: %s", err)
}
select {
case <-srv.ctx.Done():
return
case <-srv.ticker.C:
}
2023-09-01 03:11:35 +08:00
}
}
func Now() time.Time {
now := time.Now()
2025-09-13 11:14:48 +08:00
if service := globalSrv.Load(); service != nil {
if offset := service.Offset(); offset.Abs() > 0 {
now = now.Add(offset)
}
2023-09-01 03:11:35 +08:00
}
return now
}