From 3f6c423e76f0c33bcb926b0d69168734951db23f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 26 Mar 2024 20:40:59 +0800 Subject: [PATCH] Merge time service to library --- common/ntp/service.go | 62 ++++++++++++++++++++++++++------------ common/ntp/time_stub.go | 12 ++++++++ common/ntp/time_unix.go | 14 +++++++++ common/ntp/time_windows.go | 32 ++++++++++++++++++++ 4 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 common/ntp/time_stub.go create mode 100644 common/ntp/time_unix.go create mode 100644 common/ntp/time_windows.go diff --git a/common/ntp/service.go b/common/ntp/service.go index 21f9467..cbf34b3 100644 --- a/common/ntp/service.go +++ b/common/ntp/service.go @@ -10,6 +10,8 @@ import ( "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" + "github.com/sagernet/sing/service/pause" ) const TimeLayout = "2006-01-02 15:04:05 -0700" @@ -19,23 +21,26 @@ type TimeService interface { } type Options struct { - Context context.Context - Server M.Socksaddr - Interval time.Duration - Dialer N.Dialer - Logger logger.Logger + Context context.Context + Dialer N.Dialer + Logger logger.Logger + Server M.Socksaddr + Interval time.Duration + WriteToSystem bool } var _ TimeService = (*Service)(nil) type Service struct { - ctx context.Context - cancel common.ContextCancelCauseFunc - server M.Socksaddr - dialer N.Dialer - logger logger.Logger - ticker *time.Ticker - clockOffset time.Duration + ctx context.Context + cancel common.ContextCancelCauseFunc + dialer N.Dialer + logger logger.Logger + server M.Socksaddr + writeToSystem bool + ticker *time.Ticker + clockOffset time.Duration + pause pause.Manager } func NewService(options Options) *Service { @@ -47,9 +52,12 @@ func NewService(options Options) *Service { destination := options.Server if !destination.IsValid() { destination = M.Socksaddr{ - Fqdn: "time.google.com", + Fqdn: "time.apple.com", } } + if options.Logger == nil { + options.Logger = logger.NOP() + } if destination.Port == 0 { destination.Port = 123 } @@ -66,12 +74,14 @@ func NewService(options Options) *Service { dialer = N.SystemDialer } return &Service{ - ctx: ctx, - cancel: cancel, - server: destination, - dialer: dialer, - logger: options.Logger, - ticker: time.NewTicker(interval), + ctx: ctx, + cancel: cancel, + dialer: dialer, + logger: options.Logger, + writeToSystem: options.WriteToSystem, + server: destination, + ticker: time.NewTicker(interval), + pause: service.FromContext[pause.Manager](ctx), } } @@ -104,6 +114,14 @@ func (s *Service) loopUpdate() { return case <-s.ticker.C: } + if s.pause != nil { + s.pause.WaitActive() + select { + case <-s.ctx.Done(): + return + default: + } + } err := s.update() if err == nil { s.logger.Debug("updated time: ", s.TimeFunc()().Local().Format(TimeLayout)) @@ -119,5 +137,11 @@ func (s *Service) update() error { return err } s.clockOffset = response.ClockOffset + if s.writeToSystem { + writeErr := SetSystemTime(s.TimeFunc()()) + if writeErr != nil { + s.logger.Warn("write time to system: ", writeErr) + } + } return nil } diff --git a/common/ntp/time_stub.go b/common/ntp/time_stub.go new file mode 100644 index 0000000..a168625 --- /dev/null +++ b/common/ntp/time_stub.go @@ -0,0 +1,12 @@ +//go:build !(windows || linux || darwin) + +package ntp + +import ( + "os" + "time" +) + +func SetSystemTime(nowTime time.Time) error { + return os.ErrInvalid +} diff --git a/common/ntp/time_unix.go b/common/ntp/time_unix.go new file mode 100644 index 0000000..6b4226f --- /dev/null +++ b/common/ntp/time_unix.go @@ -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) +} diff --git a/common/ntp/time_windows.go b/common/ntp/time_windows.go new file mode 100644 index 0000000..c17fea1 --- /dev/null +++ b/common/ntp/time_windows.go @@ -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 +}