From 3a3bd783b5fca9886182cfed525ddca1274d15a2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 16:46:47 +0000 Subject: [PATCH] Don't let the boot-time ntpdate stall a VAX guest ntpdate's rc.d script runs ntpdate(8) inline, so everything after it in the boot sequence -- sshd included -- waits for it to return. In CI a VAX guest sat at "Setting date via ntp." for a full hour, never reached sshd, and failed the image test; the same commit passed on the next run, so it flips a coin. The QEMU ports don't have this problem because provision.sh switches ntpdate off outright: QEMU seeds the emulated RTC from the host clock, so the offset it would correct is already ~0. That reasoning doesn't carry over to SIMH, whose KA655 has no battery-backed clock to seed -- the kernel rejects what it reads ("preposterous TOD clock time") and falls back to the file system time. A VAX guest that never runs ntpdate boots years in the past, and every HTTPS fetch then fails on certificate validity instead. So keep it and bound it. Neither of the two things in its path that can block indefinitely is bounded by ntpdate's own retries: - Resolving the pool names in the stock ntp.conf, which is what the rc.d script falls back to when ntpdate_hosts is empty. An unanswered DNS lookup has no deadline here at all, which is what wedged the guest. ntpdate_hosts takes precedence over ntp.conf, so numeric addresses keep DNS off the boot path entirely. - A server that accepts the query and never answers, which -t bounds. The addresses are Cloudflare's time service, whose anycast addresses are documented as stable in a way a pool member's are not. ntpd still runs afterwards and corrects drift over the life of the VM, so they only have to be good enough for the initial step. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01M9LHkNMNNfDf2Fa1ieGT8g --- changelog.md | 7 +++++++ resources/post_install_vax.sh | 36 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/changelog.md b/changelog.md index 855ef24..3609ba2 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- Stop the boot-time `ntpdate` being able to stall a NetBSD/VAX guest + indefinitely. It runs inline in the boot sequence, ahead of `sshd`, and the + DNS lookup for the pool names in the stock `ntp.conf` has no deadline, so a + lookup that went unanswered left the guest sitting at `Setting date via + ntp.` It is now given numeric addresses, keeping DNS off the boot path, and + a per-query timeout ## [0.7.0] - 2026-08-09 ### Added diff --git a/resources/post_install_vax.sh b/resources/post_install_vax.sh index 5d63bdc..baf4850 100755 --- a/resources/post_install_vax.sh +++ b/resources/post_install_vax.sh @@ -19,6 +19,7 @@ # (no msdosfs on vax), so a key is not an option. # - Enable sshd at boot # - Set boot loader timeout to 0 +# - Bound the boot-time ntpdate, which the VAX needs and cannot let hang # - Set the hostname # - Minimize the disk image (zero unused blocks so zstd compresses it well) # @@ -119,6 +120,40 @@ configure_boot_flags() { fi } +# ntpdate's rc.d script runs ntpdate(8) inline, so everything after it in the +# boot sequence -- sshd included -- waits for it to return. +# +# The QEMU ports simply switch it off (configure_time_sync in +# resources/provision.sh): QEMU seeds the emulated RTC from the host clock, so +# the offset it would correct is already ~0. That reasoning does not carry over +# here. SIMH's KA655 has no battery-backed clock to seed, the kernel rejects +# what it reads ("preposterous TOD clock time") and falls back to the file +# system time, so a VAX guest that never runs ntpdate boots years in the past +# and every HTTPS fetch fails on certificate validity instead. +# +# So keep it, but stop it being able to hang the boot. Two things in its path +# can block indefinitely, and neither is bounded by ntpdate's own retries: +# +# 1. Resolving the pool names in the stock /etc/ntp.conf, which is what the +# rc.d script falls back to when ntpdate_hosts is empty. A DNS lookup that +# goes unanswered has no deadline here at all -- this is what wedged the +# guest at "Setting date via ntp." for an hour in CI. ntpdate_hosts takes +# precedence over ntp.conf, so numeric addresses keep DNS off the boot +# path entirely. +# 2. A server that accepts the query and never answers. -t bounds each one, +# giving a worst case of a few seconds per address rather than forever. +# +# Cloudflare's time service is used because its addresses are anycast and +# documented as stable, which a pool member's address is not. ntpd still runs +# afterwards and corrects drift over the life of the VM, so these addresses +# only have to be good enough for the initial step. +configure_time_sync() { + cat <<'EOF' >> /mnt/etc/rc.conf +ntpdate_hosts="162.159.200.1 162.159.200.123" +ntpdate_flags="-t 5" +EOF +} + set_hostname() { echo "hostname=${HOSTNAME}" >> /mnt/etc/rc.conf } @@ -179,6 +214,7 @@ configure_ssh setup_passwordless_login enable_sshd_at_boot configure_boot_flags +configure_time_sync set_hostname install_packages setup_sudo