From 00ef587eeb8c6770a12b9f785b983d7f1de29c0f Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Thu, 12 Jun 2025 18:29:47 -0500 Subject: [PATCH] fix/timesync: Ensure that auto-update waits for time sync - Added check to not attempt auto update if time sync is needed and not yet successful (delays 30 second to recheck). - Added resync of time when DHCP or link state changes if online - Added conditional* fallback from configured* NTP servers to the IP-named NTP servers, and then to the DNS named ones if that fails - Added conditional* fallback from the configured* HTTP servers to the default DNS named ones. - Uses the configuration* option for how many queries to run in parallel - Added known static IPs for time servers (in case DNS resolution isn't up yet) - Added time.cloudflare.com to fall-back NTP servers * Note: The UI for configuring many of these things doesn't exist yet, but the defaults are reasonable --- internal/network/netif.go | 4 ++-- internal/timesync/ntp.go | 29 ++++++++++++++++++++++------- internal/timesync/timesync.go | 2 +- main.go | 9 +++++++++ network.go | 27 +++++++++++++++++++++------ 5 files changed, 55 insertions(+), 16 deletions(-) diff --git a/internal/network/netif.go b/internal/network/netif.go index 5a8dab6..d77a4fa 100644 --- a/internal/network/netif.go +++ b/internal/network/netif.go @@ -48,7 +48,7 @@ type NetworkInterfaceOptions struct { DefaultHostname string OnStateChange func(state *NetworkInterfaceState) OnInitialCheck func(state *NetworkInterfaceState) - OnDhcpLeaseChange func(lease *udhcpc.Lease) + OnDhcpLeaseChange func(lease *udhcpc.Lease, state *NetworkInterfaceState) OnConfigChange func(config *NetworkConfig) NetworkConfig *NetworkConfig } @@ -94,7 +94,7 @@ func NewNetworkInterfaceState(opts *NetworkInterfaceOptions) (*NetworkInterfaceS _ = s.updateNtpServersFromLease(lease) _ = s.setHostnameIfNotSame() - opts.OnDhcpLeaseChange(lease) + opts.OnDhcpLeaseChange(lease, s) }, }) diff --git a/internal/timesync/ntp.go b/internal/timesync/ntp.go index c32de2a..b9ffa24 100644 --- a/internal/timesync/ntp.go +++ b/internal/timesync/ntp.go @@ -9,17 +9,32 @@ import ( "github.com/beevik/ntp" ) -var defaultNTPServers = []string{ +var defaultNTPServerIPs = []string{ + // These servers are known by static IP and as such don't need DNS lookups + // These are from Google and Cloudflare since if they're down, the internet + // is broken anyway + "162.159.200.1", // time.cloudflare.com IPv4 + "162.159.200.123", // time.cloudflare.com IPv4 + "2606:4700:f1::1", // time.cloudflare.com IPv6 + "2606:4700:f1::123", // time.cloudflare.com IPv6 + "216.239.35.0", // time.google.com IPv4 + "216.239.35.4", // time.google.com IPv4 + "216.239.35.8", // time.google.com IPv4 + "216.239.35.12", // time.google.com IPv4 + "2001:4860:4806::", // time.google.com IPv6 + "2001:4860:4806:4::", // time.google.com IPv6 + "2001:4860:4806:8::", // time.google.com IPv6 + "2001:4860:4806:c::", // time.google.com IPv6 +} + +var defaultNTPServerHostnames = []string{ + // should use something from https://github.com/jauderho/public-ntp-servers "time.apple.com", "time.aws.com", "time.windows.com", "time.google.com", - "162.159.200.123", // time.cloudflare.com IPv4 - "2606:4700:f1::123", // time.cloudflare.com IPv6 - "0.pool.ntp.org", - "1.pool.ntp.org", - "2.pool.ntp.org", - "3.pool.ntp.org", + "time.cloudflare.com", + "pool.ntp.org", } func (t *TimeSync) queryNetworkTime(ntpServers []string) (now *time.Time, offset *time.Duration) { diff --git a/internal/timesync/timesync.go b/internal/timesync/timesync.go index db1c96e..0afd969 100644 --- a/internal/timesync/timesync.go +++ b/internal/timesync/timesync.go @@ -188,7 +188,7 @@ Orders: case "ntp": if syncMode.Ntp && syncMode.NtpUseFallback { t.l.Info().Msg("using NTP fallback") - now, offset = t.queryNetworkTime(defaultNTPServers) + now, offset = t.queryNetworkTime(defaultNTPServerIPs) if now != nil { t.l.Info().Str("source", "NTP fallback").Time("now", *now).Msg("time obtained") break Orders diff --git a/main.go b/main.go index c25d8b8..b4de5c9 100644 --- a/main.go +++ b/main.go @@ -96,16 +96,25 @@ func Main() { if !config.AutoUpdateEnabled { return } + + if isTimeSyncNeeded() || !timeSync.IsSyncSuccess() { + logger.Debug().Msg("system time is not synced, will retry in 30 seconds") + time.Sleep(30 * time.Second) + continue + } + if currentSession != nil { logger.Debug().Msg("skipping update since a session is active") time.Sleep(1 * time.Minute) continue } + includePreRelease := config.IncludePreRelease err = TryUpdate(context.Background(), GetDeviceID(), includePreRelease) if err != nil { logger.Warn().Err(err).Msg("failed to auto update") } + time.Sleep(1 * time.Hour) } }() diff --git a/network.go b/network.go index d4f46e7..af8e50f 100644 --- a/network.go +++ b/network.go @@ -15,7 +15,7 @@ var ( networkState *network.NetworkInterfaceState ) -func networkStateChanged() { +func networkStateChanged(isOnline bool) { // do not block the main thread go waitCtrlAndRequestDisplayUpdate(true) @@ -37,6 +37,13 @@ func networkStateChanged() { networkState.GetFQDN(), }, true) } + + // if the network is now online, trigger an NTP sync if still needed + if isOnline && timeSync != nil && (isTimeSyncNeeded() || !timeSync.IsSyncSuccess()) { + if err := timeSync.Sync(); err != nil { + logger.Warn().Str("error", err.Error()).Msg("unable to sync time on network state change") + } + } } func initNetwork() error { @@ -48,13 +55,13 @@ func initNetwork() error { NetworkConfig: config.NetworkConfig, Logger: networkLogger, OnStateChange: func(state *network.NetworkInterfaceState) { - networkStateChanged() + networkStateChanged(state.IsOnline()) }, OnInitialCheck: func(state *network.NetworkInterfaceState) { - networkStateChanged() + networkStateChanged(state.IsOnline()) }, - OnDhcpLeaseChange: func(lease *udhcpc.Lease) { - networkStateChanged() + OnDhcpLeaseChange: func(lease *udhcpc.Lease, state *network.NetworkInterfaceState) { + networkStateChanged(state.IsOnline()) if currentSession == nil { return @@ -64,7 +71,15 @@ func initNetwork() error { }, OnConfigChange: func(networkConfig *network.NetworkConfig) { config.NetworkConfig = networkConfig - networkStateChanged() + networkStateChanged(false) + + if mDNS != nil { + _ = mDNS.SetListenOptions(networkConfig.GetMDNSMode()) + _ = mDNS.SetLocalNames([]string{ + networkState.GetHostname(), + networkState.GetFQDN(), + }, true) + } }, })