chore: ntp service dep with sing, optional synchronize system time

This commit is contained in:
riolu.rs
2023-09-03 17:48:52 +08:00
parent d6b80acfbc
commit d6cf2a837f
8 changed files with 130 additions and 90 deletions

View File

@ -3,7 +3,9 @@ package ntp
import (
"context"
"github.com/Dreamacro/clash/log"
"github.com/beevik/ntp"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ntp"
"sync"
"time"
)
@ -12,43 +14,37 @@ var offset time.Duration
var service *Service
type Service struct {
addr string
interval time.Duration
ticker *time.Ticker
ctx context.Context
cancel context.CancelFunc
mu sync.Mutex
running bool
server M.Socksaddr
ticker *time.Ticker
ctx context.Context
cancel context.CancelFunc
mu sync.Mutex
syncSystemTime bool
running bool
}
func ReCreateNTPService(addr string, interval time.Duration) {
func ReCreateNTPService(server string, interval time.Duration, syncSystemTime bool) {
if service != nil {
service.Stop()
}
ctx, cancel := context.WithCancel(context.Background())
service = &Service{addr: addr, interval: interval, ctx: ctx, cancel: cancel}
service = &Service{
ctx: ctx,
cancel: cancel,
server: M.ParseSocksaddr(server),
ticker: time.NewTicker(interval * time.Minute),
syncSystemTime: syncSystemTime,
}
service.Start()
}
func (srv *Service) Start() {
srv.mu.Lock()
defer srv.mu.Unlock()
log.Infoln("NTP service start")
srv.ticker = time.NewTicker(srv.interval * time.Minute)
log.Infoln("NTP service start, sync system time is %t", srv.syncSystemTime)
service.running = true
go func() {
for {
err := srv.updateTime(srv.addr)
if err != nil {
log.Warnln("updateTime failed: %s", err)
}
select {
case <-srv.ticker.C:
case <-srv.ctx.Done():
return
}
}
}()
srv.update()
go srv.loopUpdate()
}
func (srv *Service) Stop() {
@ -70,20 +66,38 @@ func (srv *Service) Running() bool {
return srv.running
}
func (srv *Service) updateTime(addr string) error {
response, err := ntp.Query(addr)
if err != nil {
return err
func (srv *Service) update() {
response, err := ntp.Exchange(context.Background(), N.SystemDialer, srv.server)
if err != nil || response == nil {
log.Errorln("initialize time: %s", err)
}
localTime := time.Now()
ntpTime := response.Time
offset = localTime.Sub(ntpTime)
offset = response.ClockOffset
if offset > time.Duration(0) {
log.Warnln("System clock is ahead of NTP time by %s", offset)
log.Infoln("System clock is ahead of NTP time by %s", offset)
} else if offset < time.Duration(0) {
log.Warnln("System clock is behind NTP time by %s", -offset)
log.Infoln("System clock is behind NTP time by %s", -offset)
}
if srv.syncSystemTime {
timeNow := response.Time
err = setSystemTime(timeNow)
if err == nil {
log.Infoln("sync system time success: %s", timeNow.Local().Format(ntp.TimeLayout))
} else {
log.Errorln("write time to system: %s", err)
srv.syncSystemTime = false
}
}
}
func (srv *Service) loopUpdate() {
for {
select {
case <-srv.ctx.Done():
return
case <-srv.ticker.C:
}
srv.update()
}
return nil
}
func Now() time.Time {

12
ntp/time_stub.go Normal file
View File

@ -0,0 +1,12 @@
//go:build !(windows || linux || darwin)
package ntp
import (
"os"
"time"
)
func setSystemTime(nowTime time.Time) error {
return os.ErrInvalid
}

14
ntp/time_unix.go Normal file
View File

@ -0,0 +1,14 @@
//go:build linux || darwin
package ntp
import (
"time"
"golang.org/x/sys/unix"
)
func setSystemTime(nowTime time.Time) error {
timeVal := unix.NsecToTimeval(nowTime.UnixNano())
return unix.Settimeofday(&timeVal)
}

32
ntp/time_windows.go Normal file
View File

@ -0,0 +1,32 @@
package ntp
import (
"time"
"unsafe"
"golang.org/x/sys/windows"
)
func setSystemTime(nowTime time.Time) error {
var systemTime windows.Systemtime
systemTime.Year = uint16(nowTime.Year())
systemTime.Month = uint16(nowTime.Month())
systemTime.Day = uint16(nowTime.Day())
systemTime.Hour = uint16(nowTime.Hour())
systemTime.Minute = uint16(nowTime.Minute())
systemTime.Second = uint16(nowTime.Second())
systemTime.Milliseconds = uint16(nowTime.UnixMilli() - nowTime.Unix()*1000)
dllKernel32 := windows.NewLazySystemDLL("kernel32.dll")
proc := dllKernel32.NewProc("SetSystemTime")
_, _, err := proc.Call(
uintptr(unsafe.Pointer(&systemTime)),
)
if err != nil && err.Error() != "The operation completed successfully." {
return err
}
return nil
}