check the terraria user can run terractl before switching to it

upgrade (before stopping anything) and prepare/run started as root now
refuse with an explanation when terractl is somewhere the terraria user
can't open, such as a copy under /root, instead of failing with
"env: Permission denied". upgrade also says when it started the server
again after a failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adamandClaude Opus 5.5 committed 2026-10-02 22:03:43 +00:00
1 parent 7adf5c2999
commit 8a509f1398
2 files changed
+14 -3

No files matched your search

+1
View File
@@ -11,6 +11,7 @@
- `list`, `new`, `import` and `enable` warn when `TERRARIA_BASE` or `TERRARIA_DIST` is somewhere the services' sandbox doesn't allow (the instances folder not in `ReadWritePaths=`, or either folder under `/home`), and print the exact drop-in to add. The daemon (`terractld.service`) is checked too when it's on. A server that can't create its folder now says why in its log, instead of only "Read-only file system".
- The guide's "Changing locations" now covers bind mounts (the simplest way to use another disk) as well as drop-ins, and a new section explains keeping instances or zips under `/home`.
- `journal-access` is offered by tab completion and listed in the guide's command reference.
- `upgrade`, and `prepare`/`run` started as root, check first that the `terraria` user can run the terractl being used. A copy under `/root` is refused with an explanation, before `upgrade` stops the server, instead of failing with `env: Permission denied`.
- `terractl` run by a relative path (e.g. `sudo ./bin/terractl prepare <name>` from a source tree) no longer fails to switch to the `terraria` user.
- New `fix-perms` command: `sudo terractl fix-perms <name>` gives every file of an instance back to the `terraria` user and reapplies the usual modes (group can read, others nothing; server binaries executable), e.g. after copying worlds in as root. Symlinks are not followed. Works with `--host` too.
- `list` warns about zips in `dist/` that the `terraria` user can't read (e.g. copied in with `scp -p` from a private file), and `info` lists them as `unreadable_zip=`. A server that fails to install because of one now says how to fix it (`sudo chmod 644 <zip>`) instead of just "cannot read", and a missing zip is reported as not found.
+13 -3
View File
@@ -1074,6 +1074,15 @@ svc_can_read() {
else (( 8#$mode & 0004 )); fi
}
# Check that the service user can run this script, which prepare/run (as root)
# and upgrade switch to. Not so for a copy in a private folder, such as a
# development copy under /root.
need_svc_can_run_self() {
command -v runuser >/dev/null || die "runuser not found; run as '$SVC_USER' instead"
runuser -u "$SVC_USER" -- test -x "$SELF" 2>/dev/null ||
die "$1 runs $SELF as the '$SVC_USER' user, who can't open it (it's in a folder only root can open, e.g. /root). Use the installed terractl, or a copy somewhere '$SVC_USER' can read."
}
# Zips in dist/ the servers can't unpack, e.g. copied in with 'scp -p' from a
# private file. Instances fail on their first start with such a zip.
unreadable_zips() {
@@ -1373,6 +1382,7 @@ cmd_upgrade() {
fi
command -v unzip >/dev/null || die "unzip is not installed"
need_svc_can_run_self "upgrade"
listing=$(unzip -Z1 "$f" 2>/dev/null) || die "$f isn't a readable zip (damaged, or not fully downloaded?)"
case $TYPE in
vanilla) grep -q '/Linux/TerrariaServer\.bin\.x86_64$' <<< "$listing" ||
@@ -1402,7 +1412,7 @@ cmd_upgrade() {
backup_file+=.tar.gz
# In a subshell, so a failure comes back here instead of exiting
if ! ( trap cleanup EXIT; cmd_export "$name" "$backup_file" ) >/dev/null; then
if [[ $was_running -eq 1 ]]; then systemctl start "$unit"; fi
if [[ $was_running -eq 1 ]]; then systemctl start "$unit"; echo "$unit started again"; fi
die "backing up '$name' failed, so its version wasn't changed"
fi
echo "backup of worlds and configs: $backup_file"
@@ -1414,7 +1424,7 @@ cmd_upgrade() {
# new server/ is complete, the old one is left in place.
if ! runuser -u "$SVC_USER" -- env HOME="$dir" "$SELF" prepare "$name"; then
set_zip_pin "$name" "$old_pin"
if [[ $was_running -eq 1 ]]; then systemctl start "$unit"; fi
if [[ $was_running -eq 1 ]]; then systemctl start "$unit"; echo "$unit started again"; fi
die "unpacking $zip failed (see above); '$name' still runs ${installed:-its old version}"
fi
@@ -1529,7 +1539,7 @@ valid_name "$1"
# debugging), switch to the service user so nothing ends up owned by root.
if [[ $command == prepare || $command == run ]]; then
if [[ $EUID -eq 0 ]]; then
command -v runuser >/dev/null || die "runuser not found; run as '$SVC_USER' instead"
need_svc_can_run_self "'$command'"
exec runuser -u "$SVC_USER" -- env HOME="$(inst_dir "$1")" \
"$SELF" "$command" "$@"
elif [[ $(id -un) != "$SVC_USER" ]]; then