diff --git a/CHANGELOG.md b/CHANGELOG.md index 30058f1..3781f65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- `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 `) instead of just "cannot read", and a missing zip is reported as not found. + ## 0.0.11 - 2026-09-30 - The servers now log to their own journal (systemd log namespace `terraria`), which members of the `terraria` group can read. `attach` and `cmd` therefore show output to `terraria` group members without `sudo` or the `systemd-journal` group, and without access to the rest of the system's logs. Read it directly with `journalctl --namespace=terraria -u terraria@`. diff --git a/bin/terractl b/bin/terractl index eef42b1..e111c0a 100755 --- a/bin/terractl +++ b/bin/terractl @@ -229,7 +229,8 @@ install_server() { command -v unzip >/dev/null || die "unzip is not installed" zip=$(pick_zip) - [[ -r $zip ]] || die "cannot read $zip" + [[ -e $zip ]] || die "$zip not found" + [[ -r $zip ]] || die "the '$SVC_USER' user can't read $zip; fix it with: sudo chmod 644 $zip" log "installing $TYPE from $(basename "$zip") into $dir/server" EXTRACT_TMP=$(mktemp -d "$dir/.extract.XXXXXX") @@ -1049,6 +1050,36 @@ cmd_enable() { fi } +# Can the service user read ? Worked out from the file's owner, group and +# mode, because whoever runs terractl may be someone else. +svc_can_read() { + local owner group mode + read -r owner group mode < <(stat -c '%U %G %a' "$1" 2>/dev/null) || return 1 + if [[ $owner == "$SVC_USER" ]]; then (( 8#$mode & 0400 )) + elif [[ $group == "$SVC_USER" ]]; then (( 8#$mode & 0040 )) + else (( 8#$mode & 0004 )); fi +} + +# 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() { + local f + for f in "$DIST_DIR"/*.zip; do + if [[ -f $f ]] && ! svc_can_read "$f"; then echo "$f"; fi + done +} + +zip_warning() { + local where="" + local -a bad=() + mapfile -t bad < <(unreadable_zips) + [[ ${#bad[@]} -gt 0 ]] || return 0 + if [[ -n ${TERRACTL_VIA:-} ]]; then where=" (on $(hostname 2>/dev/null || echo that host))"; fi + log "warning: the '$SVC_USER' user can't read these zips, so servers can't be installed from them:" + printf ' %s\n' "${bad[@]}" >&2 + log "fix it with$where: sudo chmod 644 ${bad[*]@Q}" +} + cmd_list() { local d n port boot state printf '%-20s %-11s %-6s %-9s %s\n' INSTANCE TYPE PORT BOOT STATE @@ -1065,6 +1096,7 @@ cmd_list() { printf '%-20s %-11s %-6s %-9s %s\n' "$n" "$TYPE" "${port:--}" "${boot:--}" "$state" ) 2>/dev/null || printf '%-20s %s\n' "$n" "(cannot read config - try sudo)" done + zip_warning } cmd_reinstall() { @@ -1101,6 +1133,7 @@ cmd_info() { for f in "$DIST_DIR"/*.zip; do if [[ -f $f ]]; then echo "dist_zip=$(basename "$f")"; fi done + while IFS= read -r f; do echo "unreadable_zip=$(basename "$f")"; done < <(unreadable_zips) for f in "$BASE_DIR"/*/instance.conf; do if [[ -f $f ]]; then echo "instance=$(basename "$(dirname "$f")")"; fi done diff --git a/docs/guide.md b/docs/guide.md index f7281f3..9949376 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -268,6 +268,8 @@ Zips are only read, never changed, and one zip can serve any number of instances **tModLoader zips don't include .NET.** Each tModLoader instance downloads the .NET runtime it needs into `server/dotnet/` on its first start, so that start needs internet access and `curl`. The download is kept when you upgrade or re-unpack the instance. tModLoader checks the .NET version on every start and fetches a different one itself if a new release needs it. +**The `terraria` user must be able to read the zips.** Files copied in normally are fine, but `scp -p` or `cp -p` of a private file keeps its `600` mode, and a server can't be installed from that. `terractl list` warns about such zips, and a server that fails to start because of one says so in its log. Fix them with `sudo chmod 644 /var/lib/terraria/dist/*.zip`. + Members of the `terraria` group can copy zips into `dist/` without `sudo`: ```bash