99 lines
4.4 KiB
Markdown
99 lines
4.4 KiB
Markdown
# Startup Environment Check + Self-Heal
|
|
|
|
Date: 2026-08-19
|
|
|
|
## Problem
|
|
|
|
Rebooting the Pineapple left Mark VIII in a broken state: the boot-persistent
|
|
procd service came back, but nothing reconciled pineapd/UCI/monitors, so the UI
|
|
reported state that did not match the device (pool "off" while actually
|
|
broadcasting), native recon scans failed, and scans only returned 5GHz results.
|
|
|
|
Three observed failures:
|
|
|
|
1. **native recon scan failed** — `h_recon_start` proxies
|
|
`/api/pineap/recon/new`; it returns 502 when the daemon fails (typically
|
|
pineapd down/crash-looping). Nothing verifies pineapd health before the user
|
|
starts a scan.
|
|
2. **scans only showed 5GHz** — radio0's AP interfaces (wlan0open/wlan0wpa)
|
|
pin `wlan0mon` to one 2.4GHz channel (a phy's channel is held by its AP
|
|
interface), so 2.4GHz results mostly vanish while 5GHz `wlan1mon` hopping
|
|
keeps producing results. The band UCI config itself is correct.
|
|
3. **pool "on" while UI shows "off"** — the UI's advertise state is derived
|
|
only from UCI `pineapd.@ssidpool[0].disable`. Nothing syncs pineapd's
|
|
*runtime* pool broadcast to match UCI. UCI can say disabled while pineapd is
|
|
live-broadcasting.
|
|
|
|
## Design decisions
|
|
|
|
- Keep boot auto-start; the service self-heals at every startup instead.
|
|
- The env check auto-fixes everything fixable, re-verifies, and only fails hard
|
|
on core deps (daemon unreachable after fix, pineapd down after restart, recon
|
|
DB unreadable).
|
|
- 5GHz-only scans are handled by verify + warn (no scan-time AP pausing).
|
|
|
|
## Implementation
|
|
|
|
### 1. `env_check()` in server.py (single source of truth)
|
|
|
|
Runs in order and returns `[{step, ok, detail, action}]` where `ok` is one of
|
|
`pass`, `fixed`, `warn`, `fail`:
|
|
|
|
1. **Daemon reachable** — `daemon_sock_call('GET', '/api/pineap/get_config')`.
|
|
Report-only.
|
|
2. **pineapd alive** — `pidof pineapd`. If down: stabilize UCI, restart
|
|
`/etc/init.d/pineapd`, re-verify. Core: if still down after restart -> fail.
|
|
3. **Sane-off UCI defaults** — reuse the `_stabilize_pineapd` wanted-dict (pool
|
|
`disable=1` + clear pool list, `wlan2mon.disable=1`/`hop=0`,
|
|
`wlan1mon.bands=5`/`hop=0`, `wlan0mon.bands=2`). Write + commit when
|
|
missing. Refactor so the health monitor and env check share the wanted-dict.
|
|
4. **Runtime pool sync** — when UCI says pool disabled, call
|
|
`_pineap('SSIDPOOL', 'DISABLE')` so pineapd's runtime broadcast matches the
|
|
UI. Report the resulting runtime state as `pool_runtime`.
|
|
5. **Monitors up** — `_bring_monitors_up()` for wlan0mon/wlan1mon.
|
|
6. **Recon DB readable** — read-only scan-count query against `RECON_DB`.
|
|
Report row count. Core: unreadable -> fail.
|
|
7. **2.4GHz sampling** — report enabled radio0 APs (wlan0open/wlan0wpa).
|
|
Warn: "2.4GHz under-sampled while an OpenAP/Evil WPA AP is up on radio0".
|
|
|
|
### 2. CLI mode `server.py --env-check`
|
|
|
|
`if __name__ == '__main__'` branch: run `env_check()`, print verbose
|
|
`[PASS] step — detail` / `[FIXED] ...` / `[WARN] ...` / `[FAIL] ...` lines to
|
|
stdout, exit 0 (all pass/warn) or 1 (any core fail).
|
|
|
|
### 3. Startup self-heal
|
|
|
|
`serve()` runs `env_check()` before binding, stores the report in module state,
|
|
and logs to `/tmp/pagerwebui.log` (covers boot + procd respawn).
|
|
|
|
### 4. API + UI
|
|
|
|
- `GET /api/health` gains `env` (last env-check report + timestamp +
|
|
pass/fixed/warn/fail counts) and `pool_runtime` (actual runtime broadcast
|
|
state after sync).
|
|
- `GET /api/recon/status` gains `wlan0_pinned` (true when a radio0 AP is
|
|
enabled); the recon scan bar shows a 2.4GHz under-sampling warning pill.
|
|
- `h_recon_start` failure includes `detail` in the user-facing error.
|
|
|
|
### 5. payload.sh
|
|
|
|
Before starting the service (both foreground and background paths), run
|
|
`python3 "$SCRIPT_DIR/server.py" --env-check`, show output verbosely on the
|
|
payload screen, and abort with a red message on exit 1. Bump header version to
|
|
match the current release.
|
|
|
|
### 6. Tests
|
|
|
|
- `tests/test_env_check.py` — fake `device_run` UCI harness (pattern from
|
|
`test_health.py`): defaults applied when missing / idempotent when set, pool
|
|
list cleared, runtime SSIDPOOL disable invoked when UCI says disabled,
|
|
monitors brought up, pineapd restarted when down, DB unreadable -> fail,
|
|
CLI `--env-check` prints and exits correctly.
|
|
- `test_health.py` updated for the shared stabilization refactor.
|
|
|
|
## Out of scope
|
|
|
|
- No scan-time AP pausing / radio0 AP teardown.
|
|
- No changes to radio1 / evil-twin / enterprise config handling.
|