Skip to content

rc.d/firstboot: skip without an interactive console, time out prompts - #645

Open
laffer1 wants to merge 2 commits into
masterfrom
firstboot-headless
Open

laffer1 wants to merge 2 commits into
masterfrom
firstboot-headless

Conversation

@laffer1

@laffer1 laffer1 commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

Problem

rc.d/firstboot asks its questions with a plain read. On a headless board, or one with no keyboard, boot stops at the package-mirror prompt and never reaches sshd or login. The aarch64 RPI snapshot image did exactly this in QEMU.

Change

  • Skip the questions when stdin is not a terminal ([ -t 0 ]).
  • Skip at boot when every active console in kern.console is serial. A video console (ttyv*) keeps the prompts. This check applies only when rc_quiet is set (faststart/quietstart), so running service firstboot start by hand over ssh or serial still asks the questions.
  • Timeout: each answer is read with read -t ${firstboot_timeout}, default 300 s, which can be set in rc.conf. On a timeout or end of input, firstboot prints a warning and stops without writing /etc/fbreceipt, so it runs again on the next boot.
  • Order: firstboot now requires sshd, so sshd is already running while the questions wait at the console. Someone can ssh in (board images enable sshd and DHCP) and run service firstboot start. firstboot still finishes before init starts the console getty. When sshd isn't enabled, it does nothing. rcorder order: LOGIN … sshd … cron sendmail jail firstboot bgfsck securelevel.

Testing

  • sh -n passes.
  • Helper harness with a stubbed kern.console:
    • no tty → skip;
    • manual run on serial → prompts;
    • boot with uart or ttyu0 only → skip;
    • boot with vt+uart or vt only → prompts;
    • answer given → read OK;
    • 2 s timeout → "No answer within 2 seconds", rc 1;
    • EOF → "No more input", rc 1.
  • QEMU raspi3b with the aarch64 RPI snapshot image and this script: the loader runs a dual console ("Serial Primary, Video Secondary", because the Pi has HDMI), so the prompts still appear. The 300 s timeout then fired, and boot went on to host key generation, sshd and login: on ttyu0. Before this change the same image waited forever.
  • With the second commit, the same boot logs host key generation, Starting sshd. and Starting cron. before the FirstBoot prompt. QEMU raspi3b has no network device here, so I didn't test an actual ssh login.

🤖 Generated with Claude Code

Summary by Sourcery

Make firstboot safe for unattended boots while preserving interactive configuration when a usable console or remote session is available.

New Features:

  • Add configurable timeouts to firstboot prompts so unanswered or closed input no longer blocks boot indefinitely.

Bug Fixes:

  • Prevent firstboot from blocking headless or serial-only boots by skipping noninteractive prompts when appropriate.
  • Ensure timed-out or incomplete firstboot runs leave no receipt so they can be retried later.
  • Start firstboot after sshd, allowing administrators to connect remotely and run it interactively when needed.

firstboot asked its questions with a plain read, so a board booted
without a keyboard or with only a serial console sat at the package
mirror prompt forever and never reached sshd or login.

Skip the questions when stdin is not a terminal.  At boot (rc_quiet
set by faststart/quietstart) also skip when every active console in
kern.console is a serial one; a video console (ttyv*) keeps the
prompts.  Running "service firstboot start" by hand is unaffected,
so the questions can still be answered later over ssh or serial.

Each answer is now read with a timeout, firstboot_timeout (default
300 seconds).  If it expires, or input ends, firstboot stops without
writing /etc/fbreceipt, so it runs again on the next boot.

AI-Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Lucas Holt <[email protected]>
@sourcery-ai

sourcery-ai Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Implementation

Review firstboot’s boot-time console checks, prompt timeouts and receipt handling together with its sshd ordering requirement, since these changes determine whether setup blocks boot and when it can be answered.

  • libexec/rc/rc.d/firstboot L83-126, L140-145, L266-270 (+11 small edits)

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

firstboot ran between DAEMON and LOGIN, before sshd, so while its
questions waited at the console (up to firstboot_timeout per answer)
nobody could log in over the network.  Board images enable sshd and
DHCP, so a headless board was unreachable for that time.

Require sshd so that it is already running when firstboot asks its
questions; the questions can then also be answered by logging in and
running "service firstboot start".  firstboot still finishes before
init starts the console getty.  sshd does nothing when not enabled.

AI-Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Lucas Holt <[email protected]>
@github-actions

github-actions Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Claude finished @laffer1's task in 30s —— View job


Review of PR #645 (rc.d/firstboot)

I read the diff and the surrounding rc script. I didn't run it or any tests. The change looks correct, and I found no blocking issues. It only touches a /bin/sh rc script: no C, assembly or contrib/mksh.

What I checked

  • [ -t 0 ] gate and rc_quiet console check. The logic is sound. ${active%%/*} takes the active half of kern.console. case ",${active}" in *,ttyv* matches a video console in any position. If the sysctl fails or returns nothing, it falls through to prompting, which is a safe default.
  • fb_read. read -t is supported by FreeBSD sh. A timeout returns a status above 128 and EOF returns 1, which matches your branches. read without -r is unchanged from before.
  • Receipt handling. Every fb_read ... || return 0 happens before /etc/fbreceipt is written, so a timeout or EOF reruns firstboot on the next boot, as intended.
  • POSIX and style. The script uses no Bash-isms apart from the existing echo -e. : ${firstboot_timeout:=300} after load_rc_config lets rc.conf override it.
  • Ordering. Nothing in rc.d requires firstboot except dhcp6c, which says BEFORE: firstboot and doesn't conflict with the new REQUIRE: sshd. I didn't check whether LOGIN, which sshd requires, depends on firstboot. A quick rcorder run would settle that.

Minor, non-blocking

  1. Partial runs repeat. A timeout partway through leaves earlier steps applied, for example the mirror region and the aged setting. They run again on the next boot, which is harmless but means those prompts reappear. This may be worth a line in the docs or a comment.
  2. Skip message on every boot. On a headless system the "Skipping FirstBoot Configuration" message prints every boot until someone runs service firstboot start, because no receipt is written. That seems intended.
  3. Timeout vs. dual console. On a dual-console board like the Pi, the 300 s wait still delays boot once per boot until the questions are answered. You could lower the default or document firstboot_timeout in rc.conf(5) or a man page.
  4. Hardcoded /sbin/sysrc. The script calls /sbin/sysctl and /usr/sbin/sysrc by absolute path, consistent with the existing style. No change needed.

LGTM from a correctness standpoint.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant