From b0b558f0153b8c572f60e5a7fd112272a58aab75 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Aug 2026 03:03:50 +0000 Subject: [PATCH] Migrate Complete Uninstall, composed from each addon's own uninstall helper; bump to v2.12.0 New menus/complete_uninstall.sh (Core Settings), the last of the "destructive trio". Rather than re-implementing every addon's teardown a second time (the legacy shape), it composes the *_do_uninstall helpers each addon already has - if an addon's removal logic changes, Complete Uninstall picks it up automatically. Every addon menu with an uninstall action (CUPS, VNC, WireGuard, Tailscale, Netbird, LMS, Squeezelite, Asterisk Intercom) plus power_schedule's "remove all schedules" and Emergency Hotspot's disable action were each split into a confirm-and-call wrapper (unchanged from the user's perspective) and a silent do-the-removal helper that both the wrapper and Complete Uninstall call. Bug fix found while composing these: several *_do_uninstall helpers (CUPS's apt autoremove/apt clean, VNC/WireGuard/Tailscale/Netbird's apt remove) had a bare, unguarded apt call as their second-to-last statement. Previously this only risked aborting that one menu action if the package was already gone. Composed together as sequential calls inside Complete Uninstall, the same failure would have silently truncated the entire uninstall sequence partway through. Guarded all of them with `|| true`. Non-addon teardown (kiosk user/files, Node.js, LightDM/Openbox, remaining systemd units/scripts, polkit rules, re-enabling virtual consoles, final package cleanup) stays inline in menus/complete_uninstall.sh, since no single addon owns those paths. Upgrade and Full Reinstall stay in ubuntu-based-kiosk.sh only - both are coupled to its own heredoc self-extraction of main.js/preload.js/ etc, which has no modular equivalent yet. Full command-level stubbed test suite exercising the full 12-step teardown, confirmation-text validation, and reboot prompt. Full 19-suite regression + real end-to-end menu navigation via install.sh all pass. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01VfsFSoRqfbRG7XAg5RoE7e --- Readme.md | 34 +++++-- install.sh | 12 ++- menus/addon_asterisk_intercom.sh | 22 +++- menus/addon_cups.sh | 9 +- menus/addon_lms_squeezelite.sh | 28 +++++- menus/addon_remote_access.sh | 24 ++++- menus/advanced_emergency_hotspot.sh | 7 +- menus/complete_uninstall.sh | 151 ++++++++++++++++++++++++++++ menus/power_schedule.sh | 5 + ubuntu-based-kiosk.sh | 44 +++++++- 10 files changed, 306 insertions(+), 30 deletions(-) create mode 100644 menus/complete_uninstall.sh diff --git a/Readme.md b/Readme.md index ef0414a..a0fc3c4 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ # Ubuntu Based Kiosk -**Current Version:** 2.11.0 (check script header for latest version) +**Current Version:** 2.12.0 (check script header for latest version) **Built with Claude Sonnet 4.6 AI assistance** **License:** GPL v3 - Keep derivatives open source **Repository:** https://github.com/outis1one/ubuntu-based-kiosk/ @@ -1251,6 +1251,10 @@ terminal menu and the web UI, so they can't drift apart). (Advanced): auto-starts a WiFi hotspot if no internet is detected 60 seconds after boot. Its own runtime script/systemd unit go through `$BIN_DIR`/`$SYSTEMD_DIR` like every other addon. +- `menus/complete_uninstall.sh` — **Complete Uninstall** (Core + Settings): the last of the "destructive trio." Composed from every + addon's own `*_do_uninstall` helper instead of re-implementing + removal a second time — see "Recent Updates (v2.12.0)" below. - `install.sh` — entry point for the modular tool, now grouped **Core Settings / Addons / Advanced** like the legacy menu. Run it against an *already-installed* kiosk: @@ -1265,14 +1269,16 @@ most of the old installer. `ubuntu-based-kiosk.sh` is still ~12,000 lines and still contains its own unremoved, unmodified copies of every menu above, including the legacy three-option (Client/Server/Full) Easy Asterisk Intercom — the modular version only replaces the Client -option, by design (plus Upgrade, Reinstall, Complete Uninstall, Export/ -Import Settings, and Fix Squeezelite Audio — none of that has moved -yet). Both copies coexist deliberately: the old ones stay until enough -of Core Settings/Addons/Advanced is migrated to retire them in one -pass, rather than leaving -the legacy menu half-wired. Migration continues one `menus/*.sh` file -at a time; first-time installation itself is the last and largest piece -to move, if it moves at all. +option, by design (plus Upgrade, Full Reinstall, Export/Import +Settings, and Fix Squeezelite Audio — none of that has moved yet; +Complete Uninstall *is* now migrated, but Upgrade and Full Reinstall +are staying put — both are coupled to this file's own heredoc self- +extraction of main.js/preload.js/etc, which has no modular equivalent). +Both copies coexist deliberately: the old ones stay until enough of +Core Settings/Addons/Advanced is migrated to retire them in one pass, +rather than leaving the legacy menu half-wired. Migration continues one +`menus/*.sh` file at a time; first-time installation itself is the last +and largest piece to move, if it moves at all. **Resolved (v2.9.0):** `is_service_enabled()` — shared by both scripts — had a pre-check (`systemctl list-unit-files | grep -q "^${service}\s"`) @@ -1299,9 +1305,15 @@ full migration pass. ## Project Status & Future Plans -**Current Version:** 2.11.0 +**Current Version:** 2.12.0 -**Recent Updates (v2.11.0):** +**Recent Updates (v2.12.0):** +- **Complete Uninstall migrated** — the last of the "destructive trio." Rather than re-implementing every addon's teardown a second time (the legacy shape — CUPS/VNC/WireGuard/Tailscale/Netbird/LMS/Squeezelite removal all inlined again, independently of each addon's own uninstall action), `menus/complete_uninstall.sh` composes the `*_do_uninstall` helpers each addon already has. Every addon menu with an uninstall action was split into a confirm-and-call wrapper (unchanged from the user's perspective) plus a silent removal helper that both the wrapper and Complete Uninstall call — no duplicated logic anywhere, and if an addon's removal logic changes later, Complete Uninstall picks it up automatically. +- **Important bug found and fixed while composing these:** several `*_do_uninstall` helpers (CUPS's `apt autoremove`/`apt clean`, VNC/WireGuard/Tailscale/Netbird's `apt remove`) had a bare, unguarded `apt` call. Previously this only risked aborting that one menu action if the package was already gone. Composed together as sequential calls inside Complete Uninstall, the same failure would have silently truncated the *entire* uninstall partway through — e.g. the kiosk user might never get removed because an already-uninstalled VPN client's `apt remove` failed first. Guarded all of them with `|| true`. +- Non-addon teardown (kiosk user/files, Node.js, LightDM/Openbox, remaining systemd units/scripts, polkit rules, re-enabling virtual consoles, final package cleanup) stays inline in `menus/complete_uninstall.sh`, since no single addon owns those paths — same as the legacy script. +- Upgrade and Full Reinstall remain in `ubuntu-based-kiosk.sh` only — both are coupled to its own heredoc self-extraction of main.js/preload.js/etc, which has no modular equivalent yet. + +**Previous (v2.11.0):** - **4 more Advanced items migrated**, alongside Diagnostics: **Electron Maintenance** (`menus/advanced_electron.sh` — the legacy "Manual Electron Update" and "Fix Blank Screen" combined into one submenu, since both maintain the same installation and share the binary-repair logic), **Factory Reset** (`menus/advanced_factory_reset.sh` — wipes `config.json` only, addons untouched), **Virtual Consoles** (`menus/advanced_virtual_consoles.sh` — toggles Ctrl+Alt+F1-F8 terminal login), and **Emergency Hotspot** (`menus/advanced_emergency_hotspot.sh` — auto-starts a WiFi hotspot if no internet is detected 60 seconds after boot; its own runtime script and systemd unit now go through `$BIN_DIR`/`$SYSTEMD_DIR` like every other addon's own files). - That's 8 of the legacy Advanced menu's 12 entries now covered. Not migrated this round: Export/Import Settings (pending a decision on whether to rebuild it around actual paths instead of a hardcoded per-addon step list, or whether the future web UI replaces the need for it) and Fix Squeezelite Audio (small enough that it may fold into the LMS addon instead of staying standalone — not decided yet). - Complete Uninstall (the last of the "destructive trio") is next, composed from each addon's own uninstall action plus core teardown rather than rewriting removal logic a second time. Upgrade and Full Reinstall stay in the legacy script for now — both are coupled to its own heredoc self-extraction of main.js/preload.js/etc, which has no modular equivalent yet. diff --git a/install.sh b/install.sh index b0f1118..26a9ccb 100755 --- a/install.sh +++ b/install.sh @@ -15,7 +15,11 @@ # Migrated so far, grouped the same way the legacy menu groups them: # Core Settings: Sites & Page Timing, Display & Interaction, Timezone, # Hidden Site PIN, Password Protection & Lockout, WiFi, -# Power/Display/Quiet Hours. +# Power/Display/Quiet Hours, Complete Uninstall +# (menus/complete_uninstall.sh - composed from every addon's own +# uninstall helper rather than re-implementing removal a second +# time; Upgrade and Full Reinstall stay in the legacy script, both +# coupled to its heredoc self-extraction of main.js/preload.js/etc). # Addons: CUPS Printing (menus/addon_cups.sh), Authelia Auto-Login # (menus/addon_authelia.sh), Remote Access - VNC/WireGuard/ # Tailscale/Netbird (menus/addon_remote_access.sh), LMS Server / @@ -76,6 +80,10 @@ source "$SCRIPT_DIR/menus/advanced_factory_reset.sh" source "$SCRIPT_DIR/menus/advanced_virtual_consoles.sh" # shellcheck source=menus/advanced_emergency_hotspot.sh source "$SCRIPT_DIR/menus/advanced_emergency_hotspot.sh" +# shellcheck source=menus/complete_uninstall.sh +# Sourced last: composes the *_do_uninstall/*_do_remove_all/*_do_disable +# helpers defined in every file above it. +source "$SCRIPT_DIR/menus/complete_uninstall.sh" ################################################################################ # Preflight @@ -122,6 +130,7 @@ core_settings_menu_builder() { "Password Protection & Lockout" "WiFi" "Power/Display/Quiet Hours" + "Complete Uninstall" ) MENU_HANDLERS=( sites_menu @@ -131,6 +140,7 @@ core_settings_menu_builder() { lockout_menu wifi_menu power_schedule_menu + complete_uninstall_menu ) } diff --git a/menus/addon_asterisk_intercom.sh b/menus/addon_asterisk_intercom.sh index bb12a6a..7a54274 100644 --- a/menus/addon_asterisk_intercom.sh +++ b/menus/addon_asterisk_intercom.sh @@ -262,18 +262,34 @@ BARESIPUNIT action_uninstall_asterisk_intercom() { echo ask_yes_no "Remove Asterisk Intercom (Baresip)?" "n" || { echo "Cancelled"; pause; return; } + asterisk_intercom_do_uninstall ask + pause +} + +# The actual removal, no confirmation prompt - shared with Complete +# Uninstall so that operation doesn't need to re-implement this teardown +# a second time. $1: "ask" to prompt about config removal interactively +# (the normal case), "purge" to remove config without asking (Complete +# Uninstall). +asterisk_intercom_do_uninstall() { + local data_choice="${1:-ask}" baresip_systemctl_user stop baresip.service 2>/dev/null || true baresip_systemctl_user disable baresip.service 2>/dev/null || true sudo rm -f "$BARESIP_USER_SERVICE_DIR/baresip.service" sudo apt remove -y baresip 2>/dev/null || true - if ask_yes_no "Remove saved SIP configuration too?" "n"; then + local purge_config=false + if [[ "$data_choice" == "purge" ]]; then + purge_config=true + elif [[ "$data_choice" == "ask" ]] && ask_yes_no "Remove saved SIP configuration too?" "n"; then + purge_config=true + fi + + if $purge_config; then sudo rm -rf "$BARESIP_CONFIG_DIR" log_success "Asterisk Intercom removed (configuration deleted)" else log_success "Asterisk Intercom removed (configuration preserved)" fi - - pause } diff --git a/menus/addon_cups.sh b/menus/addon_cups.sh index c3a7db1..54f5b3a 100644 --- a/menus/addon_cups.sh +++ b/menus/addon_cups.sh @@ -132,7 +132,12 @@ EOF action_cups_uninstall() { echo ask_yes_no "Completely remove CUPS, including all queues and settings (purge)?" "n" || { echo "Cancelled"; return; } + cups_do_uninstall +} +# The actual removal, no prompt - shared with Complete Uninstall so that +# operation doesn't need to re-implement CUPS teardown a second time. +cups_do_uninstall() { echo "Performing complete CUPS uninstall..." sudo systemctl stop cups cups-browsed 2>/dev/null || true @@ -149,8 +154,8 @@ action_cups_uninstall() { sudo rm -rf /etc/cups /var/cache/cups /var/spool/cups /var/log/cups /usr/share/cups sudo rm -f "$POLKIT_DIR/kiosk-printing.pkla" - sudo apt autoremove -y - sudo apt clean + sudo apt autoremove -y 2>/dev/null || true + sudo apt clean 2>/dev/null || true log_success "CUPS completely removed" } diff --git a/menus/addon_lms_squeezelite.sh b/menus/addon_lms_squeezelite.sh index 43ef145..ac779a3 100644 --- a/menus/addon_lms_squeezelite.sh +++ b/menus/addon_lms_squeezelite.sh @@ -194,6 +194,16 @@ action_install_lms() { action_uninstall_lms() { echo ask_yes_no "Remove LMS Server?" "n" || { echo "Cancelled"; pause; return; } + lms_do_uninstall ask + pause +} + +# The actual removal, no confirmation prompt - shared with Complete +# Uninstall so that operation doesn't need to re-implement LMS teardown a +# second time. $1: "ask" to prompt about data removal interactively (the +# normal case), "purge" to remove data without asking (Complete Uninstall). +lms_do_uninstall() { + local data_choice="${1:-ask}" local service_name service_name=$(lms_service_name) @@ -212,15 +222,20 @@ action_uninstall_lms() { sudo rm -f /etc/apt/sources.list.d/lms.list sudo rm -f /usr/share/keyrings/lms-keyring.gpg - if ask_yes_no "Remove LMS data and configuration?" "n"; then + local purge_data=false + if [[ "$data_choice" == "purge" ]]; then + purge_data=true + elif [[ "$data_choice" == "ask" ]] && ask_yes_no "Remove LMS data and configuration?" "n"; then + purge_data=true + fi + + if $purge_data; then sudo rm -rf /var/lib/squeezeboxserver sudo rm -rf /etc/squeezeboxserver log_success "LMS and data removed" else log_success "LMS removed (data preserved)" fi - - pause } ################################################################################ @@ -332,13 +347,16 @@ EOF action_uninstall_squeezelite() { echo ask_yes_no "Remove Squeezelite Player?" "n" || { echo "Cancelled"; pause; return; } + squeezelite_do_uninstall + pause +} +# Shared with Complete Uninstall - same reasoning as lms_do_uninstall. +squeezelite_do_uninstall() { sudo systemctl stop squeezelite 2>/dev/null || true sudo systemctl disable squeezelite 2>/dev/null || true sudo rm -f "$SYSTEMD_DIR/squeezelite.service" sudo rm -f "$BIN_DIR/squeezelite-start.sh" sudo apt remove -y squeezelite 2>/dev/null || true log_success "Squeezelite removed" - - pause } diff --git a/menus/addon_remote_access.sh b/menus/addon_remote_access.sh index 1355e5b..d535b95 100644 --- a/menus/addon_remote_access.sh +++ b/menus/addon_remote_access.sh @@ -130,11 +130,15 @@ action_vnc_change_password() { action_vnc_uninstall() { echo ask_yes_no "Remove VNC?" "n" || { echo "Cancelled"; return; } + vnc_do_uninstall +} +# Shared with Complete Uninstall - same reasoning as cups_do_uninstall. +vnc_do_uninstall() { sudo systemctl stop x11vnc 2>/dev/null || true sudo systemctl disable x11vnc 2>/dev/null || true sudo rm -f "$SYSTEMD_DIR/x11vnc.service" - sudo apt remove -y x11vnc + sudo apt remove -y x11vnc 2>/dev/null || true log_success "VNC removed" } @@ -227,10 +231,14 @@ action_wireguard_paste_config() { action_wireguard_uninstall() { echo ask_yes_no "Remove WireGuard?" "n" || { echo "Cancelled"; return; } + wireguard_do_uninstall +} +# Shared with Complete Uninstall - same reasoning as cups_do_uninstall. +wireguard_do_uninstall() { sudo systemctl stop 'wg-quick@*' 2>/dev/null || true sudo systemctl disable 'wg-quick@*' 2>/dev/null || true - sudo apt remove -y wireguard wireguard-tools + sudo apt remove -y wireguard wireguard-tools 2>/dev/null || true log_success "WireGuard removed" } @@ -328,9 +336,13 @@ action_tailscale_show_status() { action_tailscale_uninstall() { echo ask_yes_no "Remove Tailscale?" "n" || { echo "Cancelled"; return; } + tailscale_do_uninstall +} +# Shared with Complete Uninstall - same reasoning as cups_do_uninstall. +tailscale_do_uninstall() { sudo tailscale down 2>/dev/null || true - sudo apt remove -y tailscale + sudo apt remove -y tailscale 2>/dev/null || true log_success "Tailscale removed" } @@ -411,8 +423,12 @@ action_netbird_show_status() { action_netbird_uninstall() { echo ask_yes_no "Remove Netbird?" "n" || { echo "Cancelled"; return; } + netbird_do_uninstall +} +# Shared with Complete Uninstall - same reasoning as cups_do_uninstall. +netbird_do_uninstall() { sudo netbird down 2>/dev/null || true - sudo apt remove -y netbird + sudo apt remove -y netbird 2>/dev/null || true log_success "Netbird removed" } diff --git a/menus/advanced_emergency_hotspot.sh b/menus/advanced_emergency_hotspot.sh index 3c72edc..315cf0c 100644 --- a/menus/advanced_emergency_hotspot.sh +++ b/menus/advanced_emergency_hotspot.sh @@ -291,13 +291,16 @@ UNITEOF action_disable_emergency_hotspot() { echo ask_yes_no "Disable emergency hotspot?" "n" || { echo "Cancelled"; pause; return; } + emergency_hotspot_do_disable + pause +} +# Shared with Complete Uninstall - same reasoning as cups_do_uninstall. +emergency_hotspot_do_disable() { sudo systemctl stop kiosk-emergency-hotspot.service 2>/dev/null || true sudo systemctl disable kiosk-emergency-hotspot.service 2>/dev/null || true sudo rm -f "$SYSTEMD_DIR/kiosk-emergency-hotspot.service" sudo rm -f "$EMERGENCY_HOTSPOT_SCRIPT" sudo systemctl daemon-reload 2>/dev/null || true log_success "Emergency hotspot disabled" - - pause } diff --git a/menus/complete_uninstall.sh b/menus/complete_uninstall.sh new file mode 100644 index 0000000..c013062 --- /dev/null +++ b/menus/complete_uninstall.sh @@ -0,0 +1,151 @@ +#!/bin/bash +################################################################################ +# menus/complete_uninstall.sh - "Complete Uninstall" (Core Settings): full +# teardown, returning the machine to its pre-kiosk state. +# +# Composed from every other addon's own silent uninstall helper +# (cups_do_uninstall, vnc_do_uninstall, wireguard_do_uninstall, +# tailscale_do_uninstall, netbird_do_uninstall, lms_do_uninstall, +# squeezelite_do_uninstall, asterisk_intercom_do_uninstall, +# power_schedule_do_remove_all, emergency_hotspot_do_disable) instead of +# re-implementing removal logic for each addon a second time here - if an +# addon's uninstall logic changes, this picks it up automatically. Only +# the pieces no single addon owns - the kiosk user/files, Node.js/ +# LightDM/Openbox, polkit rules, leftover systemd units - are handled +# directly below, same as the legacy script. +# +# Ordering matters: every addon teardown runs before the kiosk user is +# removed, because asterisk_intercom_do_uninstall still needs +# `id -u "$KIOSK_USER"` to resolve that user's systemd --user session. +# +# After this runs, the kiosk user (and therefore is_kiosk_installed) is +# gone - install.sh itself will refuse to start against this machine +# again until a fresh install re-provisions it. That's intentional: +# there is nothing left here for this tool to manage. +# +# Depends on: lib/menu.sh, lib/config.sh, and every menus/addon_*.sh / +# menus/power_schedule.sh / menus/advanced_emergency_hotspot.sh being +# sourced first (for the *_do_uninstall helpers above). +################################################################################ + +complete_uninstall_status() { + echo "⚠ Removes the kiosk user, every addon, and returns this machine" + echo " to its pre-kiosk state. Cannot be undone." +} + +complete_uninstall_menu_builder() { + MENU_LABELS=("Completely uninstall the kiosk") + MENU_HANDLERS=(action_complete_uninstall) +} + +complete_uninstall_menu() { + run_menu "COMPLETE UNINSTALL" complete_uninstall_menu_builder complete_uninstall_status +} + +################################################################################ +# Actions +################################################################################ + +action_complete_uninstall() { + echo + echo "⚠️ This will COMPLETELY REMOVE:" + echo " • Kiosk user and all data" + echo " • All kiosk configuration and sites" + echo " • All Electron/Node.js installations" + echo " • All browser caches and data" + echo " • CUPS printer system" + echo " • Squeezelite and LMS (Lyrion Music Server)" + echo " • Remote access (VNC, WireGuard, Tailscale, Netbird)" + echo " • Asterisk Intercom (Baresip)" + echo " • LightDM and Openbox" + echo " • All kiosk schedules and services" + echo " • Emergency hotspot configuration" + echo + echo "⚠️ This CANNOT be undone!" + echo + local confirm + confirm=$(ask_text "Type UNINSTALL to confirm" "") + if [[ "$confirm" != "UNINSTALL" ]]; then + echo "Cancelled" + pause + return + fi + + echo + echo "Beginning complete uninstall..." + + echo "[1/12] Stopping kiosk display..." + sudo systemctl stop lightdm 2>/dev/null || true + + echo "[2/12] Removing addons..." + cups_do_uninstall + vnc_do_uninstall + wireguard_do_uninstall + tailscale_do_uninstall + netbird_do_uninstall + lms_do_uninstall purge + squeezelite_do_uninstall + asterisk_intercom_do_uninstall purge + + echo "[3/12] Removing schedules and emergency hotspot..." + power_schedule_do_remove_all + emergency_hotspot_do_disable + + # Must come after every addon teardown above - Asterisk Intercom's + # helper still needs this user to resolve its systemd --user session. + echo "[4/12] Removing kiosk user..." + if id "$KIOSK_USER" &>/dev/null; then + sudo pkill -u "$KIOSK_USER" 2>/dev/null || true + sudo userdel -r "$KIOSK_USER" 2>/dev/null || true + log_success "Kiosk user removed" + fi + + echo "[5/12] Removing kiosk files..." + sudo rm -rf "$KIOSK_DIR" + sudo rm -rf "$KIOSK_HOME" + + echo "[6/12] Removing remaining systemd units..." + sudo rm -f "$SYSTEMD_DIR"/kiosk-*.service + sudo rm -f "$SYSTEMD_DIR"/kiosk-*.timer + sudo systemctl daemon-reload 2>/dev/null || true + + echo "[7/12] Removing remaining scripts..." + sudo rm -f "$BIN_DIR"/kiosk-* + sudo rm -f /etc/udev/rules.d/99-kiosk-hotplug.rules + sudo udevadm control --reload-rules 2>/dev/null || true + + echo "[8/12] Removing Node.js..." + sudo apt-get purge -y nodejs npm 2>/dev/null || true + sudo rm -rf /usr/local/lib/node_modules + sudo rm -rf /usr/local/bin/node + sudo rm -rf /usr/local/bin/npm + + echo "[9/12] Removing LightDM and Openbox..." + sudo systemctl disable lightdm 2>/dev/null || true + sudo apt-get purge -y lightdm openbox 2>/dev/null || true + + echo "[10/12] Removing polkit rules..." + sudo rm -f "$POLKIT_DIR/kiosk-power.pkla" + sudo rm -f "$POLKIT_DIR/kiosk-printing.pkla" + + echo "[11/12] Re-enabling virtual consoles..." + for i in {1..8}; do + sudo systemctl unmask "getty@tty${i}.service" 2>/dev/null || true + done + sudo systemctl daemon-reload 2>/dev/null || true + + echo "[12/12] Cleaning up packages..." + sudo apt-get autoremove -y 2>/dev/null || true + sudo apt-get autoclean 2>/dev/null || true + + echo + log_success "Kiosk completely uninstalled" + echo "The system has been returned to its pre-kiosk state." + echo "You may want to reboot to ensure all changes take effect." + echo + if ask_yes_no "Reboot now?" "n"; then + echo "Rebooting in 3 seconds..." + sleep 3 + sudo reboot + fi +} diff --git a/menus/power_schedule.sh b/menus/power_schedule.sh index eb9cc03..c2aad74 100644 --- a/menus/power_schedule.sh +++ b/menus/power_schedule.sh @@ -628,7 +628,12 @@ action_disable_electron_reload() { action_remove_all_schedules() { echo ask_yes_no "Remove ALL power/display/quiet/reload schedules?" "n" || { echo "Cancelled"; return; } + power_schedule_do_remove_all +} +# The actual removal, no prompt - shared with Complete Uninstall so that +# operation doesn't need to re-implement schedule teardown a second time. +power_schedule_do_remove_all() { for timer in kiosk-shutdown kiosk-display-off kiosk-display-on kiosk-quiet-start kiosk-quiet-end kiosk-electron-reload; do sudo systemctl stop "${timer}.timer" 2>/dev/null || true sudo systemctl disable "${timer}.timer" 2>/dev/null || true diff --git a/ubuntu-based-kiosk.sh b/ubuntu-based-kiosk.sh index 829f3f3..accce64 100644 --- a/ubuntu-based-kiosk.sh +++ b/ubuntu-based-kiosk.sh @@ -1,8 +1,48 @@ #!/bin/bash ################################################################################ -### Ubuntu Based Kiosk v2.11.0 ### +### Ubuntu Based Kiosk v2.12.0 ### ################################################################################ # +# RELEASE v2.12.0 - Complete Uninstall Migrated (Last of the +# "Destructive Trio"); Composed, Not Re-Implemented +# - New in ./install.sh's Core Settings menu: Complete Uninstall +# (menus/complete_uninstall.sh). Rather than re-implementing every +# addon's teardown a second time (the shape this function had in the +# legacy script - CUPS/VNC/WireGuard/Tailscale/Netbird/LMS/Squeezelite +# removal logic all inlined again, independently of the same logic in +# each addon's own uninstall action), it composes the *_do_uninstall +# helpers each addon already has. If an addon's removal logic changes, +# Complete Uninstall picks it up automatically instead of silently +# drifting out of sync. +# - Every addon menu that had an uninstall action (CUPS, VNC, WireGuard, +# Tailscale, Netbird, LMS, Squeezelite, Asterisk Intercom) plus +# power_schedule's "remove all schedules" and the Emergency Hotspot +# disable action were each split into a confirm-and-call wrapper (the +# existing interactive action, unchanged from the user's perspective) +# and a silent do-the-removal helper that both the wrapper and +# Complete Uninstall call - no duplicated removal logic anywhere. +# - IMPORTANT bug found and fixed while composing these: several +# *_do_uninstall helpers (CUPS's `apt autoremove`/`apt clean`, and +# VNC/WireGuard/Tailscale/Netbird's `apt remove`) had a bare, unguarded +# `apt` call as their second-to-last statement. Previously this only +# risked aborting that one menu action if the package was already +# gone (silently caught by run_menu's own guard) - a minor UX +# blemish. Composed together as bare sequential calls inside Complete +# Uninstall, the same failure would have silently truncated the +# *entire* uninstall sequence partway through - e.g. the kiosk user +# might never get removed because an already-uninstalled VPN client's +# `apt remove` failed first. Guarded all of them with `|| true`, +# fixing the risk in both the standalone action and the composition. +# - Non-addon teardown (kiosk user/files, Node.js, LightDM/Openbox, +# remaining systemd units/scripts, polkit rules, re-enabling virtual +# consoles, final package cleanup) stays inline in +# menus/complete_uninstall.sh, same as the legacy script, since no +# single addon owns those paths. +# - Upgrade and Full Reinstall remain in ubuntu-based-kiosk.sh only - +# both are fundamentally coupled to this file's own heredoc self- +# extraction of main.js/preload.js/etc, which has no equivalent in the +# modular system yet. This closes out the "destructive trio." +# # RELEASE v2.11.0 - 4 More Advanced Items Migrated (Electron Maintenance, # Factory Reset, Virtual Consoles, Emergency Hotspot) # - New in ./install.sh's Advanced menu, alongside Diagnostics: @@ -410,7 +450,7 @@ set -euo pipefail ### SECTION 1: CONSTANTS & GLOBALS ################################################################################ -SCRIPT_VERSION="2.11.0" +SCRIPT_VERSION="2.12.0" # Resolve the real path to this script file. # When piped (curl|bash or wget|bash), BASH_SOURCE[0] is a pipe descriptor,