wpa_supplicant is a supervised longrun, not a oneshot that forks

The wifi oneshot backgrounded a subshell and returned at once, so that the
15s wait for wlan0 and the 20s wait to associate did not stall the boot
transition firmwareExe blocks on. Its comment was explicit that nothing
could depend on it, which was true and was not the problem.

The problem is the subshell it left behind: a child of s6rc-oneshot-runner
with its stdout on a pipe s6-rc closes the moment the up script returns, so
a sleep in the wlan0 wait followed by an echo is a write to a closed pipe.

OBSERVED on a printer: a healthy supervision tree, s6-rc reporting wifi up,
no wlan0 configured, no lease, and not one line anywhere saying why --
HelixScreen calling it "wifi system is not ready" because there was no
supplicant holding a control socket. Getting in at all needed a repacked
.tgz that brought wifi up ahead of s6. The subshell was never caught in the
act and could not be: once its pipe is gone it cannot report its own death.
Which is the argument, whatever the exact signal was. A oneshot that forks
and returns cannot be relied on and cannot be debugged when it isn't.

So: type longrun, no -B, exec wpa_supplicant in the foreground. Blocking IS
the job now, s6-supervise holds the process, and a supplicant that dies is
restarted rather than mourned. up/down are gone -- a longrun's down is
silently dropped anyway.

wifi-dhcp is new and is not optional. wpa_supplicant only associates; it
never asks for an address, and running udhcpc was the oneshot's last act.
bin/wifi-action.sh has shipped since the s6 migration for exactly this hook
and was invoked by nothing at all -- `wpa_cli -a` is what invokes it. That
also fixes what the oneshot never covered: it ran udhcpc ONCE after boot, so
an owner joining a network from the HelixScreen panel an hour later was
associated with no address, which the panel reports as a connection timeout.

Its run script re-runs the action script when wpa_state is already COMPLETED.
wpa_cli fires on CHANGES, and it is not established that it emits CONNECTED
on attach to an already-connected supplicant, so without this the lease that
finish drops would not come back on a restart. The action script replaces any
previous client first, so it is idempotent with a real event.

udhcpc keeps -b inside the action script. Different flag, different program:
wpa_cli blocks until the script returns, so a foreground udhcpc would wedge
event processing.

ntp/run reasoned about "the wifi oneshot" and about nothing being allowed to
depend on it; it still takes no wifi dependency, now because wifi being up
means a supplicant is running rather than that anything is routable. The
service tables in how-it-works.md and building.md, and test_s6rc.py -- where
wifi sat in HW_SET as "the oneshot above" and test_execline_shipped_too used
it as its example -- follow. Both wifi and wifi-dhcp stay out of BOOT_SET:
they want a real wlan0, which the replica has not got.

qa-static passes. The database is NOT compiled here -- s6-rc-compile is a
MIPS binary that runs in the replica -- so make qa-replica is still owed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPQqLMitZzrRcaehiYLHdx
This commit is contained in:
Oleksandr ShyshatskyiandClaude Opus 5 committed 2026-08-30 22:17:00 +03:00
1 parent d3dd609e84
commit 9ef21c1fb8
13 files changed
+201 -93

No files matched your search

+3 -1
View File
@@ -54,7 +54,9 @@ compiled into a database at build time and supervised by `s6-svscan`:
```
mcu-bringup oneshot toolhead boards out of their bootloaders
klipper longrun klippy (depends on mcu-bringup)
wifi oneshot wlan0 + wpa_supplicant + udhcpc
wifi longrun wpa_supplicant on wlan0
wifi-dhcp longrun udhcpc, on wpa_supplicant's association
events (depends on wifi)
nginx longrun nginx (Mainsail) on :80
moonraker longrun moonraker on :7125
camera longrun mjpg-streamer on :8080 (nginx proxies it at /webcam/)