d0b76dc6cf100241bbd23e512a0b0fbffda58e17
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0454a259fa |
Migrate Remote Access addon; fix status-function crash gap in run_menu; bump to v2.8.0
Third and biggest Addon migrated: menus/addon_remote_access.sh - VNC (x11vnc), WireGuard, Tailscale, and Netbird, each with its own install/ connect/status/uninstall flow. Same risk class as CUPS (real apt packages, real system state) but broader in scope: Tailscale and Netbird install via the vendors' own documented `curl -fsSL <url> | sh` method, preserved exactly as-is rather than redesigned. - lib/config.sh: new $WIREGUARD_DIR, same pattern as $SYSTEMD_DIR/ $BIN_DIR/etc - nothing in this file hardcodes /etc/wireguard. - lib/menu.sh: promoted power_schedule.sh's enable_and_start_timers() to a shared enable_and_start_units() (works for services now too, not just timers) - Remote Access needed the identical enable+start-with- graceful-failure-reporting pattern for x11vnc and wg-quick@, so this is fixed once and reused rather than duplicated a second time. power_schedule.sh's four call sites renamed to match. Found and fixed a real framework-level bug while building this file: run_menu()'s *handler* call has been `|| true`-guarded since v2.1.0, but the *status function* call (`"$status_func"` on its own line) was still completely bare. A status function's entire job is read-only display, but if it contains so much as a pipeline whose grep matches nothing - which pipefail turns into a pipeline failure even though the actual last command in it (e.g. sed) succeeds - that bare call would crash the *entire session*, not just fail to show status text. Found while writing wireguard_status()'s `sudo wg show | grep ... | sed ...` and deliberately verifying its exact failure mode rather than assuming run_menu already covered it. Fixed once in run_menu() itself (lib/menu.sh), protecting every status function across every menu - present and future - the same "fix once at the framework level" pattern as the v2.1.0 handler fix. Given the framework fix meant this class of bug had been silently possible since v2.1.0, audited every existing status function across every already-migrated menu for the same specific shape (a bare `var=$(...)` assignment from a grep-based pipeline, not embedded in an echo and not already guarded - embedded substitutions and if-condition contexts are both already safe on their own). Found and fixed one real instance in power_schedule_status(). menus/addon_remote_access.sh's own two equivalent pipelines (wireguard_status, netbird_status) were written with `|| true` from the start once the pattern was identified. Verified: - New scratch/stub test for addon_remote_access.sh, with curl stubbed separately from sudo (Tailscale/Netbird's install scripts must never reach the real network regardless of what sudo intercepts) and a belt-and-suspenders `sh` stub in case anything got past curl: full status/menu-builder coverage for all four sub-areas in their real, unstubbed "not installed" state (none of the four tools exist in this sandbox); VNC install/change-password/uninstall with systemd unit content verified (correct $KIOSK_USER/$KIOSK_HOME substitution); WireGuard install, paste-config (content written correctly to scratch $WIREGUARD_DIR), and uninstall - including documenting a genuine cat-until-EOF test-harness limitation (a redirected pipe's EOF is permanent for the whole stream, unlike a real terminal's per-read Ctrl+D, so only the config's *default* name is testable through simple stdin redirection - inherent to the design, matches the legacy script's identical `cat`-based approach, not a bug); Tailscale and Netbird install/connect-interactive/connect-with-key/uninstall; and all four cancel paths confirmed to make zero sudo calls. - Full regression: re-ran all 11 prior scratch/stub suites after the lib/menu.sh and power_schedule.sh changes - all still clean. - End-to-end: ran the real install.sh as a genuine non-root, non- "kiosk" user through Addons -> Remote Access -> all four sub-menus in turn, each showing accurate real (unstubbed) "not installed" status, selecting Install, declining the confirmation, and returning cleanly - zero invalid-choice errors, clean exit code 0. |
||
|
|
459da53182 |
Migrate Diagnostics menu; bump to v2.4.0
Deliberately skipped Upgrade/Full Reinstall/Complete Uninstall for now: all three are large (130-250 lines), genuinely destructive (wipe/ reinstall the kiosk), and Upgrade specifically is coupled to the legacy script's own self-extraction mechanism (it greps its own running source for embedded heredocs to pull out main.js/preload.js) - there's no modular equivalent to migrate it to yet, since those files don't exist as separate assets outside the monolith. Migrated Diagnostics instead: 4 of the legacy Advanced menu's 12 items (System Status, View Logs, Audio Diagnostics, Network Test), all read-only except one optional "play a test sound?" prompt - a deliberate change of pace with no destructive-action risk to design around, after Sites/WiFi/Power. - lib/menu.sh: ported get_vpn_ips alongside get_ip_address. - menus/diagnostics.sh: straight port, using $KIOSK_USER/$KIOSK_HOME throughout instead of the legacy code's mix of the variable and a hardcoded "kiosk" literal. Bug fixed, same set -e-safety class as v2.1.0's run_menu fix and v2.3.0's netplan/systemctl fixes, but a bigger batch this time: nearly every diagnostic command here was a bare unguarded statement whose *expected, common* failure - no lightdm running, no audio hardware, no network, missing log files, ping/nslookup not even installed - would have crashed the entire session instead of reporting "not found" and continuing. A diagnostics tool has to be the most crash-proof code in the project, since it exists to run when something is already broken. Fixed at every call site: systemctl status | head, tail on lightdm's log, journalctl, ping, nslookup, and three pactl-backed variable assignments. Also noted for future menus in this migration: writing `local var;` and `var=$(cmd)` as separate statements (good practice, and how several earlier real bugs were caught) removes an accidental safety net - `local x=$(cmd)` on one line masks the substitution's exit code with `local`'s own always-success status. Splitting them is correct, but each split assignment needs an explicit `|| true` (or real fallback) where failure is expected and non-fatal, rather than relying on that masking by accident. Caught three instances of exactly this while writing this file fresh, not just porting old bugs. Verified: - Full regression: re-ran every existing scratch-config/stub test suite (sites, display, timezone/pin, lockout, power schedule + RTC, wifi) - all still clean after the lib/menu.sh change. - New test for diagnostics.sh, exercised mostly for real (no destructive-mutation risk here, so minimal stubbing needed): system status, all three log views (including the "no such file" paths for lightdm log and electron log), full 8-step audio diagnostic with test sound declined, and network test - all report gracefully instead of crashing, confirmed by re-running after each fix until every bare unguarded statement was accounted for. - End-to-end: ran the real install.sh as a genuine non-root, non- "kiosk" user, navigating Diagnostics -> System status -> View Logs -> System journal -> Audio diagnostics (declined test sound) -> Network test -> exit. Confirmed every diagnostic path completes and returns to its menu cleanly (exit code 0) even with ping/nslookup missing and no audio hardware/network present in this environment. |
||
|
|
2375bf5eab |
Migrate WiFi and Power/Display/Quiet Hours menus; bump to v2.3.0
By far the riskiest menus migrated so far. Both can affect real system
state outside config.json in ways that are hard to reverse: WiFi
rewrites live netplan config and, over SSH, can disconnect the very
session configuring it; power scheduling can shut the physical machine
down and wake it via RTC.
- lib/config.sh: new $SYSTEMD_DIR/$CRON_D_DIR/$BIN_DIR/$NETPLAN_DIR,
same `: "${VAR:=default}"` pattern as $KIOSK_DIR. Nothing under
menus/ hardcodes /etc/systemd/system, /etc/cron.d, /usr/local/bin, or
/etc/netplan directly, so every test in this change points them at
scratch space instead of ever touching this sandbox's real systemd
units, cron, or network config.
- lib/menu.sh: ported get_ip_address (also fixing its "No IP" fallback,
which never actually fired before - `hostname -I | awk` always exits
0 even on empty output).
- menus/wifi.sh: apply_wifi_config split out from wifi_menu specifically
so tests can drive the netplan-writing logic without needing real
scan hardware. Preserves the legacy netplan backup, 60s SSH watchdog,
and restore-on-failure behavior exactly.
- menus/power_schedule.sh: power schedule (+ RTC wake), display
schedule, quiet hours, and an Electron reload timer (with its own
nested run_menu, mirroring the legacy configured/not-configured
dispatch), plus remove-all. Deliberately excludes the legacy
dispatcher's "Test schedules & system" - a shared diagnostics submenu
(audio/network/keyboard tests) that isn't specific to scheduling and
belongs with a future Advanced/Diagnostics migration instead.
Bugs found and fixed along the way, none papered over:
- The legacy dispatcher refused to open "Configure power schedule" at
all without RTC hardware, even though shutdown-only mode never needed
RTC. Now always available.
- None of the six HH:MM prompts across these menus (shutdown, wake,
display off/on, quiet start/end, custom Electron reload time) were
validated before - plain `read`, no format check. All now go through
ask_time.
- set -e safety (same class as the v2.1.0 run_menu fix), three more
instances: `ls *.yaml` when no netplan file exists still fails under
pipefail even with stderr silenced (masked in practice by cloud-init
usually leaving a file behind); the restore-and-reapply `netplan
apply` after an initial failure was a bare unguarded statement; and
`systemctl enable`/`start` after writing each of the four timer pairs
was unguarded too - caught only by testing in an environment without
a live systemd, but a real enable/start failure on actual hardware
(bad unit, daemon-reload skipped, ...) would hit the exact same crash.
Added a shared enable_and_start_timers() helper used at all four call
sites; all now report a clear warning and return to the menu instead
of taking the session down.
Testing discipline for this round, given the risk:
- No automated test calls the real netplan/nmcli/iw/wpa_cli/systemctl -
confirmed no WiFi tools or `wl*` interface exist in this sandbox, so
wifi_menu's own tools-check safely short-circuits before touching
anything; apply_wifi_config's actual YAML/backup/failure-recovery
logic is tested with sudo/netplan/get_ip_address stubbed instead.
- One stubbing pitfall caught and fixed in the test itself: `nohup sudo
bash "$watchdog" ... &` execs nohup as a real external binary, which
then execs the real sudo - a bash function stub named `sudo` does NOT
intercept that, only stubbing `nohup` itself does. Verified via pgrep
that no real watchdog process or `sleep 60` was ever spawned.
- power_schedule.sh tested with SYSTEMD_DIR/CRON_D_DIR/BIN_DIR pointed
at scratch dirs and only `sudo systemctl` stubbed (tee/rm/chmod/cp
left real, since they only ever touch scratch paths): full lifecycle
for all four schedule types plus remove-all, the RTC-available branch
(including the wake-time-before-shutdown-time hour/day wraparound
arithmetic) via a stubbed rtc_wake_available, and the new
enable_and_start_timers failure path via a stub that fails `enable`
specifically.
- End-to-end: ran the real install.sh as a genuine non-root, non-
"kiosk" user for both menus. WiFi correctly short-circuits on missing
tools without crashing. Power/Display/Quiet Hours (SYSTEMD_DIR/
CRON_D_DIR/BIN_DIR redirected to scratch space) configured all four
schedule types in sequence including the nested Electron Reload menu,
survived four consecutive real "systemctl enable/start failed"
warnings (this container has no live systemd) without the session
dying, then removed everything - confirmed the scratch dirs ended up
empty and config.json was never touched (correctly out of scope for
this menu).
|
||
|
|
8672c15469 |
Migrate Password Protection & Lockout menu; add missing ask_time helper; bump to v2.2.0
Fifth menu migrated onto lib/menu.sh + lib/config.sh: menus/lockout.sh covers enable/disable, changing the password, inactivity timeout, daily lock time, and boot password. The password is SHA-256 hashed before it's ever assigned to LOCKOUT_PASSWORD (matching main.js's comparison logic) - verified by test that the stored value is the correct hash and never plaintext. Rewrote the legacy configure_password_protection's linear "ask everything, confirm save at the end" wizard as the same immediate-save pattern used by every other migrated menu: each action (change password, change timeout, toggle boot password, ...) is a complete, standalone change, consistent with Sites/Display/Timezone/Hidden PIN. LOCKOUT_ACTIVE_START/END are deliberately left untouched - per the Readme they're inert leftover fields the app ignores, so lib/config.sh just carries whatever is already in config.json through unchanged. Testing this menu surfaced a real gap before it ever shipped: lib/menu.sh never had ask_time/validate_time at all (only validate_integer/ask_integer, ask_url, etc were ported when the framework was first built) - "set a daily lock time" would have failed for every single user with "ask_time: command not found". Ported both from the legacy script. Also promoted the ON/OFF toggle-label helper (previously private to menus/display.sh as display_onoff) to a shared onoff() in lib/menu.sh, since menus/lockout.sh needed the same thing and menu files should only ever depend on lib/, never on each other. Bumped SCRIPT_VERSION to 2.2.0 with matching changelog entries in the script header and Readme, and updated "Modular Management" to list the new menu and drop Password Protection & Lockout from the "not yet migrated" list. Verified: - Full regression: re-ran the Sites, Display, Timezone/PIN scratch-config suites after every change in this round (the onoff refactor, and again after adding ask_time) - all still clean. - New scratch-config test for lockout.sh: enable (password+timeout+daily lock+boot toggle), independently recomputed the expected SHA-256 hash and confirmed it matches config.json exactly, change password, change timeout, clear daily lock, toggle boot password, disable (confirmed every field clears), and that the menu builder's options correctly differ between the enabled and disabled states. - End-to-end: ran the real install.sh as a genuine non-root, non-"kiosk" user - Lockout menu -> enable protection with a real password entered via the masked prompt -> set 20m timeout, 23:00 daily lock, boot password on -> confirmed the menu redraws with the new state -> clean exit (code 0). Checked the resulting config.json and file permissions on disk. |
||
|
|
074b2ec2e3 |
Migrate Timezone and Hidden Site PIN menus; harden menu framework against set -e; bump to v2.1.0
Two more menus migrated onto lib/menu.sh + lib/config.sh, chosen specifically because neither touches config.json - a third and fourth shape for the framework (a system command via timedatectl, and a flat PIN file), on top of Sites' list CRUD and Display's JSON toggles. - menus/timezone.sh: also replaces the legacy script's hand-numbered 18-entry case statement with a plain data list (TIMEZONE_COMMON_ZONES) plus one handler that reads the number run_menu hands it - adding or removing a zone never touches numbering anywhere else. Required a small run_menu addition: handlers now receive the chosen 1-based number as $1, so one handler can serve a whole data-driven list instead of needing a wrapper function per entry. - menus/hidden_pin.sh: set/disable/reset the PIN gating hidden pages. Testing menus/timezone.sh surfaced a real bug before it ever shipped: this whole tool runs under `set -e`, and set_timezone() rejecting an invalid zone via a bare `return 1` as its last statement took down the *entire* install.sh session, not just that one action - a single typo would silently drop the user back to their shell. Fixed at the framework level in lib/menu.sh (run_menu now absorbs a failed handler's exit code) rather than patching set_timezone alone, since any future menu could hit the same trap. Verified against the real install.sh as a genuine non-root user: an invalid timezone now logs an error and redraws the Timezone menu instead of killing the session (confirmed exit code 0 at the end of the run). Note this specific hazard was introduced by this session's own return-1 idiom, not inherited from the legacy script, which never uses a bare return 1 in these functions. Also per the user: left the old configure_sites/configure_touch_controls/ configure_navigation_security/configure_optional_features functions in ubuntu-based-kiosk.sh untouched for now (still carrying the v2.0.0 settings-clobber and reorder bugs) rather than removing them - they'll be retired in one pass once enough of Core Settings/Addons/Advanced is migrated. Bumped SCRIPT_VERSION to 2.1.0 with matching changelog entries in the script header and Readme, and updated the Readme's "Modular Management" section to state plainly what is and isn't migrated yet. Verified: - Full regression: re-ran the Sites and Display scratch-config test suites against the updated run_menu signature - both still clean. - New scratch-config tests for hidden_pin.sh (set/mismatch/reject/ disable/reset, correct file permissions) and timezone.sh (builder entry count, common-zone pick by index, manual entry with legacy US/* alias normalization, region search + cancel, invalid-zone rejection) - all correct, with timedatectl/sudo stubbed only where needed to avoid mutating this sandbox's real system clock/timezone. - End-to-end: ran the real install.sh as a genuine non-root, non-"kiosk" user, navigating Timezone -> manual entry -> invalid zone -> confirmed no crash and a normal return to the menu, then Hidden Site PIN -> set a PIN -> confirmed the file on disk (mode 600, correct content) -> clean exit (code 0). |
||
|
|
21a0768c8c |
Add modular menu framework, migrate Sites & Page Timing onto it
Start of pulling the menu system out of the 12k-line single-file installer so individual menus can change without risking the rest of the script (network, VNC, addons, etc). This is groundwork for the planned web-based management UI, which will share the same lib/config.sh read/write layer instead of duplicating it. - lib/menu.sh: generic numbered-menu framework (auto-numbered entries, "0" always exits/returns) plus the validated input helpers menus need. - lib/config.sh: single load/save for config.json. Fixes a latent bug where the old Sites menu wrote config.json without first loading swipe/navigation/lockout settings, silently resetting them to defaults on save. - menus/sites.sh: Sites & Page Timing fully migrated - add/edit/delete/ reorder pages, set duration (auto-rotate/manual/hidden) and home page. Also fixes an off-by-one in the ported reorder logic (moving an item landed one slot short of the requested position) caught by testing. - install.sh: new entry point for managing an already-installed kiosk via `git clone` + `./install.sh`, wired to the Sites menu. Does not yet replace first-time provisioning, which still uses the existing single-file installer. All new site CRUD/reorder/home-page paths were exercised against a scratch config.json (add with/without basic auth, edit duration, set home + timeout, 2- and 3-item reorders in both directions, delete) to confirm the resulting config.json matches expectations. |