fix(uninstall): resolve the real install roots + add ./uninstall.sh

Bug: runFullUninstall used the derived $docker_dir/$containers_dir/$backup_dir,
but a bare 'init.sh uninstall' on a CUSTOM-location install has no LP_*_DIR in
scope and no /docker marker — so it defaulted to /libreportal-* and would MISS the
real data (e.g. /mnt/ssd), leaving it behind.

Fix: libreportalReadBakedRoots reads the authoritative baked record from the
systemd unit (Environment=LP_SYSTEM_DIR/CONTAINERS_DIR/BACKUPS_DIR + User=<manager>)
and runFullUninstall re-derives from it before removing anything. Legacy units
(no LP_*_DIR) fall through to the derive defaults + /docker compat shim.

Add top-level uninstall.sh: a root-only convenience that finds the installed
init.sh (via the unit's system root, then common locations) and runs it —
'sudo ./uninstall.sh [--skip-docker-images]'. Verified the unit parsing extracts
custom roots/manager and the discovery picks the right init.sh (without running the
destructive teardown).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-25 18:45:16 +01:00
parent 800c70fefd
commit 805d557fd7
2 changed files with 59 additions and 0 deletions

24
init.sh
View File

@ -1545,8 +1545,32 @@ completeInitMessage()
# systemd --user service, so stop those BEFORE removing the users. Self-contained: # systemd --user service, so stop those BEFORE removing the users. Self-contained:
# uses only init.sh's inline helpers, so it still works as it deletes /docker. # uses only init.sh's inline helpers, so it still works as it deletes /docker.
# Keep in sync with docs/FOOTPRINT.md. # Keep in sync with docs/FOOTPRINT.md.
# Discover where THIS box was actually installed — custom installs put the roots
# anywhere, and a bare `init.sh uninstall` has no LP_*_DIR in scope. The systemd
# unit is the authoritative baked record (Environment=LP_*_DIR + User=<manager>).
# Sets LP_*_DIR + sudo_user_name so the following libreportalDerivePaths resolves
# the real locations; silently no-ops on a legacy unit (then the derive defaults /
# /docker compat shim apply).
libreportalReadBakedRoots() {
local unit=/etc/systemd/system/libreportal.service
[[ -f "$unit" ]] || return 0
local s c b m
s=$(grep -oE 'LP_SYSTEM_DIR=\S+' "$unit" | head -1 | cut -d= -f2)
c=$(grep -oE 'LP_CONTAINERS_DIR=\S+' "$unit" | head -1 | cut -d= -f2)
b=$(grep -oE 'LP_BACKUPS_DIR=\S+' "$unit" | head -1 | cut -d= -f2)
m=$(grep -oE '^User=\S+' "$unit" | head -1 | cut -d= -f2)
[[ -n "$s" ]] && LP_SYSTEM_DIR="$s"
[[ -n "$c" ]] && LP_CONTAINERS_DIR="$c"
[[ -n "$b" ]] && LP_BACKUPS_DIR="$b"
[[ -n "$m" ]] && { LP_MANAGER_USER="$m"; sudo_user_name="$m"; }
}
runFullUninstall() runFullUninstall()
{ {
# Resolve the ACTUAL install roots/manager for this box before removing anything.
libreportalReadBakedRoots
libreportalDerivePaths
local mgr="${sudo_user_name:-libreportal}" local mgr="${sudo_user_name:-libreportal}"
local iuser local iuser
iuser=$(grep -h '^CFG_DOCKER_INSTALL_USER=' "${configs_dir}general/general_docker_install" 2>/dev/null | head -1 | cut -d= -f2 | awk '{print $1}') iuser=$(grep -h '^CFG_DOCKER_INSTALL_USER=' "${configs_dir}general/general_docker_install" 2>/dev/null | head -1 | cut -d= -f2 | awk '{print $1}')

35
uninstall.sh Normal file
View File

@ -0,0 +1,35 @@
#!/usr/bin/env bash
#
# LibrePortal uninstaller — convenience launcher.
#
# sudo ./uninstall.sh # remove everything
# sudo ./uninstall.sh --skip-docker-images # keep the rootless docker layer
#
# The real teardown lives in init.sh (runFullUninstall), which self-resolves the
# install's actual roots/manager from the baked systemd unit — so this just finds
# the installed init.sh and runs it. Works regardless of where LibrePortal was
# installed (custom --system-dir, etc.).
set -euo pipefail
[[ $EUID -eq 0 ]] || { echo "uninstall.sh must run as root (try: sudo)" >&2; exit 1; }
# Prefer the system root baked into the systemd unit; then common defaults; then
# the bootstrap copy in /root; then a sibling init.sh next to this script.
unit=/etc/systemd/system/libreportal.service
sysdir=""
[[ -f "$unit" ]] && sysdir=$(grep -oE 'LP_SYSTEM_DIR=\S+' "$unit" | head -1 | cut -d= -f2)
init=""
for cand in \
${sysdir:+"$sysdir/install/init.sh"} \
/libreportal-system/install/init.sh \
/docker/install/init.sh \
/root/init.sh \
"$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/init.sh"; do
[[ -n "$cand" && -f "$cand" ]] && { init="$cand"; break; }
done
[[ -n "$init" ]] || { echo "uninstall.sh: could not find init.sh to run the uninstall." >&2; exit 1; }
echo "Running uninstall via $init ..."
exec bash "$init" "$@" uninstall