sing-box/ntp/service.go

113 lines
2.6 KiB
Go
Raw Normal View History

2023-02-21 06:53:00 +00:00
package ntp
import (
"context"
2023-04-10 05:00:57 +00:00
"os"
2023-02-21 06:53:00 +00:00
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/dialer"
"github.com/sagernet/sing-box/common/settings"
C "github.com/sagernet/sing-box/constant"
2023-02-21 06:53:00 +00:00
"github.com/sagernet/sing-box/option"
2023-04-10 05:00:57 +00:00
"github.com/sagernet/sing/common"
2023-02-21 06:53:00 +00:00
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ntp"
)
2023-08-31 06:00:47 +00:00
var _ ntp.TimeService = (*Service)(nil)
2023-02-21 06:53:00 +00:00
type Service struct {
ctx context.Context
2023-04-10 05:00:57 +00:00
cancel common.ContextCancelCauseFunc
server M.Socksaddr
writeToSystem bool
dialer N.Dialer
logger logger.Logger
ticker *time.Ticker
clockOffset time.Duration
2023-02-21 06:53:00 +00:00
}
2023-08-08 08:14:03 +00:00
func NewService(ctx context.Context, router adapter.Router, logger logger.Logger, options option.NTPOptions) (*Service, error) {
2023-04-10 05:00:57 +00:00
ctx, cancel := common.ContextWithCancelCause(ctx)
2023-02-21 06:53:00 +00:00
server := options.ServerOptions.Build()
if server.Port == 0 {
server.Port = 123
}
var interval time.Duration
if options.Interval > 0 {
2023-02-21 12:20:17 +00:00
interval = time.Duration(options.Interval)
2023-02-21 06:53:00 +00:00
} else {
interval = 30 * time.Minute
}
2023-08-08 08:14:03 +00:00
outboundDialer, err := dialer.New(router, options.DialerOptions)
if err != nil {
return nil, err
}
2023-02-21 06:53:00 +00:00
return &Service{
ctx: ctx,
cancel: cancel,
server: server,
writeToSystem: options.WriteToSystem,
2023-08-08 08:14:03 +00:00
dialer: outboundDialer,
logger: logger,
ticker: time.NewTicker(interval),
2023-08-08 08:14:03 +00:00
}, nil
2023-02-21 06:53:00 +00:00
}
func (s *Service) Start() error {
err := s.update()
if err != nil {
return E.Cause(err, "initialize time")
}
s.logger.Info("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout))
2023-02-21 06:53:00 +00:00
go s.loopUpdate()
return nil
}
func (s *Service) Close() error {
s.ticker.Stop()
2023-04-10 05:00:57 +00:00
s.cancel(os.ErrClosed)
2023-02-21 06:53:00 +00:00
return nil
}
func (s *Service) TimeFunc() func() time.Time {
return func() time.Time {
return time.Now().Add(s.clockOffset)
}
}
func (s *Service) loopUpdate() {
for {
select {
case <-s.ctx.Done():
return
case <-s.ticker.C:
}
err := s.update()
if err == nil {
s.logger.Debug("updated time: ", s.TimeFunc()().Local().Format(C.TimeLayout))
2023-02-21 06:53:00 +00:00
} else {
s.logger.Warn("update time: ", err)
}
}
}
func (s *Service) update() error {
response, err := ntp.Exchange(s.ctx, s.dialer, s.server)
if err != nil {
return err
}
s.clockOffset = response.ClockOffset
if s.writeToSystem {
writeErr := settings.SetSystemTime(s.TimeFunc()())
if writeErr != nil {
s.logger.Warn("write time to system: ", writeErr)
}
}
2023-02-21 06:53:00 +00:00
return nil
}