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).
110 lines
3.6 KiB
Bash
Executable File
110 lines
3.6 KiB
Bash
Executable File
#!/bin/bash
|
|
################################################################################
|
|
# install.sh - Modular management entry point for Ubuntu Based Kiosk.
|
|
#
|
|
# This is NOT yet the full system installer - that is still the big
|
|
# single-file script (ubuntu-based-kiosk.sh) documented in Readme.md, and
|
|
# first-time provisioning of a new kiosk still goes through it. That file
|
|
# still also contains its own (unmigrated, unmodified) copies of every
|
|
# menu below - both copies coexist deliberately until enough of Core
|
|
# Settings/Addons/Advanced has moved over to retire the old ones in one
|
|
# pass. This entry point is the modular replacement, one menus/*.sh file
|
|
# at a time, so a change to (say) the Sites menu can't accidentally break
|
|
# WiFi setup or the uninstaller three thousand lines away.
|
|
#
|
|
# Migrated so far: Sites & Page Timing (menus/sites.sh), Display &
|
|
# Interaction (menus/display.sh), Timezone (menus/timezone.sh), Hidden
|
|
# Site PIN (menus/hidden_pin.sh), Password Protection & Lockout
|
|
# (menus/lockout.sh), WiFi (menus/wifi.sh), Power/Display/Quiet Hours
|
|
# (menus/power_schedule.sh).
|
|
#
|
|
# Usage (once the kiosk has already been installed):
|
|
# git clone <repo>
|
|
# cd ubuntu-based-kiosk
|
|
# ./install.sh
|
|
################################################################################
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# shellcheck source=lib/menu.sh
|
|
source "$SCRIPT_DIR/lib/menu.sh"
|
|
# shellcheck source=lib/config.sh
|
|
source "$SCRIPT_DIR/lib/config.sh"
|
|
# shellcheck source=menus/sites.sh
|
|
source "$SCRIPT_DIR/menus/sites.sh"
|
|
# shellcheck source=menus/display.sh
|
|
source "$SCRIPT_DIR/menus/display.sh"
|
|
# shellcheck source=menus/timezone.sh
|
|
source "$SCRIPT_DIR/menus/timezone.sh"
|
|
# shellcheck source=menus/hidden_pin.sh
|
|
source "$SCRIPT_DIR/menus/hidden_pin.sh"
|
|
# shellcheck source=menus/lockout.sh
|
|
source "$SCRIPT_DIR/menus/lockout.sh"
|
|
# shellcheck source=menus/wifi.sh
|
|
source "$SCRIPT_DIR/menus/wifi.sh"
|
|
# shellcheck source=menus/power_schedule.sh
|
|
source "$SCRIPT_DIR/menus/power_schedule.sh"
|
|
|
|
################################################################################
|
|
# Preflight
|
|
################################################################################
|
|
|
|
if [[ "$(whoami)" == "kiosk" ]]; then
|
|
log_error "Cannot run as user 'kiosk'"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $EUID -eq 0 ]]; then
|
|
log_error "Run as a regular user with sudo privileges, not as root"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v jq &>/dev/null; then
|
|
log_error "jq is required but not installed. Run: sudo apt-get install -y jq"
|
|
exit 1
|
|
fi
|
|
|
|
if ! is_kiosk_installed; then
|
|
echo
|
|
log_error "No installed kiosk found at ${KIOSK_DIR}."
|
|
echo
|
|
echo "This tool manages an already-installed kiosk. To provision a new"
|
|
echo "one for the first time, use the full installer instead - see"
|
|
echo "Readme.md ('Quick Install') for the current download command."
|
|
echo
|
|
exit 1
|
|
fi
|
|
|
|
################################################################################
|
|
# Top-level menu
|
|
################################################################################
|
|
|
|
main_menu_builder() {
|
|
MENU_LABELS=(
|
|
"Sites & Page Timing"
|
|
"Display & Interaction"
|
|
"Timezone"
|
|
"Hidden Site PIN"
|
|
"Password Protection & Lockout"
|
|
"WiFi"
|
|
"Power/Display/Quiet Hours"
|
|
)
|
|
MENU_HANDLERS=(
|
|
sites_menu
|
|
display_menu
|
|
timezone_menu
|
|
hidden_pin_menu
|
|
lockout_menu
|
|
wifi_menu
|
|
power_schedule_menu
|
|
)
|
|
}
|
|
|
|
main_menu_status() {
|
|
echo "Managing kiosk at: ${KIOSK_DIR}"
|
|
}
|
|
|
|
run_menu "UBUNTU BASED KIOSK - MANAGEMENT" main_menu_builder main_menu_status "Exit"
|