diff --git a/scripts/dev/lp-paths-roots-test b/scripts/dev/lp-paths-roots-test new file mode 100755 index 0000000..0863eca --- /dev/null +++ b/scripts/dev/lp-paths-roots-test @@ -0,0 +1,66 @@ +#!/bin/bash +# Where do the three roots come from when nothing exported them? +# +# scripts/dev/lp-paths-roots-test +# +# The roots reach running code three ways: the CLI wrapper exports them, the +# task-processor unit carries them as Environment=, and anything those start +# inherits them. An @reboot crontab entry is started by none of the three — it +# runs a script by absolute path — so paths.sh fell through to the +# /libreportal-* defaults. +# +# That job is crontab_boot_app_reconcile.sh, which brings every installed app up +# at boot. Pointed at the wrong root it does not fail: docker creates the +# bind-mount directories it does not find, so every app comes back EMPTY while +# the real data sits untouched on the other disk. Nothing reports an error, and +# the damage is only visible by opening an app (storage-locations §10.1). +# +# So the case that matters is the second one: no environment, relocated install. + +REPO="$(cd "$(dirname "$0")/../.." && pwd)" +BASE="$(mktemp -d "${TMPDIR:-/tmp}/lp-paths-test-XXXXXX")" +trap 'rm -rf "$BASE"' EXIT + +fail=0 +chk(){ if [[ "$2" == "$3" ]]; then echo " ok $1"; else echo " FAIL $1: got '$2' want '$3'"; fail=1; fi; } + +# A stand-in for the baked task-processor unit. +cat > "$BASE/unit" < [env assignments...] + local var="$1"; shift + env -i PATH=/usr/bin:/bin "$@" bash -c \ + 'source "$0" >/dev/null 2>&1; eval echo "\$$1"' "$REPO/scripts/source/paths.sh" "$var" +} + +echo "--- no environment, unit present (the @reboot cron case) ---" +chk "system" "$(ask LP_SYSTEM_DIR LP_UNIT_FILE="$BASE/unit")" "/mnt/d1/libreportal-system" +chk "containers" "$(ask LP_CONTAINERS_DIR LP_UNIT_FILE="$BASE/unit")" "/mnt/d2/libreportal-containers" +chk "backups" "$(ask LP_BACKUPS_DIR LP_UNIT_FILE="$BASE/unit")" "/mnt/d2/libreportal-backups" +chk "derived containers_dir" \ + "$(ask containers_dir LP_UNIT_FILE="$BASE/unit")" "/mnt/d2/libreportal-containers/" +chk "derived configs_dir" \ + "$(ask configs_dir LP_UNIT_FILE="$BASE/unit")" "/mnt/d1/libreportal-system/configs/" + +echo "--- an explicit environment still wins over the unit ---" +chk "containers" \ + "$(ask LP_CONTAINERS_DIR LP_UNIT_FILE="$BASE/unit" LP_CONTAINERS_DIR=/explicit)" "/explicit" + +echo "--- no unit: unchanged default behaviour ---" +chk "system" "$(ask LP_SYSTEM_DIR LP_UNIT_FILE=/nonexistent)" "/libreportal-system" +chk "containers" "$(ask LP_CONTAINERS_DIR LP_UNIT_FILE=/nonexistent)" "/libreportal-containers" + +echo "--- a unit that names only the system root ---" +printf '[Service]\nEnvironment=LP_SYSTEM_DIR=/mnt/d1/libreportal-system\n' > "$BASE/partial" +chk "system" "$(ask LP_SYSTEM_DIR LP_UNIT_FILE="$BASE/partial")" "/mnt/d1/libreportal-system" +chk "containers -> default" "$(ask LP_CONTAINERS_DIR LP_UNIT_FILE="$BASE/partial")" "/libreportal-containers" + +echo "" +if (( fail )); then echo "FAILED"; exit 1; fi +echo "All root-resolution checks passed." diff --git a/scripts/source/paths.sh b/scripts/source/paths.sh index d7790b2..635f437 100644 --- a/scripts/source/paths.sh +++ b/scripts/source/paths.sh @@ -24,6 +24,35 @@ # derivations in sync. # --- Resolve the three roots ------------------------------------------------ +# Nothing in the environment? Recover them from the record root baked at install +# before falling back to the defaults below. +# +# The roots reach running code three ways: the CLI wrapper exports them, the +# task-processor unit carries them as Environment=, and everything started by +# those inherits them. An @reboot crontab entry is started by none of the three +# — it invokes a script by absolute path — so it fell through to the /libreportal-* +# defaults. On a relocated install that silently pointed the BOOT APP RECONCILE +# at the wrong disk, and that job brings every installed app up: docker creates +# the bind-mount directories it does not find, so the apps come back EMPTY while +# the real data sits untouched on the other disk (storage-locations §10.1). +# +# The unit is the authoritative record — init.sh reads it back the same way, and +# libreportal-relocate rewrites it — and it is root-owned, so this is not the +# manager reading a config it can edit. +if [[ -z "${LP_SYSTEM_DIR:-}" ]]; then + _lp_unit="${LP_UNIT_FILE:-/etc/systemd/system/libreportal.service}" + if [[ -r "$_lp_unit" ]]; then + _lp_v=$(grep -m1 -oE '^Environment=LP_SYSTEM_DIR=\S+' "$_lp_unit" 2>/dev/null); _lp_v="${_lp_v#*LP_SYSTEM_DIR=}" + [[ -n "$_lp_v" ]] && LP_SYSTEM_DIR="$_lp_v" + _lp_v=$(grep -m1 -oE '^Environment=LP_CONTAINERS_DIR=\S+' "$_lp_unit" 2>/dev/null); _lp_v="${_lp_v#*LP_CONTAINERS_DIR=}" + [[ -n "$_lp_v" && -z "${LP_CONTAINERS_DIR:-}" ]] && LP_CONTAINERS_DIR="$_lp_v" + _lp_v=$(grep -m1 -oE '^Environment=LP_BACKUPS_DIR=\S+' "$_lp_unit" 2>/dev/null); _lp_v="${_lp_v#*LP_BACKUPS_DIR=}" + [[ -n "$_lp_v" && -z "${LP_BACKUPS_DIR:-}" ]] && LP_BACKUPS_DIR="$_lp_v" + unset _lp_v + fi + unset _lp_unit +fi + # Transitional compat: an EXISTING install (the legacy single /docker tree, # identified by its config marker) keeps using /docker until a deliberate # reinstall to the split layout — so deploying new code never strands a running