From 9689607409f5b53fe0e3683913ed508eac13d045 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Tue, 9 May 2023 23:11:53 +0700 Subject: [PATCH] all: wait NTP synced on Merlin On some Merlin routers, the time is broken when system reboot, and need to wait for NTP synced to get the correct time. For fetching API in cd mode successfully, ctrld need to wait until NTP set the time correctly, otherwise, the certificate validation would complain. --- cmd/ctrld/cli.go | 7 ++++++ internal/router/router.go | 50 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/cmd/ctrld/cli.go b/cmd/ctrld/cli.go index 2a12c12..daf672c 100644 --- a/cmd/ctrld/cli.go +++ b/cmd/ctrld/cli.go @@ -173,6 +173,13 @@ func initCLI() { // Log config do not have thing to validate, so it's safe to init log here, // so it's able to log information in processCDFlags. initLogging() + + if setupRouter { + if err := router.PreStart(); err != nil { + mainLog.Fatal().Err(err).Msg("failed to perform router pre-start check") + } + } + processCDFlags() if err := ctrld.ValidateConfig(validator.New(), &cfg); err != nil { mainLog.Fatal().Msgf("invalid config: %v", err) diff --git a/internal/router/router.go b/internal/router/router.go index bc628ad..1e330b4 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -2,14 +2,18 @@ package router import ( "bytes" + "context" "errors" + "fmt" "os" "os/exec" "sync" "sync/atomic" + "time" "github.com/fsnotify/fsnotify" "github.com/kardianos/service" + "tailscale.com/logtail/backoff" "github.com/Control-D-Inc/ctrld" ) @@ -89,6 +93,52 @@ func ConfigureService(sc *service.Config) error { return nil } +// PreStart blocks until the router is ready for running ctrld. +func PreStart() (err error) { + if Name() != DDWrt { + return nil + } + + pidFile := "/tmp/ctrld.pid" + // On Merlin, NTP may out of sync, so waiting for it to be ready. + // + // Remove pid file and trigger dnsmasq restart, so NTP can resolve + // server name and perform time synchronization. + pid, err := os.ReadFile(pidFile) + if err != nil { + return fmt.Errorf("PreStart: os.Readfile: %w", err) + } + if err := os.Remove(pidFile); err != nil { + return fmt.Errorf("PreStart: os.Remove: %w", err) + } + defer func() { + if werr := os.WriteFile(pidFile, pid, 0600); werr != nil { + err = errors.Join(err, werr) + return + } + if rerr := merlinRestartDNSMasq(); rerr != nil { + err = errors.Join(err, rerr) + return + } + }() + if err := merlinRestartDNSMasq(); err != nil { + return fmt.Errorf("PreStart: merlinRestartDNSMasq: %w", err) + } + + // Wait until `ntp_read=1` set. + b := backoff.NewBackoff("PreStart", func(format string, args ...any) {}, 10*time.Second) + for { + out, err := nvram("get", "ntp_ready") + if err != nil { + return fmt.Errorf("PreStart: nvram: %w", err) + } + if out == "1" { + return nil + } + b.BackOff(context.Background(), errors.New("ntp not ready")) + } +} + // PostInstall performs task after installing ctrld on router. func PostInstall() error { name := Name()