paths: recover the roots from the baked unit when nothing exported them
The three 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
: "${LP_CONTAINERS_DIR:=/libreportal-containers}"
That entry is crontab_boot_app_reconcile.sh, which brings every installed app up
at boot. On a relocated install it therefore reconciled against the DEFAULT
root, and that 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 logs an error; the only symptom is opening an app and
finding it blank (storage-locations §10.1 calls this the top data-integrity
risk).
Verified on a case-2 install (apps on /mnt/lptest2): a bare environment resolved
containers_dir to /libreportal-containers/ where the real root was
/mnt/lptest2/libreportal-containers.
Recover them from the systemd unit, which is the authoritative baked record —
init.sh already reads it back the same way, libreportal-relocate rewrites it,
and it is root-owned, so this is not the manager reading a config it can edit.
An explicit environment still wins; with no unit the defaults are unchanged.
scripts/dev/lp-paths-roots-test pins all four cases; verified it fails when the
recovery is removed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
fc92d556f1
commit
864059ab83
66
scripts/dev/lp-paths-roots-test
Executable file
66
scripts/dev/lp-paths-roots-test
Executable file
@ -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" <<EOF
|
||||
[Service]
|
||||
Environment=LP_SYSTEM_DIR=/mnt/d1/libreportal-system
|
||||
Environment=LP_CONTAINERS_DIR=/mnt/d2/libreportal-containers
|
||||
Environment=LP_BACKUPS_DIR=/mnt/d2/libreportal-backups
|
||||
EOF
|
||||
|
||||
# Each case runs in its own bash so no root leaks between them.
|
||||
ask() { # ask <var> [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."
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user