warn when the services can't reach moved folders
- list, new, import and enable check TERRARIA_BASE and TERRARIA_DIST against the units' ProtectSystem/ProtectHome/ReadWritePaths (terraria@ always, terractld when it's on) and print the drop-in to add. prepare explains why it can't create an instance folder. - Guide: bind mounts and drop-ins under "Changing locations", and a new section for instances or zips under /home. - journal-access in tab completion and the command reference. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
96f7876729
commit
7adf5c2999
5 files changed
+195
-9
No files matched your search
+107
-2
@@ -311,8 +311,11 @@ instance_running() {
|
||||
}
|
||||
|
||||
cmd_prepare() {
|
||||
local name=$1 dir; dir=$(inst_dir "$name")
|
||||
mkdir -p "$dir/data/Worlds"
|
||||
local name=$1 dir err; dir=$(inst_dir "$name")
|
||||
if ! err=$(mkdir -p "$dir/data/Worlds" 2>&1); then
|
||||
location_warnings
|
||||
die "can't create $dir/data/Worlds: ${err##*: }"
|
||||
fi
|
||||
take_lock "$name" # never re-unpack server/ under a running server
|
||||
[[ -f $dir/instance.conf ]] || write_instance_conf "$name" vanilla
|
||||
load_conf "$name"
|
||||
@@ -833,6 +836,7 @@ cmd_import() {
|
||||
echo "imported '$name' into $dir"
|
||||
fi
|
||||
echo "Review $dir/serverconfig.txt, then: $(hint root enable "$name" --now)"
|
||||
location_warnings
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------- mods ----
|
||||
@@ -1017,6 +1021,7 @@ EOF
|
||||
$(hint root enable "$name" --now)
|
||||
$(hint group attach "$name")
|
||||
EOF
|
||||
location_warnings
|
||||
}
|
||||
|
||||
# Whether the server starts at boot. Needs root: systemd doesn't let polkit
|
||||
@@ -1035,6 +1040,7 @@ cmd_enable() {
|
||||
# Enabling a name without an instance would create a default one at boot
|
||||
[[ -f $(inst_dir "$name")/instance.conf ]] ||
|
||||
die "no instance '$name' (create it with: $(hint root new "$name"))"
|
||||
location_warnings
|
||||
systemctl enable -q "$unit"
|
||||
if [[ $now -eq 1 ]]; then
|
||||
systemctl start "$unit"
|
||||
@@ -1088,6 +1094,104 @@ zip_warning() {
|
||||
log "fix it with$where: sudo chmod 644 ${bad[*]@Q}"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------ locations ----
|
||||
# The services' sandboxes (ProtectSystem=, ProtectHome=, ReadWritePaths=) are
|
||||
# set in their unit files, which can't follow /etc/default/terractl. If
|
||||
# TERRARIA_BASE or TERRARIA_DIST is moved somewhere a unit can't reach, it needs
|
||||
# a drop-in; these checks say so, with the drop-in to add.
|
||||
|
||||
# Is <path> <dir>, or inside it?
|
||||
path_in() { [[ $1 == "$2" || $1 == "$2"/* ]]; }
|
||||
|
||||
# Why <unit> can't use <path> (need: rw or ro): 'home' (ProtectHome hides it),
|
||||
# 'home-ro' (ProtectHome=read-only, not writable) or 'system' (ProtectSystem
|
||||
# makes it read-only). Prints nothing if the unit can use it, or if systemd or
|
||||
# the unit isn't there to ask.
|
||||
unit_path_problem() {
|
||||
local unit=$1 path=$2 need=$3 key value load="" ps=no ph=no p covered=0
|
||||
local -a rw=()
|
||||
command -v systemctl >/dev/null || return 0
|
||||
while IFS='=' read -r key value; do
|
||||
case $key in
|
||||
LoadState) load=$value ;;
|
||||
ProtectSystem) ps=$value ;;
|
||||
ProtectHome) ph=$value ;;
|
||||
ReadWritePaths) read -ra rw <<< "$value" ;;
|
||||
esac
|
||||
done < <(systemctl show -p LoadState,ProtectSystem,ProtectHome,ReadWritePaths "$unit" 2>/dev/null)
|
||||
[[ $load == loaded ]] || return 0
|
||||
path=$(readlink -m "$path") # where it really is, as systemd sees it
|
||||
for p in "${rw[@]}"; do
|
||||
p=${p#-} # '-': ignored if missing
|
||||
if path_in "$path" "$(readlink -m "$p")"; then covered=1; fi
|
||||
done
|
||||
if path_in "$path" /home || path_in "$path" /root || path_in "$path" /run/user; then
|
||||
case $ph in
|
||||
no) ;;
|
||||
read-only) if [[ $need == rw && $covered -eq 0 ]]; then echo home-ro; fi ;;
|
||||
*) echo home ;;
|
||||
esac
|
||||
return 0
|
||||
fi
|
||||
[[ $need == rw && $covered -eq 0 ]] || return 0
|
||||
case $ps in
|
||||
strict) echo system ;;
|
||||
yes|full)
|
||||
if path_in "$path" /usr || path_in "$path" /boot || path_in "$path" /efi ||
|
||||
{ [[ $ps == full ]] && path_in "$path" /etc; }; then
|
||||
echo system
|
||||
fi ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Warns about one unit: location_warning <unit> <name to edit> <who it breaks>
|
||||
# <dist access needed>
|
||||
location_warning() {
|
||||
local unit=$1 edit=$2 effect=$3 dist_need=$4 what path need problem home=0 where=""
|
||||
local -a lines=() rw=()
|
||||
for what in TERRARIA_BASE TERRARIA_DIST; do
|
||||
if [[ $what == TERRARIA_BASE ]]; then path=$BASE_DIR need=rw; else path=$DIST_DIR need=$dist_need; fi
|
||||
problem=$(unit_path_problem "$unit" "$path" "$need")
|
||||
case $problem in
|
||||
home)
|
||||
lines+=("$edit hides $path ($what) under ProtectHome=, so $effect")
|
||||
home=1
|
||||
if [[ $need == rw ]]; then rw+=("$path"); fi ;;
|
||||
home-ro|system)
|
||||
lines+=("$edit can't write to $path ($what), so $effect")
|
||||
if [[ $problem == home-ro ]]; then home=1; fi
|
||||
rw+=("$path") ;;
|
||||
esac
|
||||
done
|
||||
[[ ${#lines[@]} -gt 0 ]] || return 0
|
||||
if [[ -n ${TERRACTL_VIA:-} ]]; then where=" (on $(hostname 2>/dev/null || echo that host))"; fi
|
||||
printf 'terractl: warning: %s\n' "${lines[@]}" >&2
|
||||
{
|
||||
echo " Fix it with a drop-in$where: sudo systemctl edit $edit"
|
||||
echo " [Service]"
|
||||
if [[ $home -eq 1 ]]; then echo " ProtectHome=read-only"; fi
|
||||
for path in "${rw[@]}"; do echo " ReadWritePaths=$path"; done
|
||||
if [[ $home -eq 1 ]]; then
|
||||
echo " The '$SVC_USER' user also needs access to every folder above it; see"
|
||||
echo " \"Instances or zips under /home\" in the guide."
|
||||
else
|
||||
echo " See \"Changing locations\" in the guide."
|
||||
fi
|
||||
} >&2
|
||||
}
|
||||
|
||||
# The servers always; the daemon only if it's in use. The daemon writes zips
|
||||
# into dist/ when instances are copied to this host, so it needs both writable.
|
||||
location_warnings() {
|
||||
location_warning terraria@terractl-check.service terraria@.service \
|
||||
"servers can't start" ro
|
||||
if systemctl is-enabled --quiet terractld.service 2>/dev/null ||
|
||||
systemctl is-active --quiet terractld.service 2>/dev/null; then
|
||||
location_warning terractld.service terractld.service \
|
||||
"other hosts can't copy, move or import instances here" rw
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
local d n port boot state
|
||||
printf '%-20s %-11s %-6s %-9s %s\n' INSTANCE TYPE PORT BOOT STATE
|
||||
@@ -1105,6 +1209,7 @@ cmd_list() {
|
||||
) 2>/dev/null || printf '%-20s %s\n' "$n" "(cannot read config - try sudo)"
|
||||
done
|
||||
zip_warning
|
||||
location_warnings
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------ versions ----
|
||||
|
||||
Reference in new issue
Block a user