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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VfsFSoRqfbRG7XAg5RoE7e
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
+7
-2
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user